QGIS API Documentation 3.41.0-Master (45a0abf3bec)
Loading...
Searching...
No Matches
qgsmemoryfeatureiterator.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsmemoryfeatureiterator.cpp
3 ---------------------
4 begin : Juli 2012
5 copyright : (C) 2012 by Martin Dobias
6 email : wonder dot sk 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#include "qgsmemoryprovider.h"
17
18#include "qgsgeometry.h"
19#include "qgsgeometryengine.h"
20#include "qgslogger.h"
21#include "qgsspatialindex.h"
22#include "qgsproject.h"
23#include "qgsexception.h"
25
27
28QgsMemoryFeatureIterator::QgsMemoryFeatureIterator( QgsMemoryFeatureSource *source, bool ownSource, const QgsFeatureRequest &request )
29 : QgsAbstractFeatureIteratorFromSource<QgsMemoryFeatureSource>( source, ownSource, request )
30{
31 mTransform = mRequest.calculateTransform( mSource->mCrs );
32
33 try
34 {
35 mFilterRect = filterRectToSourceCrs( mTransform );
36 }
37 catch ( QgsCsException & )
38 {
39 // can't reproject mFilterRect
40 close();
41 return;
42 }
43
44 if ( !mSource->mSubsetString.isEmpty() )
45 {
46 mSubsetExpression = std::make_unique< QgsExpression >( mSource->mSubsetString );
47 mSubsetExpression->prepare( mSource->expressionContext() );
48 }
49
50 // prepare spatial filter geometries for optimal speed
51 switch ( mRequest.spatialFilterType() )
52 {
54 break;
55
57 if ( !mFilterRect.isNull() && ( mRequest.flags() & Qgis::FeatureRequestFlag::ExactIntersect ) )
58 {
59 mSelectRectGeom = QgsGeometry::fromRect( mFilterRect );
60 mSelectRectEngine.reset( QgsGeometry::createGeometryEngine( mSelectRectGeom.constGet() ) );
61 mSelectRectEngine->prepareGeometry();
62 }
63 break;
64
66 if ( !mRequest.referenceGeometry().isEmpty() )
67 {
68 mDistanceWithinGeom = mRequest.referenceGeometry();
69 mDistanceWithinEngine.reset( QgsGeometry::createGeometryEngine( mDistanceWithinGeom.constGet() ) );
70 mDistanceWithinEngine->prepareGeometry();
71 }
72 break;
73 }
74
75 // if there's spatial index, use it!
76 // (but don't use it when selection rect is not specified)
77 if ( !mFilterRect.isNull() && mSource->mSpatialIndex )
78 {
79 mUsingFeatureIdList = true;
80 mFeatureIdList = mSource->mSpatialIndex->intersects( mFilterRect );
81 QgsDebugMsgLevel( "Features returned by spatial index: " + QString::number( mFeatureIdList.count() ), 2 );
82 }
83 else if ( mRequest.filterType() == Qgis::FeatureRequestFilterType::Fid )
84 {
85 mUsingFeatureIdList = true;
86 const QgsFeatureMap::const_iterator it = mSource->mFeatures.constFind( mRequest.filterFid() );
87 if ( it != mSource->mFeatures.constEnd() )
88 mFeatureIdList.append( mRequest.filterFid() );
89 }
90 else if ( mRequest.filterType() == Qgis::FeatureRequestFilterType::Fids )
91 {
92 mUsingFeatureIdList = true;
93 const QgsFeatureIds filterFids = mRequest.filterFids();
94 mFeatureIdList = QList<QgsFeatureId>( filterFids.begin(), filterFids.end() );
95 }
96 else
97 {
98 mUsingFeatureIdList = false;
99 }
100
101 rewind();
102}
103
104QgsMemoryFeatureIterator::~QgsMemoryFeatureIterator()
105{
106 close();
107}
108
109bool QgsMemoryFeatureIterator::fetchFeature( QgsFeature &feature )
110{
111 feature.setValid( false );
112
113 if ( mClosed )
114 return false;
115
116 if ( mUsingFeatureIdList )
117 return nextFeatureUsingList( feature );
118 else
119 return nextFeatureTraverseAll( feature );
120}
121
122
123bool QgsMemoryFeatureIterator::nextFeatureUsingList( QgsFeature &feature )
124{
125 bool hasFeature = false;
126
127 // option 1: we have a list of features to traverse
128 while ( mFeatureIdListIterator != mFeatureIdList.constEnd() )
129 {
130 feature = mSource->mFeatures.value( *mFeatureIdListIterator );
131 if ( !mFilterRect.isNull() )
132 {
133 if ( mRequest.spatialFilterType() == Qgis::SpatialFilterType::BoundingBox && ( mRequest.flags() & Qgis::FeatureRequestFlag::ExactIntersect ) )
134 {
135 // do exact check in case we're doing intersection
136 if ( feature.hasGeometry() && mSelectRectEngine->intersects( feature.geometry().constGet() ) )
137 hasFeature = true;
138 }
139 else if ( mSource->mSpatialIndex )
140 {
141 // using a spatial index - so we already know that the bounding box intersects correctly
142 hasFeature = true;
143 }
144 else
145 {
146 // do bounding box check if we aren't using a spatial index
147 if ( feature.hasGeometry() && feature.geometry().boundingBoxIntersects( mFilterRect ) )
148 hasFeature = true;
149 }
150 }
151 else
152 hasFeature = true;
153
154 if ( hasFeature )
155 feature.setFields( mSource->mFields ); // allow name-based attribute lookups
156
157 if ( hasFeature && mSubsetExpression )
158 {
159 mSource->expressionContext()->setFeature( feature );
160 if ( !mSubsetExpression->evaluate( mSource->expressionContext() ).toBool() )
161 hasFeature = false;
162 }
163
164 if ( hasFeature )
165 {
166 // geometry must be in destination crs before we can perform distance within check
167 geometryToDestinationCrs( feature, mTransform );
168 }
169
170 if ( hasFeature && mRequest.spatialFilterType() == Qgis::SpatialFilterType::DistanceWithin )
171 {
172 hasFeature = mDistanceWithinEngine->distance( feature.geometry().constGet() ) <= mRequest.distanceWithin();
173 }
174
175 ++mFeatureIdListIterator;
176 if ( hasFeature )
177 break;
178 }
179
180 feature.setValid( hasFeature );
181 if ( !hasFeature )
182 {
183 close();
184 }
185
186 return hasFeature;
187}
188
189
190bool QgsMemoryFeatureIterator::nextFeatureTraverseAll( QgsFeature &feature )
191{
192 bool hasFeature = false;
193
194 // option 2: traversing the whole layer
195 while ( mSelectIterator != mSource->mFeatures.constEnd() )
196 {
197 hasFeature = false;
198 feature = *mSelectIterator;
199 if ( mFilterRect.isNull() )
200 {
201 // selection rect empty => using all features
202 hasFeature = true;
203 }
204 else
205 {
206 if ( mRequest.spatialFilterType() == Qgis::SpatialFilterType::BoundingBox && ( mRequest.flags() & Qgis::FeatureRequestFlag::ExactIntersect ) )
207 {
208 // using exact test when checking for intersection
209 if ( feature.hasGeometry() && mSelectRectEngine->intersects( feature.geometry().constGet() ) )
210 hasFeature = true;
211 }
212 else
213 {
214 // check just bounding box against rect when not using intersection
215 if ( feature.hasGeometry() && feature.geometry().boundingBox().intersects( mFilterRect ) )
216 hasFeature = true;
217 }
218 }
219
220 if ( hasFeature && mSubsetExpression )
221 {
222 mSource->expressionContext()->setFeature( feature );
223 if ( !mSubsetExpression->evaluate( mSource->expressionContext() ).toBool() )
224 hasFeature = false;
225 }
226
227 if ( hasFeature )
228 {
229 // geometry must be in destination crs before we can perform distance within check
230 geometryToDestinationCrs( feature, mTransform );
231 }
232
233 if ( hasFeature && mRequest.spatialFilterType() == Qgis::SpatialFilterType::DistanceWithin )
234 {
235 hasFeature = mDistanceWithinEngine->distance( feature.geometry().constGet() ) <= mRequest.distanceWithin();
236 }
237
238 ++mSelectIterator;
239 if ( hasFeature )
240 break;
241 }
242
243 // copy feature
244 if ( hasFeature )
245 {
246 feature.setValid( true );
247 feature.setFields( mSource->mFields ); // allow name-based attribute lookups
248 }
249 else
250 {
251 feature.setValid( false );
252 close();
253 }
254
255 return hasFeature;
256}
257
258bool QgsMemoryFeatureIterator::rewind()
259{
260 if ( mClosed )
261 return false;
262
263 if ( mUsingFeatureIdList )
264 mFeatureIdListIterator = mFeatureIdList.constBegin();
265 else
266 mSelectIterator = mSource->mFeatures.constBegin();
267
268 return true;
269}
270
271bool QgsMemoryFeatureIterator::close()
272{
273 if ( mClosed )
274 return false;
275
276 iteratorClosed();
277
278 mClosed = true;
279 return true;
280}
281
282// -------------------------
283
284QgsMemoryFeatureSource::QgsMemoryFeatureSource( const QgsMemoryProvider *p )
285 : mFields( p->mFields )
286 , mFeatures( p->mFeatures )
287 , mSpatialIndex( p->mSpatialIndex ? std::make_unique< QgsSpatialIndex >( *p->mSpatialIndex ) : nullptr ) // just shallow copy
288 , mSubsetString( p->mSubsetString )
289 , mCrs( p->mCrs )
290{
291}
292
293QgsFeatureIterator QgsMemoryFeatureSource::getFeatures( const QgsFeatureRequest &request )
294{
295 return QgsFeatureIterator( new QgsMemoryFeatureIterator( this, false, request ) );
296}
297
298QgsExpressionContext *QgsMemoryFeatureSource::expressionContext()
299{
300 // lazy construct expression context -- it's not free to calculate, and is only used when
301 // iterating over a memory layer with a subset string set
302 if ( !mExpressionContext )
303 {
304 mExpressionContext = std::make_unique< QgsExpressionContext >(
305 QList<QgsExpressionContextScope *>()
308 mExpressionContext->setFields( mFields );
309 }
310 return mExpressionContext.get();
311}
312
@ Fid
Filter using feature ID.
@ Fids
Filter using feature IDs.
@ 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.
static QgsExpressionContextScope * projectScope(const QgsProject *project)
Creates a new scope which contains variables and functions relating to a QGIS project.
static QgsExpressionContextScope * globalScope()
Creates a new scope which contains variables and functions relating to the global QGIS context.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
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
void setFields(const QgsFields &fields, bool initAttributes=false)
Assigns a field map with the feature to allow attribute access by attribute name.
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.
static QgsGeometry fromRect(const QgsRectangle &rect)
Creates a new geometry from a QgsRectangle.
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.
QgsRectangle boundingBox() const
Returns the bounding box of the geometry.
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...
static QgsProject * instance()
Returns the QgsProject singleton instance.
bool intersects(const QgsRectangle &rect) const
Returns true when rectangle intersects with other rectangle.
A spatial index for QgsFeature objects.
QSet< QgsFeatureId > QgsFeatureIds
#define QgsDebugMsgLevel(str, level)
Definition qgslogger.h:39