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 ) );