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