18#include <nlohmann/json.hpp>
29#include <QCryptographicHash>
34QgsSensorThingsSharedData::QgsSensorThingsSharedData(
const QString &uri )
36 const QVariantMap uriParts = QgsSensorThingsProviderMetadata().decodeUri( uri );
39 const QVariantList expandTo = uriParts.value( QStringLiteral(
"expandTo" ) ).toList();
40 QList< Qgis::SensorThingsEntity > expandedEntities;
41 for (
const QVariant &expansionVariant : expandTo )
46 mExpansions.append( expansion );
57 mMaximumPageSize = uriParts.value( QStringLiteral(
"pageSize" ), mMaximumPageSize ).toInt();
59 mFeatureLimit = uriParts.value( QStringLiteral(
"featureLimit" ) ).toInt();
60 mFilterExtent = uriParts.value( QStringLiteral(
"bounds" ) ).value<
QgsRectangle >();
61 mSubsetString = uriParts.value( QStringLiteral(
"sql" ) ).
toString();
65 if ( uriParts.contains( QStringLiteral(
"geometryType" ) ) )
67 const QString geometryType = uriParts.value( QStringLiteral(
"geometryType" ) ).toString();
68 if ( geometryType.compare( QLatin1String(
"point" ), Qt::CaseInsensitive ) == 0 )
72 else if ( geometryType.compare( QLatin1String(
"multipoint" ), Qt::CaseInsensitive ) == 0 )
76 else if ( geometryType.compare( QLatin1String(
"line" ), Qt::CaseInsensitive ) == 0 )
80 else if ( geometryType.compare( QLatin1String(
"polygon" ), Qt::CaseInsensitive ) == 0 )
102 mAuthCfg = dsUri.authConfigId();
103 mHeaders = dsUri.httpHeaders();
105 mRootUri = uriParts.value( QStringLiteral(
"url" ) ).toString();
108QUrl QgsSensorThingsSharedData::parseUrl(
const QUrl &url,
bool *isTestEndpoint )
110 if ( isTestEndpoint )
111 *isTestEndpoint =
false;
113 QUrl modifiedUrl( url );
114 if ( modifiedUrl.toString().contains( QLatin1String(
"fake_qgis_http_endpoint" ) ) )
116 if ( isTestEndpoint )
117 *isTestEndpoint =
true;
120 QString modifiedUrlString = modifiedUrl.toString();
122 modifiedUrlString = QUrl::fromPercentEncoding( modifiedUrlString.toUtf8() );
123 modifiedUrlString.replace( QLatin1String(
"fake_qgis_http_endpoint/" ), QLatin1String(
"fake_qgis_http_endpoint_" ) );
125 modifiedUrlString = modifiedUrlString.mid( QStringLiteral(
"http://" ).size() );
126 QString args = modifiedUrlString.indexOf(
'?' ) >= 0 ? modifiedUrlString.mid( modifiedUrlString.indexOf(
'?' ) ) : QString();
127 if ( modifiedUrlString.size() > 150 )
129 args = QCryptographicHash::hash( args.toUtf8(), QCryptographicHash::Md5 ).toHex();
133 args.replace( QLatin1String(
"?" ), QLatin1String(
"_" ) );
134 args.replace( QLatin1String(
"&" ), QLatin1String(
"_" ) );
135 args.replace( QLatin1String(
"$" ), QLatin1String(
"_" ) );
136 args.replace( QLatin1String(
"<" ), QLatin1String(
"_" ) );
137 args.replace( QLatin1String(
">" ), QLatin1String(
"_" ) );
138 args.replace( QLatin1String(
"'" ), QLatin1String(
"_" ) );
139 args.replace( QLatin1String(
"\"" ), QLatin1String(
"_" ) );
140 args.replace( QLatin1String(
" " ), QLatin1String(
"_" ) );
141 args.replace( QLatin1String(
":" ), QLatin1String(
"_" ) );
142 args.replace( QLatin1String(
"/" ), QLatin1String(
"_" ) );
143 args.replace( QLatin1String(
"\n" ), QLatin1String(
"_" ) );
148 if ( modifiedUrlString[1] ==
'/' )
150 modifiedUrlString = modifiedUrlString[0] +
":/" + modifiedUrlString.mid( 2 );
153 modifiedUrlString = modifiedUrlString.mid( 0, modifiedUrlString.indexOf(
'?' ) ) + args;
154 QgsDebugMsgLevel( QStringLiteral(
"Get %1 (after laundering)" ).arg( modifiedUrlString ), 2 );
155 modifiedUrl = QUrl::fromLocalFile( modifiedUrlString );
156 if ( !QFile::exists( modifiedUrlString ) )
158 QgsDebugError( QStringLiteral(
"Local test file %1 for URL %2 does not exist!!!" ).arg( modifiedUrlString, url.toString() ) );
171 return hasCachedAllFeatures() ? mFetchedFeatureExtent
172 : ( !mFilterExtent.isNull() ? mFilterExtent :
QgsRectangle( -180, -90, 180, 90 ) );
175long long QgsSensorThingsSharedData::featureCount(
QgsFeedback *feedback )
const
178 if ( mFeatureCount >= 0 )
179 return mFeatureCount;
187 if ( !mExpansions.isEmpty() )
193 QString countUri = QStringLiteral(
"%1?$top=0&$count=true" ).arg( mEntityBaseUri );
197 if ( !filterString.isEmpty() )
198 filterString = QStringLiteral(
"&$filter=" ) + filterString;
199 if ( !filterString.isEmpty() )
200 countUri += filterString;
202 const QUrl url = parseUrl( QUrl( countUri ) );
204 QNetworkRequest request( url );
206 mHeaders.updateNetworkRequest( request );
213 return mFeatureCount;
226 auto rootContent = json::parse( content.
content().toStdString() );
227 if ( !rootContent.contains(
"@iot.count" ) )
229 mError = QObject::tr(
"No '@iot.count' value in response" );
230 return mFeatureCount;
233 mFeatureCount = rootContent[
"@iot.count"].get<
long long>();
234 if ( mFeatureLimit > 0 && mFeatureCount > mFeatureLimit )
235 mFeatureCount = mFeatureLimit;
237 catch (
const json::parse_error &ex )
239 mError = QObject::tr(
"Error parsing response: %1" ).arg( ex.what() );
243 return mFeatureCount;
246QString QgsSensorThingsSharedData::subsetString()
const
248 return mSubsetString;
251bool QgsSensorThingsSharedData::hasCachedAllFeatures()
const
254 return mHasCachedAllFeatures
255 || ( mFeatureCount > 0 && mCachedFeatures.size() == mFeatureCount )
256 || ( mFeatureLimit > 0 && mRetrievedBaseFeatureCount >= mFeatureLimit );
264 QMap<QgsFeatureId, QgsFeature>::const_iterator it = mCachedFeatures.constFind(
id );
265 if ( it != mCachedFeatures.constEnd() )
271 if ( hasCachedAllFeatures() )
274 bool featureFetched =
false;
276 if ( mNextPage.isEmpty() )
280 int thisPageSize = mMaximumPageSize;
281 if ( mFeatureLimit > 0 && ( mCachedFeatures.size() + thisPageSize ) > mFeatureLimit )
282 thisPageSize = mFeatureLimit - mCachedFeatures.size();
284 mNextPage = QStringLiteral(
"%1?$top=%2&$count=false%3" ).arg( mEntityBaseUri ).arg( thisPageSize ).arg( !mExpandQueryString.isEmpty() ? ( QStringLiteral(
"&" ) + mExpandQueryString ) : QString() );
288 if ( !filterString.isEmpty() )
289 mNextPage += QStringLiteral(
"&$filter=" ) + filterString;
294 processFeatureRequest( mNextPage, feedback, [
id, &f, &featureFetched](
const QgsFeature & feature )
296 if ( feature.
id() ==
id )
299 featureFetched =
true;
302 }, [&featureFetched,
this]
304 return !featureFetched && !hasCachedAllFeatures();
308 mHasCachedAllFeatures =
true;
311 return featureFetched;
320 if ( hasCachedAllFeatures() || mCachedExtent.contains( extentGeom ) )
324 return qgis::listToSet( mSpatialIndex.intersects( requestExtent ) );
330 if ( !filterString.isEmpty() )
331 filterString = QStringLiteral(
"&$filter=" ) + filterString;
332 int thisPageSize = mMaximumPageSize;
334 if ( !thisPage.isEmpty() )
337 const thread_local QRegularExpression topRe( QStringLiteral(
"\\$top=\\d+" ) );
338 const QRegularExpressionMatch match = topRe.match( queryUrl );
339 if ( match.hasMatch() )
341 if ( mFeatureLimit > 0 && ( mCachedFeatures.size() + thisPageSize ) > mFeatureLimit )
342 thisPageSize = mFeatureLimit - mCachedFeatures.size();
343 queryUrl = queryUrl.left( match.capturedStart( 0 ) ) + QStringLiteral(
"$top=%1" ).arg( thisPageSize ) + queryUrl.mid( match.capturedEnd( 0 ) );
348 queryUrl = QStringLiteral(
"%1?$top=%2&$count=false%3%4" ).arg( mEntityBaseUri ).arg( thisPageSize ).arg( filterString, !mExpandQueryString.isEmpty() ? ( QStringLiteral(
"&" ) + mExpandQueryString ) : QString() );
351 if ( thisPage.isEmpty() && mCachedExtent.intersects( extentGeom ) )
357 return qgis::listToSet( mSpatialIndex.intersects( requestExtent ) );
364 bool noMoreFeatures =
false;
365 bool hasFirstPage =
false;
366 const bool res = processFeatureRequest( queryUrl, feedback, [&ids, &alreadyFetchedIds](
const QgsFeature & feature )
368 if ( !alreadyFetchedIds.contains( feature.
id() ) )
369 ids.insert( feature.
id() );
381 noMoreFeatures =
true;
383 if ( noMoreFeatures && res && ( !feedback || !feedback->
isCanceled() ) )
388 nextPage = noMoreFeatures || !res ? QString() : queryUrl;
393void QgsSensorThingsSharedData::clearCache()
398 mCachedFeatures.clear();
399 mIotIdToFeatureId.clear();
404bool QgsSensorThingsSharedData::processFeatureRequest( QString &nextPage,
QgsFeedback *feedback,
const std::function<
void(
const QgsFeature & ) > &fetchedFeatureCallback,
const std::function<
bool ()> &continueFetchingCallback,
const std::function<
void ()> &onNoMoreFeaturesCallback )
409 const QString authcfg = mAuthCfg;
412 const QList< QgsSensorThingsExpansionDefinition > expansions = mExpansions;
414 while ( continueFetchingCallback() )
423 const QUrl url = parseUrl( nextPage );
425 QNetworkRequest request( url );
450 const auto rootContent = json::parse( content.
content().toStdString() );
451 if ( !rootContent.contains(
"value" ) )
454 mError = QObject::tr(
"No 'value' in response" );
461 const auto &values = rootContent[
"value"];
462 if ( values.empty() )
466 onNoMoreFeaturesCallback();
473 for (
const auto &featureData : values )
475 auto getString = [](
const basic_json<> &json,
const char *tag ) -> QVariant
477 if ( !json.contains( tag ) )
480 std::function< QString(
const basic_json<> &obj,
bool &ok ) > objToString;
481 objToString = [&objToString](
const basic_json<> &obj,
bool & ok ) -> QString
484 if ( obj.is_number_integer() )
486 return QString::number( obj.get<
int>() );
488 else if ( obj.is_number_unsigned() )
490 return QString::number( obj.get<
unsigned>() );
492 else if ( obj.is_boolean() )
494 return QString::number( obj.get<
bool>() );
496 else if ( obj.is_number_float() )
498 return QString::number( obj.get<
double>() );
500 else if ( obj.is_array() )
503 results.reserve( obj.size() );
504 for (
const auto &item : obj )
507 const QString itemString = objToString( item, itemOk );
509 results.push_back( itemString );
511 return results.join(
',' );
513 else if ( obj.is_string() )
515 return QString::fromStdString( obj.get<std::string >() );
522 const auto &jObj = json[tag];
524 const QString r = objToString( jObj, ok );
530 auto getDateTime = [](
const basic_json<> &json,
const char *tag ) -> QVariant
532 if ( !json.contains( tag ) )
535 const auto &jObj = json[tag];
536 if ( jObj.is_string() )
538 const QString dateTimeString = QString::fromStdString( json[tag].get<std::string >() );
539 return QDateTime::fromString( dateTimeString, Qt::ISODateWithMs );
545 auto getVariantMap = [](
const basic_json<> &json,
const char *tag ) -> QVariant
547 if ( !json.contains( tag ) )
553 auto getVariantList = [](
const basic_json<> &json,
const char *tag ) -> QVariant
555 if ( !json.contains( tag ) )
561 auto getStringList = [](
const basic_json<> &json,
const char *tag ) -> QVariant
563 if ( !json.contains( tag ) )
566 const auto &jObj = json[tag];
567 if ( jObj.is_string() )
569 return QStringList{ QString::fromStdString( json[tag].get<std::string >() ) };
571 else if ( jObj.is_array() )
574 for (
const auto &element : jObj )
576 if ( element.is_string() )
577 res.append( QString::fromStdString( element.get<std::string >() ) );
585 auto getDateTimeRange = [](
const basic_json<> &json,
const char *tag ) -> std::pair< QVariant, QVariant >
587 if ( !json.contains( tag ) )
588 return { QVariant(), QVariant() };
590 const auto &jObj = json[tag];
591 if ( jObj.is_string() )
593 const QString rangeString = QString::fromStdString( json[tag].get<std::string >() );
594 const QStringList rangeParts = rangeString.split(
'/' );
595 if ( rangeParts.size() == 2 )
599 QDateTime::fromString( rangeParts.at( 0 ), Qt::ISODateWithMs ),
600 QDateTime::fromString( rangeParts.at( 1 ), Qt::ISODateWithMs )
605 const QDateTime instant = QDateTime::fromString( rangeString, Qt::ISODateWithMs );
606 if ( instant.isValid() )
607 return { instant, instant };
611 return { QVariant(), QVariant() };
614 const QString iotId = getString( featureData,
"@iot.id" ).toString();
615 if ( expansions.isEmpty() )
617 auto existingFeatureIdIt = mIotIdToFeatureId.constFind( iotId );
618 if ( existingFeatureIdIt != mIotIdToFeatureId.constEnd() )
621 fetchedFeatureCallback( *mCachedFeatures.find( *existingFeatureIdIt ) );
631 if ( featureData.contains( mGeometryField.toLocal8Bit().constData() ) )
633 const auto &geometryPart = featureData[mGeometryField.toLocal8Bit().constData()];
634 if ( geometryPart.contains(
"geometry" ) )
641 auto extendAttributes = [&getString, &getVariantMap, &getDateTimeRange, &getDateTime, &getStringList, &getVariantList](
Qgis::SensorThingsEntity entityType,
const auto & entityData,
QgsAttributes & attributes )
643 const QString iotId = getString( entityData,
"@iot.id" ).toString();
644 const QString selfLink = getString( entityData,
"@iot.selfLink" ).toString();
646 const QVariant properties = getVariantMap( entityData,
"properties" );
649 switch ( entityType )
658 << getString( entityData,
"name" )
659 << getString( entityData,
"description" )
667 << getString( entityData,
"name" )
668 << getString( entityData,
"description" )
676 << getDateTime( entityData,
"time" );
681 std::pair< QVariant, QVariant > phenomenonTime = getDateTimeRange( entityData,
"phenomenonTime" );
682 std::pair< QVariant, QVariant > resultTime = getDateTimeRange( entityData,
"resultTime" );
686 << getString( entityData,
"name" )
687 << getString( entityData,
"description" )
688 << getVariantMap( entityData,
"unitOfMeasurement" )
689 << getString( entityData,
"observationType" )
691 << phenomenonTime.first
692 << phenomenonTime.second
694 << resultTime.second;
702 << getString( entityData,
"name" )
703 << getString( entityData,
"description" )
704 << getString( entityData,
"metadata" )
712 << getString( entityData,
"name" )
713 << getString( entityData,
"definition" )
714 << getString( entityData,
"description" )
720 std::pair< QVariant, QVariant > phenomenonTime = getDateTimeRange( entityData,
"phenomenonTime" );
721 std::pair< QVariant, QVariant > validTime = getDateTimeRange( entityData,
"validTime" );
725 << phenomenonTime.first
726 << phenomenonTime.second
727 << getString( entityData,
"result" )
728 << getDateTime( entityData,
"resultTime" )
729 << getStringList( entityData,
"resultQuality" )
732 << getVariantMap( entityData,
"parameters" );
740 << getString( entityData,
"name" )
741 << getString( entityData,
"description" )
747 std::pair< QVariant, QVariant > phenomenonTime = getDateTimeRange( entityData,
"phenomenonTime" );
748 std::pair< QVariant, QVariant > resultTime = getDateTimeRange( entityData,
"resultTime" );
752 << getString( entityData,
"name" )
753 << getString( entityData,
"description" )
754 << getVariantList( entityData,
"unitOfMeasurements" )
755 << getString( entityData,
"observationType" )
756 << getStringList( entityData,
"multiObservationDataTypes" )
758 << phenomenonTime.first
759 << phenomenonTime.second
761 << resultTime.second;
769 attributes.reserve( fields.
size() );
770 extendAttributes( mEntityType, featureData, attributes );
772 auto processFeature = [
this, &fetchedFeatureCallback](
QgsFeature & feature,
const QString & rawFeatureId )
774 feature.
setId( mNextFeatureId++ );
776 mCachedFeatures.insert( feature.
id(), feature );
777 mIotIdToFeatureId.insert( rawFeatureId, feature.
id() );
778 mSpatialIndex.addFeature( feature );
781 fetchedFeatureCallback( feature );
784 const QString baseFeatureId = getString( featureData,
"@iot.id" ).toString();
785 if ( !expansions.empty() )
787 mRetrievedBaseFeatureCount++;
789 std::function< void(
const nlohmann::json &,
Qgis::SensorThingsEntity,
const QList<QgsSensorThingsExpansionDefinition > &,
const QString &,
const QgsAttributes & ) > traverseExpansion;
790 traverseExpansion = [
this, &feature, &getString, &traverseExpansion, &fetchedFeatureCallback, &extendAttributes, &processFeature](
const nlohmann::json & currentLevelData,
Qgis::SensorThingsEntity parentEntityType,
const QList<QgsSensorThingsExpansionDefinition > &expansionTargets,
const QString & lowerLevelId,
const QgsAttributes & lowerLevelAttributes )
793 const QList< QgsSensorThingsExpansionDefinition > remainingExpansionTargets = expansionTargets.mid( 1 );
797 QString currentExpansionPropertyString;
798 switch ( cardinality )
811 if ( currentLevelData.contains( currentExpansionPropertyString.toLocal8Bit().constData() ) )
813 auto parseExpandedEntity = [lowerLevelAttributes, &feature, &processFeature, &lowerLevelId, &getString, &remainingExpansionTargets, &fetchedFeatureCallback, &extendAttributes, &traverseExpansion, ¤tExpansionTarget,
this](
const json & expandedEntityElement )
816 const QString expandedEntityIotId = getString( expandedEntityElement,
"@iot.id" ).toString();
817 const QString expandedFeatureId = lowerLevelId +
'_' + expandedEntityIotId;
819 if ( remainingExpansionTargets.empty() )
821 auto existingFeatureIdIt = mIotIdToFeatureId.constFind( expandedFeatureId );
822 if ( existingFeatureIdIt != mIotIdToFeatureId.constEnd() )
825 fetchedFeatureCallback( *mCachedFeatures.find( *existingFeatureIdIt ) );
830 extendAttributes( currentExpansionTarget.childEntity(), expandedEntityElement, expandedAttributes );
831 if ( !remainingExpansionTargets.empty() )
834 traverseExpansion( expandedEntityElement, currentExpansionTarget.childEntity(), remainingExpansionTargets, expandedFeatureId, expandedAttributes );
838 feature.setAttributes( expandedAttributes );
839 processFeature( feature, expandedFeatureId );
842 const auto &expandedEntity = currentLevelData[currentExpansionPropertyString.toLocal8Bit().constData()];
843 if ( expandedEntity.is_array() )
845 for (
const auto &expandedEntityElement : expandedEntity )
847 parseExpandedEntity( expandedEntityElement );
853 else if ( expandedEntity.is_object() )
855 parseExpandedEntity( expandedEntity );
866 traverseExpansion( featureData, mEntityType, expansions, baseFeatureId, attributes );
868 if ( mFeatureLimit > 0 && mFeatureLimit <= mRetrievedBaseFeatureCount )
873 feature.setAttributes( attributes );
874 processFeature( feature, baseFeatureId );
875 mRetrievedBaseFeatureCount++;
876 if ( mFeatureLimit > 0 && mFeatureLimit <= mRetrievedBaseFeatureCount )
883 if ( rootContent.contains(
"@iot.nextLink" ) && ( mFeatureLimit == 0 || mFeatureLimit > mCachedFeatures.size() ) )
885 nextPage = QString::fromStdString( rootContent[
"@iot.nextLink"].get<std::string>() );
889 onNoMoreFeaturesCallback();
893 if ( !continueFetchingCallback() )
899 catch (
const json::parse_error &ex )
902 mError = QObject::tr(
"Error parsing response: %1" ).arg( ex.what() );
903 QgsDebugMsgLevel( QStringLiteral(
"Error parsing response: %1" ).arg( ex.what() ), 2 );
SensorThingsEntity
OGC SensorThings API entity types.
@ Sensor
A Sensor is an instrument that observes a property or phenomenon with the goal of producing an estima...
@ MultiDatastream
A MultiDatastream groups a collection of Observations and the Observations in a MultiDatastream have ...
@ ObservedProperty
An ObservedProperty specifies the phenomenon of an Observation.
@ Invalid
An invalid/unknown entity.
@ FeatureOfInterest
In the context of the Internet of Things, many Observations’ FeatureOfInterest can be the Location of...
@ Datastream
A Datastream groups a collection of Observations measuring the same ObservedProperty and produced by ...
@ Observation
An Observation is the act of measuring or otherwise determining the value of a property.
@ Location
A Location entity locates the Thing or the Things it associated with. A Thing’s Location entity is de...
@ Thing
A Thing is an object of the physical world (physical things) or the information world (virtual things...
@ HistoricalLocation
A Thing’s HistoricalLocation entity set provides the times of the current (i.e., last known) and prev...
RelationshipCardinality
Relationship cardinality.
@ ManyToMany
Many to many relationship.
@ ManyToOne
Many to one relationship.
@ OneToOne
One to one relationship.
@ OneToMany
One to many relationship.
@ MultiPointZ
MultiPointZ.
@ MultiLineStringZ
MultiLineStringZ.
@ MultiPolygonZ
MultiPolygonZ.
A thread safe class for performing blocking (sync) network requests, with full support for QGIS proxy...
void setAuthCfg(const QString &authCfg)
Sets the authentication config id which should be used during the request.
QString errorMessage() const
Returns the error message string, after a get(), post(), head() or put() request has been made.
ErrorCode get(QNetworkRequest &request, bool forceRefresh=false, QgsFeedback *feedback=nullptr, RequestFlags requestFlags=QgsBlockingNetworkRequest::RequestFlags())
Performs a "get" operation on the specified request.
@ NoError
No error was encountered.
QgsNetworkReplyContent reply() const
Returns the content of the network reply, after a get(), post(), head() or put() request has been mad...
Represents a coordinate reference system (CRS).
Stores the component parts of a data source URI (e.g.
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
void setId(QgsFeatureId id)
Sets the feature id for this feature.
void setGeometry(const QgsGeometry &geometry)
Set the feature's geometry.
Base class for feedback objects to be used for cancellation of something running in a worker thread.
bool isCanceled() const
Tells whether the operation has been canceled already.
Container of fields for a vector layer.
int size() const
Returns number of items.
A geometry is the spatial representation of a feature.
static QgsGeometry fromRect(const QgsRectangle &rect)
Creates a new geometry from a QgsRectangle.
static QgsGeometry unaryUnion(const QVector< QgsGeometry > &geometries, const QgsGeometryParameters ¶meters=QgsGeometryParameters())
Compute the unary union on a list of geometries.
QgsRectangle boundingBox() const
Returns the bounding box of the geometry.
static QgsGeometry geometryFromGeoJson(const json &geometry)
Parses a GeoJSON "geometry" value to a QgsGeometry object.
static QVariant jsonToVariant(const json &value)
Converts a JSON value to a QVariant, in case of parsing error an invalid QVariant is returned.
Encapsulates a network reply within a container which is inexpensive to copy and safe to pass between...
QByteArray content() const
Returns the reply content.
A convenience class that simplifies locking and unlocking QReadWriteLocks.
A rectangle specified with double values.
Q_INVOKABLE QString toString(int precision=16) const
Returns a string representation of form xmin,ymin : xmax,ymax Coordinates will be truncated to the sp...
QgsRectangle intersect(const QgsRectangle &rect) const
Returns the intersection with the given rectangle.
Encapsulates information about how relationships in a SensorThings API service should be expanded.
Qgis::SensorThingsEntity childEntity() const
Returns the target child entity which should be expanded.
bool isValid() const
Returns true if the definition is valid.
static QString entityToSetString(Qgis::SensorThingsEntity type)
Converts a SensorThings entity set to a SensorThings entity set string.
static QString asQueryString(Qgis::SensorThingsEntity baseType, const QList< QgsSensorThingsExpansionDefinition > &expansions)
Returns a list of expansions as a valid SensorThings API query string, eg "$expand=Locations($orderby...
static QString combineFilters(const QStringList &filters)
Combines a set of SensorThings API filter operators.
static QString filterForWkbType(Qgis::SensorThingsEntity entityType, Qgis::WkbType wkbType)
Returns a filter string which restricts results to those matching the specified entityType and wkbTyp...
static Qgis::RelationshipCardinality relationshipCardinality(Qgis::SensorThingsEntity baseType, Qgis::SensorThingsEntity relatedType, bool &valid)
Returns the cardinality of the relationship between a base entity type and a related entity type.
static bool entityTypeHasGeometry(Qgis::SensorThingsEntity type)
Returns true if the specified entity type can have geometry attached.
static QgsFields fieldsForExpandedEntityType(Qgis::SensorThingsEntity baseType, const QList< Qgis::SensorThingsEntity > &expandedTypes)
Returns the fields which correspond to a specified entity baseType, expanded using the specified list...
static QString geometryFieldForEntityType(Qgis::SensorThingsEntity type)
Returns the geometry field for a specified entity type.
static QString filterForExtent(const QString &geometryField, const QgsRectangle &extent)
Returns a filter string which restricts results to those within the specified extent.
A spatial index for QgsFeature objects.
@ Uncounted
Feature count not yet computed.
@ UnknownCount
Provider returned an unknown feature count.
T qgsEnumKeyToValue(const QString &key, const T &defaultValue, bool tryValueAsKey=true, bool *returnOk=nullptr)
Returns the value corresponding to the given key of an enum.
QString qgsEnumValueToKey(const T &value, bool *returnOk=nullptr)
Returns the value for the given key of an enum.
QSet< QgsFeatureId > QgsFeatureIds
qint64 QgsFeatureId
64 bit feature ids negative numbers are used for uncommitted/newly added features
#define QgsDebugMsgLevel(str, level)
#define QgsDebugError(str)
#define QgsSetRequestInitiatorClass(request, _class)