QGIS API Documentation 3.36.0-Maidenhead (09951dc0acf)
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
qgssensorthingsprovider.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgssensorthingsprovider.cpp
3 ----------------
4 begin : November 2023
5 copyright : (C) 2013 Nyall Dawson
6 email : nyall dot dawson at gmail dot com
7 ***************************************************************************/
8
9/***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
17
20#include "qgsapplication.h"
23#include "qgsthreadingutils.h"
24#include "qgsreadwritelocker.h"
28
29#include <QIcon>
30#include <QNetworkRequest>
31#include <nlohmann/json.hpp>
32
34
35QgsSensorThingsProvider::QgsSensorThingsProvider( const QString &uri, const ProviderOptions &options, QgsDataProvider::ReadFlags flags )
36 : QgsVectorDataProvider( uri, options, flags )
37{
38 mSharedData = std::make_shared< QgsSensorThingsSharedData >( uri );
39
40 const QUrl url( QgsSensorThingsSharedData::parseUrl( mSharedData->mRootUri ) );
41
42 QNetworkRequest request = QNetworkRequest( url );
43 QgsSetRequestInitiatorClass( request, QStringLiteral( "QgsSensorThingsProvider" ) )
44 mSharedData->mHeaders.updateNetworkRequest( request );
45
46 QgsBlockingNetworkRequest networkRequest;
47 networkRequest.setAuthCfg( mSharedData->mAuthCfg );
48
49 switch ( networkRequest.get( request ) )
50 {
52 break;
53
57 appendError( QgsErrorMessage( tr( "Connection failed: %1" ).arg( networkRequest.errorMessage() ), QStringLiteral( "SensorThings" ) ) );
58 return;
59 }
60
61 const QgsNetworkReplyContent content = networkRequest.reply();
62
63 try
64 {
65 auto rootContent = json::parse( content.content().toStdString() );
66 if ( !rootContent.contains( "value" ) )
67 {
68 appendError( QgsErrorMessage( tr( "No 'value' array in response" ), QStringLiteral( "SensorThings" ) ) );
69 return;
70 }
71
72 bool foundMatchingEntity = false;
73 for ( const auto &valueJson : rootContent["value"] )
74 {
75 if ( valueJson.contains( "name" ) && valueJson.contains( "url" ) )
76 {
77 const QString name = QString::fromStdString( valueJson["name"].get<std::string>() );
79 if ( entityType == mSharedData->mEntityType )
80 {
81 const QString url = QString::fromStdString( valueJson["url"].get<std::string>() );
82 if ( !url.isEmpty() )
83 {
84 foundMatchingEntity = true;
85 mSharedData->mEntityBaseUri = url;
86
87 // TODO:
88 // if we always retrieve feature count, is that less expensive then deferring this till we need it?
89 // by retrieving upfront, we can save a lot of requests where we've fetched features from spatial extents
90 // as we'll have a way of determining whether we've fetched all features from the source. Otherwise
91 // we never know if we've got everything yet, and are forced to re-fetched everything when a non-filtered request
92 // comes in...
93 ( void ) mSharedData->featureCount();
94 }
95 }
96 }
97 }
98
99 if ( !foundMatchingEntity )
100 {
101 appendError( QgsErrorMessage( tr( "Could not find url for %1" ).arg( qgsEnumValueToKey( mSharedData->mEntityType ) ), QStringLiteral( "SensorThings" ) ) );
102 return;
103 }
104 }
105 catch ( const json::parse_error &ex )
106 {
107 appendError( QgsErrorMessage( tr( "Error parsing response: %1" ).arg( ex.what() ), QStringLiteral( "SensorThings" ) ) );
108 return;
109 }
110
111 mValid = true;
112}
113
114QString QgsSensorThingsProvider::storageType() const
115{
117
118 return QStringLiteral( "OGC SensorThings API" );
119}
120
121QgsAbstractFeatureSource *QgsSensorThingsProvider::featureSource() const
122{
124
125 return new QgsSensorThingsFeatureSource( mSharedData );
126}
127
128QgsFeatureIterator QgsSensorThingsProvider::getFeatures( const QgsFeatureRequest &request ) const
129{
131
132 return new QgsSensorThingsFeatureIterator( new QgsSensorThingsFeatureSource( mSharedData ), true, request );
133}
134
135Qgis::WkbType QgsSensorThingsProvider::wkbType() const
136{
138
139 return mSharedData->mGeometryType;
140}
141
142long long QgsSensorThingsProvider::featureCount() const
143{
145
146 if ( ( mReadFlags & QgsDataProvider::SkipFeatureCount ) != 0 )
147 {
148 return static_cast< long long >( Qgis::FeatureCountState::UnknownCount );
149 }
150
151 const long long count = mSharedData->featureCount();
152 if ( !mSharedData->error().isEmpty() )
153 pushError( mSharedData->error() );
154
155 return count;
156}
157
158QgsFields QgsSensorThingsProvider::fields() const
159{
161
162 return mSharedData->mFields;
163}
164
165QgsLayerMetadata QgsSensorThingsProvider::layerMetadata() const
166{
168
169 return mLayerMetadata;
170}
171
172QString QgsSensorThingsProvider::htmlMetadata() const
173{
175
176 QString metadata;
177
178 QgsReadWriteLocker locker( mSharedData->mReadWriteLock, QgsReadWriteLocker::Read );
179
180 metadata += QStringLiteral( "<tr><td class=\"highlight\">" ) % tr( "Entity Type" ) % QStringLiteral( "</td><td>%1" ).arg( qgsEnumValueToKey( mSharedData->mEntityType ) ) % QStringLiteral( "</td></tr>\n" );
181 metadata += QStringLiteral( "<tr><td class=\"highlight\">" ) % tr( "Endpoint" ) % QStringLiteral( "</td><td><a href=\"%1\">%1</a>" ).arg( mSharedData->mEntityBaseUri ) % QStringLiteral( "</td></tr>\n" );
182
183 return metadata;
184}
185
186QgsVectorDataProvider::Capabilities QgsSensorThingsProvider::capabilities() const
187{
189
190 QgsVectorDataProvider::Capabilities c = QgsVectorDataProvider::SelectAtId
193
194 return c;
195}
196
197void QgsSensorThingsProvider::setDataSourceUri( const QString &uri )
198{
199 mSharedData = std::make_shared< QgsSensorThingsSharedData >( uri );
201}
202
203QgsCoordinateReferenceSystem QgsSensorThingsProvider::crs() const
204{
206
207 return mSharedData->mSourceCRS;
208}
209
210QgsRectangle QgsSensorThingsProvider::extent() const
211{
213
214#if 0
215 return mSharedData->extent();
216#endif
217
218 return QgsRectangle();
219}
220
221QString QgsSensorThingsProvider::name() const
222{
224
225 return SENSORTHINGS_PROVIDER_KEY;
226}
227
228QString QgsSensorThingsProvider::providerKey()
229{
230 return SENSORTHINGS_PROVIDER_KEY;
231}
232
233void QgsSensorThingsProvider::handlePostCloneOperations( QgsVectorDataProvider *source )
234{
235 mSharedData = qobject_cast<QgsSensorThingsProvider *>( source )->mSharedData;
236}
237
238QString QgsSensorThingsProvider::description() const
239{
240 return SENSORTHINGS_PROVIDER_DESCRIPTION;
241}
242
243void QgsSensorThingsProvider::reloadProviderData()
244{
245#if 0
246 mSharedData->clearCache();
247#endif
248}
249
250//
251// QgsSensorThingsProviderMetadata
252//
253
254QgsSensorThingsProviderMetadata::QgsSensorThingsProviderMetadata():
255 QgsProviderMetadata( QgsSensorThingsProvider::SENSORTHINGS_PROVIDER_KEY, QgsSensorThingsProvider::SENSORTHINGS_PROVIDER_DESCRIPTION )
256{
257}
258
259QIcon QgsSensorThingsProviderMetadata::icon() const
260{
261 // TODO
262 return QgsApplication::getThemeIcon( QStringLiteral( "mIconAfs.svg" ) );
263}
264
265QList<QgsDataItemProvider *> QgsSensorThingsProviderMetadata::dataItemProviders() const
266{
267 return { new QgsSensorThingsDataItemProvider() };
268}
269
270QVariantMap QgsSensorThingsProviderMetadata::decodeUri( const QString &uri ) const
271{
272 const QgsDataSourceUri dsUri = QgsDataSourceUri( uri );
273
274 QVariantMap components;
275 components.insert( QStringLiteral( "url" ), dsUri.param( QStringLiteral( "url" ) ) );
276
277 if ( !dsUri.authConfigId().isEmpty() )
278 {
279 components.insert( QStringLiteral( "authcfg" ), dsUri.authConfigId() );
280 }
281 if ( !dsUri.username().isEmpty() )
282 {
283 components.insert( QStringLiteral( "username" ), dsUri.username() );
284 }
285 if ( !dsUri.password().isEmpty() )
286 {
287 components.insert( QStringLiteral( "password" ), dsUri.password() );
288 }
289
290 // there's two different ways the referer can be set, so we need to check both. Which way it has been
291 // set depends on the widget used to create the URI. It's messy, but QgsHttpHeaders has a bunch of logic in
292 // it to handle upgrading old referer handling for connections created before QgsHttpHeaders was invented,
293 // and if we rely on that entirely then we get multiple "referer" parameters included in the URI, which is
294 // both ugly and unnecessary for a provider created post QgsHttpHeaders.
295 if ( !dsUri.param( QStringLiteral( "referer" ) ).isEmpty() )
296 {
297 components.insert( QStringLiteral( "referer" ), dsUri.param( QStringLiteral( "referer" ) ) );
298 }
299 if ( !dsUri.param( QStringLiteral( "http-header:referer" ) ).isEmpty() )
300 {
301 components.insert( QStringLiteral( "referer" ), dsUri.param( QStringLiteral( "http-header:referer" ) ) );
302 }
303
304 const QString entityParam = dsUri.param( QStringLiteral( "entity" ) );
306 if ( entity == Qgis::SensorThingsEntity::Invalid )
307 entity = QgsSensorThingsUtils::stringToEntity( entityParam );
308
309 if ( entity != Qgis::SensorThingsEntity::Invalid )
310 components.insert( QStringLiteral( "entity" ), qgsEnumValueToKey( entity ) );
311
312 bool ok = false;
313 const int maxPageSizeParam = dsUri.param( QStringLiteral( "pageSize" ) ).toInt( &ok );
314 if ( ok )
315 {
316 components.insert( QStringLiteral( "pageSize" ), maxPageSizeParam );
317 }
318
319 switch ( QgsWkbTypes::geometryType( dsUri.wkbType() ) )
320 {
322 if ( QgsWkbTypes::isMultiType( dsUri.wkbType() ) )
323 components.insert( QStringLiteral( "geometryType" ), QStringLiteral( "multipoint" ) );
324 else
325 components.insert( QStringLiteral( "geometryType" ), QStringLiteral( "point" ) );
326 break;
328 components.insert( QStringLiteral( "geometryType" ), QStringLiteral( "line" ) );
329 break;
331 components.insert( QStringLiteral( "geometryType" ), QStringLiteral( "polygon" ) );
332 break;
333
336 break;
337 }
338
339 return components;
340}
341
342QString QgsSensorThingsProviderMetadata::encodeUri( const QVariantMap &parts ) const
343{
344 QgsDataSourceUri dsUri;
345 dsUri.setParam( QStringLiteral( "url" ), parts.value( QStringLiteral( "url" ) ).toString() );
346
347 if ( !parts.value( QStringLiteral( "authcfg" ) ).toString().isEmpty() )
348 {
349 dsUri.setAuthConfigId( parts.value( QStringLiteral( "authcfg" ) ).toString() );
350 }
351 if ( !parts.value( QStringLiteral( "username" ) ).toString().isEmpty() )
352 {
353 dsUri.setUsername( parts.value( QStringLiteral( "username" ) ).toString() );
354 }
355 if ( !parts.value( QStringLiteral( "password" ) ).toString().isEmpty() )
356 {
357 dsUri.setPassword( parts.value( QStringLiteral( "password" ) ).toString() );
358 }
359 if ( !parts.value( QStringLiteral( "referer" ) ).toString().isEmpty() )
360 {
361 dsUri.setParam( QStringLiteral( "referer" ), parts.value( QStringLiteral( "referer" ) ).toString() );
362 }
363
365 parts.value( QStringLiteral( "entity" ) ).toString() );
366 if ( entity == Qgis::SensorThingsEntity::Invalid )
367 entity = QgsSensorThingsUtils::stringToEntity( parts.value( QStringLiteral( "entity" ) ).toString() );
368
369 if ( entity != Qgis::SensorThingsEntity::Invalid )
370 {
371 dsUri.setParam( QStringLiteral( "entity" ),
372 qgsEnumValueToKey( entity ) );
373 }
374
375 bool ok = false;
376 const int maxPageSizeParam = parts.value( QStringLiteral( "pageSize" ) ).toInt( &ok );
377 if ( ok )
378 {
379 dsUri.setParam( QStringLiteral( "pageSize" ), QString::number( maxPageSizeParam ) );
380 }
381
382 const QString geometryType = parts.value( QStringLiteral( "geometryType" ) ).toString();
383 if ( geometryType.compare( QLatin1String( "point" ), Qt::CaseInsensitive ) == 0 )
384 {
386 }
387 else if ( geometryType.compare( QLatin1String( "multipoint" ), Qt::CaseInsensitive ) == 0 )
388 {
390 }
391 else if ( geometryType.compare( QLatin1String( "line" ), Qt::CaseInsensitive ) == 0 )
392 {
394 }
395 else if ( geometryType.compare( QLatin1String( "polygon" ), Qt::CaseInsensitive ) == 0 )
396 {
398 }
399
400 return dsUri.uri( false );
401}
402
403QgsSensorThingsProvider *QgsSensorThingsProviderMetadata::createProvider( const QString &uri, const QgsDataProvider::ProviderOptions &options, QgsDataProvider::ReadFlags flags )
404{
405 return new QgsSensorThingsProvider( uri, options, flags );
406}
407
408QList<Qgis::LayerType> QgsSensorThingsProviderMetadata::supportedLayerTypes() const
409{
410 return { Qgis::LayerType::Vector };
411}
412
413QMap<QString, QgsAbstractProviderConnection *> QgsSensorThingsProviderMetadata::connections( bool cached )
414{
415 return connectionsProtected<QgsSensorThingsProviderConnection, QgsSensorThingsProviderConnection>( cached );
416}
417
418QgsAbstractProviderConnection *QgsSensorThingsProviderMetadata::createConnection( const QString &name )
419{
420 return new QgsSensorThingsProviderConnection( name );
421}
422
423void QgsSensorThingsProviderMetadata::deleteConnection( const QString &name )
424{
425 deleteConnectionProtected<QgsSensorThingsProviderConnection>( name );
426}
427
428void QgsSensorThingsProviderMetadata::saveConnection( const QgsAbstractProviderConnection *connection, const QString &name )
429{
430 saveConnectionProtected( connection, name );
431}
432
SensorThingsEntity
OGC SensorThings API entity types.
Definition qgis.h:4803
@ Invalid
An invalid/unknown entity.
@ Polygon
Polygons.
@ Unknown
Unknown types.
@ Null
No geometry.
@ Vector
Vector layer.
WkbType
The WKB type describes the number of dimensions a geometry has.
Definition qgis.h:182
@ MultiPointZ
MultiPointZ.
@ PointZ
PointZ.
@ MultiLineStringZ
MultiLineStringZ.
@ MultiPolygonZ
MultiPolygonZ.
Base class that can be used for any class that is capable of returning features.
The QgsAbstractProviderConnection provides 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...
QString errorMessage() const
Returns the error message string, after a get(), post(), head() or put() request has been made.
@ 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...
This class represents a coordinate reference system (CRS).
@ SkipFeatureCount
Make featureCount() return -1 to indicate unknown, and subLayers() to return a unknown feature count ...
virtual void setDataSourceUri(const QString &uri)
Set the data source specification.
Class for storing the component parts of a RDBMS data source URI (e.g.
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.
void setPassword(const QString &password)
Sets the password for the URI.
QgsErrorMessage represents single error message.
Definition qgserror.h:33
Wrapper for iterator of features from vector data provider or vector layer.
This class wraps a request for features to a vector layer (or directly its vector data provider).
Container of fields for a vector layer.
Definition qgsfields.h:45
A structured metadata store for a map layer.
Encapsulates a network reply within a container which is inexpensive to copy and safe to pass between...
QByteArray content() const
Returns the reply content.
Holds data provider key, description, and associated shared library file or function pointer informat...
The QgsReadWriteLocker class is a convenience class that simplifies locking and unlocking QReadWriteL...
A rectangle specified with double values.
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...
This is the base class for vector data providers.
@ ReadLayerMetadata
Provider can read layer metadata from data store. Since QGIS 3.0. See QgsDataProvider::layerMetadata(...
@ SelectAtId
Fast access to features using their ID.
@ ReloadData
Provider is able to force reload data.
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 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.
Definition qgis.h:5335
#define QgsSetRequestInitiatorClass(request, _class)
#define QGIS_PROTECT_QOBJECT_THREAD_ACCESS
Setting options for creating vector data providers.