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...