28 #include "nlohmann/json.hpp"    34   const auto parts { bbox.split( 
',', QString::SplitBehavior::SkipEmptyParts ) };
    37   if ( parts.count() == 4 ||  parts.count() == 6 )
    39     const auto hasZ { parts.count() == 6 };
    40     auto toDouble = [ & ]( 
const int i ) -> 
double    44       return parts[i].toDouble( &ok );
    50                            toDouble( 3 ), toDouble( 4 ) );
    55                            toDouble( 2 ), toDouble( 3 ) );
    69   dimensions.erase( std::remove_if( dimensions.begin(),
    73     return dim.name.toLower() != QStringLiteral( 
"time" )
    74            && dim.name.toLower() != QStringLiteral( 
"date" ) ;
    75   } ), dimensions.end() );
    78   if ( dimensions.isEmpty() )
    80     const auto constFields { layer->
fields() };
    81     for ( 
const auto &f : constFields )
    83       if ( f.isDateOrTime() )
    86                            QStringLiteral( 
"time" ) :
    87                            QStringLiteral( 
"date" ), f.name() ) );
    96 template<
typename T, 
class T2> T QgsServerApiUtils::parseTemporalInterval( 
const QString &interval )
    98   auto parseDate = [ ]( 
const QString & date ) -> T2
   101     if ( date == QStringLiteral( 
".." ) || date.isEmpty() )
   107       T2 result { T2::fromString( date, Qt::DateFormat::ISODate ) };
   108       if ( !result.isValid() )
   115   const QStringList parts { interval.split( 
'/' ) };
   116   if ( parts.length() != 2 )
   120   T result { parseDate( parts[0] ), parseDate( parts[1] ) };
   122   if ( result.isEmpty() )
   132   return QgsServerApiUtils::parseTemporalInterval<QgsDateRange, QDate>( interval );
   137   return QgsServerApiUtils::parseTemporalInterval<QgsDateTimeRange, QDateTime>( interval );
   143   QStringList conditions;
   146   if ( dimensions.isEmpty() )
   152   auto fieldTypeFromName = [ & ]( 
const QString & fieldName, 
const QgsVectorLayer * layer ) -> QVariant::Type
   157       return QVariant::Type::Invalid;
   164   auto refFieldCast = [ & ]( 
const QString & fieldName, QVariant::Type queryType, QVariant::Type fieldType ) -> QString
   167     const auto fieldRealType { fieldTypeFromName( fieldName, layer ) };
   168     if ( fieldRealType == QVariant::Type::Invalid )
   175     if ( fieldRealType == QVariant::Type::String )
   178       if ( fieldType != queryType || fieldType == QVariant::Type::Date )
   185                .arg( queryType == QVariant::Type::Date ? QStringLiteral( 
"to_date" ) : QStringLiteral( 
"to_datetime" ) );
   188     else if ( fieldType == queryType || fieldType == QVariant::Type::Date )
   195              .arg( queryType == QVariant::Type::Date ? QStringLiteral( 
"to_date" ) : QStringLiteral( 
"to_datetime" ) );
   200   auto quoteValue = [ ]( 
const QString & value ) -> QString
   202     if ( value.length() == 10 )
   213   auto makeFilter = [ "eValue ]( 
const QString & fieldBegin, 
const QString & fieldEnd,
   214                                      const QString & fieldBeginCasted, 
const QString & fieldEndCasted,
   215                                      const QString & queryBegin, 
const QString & queryEnd ) -> QString
   220     if ( ! queryBegin.isEmpty() && ! queryEnd.isEmpty() )
   223       if ( ! fieldEndCasted.isEmpty() )
   225         result = QStringLiteral( 
"( %1 IS NULL OR %2 <= %6 ) AND ( %4 IS NULL OR %5 >= %3 )" )
   228               quoteValue( queryBegin ),
   231               quoteValue( queryEnd ) );
   235         result = QStringLiteral( 
"( %1 IS NULL OR ( %2 <= %3 AND %3 <= %4 ) )" )
   237                        quoteValue( queryBegin ),
   239                        quoteValue( queryEnd ) );
   243     else if ( ! queryBegin.isEmpty() ) 
   245       if ( ! fieldEndCasted.isEmpty() )
   247         result = QStringLiteral( 
"( %1 IS NULL OR %2 >= %3 )" ).arg( fieldEnd, fieldEndCasted, quoteValue( queryBegin ) );
   251         result = QStringLiteral( 
"( %1 IS NULL OR %2 >= %3 )" ).arg( fieldBegin, fieldBeginCasted, quoteValue( queryBegin ) );
   256       result = QStringLiteral( 
"( %1 IS NULL OR %2 <= %3 )" ).arg( fieldBegin, fieldBeginCasted, quoteValue( queryEnd ) );
   262   QString testType { interval };
   263   if ( interval.contains( 
'/' ) )
   265     const QStringList parts { interval.split( 
'/' ) };
   267     if ( testType.isEmpty() || testType == QStringLiteral( 
".." ) )
   273   const bool inputQueryIsDateTime { testType.length() > 10 };
   274   const QVariant::Type queryType { inputQueryIsDateTime ? QVariant::Type::DateTime : QVariant::Type::Date };
   277   if ( interval.contains( 
'/' ) )
   279     if ( ! inputQueryIsDateTime )
   283       for ( 
const auto &dimension : qgis::as_const( dimensions ) )
   287         const QVariant::Type fieldType { dimension.name.toLower() == QStringLiteral( 
"time" ) ? QVariant::Type::DateTime :  QVariant::Type::Date };
   289         const auto fieldBeginCasted { refFieldCast( dimension.fieldName, queryType, fieldType ) };
   290         if ( fieldBeginCasted.isEmpty() )
   299         const auto fieldEndCasted { refFieldCast( dimension.endFieldName, queryType, fieldType ) };
   300         if ( ! dateInterval.begin().isValid( ) && ! dateInterval.end().isValid( ) )
   306           conditions.push_back( makeFilter( fieldBegin,
   310                                             dateInterval.begin().toString( Qt::DateFormat::ISODate ),
   311                                             dateInterval.end().toString( Qt::DateFormat::ISODate ) ) );
   319       for ( 
const auto &dimension : qgis::as_const( dimensions ) )
   323         const QVariant::Type fieldType { dimension.name.toLower() == QStringLiteral( 
"time" ) ? QVariant::Type::DateTime :  QVariant::Type::Date };
   325         const auto fieldfBeginCasted { refFieldCast( dimension.fieldName, queryType, fieldType ) };
   326         if ( fieldfBeginCasted.isEmpty() )
   334         const auto fieldEndCasted { refFieldCast( dimension.endFieldName, queryType, fieldType ) };
   335         if ( ! dateTimeInterval.begin().isValid( ) && ! dateTimeInterval.end().isValid( ) )
   345           if ( fieldType == QVariant::Type::Date )
   347             beginQuery = dateTimeInterval.begin().date().toString( Qt::DateFormat::ISODate );
   348             endQuery = dateTimeInterval.end().date().toString( Qt::DateFormat::ISODate );
   352             beginQuery = dateTimeInterval.begin().toString( Qt::DateFormat::ISODate );
   353             endQuery = dateTimeInterval.end().toString( Qt::DateFormat::ISODate );
   355           conditions.push_back( makeFilter( fieldBegin,
   368     for ( 
const auto &dimension : qgis::as_const( dimensions ) )
   371       const bool fieldIsDateTime { dimension.name.toLower() == QStringLiteral( 
"time" ) };
   372       const QVariant::Type fieldType { fieldIsDateTime ? QVariant::Type::DateTime :  QVariant::Type::Date };
   374       const auto fieldRefBegin { refFieldCast( dimension.fieldName, queryType, fieldType ) };
   375       if ( fieldRefBegin.isEmpty() )
   382       const auto fieldRefEnd { refFieldCast( dimension.endFieldName, queryType, fieldType ) };
   389       if ( ! inputQueryIsDateTime || ! fieldIsDateTime )
   391         QString castedInterval { interval };
   393         if ( inputQueryIsDateTime )
   395           castedInterval = QDate::fromString( castedInterval, Qt::DateFormat::ISODate ).toString( Qt::DateFormat::ISODate );
   401         QString castedInterval { interval };
   403         if ( ! inputQueryIsDateTime )
   405           castedInterval = QDateTime::fromString( castedInterval, Qt::DateFormat::ISODate ).toString( Qt::DateFormat::ISODate );
   410       if ( ! fieldRefEnd.isEmpty() )
   412         condition = QStringLiteral( 
"( %1 IS NULL OR %2 <= %3 ) AND ( %5 IS NULL OR %3 <= %4 )" ).arg(
   421         condition = QStringLiteral( 
"( %1 IS NULL OR %2 = %3 )" )
   427       conditions.push_back( condition );
   431   if ( ! conditions.isEmpty() )
   433     expression.
setExpression( conditions.join( QStringLiteral( 
" AND " ) ) );
   440   auto extent { layer->
extent() };
   441   if ( layer->
crs().
authid() != QLatin1String( 
"EPSG:4326" ) )
   447   return {{ extent.xMinimum(), extent.yMinimum(), extent.xMaximum(), extent.yMaximum() }};
   455     QgsDateTimeRange result;
   462     QDateTime min { layer->
minimumValue( fieldIdx ).toDateTime() };
   463     QDateTime max { layer->
maximumValue( fieldIdx ).toDateTime() };
   464     if ( ! dimInfo.endFieldName.isEmpty() )
   469         QDateTime minEnd { layer->
minimumValue( fieldIdx ).toDateTime() };
   470         QDateTime maxEnd { layer->
maximumValue( fieldIdx ).toDateTime() };
   471         if ( minEnd.isValid() )
   473           min = std::min<QDateTime>( min, layer->
minimumValue( fieldIdx ).toDateTime() );
   475         if ( maxEnd.isValid() )
   477           max = std::max<QDateTime>( max, layer->
maximumValue( fieldIdx ).toDateTime() );
   485   if ( dimensions.isEmpty() )
   493       QgsDateTimeRange extent;
   495       for ( 
const auto &dimension : dimensions )
   500           extent = range( dimension );
   505           extent.extend( range( dimension ) );
   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 ) };
   512       if ( beginVal.isEmpty() && endVal.isEmpty() )
   514         ret.push_back( { 
nullptr, 
nullptr } );
   516       else if ( beginVal.isEmpty() )
   518         ret.push_back( { 
nullptr, endVal.toStdString() } );
   520       else if ( endVal.isEmpty() )
   522         ret.push_back( { beginVal.toStdString(), 
nullptr } );
   526         ret.push_back( { beginVal.toStdString(), endVal.toStdString() } );
   530     catch ( std::exception &ex )
   532       const QString errorMessage { QStringLiteral( 
"Error creating temporal extent: %1" ).arg( ex.what() ) };
   553   const auto parts { QUrl( bboxCrs ).path().split( 
'/' ) };
   554   if ( parts.count() == 6 )
   556     return crs.
fromOgcWmsCrs( QStringLiteral( 
"urn:ogc:def:crs:%1:%2:%3" ).arg( parts[3], parts[4], parts[5] ) );
   566   return publishedWfsLayers< QgsVectorLayer * >( context );
   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() )
   577   return result.replace( 
'\'', QStringLiteral( 
"\'" ) );
   583   QStringList result { { QStringLiteral( 
"http://www.opengis.net/def/crs/OGC/1.3/CRS84" )}};
   587     for ( 
const QString &crsId : outputCrsList )
   590       if ( ! crsUri.isEmpty() )
   592         result.push_back( crsUri );
   601   const auto parts { crs.
authid().split( 
':' ) };
   602   if ( parts.length() == 2 )
   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" ) )
   608       return  QStringLiteral( 
"http://www.opengis.net/def/crs/OGC/1.3/%1" ).arg( parts[1] ) ;
   624   QList<QPair<QString, QString> > qi;
   625   QString result { path };
   626   const auto constItems { QUrlQuery( requestUrl ).queryItems() };
   627   for ( 
const auto &i : constItems )
   629     if ( i.first.compare( QStringLiteral( 
"MAP" ), Qt::CaseSensitivity::CaseInsensitive ) == 0 )
   636     if ( ! path.endsWith( 
'?' ) )
   640     result.append( QStringLiteral( 
"MAP=%1" ).arg( qi.first().second ) );
 int lookupField(const QString &fieldName) const
Looks up field's index from the field name. 
 
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. 
 
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. 
 
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) 
 
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) ...
 
Encapsulates a QGIS project, including sets of map layers and their styles, layouts, annotations, canvases, etc. 
 
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. 
 
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) 
 
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. 
 
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
 
static json temporalExtent(const QgsVectorLayer *layer)
temporalExtent returns a json array with an array of [min, max] temporal extent for the given layer...