QGIS API Documentation  2.99.0-Master (b95d432)
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"
21 
22 QgsVectorLayerCache::QgsVectorLayerCache( QgsVectorLayer *layer, int cacheSize, QObject *parent )
23  : QObject( parent )
24  , mLayer( layer )
25  , mFullCache( false )
26 {
27  mCache.setMaxCost( cacheSize );
28 
29  connect( mLayer, &QgsVectorLayer::featureDeleted, this, &QgsVectorLayerCache::featureDeleted );
30  connect( mLayer, &QgsVectorLayer::featureAdded, this, &QgsVectorLayerCache::onFeatureAdded );
31  connect( mLayer, &QgsVectorLayer::destroyed, this, &QgsVectorLayerCache::layerDeleted );
32 
33  setCacheGeometry( true );
36 
37  connect( mLayer, &QgsVectorLayer::attributeDeleted, this, &QgsVectorLayerCache::attributeDeleted );
38  connect( mLayer, &QgsVectorLayer::updatedFields, this, &QgsVectorLayerCache::invalidate );
39  connect( mLayer, &QgsVectorLayer::dataChanged, this, &QgsVectorLayerCache::invalidate );
40  connect( mLayer, &QgsVectorLayer::attributeValueChanged, this, &QgsVectorLayerCache::onAttributeValueChanged );
41 }
42 
44 {
45  qDeleteAll( mCacheIndices );
46  mCacheIndices.clear();
47 }
48 
50 {
51  mCache.setMaxCost( cacheSize );
52 }
53 
55 {
56  return mCache.maxCost();
57 }
58 
60 {
61  bool shouldCacheGeometry = cacheGeometry && mLayer->isSpatial();
62  bool mustInvalidate = shouldCacheGeometry && !mCacheGeometry; // going from no geometry -> geometry, so have to clear existing cache entries
63  mCacheGeometry = shouldCacheGeometry;
64  if ( cacheGeometry )
65  {
66  connect( mLayer, &QgsVectorLayer::geometryChanged, this, &QgsVectorLayerCache::geometryChanged );
67  }
68  else
69  {
70  disconnect( mLayer, &QgsVectorLayer::geometryChanged, this, &QgsVectorLayerCache::geometryChanged );
71  }
72  if ( mustInvalidate )
73  {
74  invalidate();
75  }
76 }
77 
79 {
80  mCachedAttributes = attributes;
81 }
82 
83 void QgsVectorLayerCache::setFullCache( bool fullCache )
84 {
85  mFullCache = fullCache;
86 
87  if ( mFullCache )
88  {
89  // Add a little more than necessary...
90  setCacheSize( mLayer->featureCount() + 100 );
91 
92  // Initialize the cache...
94  .setSubsetOfAttributes( mCachedAttributes )
95  .setFlags( mCacheGeometry ? QgsFeatureRequest::NoFlags : QgsFeatureRequest::NoGeometry ) ) );
96 
97  int i = 0;
98 
99  QTime t;
100  t.start();
101 
102  QgsFeature f;
103  while ( it.nextFeature( f ) )
104  {
105  ++i;
106 
107  if ( t.elapsed() > 1000 )
108  {
109  bool cancel = false;
110  emit progress( i, cancel );
111  if ( cancel )
112  break;
113 
114  t.restart();
115  }
116  }
117 
118  it.close();
119 
120  emit finished();
121  }
122 }
123 
125 {
126  mCacheIndices.append( cacheIndex );
127 }
128 
129 void QgsVectorLayerCache::setCacheAddedAttributes( bool cacheAddedAttributes )
130 {
131  if ( cacheAddedAttributes )
132  {
133  connect( mLayer, &QgsVectorLayer::attributeAdded, this, &QgsVectorLayerCache::attributeAdded );
134  }
135  else
136  {
137  disconnect( mLayer, &QgsVectorLayer::attributeAdded, this, &QgsVectorLayerCache::attributeAdded );
138  }
139 }
140 
141 bool QgsVectorLayerCache::featureAtId( QgsFeatureId featureId, QgsFeature &feature, bool skipCache )
142 {
143  bool featureFound = false;
144 
145  QgsCachedFeature *cachedFeature = nullptr;
146 
147  if ( !skipCache )
148  {
149  cachedFeature = mCache[ featureId ];
150  }
151 
152  if ( cachedFeature )
153  {
154  feature = QgsFeature( *cachedFeature->feature() );
155  featureFound = true;
156  }
157  else if ( mLayer->getFeatures( QgsFeatureRequest()
158  .setFilterFid( featureId )
159  .setSubsetOfAttributes( mCachedAttributes )
160  .setFlags( !mCacheGeometry ? QgsFeatureRequest::NoGeometry : QgsFeatureRequest::Flags( nullptr ) ) )
161  .nextFeature( feature ) )
162  {
163  cacheFeature( feature );
164  featureFound = true;
165  }
166 
167  return featureFound;
168 }
169 
171 {
172  return mCache.remove( fid );
173 }
174 
176 {
177  return mLayer;
178 }
179 
181 {
182  return mLayer->crs();
183 }
184 
186 {
187  return mLayer->wkbType();
188 }
189 
191 {
192  return mLayer->fields();
193 }
194 
196 {
197  return mLayer->featureCount();
198 }
199 
201 {
202  // If a request is too large for the cache don't notify to prevent from indexing incomplete requests
203  if ( fids.count() <= mCache.size() )
204  {
205  Q_FOREACH ( QgsAbstractCacheIndex *idx, mCacheIndices )
206  {
207  idx->requestCompleted( featureRequest, fids );
208  }
209  if ( featureRequest.filterType() == QgsFeatureRequest::FilterNone )
210  {
211  mFullCache = true;
212  }
213  }
214 }
215 
217 {
218  Q_FOREACH ( QgsAbstractCacheIndex *idx, mCacheIndices )
219  {
220  idx->flushFeature( fid );
221  }
222 }
223 
224 void QgsVectorLayerCache::onAttributeValueChanged( QgsFeatureId fid, int field, const QVariant &value )
225 {
226  QgsCachedFeature *cachedFeat = mCache[ fid ];
227 
228  if ( cachedFeat )
229  {
230  cachedFeat->mFeature->setAttribute( field, value );
231  }
232 
233  emit attributeValueChanged( fid, field, value );
234 }
235 
236 void QgsVectorLayerCache::featureDeleted( QgsFeatureId fid )
237 {
238  mCache.remove( fid );
239 }
240 
241 void QgsVectorLayerCache::onFeatureAdded( QgsFeatureId fid )
242 {
243  if ( mFullCache )
244  {
245  if ( cacheSize() <= mLayer->featureCount() )
246  {
247  setCacheSize( mLayer->featureCount() + 100 );
248  }
249 
250  QgsFeature feat;
251  featureAtId( fid, feat );
252  }
253  emit featureAdded( fid );
254 }
255 
256 void QgsVectorLayerCache::attributeAdded( int field )
257 {
258  Q_UNUSED( field )
259  mCachedAttributes.append( field );
260  invalidate();
261 }
262 
263 void QgsVectorLayerCache::attributeDeleted( int field )
264 {
265  QgsAttributeList attrs = mCachedAttributes;
266  mCachedAttributes.clear();
267 
268  Q_FOREACH ( int attr, attrs )
269  {
270  if ( attr < field )
271  mCachedAttributes << attr;
272  else if ( attr > field )
273  mCachedAttributes << attr - 1;
274  }
275 }
276 
277 void QgsVectorLayerCache::geometryChanged( QgsFeatureId fid, const QgsGeometry &geom )
278 {
279  QgsCachedFeature *cachedFeat = mCache[ fid ];
280 
281  if ( cachedFeat )
282  {
283  cachedFeat->mFeature->setGeometry( geom );
284  }
285 }
286 
287 void QgsVectorLayerCache::layerDeleted()
288 {
289  emit cachedLayerDeleted();
290  mLayer = nullptr;
291 }
292 
293 void QgsVectorLayerCache::invalidate()
294 {
295  mCache.clear();
296  mFullCache = false;
297  emit invalidated();
298 }
299 
300 bool QgsVectorLayerCache::canUseCacheForRequest( const QgsFeatureRequest &featureRequest, QgsFeatureIterator &it )
301 {
302  // check first for available indices
303  Q_FOREACH ( QgsAbstractCacheIndex *idx, mCacheIndices )
304  {
305  if ( idx->getCacheIterator( it, featureRequest ) )
306  {
307  return true;
308  }
309  }
310 
311  // no indexes available, but maybe we have already cached all required features anyway?
312  switch ( featureRequest.filterType() )
313  {
315  {
316  if ( mCache.contains( featureRequest.filterFid() ) )
317  {
318  it = QgsFeatureIterator( new QgsCachedFeatureIterator( this, featureRequest ) );
319  return true;
320  }
321  break;
322  }
324  {
325  if ( mCache.keys().toSet().contains( featureRequest.filterFids() ) )
326  {
327  it = QgsFeatureIterator( new QgsCachedFeatureIterator( this, featureRequest ) );
328  return true;
329  }
330  break;
331  }
334  {
335  if ( mFullCache )
336  {
337  it = QgsFeatureIterator( new QgsCachedFeatureIterator( this, featureRequest ) );
338  return true;
339  }
340  break;
341  }
342 
343  }
344  return false;
345 }
346 
348 {
350  bool requiresWriterIt = true; // If a not yet cached, but cachable request is made, this stays true.
351 
352  if ( checkInformationCovered( featureRequest ) )
353  {
354  // If we have a full cache available, run on this
355  if ( mFullCache )
356  {
357  it = QgsFeatureIterator( new QgsCachedFeatureIterator( this, featureRequest ) );
358  requiresWriterIt = false;
359  }
360  else
361  {
362  // may still be able to satisfy request using cache
363  requiresWriterIt = !canUseCacheForRequest( featureRequest, it );
364  }
365  }
366  else
367  {
368  // Let the layer answer the request, so no caching of requests
369  // we don't want to cache is done
370  requiresWriterIt = false;
371  it = mLayer->getFeatures( featureRequest );
372  }
373 
374  if ( requiresWriterIt && mLayer->dataProvider() )
375  {
376  // No index was able to satisfy the request
377  QgsFeatureRequest myRequest = QgsFeatureRequest( featureRequest );
378 
379  // Make sure if we cache the geometry, it gets fetched
380  if ( mCacheGeometry && mLayer->isSpatial() )
381  myRequest.setFlags( featureRequest.flags() & ~QgsFeatureRequest::NoGeometry );
382 
383  // Make sure, all the cached attributes are requested as well
384  QSet<int> attrs = featureRequest.subsetOfAttributes().toSet() + mCachedAttributes.toSet();
385  myRequest.setSubsetOfAttributes( attrs.toList() );
386 
387  it = QgsFeatureIterator( new QgsCachedFeatureWriterIterator( this, myRequest ) );
388  }
389 
390  return it;
391 }
392 
394 {
395  return mCache.contains( fid );
396 }
397 
399 {
400  QgsAttributeList requestedAttributes;
401 
402  if ( !featureRequest.flags().testFlag( QgsFeatureRequest::SubsetOfAttributes ) )
403  {
404  requestedAttributes = mLayer->attributeList();
405  }
406  else
407  {
408  requestedAttributes = featureRequest.subsetOfAttributes();
409  }
410 
411  // Check if we even cache the information requested
412  Q_FOREACH ( int attr, requestedAttributes )
413  {
414  if ( !mCachedAttributes.contains( attr ) )
415  {
416  return false;
417  }
418  }
419 
420  // If the request needs geometry but we don't cache this...
421  if ( !featureRequest.flags().testFlag( QgsFeatureRequest::NoGeometry )
422  && !mCacheGeometry )
423  {
424  return false;
425  }
426 
427  return true;
428 }
Wrapper for iterator of features from vector data provider or vector layer.
friend class QgsCachedFeatureWriterIterator
Filter using feature ID.
Filter using feature IDs.
QgsVectorLayerCache(QgsVectorLayer *layer, int cacheSize, QObject *parent=nullptr)
const Flags & flags() const
void invalidated()
The cache has been invalidated and cleared.
bool cacheGeometry() const
Returns true if the cache will fetch and cache feature geometries.
QSet< QgsFeatureId > QgsFeatureIds
Definition: qgsfeature.h:519
bool removeCachedFeature(QgsFeatureId fid)
Removes the feature identified by fid from the cache if present.
QgsFeatureId filterFid() const
Get the feature ID that should be fetched.
QgsFeatureRequest & setSubsetOfAttributes(const QgsAttributeList &attrs)
Set a subset of attributes that will be fetched.
FilterType filterType() const
Return the filter type which is currently set on this request.
Container of fields for a vector layer.
Definition: qgsfields.h:41
const QgsFeatureIds & filterFids() const
Get feature IDs that should be fetched.
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:96
void requestCompleted(const QgsFeatureRequest &featureRequest, const QgsFeatureIds &fids)
Gets called, whenever the full list of feature ids for a certain request is known.
QgsVectorLayer * layer()
Returns the layer to which this cache belongs.
bool isFidCached(const QgsFeatureId fid) const
Check if a certain feature id is cached.
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:61
void attributeValueChanged(QgsFeatureId fid, int field, const QVariant &value)
Is emitted when an attribute is changed.
void featureDeleted(QgsFeatureId fid)
Emitted when a feature has been deleted.
void setCacheSubsetOfAttributes(const QgsAttributeList &attributes)
Set the subset of attributes to be cached.
virtual void requestCompleted(const QgsFeatureRequest &featureRequest, const QgsFeatureIds &fids)
Implement this method to update the the indices, in case you need information contained by the reques...
void featureAdded(QgsFeatureId fid)
Is emitted, when a new feature has been added to the layer and this cache.
Type
The WKB type describes the number of dimensions a geometry has.
Definition: qgswkbtypes.h:66
QgsFields fields() const override
Returns the list of fields of this layer.
QgsCoordinateReferenceSystem sourceCrs() const
Returns the coordinate reference system for features in the cache.
long featureCount(const QString &legendKey) const
Number of features rendered with specified legend key.
void progress(int i, bool &cancel)
When filling the cache, this signal gets emitted periodically to notify about the progress and to be ...
QgsWkbTypes::Type wkbType() const override
Returns the WKBType or WKBUnknown in case of error.
This class wraps a request for features to a vector layer (or directly its vector data provider)...
virtual bool isSpatial() const override
Returns true if this is a geometry layer and false in case of NoGeometry (table only) or UnknownGeome...
QgsCoordinateReferenceSystem crs() const
Returns the layer&#39;s spatial reference system.
void geometryChanged(QgsFeatureId fid, const QgsGeometry &geometry)
Is emitted whenever a geometry change is done in the edit buffer.
QgsAttributeList attributeList() const
Returns list of attribute indexes.
QgsFields fields() const
Returns the fields associated with features in the cache.
void attributeValueChanged(QgsFeatureId fid, int idx, const QVariant &value)
Is emitted whenever an attribute value change is done in the edit buffer.
void featureAdded(QgsFeatureId fid)
Emitted when a new feature has been added to the layer.
Fetch only a subset of attributes (setSubsetOfAttributes sets this flag)
void cachedLayerDeleted()
Is emitted when the cached layer is deleted.
bool checkInformationCovered(const QgsFeatureRequest &featureRequest)
Checks if the information required to complete the request is cached.
QgsAttributeList subsetOfAttributes() const
Return the subset of attributes which at least need to be fetched.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const override
Query the layer for features specified in request.
virtual bool getCacheIterator(QgsFeatureIterator &featureIterator, const QgsFeatureRequest &featureRequest)=0
Is called, when a feature request is issued on a cached layer.
void attributeAdded(int idx)
Will be emitted, when a new attribute has been added to this vector layer.
long featureCount() const
Returns the number of features contained in the source, or -1 if the feature count is unknown...
Abstract base class for cache indices.
Definition: qgscacheindex.h:30
No filter is applied.
void setCacheGeometry(bool cacheGeometry)
Enable or disable the caching of geometries.
void setFullCache(bool fullCache)
This enables or disables full caching.
virtual void flushFeature(const QgsFeatureId fid)=0
Is called, whenever a feature is removed from the cache.
bool featureAtId(QgsFeatureId featureId, QgsFeature &feature, bool skipCache=false)
Gets the feature at the given feature id.
void setCacheSize(int cacheSize)
Sets the maximum number of features to keep in the cache.
This class represents a coordinate reference system (CRS).
QgsWkbTypes::Type wkbType() const
Returns the geometry type for features in the cache.
QgsVectorDataProvider * dataProvider() override
Returns the layer&#39;s data provider.
qint64 QgsFeatureId
Definition: qgsfeature.h:37
void dataChanged()
Data of layer changed.
void featureRemoved(QgsFeatureId fid)
Gets called, whenever a feature has been removed.
int cacheSize()
Returns the maximum number of features this cache will hold.
QList< int > QgsAttributeList
Definition: qgsfield.h:27
bool nextFeature(QgsFeature &f)
void attributeDeleted(int idx)
Will be emitted, when an attribute has been deleted from this vector layer.
Geometry is not required. It may still be returned if e.g. required for a filter condition.
void addCacheIndex(QgsAbstractCacheIndex *cacheIndex)
Adds a QgsAbstractCacheIndex to this cache.
Represents a vector layer which manages a vector based data sets.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &featureRequest=QgsFeatureRequest())
Query this VectorLayerCache for features.
void updatedFields()
Is emitted, whenever the fields available from this layer have been changed.
void finished()
When filling the cache, this signal gets emitted once the cache is fully initialized.
friend class QgsCachedFeatureIterator
void setCacheAddedAttributes(bool cacheAddedAttributes)
If this is enabled, the subset of cached attributes will automatically be extended to also include ne...
QgsFeatureRequest & setFlags(QgsFeatureRequest::Flags flags)
Set flags that affect how features will be fetched.