Branch data Line data Source code
1 : : /***************************************************************************
2 : : qgsvectorlayercache.h
3 : : Cache features of a vector layer
4 : : -------------------
5 : : begin : January 2013
6 : : copyright : (C) Matthias Kuhn
7 : : email : matthias at opengis dot ch
8 : :
9 : : ***************************************************************************
10 : : * *
11 : : * This program is free software; you can redistribute it and/or modify *
12 : : * it under the terms of the GNU General Public License as published by *
13 : : * the Free Software Foundation; either version 2 of the License, or *
14 : : * (at your option) any later version. *
15 : : * *
16 : : ***************************************************************************/
17 : :
18 : :
19 : : #ifndef QgsVectorLayerCache_H
20 : : #define QgsVectorLayerCache_H
21 : :
22 : : #include "qgis_core.h"
23 : : #include "qgis_sip.h"
24 : : #include "qgsfield.h"
25 : : #include "qgsfeaturerequest.h"
26 : : #include "qgsfeatureiterator.h"
27 : :
28 : : #include <QCache>
29 : :
30 : : class QgsVectorLayer;
31 : : class QgsFeature;
32 : : class QgsCachedFeatureIterator;
33 : : class QgsAbstractCacheIndex;
34 : :
35 : : /**
36 : : * \ingroup core
37 : : * \brief This class caches features of a given QgsVectorLayer.
38 : : *
39 : : * \brief
40 : : * The cached features can be indexed by QgsAbstractCacheIndex.
41 : : *
42 : : * Proper indexing for a given use-case may speed up performance substantially.
43 : : */
44 : :
45 : : class CORE_EXPORT QgsVectorLayerCache : public QObject
46 : : {
47 : : Q_OBJECT
48 : :
49 : : private:
50 : :
51 : : /**
52 : : * This is a wrapper class around a cached QgsFeature, which
53 : : * will inform the cache, when it has been deleted, so indexes can be
54 : : * updated that the wrapped feature needs to be fetched again if needed.
55 : : */
56 : : class QgsCachedFeature
57 : : {
58 : : public:
59 : :
60 : : /**
61 : : * Will create a new cached feature.
62 : : *
63 : : * \param feat The feature to cache. A copy will be made.
64 : : * \param vlCache The cache to inform when the feature has been removed from the cache.
65 : : */
66 : 0 : QgsCachedFeature( const QgsFeature &feat, QgsVectorLayerCache *vlCache )
67 : 0 : : mCache( vlCache )
68 : : {
69 : 0 : mFeature = new QgsFeature( feat );
70 : 0 : }
71 : :
72 : 0 : ~QgsCachedFeature()
73 : : {
74 : : // That's the reason we need this wrapper:
75 : : // Inform the cache that this feature has been removed
76 : 0 : mCache->featureRemoved( mFeature->id() );
77 : 0 : delete mFeature;
78 : 0 : }
79 : :
80 : 0 : inline const QgsFeature *feature() { return mFeature; }
81 : :
82 : : private:
83 : 0 : QgsFeature *mFeature = nullptr;
84 : : QgsVectorLayerCache *mCache = nullptr;
85 : :
86 : : friend class QgsVectorLayerCache;
87 : : Q_DISABLE_COPY( QgsCachedFeature )
88 : : };
89 : :
90 : : public:
91 : : QgsVectorLayerCache( QgsVectorLayer *layer, int cacheSize, QObject *parent SIP_TRANSFERTHIS = nullptr );
92 : : ~QgsVectorLayerCache() override;
93 : :
94 : : /**
95 : : * Sets the maximum number of features to keep in the cache. Some features will be removed from
96 : : * the cache if the number is smaller than the previous size of the cache.
97 : : *
98 : : * \param cacheSize indicates the maximum number of features to keep in the cache
99 : : */
100 : : void setCacheSize( int cacheSize );
101 : :
102 : : /**
103 : : * \brief
104 : : * Returns the maximum number of features this cache will hold.
105 : : * In case full caching is enabled, this number can change, as new features get added.
106 : : *
107 : : * \returns int
108 : : */
109 : : int cacheSize();
110 : :
111 : : /**
112 : : * Enable or disable the caching of geometries
113 : : *
114 : : * \param cacheGeometry Enable or disable the caching of geometries
115 : : * \see cacheGeometry()
116 : : */
117 : : void setCacheGeometry( bool cacheGeometry );
118 : :
119 : : /**
120 : : * Returns TRUE if the cache will fetch and cache feature geometries.
121 : : * \see setCacheGeometry()
122 : : * \since QGIS 3.0
123 : : */
124 : : bool cacheGeometry() const { return mCacheGeometry; }
125 : :
126 : : /**
127 : : * Set the subset of attributes to be cached
128 : : *
129 : : * \param attributes The attributes to be cached
130 : : */
131 : : void setCacheSubsetOfAttributes( const QgsAttributeList &attributes );
132 : :
133 : : /**
134 : : * If this is enabled, the subset of cached attributes will automatically be extended
135 : : * to also include newly added attributes.
136 : : *
137 : : * \param cacheAddedAttributes Automatically cache new attributes
138 : : */
139 : : void setCacheAddedAttributes( bool cacheAddedAttributes );
140 : :
141 : : /**
142 : : * \brief
143 : : * This enables or disables full caching.
144 : : * If enabled, all features will be held in the cache. The cache size will incrementally
145 : : * be increased to offer space for all features.
146 : : * When enabled, all features will be read into cache. As this feature will most likely
147 : : * be used for slow data sources, be aware, that the call to this method might take a long time.
148 : : *
149 : : * \param fullCache TRUE: enable full caching, FALSE: disable full caching
150 : : * \note when a cache is invalidated() (e.g. by adding an attribute to a layer) this setting
151 : : * is reset. A full cache rebuild must be performed by calling setFullCache( TRUE ) again.
152 : : * \see hasFullCache()
153 : : */
154 : : void setFullCache( bool fullCache );
155 : :
156 : : /**
157 : : * Returns TRUE if the cache is complete, ie it contains all features. This may happen as
158 : : * a result of a call to setFullCache() or by through a feature request which resulted in
159 : : * all available features being cached.
160 : : * \see setFullCache()
161 : : * \since QGIS 3.0
162 : : */
163 : 0 : bool hasFullCache() const { return mFullCache; }
164 : :
165 : : /**
166 : : * \brief
167 : : * Adds a QgsAbstractCacheIndex to this cache. Cache indices know about features present
168 : : * in this cache and decide, if enough information is present in the cache to respond to a QgsFeatureRequest.
169 : : * The layer cache will take ownership of the index.
170 : : *
171 : : * \param cacheIndex The cache index to add.
172 : : */
173 : : void addCacheIndex( QgsAbstractCacheIndex *cacheIndex SIP_TRANSFER );
174 : :
175 : : /**
176 : : * Query this VectorLayerCache for features.
177 : : * If the VectorLayerCache (and moreover any of its indices) is able to satisfy
178 : : * the request, the returned QgsFeatureIterator will iterate over cached features.
179 : : * If it's not possible to fully satisfy the request from the cache, part or all of the features
180 : : * will be requested from the data provider.
181 : : * \param featureRequest The request specifying filter and required data.
182 : : * \returns An iterator over the requested data.
183 : : */
184 : : QgsFeatureIterator getFeatures( const QgsFeatureRequest &featureRequest = QgsFeatureRequest() );
185 : :
186 : : /**
187 : : * Query the layer for features matching a given expression.
188 : : */
189 : : inline QgsFeatureIterator getFeatures( const QString &expression )
190 : : {
191 : : return getFeatures( QgsFeatureRequest( expression ) );
192 : : }
193 : :
194 : : /**
195 : : * Query the layer for the feature with the given id.
196 : : * If there is no such feature, the returned feature will be invalid.
197 : : */
198 : : inline QgsFeature getFeature( QgsFeatureId fid )
199 : : {
200 : : QgsFeature feature;
201 : : getFeatures( QgsFeatureRequest( fid ) ).nextFeature( feature );
202 : : return feature;
203 : : }
204 : :
205 : : /**
206 : : * Query the layer for the features with the given ids.
207 : : */
208 : : inline QgsFeatureIterator getFeatures( const QgsFeatureIds &fids )
209 : : {
210 : : return getFeatures( QgsFeatureRequest( fids ) );
211 : : }
212 : :
213 : : /**
214 : : * Query the layer for the features which intersect the specified rectangle.
215 : : */
216 : : inline QgsFeatureIterator getFeatures( const QgsRectangle &rectangle )
217 : : {
218 : : return getFeatures( QgsFeatureRequest( rectangle ) );
219 : : }
220 : :
221 : : /**
222 : : * Check if a certain feature id is cached.
223 : : * \param fid The feature id to look for
224 : : * \returns TRUE if this id is in the cache
225 : : * \see cachedFeatureIds()
226 : : */
227 : : bool isFidCached( QgsFeatureId fid ) const;
228 : :
229 : : /**
230 : : * Returns the set of feature IDs for features which are cached.
231 : : * \see isFidCached()
232 : : * \since QGIS 3.0
233 : : */
234 : 0 : QgsFeatureIds cachedFeatureIds() const { return qgis::listToSet( mCache.keys() ); }
235 : :
236 : : /**
237 : : * Gets the feature at the given feature id. Considers the changed, added, deleted and permanent features
238 : : * \param featureId The id of the feature to query
239 : : * \param feature The result of the operation will be written to this feature
240 : : * \param skipCache Will query the layer regardless if the feature is in the cache already
241 : : * \returns TRUE in case of success
242 : : */
243 : : bool featureAtId( QgsFeatureId featureId, QgsFeature &feature, bool skipCache = false );
244 : :
245 : : /**
246 : : * Removes the feature identified by fid from the cache if present.
247 : : * \param fid The id of the feature to delete
248 : : * \returns TRUE if the feature was removed, FALSE if the feature id was not found in the cache
249 : : */
250 : : bool removeCachedFeature( QgsFeatureId fid );
251 : :
252 : : /**
253 : : * Returns the layer to which this cache belongs
254 : : */
255 : : QgsVectorLayer *layer();
256 : :
257 : : /**
258 : : * Returns the coordinate reference system for features in the cache.
259 : : */
260 : : QgsCoordinateReferenceSystem sourceCrs() const;
261 : :
262 : : /**
263 : : * Returns the fields associated with features in the cache.
264 : : */
265 : : QgsFields fields() const;
266 : :
267 : : /**
268 : : * Returns the geometry type for features in the cache.
269 : : */
270 : : QgsWkbTypes::Type wkbType() const;
271 : :
272 : : #ifdef SIP_RUN
273 : :
274 : : /**
275 : : * Returns the number of features contained in the source, or -1
276 : : * if the feature count is unknown.
277 : : */
278 : : int __len__() const;
279 : : % MethodCode
280 : : sipRes = sipCpp->featureCount();
281 : : % End
282 : :
283 : : //! Ensures that bool(obj) returns TRUE (otherwise __len__() would be used)
284 : : int __bool__() const;
285 : : % MethodCode
286 : : sipRes = true;
287 : : % End
288 : : #endif
289 : :
290 : : /**
291 : : * Returns the number of features contained in the source, or -1
292 : : * if the feature count is unknown.
293 : : */
294 : : long featureCount() const;
295 : :
296 : : protected:
297 : :
298 : : /**
299 : : * \brief
300 : : * Gets called, whenever the full list of feature ids for a certain request is known.
301 : : * Broadcasts this information to indices, so they can update their tables.
302 : : *
303 : : * \param featureRequest The feature request that was answered
304 : : * \param fids The feature ids that have been returned
305 : : */
306 : : void requestCompleted( const QgsFeatureRequest &featureRequest, const QgsFeatureIds &fids );
307 : :
308 : : /**
309 : : * \brief
310 : : * Gets called, whenever a feature has been removed.
311 : : * Broadcasts this information to indices, so they can invalidate their cache if required.
312 : : *
313 : : * \param fid The feature id of the removed feature.
314 : : */
315 : : void featureRemoved( QgsFeatureId fid );
316 : :
317 : : /**
318 : : * \brief
319 : : * Checks if the information required to complete the request is cached.
320 : : * i.e. If all attributes required and the geometry is held in the cache.
321 : : * Please note, that this does not check, if the requested features are cached.
322 : : *
323 : : *
324 : : * \param featureRequest The QgsFeatureRequest to be answered
325 : : * \returns TRUE if the information is being cached, FALSE if not
326 : : */
327 : : bool checkInformationCovered( const QgsFeatureRequest &featureRequest );
328 : :
329 : :
330 : : signals:
331 : :
332 : : /**
333 : : * When filling the cache, this signal gets emitted periodically to notify about the progress
334 : : * and to be able to cancel an operation.
335 : : *
336 : : * \param i The number of already fetched features
337 : : * \param cancel A reference to a boolean variable. Set to TRUE and the operation will be canceled.
338 : : *
339 : : * \note not available in Python bindings
340 : : */
341 : : void progress( int i, bool &cancel ) SIP_SKIP;
342 : :
343 : : /**
344 : : * When filling the cache, this signal gets emitted once the cache is fully initialized.
345 : : */
346 : : void finished();
347 : :
348 : : /**
349 : : * \brief Is emitted when the cached layer is deleted. Is emitted when the cached layers layerDelete()
350 : : * signal is being emitted, but before the local reference to it has been set to NULLPTR. So call to
351 : : * layer() will still return a valid pointer for cleanup purpose.
352 : : */
353 : : void cachedLayerDeleted();
354 : :
355 : : /**
356 : : * Emitted when an attribute is changed. Is re-emitted after the layer itself emits this signal.
357 : : * You should connect to this signal, to be sure, to not get a cached value if querying the cache.
358 : : */
359 : : void attributeValueChanged( QgsFeatureId fid, int field, const QVariant &value );
360 : :
361 : : /**
362 : : * Emitted when a new feature has been added to the layer and this cache.
363 : : * You should connect to this signal instead of the layers', if you want to be sure
364 : : * that this cache has updated information for the new feature
365 : : *
366 : : * \param fid The featureid of the changed feature
367 : : */
368 : : void featureAdded( QgsFeatureId fid );
369 : :
370 : : /**
371 : : * The cache has been invalidated and cleared. Note that when a cache is invalidated
372 : : * the fullCache() setting will be cleared, and a full cache rebuild via setFullCache( TRUE )
373 : : * will need to be performed.
374 : : */
375 : : void invalidated();
376 : :
377 : : private slots:
378 : : void onAttributeValueChanged( QgsFeatureId fid, int field, const QVariant &value );
379 : : void onJoinAttributeValueChanged( QgsFeatureId fid, int field, const QVariant &value );
380 : : void featureDeleted( QgsFeatureId fid );
381 : : void onFeatureAdded( QgsFeatureId fid );
382 : : void attributeAdded( int field );
383 : : void attributeDeleted( int field );
384 : : void geometryChanged( QgsFeatureId fid, const QgsGeometry &geom );
385 : : void layerDeleted();
386 : : void invalidate();
387 : :
388 : : private:
389 : :
390 : : void connectJoinedLayers() const;
391 : :
392 : 0 : inline void cacheFeature( QgsFeature &feat )
393 : : {
394 : 0 : QgsCachedFeature *cachedFeature = new QgsCachedFeature( feat, this );
395 : 0 : mCache.insert( feat.id(), cachedFeature );
396 : 0 : }
397 : :
398 : : QgsVectorLayer *mLayer = nullptr;
399 : : QCache< QgsFeatureId, QgsCachedFeature > mCache;
400 : :
401 : : bool mCacheGeometry = true;
402 : : bool mFullCache = false;
403 : : QList<QgsAbstractCacheIndex *> mCacheIndices;
404 : :
405 : : QgsAttributeList mCachedAttributes;
406 : :
407 : : friend class QgsCachedFeatureIterator;
408 : : friend class QgsCachedFeatureWriterIterator;
409 : : friend class QgsCachedFeature;
410 : :
411 : : /**
412 : : * Returns TRUE if the cache contains all the features required for a specified request.
413 : : * \param featureRequest feature request
414 : : * \param it will be set to iterator for matching features
415 : : * \returns TRUE if cache can satisfy request
416 : : * \note this method only checks for available features, not whether the cache
417 : : * contains required attributes or geometry. For that, use checkInformationCovered()
418 : : */
419 : : bool canUseCacheForRequest( const QgsFeatureRequest &featureRequest, QgsFeatureIterator &it );
420 : :
421 : : friend class TestVectorLayerCache;
422 : : };
423 : : #endif // QgsVectorLayerCache_H
|