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