QGIS API Documentation  3.24.2-Tisler (13c1a02865)
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< 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 
103 template<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 
137 QgsDateRange QgsServerApiUtils::parseTemporalDateInterval( const QString &interval )
138 {
139  return QgsServerApiUtils::parseTemporalInterval<QgsDateRange, QDate>( interval );
140 }
141 
142 QgsDateTimeRange 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  {
197  return QgsExpression::quotedColumnRef( fieldName );
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 
555 QVariantList 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 
579 const QVector<QgsVectorLayer *> QgsServerApiUtils::publishedWfsLayers( const QgsServerApiContext &context )
580 {
581  return publishedWfsLayers< QgsVectorLayer * >( context );
582 }
583 
584 QString QgsServerApiUtils::sanitizedFieldValue( const QString &value )
585 {
586  QString result { QUrl( value ).toString() };
587  return result.replace( '\'', QLatin1String( "\'" ) );
588 }
589 
590 QStringList QgsServerApiUtils::publishedCrsList( const QgsProject *project )
591 {
592  // This must be always available in OGC APIs
593  QStringList result { { QStringLiteral( "http://www.opengis.net/def/crs/OGC/1.3/CRS84" )}};
594  if ( project )
595  {
596  const QStringList outputCrsList = QgsServerProjectUtils::wmsOutputCrsList( *project );
597  for ( const QString &crsId : outputCrsList )
598  {
599  const auto crsUri { crsToOgcUri( QgsCoordinateReferenceSystem::fromOgcWmsCrs( crsId ) ) };
600  if ( ! crsUri.isEmpty() )
601  {
602  result.push_back( crsUri );
603  }
604  }
605  }
606  return result;
607 }
608 
610 {
611  const auto parts { crs.authid().split( ':' ) };
612  if ( parts.length() == 2 )
613  {
614  if ( parts[0] == QLatin1String( "EPSG" ) )
615  return QStringLiteral( "http://www.opengis.net/def/crs/EPSG/9.6.2/%1" ).arg( parts[1] ) ;
616  else if ( parts[0] == QLatin1String( "OGC" ) )
617  {
618  return QStringLiteral( "http://www.opengis.net/def/crs/OGC/1.3/%1" ).arg( parts[1] ) ;
619  }
620  else
621  {
622  QgsMessageLog::logMessage( QStringLiteral( "Error converting published CRS to URI %1: (not OGC or EPSG)" ).arg( crs.authid() ), QStringLiteral( "Server" ), Qgis::MessageLevel::Critical );
623  }
624  }
625  else
626  {
627  QgsMessageLog::logMessage( QStringLiteral( "Error converting published CRS to URI: %1" ).arg( crs.authid() ), QStringLiteral( "Server" ), Qgis::MessageLevel::Critical );
628  }
629  return QString();
630 }
631 
632 QString QgsServerApiUtils::appendMapParameter( const QString &path, const QUrl &requestUrl )
633 {
634  QList<QPair<QString, QString> > qi;
635  QString result { path };
636  const auto constItems { QUrlQuery( requestUrl ).queryItems() };
637  for ( const auto &i : constItems )
638  {
639  if ( i.first.compare( QStringLiteral( "MAP" ), Qt::CaseSensitivity::CaseInsensitive ) == 0 )
640  {
641  qi.push_back( i );
642  }
643  }
644  if ( ! qi.empty() )
645  {
646  if ( ! path.endsWith( '?' ) )
647  {
648  result += '?';
649  }
650  result.append( QStringLiteral( "MAP=%1" ).arg( qi.first().second ) );
651  }
652  return result;
653 }
654 
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
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:349
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.
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:101
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 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.