QGIS API Documentation 4.1.0-Master (3fcefe620d1)
Loading...
Searching...
No Matches
qgssensorthingsutils.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgssensorthingsutils.cpp
3 --------------------
4 begin : November 2023
5 copyright : (C) 2023 by Nyall Dawson
6 email : nyall dot dawson at gmail dot com
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15
17
18#include <nlohmann/json.hpp>
19
21#include "qgsfield.h"
22#include "qgsfields.h"
23#include "qgslogger.h"
25#include "qgsrectangle.h"
27#include "qgswkbtypes.h"
28
29#include <QNetworkRequest>
30#include <QRegularExpression>
31#include <QRegularExpressionMatch>
32#include <QString>
33#include <QUrl>
34
35using namespace Qt::StringLiterals;
36
37//
38// QgsSensorThingsExpansionDefinition
39//
41 : mChildEntity( childEntity )
42 , mOrderBy( orderBy )
43 , mSortOrder( sortOrder )
44 , mLimit( limit )
45 , mFilter( filter )
46{}
47
49{
50 switch ( entity )
51 {
54
74
75 // no special defaults for these entities
77
79 // default to descending sort by phenomenonTime
80 return QgsSensorThingsExpansionDefinition( Qgis::SensorThingsEntity::Observation, u"phenomenonTime"_s, Qt::SortOrder::DescendingOrder );
81
85 // use smaller limit by default
86 return QgsSensorThingsExpansionDefinition( entity, QString(), Qt::SortOrder::AscendingOrder, 10 );
87 }
89}
90
95
100
102{
103 mChildEntity = entity;
104}
105
107{
108 return mSortOrder;
109}
110
112{
113 mSortOrder = order;
114}
115
117{
118 return mLimit;
119}
120
122{
123 mLimit = limit;
124}
125
127{
128 return mFilter;
129}
130
132{
133 mFilter = filter;
134}
135
137{
138 if ( !isValid() )
139 return QString();
140
141 QStringList parts;
142 parts.append( qgsEnumValueToKey( mChildEntity ) );
143 if ( !mOrderBy.isEmpty() )
144 parts.append( u"orderby=%1,%2"_s.arg( mOrderBy, mSortOrder == Qt::SortOrder::AscendingOrder ? u"asc"_s : u"desc"_s ) );
145 if ( mLimit >= 0 )
146 parts.append( u"limit=%1"_s.arg( mLimit ) );
147 if ( !mFilter.trimmed().isEmpty() )
148 {
149 QString escapedFilter = mFilter;
150 escapedFilter.replace( ':', "\\colon"_L1 );
151 parts.append( u"filter=%1"_s.arg( escapedFilter ) );
152 }
153 return parts.join( ':' );
154}
155
157{
158 const QStringList parts = string.split( ':', Qt::SkipEmptyParts );
159 if ( parts.empty() )
161
163 definition.setLimit( -1 );
164 for ( int i = 1; i < parts.count(); ++i )
165 {
166 const QString &part = parts.at( i );
167 const thread_local QRegularExpression orderByRegEx( u"^orderby=(.*),(.*?)$"_s );
168 const thread_local QRegularExpression orderLimitRegEx( u"^limit=(\\d+)$"_s );
169 const thread_local QRegularExpression filterRegEx( u"^filter=(.*)$"_s );
170
171 const QRegularExpressionMatch orderByMatch = orderByRegEx.match( part );
172 if ( orderByMatch.hasMatch() )
173 {
174 definition.setOrderBy( orderByMatch.captured( 1 ) );
175 definition.setSortOrder( orderByMatch.captured( 2 ) == "asc"_L1 ? Qt::SortOrder::AscendingOrder : Qt::SortOrder::DescendingOrder );
176 continue;
177 }
178
179 const QRegularExpressionMatch limitMatch = orderLimitRegEx.match( part );
180 if ( limitMatch.hasMatch() )
181 {
182 definition.setLimit( limitMatch.captured( 1 ).toInt() );
183 continue;
184 }
185
186 const QRegularExpressionMatch filterMatch = filterRegEx.match( part );
187 if ( filterMatch.hasMatch() )
188 {
189 QString filter = filterMatch.captured( 1 );
190 filter.replace( "\\colon"_L1, ":"_L1 );
191 definition.setFilter( filter );
192 continue;
193 }
194 }
195 return definition;
196}
197
198QString QgsSensorThingsExpansionDefinition::asQueryString( Qgis::SensorThingsEntity parentEntityType, const QStringList &additionalOptions ) const
199{
200 if ( !isValid() )
201 return QString();
202
203 bool ok = false;
204 // From the specifications, it SOMETIMES the plural form is used for the expansion query, and sometimes singular.
205 // The choice depends on the cardinality of the relationship between the involved entities.
206 const Qgis::RelationshipCardinality cardinality = QgsSensorThingsUtils::relationshipCardinality( parentEntityType, mChildEntity, ok );
207 QString childEntityString;
208 if ( !ok )
209 {
210 childEntityString = QgsSensorThingsUtils::entityToSetString( mChildEntity );
211 }
212 else
213 {
214 switch ( cardinality )
215 {
218 // use singular strings, eg "Thing"
219 childEntityString = qgsEnumValueToKey( mChildEntity );
220 break;
221
224 // use plural strings, eg "Things"
225 childEntityString = QgsSensorThingsUtils::entityToSetString( mChildEntity );
226 break;
227 }
228 }
229
230 QString res = u"$expand=%1"_s.arg( childEntityString );
231
232 QStringList queryOptions;
233 if ( !mOrderBy.isEmpty() )
234 queryOptions.append( u"$orderby=%1%2"_s.arg( mOrderBy, mSortOrder == Qt::SortOrder::AscendingOrder ? QString() : u" desc"_s ) );
235
236 if ( mLimit > -1 )
237 queryOptions.append( u"$top=%1"_s.arg( mLimit ) );
238
239 if ( !mFilter.isEmpty() )
240 queryOptions.append( u"$filter=%1"_s.arg( mFilter ) );
241
242 queryOptions.append( additionalOptions );
243
244 if ( !queryOptions.isEmpty() )
245 res.append( u"(%1)"_s.arg( queryOptions.join( ';' ) ) );
246
247 return res;
248}
249
251{
252 if ( mChildEntity == Qgis::SensorThingsEntity::Invalid )
253 return other.mChildEntity == Qgis::SensorThingsEntity::Invalid;
254
255 return mChildEntity == other.mChildEntity && mSortOrder == other.mSortOrder && mLimit == other.mLimit && mOrderBy == other.mOrderBy && mFilter == other.mFilter;
256}
257
259{
260 return !( *this == other );
261}
262
264{
265 return mOrderBy;
266}
267
269{
270 mOrderBy = field;
271}
272
273//
274// QgsSensorThingsUtils
275//
276
278{
279 const QString trimmed = type.trimmed();
280 if ( trimmed.compare( "Thing"_L1, Qt::CaseInsensitive ) == 0 )
282 if ( trimmed.compare( "Location"_L1, Qt::CaseInsensitive ) == 0 )
284 if ( trimmed.compare( "HistoricalLocation"_L1, Qt::CaseInsensitive ) == 0 )
286 if ( trimmed.compare( "Datastream"_L1, Qt::CaseInsensitive ) == 0 )
288 if ( trimmed.compare( "Sensor"_L1, Qt::CaseInsensitive ) == 0 )
290 if ( trimmed.compare( "ObservedProperty"_L1, Qt::CaseInsensitive ) == 0 )
292 if ( trimmed.compare( "Observation"_L1, Qt::CaseInsensitive ) == 0 )
294 if ( trimmed.compare( "FeatureOfInterest"_L1, Qt::CaseInsensitive ) == 0 )
296 if ( trimmed.compare( "MultiDatastream"_L1, Qt::CaseInsensitive ) == 0 )
298 if ( trimmed.compare( "Feature"_L1, Qt::CaseInsensitive ) == 0 )
300 if ( trimmed.compare( "FeatureType"_L1, Qt::CaseInsensitive ) == 0 )
302 if ( trimmed.compare( "Deployment"_L1, Qt::CaseInsensitive ) == 0 )
304 if ( trimmed.compare( "ObservingProcedure"_L1, Qt::CaseInsensitive ) == 0 )
306 if ( trimmed.compare( "Sampling"_L1, Qt::CaseInsensitive ) == 0 )
308 if ( trimmed.compare( "SamplingProcedure"_L1, Qt::CaseInsensitive ) == 0 )
310 if ( trimmed.compare( "Sampler"_L1, Qt::CaseInsensitive ) == 0 )
312 if ( trimmed.compare( "PreparationStep"_L1, Qt::CaseInsensitive ) == 0 )
314 if ( trimmed.compare( "PreparationProcedure"_L1, Qt::CaseInsensitive ) == 0 )
316 if ( trimmed.compare( "ThingRelation"_L1, Qt::CaseInsensitive ) == 0 )
318 if ( trimmed.compare( "RelationRole"_L1, Qt::CaseInsensitive ) == 0 )
320 if ( trimmed.compare( "FeatureRelation"_L1, Qt::CaseInsensitive ) == 0 )
322 if ( trimmed.compare( "DatastreamRelation"_L1, Qt::CaseInsensitive ) == 0 )
324 if ( trimmed.compare( "ObservationRelation"_L1, Qt::CaseInsensitive ) == 0 )
326
328}
329
331{
332 switch ( type )
333 {
335 return QString();
337 return plural ? QObject::tr( "Things" ) : QObject::tr( "Thing" );
339 return plural ? QObject::tr( "Locations" ) : QObject::tr( "Location" );
341 return plural ? QObject::tr( "Historical Locations" ) : QObject::tr( "Historical Location" );
343 return plural ? QObject::tr( "Datastreams" ) : QObject::tr( "Datastream" );
345 return plural ? QObject::tr( "Sensors" ) : QObject::tr( "Sensor" );
347 return plural ? QObject::tr( "Observed Properties" ) : QObject::tr( "Observed Property" );
349 return plural ? QObject::tr( "Observations" ) : QObject::tr( "Observation" );
351 return plural ? QObject::tr( "Features of Interest" ) : QObject::tr( "Feature of Interest" );
353 return plural ? QObject::tr( "MultiDatastreams" ) : QObject::tr( "MultiDatastream" );
355 return plural ? QObject::tr( "Features" ) : QObject::tr( "Feature" );
357 return plural ? QObject::tr( "Feature Types" ) : QObject::tr( "Feature Type" );
359 return plural ? QObject::tr( "Deployments" ) : QObject::tr( "Deployment" );
361 return plural ? QObject::tr( "Observing Procedures" ) : QObject::tr( "Observing Procedure" );
363 return plural ? QObject::tr( "Samplings" ) : QObject::tr( "Sampling" );
365 return plural ? QObject::tr( "Sampling Procedures" ) : QObject::tr( "Sampling Procedure" );
367 return plural ? QObject::tr( "Samplers" ) : QObject::tr( "Sampler" );
369 return plural ? QObject::tr( "Preparation Steps" ) : QObject::tr( "Preparation Step" );
371 return plural ? QObject::tr( "Preparation Procedures" ) : QObject::tr( "Preparation Procedure" );
373 return plural ? QObject::tr( "Thing Relations" ) : QObject::tr( "Thing Relation" );
375 return plural ? QObject::tr( "Relation Roles" ) : QObject::tr( "Relation Role" );
377 return plural ? QObject::tr( "Feature Relations" ) : QObject::tr( "Feature Relation" );
379 return plural ? QObject::tr( "Datastream Relations" ) : QObject::tr( "Datastream Relation" );
381 return plural ? QObject::tr( "Observation Relations" ) : QObject::tr( "Observation Relation" );
382 }
384}
385
387{
388 const QString trimmed = type.trimmed();
389 if ( trimmed.compare( "Things"_L1, Qt::CaseInsensitive ) == 0 )
391 if ( trimmed.compare( "Locations"_L1, Qt::CaseInsensitive ) == 0 )
393 if ( trimmed.compare( "HistoricalLocations"_L1, Qt::CaseInsensitive ) == 0 )
395 if ( trimmed.compare( "Datastreams"_L1, Qt::CaseInsensitive ) == 0 )
397 if ( trimmed.compare( "Sensors"_L1, Qt::CaseInsensitive ) == 0 )
399 if ( trimmed.compare( "ObservedProperties"_L1, Qt::CaseInsensitive ) == 0 )
401 if ( trimmed.compare( "Observations"_L1, Qt::CaseInsensitive ) == 0 )
403 if ( trimmed.compare( "FeaturesOfInterest"_L1, Qt::CaseInsensitive ) == 0 )
405 if ( trimmed.compare( "MultiDatastreams"_L1, Qt::CaseInsensitive ) == 0 )
407 if ( trimmed.compare( "Features"_L1, Qt::CaseInsensitive ) == 0 )
409 if ( trimmed.compare( "FeatureTypes"_L1, Qt::CaseInsensitive ) == 0 )
411 if ( trimmed.compare( "Deployments"_L1, Qt::CaseInsensitive ) == 0 )
413 if ( trimmed.compare( "ObservingProcedures"_L1, Qt::CaseInsensitive ) == 0 )
415 if ( trimmed.compare( "Samplings"_L1, Qt::CaseInsensitive ) == 0 ) // confirm?
417 if ( trimmed.compare( "SamplingProcedures"_L1, Qt::CaseInsensitive ) == 0 )
419 if ( trimmed.compare( "Samplers"_L1, Qt::CaseInsensitive ) == 0 )
421 if ( trimmed.compare( "PreparationSteps"_L1, Qt::CaseInsensitive ) == 0 )
423 if ( trimmed.compare( "PreparationProcedures"_L1, Qt::CaseInsensitive ) == 0 )
425 if ( trimmed.compare( "ThingRelations"_L1, Qt::CaseInsensitive ) == 0 )
427 if ( trimmed.compare( "RelationRoles"_L1, Qt::CaseInsensitive ) == 0 )
429 if ( trimmed.compare( "FeatureRelations"_L1, Qt::CaseInsensitive ) == 0 )
431 if ( trimmed.compare( "DatastreamRelations"_L1, Qt::CaseInsensitive ) == 0 )
433 if ( trimmed.compare( "ObservationRelations"_L1, Qt::CaseInsensitive ) == 0 )
435
437}
438
440{
441 switch ( type )
442 {
444 return QString();
446 return u"Things"_s;
448 return u"Locations"_s;
450 return u"HistoricalLocations"_s;
452 return u"Datastreams"_s;
454 return u"Sensors"_s;
456 return u"ObservedProperties"_s;
458 return u"Observations"_s;
460 return u"FeaturesOfInterest"_s;
462 return u"MultiDatastreams"_s;
464 return u"Features"_s;
466 return u"FeatureTypes"_s;
468 return u"Deployments"_s;
470 return u"ObservingProcedures"_s;
472 return u"Samplings"_s; // confirm?
474 return u"SamplingProcedures"_s;
476 return u"Samplers"_s;
478 return u"PreparationSteps"_s;
480 return u"PreparationProcedures"_s;
482 return u"ThingRelations"_s;
484 return u"RelationRoles"_s;
486 return u"FeatureRelations"_s;
488 return u"DatastreamRelations"_s;
490 return u"ObservationRelations"_s;
491 }
493}
494
496{
497 switch ( type )
498 {
500 return {};
501
503 // https://docs.ogc.org/is/18-088/18-088.html#thing
504 return {
505 u"id"_s,
506 u"selfLink"_s,
507 u"name"_s,
508 u"description"_s,
509 u"properties"_s,
510 };
511
513 // https://docs.ogc.org/is/18-088/18-088.html#location
514 return {
515 u"id"_s,
516 u"selfLink"_s,
517 u"name"_s,
518 u"description"_s,
519 u"properties"_s,
520 };
521
523 // https://docs.ogc.org/is/18-088/18-088.html#historicallocation
524 return {
525 u"id"_s,
526 u"selfLink"_s,
527 u"time"_s,
528 };
529
531 // https://docs.ogc.org/is/18-088/18-088.html#datastream
532 return {
533 u"id"_s,
534 u"selfLink"_s,
535 u"name"_s,
536 u"description"_s,
537 u"unitOfMeasurement"_s,
538 u"observationType"_s,
539 u"properties"_s,
540 u"phenomenonTime"_s,
541 u"resultTime"_s,
542 };
543
545 // https://docs.ogc.org/is/18-088/18-088.html#sensor
546 return {
547 u"id"_s,
548 u"selfLink"_s,
549 u"name"_s,
550 u"description"_s,
551 u"metadata"_s,
552 u"properties"_s,
553 };
554
556 // https://docs.ogc.org/is/18-088/18-088.html#observedproperty
557 return {
558 u"id"_s,
559 u"selfLink"_s,
560 u"name"_s,
561 u"definition"_s,
562 u"description"_s,
563 u"properties"_s,
564 };
565
567 // https://docs.ogc.org/is/18-088/18-088.html#observation
568 return {
569 u"id"_s,
570 u"selfLink"_s,
571 u"phenomenonTime"_s,
572 u"result"_s,
573 u"resultTime"_s,
574 u"resultQuality"_s,
575 u"validTime"_s,
576 u"parameters"_s,
577 };
578
580 // https://docs.ogc.org/is/18-088/18-088.html#featureofinterest
581 return {
582 u"id"_s,
583 u"selfLink"_s,
584 u"name"_s,
585 u"description"_s,
586 u"properties"_s,
587 };
588
590 // TODO -- link to final 2.0 definition
591 return {
592 u"id"_s,
593 u"selfLink"_s,
594 u"name"_s,
595 u"description"_s,
596 u"properties"_s,
597 };
598
600 // TODO -- link to final 2.0 definition
601 return {
602 u"id"_s,
603 u"selfLink"_s,
604 u"name"_s,
605 u"description"_s,
606 u"properties"_s,
607 };
608
610 // https://docs.ogc.org/is/18-088/18-088.html#multidatastream-extension
611 return {
612 u"id"_s,
613 u"selfLink"_s,
614 u"name"_s,
615 u"description"_s,
616 u"unitOfMeasurements"_s,
617 u"observationType"_s,
618 u"multiObservationDataTypes"_s,
619 u"properties"_s,
620 u"phenomenonTime"_s,
621 u"resultTime"_s,
622 };
623
625 // https://hylkevds.github.io/24-046/24-046.html#deployment
626 return {
627 u"id"_s,
628 u"name"_s,
629 u"description"_s,
630 u"properties"_s,
631 u"encodingType"_s,
632 u"position"_s,
633 u"time"_s,
634 };
635
637 // https://hylkevds.github.io/24-046/24-046.html#observingprocedure
638 return {
639 u"id"_s,
640 u"name"_s,
641 u"definition"_s,
642 u"description"_s,
643 u"properties"_s,
644 };
645
647 // https://hylkevds.github.io/24-046/24-046.html#sampling-extension
648 return {
649 u"id"_s,
650 u"name"_s,
651 u"definition"_s,
652 u"description"_s,
653 u"encodingType"_s,
654 u"properties"_s,
655 u"time"_s,
656 };
657
659 // https://hylkevds.github.io/24-046/24-046.html#sampling-extension
660 return {
661 u"id"_s,
662 u"name"_s,
663 u"definition"_s,
664 u"description"_s,
665 u"properties"_s,
666 };
667
669 // https://hylkevds.github.io/24-046/24-046.html#sampling-extension
670 return {
671 u"id"_s,
672 u"name"_s,
673 u"definition"_s,
674 u"description"_s,
675 u"properties"_s,
676 u"samplerType"_s,
677 };
678
680 // https://hylkevds.github.io/24-046/24-046.html#sampling-extension
681 return {
682 u"id"_s,
683 u"name"_s,
684 u"definition"_s,
685 u"description"_s,
686 u"properties"_s,
687 u"time"_s,
688 };
689
691 // https://hylkevds.github.io/24-046/24-046.html#sampling-extension
692 return {
693 u"id"_s,
694 u"name"_s,
695 u"definition"_s,
696 u"description"_s,
697 u"properties"_s,
698 };
699
701 // https://hylkevds.github.io/24-046/24-046.html#relations-extension
702 return { u"id"_s, u"externalTarget"_s };
703
705 // https://hylkevds.github.io/24-046/24-046.html#relations-extension
706 return { u"id"_s, u"externalTarget"_s };
707
709 // https://hylkevds.github.io/24-046/24-046.html#relations-extension
710 return { u"id"_s, u"externalTarget"_s };
711
713 // https://hylkevds.github.io/24-046/24-046.html#relations-extension
714 return { u"id"_s, u"externalTarget"_s };
715
717 // https://hylkevds.github.io/24-046/24-046.html#relations-extension
718 return {
719 u"id"_s,
720 u"name"_s,
721 u"definition"_s,
722 u"inverseName"_s,
723 u"inverseDefinition"_s,
724 u"description"_s,
725 u"properties"_s,
726 };
727 }
728
729 return {};
730}
731
733{
734 QgsFields fields;
735
736 // common fields: https://docs.ogc.org/is/18-088/18-088.html#common-control-information
737 fields.append( QgsField( u"id"_s, QMetaType::Type::QString ) );
738 fields.append( QgsField( u"selfLink"_s, QMetaType::Type::QString ) );
739
740 switch ( type )
741 {
743 break;
744
746 // https://docs.ogc.org/is/18-088/18-088.html#thing
747 fields.append( QgsField( u"name"_s, QMetaType::Type::QString ) );
748 fields.append( QgsField( u"description"_s, QMetaType::Type::QString ) );
749 fields.append( QgsField( u"properties"_s, QMetaType::Type::QVariantMap, u"json"_s, 0, 0, QString(), QMetaType::Type::QString ) );
750 break;
751
753 // https://docs.ogc.org/is/18-088/18-088.html#location
754 fields.append( QgsField( u"name"_s, QMetaType::Type::QString ) );
755 fields.append( QgsField( u"description"_s, QMetaType::Type::QString ) );
756 fields.append( QgsField( u"properties"_s, QMetaType::Type::QVariantMap, u"json"_s, 0, 0, QString(), QMetaType::Type::QString ) );
757 break;
758
760 // https://docs.ogc.org/is/18-088/18-088.html#historicallocation
761 fields.append( QgsField( u"time"_s, QMetaType::Type::QDateTime ) );
762 break;
763
765 // https://docs.ogc.org/is/18-088/18-088.html#datastream
766 fields.append( QgsField( u"name"_s, QMetaType::Type::QString ) );
767 fields.append( QgsField( u"description"_s, QMetaType::Type::QString ) );
768 fields.append( QgsField( u"unitOfMeasurement"_s, QMetaType::Type::QVariantMap, u"json"_s, 0, 0, QString(), QMetaType::Type::QString ) );
769 fields.append( QgsField( u"observationType"_s, QMetaType::Type::QString ) );
770 fields.append( QgsField( u"properties"_s, QMetaType::Type::QVariantMap, u"json"_s, 0, 0, QString(), QMetaType::Type::QString ) );
771 if ( includeRangeFieldProxies )
772 {
773 fields.append( QgsField( u"phenomenonTimeStart"_s, QMetaType::Type::QDateTime ) );
774 fields.append( QgsField( u"phenomenonTimeEnd"_s, QMetaType::Type::QDateTime ) );
775 fields.append( QgsField( u"resultTimeStart"_s, QMetaType::Type::QDateTime ) );
776 fields.append( QgsField( u"resultTimeEnd"_s, QMetaType::Type::QDateTime ) );
777 }
778 break;
779
781 // https://docs.ogc.org/is/18-088/18-088.html#sensor
782 fields.append( QgsField( u"name"_s, QMetaType::Type::QString ) );
783 fields.append( QgsField( u"description"_s, QMetaType::Type::QString ) );
784 fields.append( QgsField( u"metadata"_s, QMetaType::Type::QString ) );
785 fields.append( QgsField( u"properties"_s, QMetaType::Type::QVariantMap, u"json"_s, 0, 0, QString(), QMetaType::Type::QString ) );
786 break;
787
789 // https://docs.ogc.org/is/18-088/18-088.html#observedproperty
790 fields.append( QgsField( u"name"_s, QMetaType::Type::QString ) );
791 fields.append( QgsField( u"definition"_s, QMetaType::Type::QString ) );
792 fields.append( QgsField( u"description"_s, QMetaType::Type::QString ) );
793 fields.append( QgsField( u"properties"_s, QMetaType::Type::QVariantMap, u"json"_s, 0, 0, QString(), QMetaType::Type::QString ) );
794 break;
795
797 // https://docs.ogc.org/is/18-088/18-088.html#observation
798 if ( includeRangeFieldProxies )
799 {
800 fields.append( QgsField( u"phenomenonTimeStart"_s, QMetaType::Type::QDateTime ) );
801 fields.append( QgsField( u"phenomenonTimeEnd"_s, QMetaType::Type::QDateTime ) );
802 }
803
804 // TODO -- handle type correctly
805 fields.append( QgsField( u"result"_s, QMetaType::Type::QString ) );
806
807 fields.append( QgsField( u"resultTime"_s, QMetaType::Type::QDateTime ) );
808 fields.append( QgsField( u"resultQuality"_s, QMetaType::Type::QStringList, QString(), 0, 0, QString(), QMetaType::Type::QString ) );
809 if ( includeRangeFieldProxies )
810 {
811 fields.append( QgsField( u"validTimeStart"_s, QMetaType::Type::QDateTime ) );
812 fields.append( QgsField( u"validTimeEnd"_s, QMetaType::Type::QDateTime ) );
813 }
814 fields.append( QgsField( u"parameters"_s, QMetaType::Type::QVariantMap, u"json"_s, 0, 0, QString(), QMetaType::Type::QString ) );
815 break;
816
818 // https://docs.ogc.org/is/18-088/18-088.html#featureofinterest
819 fields.append( QgsField( u"name"_s, QMetaType::Type::QString ) );
820 fields.append( QgsField( u"description"_s, QMetaType::Type::QString ) );
821 fields.append( QgsField( u"properties"_s, QMetaType::Type::QVariantMap, u"json"_s, 0, 0, QString(), QMetaType::Type::QString ) );
822 break;
823
825 // TODO -- insert link to final 2.0 spec
826 fields.append( QgsField( u"name"_s, QMetaType::Type::QString ) );
827 fields.append( QgsField( u"description"_s, QMetaType::Type::QString ) );
828 fields.append( QgsField( u"properties"_s, QMetaType::Type::QVariantMap, u"json"_s, 0, 0, QString(), QMetaType::Type::QString ) );
829 break;
830
832 // TODO -- insert link to final 2.0 spec
833 fields.append( QgsField( u"name"_s, QMetaType::Type::QString ) );
834 fields.append( QgsField( u"description"_s, QMetaType::Type::QString ) );
835 fields.append( QgsField( u"properties"_s, QMetaType::Type::QVariantMap, u"json"_s, 0, 0, QString(), QMetaType::Type::QString ) );
836 break;
837
839 // https://docs.ogc.org/is/18-088/18-088.html#multidatastream-extension
840 fields.append( QgsField( u"name"_s, QMetaType::Type::QString ) );
841 fields.append( QgsField( u"description"_s, QMetaType::Type::QString ) );
842 fields.append( QgsField( u"unitOfMeasurements"_s, QMetaType::Type::QVariantMap, u"json"_s, 0, 0, QString(), QMetaType::Type::QString ) );
843 fields.append( QgsField( u"observationType"_s, QMetaType::Type::QString ) );
844 fields.append( QgsField( u"multiObservationDataTypes"_s, QMetaType::Type::QStringList, QString(), 0, 0, QString(), QMetaType::Type::QString ) );
845 fields.append( QgsField( u"properties"_s, QMetaType::Type::QVariantMap, u"json"_s, 0, 0, QString(), QMetaType::Type::QString ) );
846 if ( includeRangeFieldProxies )
847 {
848 fields.append( QgsField( u"phenomenonTimeStart"_s, QMetaType::Type::QDateTime ) );
849 fields.append( QgsField( u"phenomenonTimeEnd"_s, QMetaType::Type::QDateTime ) );
850 fields.append( QgsField( u"resultTimeStart"_s, QMetaType::Type::QDateTime ) );
851 fields.append( QgsField( u"resultTimeEnd"_s, QMetaType::Type::QDateTime ) );
852 }
853 break;
854
856 // https://hylkevds.github.io/24-046/24-046.html#deployment
857 fields.append( QgsField( u"name"_s, QMetaType::Type::QString ) );
858 fields.append( QgsField( u"description"_s, QMetaType::Type::QString ) );
859 fields.append( QgsField( u"properties"_s, QMetaType::Type::QVariantMap, u"json"_s, 0, 0, QString(), QMetaType::Type::QString ) );
860 if ( includeRangeFieldProxies )
861 {
862 fields.append( QgsField( u"timeStart"_s, QMetaType::Type::QDateTime ) );
863 fields.append( QgsField( u"timeEnd"_s, QMetaType::Type::QDateTime ) );
864 }
865 break;
866
868 // https://hylkevds.github.io/24-046/24-046.html#observingprocedure
869 fields.append( QgsField( u"name"_s, QMetaType::Type::QString ) );
870 fields.append( QgsField( u"definition"_s, QMetaType::Type::QString ) );
871 fields.append( QgsField( u"description"_s, QMetaType::Type::QString ) );
872 fields.append( QgsField( u"properties"_s, QMetaType::Type::QVariantMap, u"json"_s, 0, 0, QString(), QMetaType::Type::QString ) );
873 break;
874
876 // https://hylkevds.github.io/24-046/24-046.html#sampling-extension
877 fields.append( QgsField( u"name"_s, QMetaType::Type::QString ) );
878 fields.append( QgsField( u"definition"_s, QMetaType::Type::QString ) );
879 fields.append( QgsField( u"description"_s, QMetaType::Type::QString ) );
880 fields.append( QgsField( u"properties"_s, QMetaType::Type::QVariantMap, u"json"_s, 0, 0, QString(), QMetaType::Type::QString ) );
881 if ( includeRangeFieldProxies )
882 {
883 fields.append( QgsField( u"timeStart"_s, QMetaType::Type::QDateTime ) );
884 fields.append( QgsField( u"timeEnd"_s, QMetaType::Type::QDateTime ) );
885 }
886 break;
887
889 // https://hylkevds.github.io/24-046/24-046.html#sampling-extension
890 fields.append( QgsField( u"name"_s, QMetaType::Type::QString ) );
891 fields.append( QgsField( u"definition"_s, QMetaType::Type::QString ) );
892 fields.append( QgsField( u"description"_s, QMetaType::Type::QString ) );
893 fields.append( QgsField( u"properties"_s, QMetaType::Type::QVariantMap, u"json"_s, 0, 0, QString(), QMetaType::Type::QString ) );
894 break;
895
897 // https://hylkevds.github.io/24-046/24-046.html#sampling-extension
898 fields.append( QgsField( u"name"_s, QMetaType::Type::QString ) );
899 fields.append( QgsField( u"definition"_s, QMetaType::Type::QString ) );
900 fields.append( QgsField( u"description"_s, QMetaType::Type::QString ) );
901 fields.append( QgsField( u"properties"_s, QMetaType::Type::QVariantMap, u"json"_s, 0, 0, QString(), QMetaType::Type::QString ) );
902 fields.append( QgsField( u"samplerType"_s, QMetaType::Type::QString ) );
903 break;
904
906 // https://hylkevds.github.io/24-046/24-046.html#sampling-extension
907 fields.append( QgsField( u"name"_s, QMetaType::Type::QString ) );
908 fields.append( QgsField( u"definition"_s, QMetaType::Type::QString ) );
909 fields.append( QgsField( u"description"_s, QMetaType::Type::QString ) );
910 fields.append( QgsField( u"properties"_s, QMetaType::Type::QVariantMap, u"json"_s, 0, 0, QString(), QMetaType::Type::QString ) );
911 if ( includeRangeFieldProxies )
912 {
913 fields.append( QgsField( u"timeStart"_s, QMetaType::Type::QDateTime ) );
914 fields.append( QgsField( u"timeEnd"_s, QMetaType::Type::QDateTime ) );
915 }
916 break;
917
919 // https://hylkevds.github.io/24-046/24-046.html#sampling-extension
920 fields.append( QgsField( u"name"_s, QMetaType::Type::QString ) );
921 fields.append( QgsField( u"definition"_s, QMetaType::Type::QString ) );
922 fields.append( QgsField( u"description"_s, QMetaType::Type::QString ) );
923 fields.append( QgsField( u"properties"_s, QMetaType::Type::QVariantMap, u"json"_s, 0, 0, QString(), QMetaType::Type::QString ) );
924 break;
925
927 // https://hylkevds.github.io/24-046/24-046.html#relations-extension
928 fields.append( QgsField( u"externalTarget"_s, QMetaType::Type::QString ) );
929 break;
930
932 // https://hylkevds.github.io/24-046/24-046.html#relations-extension
933 fields.append( QgsField( u"externalTarget"_s, QMetaType::Type::QString ) );
934 break;
935
937 // https://hylkevds.github.io/24-046/24-046.html#relations-extension
938 fields.append( QgsField( u"name"_s, QMetaType::Type::QString ) );
939 fields.append( QgsField( u"definition"_s, QMetaType::Type::QString ) );
940 fields.append( QgsField( u"inverseName"_s, QMetaType::Type::QString ) );
941 fields.append( QgsField( u"inverseDefinition"_s, QMetaType::Type::QString ) );
942 fields.append( QgsField( u"description"_s, QMetaType::Type::QString ) );
943 fields.append( QgsField( u"properties"_s, QMetaType::Type::QVariantMap, u"json"_s, 0, 0, QString(), QMetaType::Type::QString ) );
944 break;
945
946
948 // https://hylkevds.github.io/24-046/24-046.html#_2d386fc6-41ac-7af3-6b6b-4590cbcd9087
949 fields.append( QgsField( u"externalTarget"_s, QMetaType::Type::QString ) );
950 break;
951
953 // https://hylkevds.github.io/24-046/24-046.html#_f990126f-f23a-4a7c-e602-29ca553ce612
954 fields.append( QgsField( u"externalTarget"_s, QMetaType::Type::QString ) );
955 break;
956 }
957
958 return fields;
959}
960
961QgsFields QgsSensorThingsUtils::fieldsForExpandedEntityType( Qgis::SensorThingsEntity baseType, const QList<Qgis::SensorThingsEntity> &expandedTypes )
962{
963 if ( expandedTypes.empty() )
964 return fieldsForEntityType( baseType );
965
966 QgsFields fields = fieldsForEntityType( baseType );
967 QString path;
968 for ( const Qgis::SensorThingsEntity expandedType : expandedTypes )
969 {
970 path = ( path.isEmpty() ? QString() : ( path + '_' ) ) + qgsEnumValueToKey( expandedType );
971 const QgsFields expandedFields = fieldsForEntityType( expandedType );
972 for ( const QgsField &expandedField : expandedFields )
973 {
974 QgsField renamedExpandedField = expandedField;
975 renamedExpandedField.setName( u"%1_%2"_s.arg( path, expandedField.name() ) );
976 fields.append( renamedExpandedField );
977 }
978 }
979 return fields;
980}
981
983{
984 switch ( type )
985 {
1003 return QString();
1004
1007 return u"location"_s;
1008
1011 return u"feature"_s;
1012
1015 return u"observedArea"_s;
1016
1018 return u"position"_s;
1019 }
1021}
1022
1057
1092
1094{
1095 QString geometryTypeString;
1096 switch ( QgsWkbTypes::geometryType( wkbType ) )
1097 {
1099 geometryTypeString = u"Point"_s;
1100 break;
1102 geometryTypeString = u"Polygon"_s;
1103 break;
1105 geometryTypeString = u"LineString"_s;
1106 break;
1107
1110 return QString();
1111 }
1112
1113 const QString filterTarget = geometryFieldForEntityType( entityType );
1114 if ( filterTarget.isEmpty() )
1115 return QString();
1116
1117 return u"%1/type eq '%2' or %1/geometry/type eq '%2'"_s.arg( filterTarget, geometryTypeString );
1118}
1119
1120QString QgsSensorThingsUtils::filterForExtent( const QString &geometryField, const QgsRectangle &extent )
1121{
1122 // TODO -- confirm using 'geography' is always correct here
1123 return ( extent.isNull() || geometryField.isEmpty() ) ? QString() : u"geo.intersects(%1, geography'%2')"_s.arg( geometryField, extent.asWktPolygon() );
1124}
1125
1126QString QgsSensorThingsUtils::combineFilters( const QStringList &filters )
1127{
1128 QStringList nonEmptyFilters;
1129 for ( const QString &filter : filters )
1130 {
1131 if ( !filter.isEmpty() )
1132 nonEmptyFilters.append( filter );
1133 }
1134 if ( nonEmptyFilters.empty() )
1135 return QString();
1136 if ( nonEmptyFilters.size() == 1 )
1137 return nonEmptyFilters.at( 0 );
1138
1139 return u"("_s + nonEmptyFilters.join( ") and ("_L1 ) + u")"_s;
1140}
1141
1143{
1145 QNetworkRequest request = QNetworkRequest( QUrl( uri ) );
1146 QgsSetRequestInitiatorClass( request, u"QgsSensorThingsUtils"_s )
1147
1148 QgsBlockingNetworkRequest networkRequest;
1149 networkRequest.setAuthCfg( authCfg );
1150
1151 switch ( networkRequest.get( request, false, feedback ) )
1152 {
1154 break;
1155
1159 QgsDebugError( u"Connection failed: %1"_s.arg( networkRequest.errorMessage() ) );
1160 return res;
1161 }
1162
1163 const QgsNetworkReplyContent content = networkRequest.reply();
1164 try
1165 {
1166 auto rootContent = nlohmann::json::parse( content.content().toStdString() );
1167 if ( rootContent.contains( "serverSettings" ) && rootContent["serverSettings"].contains( "conformance" ) )
1168 {
1169 for ( const auto &valueJson : rootContent["serverSettings"]["conformance"] )
1170 {
1171 const QString conformance = QString::fromStdString( valueJson.get<std::string>() );
1172 const thread_local QRegularExpression sDataModelRx( u".*/datamodel\\b"_s );
1173 if ( sDataModelRx.match( conformance ).hasMatch() )
1174 {
1175 // extract version from datamodel value
1176 const thread_local QRegularExpression sVersionRx( u"\\d+\\.\\d+"_s );
1177 const QRegularExpressionMatch versionMatch = sVersionRx.match( conformance );
1178 if ( versionMatch.hasMatch() )
1179 {
1180 const QString versionString = versionMatch.captured( 0 );
1181 bool ok = false;
1182 const double version = versionString.toDouble( &ok );
1183 if ( ok )
1184 {
1185 if ( version < 2.0 )
1186 {
1188 }
1189 else
1190 {
1192 }
1193 }
1194 }
1195 }
1196
1197 const thread_local QRegularExpression sMultiDataStreamRx( u".*/req/multi-datastream\\b"_s );
1198 const thread_local QRegularExpression sSensingOmRx( u".*/req/sensing-extension-om\\b"_s );
1199 const thread_local QRegularExpression sSensingRelationsRx( u".*/req/sensing-extension-relations\\b"_s );
1200 const thread_local QRegularExpression sSensingSamplingRx( u".*/req/sensing-extension-sampling\\b"_s );
1201 if ( sMultiDataStreamRx.match( conformance ).hasMatch() )
1203 else if ( sSensingOmRx.match( conformance ).hasMatch() )
1205 else if ( sSensingRelationsRx.match( conformance ).hasMatch() )
1207 else if ( sSensingSamplingRx.match( conformance ).hasMatch() )
1209 }
1210 }
1211 if ( rootContent.contains( "value" ) )
1212 {
1213 for ( const auto &valueJson : rootContent["value"] )
1214 {
1215 if ( valueJson.contains( "name" ) && valueJson.contains( "url" ) )
1216 {
1217 const QString name = QString::fromStdString( valueJson["name"].get<std::string>() );
1219 if ( entityType != Qgis::SensorThingsEntity::Invalid )
1220 {
1221 res.availableEntities.insert( entityType );
1222 }
1223 }
1224 }
1225 }
1226 }
1227 catch ( const nlohmann::json::parse_error &ex )
1228 {
1229 QgsDebugError( u"Error parsing response: %1"_s.arg( ex.what() ) );
1230 return {};
1231 }
1232 return res;
1233}
1234
1235QList<Qgis::GeometryType> QgsSensorThingsUtils::availableGeometryTypes( const QString &uri, Qgis::SensorThingsEntity type, QgsFeedback *feedback, const QString &authCfg )
1236{
1237 QNetworkRequest request = QNetworkRequest( QUrl( uri ) );
1238 QgsSetRequestInitiatorClass( request, u"QgsSensorThingsUtils"_s )
1239
1240 QgsBlockingNetworkRequest networkRequest;
1241 networkRequest.setAuthCfg( authCfg );
1242
1243 switch ( networkRequest.get( request ) )
1244 {
1246 break;
1247
1251 QgsDebugError( u"Connection failed: %1"_s.arg( networkRequest.errorMessage() ) );
1252 return {};
1253 }
1254
1255 QString entityBaseUri;
1256 const QgsNetworkReplyContent content = networkRequest.reply();
1257 try
1258 {
1259 auto rootContent = nlohmann::json::parse( content.content().toStdString() );
1260 if ( !rootContent.contains( "value" ) )
1261 {
1262 QgsDebugError( u"No 'value' array in response"_s );
1263 return {};
1264 }
1265
1266 bool foundMatchingEntity = false;
1267 for ( const auto &valueJson : rootContent["value"] )
1268 {
1269 if ( valueJson.contains( "name" ) && valueJson.contains( "url" ) )
1270 {
1271 const QString name = QString::fromStdString( valueJson["name"].get<std::string>() );
1273 if ( entityType == type )
1274 {
1275 const QString url = QString::fromStdString( valueJson["url"].get<std::string>() );
1276 if ( !url.isEmpty() )
1277 {
1278 foundMatchingEntity = true;
1279 entityBaseUri = url;
1280 break;
1281 }
1282 }
1283 }
1284 }
1285
1286 if ( !foundMatchingEntity )
1287 {
1288 QgsDebugError( u"Could not find url for %1"_s.arg( qgsEnumValueToKey( type ) ) );
1289 return {};
1290 }
1291 }
1292 catch ( const nlohmann::json::parse_error &ex )
1293 {
1294 QgsDebugError( u"Error parsing response: %1"_s.arg( ex.what() ) );
1295 return {};
1296 }
1297
1298 auto getCountForType = [entityBaseUri, type, authCfg, feedback]( Qgis::GeometryType geometryType ) -> long long {
1299 // return no features, just the total count
1300 QString countUri = u"%1?$top=0&$count=true"_s.arg( entityBaseUri );
1302 const QString typeFilter = QgsSensorThingsUtils::filterForWkbType( type, wkbType );
1303 if ( !typeFilter.isEmpty() )
1304 countUri += u"&$filter="_s + typeFilter;
1305
1306 const QUrl url( countUri );
1307
1308 QNetworkRequest request( url );
1309 QgsSetRequestInitiatorClass( request, u"QgsSensorThingsSharedData"_s );
1310
1311 QgsBlockingNetworkRequest networkRequest;
1312 networkRequest.setAuthCfg( authCfg );
1313 const QgsBlockingNetworkRequest::ErrorCode error = networkRequest.get( request, false, feedback );
1314
1315 if ( feedback && feedback->isCanceled() )
1316 return -1;
1317
1318 // Handle network errors
1320 {
1321 QgsDebugError( u"Network error: %1"_s.arg( networkRequest.errorMessage() ) );
1322 return -1;
1323 }
1324 else
1325 {
1326 const QgsNetworkReplyContent content = networkRequest.reply();
1327 try
1328 {
1329 auto rootContent = nlohmann::json::parse( content.content().toStdString() );
1330 if ( rootContent.contains( "@iot.count" ) )
1331 {
1332 return rootContent["@iot.count"].get<long long>();
1333 }
1334 else if ( rootContent.contains( "@count" ) )
1335 {
1336 return rootContent["@count"].get<long long>();
1337 }
1338
1339 QgsDebugError( u"No 'count' value in response"_s );
1340 return -1;
1341 }
1342 catch ( const nlohmann::json::parse_error &ex )
1343 {
1344 QgsDebugError( u"Error parsing response: %1"_s.arg( ex.what() ) );
1345 return -1;
1346 }
1347 }
1348 };
1349
1350 QList<Qgis::GeometryType> types;
1352 {
1353 const long long matchCount = getCountForType( geometryType );
1354 if ( matchCount < 0 )
1355 return {};
1356 else if ( matchCount > 0 )
1357 types.append( geometryType );
1358 }
1359 return types;
1360}
1361
1363{
1364 // note that we are restricting these choices so that the geometry enabled entity type MUST be the base type
1365
1366 // NOLINTBEGIN(bugprone-branch-clone)
1367 switch ( type )
1368 {
1370 return {};
1371
1373 return {
1377 };
1378
1380 return {
1383 };
1384
1387
1390
1392 return {
1395 };
1396
1399
1402
1405
1408
1410 return {};
1411
1414
1417
1420
1423
1426
1429
1432
1435
1438
1441
1444
1447
1450 }
1451 // NOLINTEND(bugprone-branch-clone)
1452
1454}
1455
1457{
1458 valid = true;
1459 switch ( baseType )
1460 {
1462 break;
1463
1465 {
1466 switch ( relatedType )
1467 {
1470
1478
1496 break;
1497 }
1498 break;
1499 }
1501 {
1502 switch ( relatedType )
1503 {
1507
1530 break;
1531 }
1532 break;
1533 }
1535 {
1536 switch ( relatedType )
1537 {
1539 // The SensorThings specification MAY be wrong here. There's an inconsistency between
1540 // the inheritance graph which shows HistoricalLocation linking to "location", when
1541 // the text description describes the relationship as linking to "locationS".
1542 // We assume the text description is correct and the graph is wrong, as the reverse
1543 // relationship between Location and HistoricalLocation is many-to-many.
1545
1548
1571 break;
1572 }
1573
1574 break;
1575 }
1576
1578 {
1579 switch ( relatedType )
1580 {
1587
1591
1594
1611 break;
1612 }
1613
1614 break;
1615 }
1616
1618 {
1619 switch ( relatedType )
1620 {
1625
1628
1649 break;
1650 }
1651
1652 break;
1653 }
1654
1656 {
1657 switch ( relatedType )
1658 {
1661
1665
1687 break;
1688 }
1689 break;
1690 }
1691
1693 {
1694 switch ( relatedType )
1695 {
1698
1704
1724 break;
1725 }
1726 break;
1727 }
1728
1730 {
1731 switch ( relatedType )
1732 {
1735
1759 break;
1760 }
1761
1762 break;
1763 }
1764
1766 {
1767 switch ( relatedType )
1768 {
1773
1777
1797 break;
1798 }
1799
1800 break;
1801 }
1802
1804 {
1805 switch ( relatedType )
1806 {
1809
1833 break;
1834 }
1835
1836 break;
1837 }
1838
1840 {
1841 switch ( relatedType )
1842 {
1846
1849
1852
1873 break;
1874 }
1875 break;
1876 }
1877
1879 {
1880 switch ( relatedType )
1881 {
1885
1888
1910 break;
1911 }
1912
1913 break;
1914 }
1915
1917 {
1918 switch ( relatedType )
1919 {
1923
1926
1948 break;
1949 }
1950
1951 break;
1952 }
1953
1955 {
1956 switch ( relatedType )
1957 {
1960
1963
1986 break;
1987 }
1988
1989 break;
1990 }
1991
1993 {
1994 switch ( relatedType )
1995 {
2000
2003
2024 break;
2025 }
2026
2027 break;
2028 }
2029
2031 {
2032 switch ( relatedType )
2033 {
2037
2060 break;
2061 }
2062
2063 break;
2064 }
2065
2067 {
2068 switch ( relatedType )
2069 {
2072
2096 break;
2097 }
2098
2099 break;
2100 }
2101
2103 {
2104 switch ( relatedType )
2105 {
2109
2132 break;
2133 }
2134
2135 break;
2136 }
2137
2139 {
2140 switch ( relatedType )
2141 {
2145
2168 break;
2169 }
2170
2171 break;
2172 }
2173
2175 {
2176 switch ( relatedType )
2177 {
2181
2204 break;
2205 }
2206
2207 break;
2208 }
2209
2211 {
2212 switch ( relatedType )
2213 {
2217
2240 break;
2241 }
2242
2243 break;
2244 }
2245
2247 {
2248 switch ( relatedType )
2249 {
2253
2276 break;
2277 }
2278
2279 break;
2280 }
2281
2283 {
2284 switch ( relatedType )
2285 {
2291
2312
2313 break;
2314 }
2315
2316 break;
2317 }
2318 }
2319
2320 valid = false;
2322}
2323
2324QString QgsSensorThingsUtils::asQueryString( Qgis::SensorThingsEntity baseType, const QList<QgsSensorThingsExpansionDefinition> &expansions )
2325{
2326 QString res;
2327 if ( expansions.empty() )
2328 return res;
2329
2330 for ( int i = expansions.size() - 1; i >= 0; i-- )
2331 {
2332 const QgsSensorThingsExpansionDefinition &expansion = expansions.at( i );
2333 if ( !expansion.isValid() )
2334 continue;
2335
2336 const Qgis::SensorThingsEntity parentType = i > 0 ? expansions.at( i - 1 ).childEntity() : baseType;
2337
2338 res = expansion.asQueryString( parentType, res.isEmpty() ? QStringList() : QStringList { res } );
2339 }
2340
2341 return res;
2342}
SensorThingsEntity
OGC SensorThings API entity types.
Definition qgis.h:6625
@ Sensor
A Sensor is an instrument that observes a property or phenomenon with the goal of producing an estima...
Definition qgis.h:6631
@ MultiDatastream
A MultiDatastream groups a collection of Observations and the Observations in a MultiDatastream have ...
Definition qgis.h:6635
@ Sampling
The Sampling is the act of taking one or more Samples. The Sampling takes Samples from a SampledFeatu...
Definition qgis.h:6641
@ DatastreamRelation
A DatastreamRelation Entity relates a source Datastream to a target Datastream, or to an external res...
Definition qgis.h:6649
@ Feature
A Feature is an abstraction of real-world phenomena. It acts as an independent entity that can repres...
Definition qgis.h:6637
@ FeatureRelation
A FeatureRelation Entity relates a source Feature to a target Feature, or to an external resource,...
Definition qgis.h:6648
@ ObservedProperty
An ObservedProperty specifies the phenomenon of an Observation.
Definition qgis.h:6632
@ Invalid
An invalid/unknown entity.
Definition qgis.h:6626
@ Sampler
The Sampler describes the machine, device, human or other entity that executed the sampling procedure...
Definition qgis.h:6643
@ PreparationStep
When applying a PreparationProcdedure to a Sample, the process is recorded in individual PreparationS...
Definition qgis.h:6644
@ RelationRole
The RelationRole Entity holds a name and definition for both directions of the relation....
Definition qgis.h:6647
@ SamplingProcedure
The SamplingProcedure describes the method, or procedure, that the Sampler uses to create Samples....
Definition qgis.h:6642
@ ObservationRelation
A ObservationRelation Entity relates a source Observation to a target Observation,...
Definition qgis.h:6650
@ FeatureOfInterest
In the context of the Internet of Things, many Observations’ FeatureOfInterest can be the Location of...
Definition qgis.h:6634
@ Datastream
A Datastream groups a collection of Observations measuring the same ObservedProperty and produced by ...
Definition qgis.h:6630
@ PreparationProcedure
After a sample is taken, a preparation procedure can be applied to it. The difference with the sampli...
Definition qgis.h:6645
@ Observation
An Observation is the act of measuring or otherwise determining the value of a property.
Definition qgis.h:6633
@ ObservingProcedure
An Observing Procedure. Implemented in the "Sensing Extension (Observations & Measurements)".
Definition qgis.h:6640
@ Location
A Location entity locates the Thing or the Things it associated with. A Thing’s Location entity is de...
Definition qgis.h:6628
@ ThingRelation
A ThingRelation Entity relates a source Thing to a target Thing, or to an external resource,...
Definition qgis.h:6646
@ FeatureType
A FeatureType provides the classification and schema definition for a Feature, describing the common ...
Definition qgis.h:6638
@ Thing
A Thing is an object of the physical world (physical things) or the information world (virtual things...
Definition qgis.h:6627
@ Deployment
A Deployment is the association of a Sensor to a Thing that hosts this Sensor, and to the Datastreams...
Definition qgis.h:6639
@ HistoricalLocation
A Thing’s HistoricalLocation entity set provides the times of the current (i.e., last known) and prev...
Definition qgis.h:6629
@ MultiDatastream
MultiDatastream extension.
Definition qgis.h:6604
@ SensingExtensionObservationsMeasurements
Sensing Extension (Observations & Measurements).
Definition qgis.h:6605
@ SensingExtensionSampling
Sensing Extension (Sampling).
Definition qgis.h:6606
@ SensingExtensionRelations
Sensing Extension (Relations).
Definition qgis.h:6607
GeometryType
The geometry types are used to group Qgis::WkbType in a coarse way.
Definition qgis.h:379
@ Point
Points.
Definition qgis.h:380
@ Line
Lines.
Definition qgis.h:381
@ Polygon
Polygons.
Definition qgis.h:382
@ Unknown
Unknown types.
Definition qgis.h:383
@ Null
No geometry.
Definition qgis.h:384
RelationshipCardinality
Relationship cardinality.
Definition qgis.h:4815
@ ManyToMany
Many to many relationship.
Definition qgis.h:4819
@ ManyToOne
Many to one relationship.
Definition qgis.h:4818
@ OneToOne
One to one relationship.
Definition qgis.h:4816
@ OneToMany
One to many relationship.
Definition qgis.h:4817
WkbType
The WKB type describes the number of dimensions a geometry has.
Definition qgis.h:294
@ Point
Point.
Definition qgis.h:296
@ LineString
LineString.
Definition qgis.h:297
@ Polygon
Polygon.
Definition qgis.h:298
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...
Base class for feedback objects to be used for cancellation of something running in a worker thread.
Definition qgsfeedback.h:44
bool isCanceled() const
Tells whether the operation has been canceled already.
Definition qgsfeedback.h:56
Encapsulate a field in an attribute table or data source.
Definition qgsfield.h:56
void setName(const QString &name)
Set the field name.
Definition qgsfield.cpp:224
Container of fields for a vector layer.
Definition qgsfields.h:46
bool append(const QgsField &field, Qgis::FieldOrigin origin=Qgis::FieldOrigin::Provider, int originIndex=-1)
Appends a field.
Definition qgsfields.cpp:75
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 rectangle specified with double values.
Q_INVOKABLE QString asWktPolygon() const
Returns a string representation of the rectangle as a WKT Polygon.
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.
void setLimit(int limit)
Sets the limit on the number of child features to fetch.
void setChildEntity(Qgis::SensorThingsEntity entity)
Sets the target child entity which should be expanded.
void setSortOrder(Qt::SortOrder order)
Sets the sort order for the expanded child entities.
static QgsSensorThingsExpansionDefinition defaultDefinitionForEntity(Qgis::SensorThingsEntity entity)
Returns an expansion definition for the specified entity type, populated with reasonable defaults whi...
static QgsSensorThingsExpansionDefinition fromString(const QString &string)
Returns a QgsSensorThingsExpansionDefinition from a string representation.
bool operator!=(const QgsSensorThingsExpansionDefinition &other) const
void setFilter(const QString &filter)
Returns the string filter to filter expanded child entities by.
QString asQueryString(Qgis::SensorThingsEntity parentEntityType, const QStringList &additionalOptions=QStringList()) const
Returns the expansion as a valid SensorThings API query string, eg "$expand=Observations($orderby=phe...
bool operator==(const QgsSensorThingsExpansionDefinition &other) const
int limit() const
Returns the limit on the number of child features to fetch.
void setOrderBy(const QString &field)
Sets the field name to order the expanded child entities by.
Qt::SortOrder sortOrder() const
Returns the sort order for the expanded child entities.
QString toString() const
Returns a string encapsulation of the expansion definition.
QString orderBy() const
Returns the field name to order the expanded child entities by.
QString filter() const
Returns the string filter to filter expanded child entities by.
QgsSensorThingsExpansionDefinition(Qgis::SensorThingsEntity childEntity=Qgis::SensorThingsEntity::Invalid, const QString &orderBy=QString(), Qt::SortOrder sortOrder=Qt::SortOrder::AscendingOrder, int limit=QgsSensorThingsUtils::DEFAULT_EXPANSION_LIMIT, const QString &filter=QString())
Constructor for QgsSensorThingsExpansionDefinition, targeting the specified child entity type.
bool isValid() const
Returns true if the definition is valid.
static QStringList propertiesForEntityType(Qgis::SensorThingsEntity type)
Returns the SensorThings properties which correspond to a specified entity type.
static QList< Qgis::GeometryType > availableGeometryTypes(const QString &uri, Qgis::SensorThingsEntity type, QgsFeedback *feedback=nullptr, const QString &authCfg=QString())
Returns a list of available geometry types for the server at the specified uri and entity type.
static Qgis::SensorThingsEntity stringToEntity(const QString &type)
Converts a string value to a Qgis::SensorThingsEntity type.
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 Qgis::SensorThingsEntity entitySetStringToEntity(const QString &type)
Converts a string value corresponding to a SensorThings entity set to a Qgis::SensorThingsEntity type...
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 Qgis::GeometryType geometryTypeForEntity(Qgis::SensorThingsEntity type)
Returns the geometry type for if the specified entity type.
static QgsFields fieldsForEntityType(Qgis::SensorThingsEntity type, bool includeRangeFieldProxies=true)
Returns the fields which correspond to a specified entity type.
static QString displayString(Qgis::SensorThingsEntity type, bool plural=false)
Converts a Qgis::SensorThingsEntity type to a user-friendly translated string.
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 QList< Qgis::SensorThingsEntity > expandableTargets(Qgis::SensorThingsEntity type)
Returns a list of permissible expand targets for a given base entity type.
static ServiceCapabilities determineServiceCapabilities(const QString &uri, QgsFeedback *feedback=nullptr, const QString &authCfg=QString())
Retrieves general service capabilities for a SensorThings server.
static QString filterForExtent(const QString &geometryField, const QgsRectangle &extent)
Returns a filter string which restricts results to those within the specified extent.
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...
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.
Definition qgis.h:7595
QString qgsEnumValueToKey(const T &value, bool *returnOk=nullptr)
Returns the value for the given key of an enum.
Definition qgis.h:7576
#define BUILTIN_UNREACHABLE
Definition qgis.h:7974
#define QgsDebugError(str)
Definition qgslogger.h:71
#define QgsSetRequestInitiatorClass(request, _class)
SensorThings service capabilities.
QSet< Qgis::SensorThingsEntity > availableEntities
Available SensorThings entities.
Qgis::SensorThingsExtensions availableExtensions
Available SensorThings extensions.
Qgis::SensorThingsVersion version
SensorThings API version.