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