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