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.