20#include <nlohmann/json.hpp>
35#include <QNetworkRequest>
38#include "moc_qgssensorthingsprovider.cpp"
40using namespace Qt::StringLiterals;
44QgsSensorThingsProvider::QgsSensorThingsProvider(
const QString &uri,
const ProviderOptions &options,
Qgis::DataProviderReadFlags flags )
47 mSharedData = std::make_shared< QgsSensorThingsSharedData >( uri );
49 const QUrl url( QgsSensorThingsSharedData::parseUrl( mSharedData->mRootUri ) );
51 QNetworkRequest request = QNetworkRequest( url );
55 networkRequest.
setAuthCfg( mSharedData->mAuthCfg );
57 switch ( networkRequest.
get( request ) )
73 auto rootContent = json::parse( content.
content().toStdString() );
76 if ( rootContent.contains(
"serverSettings" ) && rootContent[
"serverSettings"].contains(
"conformance" ) )
78 for (
const auto &valueJson : rootContent[
"serverSettings"][
"conformance"] )
80 const QString conformance = QString::fromStdString( valueJson.get<std::string>() );
81 const thread_local QRegularExpression sDataModelRx( u
".*/datamodel\\b"_s );
82 if ( sDataModelRx.match( conformance ).hasMatch() )
85 const thread_local QRegularExpression sVersionRx( u
"\\d+\\.\\d+"_s );
86 const QRegularExpressionMatch versionMatch = sVersionRx.match( conformance );
87 if ( versionMatch.hasMatch() )
89 const QString versionString = versionMatch.captured( 0 );
90 mSharedData->mVersion = QVersionNumber::fromString( versionString );
96 if ( !rootContent.contains(
"value" ) )
98 appendError(
QgsErrorMessage( tr(
"No 'value' array in response" ), u
"SensorThings"_s ) );
102 bool foundMatchingEntity =
false;
103 for (
const auto &valueJson : rootContent[
"value"] )
105 if ( valueJson.contains(
"name" ) && valueJson.contains(
"url" ) )
107 const QString name = QString::fromStdString( valueJson[
"name"].get<std::string>() );
109 if ( entityType == mSharedData->mEntityType )
111 const QString url = QString::fromStdString( valueJson[
"url"].get<std::string>() );
112 if ( !url.isEmpty() )
114 foundMatchingEntity =
true;
115 mSharedData->mEntityBaseUri = url;
123 ( void ) mSharedData->featureCount();
129 if ( !foundMatchingEntity )
131 switch ( mSharedData->mEntityType )
147 appendError(
QgsErrorMessage( tr(
"MultiDatastreams are not supported by this connection" ), u
"SensorThings"_s ) );
152 appendError(
QgsErrorMessage( tr(
"FeaturesOfInterest are not supported by this connection" ), u
"SensorThings"_s ) );
157 appendError(
QgsErrorMessage( tr(
"FeatureTypes are not supported by this connection" ), u
"SensorThings"_s ) );
162 appendError(
QgsErrorMessage( tr(
"Deployments are not supported by this connection" ), u
"SensorThings"_s ) );
167 appendError(
QgsErrorMessage( tr(
"ObservingProcedures are not supported by this connection" ), u
"SensorThings"_s ) );
172 appendError(
QgsErrorMessage( tr(
"Samplings are not supported by this connection" ), u
"SensorThings"_s ) );
177 appendError(
QgsErrorMessage( tr(
"SamplingProcedures are not supported by this connection" ), u
"SensorThings"_s ) );
182 appendError(
QgsErrorMessage( tr(
"Samplers are not supported by this connection" ), u
"SensorThings"_s ) );
187 appendError(
QgsErrorMessage( tr(
"PreparationSteps are not supported by this connection" ), u
"SensorThings"_s ) );
192 appendError(
QgsErrorMessage( tr(
"PreparationProcedures are not supported by this connection" ), u
"SensorThings"_s ) );
197 appendError(
QgsErrorMessage( tr(
"ThingRelations are not supported by this connection" ), u
"SensorThings"_s ) );
202 appendError(
QgsErrorMessage( tr(
"RelationRoles are not supported by this connection" ), u
"SensorThings"_s ) );
207 appendError(
QgsErrorMessage( tr(
"FeatureRelations are not supported by this connection" ), u
"SensorThings"_s ) );
212 appendError(
QgsErrorMessage( tr(
"DatastreamRelations are not supported by this connection" ), u
"SensorThings"_s ) );
217 appendError(
QgsErrorMessage( tr(
"ObservationRelations are not supported by this connection" ), u
"SensorThings"_s ) );
225 catch (
const json::parse_error &ex )
227 appendError(
QgsErrorMessage( tr(
"Error parsing response: %1" ).arg( ex.what() ), u
"SensorThings"_s ) );
234QString QgsSensorThingsProvider::storageType()
const
238 return u
"OGC SensorThings API"_s;
245 return new QgsSensorThingsFeatureSource( mSharedData );
252 return new QgsSensorThingsFeatureIterator(
new QgsSensorThingsFeatureSource( mSharedData ),
true, request );
259 return mSharedData->mGeometryType;
262long long QgsSensorThingsProvider::featureCount()
const
271 const long long count = mSharedData->featureCount();
272 if ( !mSharedData->error().isEmpty() )
273 pushError( mSharedData->error() );
278QgsFields QgsSensorThingsProvider::fields()
const
282 return mSharedData->mFields;
289 return mLayerMetadata;
292QString QgsSensorThingsProvider::htmlMetadata()
const
299 metadata += u
"<tr><td class=\"highlight\">"_s % tr(
"SensorThings Version" ) % u
"</td><td>%1"_s.arg( mSharedData->mVersion.toString() ) % u
"</td></tr>\n"_s;
300 metadata += u
"<tr><td class=\"highlight\">"_s % tr(
"Entity Type" ) % u
"</td><td>%1"_s.arg(
qgsEnumValueToKey( mSharedData->mEntityType ) ) % u
"</td></tr>\n"_s;
301 metadata += u
"<tr><td class=\"highlight\">"_s % tr(
"Endpoint" ) % u
"</td><td><a href=\"%1\">%1</a>"_s.arg( mSharedData->mEntityBaseUri ) % u
"</td></tr>\n"_s;
306QVariantMap QgsSensorThingsProvider::metadata()
const
310 QVariantMap metadata;
313 metadata.insert( u
"SensorThingsVersion"_s, mSharedData->mVersion.toString() );
333bool QgsSensorThingsProvider::supportsSubsetString()
const
339QString QgsSensorThingsProvider::subsetStringDialect()
const
341 return tr(
"OGC SensorThings filter" );
344QString QgsSensorThingsProvider::subsetStringHelpUrl()
const
346 return u
"https://docs.ogc.org/is/18-088/18-088.html#filter"_s;
349QString QgsSensorThingsProvider::subsetString()
const
352 return mSharedData->subsetString();
355bool QgsSensorThingsProvider::setSubsetString(
const QString &subset,
bool )
359 const QString trimmedSubset = subset.trimmed();
360 if ( trimmedSubset == mSharedData->subsetString() )
365 const QString baseUri = mSharedData->mEntityBaseUri;
368 uri.
setSql( trimmedSubset );
369 setDataSourceUri( uri.
uri(
false ) );
371 mSharedData->mEntityBaseUri = baseUri;
380void QgsSensorThingsProvider::setDataSourceUri(
const QString &uri )
384 mSharedData = std::make_shared< QgsSensorThingsSharedData >( uri );
392 return mSharedData->mSourceCRS;
398 return mSharedData->extent();
401QString QgsSensorThingsProvider::name()
const
405 return SENSORTHINGS_PROVIDER_KEY;
408QString QgsSensorThingsProvider::providerKey()
410 return SENSORTHINGS_PROVIDER_KEY;
415 mSharedData = qobject_cast<QgsSensorThingsProvider *>( source )->mSharedData;
418QString QgsSensorThingsProvider::description()
const
420 return SENSORTHINGS_PROVIDER_DESCRIPTION;
423bool QgsSensorThingsProvider::renderInPreview(
const PreviewContext & )
429void QgsSensorThingsProvider::reloadProviderData()
432 mSharedData->clearCache();
440QgsSensorThingsProviderMetadata::QgsSensorThingsProviderMetadata()
441 :
QgsProviderMetadata( QgsSensorThingsProvider::SENSORTHINGS_PROVIDER_KEY, QgsSensorThingsProvider::SENSORTHINGS_PROVIDER_DESCRIPTION )
449QIcon QgsSensorThingsProviderMetadata::icon()
const
454QList<QgsDataItemProvider *> QgsSensorThingsProviderMetadata::dataItemProviders()
const
456 return {
new QgsSensorThingsDataItemProvider() };
459QVariantMap QgsSensorThingsProviderMetadata::decodeUri(
const QString &uri )
const
463 QVariantMap components;
464 components.insert( u
"url"_s, dsUri.
param( u
"url"_s ) );
468 components.insert( u
"authcfg"_s, dsUri.
authConfigId() );
472 components.insert( u
"username"_s, dsUri.
username() );
476 components.insert( u
"password"_s, dsUri.
password() );
484 if ( !dsUri.
param( u
"referer"_s ).isEmpty() )
486 components.insert( u
"referer"_s, dsUri.
param( u
"referer"_s ) );
488 if ( !dsUri.
param( u
"http-header:referer"_s ).isEmpty() )
490 components.insert( u
"referer"_s, dsUri.
param( u
"http-header:referer"_s ) );
493 const QString entityParam = dsUri.
param( u
"entity"_s );
501 const QStringList expandToParam = dsUri.
param( u
"expandTo"_s ).split(
';', Qt::SkipEmptyParts );
502 if ( !expandToParam.isEmpty() )
504 QVariantList expandParts;
505 for (
const QString &expandString : expandToParam )
510 expandParts.append( QVariant::fromValue( definition ) );
513 if ( !expandParts.isEmpty() )
515 components.insert( u
"expandTo"_s, expandParts );
520 const int maxPageSizeParam = dsUri.
param( u
"pageSize"_s ).toInt( &ok );
523 components.insert( u
"pageSize"_s, maxPageSizeParam );
527 const int featureLimitParam = dsUri.
param( u
"featureLimit"_s ).toInt( &ok );
530 components.insert( u
"featureLimit"_s, featureLimitParam );
537 components.insert( u
"geometryType"_s, u
"multipoint"_s );
539 components.insert( u
"geometryType"_s, u
"point"_s );
542 components.insert( u
"geometryType"_s, u
"line"_s );
545 components.insert( u
"geometryType"_s, u
"polygon"_s );
553 const QStringList bbox = dsUri.
param( u
"bbox"_s ).split(
',' );
554 if ( bbox.size() == 4 )
565 if ( xminOk && yminOk && xmaxOk && ymaxOk )
566 components.insert( u
"bounds"_s, r );
569 if ( !dsUri.
sql().isEmpty() )
570 components.insert( u
"sql"_s, dsUri.
sql() );
575QString QgsSensorThingsProviderMetadata::encodeUri(
const QVariantMap &parts )
const
578 dsUri.
setParam( u
"url"_s, parts.value( u
"url"_s ).toString() );
580 if ( !parts.value( u
"authcfg"_s ).toString().isEmpty() )
584 if ( !parts.value( u
"username"_s ).toString().isEmpty() )
586 dsUri.
setUsername( parts.value( u
"username"_s ).toString() );
588 if ( !parts.value( u
"password"_s ).toString().isEmpty() )
590 dsUri.
setPassword( parts.value( u
"password"_s ).toString() );
592 if ( !parts.value( u
"referer"_s ).toString().isEmpty() )
594 dsUri.
setParam( u
"referer"_s, parts.value( u
"referer"_s ).toString() );
606 const QVariantList expandToParam = parts.value( u
"expandTo"_s ).toList();
607 if ( !expandToParam.isEmpty() )
609 QStringList expandToStringList;
610 for (
const QVariant &expansion : expandToParam )
613 if ( !expansionDefinition.
isValid() )
616 expandToStringList.append( expansionDefinition.
toString() );
618 if ( !expandToStringList.isEmpty() )
620 dsUri.
setParam( u
"expandTo"_s, expandToStringList.join(
';' ) );
625 const int maxPageSizeParam = parts.value( u
"pageSize"_s ).toInt( &ok );
628 dsUri.
setParam( u
"pageSize"_s, QString::number( maxPageSizeParam ) );
632 const int featureLimitParam = parts.value( u
"featureLimit"_s ).toInt( &ok );
635 dsUri.
setParam( u
"featureLimit"_s, QString::number( featureLimitParam ) );
638 const QString geometryType = parts.value( u
"geometryType"_s ).toString();
639 if ( geometryType.compare(
"point"_L1, Qt::CaseInsensitive ) == 0 )
643 else if ( geometryType.compare(
"multipoint"_L1, Qt::CaseInsensitive ) == 0 )
647 else if ( geometryType.compare(
"line"_L1, Qt::CaseInsensitive ) == 0 )
651 else if ( geometryType.compare(
"polygon"_L1, Qt::CaseInsensitive ) == 0 )
656 if ( parts.contains( u
"bounds"_s ) && parts.value( u
"bounds"_s ).userType() == qMetaTypeId<QgsRectangle>() )
662 if ( !parts.value( u
"sql"_s ).toString().isEmpty() )
663 dsUri.
setSql( parts.value( u
"sql"_s ).toString() );
665 return dsUri.
uri(
false );
670 return new QgsSensorThingsProvider( uri, options, flags );
673QList<Qgis::LayerType> QgsSensorThingsProviderMetadata::supportedLayerTypes()
const
678QMap<QString, QgsAbstractProviderConnection *> QgsSensorThingsProviderMetadata::connections(
bool cached )
680 return connectionsProtected<QgsSensorThingsProviderConnection, QgsSensorThingsProviderConnection>( cached );
688void QgsSensorThingsProviderMetadata::deleteConnection(
const QString &name )
690 deleteConnectionProtected<QgsSensorThingsProviderConnection>( name );
695 saveConnectionProtected( connection, name );
@ SelectAtId
Fast access to features using their ID.
@ ReloadData
Provider is able to force reload data.
@ ReadLayerMetadata
Provider can read layer metadata from data store. Since QGIS 3.0. See QgsDataProvider::layerMetadata(...
QFlags< DataProviderFlag > DataProviderFlags
Data provider flags.
@ FastExtent2D
Provider's 2D extent retrieval via QgsDataProvider::extent() is always guaranteed to be trivial/fast ...
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 ...
@ Sampling
The Sampling is the act of taking one or more Samples. The Sampling takes Samples from a SampledFeatu...
@ DatastreamRelation
A DatastreamRelation Entity relates a source Datastream to a target Datastream, or to an external res...
@ Feature
A Feature is an abstraction of real-world phenomena. It acts as an independent entity that can repres...
@ FeatureRelation
A FeatureRelation Entity relates a source Feature to a target Feature, or to an external resource,...
@ ObservedProperty
An ObservedProperty specifies the phenomenon of an Observation.
@ Invalid
An invalid/unknown entity.
@ Sampler
The Sampler describes the machine, device, human or other entity that executed the sampling procedure...
@ PreparationStep
When applying a PreparationProcdedure to a Sample, the process is recorded in individual PreparationS...
@ RelationRole
The RelationRole Entity holds a name and definition for both directions of the relation....
@ SamplingProcedure
The SamplingProcedure describes the method, or procedure, that the Sampler uses to create Samples....
@ ObservationRelation
A ObservationRelation Entity relates a source Observation to a target Observation,...
@ 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 ...
@ PreparationProcedure
After a sample is taken, a preparation procedure can be applied to it. The difference with the sampli...
@ Observation
An Observation is the act of measuring or otherwise determining the value of a property.
@ ObservingProcedure
An Observing Procedure. Implemented in the "Sensing Extension (Observations & Measurements)".
@ Location
A Location entity locates the Thing or the Things it associated with. A Thing’s Location entity is de...
@ ThingRelation
A ThingRelation Entity relates a source Thing to a target Thing, or to an external resource,...
@ FeatureType
A FeatureType provides the classification and schema definition for a Feature, describing the common ...
@ Thing
A Thing is an object of the physical world (physical things) or the information world (virtual things...
@ Deployment
A Deployment is the association of a Sensor to a Thing that hosts this Sensor, and to the Datastreams...
@ HistoricalLocation
A Thing’s HistoricalLocation entity set provides the times of the current (i.e., last known) and prev...
QFlags< DataProviderReadFlag > DataProviderReadFlags
Flags which control data provider construction.
QFlags< VectorProviderCapability > VectorProviderCapabilities
Vector data provider capabilities.
@ SkipFeatureCount
Make featureCount() return -1 to indicate unknown, and subLayers() to return a unknown feature count ...
WkbType
The WKB type describes the number of dimensions a geometry has.
@ MultiPointZ
MultiPointZ.
@ MultiLineStringZ
MultiLineStringZ.
@ MultiPolygonZ
MultiPolygonZ.
Base class that can be used for any class that is capable of returning features.
An interface for data provider connections.
static QIcon getThemeIcon(const QString &name, const QColor &fillColor=QColor(), const QColor &strokeColor=QColor())
Helper to get a theme icon.
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.
@ NetworkError
A network error occurred.
@ ServerExceptionError
An exception was raised by the server.
@ NoError
No error was encountered.
@ TimeoutError
Timeout was reached before a reply was received.
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).
virtual void setDataSourceUri(const QString &uri)
Set the data source specification.
Stores the component parts of a data source URI (e.g.
void setSql(const QString &sql)
Sets the sql filter for the URI.
void setAuthConfigId(const QString &authcfg)
Sets the authentication configuration ID for the URI.
QString uri(bool expandAuthConfig=true) const
Returns the complete URI as a string.
void setUsername(const QString &username)
Sets the username for the URI.
QString param(const QString &key) const
Returns a generic parameter value corresponding to the specified key.
QString username() const
Returns the username stored in the URI.
Qgis::WkbType wkbType() const
Returns the WKB type associated with the URI.
void setWkbType(Qgis::WkbType type)
Sets the WKB type associated with the URI.
void setParam(const QString &key, const QString &value)
Sets a generic parameter value on the URI.
QString password() const
Returns the password stored in the URI.
QString authConfigId() const
Returns any associated authentication configuration ID stored in the URI.
QString sql() const
Returns the SQL filter stored in the URI, if set.
void setPassword(const QString &password)
Sets the password for the URI.
Represents a single error message.
Wrapper for iterator of features from vector data provider or vector layer.
Wraps a request for features to a vector layer (or directly its vector data provider).
Container of fields for a vector layer.
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::MessageLevel::Warning, bool notifyUser=true, const char *file=__builtin_FILE(), const char *function=__builtin_FUNCTION(), int line=__builtin_LINE(), Qgis::StringFormat format=Qgis::StringFormat::PlainText)
Adds a message to the log instance (and creates it if necessary).
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.
void setYMinimum(double y)
Set the minimum y value.
void setXMinimum(double x)
Set the minimum x value.
void setYMaximum(double y)
Set the maximum y value.
void setXMaximum(double x)
Set the maximum x value.
Encapsulates information about how relationships in a SensorThings API service should be expanded.
static QgsSensorThingsExpansionDefinition fromString(const QString &string)
Returns a QgsSensorThingsExpansionDefinition from a string representation.
QString toString() const
Returns a string encapsulation of the expansion definition.
bool isValid() const
Returns true if the definition is valid.
Represents connections to SensorThings data sources.
static Qgis::SensorThingsEntity stringToEntity(const QString &type)
Converts a string value to a Qgis::SensorThingsEntity type.
static Qgis::SensorThingsEntity entitySetStringToEntity(const QString &type)
Converts a string value corresponding to a SensorThings entity set to a Qgis::SensorThingsEntity type...
Base class for vector data providers.
static Qgis::GeometryType geometryType(Qgis::WkbType type)
Returns the geometry type for a WKB type, e.g., both MultiPolygon and CurvePolygon would have a Polyg...
static Q_INVOKABLE bool isMultiType(Qgis::WkbType type)
Returns true if the WKB type is a multi type.
@ UnknownCount
Provider returned an unknown feature count.
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
QString qgsEnumValueToKey(const T &value, bool *returnOk=nullptr)
Returns the value for the given key of an enum.
#define QgsSetRequestInitiatorClass(request, _class)
#define QGIS_PROTECT_QOBJECT_THREAD_ACCESS
Setting options for creating vector data providers.