QGIS API Documentation  2.99.0-Master (8ec3eaf)
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 
59 void QgsVectorLayerCache::setCacheGeometry( bool cacheGeometry )
60 {
61  mCacheGeometry = cacheGeometry && mLayer->hasGeometryType();
62  if ( cacheGeometry )
63  {
64  connect( mLayer, &QgsVectorLayer::geometryChanged, this, &QgsVectorLayerCache::geometryChanged );
65  }
66  else
67  {
68  disconnect( mLayer, &QgsVectorLayer::geometryChanged, this, &QgsVectorLayerCache::geometryChanged );
69  }
70 }
71 
73 {
74  mCachedAttributes = attributes;
75 }
76 
77 void QgsVectorLayerCache::setFullCache( bool fullCache )
78 {
79  mFullCache = fullCache;
80 
81  if ( mFullCache )
82  {
83  // Add a little more than necessary...
84  setCacheSize( mLayer->featureCount() + 100 );
85 
86  // Initialize the cache...
88  .setSubsetOfAttributes( mCachedAttributes )
89  .setFlags( mCacheGeometry ? QgsFeatureRequest::NoFlags : QgsFeatureRequest::NoGeometry ) ) );
90 
91  int i = 0;
92 
93  QTime t;
94  t.start();
95 
96  QgsFeature f;
97  while ( it.nextFeature( f ) )
98  {
99  ++i;
100 
101  if ( t.elapsed() > 1000 )
102  {
103  bool cancel = false;
104  emit progress( i, cancel );
105  if ( cancel )
106  break;
107 
108  t.restart();
109  }
110  }
111 
112  it.close();
113 
114  emit finished();
115  }
116 }
117 
119 {
120  mCacheIndices.append( cacheIndex );
121 }
122 
123 void QgsVectorLayerCache::setCacheAddedAttributes( bool cacheAddedAttributes )
124 {
125  if ( cacheAddedAttributes )
126  {
127  connect( mLayer, &QgsVectorLayer::attributeAdded, this, &QgsVectorLayerCache::attributeAdded );
128  }
129  else
130  {
131  disconnect( mLayer, &QgsVectorLayer::attributeAdded, this, &QgsVectorLayerCache::attributeAdded );
132  }
133 }
134 
135 bool QgsVectorLayerCache::featureAtId( QgsFeatureId featureId, QgsFeature& feature, bool skipCache )
136 {
137  bool featureFound = false;
138 
139  QgsCachedFeature* cachedFeature = nullptr;
140 
141  if ( !skipCache )
142  {
143  cachedFeature = mCache[ featureId ];
144  }
145 
146  if ( cachedFeature )
147  {
148  feature = QgsFeature( *cachedFeature->feature() );
149  featureFound = true;
150  }
151  else if ( mLayer->getFeatures( QgsFeatureRequest()
152  .setFilterFid( featureId )
153  .setSubsetOfAttributes( mCachedAttributes )
154  .setFlags( !mCacheGeometry ? QgsFeatureRequest::NoGeometry : QgsFeatureRequest::Flags( nullptr ) ) )
155  .nextFeature( feature ) )
156  {
157  cacheFeature( feature );
158  featureFound = true;
159  }
160 
161  return featureFound;
162 }
163 
165 {
166  return mCache.remove( fid );
167 }
168 
170 {
171  return mLayer;
172 }
173 
175 {
176  // If a request is too large for the cache don't notify to prevent from indexing incomplete requests
177  if ( fids.count() <= mCache.size() )
178  {
179  Q_FOREACH ( QgsAbstractCacheIndex* idx, mCacheIndices )
180  {
181  idx->requestCompleted( featureRequest, fids );
182  }
183  if ( featureRequest.filterType() == QgsFeatureRequest::FilterNone )
184  {
185  mFullCache = true;
186  }
187  }
188 }
189 
191 {
192  Q_FOREACH ( QgsAbstractCacheIndex* idx, mCacheIndices )
193  {
194  idx->flushFeature( fid );
195  }
196 }
197 
198 void QgsVectorLayerCache::onAttributeValueChanged( QgsFeatureId fid, int field, const QVariant& value )
199 {
200  QgsCachedFeature* cachedFeat = mCache[ fid ];
201 
202  if ( cachedFeat )
203  {
204  cachedFeat->mFeature->setAttribute( field, value );
205  }
206 
207  emit attributeValueChanged( fid, field, value );
208 }
209 
210 void QgsVectorLayerCache::featureDeleted( QgsFeatureId fid )
211 {
212  mCache.remove( fid );
213 }
214 
215 void QgsVectorLayerCache::onFeatureAdded( QgsFeatureId fid )
216 {
217  if ( mFullCache )
218  {
219  if ( cacheSize() <= mLayer->featureCount() )
220  {
221  setCacheSize( mLayer->featureCount() + 100 );
222  }
223 
224  QgsFeature feat;
225  featureAtId( fid, feat );
226  }
227  emit featureAdded( fid );
228 }
229 
230 void QgsVectorLayerCache::attributeAdded( int field )
231 {
232  Q_UNUSED( field )
233  mCachedAttributes.append( field );
234  mCache.clear();
235 }
236 
237 void QgsVectorLayerCache::attributeDeleted( int field )
238 {
239  QgsAttributeList attrs = mCachedAttributes;
240  mCachedAttributes.clear();
241 
242  Q_FOREACH ( int attr, attrs )
243  {
244  if ( attr < field )
245  mCachedAttributes << attr;
246  else if ( attr > field )
247  mCachedAttributes << attr - 1;
248  }
249 }
250 
251 void QgsVectorLayerCache::geometryChanged( QgsFeatureId fid, const QgsGeometry& geom )
252 {
253  QgsCachedFeature* cachedFeat = mCache[ fid ];
254 
255  if ( cachedFeat )
256  {
257  cachedFeat->mFeature->setGeometry( geom );
258  }
259 }
260 
261 void QgsVectorLayerCache::layerDeleted()
262 {
263  emit cachedLayerDeleted();
264  mLayer = nullptr;
265 }
266 
267 void QgsVectorLayerCache::invalidate()
268 {
269  mCache.clear();
270  emit invalidated();
271 }
272 
273 bool QgsVectorLayerCache::canUseCacheForRequest( const QgsFeatureRequest &featureRequest, QgsFeatureIterator& it )
274 {
275  // check first for available indices
276  Q_FOREACH ( QgsAbstractCacheIndex *idx, mCacheIndices )
277  {
278  if ( idx->getCacheIterator( it, featureRequest ) )
279  {
280  return true;
281  }
282  }
283 
284  // no indexes available, but maybe we have already cached all required features anyway?
285  switch ( featureRequest.filterType() )
286  {
288  {
289  if ( mCache.contains( featureRequest.filterFid() ) )
290  {
291  it = QgsFeatureIterator( new QgsCachedFeatureIterator( this, featureRequest ) );
292  return true;
293  }
294  break;
295  }
297  {
298  if ( mCache.keys().toSet().contains( featureRequest.filterFids() ) )
299  {
300  it = QgsFeatureIterator( new QgsCachedFeatureIterator( this, featureRequest ) );
301  return true;
302  }
303  break;
304  }
308  {
309  if ( mFullCache )
310  {
311  it = QgsFeatureIterator( new QgsCachedFeatureIterator( this, featureRequest ) );
312  return true;
313  }
314  break;
315  }
316 
317  }
318  return false;
319 }
320 
322 {
324  bool requiresWriterIt = true; // If a not yet cached, but cachable request is made, this stays true.
325 
326  if ( checkInformationCovered( featureRequest ) )
327  {
328  // If we have a full cache available, run on this
329  if ( mFullCache )
330  {
331  it = QgsFeatureIterator( new QgsCachedFeatureIterator( this, featureRequest ) );
332  requiresWriterIt = false;
333  }
334  else
335  {
336  // may still be able to satisfy request using cache
337  requiresWriterIt = !canUseCacheForRequest( featureRequest, it );
338  }
339  }
340  else
341  {
342  // Let the layer answer the request, so no caching of requests
343  // we don't want to cache is done
344  requiresWriterIt = false;
345  it = mLayer->getFeatures( featureRequest );
346  }
347 
348  if ( requiresWriterIt && mLayer->dataProvider() )
349  {
350  // No index was able to satisfy the request
351  QgsFeatureRequest myRequest = QgsFeatureRequest( featureRequest );
352 
353  // Make sure if we cache the geometry, it gets fetched
354  if ( mCacheGeometry && mLayer->hasGeometryType() )
355  myRequest.setFlags( featureRequest.flags() & ~QgsFeatureRequest::NoGeometry );
356 
357  // Make sure, all the cached attributes are requested as well
358  QSet<int> attrs = featureRequest.subsetOfAttributes().toSet() + mCachedAttributes.toSet();
359  myRequest.setSubsetOfAttributes( attrs.toList() );
360 
361  it = QgsFeatureIterator( new QgsCachedFeatureWriterIterator( this, myRequest ) );
362  }
363 
364  return it;
365 }
366 
368 {
369  return mCache.contains( fid );
370 }
371 
373 {
374  QgsAttributeList requestedAttributes;
375 
376  if ( !featureRequest.flags().testFlag( QgsFeatureRequest::SubsetOfAttributes ) )
377  {
378  requestedAttributes = mLayer->attributeList();
379  }
380  else
381  {
382  requestedAttributes = featureRequest.subsetOfAttributes();
383  }
384 
385  // Check if we even cache the information requested
386  Q_FOREACH ( int attr, requestedAttributes )
387  {
388  if ( !mCachedAttributes.contains( attr ) )
389  {
390  return false;
391  }
392  }
393 
394  // If the request needs geometry but we don't cache this...
395  if ( !featureRequest.flags().testFlag( QgsFeatureRequest::NoGeometry )
396  && !mCacheGeometry )
397  {
398  return false;
399  }
400 
401  return true;
402 }
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.
QSet< QgsFeatureId > QgsFeatureIds
Definition: qgsfeature.h:355
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.
const QgsFeatureIds & filterFids() const
Get feature IDs that should be fetched.
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:78
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:135
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...
bool hasGeometryType() const
Returns true if this is a geometry layer and false in case of NoGeometry (table only) or UnknownGeome...
void featureAdded(QgsFeatureId fid)
Is emitted, when a new feature has been added to the layer and this cache.
long featureCount(const QString &legendKey) const
Number of features rendered with specified legend key.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const
Query the layer for features specified in request.
void progress(int i, bool &cancel)
When filling the cache, this signal gets emitted periodically to notify about the progress and to be ...
This class wraps a request for features to a vector layer (or directly its vector data provider)...
QList< int > QgsAttributeList
Obsolete, will be ignored. If a filterRect is set it will be used anyway. Filter using a rectangle...
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.
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.
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.
Abstract base class for cache indices.
Definition: qgscacheindex.h:29
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.
qint64 QgsFeatureId
Definition: qgsfeature.h:32
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.
QgsVectorDataProvider * dataProvider()
Returns the data provider.
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.