QGIS API Documentation  3.10.0-A Coruña (6c816b4204)
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 "qgsmessagelog.h"
23 #include "qgsproject.h"
24 #include "qgsexception.h"
26 
28 
29 QgsMemoryFeatureIterator::QgsMemoryFeatureIterator( QgsMemoryFeatureSource *source, bool ownSource, const QgsFeatureRequest &request )
30  : QgsAbstractFeatureIteratorFromSource<QgsMemoryFeatureSource>( source, ownSource, request )
31 {
32  if ( mRequest.destinationCrs().isValid() && mRequest.destinationCrs() != mSource->mCrs )
33  {
34  mTransform = QgsCoordinateTransform( mSource->mCrs, mRequest.destinationCrs(), mRequest.transformContext() );
35  }
36  try
37  {
38  mFilterRect = filterRectToSourceCrs( mTransform );
39  }
40  catch ( QgsCsException & )
41  {
42  // can't reproject mFilterRect
43  close();
44  return;
45  }
46 
47  if ( !mSource->mSubsetString.isEmpty() )
48  {
49  mSubsetExpression = qgis::make_unique< QgsExpression >( mSource->mSubsetString );
50  mSubsetExpression->prepare( &mSource->mExpressionContext );
51  }
52 
53  if ( !mFilterRect.isNull() && mRequest.flags() & QgsFeatureRequest::ExactIntersect )
54  {
55  mSelectRectGeom = QgsGeometry::fromRect( mFilterRect );
56  mSelectRectEngine.reset( QgsGeometry::createGeometryEngine( mSelectRectGeom.constGet() ) );
57  mSelectRectEngine->prepareGeometry();
58  }
59 
60  // if there's spatial index, use it!
61  // (but don't use it when selection rect is not specified)
62  if ( !mFilterRect.isNull() && mSource->mSpatialIndex )
63  {
64  mUsingFeatureIdList = true;
65  mFeatureIdList = mSource->mSpatialIndex->intersects( mFilterRect );
66  QgsDebugMsg( "Features returned by spatial index: " + QString::number( mFeatureIdList.count() ) );
67  }
68  else if ( mRequest.filterType() == QgsFeatureRequest::FilterFid )
69  {
70  mUsingFeatureIdList = true;
71  QgsFeatureMap::const_iterator it = mSource->mFeatures.constFind( mRequest.filterFid() );
72  if ( it != mSource->mFeatures.constEnd() )
73  mFeatureIdList.append( mRequest.filterFid() );
74  }
75  else if ( mRequest.filterType() == QgsFeatureRequest::FilterFids )
76  {
77  mUsingFeatureIdList = true;
78  mFeatureIdList = mRequest.filterFids().toList();
79  }
80  else
81  {
82  mUsingFeatureIdList = false;
83  }
84 
85  rewind();
86 }
87 
88 QgsMemoryFeatureIterator::~QgsMemoryFeatureIterator()
89 {
90  close();
91 }
92 
93 bool QgsMemoryFeatureIterator::fetchFeature( QgsFeature &feature )
94 {
95  feature.setValid( false );
96 
97  if ( mClosed )
98  return false;
99 
100  if ( mUsingFeatureIdList )
101  return nextFeatureUsingList( feature );
102  else
103  return nextFeatureTraverseAll( feature );
104 }
105 
106 
107 bool QgsMemoryFeatureIterator::nextFeatureUsingList( QgsFeature &feature )
108 {
109  bool hasFeature = false;
110 
111  // option 1: we have a list of features to traverse
112  QgsFeature candidate;
113  while ( mFeatureIdListIterator != mFeatureIdList.constEnd() )
114  {
115  candidate = mSource->mFeatures.value( *mFeatureIdListIterator );
116  if ( !mFilterRect.isNull() )
117  {
118  if ( mRequest.flags() & QgsFeatureRequest::ExactIntersect )
119  {
120  // do exact check in case we're doing intersection
121  if ( candidate.hasGeometry() && mSelectRectEngine->intersects( candidate.geometry().constGet() ) )
122  hasFeature = true;
123  }
124  else if ( mSource->mSpatialIndex )
125  {
126  // using a spatial index - so we already know that the bounding box intersects correctly
127  hasFeature = true;
128  }
129  else
130  {
131  // do bounding box check if we aren't using a spatial index
132  if ( candidate.hasGeometry() && candidate.geometry().boundingBoxIntersects( mFilterRect ) )
133  hasFeature = true;
134  }
135  }
136  else
137  hasFeature = true;
138 
139  if ( hasFeature && mSubsetExpression )
140  {
141  mSource->mExpressionContext.setFeature( candidate );
142  if ( !mSubsetExpression->evaluate( &mSource->mExpressionContext ).toBool() )
143  hasFeature = false;
144  }
145 
146  if ( hasFeature )
147  break;
148 
149  ++mFeatureIdListIterator;
150  }
151 
152  // copy feature
153  if ( hasFeature )
154  {
155  feature = candidate;
156  ++mFeatureIdListIterator;
157  }
158  else
159  close();
160 
161  if ( hasFeature )
162  {
163  feature.setFields( mSource->mFields ); // allow name-based attribute lookups
164  geometryToDestinationCrs( feature, mTransform );
165  }
166 
167  return hasFeature;
168 }
169 
170 
171 bool QgsMemoryFeatureIterator::nextFeatureTraverseAll( QgsFeature &feature )
172 {
173  bool hasFeature = false;
174 
175  // option 2: traversing the whole layer
176  while ( mSelectIterator != mSource->mFeatures.constEnd() )
177  {
178  if ( mFilterRect.isNull() )
179  {
180  // selection rect empty => using all features
181  hasFeature = true;
182  }
183  else
184  {
185  if ( mRequest.flags() & QgsFeatureRequest::ExactIntersect )
186  {
187  // using exact test when checking for intersection
188  if ( mSelectIterator->hasGeometry() && mSelectRectEngine->intersects( mSelectIterator->geometry().constGet() ) )
189  hasFeature = true;
190  }
191  else
192  {
193  // check just bounding box against rect when not using intersection
194  if ( mSelectIterator->hasGeometry() && mSelectIterator->geometry().boundingBox().intersects( mFilterRect ) )
195  hasFeature = true;
196  }
197  }
198 
199  if ( mSubsetExpression )
200  {
201  mSource->mExpressionContext.setFeature( *mSelectIterator );
202  if ( !mSubsetExpression->evaluate( &mSource->mExpressionContext ).toBool() )
203  hasFeature = false;
204  }
205 
206  if ( hasFeature )
207  break;
208 
209  ++mSelectIterator;
210  }
211 
212  // copy feature
213  if ( hasFeature )
214  {
215  feature = mSelectIterator.value();
216  ++mSelectIterator;
217  feature.setValid( true );
218  feature.setFields( mSource->mFields ); // allow name-based attribute lookups
219  geometryToDestinationCrs( feature, mTransform );
220  }
221  else
222  close();
223 
224  return hasFeature;
225 }
226 
227 bool QgsMemoryFeatureIterator::rewind()
228 {
229  if ( mClosed )
230  return false;
231 
232  if ( mUsingFeatureIdList )
233  mFeatureIdListIterator = mFeatureIdList.constBegin();
234  else
235  mSelectIterator = mSource->mFeatures.constBegin();
236 
237  return true;
238 }
239 
240 bool QgsMemoryFeatureIterator::close()
241 {
242  if ( mClosed )
243  return false;
244 
245  iteratorClosed();
246 
247  mClosed = true;
248  return true;
249 }
250 
251 // -------------------------
252 
253 QgsMemoryFeatureSource::QgsMemoryFeatureSource( const QgsMemoryProvider *p )
254  : mFields( p->mFields )
255  , mFeatures( p->mFeatures )
256  , mSpatialIndex( p->mSpatialIndex ? qgis::make_unique< QgsSpatialIndex >( *p->mSpatialIndex ) : nullptr ) // just shallow copy
257  , mSubsetString( p->mSubsetString )
258  , mCrs( p->mCrs )
259 {
260  mExpressionContext << QgsExpressionContextUtils::globalScope()
262  mExpressionContext.setFields( mFields );
263 }
264 
265 QgsFeatureIterator QgsMemoryFeatureSource::getFeatures( const QgsFeatureRequest &request )
266 {
267  return QgsFeatureIterator( new QgsMemoryFeatureIterator( this, false, request ) );
268 }
269 
bool boundingBoxIntersects(const QgsRectangle &rectangle) const
Returns true if the bounding box of this geometry intersects with a rectangle.
Wrapper for iterator of features from vector data provider or vector layer.
Filter using feature ID.
Filter using feature IDs.
void setFields(const QgsFields &fields, bool initAttributes=false)
Assign a field map with the feature to allow attribute access by attribute name.
Definition: qgsfeature.cpp:162
Use exact geometry intersection (slower) instead of bounding boxes.
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
static QgsExpressionContextScope * projectScope(const QgsProject *project)
Creates a new scope which contains variables and functions relating to a QGIS project.
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:55
bool hasGeometry() const
Returns true if the feature has an associated geometry.
Definition: qgsfeature.cpp:197
static QgsGeometry fromRect(const QgsRectangle &rect)
Creates a new geometry from a QgsRectangle.
static QgsExpressionContextScope * globalScope()
Creates a new scope which contains variables and functions relating to the global QGIS context...
This class wraps a request for features to a vector layer (or directly its vector data provider)...
static QgsGeometryEngine * createGeometryEngine(const QgsAbstractGeometry *geometry)
Creates and returns a new geometry engine.
const QgsAbstractGeometry * constGet() const
Returns a non-modifiable (const) reference to the underlying abstract geometry primitive.
void setValid(bool validity)
Sets the validity of the feature.
Definition: qgsfeature.cpp:188
A spatial index for QgsFeature objects.
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:442
Class for doing transforms between two map coordinate systems.
QgsGeometry geometry
Definition: qgsfeature.h:67
Custom exception class for Coordinate Reference System related exceptions.
Definition: qgsexception.h:65
Helper template that cares of two things: 1.