1 /***************************************************************************
2     qgscachedfeatureiterator.cpp
3      --------------------------------------
4     Date                 : 12.2.2013
5     Copyright            : (C) 2013 Matthias Kuhn
6     Email                : matthias at opengis dot ch
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  ***************************************************************************/
15 
16 #include "qgscachedfeatureiterator.h"
17 #include "qgsvectorlayercache.h"
18 #include "qgsexception.h"
19 #include "qgsvectorlayer.h"
20 #include "qgsgeometryengine.h"
21 
QgsCachedFeatureIterator(QgsVectorLayerCache * vlCache,const QgsFeatureRequest & featureRequest)22 QgsCachedFeatureIterator::QgsCachedFeatureIterator( QgsVectorLayerCache *vlCache, const QgsFeatureRequest &featureRequest )
23   : QgsAbstractFeatureIterator( featureRequest )
24   , mVectorLayerCache( vlCache )
25 {
26   if ( mRequest.destinationCrs().isValid() && mRequest.destinationCrs() != mVectorLayerCache->sourceCrs() )
27   {
28     mTransform = QgsCoordinateTransform( mVectorLayerCache->sourceCrs(), mRequest.destinationCrs(), mRequest.transformContext() );
29   }
30   try
31   {
32     mFilterRect = filterRectToSourceCrs( mTransform );
33   }
34   catch ( QgsCsException & )
35   {
36     // can't reproject mFilterRect
37     close();
38     return;
39   }
40 
41   // prepare spatial filter geometries for optimal speed
42   switch ( mRequest.spatialFilterType() )
43   {
44     case Qgis::SpatialFilterType::NoFilter:
45     case Qgis::SpatialFilterType::BoundingBox:
46       break;
47 
48     case Qgis::SpatialFilterType::DistanceWithin:
49       if ( !mRequest.referenceGeometry().isEmpty() )
50       {
51         mDistanceWithinGeom = mRequest.referenceGeometry();
52         mDistanceWithinEngine.reset( QgsGeometry::createGeometryEngine( mDistanceWithinGeom.constGet() ) );
53         mDistanceWithinEngine->prepareGeometry();
54         mDistanceWithin = mRequest.distanceWithin();
55       }
56       break;
57   }
58 
59   if ( !mFilterRect.isNull() )
60   {
61     // update request to be the unprojected filter rect
62     mRequest.setFilterRect( mFilterRect );
63   }
64 
65   switch ( featureRequest.filterType() )
66   {
67     case QgsFeatureRequest::FilterFids:
68       mFeatureIds = QList< QgsFeatureId >( qgis::setToList( featureRequest.filterFids() ) );
69       break;
70 
71     case QgsFeatureRequest::FilterFid:
72       mFeatureIds = QList< QgsFeatureId >() << featureRequest.filterFid();
73       break;
74 
75     default:
76 #if QT_VERSION < QT_VERSION_CHECK(5, 15, 0)
77       mFeatureIds.clear();
78       mFeatureIds.reserve( static_cast< int >( mVectorLayerCache->mCacheOrderedKeys.size() ) );
79       for ( auto it = mVectorLayerCache->mCacheOrderedKeys.begin(); it != mVectorLayerCache->mCacheOrderedKeys.end(); ++it )
80         mFeatureIds << *it;
81 #else
82       mFeatureIds = QList( mVectorLayerCache->mCacheOrderedKeys.begin(), mVectorLayerCache->mCacheOrderedKeys.end() );
83 #endif
84       break;
85   }
86 
87   mFeatureIdIterator = mFeatureIds.constBegin();
88 
89   if ( mFeatureIdIterator == mFeatureIds.constEnd() )
90     close();
91 }
92 
93 QgsCachedFeatureIterator::~QgsCachedFeatureIterator() = default;
94 
fetchFeature(QgsFeature & f)95 bool QgsCachedFeatureIterator::fetchFeature( QgsFeature &f )
96 {
97   f.setValid( false );
98 
99   if ( mClosed )
100     return false;
101 
102   while ( mFeatureIdIterator != mFeatureIds.constEnd() )
103   {
104     if ( !mVectorLayerCache->mCache.contains( *mFeatureIdIterator ) )
105     {
106       ++mFeatureIdIterator;
107       continue;
108     }
109 
110     f = QgsFeature( *mVectorLayerCache->mCache[*mFeatureIdIterator]->feature() );
111     ++mFeatureIdIterator;
112     if ( mRequest.acceptFeature( f ) )
113     {
114       f.setValid( true );
115       geometryToDestinationCrs( f, mTransform );
116 
117       bool result = true;
118       if ( mDistanceWithinEngine && mDistanceWithinEngine->distance( f.geometry().constGet() ) > mDistanceWithin )
119       {
120         f.setValid( false );
121         result = false;
122       }
123 
124       if ( result )
125         return true;
126     }
127   }
128   close();
129   return false;
130 }
131 
rewind()132 bool QgsCachedFeatureIterator::rewind()
133 {
134   mFeatureIdIterator = mFeatureIds.constBegin();
135   return true;
136 }
137 
close()138 bool QgsCachedFeatureIterator::close()
139 {
140   mClosed = true;
141   mFeatureIds.clear();
142   return true;
143 }
144 
QgsCachedFeatureWriterIterator(QgsVectorLayerCache * vlCache,const QgsFeatureRequest & featureRequest)145 QgsCachedFeatureWriterIterator::QgsCachedFeatureWriterIterator( QgsVectorLayerCache *vlCache, const QgsFeatureRequest &featureRequest )
146   : QgsAbstractFeatureIterator( featureRequest )
147   , mVectorLayerCache( vlCache )
148 {
149   if ( mRequest.destinationCrs().isValid() && mRequest.destinationCrs() != mVectorLayerCache->sourceCrs() )
150   {
151     mTransform = QgsCoordinateTransform( mVectorLayerCache->sourceCrs(), mRequest.destinationCrs(), mRequest.transformContext() );
152   }
153   try
154   {
155     mFilterRect = filterRectToSourceCrs( mTransform );
156   }
157   catch ( QgsCsException & )
158   {
159     // can't reproject mFilterRect
160     close();
161     return;
162   }
163   if ( !mFilterRect.isNull() )
164   {
165     // update request to be the unprojected filter rect
166     mRequest.setFilterRect( mFilterRect );
167   }
168 
169   mFeatIt = vlCache->layer()->getFeatures( mRequest );
170 }
171 
fetchFeature(QgsFeature & f)172 bool QgsCachedFeatureWriterIterator::fetchFeature( QgsFeature &f )
173 {
174   if ( mClosed )
175   {
176     f.setValid( false );
177     return false;
178   }
179   if ( mFeatIt.nextFeature( f ) )
180   {
181     // As long as features can be fetched from the provider: Write them to cache
182     mVectorLayerCache->cacheFeature( f );
183     mFids.insert( f.id() );
184     geometryToDestinationCrs( f, mTransform );
185     return true;
186   }
187   else
188   {
189     // Once no more features can be fetched: Inform the cache, that
190     // the request has been completed
191     mVectorLayerCache->requestCompleted( mRequest, mFids );
192     return false;
193   }
194 }
195 
rewind()196 bool QgsCachedFeatureWriterIterator::rewind()
197 {
198   mFids.clear();
199   return mFeatIt.rewind();
200 }
201 
close()202 bool QgsCachedFeatureWriterIterator::close()
203 {
204   mClosed = true;
205   return mFeatIt.close();
206 }
207