QGIS API Documentation  2.99.0-Master (53aba61)
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 #include "qgsvectorlayerjoininfo.h"
23 
24 QgsVectorLayerCache::QgsVectorLayerCache( QgsVectorLayer *layer, int cacheSize, QObject *parent )
25  : QObject( parent )
26  , mLayer( layer )
27 {
28  mCache.setMaxCost( cacheSize );
29 
30  connect( mLayer, &QgsVectorLayer::featureDeleted, this, &QgsVectorLayerCache::featureDeleted );
31  connect( mLayer, &QgsVectorLayer::featureAdded, this, &QgsVectorLayerCache::onFeatureAdded );
32  connect( mLayer, &QgsVectorLayer::destroyed, this, &QgsVectorLayerCache::layerDeleted );
33 
34  setCacheGeometry( true );
37 
38  connect( mLayer, &QgsVectorLayer::attributeDeleted, this, &QgsVectorLayerCache::attributeDeleted );
39  connect( mLayer, &QgsVectorLayer::updatedFields, this, &QgsVectorLayerCache::invalidate );
40  connect( mLayer, &QgsVectorLayer::dataChanged, this, &QgsVectorLayerCache::invalidate );
41  connect( mLayer, &QgsVectorLayer::attributeValueChanged, this, &QgsVectorLayerCache::onAttributeValueChanged );
42 
43  connectJoinedLayers();
44 }
45 
47 {
48  qDeleteAll( mCacheIndices );
49  mCacheIndices.clear();
50 }
51 
53 {
54  mCache.setMaxCost( cacheSize );
55 }
56 
58 {
59  return mCache.maxCost();
60 }
61 
63 {
64  bool shouldCacheGeometry = cacheGeometry && mLayer->isSpatial();
65  bool mustInvalidate = shouldCacheGeometry && !mCacheGeometry; // going from no geometry -> geometry, so have to clear existing cache entries
66  mCacheGeometry = shouldCacheGeometry;
67  if ( cacheGeometry )
68  {
69  connect( mLayer, &QgsVectorLayer::geometryChanged, this, &QgsVectorLayerCache::geometryChanged );
70  }
71  else
72  {
73  disconnect( mLayer, &QgsVectorLayer::geometryChanged, this, &QgsVectorLayerCache::geometryChanged );
74  }
75  if ( mustInvalidate )
76  {
77  invalidate();
78  }
79 }
80 
82 {
83  mCachedAttributes = attributes;
84 }
85 
86 void QgsVectorLayerCache::setFullCache( bool fullCache )
87 {
88  mFullCache = fullCache;
89 
90  if ( mFullCache )
91  {
92  // Add a little more than necessary...
93  setCacheSize( mLayer->featureCount() + 100 );
94 
95  // Initialize the cache...
97  .setSubsetOfAttributes( mCachedAttributes )
98  .setFlags( mCacheGeometry ? QgsFeatureRequest::NoFlags : QgsFeatureRequest::NoGeometry ) ) );
99 
100  int i = 0;
101 
102  QTime t;
103  t.start();
104 
105  QgsFeature f;
106  while ( it.nextFeature( f ) )
107  {
108  ++i;
109 
110  if ( t.elapsed() > 1000 )
111  {
112  bool cancel = false;
113  emit progress( i, cancel );
114  if ( cancel )
115  break;
116 
117  t.restart();
118  }
119  }
120 
121  it.close();
122 
123  emit finished();
124  }
125 }
126 
128 {
129  mCacheIndices.append( cacheIndex );
130 }
131 
132 void QgsVectorLayerCache::setCacheAddedAttributes( bool cacheAddedAttributes )
133 {
134  if ( cacheAddedAttributes )
135  {
136  connect( mLayer, &QgsVectorLayer::attributeAdded, this, &QgsVectorLayerCache::attributeAdded );
137  }
138  else
139  {
140  disconnect( mLayer, &QgsVectorLayer::attributeAdded, this, &QgsVectorLayerCache::attributeAdded );
141  }
142 }
143 
144 bool QgsVectorLayerCache::featureAtId( QgsFeatureId featureId, QgsFeature &feature, bool skipCache )
145 {
146  bool featureFound = false;
147 
148  QgsCachedFeature *cachedFeature = nullptr;
149 
150  if ( !skipCache )
151  {
152  cachedFeature = mCache[ featureId ];
153  }
154 
155  if ( cachedFeature )
156  {
157  feature = QgsFeature( *cachedFeature->feature() );
158  featureFound = true;
159  }
160  else if ( mLayer->getFeatures( QgsFeatureRequest()
161  .setFilterFid( featureId )
162  .setSubsetOfAttributes( mCachedAttributes )
163  .setFlags( !mCacheGeometry ? QgsFeatureRequest::NoGeometry : QgsFeatureRequest::Flags( nullptr ) ) )
164  .nextFeature( feature ) )
165  {
166  cacheFeature( feature );
167  featureFound = true;
168  }
169 
170  return featureFound;
171 }
172 
174 {
175  return mCache.remove( fid );
176 }
177 
179 {
180  return mLayer;
181 }
182 
184 {
185  return mLayer->crs();
186 }
187 
189 {
190  return mLayer->wkbType();
191 }
192 
194 {
195  return mLayer->fields();
196 }
197 
199 {
200  return mLayer->featureCount();
201 }
202 
204 {
205  // If a request is too large for the cache don't notify to prevent from indexing incomplete requests
206  if ( fids.count() <= mCache.size() )
207  {
208  Q_FOREACH ( QgsAbstractCacheIndex *idx, mCacheIndices )
209  {
210  idx->requestCompleted( featureRequest, fids );
211  }
212  if ( featureRequest.filterType() == QgsFeatureRequest::FilterNone )
213  {
214  mFullCache = true;
215  }
216  }
217 }
218 
220 {
221  Q_FOREACH ( QgsAbstractCacheIndex *idx, mCacheIndices )
222  {
223  idx->flushFeature( fid );
224  }
225 }
226 
227 void QgsVectorLayerCache::onAttributeValueChanged( QgsFeatureId fid, int field, const QVariant &value )
228 {
229  QgsCachedFeature *cachedFeat = mCache[ fid ];
230 
231  if ( cachedFeat )
232  {
233  cachedFeat->mFeature->setAttribute( field, value );
234  }
235 
236  emit attributeValueChanged( fid, field, value );
237 }
238 
239 void QgsVectorLayerCache::onJoinAttributeValueChanged( QgsFeatureId fid, int field, const QVariant &value )
240 {
241  const QgsVectorLayer *joinLayer = qobject_cast<const QgsVectorLayer *>( sender() );
242 
243  Q_FOREACH ( const QgsVectorLayerJoinInfo &info, mLayer->vectorJoins() )
244  {
245  if ( joinLayer == info.joinLayer() )
246  {
247  const QgsFeature feature = mLayer->joinBuffer()->targetedFeatureOf( &info, joinLayer->getFeature( fid ) );
248 
249  const QString fieldName = info.prefixedFieldName( joinLayer->fields().field( field ) );
250  const int fieldIndex = mLayer->fields().indexFromName( fieldName );
251 
252  if ( feature.isValid() && fieldIndex != -1 )
253  {
254  onAttributeValueChanged( feature.id(), fieldIndex, value );
255  return;
256  }
257  }
258  }
259 }
260 
261 void QgsVectorLayerCache::featureDeleted( QgsFeatureId fid )
262 {
263  mCache.remove( fid );
264 }
265 
266 void QgsVectorLayerCache::onFeatureAdded( QgsFeatureId fid )
267 {
268  if ( mFullCache )
269  {
270  if ( cacheSize() <= mLayer->featureCount() )
271  {
272  setCacheSize( mLayer->featureCount() + 100 );
273  }
274 
275  QgsFeature feat;
276  featureAtId( fid, feat );
277  }
278  emit featureAdded( fid );
279 }
280 
281 void QgsVectorLayerCache::attributeAdded( int field )
282 {
283  Q_UNUSED( field )
284  mCachedAttributes.append( field );
285  invalidate();
286 }
287 
288 void QgsVectorLayerCache::attributeDeleted( int field )
289 {
290  QgsAttributeList attrs = mCachedAttributes;
291  mCachedAttributes.clear();
292 
293  Q_FOREACH ( int attr, attrs )
294  {
295  if ( attr < field )
296  mCachedAttributes << attr;
297  else if ( attr > field )
298  mCachedAttributes << attr - 1;
299  }
300 }
301 
302 void QgsVectorLayerCache::geometryChanged( QgsFeatureId fid, const QgsGeometry &geom )
303 {
304  QgsCachedFeature *cachedFeat = mCache[ fid ];
305 
306  if ( cachedFeat )
307  {
308  cachedFeat->mFeature->setGeometry( geom );
309  }
310 }
311 
312 void QgsVectorLayerCache::layerDeleted()
313 {
314  emit cachedLayerDeleted();
315  mLayer = nullptr;
316 }
317 
318 void QgsVectorLayerCache::invalidate()
319 {
320  mCache.clear();
321  mFullCache = false;
322  emit invalidated();
323 }
324 
325 bool QgsVectorLayerCache::canUseCacheForRequest( const QgsFeatureRequest &featureRequest, QgsFeatureIterator &it )
326 {
327  // check first for available indices
328  Q_FOREACH ( QgsAbstractCacheIndex *idx, mCacheIndices )
329  {
330  if ( idx->getCacheIterator( it, featureRequest ) )
331  {
332  return true;
333  }
334  }
335 
336  // no indexes available, but maybe we have already cached all required features anyway?
337  switch ( featureRequest.filterType() )
338  {
340  {
341  if ( mCache.contains( featureRequest.filterFid() ) )
342  {
343  it = QgsFeatureIterator( new QgsCachedFeatureIterator( this, featureRequest ) );
344  return true;
345  }
346  break;
347  }
349  {
350  if ( mCache.keys().toSet().contains( featureRequest.filterFids() ) )
351  {
352  it = QgsFeatureIterator( new QgsCachedFeatureIterator( this, featureRequest ) );
353  return true;
354  }
355  break;
356  }
359  {
360  if ( mFullCache )
361  {
362  it = QgsFeatureIterator( new QgsCachedFeatureIterator( this, featureRequest ) );
363  return true;
364  }
365  break;
366  }
367 
368  }
369  return false;
370 }
371 
373 {
375  bool requiresWriterIt = true; // If a not yet cached, but cachable request is made, this stays true.
376 
377  if ( checkInformationCovered( featureRequest ) )
378  {
379  // If we have a full cache available, run on this
380  if ( mFullCache )
381  {
382  it = QgsFeatureIterator( new QgsCachedFeatureIterator( this, featureRequest ) );
383  requiresWriterIt = false;
384  }
385  else
386  {
387  // may still be able to satisfy request using cache
388  requiresWriterIt = !canUseCacheForRequest( featureRequest, it );
389  }
390  }
391  else
392  {
393  // Let the layer answer the request, so no caching of requests
394  // we don't want to cache is done
395  requiresWriterIt = false;
396  it = mLayer->getFeatures( featureRequest );
397  }
398 
399  if ( requiresWriterIt && mLayer->dataProvider() )
400  {
401  // No index was able to satisfy the request
402  QgsFeatureRequest myRequest = QgsFeatureRequest( featureRequest );
403 
404  // Make sure if we cache the geometry, it gets fetched
405  if ( mCacheGeometry && mLayer->isSpatial() )
406  myRequest.setFlags( featureRequest.flags() & ~QgsFeatureRequest::NoGeometry );
407 
408  // Make sure, all the cached attributes are requested as well
409  QSet<int> attrs = featureRequest.subsetOfAttributes().toSet() + mCachedAttributes.toSet();
410  myRequest.setSubsetOfAttributes( attrs.toList() );
411 
412  it = QgsFeatureIterator( new QgsCachedFeatureWriterIterator( this, myRequest ) );
413  }
414 
415  return it;
416 }
417 
419 {
420  return mCache.contains( fid );
421 }
422 
424 {
425  QgsAttributeList requestedAttributes;
426 
427  if ( !featureRequest.flags().testFlag( QgsFeatureRequest::SubsetOfAttributes ) )
428  {
429  requestedAttributes = mLayer->attributeList();
430  }
431  else
432  {
433  requestedAttributes = featureRequest.subsetOfAttributes();
434  }
435 
436  // Check if we even cache the information requested
437  Q_FOREACH ( int attr, requestedAttributes )
438  {
439  if ( !mCachedAttributes.contains( attr ) )
440  {
441  return false;
442  }
443  }
444 
445  // If the request needs geometry but we don't cache this...
446  return !( !featureRequest.flags().testFlag( QgsFeatureRequest::NoGeometry )
447  && !mCacheGeometry );
448 }
449 
450 void QgsVectorLayerCache::connectJoinedLayers() const
451 {
452  Q_FOREACH ( const QgsVectorLayerJoinInfo &info, mLayer->vectorJoins() )
453  {
454  const QgsVectorLayer *vl = info.joinLayer();
455  if ( vl )
456  connect( vl, &QgsVectorLayer::attributeValueChanged, this, &QgsVectorLayerCache::onJoinAttributeValueChanged );
457  }
458 }
bool isValid() const
Returns the validity of this feature.
Definition: qgsfeature.cpp:176
QgsFeatureId id
Definition: qgsfeature.h:70
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.
QgsFeature getFeature(QgsFeatureId fid) const
Query the layer for the feature with the given id.
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:92
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
int indexFromName(const QString &fieldName) const
Get the field index from the field name.
Definition: qgsfields.cpp:174
const QList< QgsVectorLayerJoinInfo > vectorJoins() const
QgsVectorLayer * joinLayer() const
Returns joined layer (may be null if the reference was set by layer ID and not resolved yet) ...
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.
Defines left outer join from our vector layer to some other vector layer.
QString prefixedFieldName(const QgsField &field) const
Returns the prefixed name of the field.
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.
QgsVectorLayerJoinBuffer * joinBuffer()
Accessor to the join buffer object.
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
QgsFeature targetedFeatureOf(const QgsVectorLayerJoinInfo *info, const QgsFeature &feature) const
Returns the targeted feature corresponding to the joined feature.
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
QgsField field(int fieldIdx) const
Get field at particular index (must be in range 0..N-1)
Definition: qgsfields.cpp:140
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.