QGIS API Documentation 4.1.0-Master (5bf3c20f3c9)
Loading...
Searching...
No Matches
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
22#include <nlohmann/json.hpp>
23
25#include "qgsmessagelog.h"
26#include "qgsrectangle.h"
28#include "qgsvectorlayer.h"
29
30#include <QString>
31#include <QUrl>
32#include <QUrlQuery>
33
34using namespace Qt::StringLiterals;
35
37{
38 const QStringList parts { bbox.split( ',', Qt::SplitBehaviorFlags::SkipEmptyParts ) };
39 // Note: Z is ignored
40 bool ok { true };
41 if ( parts.count() == 4 || parts.count() == 6 )
42 {
43 const auto hasZ { parts.count() == 6 };
44 auto toDouble = [&]( const int i ) -> double {
45 if ( !ok )
46 return 0;
47 return parts[i].toDouble( &ok );
48 };
49 QgsRectangle rect;
50 if ( hasZ )
51 {
52 rect = QgsRectangle( toDouble( 0 ), toDouble( 1 ), toDouble( 3 ), toDouble( 4 ) );
53 }
54 else
55 {
56 rect = QgsRectangle( toDouble( 0 ), toDouble( 1 ), 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 if ( !layer )
69 return {};
70
71 const QgsMapLayerServerProperties *serverProperties = layer->serverProperties();
72 QList<QgsMapLayerServerProperties::WmsDimensionInfo> dimensions { serverProperties->wmsDimensions() };
73 // Filter only date and time
74 dimensions.erase(
75 std::remove_if( dimensions.begin(), dimensions.end(), []( QgsMapLayerServerProperties::WmsDimensionInfo &dim ) { return dim.name.toLower() != "time"_L1 && dim.name.toLower() != "date"_L1; } ),
76 dimensions.end()
77 );
78
79 // Automatically pick up the first date/datetime field if dimensions is empty
80 if ( dimensions.isEmpty() )
81 {
82 const auto constFields { layer->fields() };
83 for ( const auto &f : constFields )
84 {
85 if ( f.isDateOrTime() )
86 {
87 dimensions.append( QgsMapLayerServerProperties::WmsDimensionInfo( f.type() == QMetaType::Type::QDateTime ? u"time"_s : u"date"_s, f.name() ) );
88 break;
89 }
90 }
91 }
92 return dimensions;
93}
94
96template<typename T, class T2> T QgsServerApiUtils::parseTemporalInterval( const QString &interval )
97{
98 auto parseDate = []( const QString &date ) -> T2 {
99 T2 result;
100 if ( date == ".."_L1 || date.isEmpty() )
101 {
102 return result;
103 }
104 else
105 {
106 T2 result { T2::fromString( date, Qt::DateFormat::ISODate ) };
107 if ( !result.isValid() )
108 {
109 throw QgsServerApiBadRequestException( u"%1 is not a valid date/datetime."_s.arg( date ) );
110 }
111 return result;
112 }
113 };
114 const QStringList parts { interval.split( '/' ) };
115 if ( parts.size() != 2 )
116 {
117 throw QgsServerApiBadRequestException( u"%1 is not a valid datetime interval."_s.arg( interval ), u"Server"_s );
118 }
119 // cppcheck-suppress containerOutOfBounds
120 T result { parseDate( parts[0] ), parseDate( parts[1] ) };
121 // Check validity
122 if ( result.isEmpty() )
123 {
124 throw QgsServerApiBadRequestException( u"%1 is not a valid datetime interval (empty)."_s.arg( interval ), u"Server"_s );
125 }
126 return result;
127}
129
131{
132 return QgsServerApiUtils::parseTemporalInterval<QgsDateRange, QDate>( interval );
133}
134
136{
137 return QgsServerApiUtils::parseTemporalInterval<QgsDateTimeRange, QDateTime>( interval );
138}
139
141{
142 QgsExpression expression;
143 QStringList conditions;
144
145 const auto dimensions { QgsServerApiUtils::temporalDimensions( layer ) };
146 if ( dimensions.isEmpty() )
147 {
148 return expression;
149 }
150
151 // helper to get the field type from the field name
152 auto fieldTypeFromName = [&]( const QString &fieldName, const QgsVectorLayer *layer ) -> QMetaType::Type {
153 int fieldIdx { layer->fields().lookupField( fieldName ) };
154 if ( fieldIdx < 0 )
155 {
156 return QMetaType::Type::UnknownType;
157 }
158 const QgsField field { layer->fields().at( fieldIdx ) };
159 return field.type();
160 };
161
162 // helper to cast the field value
163 auto refFieldCast = [&]( const QString &fieldName, QMetaType::Type queryType, QMetaType::Type fieldType ) -> QString {
164 const auto fieldRealType { fieldTypeFromName( fieldName, layer ) };
165 if ( fieldRealType == QMetaType::Type::UnknownType )
166 {
167 return QString();
168 }
169
170 // Downcast only datetime -> date
171 // always cast strings
172 if ( fieldRealType == QMetaType::Type::QString )
173 {
174 // Cast to query type but only downcast
175 if ( fieldType != queryType || fieldType == QMetaType::Type::QDate )
176 {
177 return u"to_date( %1 )"_s.arg( QgsExpression::quotedColumnRef( fieldName ) );
178 }
179 else
180 {
181 return u"%2( %1 )"_s.arg( QgsExpression::quotedColumnRef( fieldName ) ).arg( queryType == QMetaType::Type::QDate ? u"to_date"_s : u"to_datetime"_s );
182 }
183 }
184 else if ( fieldType == queryType || fieldType == QMetaType::Type::QDate )
185 {
187 }
188 else
189 {
190 return u"%2( %1 )"_s.arg( QgsExpression::quotedColumnRef( fieldName ) ).arg( queryType == QMetaType::Type::QDate ? u"to_date"_s : u"to_datetime"_s );
191 }
192 };
193
194 // Quote and cast a query value
195 auto quoteValue = []( const QString &value ) -> QString {
196 if ( value.length() == 10 )
197 {
198 return u"to_date( %1 )"_s.arg( QgsExpression::quotedValue( value ) );
199 }
200 else
201 {
202 return u"to_datetime( %1 )"_s.arg( QgsExpression::quotedValue( value ) );
203 }
204 };
205
206 // helper to build the interval filter, fieldType is the underlying field type, queryType is the input query type
207 auto makeFilter =
208 [&quoteValue]( const QString &fieldBegin, const QString &fieldEnd, const QString &fieldBeginCasted, const QString &fieldEndCasted, const QString &queryBegin, const QString &queryEnd ) -> QString {
209 QString result;
210
211 // It's a closed interval query, go for overlap
212 if ( !queryBegin.isEmpty() && !queryEnd.isEmpty() )
213 {
214 // Overlap of two intervals
215 if ( !fieldEndCasted.isEmpty() )
216 {
217 result = u"( %1 IS NULL OR %2 <= %6 ) AND ( %4 IS NULL OR %5 >= %3 )"_s.arg( fieldBegin, fieldBeginCasted, quoteValue( queryBegin ), fieldEnd, fieldEndCasted, quoteValue( queryEnd ) );
218 }
219 else // Overlap of single value
220 {
221 result = u"( %1 IS NULL OR ( %2 <= %3 AND %3 <= %4 ) )"_s.arg( fieldBegin, quoteValue( queryBegin ), fieldBeginCasted, quoteValue( queryEnd ) );
222 }
223 }
224 else if ( !queryBegin.isEmpty() ) // >=
225 {
226 if ( !fieldEndCasted.isEmpty() )
227 {
228 result = u"( %1 IS NULL OR %2 >= %3 )"_s.arg( fieldEnd, fieldEndCasted, quoteValue( queryBegin ) );
229 }
230 else
231 {
232 result = u"( %1 IS NULL OR %2 >= %3 )"_s.arg( fieldBegin, fieldBeginCasted, quoteValue( queryBegin ) );
233 }
234 }
235 else // <=
236 {
237 result = u"( %1 IS NULL OR %2 <= %3 )"_s.arg( fieldBegin, fieldBeginCasted, quoteValue( queryEnd ) );
238 }
239 return result;
240 };
241
242 // Determine if it is a date or a datetime interval (mixed are not supported)
243 QString testType { interval };
244 if ( interval.contains( '/' ) )
245 {
246 const QStringList parts { interval.split( '/' ) };
247 testType = parts[0];
248 if ( testType.isEmpty() || testType == ".."_L1 )
249 {
250 // cppcheck-suppress containerOutOfBounds
251 testType = parts[1];
252 }
253 }
254 // Determine query input type: datetime is always longer than 10 chars
255 const bool inputQueryIsDateTime { testType.length() > 10 };
256 const QMetaType::Type queryType { inputQueryIsDateTime ? QMetaType::Type::QDateTime : QMetaType::Type::QDate };
257
258 // Is it an interval?
259 if ( interval.contains( '/' ) )
260 {
261 if ( !inputQueryIsDateTime )
262 {
264
265 for ( const auto &dimension : std::as_const( dimensions ) )
266 {
267 // Determine the field type from the dimension name "time"/"date"
268 const QMetaType::Type fieldType { dimension.name.toLower() == "time"_L1 ? QMetaType::Type::QDateTime : QMetaType::Type::QDate };
269
270 const auto fieldBeginCasted { refFieldCast( dimension.fieldName, queryType, fieldType ) };
271 if ( fieldBeginCasted.isEmpty() )
272 {
273 continue;
274 }
275
276 const auto fieldBegin = QgsExpression::quotedColumnRef( dimension.fieldName );
277 const auto fieldEnd = QgsExpression::quotedColumnRef( dimension.endFieldName );
278
279 // This may be empty:
280 const auto fieldEndCasted { refFieldCast( dimension.endFieldName, queryType, fieldType ) };
281 if ( !dateInterval.begin().isValid() && !dateInterval.end().isValid() )
282 {
283 // Nothing to do here: log?
284 }
285 else
286 {
287 conditions.push_back(
288 makeFilter( fieldBegin, fieldEnd, fieldBeginCasted, fieldEndCasted, dateInterval.begin().toString( Qt::DateFormat::ISODate ), dateInterval.end().toString( Qt::DateFormat::ISODate ) )
289 );
290 }
291 }
292 }
293 else // try datetime
294 {
296 for ( const auto &dimension : std::as_const( dimensions ) )
297 {
298 // Determine the field type from the dimension name "time"/"date"
299 const QMetaType::Type fieldType { dimension.name.toLower() == "time"_L1 ? QMetaType::Type::QDateTime : QMetaType::Type::QDate };
300
301 const auto fieldfBeginCasted { refFieldCast( dimension.fieldName, queryType, fieldType ) };
302 if ( fieldfBeginCasted.isEmpty() )
303 {
304 continue;
305 }
306 const auto fieldBegin = QgsExpression::quotedColumnRef( dimension.fieldName );
307 const auto fieldEnd = QgsExpression::quotedColumnRef( dimension.endFieldName );
308
309 // This may be empty:
310 const auto fieldEndCasted { refFieldCast( dimension.endFieldName, queryType, fieldType ) };
311 if ( !dateTimeInterval.begin().isValid() && !dateTimeInterval.end().isValid() )
312 {
313 // Nothing to do here: log?
314 }
315 else
316 {
317 // Cast the query value according to the field type
318 QString beginQuery;
319 QString endQuery;
320 // Drop the time
321 if ( fieldType == QMetaType::Type::QDate )
322 {
323 beginQuery = dateTimeInterval.begin().date().toString( Qt::DateFormat::ISODate );
324 endQuery = dateTimeInterval.end().date().toString( Qt::DateFormat::ISODate );
325 }
326 else
327 {
328 beginQuery = dateTimeInterval.begin().toString( Qt::DateFormat::ISODate );
329 endQuery = dateTimeInterval.end().toString( Qt::DateFormat::ISODate );
330 }
331 conditions.push_back( makeFilter( fieldBegin, fieldEnd, fieldfBeginCasted, fieldEndCasted, beginQuery, endQuery ) );
332 }
333 }
334 }
335 }
336 else // single value
337 {
338 for ( const auto &dimension : std::as_const( dimensions ) )
339 {
340 // Determine the field type from the dimension name "time"/"date"
341 const bool fieldIsDateTime { dimension.name.toLower() == "time"_L1 };
342 const QMetaType::Type fieldType { fieldIsDateTime ? QMetaType::Type::QDateTime : QMetaType::Type::QDate };
343
344 const auto fieldRefBegin { refFieldCast( dimension.fieldName, queryType, fieldType ) };
345 if ( fieldRefBegin.isEmpty() )
346 {
347 continue;
348 }
349 const auto fieldBegin = QgsExpression::quotedColumnRef( dimension.fieldName );
350
351 // This may be empty:
352 const auto fieldRefEnd { refFieldCast( dimension.endFieldName, queryType, fieldType ) };
353 const auto fieldEnd = QgsExpression::quotedColumnRef( dimension.endFieldName );
354
355 QString condition;
356 QString castedValue;
357
358 // field has possibly been downcasted
359 if ( !inputQueryIsDateTime || !fieldIsDateTime )
360 {
361 QString castedInterval { interval };
362 // Check if we need to downcast interval from datetime
363 if ( inputQueryIsDateTime )
364 {
365 castedInterval = QDate::fromString( castedInterval, Qt::DateFormat::ISODate ).toString( Qt::DateFormat::ISODate );
366 }
367 castedValue = u"to_date( %1 )"_s.arg( QgsExpression::quotedValue( castedInterval ) );
368 }
369 else
370 {
371 QString castedInterval { interval };
372 // Check if we need to upcast interval to datetime
373 if ( !inputQueryIsDateTime )
374 {
375 castedInterval = QDateTime::fromString( castedInterval, Qt::DateFormat::ISODate ).toString( Qt::DateFormat::ISODate );
376 }
377 castedValue = u"to_datetime( %1 )"_s.arg( QgsExpression::quotedValue( castedInterval ) );
378 }
379
380 if ( !fieldRefEnd.isEmpty() )
381 {
382 condition = u"( %1 IS NULL OR %2 <= %3 ) AND ( %5 IS NULL OR %3 <= %4 )"_s.arg( fieldBegin, fieldRefBegin, castedValue, fieldRefEnd, fieldEnd );
383 }
384 else
385 {
386 condition = u"( %1 IS NULL OR %2 = %3 )"_s.arg( fieldBegin, fieldRefBegin, castedValue );
387 }
388 conditions.push_back( condition );
389 }
390 }
391 if ( !conditions.isEmpty() )
392 {
393 expression.setExpression( conditions.join( " AND "_L1 ) );
394 }
395 return expression;
396}
397
399{
400 auto extent { layer->extent() };
401 if ( layer->crs().authid() != "EPSG:4326"_L1 )
402 {
403 static const QgsCoordinateReferenceSystem targetCrs( u"EPSG:4326"_s );
404 const QgsCoordinateTransform ct( layer->crs(), targetCrs, layer->transformContext() );
405 extent = ct.transform( extent );
406 }
407 return { { extent.xMinimum(), extent.yMinimum(), extent.xMaximum(), extent.yMaximum() } };
408}
409
411{
412 // Helper to get min/max from a dimension
413 auto range = [&]( const QgsMapLayerServerProperties::WmsDimensionInfo &dimInfo ) -> QgsDateTimeRange {
414 QgsDateTimeRange result;
415 // min
416 int fieldIdx { layer->fields().lookupField( dimInfo.fieldName ) };
417 if ( fieldIdx < 0 )
418 {
419 return result;
420 }
421
422 QVariant minVal;
423 QVariant maxVal;
424 layer->minimumAndMaximumValue( fieldIdx, minVal, maxVal );
425
426 QDateTime min { minVal.toDateTime() };
427 QDateTime max { maxVal.toDateTime() };
428 if ( !dimInfo.endFieldName.isEmpty() )
429 {
430 fieldIdx = layer->fields().lookupField( dimInfo.endFieldName );
431 if ( fieldIdx >= 0 )
432 {
433 QVariant minVal;
434 QVariant maxVal;
435 layer->minimumAndMaximumValue( fieldIdx, minVal, maxVal );
436
437 QDateTime minEnd { minVal.toDateTime() };
438 QDateTime maxEnd { maxVal.toDateTime() };
439 if ( minEnd.isValid() )
440 {
441 min = std::min<QDateTime>( min, minEnd );
442 }
443 if ( maxEnd.isValid() )
444 {
445 max = std::max<QDateTime>( max, maxEnd );
446 }
447 }
448 }
449 return { min, max };
450 };
451
452 const QList<QgsMapLayerServerProperties::WmsDimensionInfo> dimensions { QgsServerApiUtils::temporalDimensions( layer ) };
453 if ( dimensions.isEmpty() )
454 {
455 return nullptr;
456 }
457 else
458 {
459 try
460 {
461 QgsDateTimeRange extent;
462 bool isFirst = true;
463 for ( const auto &dimension : dimensions )
464 {
465 // Get min/max for dimension
466 if ( isFirst )
467 {
468 extent = range( dimension );
469 isFirst = false;
470 }
471 else
472 {
473 extent.extend( range( dimension ) );
474 }
475 }
476 json ret = json::array();
477 const QString beginVal { extent.begin().toString( Qt::DateFormat::ISODate ) };
478 const QString endVal { extent.end().toString( Qt::DateFormat::ISODate ) };
479 // We cannot mix nullptr and std::string :(
480 if ( beginVal.isEmpty() && endVal.isEmpty() )
481 {
482 ret.push_back( { nullptr, nullptr } );
483 }
484 else if ( beginVal.isEmpty() )
485 {
486 ret.push_back( { nullptr, endVal.toStdString() } );
487 }
488 else if ( endVal.isEmpty() )
489 {
490 ret.push_back( { beginVal.toStdString(), nullptr } );
491 }
492 else
493 {
494 ret.push_back( { beginVal.toStdString(), endVal.toStdString() } );
495 }
496 return ret;
497 }
498 catch ( std::exception &ex )
499 {
500 const QString errorMessage { u"Error creating temporal extent: %1"_s.arg( ex.what() ) };
501 QgsMessageLog::logMessage( errorMessage, u"Server"_s, Qgis::MessageLevel::Critical );
502 throw QgsServerApiInternalServerError( errorMessage );
503 }
504 }
505}
506
508{
509 QVariantList list;
510 list.push_back( QgsJsonUtils::parseArray( QString::fromStdString( temporalExtent( layer )[0].dump() ) ) );
511 return list;
512}
513
515{
516 // We get this:
517 // http://www.opengis.net/def/crs/OGC/1.3/CRS84
518 // We want this:
519 // "urn:ogc:def:crs:<auth>:[<version>]:<code>"
520 const auto parts { QUrl( bboxCrs ).path().split( '/' ) };
521 if ( parts.count() == 6 )
522 {
523 return QgsCoordinateReferenceSystem::fromOgcWmsCrs( u"urn:ogc:def:crs:%1:%2:%3"_s.arg( parts[3], parts[4], parts[5] ) );
524 }
525 else
526 {
528 }
529}
530
531const QVector<QgsVectorLayer *> QgsServerApiUtils::publishedWfsLayers( const QgsServerApiContext &context )
532{
533 return publishedWfsLayers<QgsVectorLayer *>( context );
534}
535
536QString QgsServerApiUtils::fieldName( const QString &name, const QgsVectorLayer *layer )
537{
538 if ( layer->fields().names().contains( name ) )
539 {
540 return name;
541 }
542 const QgsFields fields { layer->fields() };
543 for ( const QgsField &field : std::as_const( fields ) )
544 {
545 if ( field.displayName() == name )
546 {
547 return field.name();
548 }
549 }
550 throw QgsServerApiBadRequestException { u"Field '%1' is not a valid field name for layer: %2"_s.arg( name, layer->name() ) };
551}
552
553QString QgsServerApiUtils::sanitizedFieldValue( const QString &value )
554{
555 QString result { QUrl( value ).toString() };
556 return result.replace( '\'', "\'"_L1 );
557}
558
560{
561 // This must be always available in OGC APIs
562 QStringList result { { u"http://www.opengis.net/def/crs/OGC/1.3/CRS84"_s } };
563 if ( project )
564 {
565 const QStringList outputCrsList = QgsServerProjectUtils::wmsOutputCrsList( *project );
566 for ( const QString &crsId : outputCrsList )
567 {
568 const auto crsUri { QgsCoordinateReferenceSystem::fromOgcWmsCrs( crsId ).toOgcUri() };
569 if ( !crsUri.isEmpty() )
570 {
571 result.push_back( crsUri );
572 }
573 }
574 }
575 return result;
576}
577
579{
580 return crs.toOgcUri();
581}
582
583QString QgsServerApiUtils::appendMapParameter( const QString &path, const QUrl &requestUrl )
584{
585 QList<QPair<QString, QString>> qi;
586 QString result { path };
587 const auto constItems { QUrlQuery( requestUrl ).queryItems() };
588 for ( const auto &i : constItems )
589 {
590 if ( i.first.compare( u"MAP"_s, Qt::CaseSensitivity::CaseInsensitive ) == 0 )
591 {
592 qi.push_back( i );
593 }
594 }
595 if ( !qi.empty() )
596 {
597 if ( !path.endsWith( '?' ) )
598 {
599 result += '?';
600 }
601 result.append( u"MAP=%1"_s.arg( qi.first().second ) );
602 }
603 return result;
604}
@ Critical
Critical/error message.
Definition qgis.h:163
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...
Handles coordinate transforms between two 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.
Handles 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:56
QMetaType::Type type
Definition qgsfield.h:63
Container of fields for a vector layer.
Definition qgsfields.h:46
QgsField at(int i) const
Returns the field at particular index (must be in range 0..N-1).
Q_INVOKABLE int lookupField(const QString &fieldName) const
Looks up field's index from the field name.
QStringList names
Definition qgsfields.h:51
static Q_INVOKABLE QVariantList parseArray(const QString &json, QMetaType::Type type=QMetaType::Type::UnknownType)
Parse a simple array (depth=1).
Manages QGIS Server properties for a map layer.
QString name
Definition qgsmaplayer.h:87
QgsCoordinateReferenceSystem crs
Definition qgsmaplayer.h:90
QgsMapLayerServerProperties * serverProperties()
Returns QGIS Server Properties for the map layer.
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, const char *file=__builtin_FILE(), const char *function=__builtin_FUNCTION(), int line=__builtin_LINE(), Qgis::StringFormat format=Qgis::StringFormat::PlainText)
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:113
A rectangle specified with double values.
Bad request error API exception.
Encapsulates the resources for a particular client 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.
static QStringList wmsOutputCrsList(const QgsProject &project)
Returns the WMS output CRS list.
const QList< QgsServerWmsDimensionProperties::WmsDimensionInfo > wmsDimensions() const
Returns the QGIS Server WMS Dimension list.
T begin() const
Returns the beginning of the range.
Definition qgsrange.h:408
bool extend(const QgsTemporalRange< T > &other)
Extends the range in place by extending this range out to include an other range.
Definition qgsrange.h:575
T end() const
Returns the upper bound of the range.
Definition qgsrange.h:415
Represents a vector layer which manages a vector based dataset.
QgsRectangle extent() const final
Returns the extent of the layer.
void minimumAndMaximumValue(int index, QVariant &minimum, QVariant &maximum) const
Calculates both the minimum and maximum value for an attribute column.
#define SIP_PYNAME(name)
Definition qgis_sip.h:88
QgsTemporalRange< QDate > QgsDateRange
QgsRange which stores a range of dates.
Definition qgsrange.h:691
QgsTemporalRange< QDateTime > QgsDateTimeRange
QgsRange which stores a range of date times.
Definition qgsrange.h:705
Setting to define QGIS Server WMS Dimension.