QGIS API Documentation 3.37.0-Master (fdefdf9c27f)
qgsserverapiutils.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsserverapiutils.cpp
3
4 Class defining utilities for QGIS server APIs.
5 -------------------
6 begin : 2019-04-16
7 copyright : (C) 2019 by Alessandro Pasotti
8 email : elpaso at itopen dot it
9 ***************************************************************************/
10
11/***************************************************************************
12 * *
13 * This program is free software; you can redistribute it and/or modify *
14 * it under the terms of the GNU General Public License as published by *
15 * the Free Software Foundation; either version 2 of the License, or *
16 * (at your option) any later version. *
17 * *
18 ***************************************************************************/
19
20#include "qgsserverapiutils.h"
21#include "qgsrectangle.h"
22#include "qgsvectorlayer.h"
25#include "qgsmessagelog.h"
26
27
28#include "nlohmann/json.hpp"
29
30#include <QUrl>
31#include <QUrlQuery>
32
34{
35 const QStringList parts { bbox.split( ',', Qt::SplitBehaviorFlags::SkipEmptyParts ) };
36 // Note: Z is ignored
37 bool ok { true };
38 if ( parts.count() == 4 || parts.count() == 6 )
39 {
40 const auto hasZ { parts.count() == 6 };
41 auto toDouble = [ & ]( const int i ) -> double
42 {
43 if ( ! ok )
44 return 0;
45 return parts[i].toDouble( &ok );
46 };
47 QgsRectangle rect;
48 if ( hasZ )
49 {
50 rect = QgsRectangle( toDouble( 0 ), toDouble( 1 ),
51 toDouble( 3 ), toDouble( 4 ) );
52 }
53 else
54 {
55 rect = QgsRectangle( toDouble( 0 ), toDouble( 1 ),
56 toDouble( 2 ), toDouble( 3 ) );
57 }
58 if ( ok )
59 {
60 return rect;
61 }
62 }
63 return QgsRectangle();
64}
65
66QList< QgsMapLayerServerProperties::WmsDimensionInfo > QgsServerApiUtils::temporalDimensions( const QgsVectorLayer *layer )
67{
68
69 const QgsMapLayerServerProperties *serverProperties = layer->serverProperties();
70 QList< QgsMapLayerServerProperties::WmsDimensionInfo > dimensions { serverProperties->wmsDimensions() };
71 // Filter only date and time
72 dimensions.erase( std::remove_if( dimensions.begin(),
73 dimensions.end(),
75 {
76 return dim.name.toLower() != QStringLiteral( "time" )
77 && dim.name.toLower() != QStringLiteral( "date" ) ;
78 } ), dimensions.end() );
79
80 // Automatically pick up the first date/datetime field if dimensions is empty
81 if ( dimensions.isEmpty() )
82 {
83 const auto constFields { layer->fields() };
84 for ( const auto &f : constFields )
85 {
86 if ( f.isDateOrTime() )
87 {
88 dimensions.append( QgsMapLayerServerProperties::WmsDimensionInfo( f.type() == QVariant::DateTime ?
89 QStringLiteral( "time" ) :
90 QStringLiteral( "date" ), f.name() ) );
91 break;
92 }
93 }
94 }
95 return dimensions;
96}
97
99template<typename T, class T2> T QgsServerApiUtils::parseTemporalInterval( const QString &interval )
100{
101 auto parseDate = [ ]( const QString & date ) -> T2
102 {
103 T2 result;
104 if ( date == QLatin1String( ".." ) || date.isEmpty() )
105 {
106 return result;
107 }
108 else
109 {
110 T2 result { T2::fromString( date, Qt::DateFormat::ISODate ) };
111 if ( !result.isValid() )
112 {
113 throw QgsServerApiBadRequestException( QStringLiteral( "%1 is not a valid date/datetime." ).arg( date ) );
114 }
115 return result;
116 }
117 };
118 const QStringList parts { interval.split( '/' ) };
119 if ( parts.size() != 2 )
120 {
121 throw QgsServerApiBadRequestException( QStringLiteral( "%1 is not a valid datetime interval." ).arg( interval ), QStringLiteral( "Server" ) );
122 }
123 // cppcheck-suppress containerOutOfBounds
124 T result { parseDate( parts[0] ), parseDate( parts[1] ) };
125 // Check validity
126 if ( result.isEmpty() )
127 {
128 throw QgsServerApiBadRequestException( QStringLiteral( "%1 is not a valid datetime interval (empty)." ).arg( interval ), QStringLiteral( "Server" ) );
129 }
130 return result;
131}
133
135{
136 return QgsServerApiUtils::parseTemporalInterval<QgsDateRange, QDate>( interval );
137}
138
140{
141 return QgsServerApiUtils::parseTemporalInterval<QgsDateTimeRange, QDateTime>( interval );
142}
143
145{
146 QgsExpression expression;
147 QStringList conditions;
148
149 const auto dimensions { QgsServerApiUtils::temporalDimensions( layer ) };
150 if ( dimensions.isEmpty() )
151 {
152 return expression;
153 }
154
155 // helper to get the field type from the field name
156 auto fieldTypeFromName = [ & ]( const QString & fieldName, const QgsVectorLayer * layer ) -> QVariant::Type
157 {
158 int fieldIdx { layer->fields().lookupField( fieldName ) };
159 if ( fieldIdx < 0 )
160 {
161 return QVariant::Type::Invalid;
162 }
163 const QgsField field { layer->fields().at( fieldIdx ) };
164 return field.type();
165 };
166
167 // helper to cast the field value
168 auto refFieldCast = [ & ]( const QString & fieldName, QVariant::Type queryType, QVariant::Type fieldType ) -> QString
169 {
170
171 const auto fieldRealType { fieldTypeFromName( fieldName, layer ) };
172 if ( fieldRealType == QVariant::Type::Invalid )
173 {
174 return QString();
175 }
176
177 // Downcast only datetime -> date
178 // always cast strings
179 if ( fieldRealType == QVariant::Type::String )
180 {
181 // Cast to query type but only downcast
182 if ( fieldType != queryType || fieldType == QVariant::Type::Date )
183 {
184 return QStringLiteral( "to_date( %1 )" ).arg( QgsExpression::quotedColumnRef( fieldName ) );
185 }
186 else
187 {
188 return QStringLiteral( "%2( %1 )" ).arg( QgsExpression::quotedColumnRef( fieldName ) )
189 .arg( queryType == QVariant::Type::Date ? QStringLiteral( "to_date" ) : QStringLiteral( "to_datetime" ) );
190 }
191 }
192 else if ( fieldType == queryType || fieldType == QVariant::Type::Date )
193 {
195 }
196 else
197 {
198 return QStringLiteral( "%2( %1 )" ).arg( QgsExpression::quotedColumnRef( fieldName ) )
199 .arg( queryType == QVariant::Type::Date ? QStringLiteral( "to_date" ) : QStringLiteral( "to_datetime" ) );
200 }
201 };
202
203 // Quote and cast a query value
204 auto quoteValue = [ ]( const QString & value ) -> QString
205 {
206 if ( value.length() == 10 )
207 {
208 return QStringLiteral( "to_date( %1 )" ).arg( QgsExpression::quotedValue( value ) );
209 }
210 else
211 {
212 return QStringLiteral( "to_datetime( %1 )" ).arg( QgsExpression::quotedValue( value ) );
213 }
214 };
215
216 // helper to build the interval filter, fieldType is the underlying field type, queryType is the input query type
217 auto makeFilter = [ &quoteValue ]( const QString & fieldBegin, const QString & fieldEnd,
218 const QString & fieldBeginCasted, const QString & fieldEndCasted,
219 const QString & queryBegin, const QString & queryEnd ) -> QString
220 {
221 QString result;
222
223 // It's a closed interval query, go for overlap
224 if ( ! queryBegin.isEmpty() && ! queryEnd.isEmpty() )
225 {
226 // Overlap of two intervals
227 if ( ! fieldEndCasted.isEmpty() )
228 {
229 result = QStringLiteral( "( %1 IS NULL OR %2 <= %6 ) AND ( %4 IS NULL OR %5 >= %3 )" )
230 .arg( fieldBegin,
231 fieldBeginCasted,
232 quoteValue( queryBegin ),
233 fieldEnd,
234 fieldEndCasted,
235 quoteValue( queryEnd ) );
236 }
237 else // Overlap of single value
238 {
239 result = QStringLiteral( "( %1 IS NULL OR ( %2 <= %3 AND %3 <= %4 ) )" )
240 .arg( fieldBegin,
241 quoteValue( queryBegin ),
242 fieldBeginCasted,
243 quoteValue( queryEnd ) );
244 }
245
246 }
247 else if ( ! queryBegin.isEmpty() ) // >=
248 {
249 if ( ! fieldEndCasted.isEmpty() )
250 {
251 result = QStringLiteral( "( %1 IS NULL OR %2 >= %3 )" ).arg( fieldEnd, fieldEndCasted, quoteValue( queryBegin ) );
252 }
253 else
254 {
255 result = QStringLiteral( "( %1 IS NULL OR %2 >= %3 )" ).arg( fieldBegin, fieldBeginCasted, quoteValue( queryBegin ) );
256 }
257 }
258 else // <=
259 {
260 result = QStringLiteral( "( %1 IS NULL OR %2 <= %3 )" ).arg( fieldBegin, fieldBeginCasted, quoteValue( queryEnd ) );
261 }
262 return result;
263 };
264
265 // Determine if it is a date or a datetime interval (mixed are not supported)
266 QString testType { interval };
267 if ( interval.contains( '/' ) )
268 {
269 const QStringList parts { interval.split( '/' ) };
270 testType = parts[0];
271 if ( testType.isEmpty() || testType == QLatin1String( ".." ) )
272 {
273 // cppcheck-suppress containerOutOfBounds
274 testType = parts[1];
275 }
276 }
277 // Determine query input type: datetime is always longer than 10 chars
278 const bool inputQueryIsDateTime { testType.length() > 10 };
279 const QVariant::Type queryType { inputQueryIsDateTime ? QVariant::Type::DateTime : QVariant::Type::Date };
280
281 // Is it an interval?
282 if ( interval.contains( '/' ) )
283 {
284 if ( ! inputQueryIsDateTime )
285 {
287
288 for ( const auto &dimension : std::as_const( dimensions ) )
289 {
290
291 // Determine the field type from the dimension name "time"/"date"
292 const QVariant::Type fieldType { dimension.name.toLower() == QLatin1String( "time" ) ? QVariant::Type::DateTime : QVariant::Type::Date };
293
294 const auto fieldBeginCasted { refFieldCast( dimension.fieldName, queryType, fieldType ) };
295 if ( fieldBeginCasted.isEmpty() )
296 {
297 continue;
298 }
299
300 const auto fieldBegin = QgsExpression::quotedColumnRef( dimension.fieldName );
301 const auto fieldEnd = QgsExpression::quotedColumnRef( dimension.endFieldName );
302
303 // This may be empty:
304 const auto fieldEndCasted { refFieldCast( dimension.endFieldName, queryType, fieldType ) };
305 if ( ! dateInterval.begin().isValid( ) && ! dateInterval.end().isValid( ) )
306 {
307 // Nothing to do here: log?
308 }
309 else
310 {
311 conditions.push_back( makeFilter( fieldBegin,
312 fieldEnd,
313 fieldBeginCasted,
314 fieldEndCasted,
315 dateInterval.begin().toString( Qt::DateFormat::ISODate ),
316 dateInterval.end().toString( Qt::DateFormat::ISODate ) ) );
317
318 }
319 }
320 }
321 else // try datetime
322 {
324 for ( const auto &dimension : std::as_const( dimensions ) )
325 {
326
327 // Determine the field type from the dimension name "time"/"date"
328 const QVariant::Type fieldType { dimension.name.toLower() == QLatin1String( "time" ) ? QVariant::Type::DateTime : QVariant::Type::Date };
329
330 const auto fieldfBeginCasted { refFieldCast( dimension.fieldName, queryType, fieldType ) };
331 if ( fieldfBeginCasted.isEmpty() )
332 {
333 continue;
334 }
335 const auto fieldBegin = QgsExpression::quotedColumnRef( dimension.fieldName );
336 const auto fieldEnd = QgsExpression::quotedColumnRef( dimension.endFieldName );
337
338 // This may be empty:
339 const auto fieldEndCasted { refFieldCast( dimension.endFieldName, queryType, fieldType ) };
340 if ( ! dateTimeInterval.begin().isValid( ) && ! dateTimeInterval.end().isValid( ) )
341 {
342 // Nothing to do here: log?
343 }
344 else
345 {
346 // Cast the query value according to the field type
347 QString beginQuery;
348 QString endQuery;
349 // Drop the time
350 if ( fieldType == QVariant::Type::Date )
351 {
352 beginQuery = dateTimeInterval.begin().date().toString( Qt::DateFormat::ISODate );
353 endQuery = dateTimeInterval.end().date().toString( Qt::DateFormat::ISODate );
354 }
355 else
356 {
357 beginQuery = dateTimeInterval.begin().toString( Qt::DateFormat::ISODate );
358 endQuery = dateTimeInterval.end().toString( Qt::DateFormat::ISODate );
359 }
360 conditions.push_back( makeFilter( fieldBegin,
361 fieldEnd,
362 fieldfBeginCasted,
363 fieldEndCasted,
364 beginQuery,
365 endQuery ) );
366 }
367 }
368 }
369 }
370 else // single value
371 {
372
373 for ( const auto &dimension : std::as_const( dimensions ) )
374 {
375 // Determine the field type from the dimension name "time"/"date"
376 const bool fieldIsDateTime { dimension.name.toLower() == QLatin1String( "time" ) };
377 const QVariant::Type fieldType { fieldIsDateTime ? QVariant::Type::DateTime : QVariant::Type::Date };
378
379 const auto fieldRefBegin { refFieldCast( dimension.fieldName, queryType, fieldType ) };
380 if ( fieldRefBegin.isEmpty() )
381 {
382 continue;
383 }
384 const auto fieldBegin = QgsExpression::quotedColumnRef( dimension.fieldName );
385
386 // This may be empty:
387 const auto fieldRefEnd { refFieldCast( dimension.endFieldName, queryType, fieldType ) };
388 const auto fieldEnd = QgsExpression::quotedColumnRef( dimension.endFieldName );
389
390 QString condition;
391 QString castedValue;
392
393 // field has possibly been downcasted
394 if ( ! inputQueryIsDateTime || ! fieldIsDateTime )
395 {
396 QString castedInterval { interval };
397 // Check if we need to downcast interval from datetime
398 if ( inputQueryIsDateTime )
399 {
400 castedInterval = QDate::fromString( castedInterval, Qt::DateFormat::ISODate ).toString( Qt::DateFormat::ISODate );
401 }
402 castedValue = QStringLiteral( "to_date( %1 )" ).arg( QgsExpression::quotedValue( castedInterval ) );
403 }
404 else
405 {
406 QString castedInterval { interval };
407 // Check if we need to upcast interval to datetime
408 if ( ! inputQueryIsDateTime )
409 {
410 castedInterval = QDateTime::fromString( castedInterval, Qt::DateFormat::ISODate ).toString( Qt::DateFormat::ISODate );
411 }
412 castedValue = QStringLiteral( "to_datetime( %1 )" ).arg( QgsExpression::quotedValue( castedInterval ) );
413 }
414
415 if ( ! fieldRefEnd.isEmpty() )
416 {
417 condition = QStringLiteral( "( %1 IS NULL OR %2 <= %3 ) AND ( %5 IS NULL OR %3 <= %4 )" ).arg(
418 fieldBegin,
419 fieldRefBegin,
420 castedValue,
421 fieldRefEnd,
422 fieldEnd );
423 }
424 else
425 {
426 condition = QStringLiteral( "( %1 IS NULL OR %2 = %3 )" )
427 .arg( fieldBegin,
428 fieldRefBegin,
429 castedValue );
430
431 }
432 conditions.push_back( condition );
433
434 }
435 }
436 if ( ! conditions.isEmpty() )
437 {
438 expression.setExpression( conditions.join( QLatin1String( " AND " ) ) );
439 }
440 return expression;
441}
442
444{
445 auto extent { layer->extent() };
446 if ( layer->crs().authid() != QLatin1String( "EPSG:4326" ) )
447 {
448 static const QgsCoordinateReferenceSystem targetCrs( QStringLiteral( "EPSG:4326" ) );
449 const QgsCoordinateTransform ct( layer->crs(), targetCrs, layer->transformContext() );
450 extent = ct.transform( extent );
451 }
452 return {{ extent.xMinimum(), extent.yMinimum(), extent.xMaximum(), extent.yMaximum() }};
453}
454
456{
457 // Helper to get min/max from a dimension
458 auto range = [ & ]( const QgsMapLayerServerProperties::WmsDimensionInfo & dimInfo ) -> QgsDateTimeRange
459 {
460 QgsDateTimeRange result;
461 // min
462 int fieldIdx { layer->fields().lookupField( dimInfo.fieldName )};
463 if ( fieldIdx < 0 )
464 {
465 return result;
466 }
467
468 QVariant minVal;
469 QVariant maxVal;
470 layer->minimumAndMaximumValue( fieldIdx, minVal, maxVal );
471
472 QDateTime min { minVal.toDateTime() };
473 QDateTime max { maxVal.toDateTime() };
474 if ( ! dimInfo.endFieldName.isEmpty() )
475 {
476 fieldIdx = layer->fields().lookupField( dimInfo.endFieldName );
477 if ( fieldIdx >= 0 )
478 {
479 QVariant minVal;
480 QVariant maxVal;
481 layer->minimumAndMaximumValue( fieldIdx, minVal, maxVal );
482
483 QDateTime minEnd { minVal.toDateTime() };
484 QDateTime maxEnd { maxVal.toDateTime() };
485 if ( minEnd.isValid() )
486 {
487 min = std::min<QDateTime>( min, minEnd );
488 }
489 if ( maxEnd.isValid() )
490 {
491 max = std::max<QDateTime>( max, maxEnd );
492 }
493 }
494 }
495 return { min, max };
496 };
497
498 const QList<QgsMapLayerServerProperties::WmsDimensionInfo> dimensions { QgsServerApiUtils::temporalDimensions( layer ) };
499 if ( dimensions.isEmpty() )
500 {
501 return nullptr;
502 }
503 else
504 {
505 try
506 {
507 QgsDateTimeRange extent;
508 bool isFirst = true;
509 for ( const auto &dimension : dimensions )
510 {
511 // Get min/max for dimension
512 if ( isFirst )
513 {
514 extent = range( dimension );
515 isFirst = false;
516 }
517 else
518 {
519 extent.extend( range( dimension ) );
520 }
521 }
522 json ret = json::array();
523 const QString beginVal { extent.begin().toString( Qt::DateFormat::ISODate ) };
524 const QString endVal { extent.end().toString( Qt::DateFormat::ISODate ) };
525 // We cannot mix nullptr and std::string :(
526 if ( beginVal.isEmpty() && endVal.isEmpty() )
527 {
528 ret.push_back( { nullptr, nullptr } );
529 }
530 else if ( beginVal.isEmpty() )
531 {
532 ret.push_back( { nullptr, endVal.toStdString() } );
533 }
534 else if ( endVal.isEmpty() )
535 {
536 ret.push_back( { beginVal.toStdString(), nullptr } );
537 }
538 else
539 {
540 ret.push_back( { beginVal.toStdString(), endVal.toStdString() } );
541 }
542 return ret;
543 }
544 catch ( std::exception &ex )
545 {
546 const QString errorMessage { QStringLiteral( "Error creating temporal extent: %1" ).arg( ex.what() ) };
547 QgsMessageLog::logMessage( errorMessage, QStringLiteral( "Server" ), Qgis::MessageLevel::Critical );
548 throw QgsServerApiInternalServerError( errorMessage );
549 }
550 }
551}
552
553QVariantList QgsServerApiUtils::temporalExtentList( const QgsVectorLayer *layer ) SIP_PYNAME( temporalExtent )
554{
555 QVariantList list;
556 list.push_back( QgsJsonUtils::parseArray( QString::fromStdString( temporalExtent( layer )[0].dump() ) ) );
557 return list;
558}
559
561{
562 // We get this:
563 // http://www.opengis.net/def/crs/OGC/1.3/CRS84
564 // We want this:
565 // "urn:ogc:def:crs:<auth>:[<version>]:<code>"
566 const auto parts { QUrl( bboxCrs ).path().split( '/' ) };
567 if ( parts.count() == 6 )
568 {
569 return QgsCoordinateReferenceSystem::fromOgcWmsCrs( QStringLiteral( "urn:ogc:def:crs:%1:%2:%3" ).arg( parts[3], parts[4], parts[5] ) );
570 }
571 else
572 {
574 }
575}
576
577const QVector<QgsVectorLayer *> QgsServerApiUtils::publishedWfsLayers( const QgsServerApiContext &context )
578{
579 return publishedWfsLayers< QgsVectorLayer * >( context );
580}
581
582QString QgsServerApiUtils::fieldName( const QString &name, const QgsVectorLayer *layer )
583{
584 if ( layer->fields().names().contains( name ) )
585 {
586 return name;
587 }
588 const QgsFields fields { layer->fields() };
589 for ( const QgsField &field : std::as_const( fields ) )
590 {
591 if ( field.displayName() == name )
592 {
593 return field.name();
594 }
595 }
596 throw QgsServerApiBadRequestException{ QStringLiteral( "Field '%1' is not a valid field name for layer: %2" ).arg( name, layer->name() ) };
597}
598
599QString QgsServerApiUtils::sanitizedFieldValue( const QString &value )
600{
601 QString result { QUrl( value ).toString() };
602 return result.replace( '\'', QLatin1String( "\'" ) );
603}
604
606{
607 // This must be always available in OGC APIs
608 QStringList result { { QStringLiteral( "http://www.opengis.net/def/crs/OGC/1.3/CRS84" )}};
609 if ( project )
610 {
611 const QStringList outputCrsList = QgsServerProjectUtils::wmsOutputCrsList( *project );
612 for ( const QString &crsId : outputCrsList )
613 {
614 const auto crsUri { QgsCoordinateReferenceSystem::fromOgcWmsCrs( crsId ).toOgcUri() };
615 if ( ! crsUri.isEmpty() )
616 {
617 result.push_back( crsUri );
618 }
619 }
620 }
621 return result;
622}
623
625{
626 return crs.toOgcUri();
627}
628
629QString QgsServerApiUtils::appendMapParameter( const QString &path, const QUrl &requestUrl )
630{
631 QList<QPair<QString, QString> > qi;
632 QString result { path };
633 const auto constItems { QUrlQuery( requestUrl ).queryItems() };
634 for ( const auto &i : constItems )
635 {
636 if ( i.first.compare( QStringLiteral( "MAP" ), Qt::CaseSensitivity::CaseInsensitive ) == 0 )
637 {
638 qi.push_back( i );
639 }
640 }
641 if ( ! qi.empty() )
642 {
643 if ( ! path.endsWith( '?' ) )
644 {
645 result += '?';
646 }
647 result.append( QStringLiteral( "MAP=%1" ).arg( qi.first().second ) );
648 }
649 return result;
650}
651
This class represents a coordinate reference system (CRS).
static QgsCoordinateReferenceSystem fromOgcWmsCrs(const QString &ogcCrs)
Creates a CRS from a given OGC WMS-format Coordinate Reference System string.
QString toOgcUri() const
Returns the crs as OGC URI (format: http://www.opengis.net/def/crs/OGC/1.3/CRS84) Returns an empty st...
Class for doing transforms between two map coordinate systems.
QgsPointXY transform(const QgsPointXY &point, Qgis::TransformDirection direction=Qgis::TransformDirection::Forward) const
Transform the point from the source CRS to the destination CRS.
Class for parsing and evaluation of expressions (formerly called "search strings").
static QString quotedValue(const QVariant &value)
Returns a string representation of a literal value, including appropriate quotations where required.
void setExpression(const QString &expression)
Set the expression string, will reset the whole internal structure.
static QString quotedColumnRef(QString name)
Returns a quoted column reference (in double quotes)
Encapsulate a field in an attribute table or data source.
Definition: qgsfield.h:53
QVariant::Type type
Definition: qgsfield.h:60
Container of fields for a vector layer.
Definition: qgsfields.h:45
bool append(const QgsField &field, FieldOrigin origin=OriginProvider, int originIndex=-1)
Appends a field. The field must have unique name, otherwise it is rejected (returns false)
Definition: qgsfields.cpp:59
QgsField at(int i) const
Returns the field at particular index (must be in range 0..N-1).
Definition: qgsfields.cpp:163
int lookupField(const QString &fieldName) const
Looks up field's index from the field name.
Definition: qgsfields.cpp:359
QStringList names() const
Returns a list with field names.
Definition: qgsfields.cpp:143
static Q_INVOKABLE QVariantList parseArray(const QString &json, QVariant::Type type=QVariant::Invalid)
Parse a simple array (depth=1)
Manages QGIS Server properties for a map layer.
QString name
Definition: qgsmaplayer.h:78
QgsCoordinateReferenceSystem crs
Definition: qgsmaplayer.h:81
QgsMapLayerServerProperties * serverProperties()
Returns QGIS Server Properties for the map layer.
Definition: qgsmaplayer.h:422
QgsCoordinateTransformContext transformContext() const
Returns the layer data provider coordinate transform context or a default transform context if the la...
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::MessageLevel::Warning, bool notifyUser=true)
Adds a message to the log instance (and creates it if necessary).
Encapsulates a QGIS project, including sets of map layers and their styles, layouts,...
Definition: qgsproject.h:107
A rectangle specified with double values.
Definition: qgsrectangle.h:42
Bad request error API exception.
The QgsServerApiContext class encapsulates the resources for a particular client request: the request...
Internal server error API exception.
static QString sanitizedFieldValue(const QString &value)
Sanitizes the input value by removing URL encoding.
static QgsExpression temporalFilterExpression(const QgsVectorLayer *layer, const QString &interval)
Parses the interval and constructs a (possibly invalid) temporal filter expression for the given laye...
static QStringList publishedCrsList(const QgsProject *project)
Returns the list of CRSs (format: http://www.opengis.net/def/crs/OGC/1.3/CRS84) available for this pr...
static QVariantList temporalExtentList(const QgsVectorLayer *layer)
temporalExtent returns a json array with an array of [min, max] temporal extent for the given layer.
static QString fieldName(const QString &name, const QgsVectorLayer *layer)
Given a field name (or display name) and a layer returns the actual name of the field.
static QgsCoordinateReferenceSystem parseCrs(const QString &bboxCrs)
Parses the CRS URI bboxCrs (example: "http://www.opengis.net/def/crs/OGC/1.3/CRS84") into a QGIS CRS ...
static Q_DECL_DEPRECATED QString crsToOgcUri(const QgsCoordinateReferenceSystem &crs)
Returns a crs as OGC URI (format: http://www.opengis.net/def/crs/OGC/1.3/CRS84) Returns an empty stri...
static json temporalExtent(const QgsVectorLayer *layer)
temporalExtent returns a json array with an array of [min, max] temporal extent for the given layer.
static const QVector< QgsVectorLayer * > publishedWfsLayers(const QgsServerApiContext &context)
Returns the list of layers accessible to the service for a given context.
static json layerExtent(const QgsVectorLayer *layer)
layerExtent returns json array with [xMin,yMin,xMax,yMax] CRS84 extent for the given layer
static QgsDateTimeRange parseTemporalDateTimeInterval(const QString &interval)
Parses a datetime interval and returns a QgsDateTimeRange.
static QgsDateRange parseTemporalDateInterval(const QString &interval)
Parses a date interval and returns a QgsDateRange.
static QList< QgsServerWmsDimensionProperties::WmsDimensionInfo > temporalDimensions(const QgsVectorLayer *layer)
Returns a list of temporal dimensions information for the given layer (either configured in wmsDimens...
static QgsRectangle parseBbox(const QString &bbox)
Parses a comma separated bbox into a (possibly empty) QgsRectangle.
static QString appendMapParameter(const QString &path, const QUrl &requestUrl)
Appends MAP query string parameter from current requestUrl to the given path.
const QList< QgsServerWmsDimensionProperties::WmsDimensionInfo > wmsDimensions() const
Returns the QGIS Server WMS Dimension list.
A template based class for storing temporal ranges (beginning to end values).
Definition: qgsrange.h:419
T begin() const
Returns the beginning of the range.
Definition: qgsrange.h:444
bool extend(const QgsTemporalRange< T > &other)
Extends the range in place by extending this range out to include an other range.
Definition: qgsrange.h:604
T end() const
Returns the upper bound of the range.
Definition: qgsrange.h:451
Represents a vector layer which manages a vector based data sets.
QgsFields fields() const FINAL
Returns the list of fields of this layer.
void minimumAndMaximumValue(int index, QVariant &minimum, QVariant &maximum) const
Calculates both the minimum and maximum value for an attribute column.
QgsRectangle extent() const FINAL
Returns the extent of the layer.
SERVER_EXPORT QStringList wmsOutputCrsList(const QgsProject &project)
Returns the WMS output CRS list.
#define SIP_PYNAME(name)
Definition: qgis_sip.h:81
const QgsCoordinateReferenceSystem & crs
Setting to define QGIS Server WMS Dimension.