28#include "nlohmann/json.hpp"
35#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
36 const QStringList parts { bbox.split(
',', QString::SplitBehavior::SkipEmptyParts ) };
38 const QStringList parts { bbox.split(
',', Qt::SplitBehaviorFlags::SkipEmptyParts ) };
42 if ( parts.count() == 4 || parts.count() == 6 )
44 const auto hasZ { parts.count() == 6 };
45 auto toDouble = [ & ](
const int i ) ->
double
49 return parts[i].toDouble( &ok );
55 toDouble( 3 ), toDouble( 4 ) );
60 toDouble( 2 ), toDouble( 3 ) );
74 QList< QgsMapLayerServerProperties::WmsDimensionInfo > dimensions { serverProperties->
wmsDimensions() };
76 dimensions.erase( std::remove_if( dimensions.begin(),
80 return dim.name.toLower() != QStringLiteral(
"time" )
81 && dim.name.toLower() != QStringLiteral(
"date" ) ;
82 } ), dimensions.end() );
85 if ( dimensions.isEmpty() )
87 const auto constFields { layer->
fields() };
88 for (
const auto &f : constFields )
90 if ( f.isDateOrTime() )
93 QStringLiteral(
"time" ) :
94 QStringLiteral(
"date" ), f.name() ) );
103template<
typename T,
class T2> T QgsServerApiUtils::parseTemporalInterval(
const QString &interval )
105 auto parseDate = [ ](
const QString & date ) -> T2
108 if ( date == QLatin1String(
".." ) || date.isEmpty() )
114 T2 result { T2::fromString( date, Qt::DateFormat::ISODate ) };
115 if ( !result.isValid() )
122 const QStringList parts { interval.split(
'/' ) };
123 if ( parts.size() != 2 )
128 T result { parseDate( parts[0] ), parseDate( parts[1] ) };
130 if ( result.isEmpty() )
140 return QgsServerApiUtils::parseTemporalInterval<QgsDateRange, QDate>( interval );
145 return QgsServerApiUtils::parseTemporalInterval<QgsDateTimeRange, QDateTime>( interval );
151 QStringList conditions;
154 if ( dimensions.isEmpty() )
165 return QVariant::Type::Invalid;
172 auto refFieldCast = [ & ](
const QString &
fieldName, QVariant::Type queryType, QVariant::Type fieldType ) -> QString
175 const auto fieldRealType { fieldTypeFromName(
fieldName, layer ) };
176 if ( fieldRealType == QVariant::Type::Invalid )
183 if ( fieldRealType == QVariant::Type::String )
186 if ( fieldType != queryType || fieldType == QVariant::Type::Date )
193 .arg( queryType == QVariant::Type::Date ? QStringLiteral(
"to_date" ) : QStringLiteral(
"to_datetime" ) );
196 else if ( fieldType == queryType || fieldType == QVariant::Type::Date )
203 .arg( queryType == QVariant::Type::Date ? QStringLiteral(
"to_date" ) : QStringLiteral(
"to_datetime" ) );
208 auto quoteValue = [ ](
const QString & value ) -> QString
210 if ( value.length() == 10 )
221 auto makeFilter = [ "eValue ](
const QString & fieldBegin,
const QString & fieldEnd,
222 const QString & fieldBeginCasted,
const QString & fieldEndCasted,
223 const QString & queryBegin,
const QString & queryEnd ) -> QString
228 if ( ! queryBegin.isEmpty() && ! queryEnd.isEmpty() )
231 if ( ! fieldEndCasted.isEmpty() )
233 result = QStringLiteral(
"( %1 IS NULL OR %2 <= %6 ) AND ( %4 IS NULL OR %5 >= %3 )" )
236 quoteValue( queryBegin ),
239 quoteValue( queryEnd ) );
243 result = QStringLiteral(
"( %1 IS NULL OR ( %2 <= %3 AND %3 <= %4 ) )" )
245 quoteValue( queryBegin ),
247 quoteValue( queryEnd ) );
251 else if ( ! queryBegin.isEmpty() )
253 if ( ! fieldEndCasted.isEmpty() )
255 result = QStringLiteral(
"( %1 IS NULL OR %2 >= %3 )" ).arg( fieldEnd, fieldEndCasted, quoteValue( queryBegin ) );
259 result = QStringLiteral(
"( %1 IS NULL OR %2 >= %3 )" ).arg( fieldBegin, fieldBeginCasted, quoteValue( queryBegin ) );
264 result = QStringLiteral(
"( %1 IS NULL OR %2 <= %3 )" ).arg( fieldBegin, fieldBeginCasted, quoteValue( queryEnd ) );
270 QString testType { interval };
271 if ( interval.contains(
'/' ) )
273 const QStringList parts { interval.split(
'/' ) };
275 if ( testType.isEmpty() || testType == QLatin1String(
".." ) )
282 const bool inputQueryIsDateTime { testType.length() > 10 };
283 const QVariant::Type queryType { inputQueryIsDateTime ? QVariant::Type::DateTime : QVariant::Type::Date };
286 if ( interval.contains(
'/' ) )
288 if ( ! inputQueryIsDateTime )
292 for (
const auto &dimension : std::as_const( dimensions ) )
296 const QVariant::Type fieldType { dimension.name.toLower() == QLatin1String(
"time" ) ? QVariant::Type::DateTime : QVariant::Type::Date };
298 const auto fieldBeginCasted { refFieldCast( dimension.fieldName, queryType, fieldType ) };
299 if ( fieldBeginCasted.isEmpty() )
308 const auto fieldEndCasted { refFieldCast( dimension.endFieldName, queryType, fieldType ) };
309 if ( ! dateInterval.begin().isValid( ) && ! dateInterval.end().isValid( ) )
315 conditions.push_back( makeFilter( fieldBegin,
319 dateInterval.begin().toString( Qt::DateFormat::ISODate ),
320 dateInterval.end().toString( Qt::DateFormat::ISODate ) ) );
328 for (
const auto &dimension : std::as_const( dimensions ) )
332 const QVariant::Type fieldType { dimension.name.toLower() == QLatin1String(
"time" ) ? QVariant::Type::DateTime : QVariant::Type::Date };
334 const auto fieldfBeginCasted { refFieldCast( dimension.fieldName, queryType, fieldType ) };
335 if ( fieldfBeginCasted.isEmpty() )
343 const auto fieldEndCasted { refFieldCast( dimension.endFieldName, queryType, fieldType ) };
344 if ( ! dateTimeInterval.begin().isValid( ) && ! dateTimeInterval.end().isValid( ) )
354 if ( fieldType == QVariant::Type::Date )
356 beginQuery = dateTimeInterval.begin().date().toString( Qt::DateFormat::ISODate );
357 endQuery = dateTimeInterval.end().date().toString( Qt::DateFormat::ISODate );
361 beginQuery = dateTimeInterval.begin().toString( Qt::DateFormat::ISODate );
362 endQuery = dateTimeInterval.end().toString( Qt::DateFormat::ISODate );
364 conditions.push_back( makeFilter( fieldBegin,
377 for (
const auto &dimension : std::as_const( dimensions ) )
380 const bool fieldIsDateTime { dimension.name.toLower() == QLatin1String(
"time" ) };
381 const QVariant::Type fieldType { fieldIsDateTime ? QVariant::Type::DateTime : QVariant::Type::Date };
383 const auto fieldRefBegin { refFieldCast( dimension.fieldName, queryType, fieldType ) };
384 if ( fieldRefBegin.isEmpty() )
391 const auto fieldRefEnd { refFieldCast( dimension.endFieldName, queryType, fieldType ) };
398 if ( ! inputQueryIsDateTime || ! fieldIsDateTime )
400 QString castedInterval { interval };
402 if ( inputQueryIsDateTime )
404 castedInterval = QDate::fromString( castedInterval, Qt::DateFormat::ISODate ).toString( Qt::DateFormat::ISODate );
410 QString castedInterval { interval };
412 if ( ! inputQueryIsDateTime )
414 castedInterval = QDateTime::fromString( castedInterval, Qt::DateFormat::ISODate ).toString( Qt::DateFormat::ISODate );
419 if ( ! fieldRefEnd.isEmpty() )
421 condition = QStringLiteral(
"( %1 IS NULL OR %2 <= %3 ) AND ( %5 IS NULL OR %3 <= %4 )" ).arg(
430 condition = QStringLiteral(
"( %1 IS NULL OR %2 = %3 )" )
436 conditions.push_back( condition );
440 if ( ! conditions.isEmpty() )
442 expression.
setExpression( conditions.join( QLatin1String(
" AND " ) ) );
449 auto extent { layer->
extent() };
450 if ( layer->
crs().
authid() != QLatin1String(
"EPSG:4326" ) )
456 return {{ extent.xMinimum(), extent.yMinimum(), extent.xMaximum(), extent.yMaximum() }};
476 QDateTime min { minVal.toDateTime() };
477 QDateTime max { maxVal.toDateTime() };
478 if ( ! dimInfo.endFieldName.isEmpty() )
487 QDateTime minEnd { minVal.toDateTime() };
488 QDateTime maxEnd { maxVal.toDateTime() };
489 if ( minEnd.isValid() )
491 min = std::min<QDateTime>( min, minEnd );
493 if ( maxEnd.isValid() )
495 max = std::max<QDateTime>( max, maxEnd );
503 if ( dimensions.isEmpty() )
513 for (
const auto &dimension : dimensions )
518 extent = range( dimension );
523 extent.
extend( range( dimension ) );
526 json ret = json::array();
527 const QString beginVal { extent.
begin().toString( Qt::DateFormat::ISODate ) };
528 const QString endVal { extent.
end().toString( Qt::DateFormat::ISODate ) };
530 if ( beginVal.isEmpty() && endVal.isEmpty() )
532 ret.push_back( {
nullptr,
nullptr } );
534 else if ( beginVal.isEmpty() )
536 ret.push_back( {
nullptr, endVal.toStdString() } );
538 else if ( endVal.isEmpty() )
540 ret.push_back( { beginVal.toStdString(),
nullptr } );
544 ret.push_back( { beginVal.toStdString(), endVal.toStdString() } );
548 catch ( std::exception &ex )
550 const QString errorMessage { QStringLiteral(
"Error creating temporal extent: %1" ).arg( ex.what() ) };
570 const auto parts { QUrl( bboxCrs ).path().split(
'/' ) };
571 if ( parts.count() == 6 )
583 return publishedWfsLayers< QgsVectorLayer * >( context );
593 for (
const QgsField &field : std::as_const( fields ) )
595 if ( field.displayName() == name )
605 QString result { QUrl( value ).toString() };
606 return result.replace(
'\'', QLatin1String(
"\'" ) );
612 QStringList result { { QStringLiteral(
"http://www.opengis.net/def/crs/OGC/1.3/CRS84" )}};
616 for (
const QString &crsId : outputCrsList )
619 if ( ! crsUri.isEmpty() )
621 result.push_back( crsUri );
635 QList<QPair<QString, QString> > qi;
636 QString result { path };
637 const auto constItems { QUrlQuery( requestUrl ).queryItems() };
638 for (
const auto &i : constItems )
640 if ( i.first.compare( QStringLiteral(
"MAP" ), Qt::CaseSensitivity::CaseInsensitive ) == 0 )
647 if ( ! path.endsWith(
'?' ) )
651 result.append( QStringLiteral(
"MAP=%1" ).arg( qi.first().second ) );
@ Critical
Critical/error message.
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 toOgcUri() const
Returns the crs as OGC URI (format: http://www.opengis.net/def/crs/OGC/1.3/CRS84) Returns an empty st...
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.
Container of fields for a 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)
QgsField at(int i) const
Returns the 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.
QStringList names() const
Returns a list with field names.
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
QgsMapLayerServerProperties * serverProperties()
Returns QGIS Server Properties for the map layer.
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,...
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 QString fieldName(const QString &name, const QgsVectorLayer *layer)
Given a field name (or display name) and a layer returns the actual name of the field.
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 Q_DECL_DEPRECATED 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 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 QgsDateTimeRange parseTemporalDateTimeInterval(const QString &interval)
Parses a datetime interval and returns a QgsDateTimeRange.
static QgsDateRange parseTemporalDateInterval(const QString &interval)
Parses a date interval and returns a QgsDateRange.
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.
T begin() const
Returns the beginning of the range.
bool extend(const QgsTemporalRange< T > &other)
Extends the range in place by extending this range out to include an other range.
T end() const
Returns the upper bound of the range.
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.
QgsTemporalRange< QDate > QgsDateRange
QgsRange which stores a range of dates.
const QgsCoordinateReferenceSystem & crs
Setting to define QGIS Server WMS Dimension.