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