QGIS API Documentation  3.18.1-Zürich (202f1bf7e5)
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
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 == QLatin1String( ".." ) || 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 == QLatin1String( ".." ) )
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() == QLatin1String( "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() == QLatin1String( "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() == QLatin1String( "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( QLatin1String( " 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 
539 QVariantList QgsServerApiUtils::temporalExtentList( const QgsVectorLayer *layer ) SIP_PYNAME( temporalExtent )
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  return result.replace( '\'', QLatin1String( "\'" ) );
573 }
574 
575 QStringList QgsServerApiUtils::publishedCrsList( const QgsProject *project )
576 {
577  // This must be always available in OGC APIs
578  QStringList result { { QStringLiteral( "http://www.opengis.net/def/crs/OGC/1.3/CRS84" )}};
579  if ( project )
580  {
581  const QStringList outputCrsList = QgsServerProjectUtils::wmsOutputCrsList( *project );
582  for ( const QString &crsId : outputCrsList )
583  {
584  const auto crsUri { crsToOgcUri( QgsCoordinateReferenceSystem::fromOgcWmsCrs( crsId ) ) };
585  if ( ! crsUri.isEmpty() )
586  {
587  result.push_back( crsUri );
588  }
589  }
590  }
591  return result;
592 }
593 
595 {
596  const auto parts { crs.authid().split( ':' ) };
597  if ( parts.length() == 2 )
598  {
599  if ( parts[0] == QLatin1String( "EPSG" ) )
600  return QStringLiteral( "http://www.opengis.net/def/crs/EPSG/9.6.2/%1" ).arg( parts[1] ) ;
601  else if ( parts[0] == QLatin1String( "OGC" ) )
602  {
603  return QStringLiteral( "http://www.opengis.net/def/crs/OGC/1.3/%1" ).arg( parts[1] ) ;
604  }
605  else
606  {
607  QgsMessageLog::logMessage( QStringLiteral( "Error converting published CRS to URI %1: (not OGC or EPSG)" ).arg( crs.authid() ), QStringLiteral( "Server" ), Qgis::Critical );
608  }
609  }
610  else
611  {
612  QgsMessageLog::logMessage( QStringLiteral( "Error converting published CRS to URI: %1" ).arg( crs.authid() ), QStringLiteral( "Server" ), Qgis::Critical );
613  }
614  return QString();
615 }
616 
617 QString QgsServerApiUtils::appendMapParameter( const QString &path, const QUrl &requestUrl )
618 {
619  QList<QPair<QString, QString> > qi;
620  QString result { path };
621  const auto constItems { QUrlQuery( requestUrl ).queryItems() };
622  for ( const auto &i : constItems )
623  {
624  if ( i.first.compare( QStringLiteral( "MAP" ), Qt::CaseSensitivity::CaseInsensitive ) == 0 )
625  {
626  qi.push_back( i );
627  }
628  }
629  if ( ! qi.empty() )
630  {
631  if ( ! path.endsWith( '?' ) )
632  {
633  result += '?';
634  }
635  result.append( QStringLiteral( "MAP=%1" ).arg( qi.first().second ) );
636  }
637  return result;
638 }
639 
@ Critical
Definition: qgis.h:92
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 authid() const
Returns the authority identifier for the CRS.
Class for doing transforms between two map coordinate systems.
QgsPointXY transform(const QgsPointXY &point, TransformDirection direction=ForwardTransform) 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
QVariant::Type type
Definition: qgsfield.h:58
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
Gets 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:344
static QVariantList parseArray(const QString &json, QVariant::Type type=QVariant::Invalid)
Parse a simple array (depth=1)
QgsCoordinateReferenceSystem crs
Definition: qgsmaplayer.h:91
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::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:99
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 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 QList< QgsVectorLayerServerProperties::WmsDimensionInfo > temporalDimensions(const QgsVectorLayer *layer)
Returns a list of temporal dimensions information for the given layer (either configured in wmsDimens...
static json layerExtent(const QgsVectorLayer *layer)
layerExtent returns json array with [xMin,yMin,xMax,yMax] CRS84 extent for the given layer
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< QgsVectorLayerServerProperties::WmsDimensionInfo > wmsDimensions() const
Returns the QGIS Server WMS Dimension list.
Represents a vector layer which manages a vector based data sets.
QVariant maximumValue(int index) const FINAL
Returns the maximum value for an attribute column or an invalid variant in case of error.
QgsVectorLayerServerProperties * serverProperties() const
Returns QGIS Server Properties of the vector layer.
QgsFields fields() const FINAL
Returns the list of fields of this layer.
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.
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:472
const QgsCoordinateReferenceSystem & crs
Setting to define QGIS Server WMS Dimension.