QGIS API Documentation 3.35.0-Master (6cf940c20d6)
Loading...
Searching...
No Matches
qgsvectorlayercache.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsvectorlayercache.cpp
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#include "qgsvectorlayercache.h"
19#include "qgscacheindex.h"
23#include "qgsvectorlayer.h"
24
25#include <QElapsedTimer>
26
27QgsVectorLayerCache::QgsVectorLayerCache( QgsVectorLayer *layer, int cacheSize, QObject *parent )
28 : QObject( parent )
29 , mLayer( layer )
30{
31 mCache.setMaxCost( cacheSize );
32
33 connect( mLayer, &QgsVectorLayer::featureDeleted, this, &QgsVectorLayerCache::featureDeleted );
34 connect( mLayer, &QgsVectorLayer::featureAdded, this, &QgsVectorLayerCache::onFeatureAdded );
35 connect( mLayer, &QgsVectorLayer::destroyed, this, &QgsVectorLayerCache::layerDeleted );
36
37 setCacheGeometry( true );
40
41 connect( mLayer, &QgsVectorLayer::attributeDeleted, this, &QgsVectorLayerCache::attributeDeleted );
42 connect( mLayer, &QgsVectorLayer::updatedFields, this, &QgsVectorLayerCache::invalidate );
43 connect( mLayer, &QgsVectorLayer::dataChanged, this, &QgsVectorLayerCache::invalidate );
44 connect( mLayer, &QgsVectorLayer::attributeValueChanged, this, &QgsVectorLayerCache::onAttributeValueChanged );
45
46 connectJoinedLayers();
47}
48
50{
51 qDeleteAll( mCacheIndices );
52 mCacheIndices.clear();
53}
54
56{
57 mCache.setMaxCost( cacheSize );
58}
59
61{
62 return mCache.maxCost();
63}
64
65void QgsVectorLayerCache::setCacheGeometry( bool cacheGeometry )
66{
67 bool shouldCacheGeometry = cacheGeometry && mLayer->isSpatial();
68 bool mustInvalidate = shouldCacheGeometry && !mCacheGeometry; // going from no geometry -> geometry, so have to clear existing cache entries
69 mCacheGeometry = shouldCacheGeometry;
70 if ( cacheGeometry )
71 {
72 connect( mLayer, &QgsVectorLayer::geometryChanged, this, &QgsVectorLayerCache::geometryChanged, Qt::UniqueConnection );
73 }
74 else
75 {
76 disconnect( mLayer, &QgsVectorLayer::geometryChanged, this, &QgsVectorLayerCache::geometryChanged );
77 }
78 if ( mustInvalidate )
79 {
80 invalidate();
81 }
82}
83
85{
86 if ( ! mCache.isEmpty() && ! QSet<int>( mCachedAttributes.cbegin(), mCachedAttributes.cend() )
87 .contains( QSet<int>( attributes.cbegin(), attributes.cend( ) ) ) )
88 invalidate();
89 mCachedAttributes = attributes;
90}
91
93{
94 return mCachedAttributes;
95}
96
98{
99 mFullCache = fullCache;
100
101 if ( mFullCache )
102 {
103 // Add a little more than necessary...
104 setCacheSize( mLayer->featureCount() + 100 );
105
106 // Initialize the cache...
108 .setSubsetOfAttributes( mCachedAttributes )
109 .setFlags( mCacheGeometry ? QgsFeatureRequest::NoFlags : QgsFeatureRequest::NoGeometry ) ) );
110
111 int i = 0;
112
113 QElapsedTimer t;
114 t.start();
115
116 QgsFeature f;
117 while ( it.nextFeature( f ) )
118 {
119 ++i;
120
121 if ( t.elapsed() > 1000 )
122 {
123 bool cancel = false;
124 emit progress( i, cancel );
125 if ( cancel )
126 break;
127
128 t.restart();
129 }
130 }
131
132 it.close();
133
134 emit finished();
135 }
136}
137
139{
140 mCacheIndices.append( cacheIndex );
141}
142
143void QgsVectorLayerCache::setCacheAddedAttributes( bool cacheAddedAttributes )
144{
145 if ( cacheAddedAttributes )
146 {
147 connect( mLayer, &QgsVectorLayer::attributeAdded, this, &QgsVectorLayerCache::attributeAdded );
148 }
149 else
150 {
151 disconnect( mLayer, &QgsVectorLayer::attributeAdded, this, &QgsVectorLayerCache::attributeAdded );
152 }
153}
154
155bool QgsVectorLayerCache::featureAtId( QgsFeatureId featureId, QgsFeature &feature, bool skipCache )
156{
157 bool featureFound = false;
158
159 QgsCachedFeature *cachedFeature = nullptr;
160
161 if ( !skipCache )
162 {
163 cachedFeature = mCache[ featureId ];
164 }
165
166 if ( cachedFeature )
167 {
168 feature = QgsFeature( *cachedFeature->feature() );
169 featureFound = true;
170 }
171 else
172 {
173 QgsFeatureRequest request { featureId };
174 const bool allAttrsFetched { mCachedAttributes.count( ) == mLayer->fields().count() };
175 if ( ! allAttrsFetched )
176 {
177 request.setSubsetOfAttributes( mCachedAttributes );
178 }
179 if ( !mCacheGeometry )
180 {
181 request.setFlags( request.flags().setFlag( QgsFeatureRequest::NoGeometry ) );
182 }
183 if ( mLayer->getFeatures( request ).nextFeature( feature ) )
184 {
185 cacheFeature( feature, allAttrsFetched );
186 featureFound = true;
187 }
188 }
189
190 return featureFound;
191}
192
194{
195
196 bool featureFound = false;
197
198 QgsCachedFeature *cachedFeature = nullptr;
199
200 if ( !skipCache )
201 {
202 cachedFeature = mCache[ featureId ];
203 }
204
205 if ( cachedFeature && cachedFeature->allAttributesFetched() )
206 {
207 feature = QgsFeature( *cachedFeature->feature() );
208 featureFound = true;
209 }
210 else if ( mLayer->getFeatures( QgsFeatureRequest()
211 .setFilterFid( featureId )
212 .setFlags( !mCacheGeometry ? QgsFeatureRequest::NoGeometry : QgsFeatureRequest::Flags() ) )
213 .nextFeature( feature ) )
214 {
215 cacheFeature( feature, true );
216 featureFound = true;
217 }
218
219 return featureFound;
220}
221
223{
224 bool removed = mCache.remove( fid );
225 if ( removed )
226 {
227 if ( auto unorderedIt = std::find( mCacheUnorderedKeys.begin(), mCacheUnorderedKeys.end(), fid ); unorderedIt != mCacheUnorderedKeys.end() )
228 {
229 mCacheUnorderedKeys.erase( unorderedIt );
230
231 if ( auto orderedIt = std::find( mCacheOrderedKeys.begin(), mCacheOrderedKeys.end(), fid ); orderedIt != mCacheOrderedKeys.end() )
232 mCacheOrderedKeys.erase( orderedIt );
233 }
234 }
235 return removed;
236}
237
239{
240 return mLayer;
241}
242
247
249{
250 return mLayer->wkbType();
251}
252
254{
255 return mLayer->fields();
256}
257
259{
260 return mLayer->featureCount();
261}
262
264{
265 // If a request is too large for the cache don't notify to prevent from indexing incomplete requests
266 if ( fids.count() <= mCache.size() )
267 {
268 for ( const auto &idx : std::as_const( mCacheIndices ) )
269 {
270 idx->requestCompleted( featureRequest, fids );
271 }
272 if ( featureRequest.filterType() == QgsFeatureRequest::FilterNone &&
273 ( featureRequest.spatialFilterType() == Qgis::SpatialFilterType::NoFilter || featureRequest.filterRect().contains( mLayer->extent() ) ) )
274 {
275 mFullCache = true;
276 }
277 }
278}
279
281{
282 const auto constMCacheIndices = mCacheIndices;
283 for ( QgsAbstractCacheIndex *idx : constMCacheIndices )
284 {
285 idx->flushFeature( fid );
286 }
287}
288
289void QgsVectorLayerCache::onAttributeValueChanged( QgsFeatureId fid, int field, const QVariant &value )
290{
291 QgsCachedFeature *cachedFeat = mCache[ fid ];
292
293 if ( cachedFeat )
294 {
295 cachedFeat->mFeature->setAttribute( field, value );
296 }
297
298 emit attributeValueChanged( fid, field, value );
299}
300
301void QgsVectorLayerCache::onJoinAttributeValueChanged( QgsFeatureId fid, int field, const QVariant &value )
302{
303 const QgsVectorLayer *joinLayer = qobject_cast<const QgsVectorLayer *>( sender() );
304
305 const auto constVectorJoins = mLayer->vectorJoins();
306 for ( const QgsVectorLayerJoinInfo &info : constVectorJoins )
307 {
308 if ( joinLayer == info.joinLayer() )
309 {
310 const QgsFeature feature = mLayer->joinBuffer()->targetedFeatureOf( &info, joinLayer->getFeature( fid ) );
311
312 const QString fieldName = info.prefixedFieldName( joinLayer->fields().field( field ) );
313 const int fieldIndex = mLayer->fields().indexFromName( fieldName );
314
315 if ( feature.isValid() && fieldIndex != -1 )
316 {
317 onAttributeValueChanged( feature.id(), fieldIndex, value );
318 return;
319 }
320 }
321 }
322}
323
324void QgsVectorLayerCache::featureDeleted( QgsFeatureId fid )
325{
326 mCache.remove( fid );
327
328 if ( auto it = mCacheUnorderedKeys.find( fid ); it != mCacheUnorderedKeys.end() )
329 {
330 mCacheUnorderedKeys.erase( it );
331 if ( auto orderedIt = std::find( mCacheOrderedKeys.begin(), mCacheOrderedKeys.end(), fid ); orderedIt != mCacheOrderedKeys.end() )
332 mCacheOrderedKeys.erase( orderedIt );
333 }
334}
335
336void QgsVectorLayerCache::onFeatureAdded( QgsFeatureId fid )
337{
338 if ( mFullCache )
339 {
340 if ( cacheSize() <= mLayer->featureCount() )
341 {
342 setCacheSize( mLayer->featureCount() + 100 );
343 }
344
345 QgsFeature feat;
346 featureAtId( fid, feat );
347 }
348 emit featureAdded( fid );
349}
350
351void QgsVectorLayerCache::attributeAdded( int field )
352{
353 Q_UNUSED( field )
354 mCachedAttributes.append( field );
355 invalidate();
356}
357
358void QgsVectorLayerCache::attributeDeleted( int field )
359{
360 QgsAttributeList attrs = mCachedAttributes;
361 mCachedAttributes.clear();
362
363 const auto constAttrs = attrs;
364 for ( int attr : constAttrs )
365 {
366 if ( attr < field )
367 mCachedAttributes << attr;
368 else if ( attr > field )
369 mCachedAttributes << attr - 1;
370 }
371}
372
373void QgsVectorLayerCache::geometryChanged( QgsFeatureId fid, const QgsGeometry &geom )
374{
375 QgsCachedFeature *cachedFeat = mCache[ fid ];
376
377 if ( cachedFeat )
378 {
379 cachedFeat->mFeature->setGeometry( geom );
380 }
381}
382
383void QgsVectorLayerCache::layerDeleted()
384{
385 emit cachedLayerDeleted();
386 mLayer = nullptr;
387}
388
389void QgsVectorLayerCache::invalidate()
390{
391 if ( ! mCache.isEmpty() )
392 {
393 mCache.clear();
394 mCacheOrderedKeys.clear();
395 mCacheUnorderedKeys.clear();
396 mFullCache = false;
397 emit invalidated();
398 }
399}
400
401bool QgsVectorLayerCache::canUseCacheForRequest( const QgsFeatureRequest &featureRequest, QgsFeatureIterator &it )
402{
403 // check first for available indices
404 const auto constMCacheIndices = mCacheIndices;
405 for ( QgsAbstractCacheIndex *idx : constMCacheIndices )
406 {
407 if ( idx->getCacheIterator( it, featureRequest ) )
408 {
409 return true;
410 }
411 }
412
413 // no indexes available, but maybe we have already cached all required features anyway?
414 switch ( featureRequest.filterType() )
415 {
417 {
418 if ( mCache.contains( featureRequest.filterFid() ) )
419 {
420 it = QgsFeatureIterator( new QgsCachedFeatureIterator( this, featureRequest ) );
421 return true;
422 }
423 break;
424 }
426 {
427 if ( cachedFeatureIds().contains( featureRequest.filterFids() ) )
428 {
429 it = QgsFeatureIterator( new QgsCachedFeatureIterator( this, featureRequest ) );
430 return true;
431 }
432 break;
433 }
436 {
437 if ( mFullCache )
438 {
439 it = QgsFeatureIterator( new QgsCachedFeatureIterator( this, featureRequest ) );
440 return true;
441 }
442 break;
443 }
444
445 }
446 return false;
447}
448
450{
452 bool requiresWriterIt = true; // If a not yet cached, but cacheable request is made, this stays true.
453
454 if ( checkInformationCovered( featureRequest ) )
455 {
456 // If we have a full cache available, run on this
457 if ( mFullCache )
458 {
459 it = QgsFeatureIterator( new QgsCachedFeatureIterator( this, featureRequest ) );
460 requiresWriterIt = false;
461 }
462 else
463 {
464 // may still be able to satisfy request using cache
465 requiresWriterIt = !canUseCacheForRequest( featureRequest, it );
466 }
467 }
468 else
469 {
470 // Let the layer answer the request, so no caching of requests
471 // we don't want to cache is done
472 requiresWriterIt = false;
473 it = mLayer->getFeatures( featureRequest );
474 }
475
476 if ( requiresWriterIt && mLayer->dataProvider() )
477 {
478 // No index was able to satisfy the request
479 QgsFeatureRequest myRequest = QgsFeatureRequest( featureRequest );
480
481 // Make sure if we cache the geometry, it gets fetched
482 if ( mCacheGeometry && mLayer->isSpatial() )
483 myRequest.setFlags( featureRequest.flags() & ~QgsFeatureRequest::NoGeometry );
484
485 // Make sure all the cached attributes are requested as well if requesting a subset
486 if ( myRequest.flags().testFlag( QgsFeatureRequest::SubsetOfAttributes ) )
487 {
488 if ( mCachedAttributes.count( ) != mLayer->fields().count() )
489 {
490 const QgsAttributeList requestSubset = featureRequest.subsetOfAttributes();
491 QSet<int> attrs( requestSubset.begin(), requestSubset.end() );
492 for ( int attr : std::as_const( mCachedAttributes ) )
493 attrs.insert( attr );
494 myRequest.setSubsetOfAttributes( attrs.values() );
495 }
496 else // we are already caching all attributes
497 {
499 myRequest.setFlags( myRequest.flags().setFlag( QgsFeatureRequest::Flag::SubsetOfAttributes, false ) );
500 }
501 }
502
503 it = QgsFeatureIterator( new QgsCachedFeatureWriterIterator( this, myRequest ) );
504 }
505
506 return it;
507}
508
510{
511 return mCache.contains( fid );
512}
513
515{
516 const QList< QgsFeatureId > keys = mCache.keys();
517 return QgsFeatureIds( keys.begin(), keys.end() );
518}
519
521{
522 QgsAttributeList requestedAttributes;
523
524 if ( !featureRequest.flags().testFlag( QgsFeatureRequest::SubsetOfAttributes ) )
525 {
526 requestedAttributes = mLayer->attributeList();
527 }
528 else
529 {
530 requestedAttributes = featureRequest.subsetOfAttributes();
531 }
532
533 // Check if we even cache the information requested
534 const auto constRequestedAttributes = requestedAttributes;
535 for ( int attr : constRequestedAttributes )
536 {
537 if ( !mCachedAttributes.contains( attr ) )
538 {
539 return false;
540 }
541 }
542
543 // If the request needs geometry but we don't cache this...
544 return !( !featureRequest.flags().testFlag( QgsFeatureRequest::NoGeometry )
545 && !mCacheGeometry );
546}
547
548void QgsVectorLayerCache::connectJoinedLayers() const
549{
550 const auto constVectorJoins = mLayer->vectorJoins();
551 for ( const QgsVectorLayerJoinInfo &info : constVectorJoins )
552 {
553 const QgsVectorLayer *vl = info.joinLayer();
554 if ( vl )
555 connect( vl, &QgsVectorLayer::attributeValueChanged, this, &QgsVectorLayerCache::onJoinAttributeValueChanged );
556 }
557}
558
559bool QgsVectorLayerCache::QgsCachedFeature::allAttributesFetched() const
560{
561 return mAllAttributesFetched;
562}
563
@ NoFilter
No spatial filtering of features.
WkbType
The WKB type describes the number of dimensions a geometry has.
Definition qgis.h:182
Abstract base class for cache indices.
This class represents a coordinate reference system (CRS).
Wrapper for iterator of features from vector data provider or vector layer.
bool nextFeature(QgsFeature &f)
This class wraps a request for features to a vector layer (or directly its vector data provider).
QgsRectangle filterRect() const
Returns the rectangle from which features will be taken.
QgsFeatureRequest & setFlags(QgsFeatureRequest::Flags flags)
Sets flags that affect how features will be fetched.
Flags flags() const
Returns the flags which affect how features are fetched.
QgsFeatureRequest & setSubsetOfAttributes(const QgsAttributeList &attrs)
Set a subset of attributes that will be fetched.
@ SubsetOfAttributes
Fetch only a subset of attributes (setSubsetOfAttributes sets this flag)
@ NoGeometry
Geometry is not required. It may still be returned if e.g. required for a filter condition.
Qgis::SpatialFilterType spatialFilterType() const
Returns the spatial filter type which is currently set on this request.
QgsAttributeList subsetOfAttributes() const
Returns the subset of attributes which at least need to be fetched.
FilterType filterType() const
Returns the attribute/ID filter type which is currently set on this request.
@ FilterFid
Filter using feature ID.
@ FilterFids
Filter using feature IDs.
@ FilterNone
No filter is applied.
@ FilterExpression
Filter using expression.
const QgsFeatureIds & filterFids() const
Returns the feature IDs that should be fetched.
QgsFeatureId filterFid() const
Returns the feature ID that should be fetched.
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition qgsfeature.h:56
QgsFeatureId id
Definition qgsfeature.h:64
bool isValid() const
Returns the validity of this feature.
Container of fields for a vector layer.
Definition qgsfields.h:45
int indexFromName(const QString &fieldName) const
Gets the field index from the field name.
int count() const
Returns number of items.
QgsField field(int fieldIdx) const
Returns the field at particular index (must be in range 0..N-1).
A geometry is the spatial representation of a feature.
QgsCoordinateReferenceSystem crs
Definition qgsmaplayer.h:81
void dataChanged()
Data of layer changed.
bool contains(const QgsRectangle &rect) const
Returns true when rectangle contains other rectangle.
bool isFidCached(QgsFeatureId fid) const
Check if a certain feature id is cached.
void setFullCache(bool fullCache)
This enables or disables full caching.
void finished()
When filling the cache, this signal gets emitted once the cache is fully initialized.
void featureRemoved(QgsFeatureId fid)
Gets called, whenever a feature has been removed.
void setCacheAddedAttributes(bool cacheAddedAttributes)
If this is enabled, the subset of cached attributes will automatically be extended to also include ne...
void invalidated()
The cache has been invalidated and cleared.
void setCacheSize(int cacheSize)
Sets the maximum number of features to keep in the cache.
void setCacheSubsetOfAttributes(const QgsAttributeList &attributes)
Set the list (possibly a subset) of attributes to be cached.
void featureAdded(QgsFeatureId fid)
Emitted when a new feature has been added to the layer and this cache.
QgsFields fields() const
Returns the fields associated with features in the cache.
void cachedLayerDeleted()
Is emitted when the cached layer is deleted.
friend class QgsCachedFeatureWriterIterator
void requestCompleted(const QgsFeatureRequest &featureRequest, const QgsFeatureIds &fids)
Gets called, whenever the full list of feature ids for a certain request is known.
void attributeValueChanged(QgsFeatureId fid, int field, const QVariant &value)
Emitted when an attribute is changed.
bool removeCachedFeature(QgsFeatureId fid)
Removes the feature identified by fid from the cache if present.
void progress(int i, bool &cancel)
When filling the cache, this signal gets emitted periodically to notify about the progress and to be ...
QgsVectorLayer * layer()
Returns the layer to which this cache belongs.
long long featureCount() const
Returns the number of features contained in the source, or -1 if the feature count is unknown.
QgsCoordinateReferenceSystem sourceCrs() const
Returns the coordinate reference system for features in the cache.
bool checkInformationCovered(const QgsFeatureRequest &featureRequest)
Checks if the information required to complete the request is cached.
QgsFeatureIds cachedFeatureIds() const
Returns the set of feature IDs for features which are cached.
bool featureAtIdWithAllAttributes(QgsFeatureId featureId, QgsFeature &feature, bool skipCache=false)
Gets the feature at the given feature id with all attributes, if the cached feature already contains ...
bool cacheGeometry() const
Returns true if the cache will fetch and cache feature geometries.
Qgis::WkbType wkbType() const
Returns the geometry type for features in the cache.
int cacheSize()
Returns the maximum number of features this cache will hold.
friend class QgsCachedFeatureIterator
QgsAttributeList cacheSubsetOfAttributes() const
Returns the list (possibly a subset) of cached attributes.
void addCacheIndex(QgsAbstractCacheIndex *cacheIndex)
Adds a QgsAbstractCacheIndex to this cache.
QgsVectorLayerCache(QgsVectorLayer *layer, int cacheSize, QObject *parent=nullptr)
void setCacheGeometry(bool cacheGeometry)
Enable or disable the caching of geometries.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &featureRequest=QgsFeatureRequest())
Query this VectorLayerCache for features.
bool featureAtId(QgsFeatureId featureId, QgsFeature &feature, bool skipCache=false)
Gets the feature at the given feature id.
QgsFeature targetedFeatureOf(const QgsVectorLayerJoinInfo *info, const QgsFeature &feature) const
Returns the targeted feature corresponding to the joined feature.
Defines left outer join from our vector layer to some other vector layer.
Represents a vector layer which manages a vector based data sets.
void attributeAdded(int idx)
Will be emitted, when a new attribute has been added to this vector layer.
long long featureCount(const QString &legendKey) const
Number of features rendered with specified legend key.
bool isSpatial() const FINAL
Returns true if this is a geometry layer and false in case of NoGeometry (table only) or UnknownGeome...
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const FINAL
Queries the layer for features specified in request.
QgsFields fields() const FINAL
Returns the list of fields of this layer.
QgsAttributeList attributeList() const
Returns list of attribute indexes.
Q_INVOKABLE Qgis::WkbType wkbType() const FINAL
Returns the WKBType or WKBUnknown in case of error.
void attributeDeleted(int idx)
Will be emitted, when an attribute has been deleted from this vector layer.
QgsVectorDataProvider * dataProvider() FINAL
Returns the layer's data provider, it may be nullptr.
QgsVectorLayerJoinBuffer * joinBuffer()
Returns the join buffer object.
QgsFeature getFeature(QgsFeatureId fid) const
Queries the layer for the feature with the given id.
void attributeValueChanged(QgsFeatureId fid, int idx, const QVariant &value)
Emitted whenever an attribute value change is done in the edit buffer.
QgsRectangle extent() const FINAL
Returns the extent of the layer.
void updatedFields()
Emitted whenever the fields available from this layer have been changed.
void featureAdded(QgsFeatureId fid)
Emitted when a new feature has been added to the layer.
void featureDeleted(QgsFeatureId fid)
Emitted when a feature has been deleted.
const QList< QgsVectorLayerJoinInfo > vectorJoins() const
void geometryChanged(QgsFeatureId fid, const QgsGeometry &geometry)
Emitted whenever a geometry change is done in the edit buffer.
QSet< QgsFeatureId > QgsFeatureIds
qint64 QgsFeatureId
64 bit feature ids negative numbers are used for uncommitted/newly added features
QList< int > QgsAttributeList
Definition qgsfield.h:27