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