QGIS API Documentation  3.0.2-Girona (307d082)
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
75  {
76  mUsingFeatureIdList = false;
77  }
78 
79  rewind();
80 }
81 
82 QgsMemoryFeatureIterator::~QgsMemoryFeatureIterator()
83 {
84  close();
85 }
86 
87 bool QgsMemoryFeatureIterator::fetchFeature( QgsFeature &feature )
88 {
89  feature.setValid( false );
90 
91  if ( mClosed )
92  return false;
93 
94  if ( mUsingFeatureIdList )
95  return nextFeatureUsingList( feature );
96  else
97  return nextFeatureTraverseAll( feature );
98 }
99 
100 
101 bool QgsMemoryFeatureIterator::nextFeatureUsingList( QgsFeature &feature )
102 {
103  bool hasFeature = false;
104 
105  // option 1: we have a list of features to traverse
106  while ( mFeatureIdListIterator != mFeatureIdList.constEnd() )
107  {
108  if ( !mFilterRect.isNull() && mRequest.flags() & QgsFeatureRequest::ExactIntersect )
109  {
110  // do exact check in case we're doing intersection
111  if ( mSource->mFeatures.value( *mFeatureIdListIterator ).hasGeometry() && mSelectRectEngine->intersects( mSource->mFeatures.value( *mFeatureIdListIterator ).geometry().constGet() ) )
112  hasFeature = true;
113  }
114  else
115  hasFeature = true;
116 
117  if ( mSubsetExpression )
118  {
119  mSource->mExpressionContext.setFeature( mSource->mFeatures.value( *mFeatureIdListIterator ) );
120  if ( !mSubsetExpression->evaluate( &mSource->mExpressionContext ).toBool() )
121  hasFeature = false;
122  }
123 
124  if ( hasFeature )
125  break;
126 
127  ++mFeatureIdListIterator;
128  }
129 
130  // copy feature
131  if ( hasFeature )
132  {
133  feature = mSource->mFeatures.value( *mFeatureIdListIterator );
134  ++mFeatureIdListIterator;
135  }
136  else
137  close();
138 
139  if ( hasFeature )
140  {
141  feature.setFields( mSource->mFields ); // allow name-based attribute lookups
142  geometryToDestinationCrs( feature, mTransform );
143  }
144 
145  return hasFeature;
146 }
147 
148 
149 bool QgsMemoryFeatureIterator::nextFeatureTraverseAll( QgsFeature &feature )
150 {
151  bool hasFeature = false;
152 
153  // option 2: traversing the whole layer
154  while ( mSelectIterator != mSource->mFeatures.constEnd() )
155  {
156  if ( mFilterRect.isNull() )
157  {
158  // selection rect empty => using all features
159  hasFeature = true;
160  }
161  else
162  {
163  if ( mRequest.flags() & QgsFeatureRequest::ExactIntersect )
164  {
165  // using exact test when checking for intersection
166  if ( mSelectIterator->hasGeometry() && mSelectRectEngine->intersects( mSelectIterator->geometry().constGet() ) )
167  hasFeature = true;
168  }
169  else
170  {
171  // check just bounding box against rect when not using intersection
172  if ( mSelectIterator->hasGeometry() && mSelectIterator->geometry().boundingBox().intersects( mFilterRect ) )
173  hasFeature = true;
174  }
175  }
176 
177  if ( mSubsetExpression )
178  {
179  mSource->mExpressionContext.setFeature( *mSelectIterator );
180  if ( !mSubsetExpression->evaluate( &mSource->mExpressionContext ).toBool() )
181  hasFeature = false;
182  }
183 
184  if ( hasFeature )
185  break;
186 
187  ++mSelectIterator;
188  }
189 
190  // copy feature
191  if ( hasFeature )
192  {
193  feature = mSelectIterator.value();
194  ++mSelectIterator;
195  feature.setValid( true );
196  feature.setFields( mSource->mFields ); // allow name-based attribute lookups
197  geometryToDestinationCrs( feature, mTransform );
198  }
199  else
200  close();
201 
202  return hasFeature;
203 }
204 
205 bool QgsMemoryFeatureIterator::rewind()
206 {
207  if ( mClosed )
208  return false;
209 
210  if ( mUsingFeatureIdList )
211  mFeatureIdListIterator = mFeatureIdList.constBegin();
212  else
213  mSelectIterator = mSource->mFeatures.constBegin();
214 
215  return true;
216 }
217 
218 bool QgsMemoryFeatureIterator::close()
219 {
220  if ( mClosed )
221  return false;
222 
223  iteratorClosed();
224 
225  mClosed = true;
226  return true;
227 }
228 
229 // -------------------------
230 
231 QgsMemoryFeatureSource::QgsMemoryFeatureSource( const QgsMemoryProvider *p )
232  : mFields( p->mFields )
233  , mFeatures( p->mFeatures )
234  , mSpatialIndex( p->mSpatialIndex ? qgis::make_unique< QgsSpatialIndex >( *p->mSpatialIndex ) : nullptr ) // just shallow copy
235  , mSubsetString( p->mSubsetString )
236  , mCrs( p->mCrs )
237 {
238  mExpressionContext << QgsExpressionContextUtils::globalScope()
240  mExpressionContext.setFields( mFields );
241 }
242 
243 QgsFeatureIterator QgsMemoryFeatureSource::getFeatures( const QgsFeatureRequest &request )
244 {
245  return QgsFeatureIterator( new QgsMemoryFeatureIterator( this, false, request ) );
246 }
247 
Wrapper for iterator of features from vector data provider or vector layer.
Filter using feature ID.
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:155
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:62
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.
void setValid(bool validity)
Sets the validity of the feature.
Definition: qgsfeature.cpp:181
A spatial index for QgsFeature objects.
static QgsProject * instance()
Returns the QgsProject singleton instance.
Definition: qgsproject.cpp:383
Class for doing transforms between two map coordinate systems.
Custom exception class for Coordinate Reference System related exceptions.
Definition: qgsexception.h:65
Helper template that cares of two things: 1.