QGIS API Documentation 3.37.0-Master (fdefdf9c27f)
qgssensorthingsfeatureiterator.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgssensorthingsfeatureiterator.cpp
3 ----------------
4 begin : November 2023
5 copyright : (C) 2013 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 ***************************************************************************/
17#include "qgsexception.h"
18#include "qgsfeedback.h"
19#include "qgsgeometryengine.h"
20
22
23//
24// QgsSensorThingsFeatureSource
25//
26
27QgsSensorThingsFeatureSource::QgsSensorThingsFeatureSource( const std::shared_ptr<QgsSensorThingsSharedData> &sharedData )
28 : mSharedData( sharedData )
29{
30}
31
32QgsFeatureIterator QgsSensorThingsFeatureSource::getFeatures( const QgsFeatureRequest &request )
33{
34 return QgsFeatureIterator( new QgsSensorThingsFeatureIterator( this, false, request ) );
35}
36
37QgsSensorThingsSharedData *QgsSensorThingsFeatureSource::sharedData() const
38{
39 return mSharedData.get();
40}
41
42
43//
44// QgsSensorThingsFeatureIterator
45//
46
47QgsSensorThingsFeatureIterator::QgsSensorThingsFeatureIterator( QgsSensorThingsFeatureSource *source, bool ownSource, const QgsFeatureRequest &request )
48 : QgsAbstractFeatureIteratorFromSource<QgsSensorThingsFeatureSource>( source, ownSource, request )
49 , mInterruptionChecker( request.feedback() )
50{
51 if ( mRequest.destinationCrs().isValid() && mRequest.destinationCrs() != mSource->sharedData()->crs() )
52 {
53 mTransform = QgsCoordinateTransform( mSource->sharedData()->crs(), mRequest.destinationCrs(), mRequest.transformContext() );
54 }
55 try
56 {
57 mFilterRect = filterRectToSourceCrs( mTransform );
58 if ( !mRequest.filterRect().isNull() && mFilterRect.isNull() )
59 {
60 close();
61 return;
62 }
63 }
64 catch ( QgsCsException & )
65 {
66 // can't reproject mFilterRect
67 close();
68 return;
69 }
70
71 QgsFeatureIds requestIds;
72 if ( mRequest.filterType() == Qgis::FeatureRequestFilterType::Fids )
73 {
74 requestIds = mRequest.filterFids();
75 }
76 else if ( mRequest.filterType() == Qgis::FeatureRequestFilterType::Fid )
77 {
78 requestIds.insert( mRequest.filterFid() );
79 }
80
81 if ( !mFilterRect.isNull() )
82 {
83 // defer request to find features in filter rect until first feature is requested
84 // this allows time for a interruption checker to be installed on the iterator
85 // and avoids performing this expensive check in the main thread when just
86 // preparing iterators
87 mDeferredFeaturesInFilterRectCheck = true;
88 }
89
90 // prepare spatial filter geometries for optimal speed
91 switch ( mRequest.spatialFilterType() )
92 {
95 break;
96
98 if ( !mRequest.referenceGeometry().isEmpty() )
99 {
100 mDistanceWithinGeom = mRequest.referenceGeometry();
101 mDistanceWithinEngine.reset( QgsGeometry::createGeometryEngine( mDistanceWithinGeom.constGet() ) );
102 mDistanceWithinEngine->prepareGeometry();
103 }
104 break;
105 }
106
107 mRequestFeatureIdList = qgis::setToList( requestIds );
108 std::sort( mRequestFeatureIdList.begin(), mRequestFeatureIdList.end() );
109 mRemainingFeatureIds = mRequestFeatureIdList;
110 if ( !mRemainingFeatureIds.empty() )
111 mFeatureIterator = mRemainingFeatureIds.at( 0 );
112
113 mGeometryTestFilterRect = mFilterRect;
114}
115
116QgsSensorThingsFeatureIterator::~QgsSensorThingsFeatureIterator()
117{
118 QgsSensorThingsFeatureIterator::close();
119}
120
121bool QgsSensorThingsFeatureIterator::fetchFeature( QgsFeature &f )
122{
123 f.setValid( false );
124
125 if ( mClosed )
126 return false;
127
128 if ( mInterruptionChecker && mInterruptionChecker->isCanceled() )
129 return false;
130
131 if ( mDeferredFeaturesInFilterRectCheck || !mCurrentPage.isEmpty() )
132 {
133 const QgsFeatureIds featuresInRect = mSource->sharedData()->getFeatureIdsInExtent( mFilterRect, mInterruptionChecker, mCurrentPage, mNextPage, mAlreadyFetchedIds );
134 mCurrentPage.clear();
135
136 if ( !mRequestFeatureIdList.isEmpty() )
137 {
138 QgsFeatureIds requestIds = qgis::listToSet( mRequestFeatureIdList );
139 requestIds.intersect( featuresInRect );
140 mCurrentPageFeatureIdList = qgis::setToList( requestIds );
141 }
142 else
143 {
144 mCurrentPageFeatureIdList = qgis::setToList( featuresInRect );
145 }
146 if ( mCurrentPageFeatureIdList.empty() )
147 {
148 if ( mNextPage.isEmpty() )
149 {
150 return false;
151 }
152 else
153 {
154 mCurrentPage = mNextPage;
155 return fetchFeature( f );
156 }
157 }
158
159 std::sort( mCurrentPageFeatureIdList.begin(), mCurrentPageFeatureIdList.end() );
160 mRemainingFeatureIds = mCurrentPageFeatureIdList;
161 if ( !mRemainingFeatureIds.empty() )
162 mFeatureIterator = mRemainingFeatureIds.at( 0 );
163
164 mDeferredFeaturesInFilterRectCheck = false;
165
166 if ( !( mRequest.flags() & Qgis::FeatureRequestFlag::ExactIntersect ) )
167 {
168 // discard the filter rect - we know that the features in mRemainingFeatureIds are guaranteed
169 // to be intersecting the rect, so avoid any extra unnecessary checks
170 mGeometryTestFilterRect = QgsRectangle();
171 }
172 }
173
174 if ( !mCurrentPageFeatureIdList.empty() && mRemainingFeatureIds.empty() )
175 {
176 if ( mNextPage.isEmpty() )
177 {
178 return false;
179 }
180 else
181 {
182 // request next page
183 mCurrentPage = mNextPage;
184 return fetchFeature( f );
185 }
186 }
187
188 switch ( mRequest.filterType() )
189 {
191 {
192 if ( mRemainingFeatureIds.empty() )
193 return false;
194
195 bool result = mSource->sharedData()->getFeature( mRequest.filterFid(), f, mInterruptionChecker );
196 mAlreadyFetchedIds.insert( mRequest.filterFid() );
197 if ( mInterruptionChecker && mInterruptionChecker->isCanceled() )
198 return false;
199
200 geometryToDestinationCrs( f, mTransform );
201 if ( mDistanceWithinEngine && mDistanceWithinEngine->distance( f.geometry().constGet() ) > mRequest.distanceWithin() )
202 {
203 result = false;
204 }
205 f.setValid( result );
206
207 mRemainingFeatureIds.removeAll( f.id() );
208 return result;
209 }
210
214 {
215 while ( true )
216 {
217 if ( mInterruptionChecker && mInterruptionChecker->isCanceled() )
218 return false;
219
220 if ( !mCurrentPageFeatureIdList.empty() && mRemainingFeatureIds.empty() )
221 {
222 if ( mNextPage.isEmpty() )
223 {
224 return false;
225 }
226 else
227 {
228 // fetch next page
229 mCurrentPage = mNextPage;
230 return fetchFeature( f );
231 }
232 }
233
234 bool success = mSource->sharedData()->getFeature( mFeatureIterator, f, mInterruptionChecker );
235 mAlreadyFetchedIds.insert( mFeatureIterator );
236
237 if ( !mCurrentPageFeatureIdList.empty() )
238 {
239 mRemainingFeatureIds.removeAll( mFeatureIterator );
240 if ( !mRemainingFeatureIds.empty() )
241 mFeatureIterator = mRemainingFeatureIds.at( 0 );
242 }
243 else
244 {
245 if ( !success )
246 return false;
247 ++mFeatureIterator;
248 }
249
250 if ( success && !mGeometryTestFilterRect.isNull() )
251 {
252 if ( !f.hasGeometry() )
253 success = false;
254 else
255 {
256 if ( mRequest.spatialFilterType() == Qgis::SpatialFilterType::BoundingBox && mRequest.flags() & Qgis::FeatureRequestFlag::ExactIntersect )
257 {
258 // exact intersection check requested
259 if ( !f.geometry().intersects( mGeometryTestFilterRect ) )
260 success = false;
261 }
262 else
263 {
264 // bounding box intersect check only
265 if ( !f.geometry().boundingBoxIntersects( mGeometryTestFilterRect ) )
266 success = false;
267 }
268 }
269 }
270
271 if ( !success )
272 continue;
273 geometryToDestinationCrs( f, mTransform );
274
275 bool result = true;
276 if ( mDistanceWithinEngine && mDistanceWithinEngine->distance( f.geometry().constGet() ) > mRequest.distanceWithin() )
277 result = false;
278
279 if ( result )
280 return true;
281 }
282 return false;
283 }
284 }
285
286 return false;
287}
288
289bool QgsSensorThingsFeatureIterator::rewind()
290{
291 if ( mClosed )
292 return false;
293 mFeatureIterator = 0;
294 mCurrentPage.clear();
295 mAlreadyFetchedIds.clear();
296 mRemainingFeatureIds = mRequestFeatureIdList;
297 if ( !mRemainingFeatureIds.empty() )
298 mFeatureIterator = mRemainingFeatureIds.at( 0 );
299 return true;
300}
301
302bool QgsSensorThingsFeatureIterator::close()
303{
304 if ( mClosed )
305 return false;
306 iteratorClosed();
307 mClosed = true;
308 return true;
309}
310
311void QgsSensorThingsFeatureIterator::setInterruptionChecker( QgsFeedback *interruptionChecker )
312{
313 mInterruptionChecker = interruptionChecker;
314}
315
@ Fid
Filter using feature ID.
@ Fids
Filter using feature IDs.
@ Expression
Filter using expression.
@ NoFilter
No filter is applied.
@ ExactIntersect
Use exact geometry intersection (slower) instead of bounding boxes.
@ DistanceWithin
Filter by distance to reference geometry.
@ BoundingBox
Filter using a bounding box.
@ NoFilter
No spatial filtering of features.
Helper template that cares of two things: 1.
Class for doing transforms between two map coordinate systems.
Custom exception class for Coordinate Reference System related exceptions.
Definition: qgsexception.h:67
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).
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition: qgsfeature.h:56
QgsGeometry geometry
Definition: qgsfeature.h:67
void setValid(bool validity)
Sets the validity of the feature.
Definition: qgsfeature.cpp:221
bool hasGeometry() const
Returns true if the feature has an associated geometry.
Definition: qgsfeature.cpp:230
Q_GADGET QgsFeatureId id
Definition: qgsfeature.h:64
Base class for feedback objects to be used for cancellation of something running in a worker thread.
Definition: qgsfeedback.h:44
bool boundingBoxIntersects(const QgsRectangle &rectangle) const
Returns true if the bounding box of this geometry intersects with a rectangle.
const QgsAbstractGeometry * constGet() const
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
static QgsGeometryEngine * createGeometryEngine(const QgsAbstractGeometry *geometry, double precision=0.0)
Creates and returns a new geometry engine representing the specified geometry using precision on a gr...
bool intersects(const QgsRectangle &rectangle) const
Returns true if this geometry exactly intersects with a rectangle.
A rectangle specified with double values.
Definition: qgsrectangle.h:42
QSet< QgsFeatureId > QgsFeatureIds
Definition: qgsfeatureid.h:37