QGIS API Documentation 3.39.0-Master (47f7b3a4989)
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 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.
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)
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