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 == QLatin1String( 
".." ) || 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 == QLatin1String( 
".." ) )
 
  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() == QLatin1String( 
"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() == QLatin1String( 
"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() == QLatin1String( 
"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( QLatin1String( 
" 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   return result.replace( 
'\'', QLatin1String( 
"\'" ) );
 
  578   QStringList result { { QStringLiteral( 
"http://www.opengis.net/def/crs/OGC/1.3/CRS84" )}};
 
  582     for ( 
const QString &crsId : outputCrsList )
 
  585       if ( ! crsUri.isEmpty() )
 
  587         result.push_back( crsUri );
 
  596   const auto parts { 
crs.
authid().split( 
':' ) };
 
  597   if ( parts.length() == 2 )
 
  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" ) )
 
  603       return  QStringLiteral( 
"http://www.opengis.net/def/crs/OGC/1.3/%1" ).arg( parts[1] ) ;
 
  619   QList<QPair<QString, QString> > qi;
 
  620   QString result { path };
 
  621   const auto constItems { QUrlQuery( requestUrl ).queryItems() };
 
  622   for ( 
const auto &i : constItems )
 
  624     if ( i.first.compare( QStringLiteral( 
"MAP" ), Qt::CaseSensitivity::CaseInsensitive ) == 0 )
 
  631     if ( ! path.endsWith( 
'?' ) )
 
  635     result.append( QStringLiteral( 
"MAP=%1" ).arg( qi.first().second ) );
 
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 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.
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)
QgsField at(int i) const
Gets field at particular index (must be in range 0..N-1)
int lookupField(const QString &fieldName) const
Looks up field's index from the field name.
static QVariantList parseArray(const QString &json, QVariant::Type type=QVariant::Invalid)
Parse a simple array (depth=1)
QgsCoordinateReferenceSystem crs
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,...
A rectangle specified with double values.
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.
const QgsCoordinateReferenceSystem & crs
Setting to define QGIS Server WMS Dimension.