QGIS API Documentation 3.41.0-Master (fda2aa46e9a)
Loading...
Searching...
No Matches
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 mTransform = mRequest.calculateTransform( mSource->sharedData()->crs() );
52 try
53 {
54 mFilterRect = filterRectToSourceCrs( mTransform );
55 if ( !mRequest.filterRect().isNull() && mFilterRect.isNull() )
56 {
57 close();
58 return;
59 }
60 }
61 catch ( QgsCsException & )
62 {
63 // can't reproject mFilterRect
64 close();
65 return;
66 }
67
68 QgsFeatureIds requestIds;
69 if ( mRequest.filterType() == Qgis::FeatureRequestFilterType::Fids )
70 {
71 requestIds = mRequest.filterFids();
72 }
73 else if ( mRequest.filterType() == Qgis::FeatureRequestFilterType::Fid )
74 {
75 requestIds.insert( mRequest.filterFid() );
76 }
77
78 if ( !mFilterRect.isNull() )
79 {
80 // defer request to find features in filter rect until first feature is requested
81 // this allows time for a interruption checker to be installed on the iterator
82 // and avoids performing this expensive check in the main thread when just
83 // preparing iterators
84 mDeferredFeaturesInFilterRectCheck = true;
85 }
86
87 // prepare spatial filter geometries for optimal speed
88 switch ( mRequest.spatialFilterType() )
89 {
92 break;
93
95 if ( !mRequest.referenceGeometry().isEmpty() )
96 {
97 mDistanceWithinGeom = mRequest.referenceGeometry();
98 mDistanceWithinEngine.reset( QgsGeometry::createGeometryEngine( mDistanceWithinGeom.constGet() ) );
99 mDistanceWithinEngine->prepareGeometry();
100 }
101 break;
102 }
103
104 mRequestFeatureIdList = qgis::setToList( requestIds );
105 std::sort( mRequestFeatureIdList.begin(), mRequestFeatureIdList.end() );
106 mRemainingFeatureIds = mRequestFeatureIdList;
107 if ( !mRemainingFeatureIds.empty() )
108 mFeatureIterator = mRemainingFeatureIds.at( 0 );
109
110 mGeometryTestFilterRect = mFilterRect;
111}
112
113QgsSensorThingsFeatureIterator::~QgsSensorThingsFeatureIterator()
114{
115 QgsSensorThingsFeatureIterator::close();
116}
117
118bool QgsSensorThingsFeatureIterator::fetchFeature( QgsFeature &f )
119{
120 f.setValid( false );
121
122 if ( mClosed )
123 return false;
124
125 if ( mInterruptionChecker && mInterruptionChecker->isCanceled() )
126 return false;
127
128 if ( mDeferredFeaturesInFilterRectCheck || !mCurrentPage.isEmpty() )
129 {
130 const QgsFeatureIds featuresInRect = mSource->sharedData()->getFeatureIdsInExtent( mFilterRect, mInterruptionChecker, mCurrentPage, mNextPage, mAlreadyFetchedIds );
131 mCurrentPage.clear();
132
133 if ( !mRequestFeatureIdList.isEmpty() )
134 {
135 QgsFeatureIds requestIds = qgis::listToSet( mRequestFeatureIdList );
136 requestIds.intersect( featuresInRect );
137 mCurrentPageFeatureIdList = qgis::setToList( requestIds );
138 }
139 else
140 {
141 mCurrentPageFeatureIdList = qgis::setToList( featuresInRect );
142 }
143 if ( mCurrentPageFeatureIdList.empty() )
144 {
145 if ( mNextPage.isEmpty() )
146 {
147 return false;
148 }
149 else
150 {
151 mCurrentPage = mNextPage;
152 return fetchFeature( f );
153 }
154 }
155
156 std::sort( mCurrentPageFeatureIdList.begin(), mCurrentPageFeatureIdList.end() );
157 mRemainingFeatureIds = mCurrentPageFeatureIdList;
158 if ( !mRemainingFeatureIds.empty() )
159 mFeatureIterator = mRemainingFeatureIds.at( 0 );
160
161 mDeferredFeaturesInFilterRectCheck = false;
162
163 if ( !( mRequest.flags() & Qgis::FeatureRequestFlag::ExactIntersect ) )
164 {
165 // discard the filter rect - we know that the features in mRemainingFeatureIds are guaranteed
166 // to be intersecting the rect, so avoid any extra unnecessary checks
167 mGeometryTestFilterRect = QgsRectangle();
168 }
169 }
170
171 if ( !mCurrentPageFeatureIdList.empty() && mRemainingFeatureIds.empty() )
172 {
173 if ( mNextPage.isEmpty() )
174 {
175 return false;
176 }
177 else
178 {
179 // request next page
180 mCurrentPage = mNextPage;
181 return fetchFeature( f );
182 }
183 }
184
185 switch ( mRequest.filterType() )
186 {
188 {
189 if ( mRemainingFeatureIds.empty() )
190 return false;
191
192 bool result = mSource->sharedData()->getFeature( mRequest.filterFid(), f, mInterruptionChecker );
193 mAlreadyFetchedIds.insert( mRequest.filterFid() );
194 if ( mInterruptionChecker && mInterruptionChecker->isCanceled() )
195 return false;
196
197 geometryToDestinationCrs( f, mTransform );
198 if ( mDistanceWithinEngine && mDistanceWithinEngine->distance( f.geometry().constGet() ) > mRequest.distanceWithin() )
199 {
200 result = false;
201 }
202 f.setValid( result );
203
204 mRemainingFeatureIds.removeAll( f.id() );
205 return result;
206 }
207
211 {
212 while ( true )
213 {
214 if ( mInterruptionChecker && mInterruptionChecker->isCanceled() )
215 return false;
216
217 if ( !mCurrentPageFeatureIdList.empty() && mRemainingFeatureIds.empty() )
218 {
219 if ( mNextPage.isEmpty() )
220 {
221 return false;
222 }
223 else
224 {
225 // fetch next page
226 mCurrentPage = mNextPage;
227 return fetchFeature( f );
228 }
229 }
230
231 bool success = mSource->sharedData()->getFeature( mFeatureIterator, f, mInterruptionChecker );
232 mAlreadyFetchedIds.insert( mFeatureIterator );
233
234 if ( !mCurrentPageFeatureIdList.empty() )
235 {
236 mRemainingFeatureIds.removeAll( mFeatureIterator );
237 if ( !mRemainingFeatureIds.empty() )
238 mFeatureIterator = mRemainingFeatureIds.at( 0 );
239 }
240 else
241 {
242 if ( !success )
243 return false;
244 ++mFeatureIterator;
245 }
246
247 if ( success && !mGeometryTestFilterRect.isNull() )
248 {
249 if ( !f.hasGeometry() )
250 success = false;
251 else
252 {
253 if ( mRequest.spatialFilterType() == Qgis::SpatialFilterType::BoundingBox && mRequest.flags() & Qgis::FeatureRequestFlag::ExactIntersect )
254 {
255 // exact intersection check requested
256 if ( !f.geometry().intersects( mGeometryTestFilterRect ) )
257 success = false;
258 }
259 else
260 {
261 // bounding box intersect check only
262 if ( !f.geometry().boundingBoxIntersects( mGeometryTestFilterRect ) )
263 success = false;
264 }
265 }
266 }
267
268 if ( !success )
269 continue;
270 geometryToDestinationCrs( f, mTransform );
271
272 bool result = true;
273 if ( mDistanceWithinEngine && mDistanceWithinEngine->distance( f.geometry().constGet() ) > mRequest.distanceWithin() )
274 result = false;
275
276 if ( result )
277 return true;
278 }
279 return false;
280 }
281 }
282
283 return false;
284}
285
286bool QgsSensorThingsFeatureIterator::rewind()
287{
288 if ( mClosed )
289 return false;
290 mFeatureIterator = 0;
291 mCurrentPage.clear();
292 mAlreadyFetchedIds.clear();
293 mRemainingFeatureIds = mRequestFeatureIdList;
294 if ( !mRemainingFeatureIds.empty() )
295 mFeatureIterator = mRemainingFeatureIds.at( 0 );
296 return true;
297}
298
299bool QgsSensorThingsFeatureIterator::close()
300{
301 if ( mClosed )
302 return false;
303 iteratorClosed();
304 mClosed = true;
305 return true;
306}
307
308void QgsSensorThingsFeatureIterator::setInterruptionChecker( QgsFeedback *interruptionChecker )
309{
310 mInterruptionChecker = interruptionChecker;
311}
312
@ 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.
Custom exception class for Coordinate Reference System related exceptions.
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:58
QgsFeatureId id
Definition qgsfeature.h:66
QgsGeometry geometry
Definition qgsfeature.h:69
void setValid(bool validity)
Sets the validity of the feature.
bool hasGeometry() const
Returns true if the feature has an associated geometry.
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, Qgis::GeosCreationFlags flags=Qgis::GeosCreationFlag::SkipEmptyInteriorRings)
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.
QSet< QgsFeatureId > QgsFeatureIds