QGIS API Documentation  3.4.15-Madeira (e83d02e274)
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 #include "qgsvectorlayer.h"
24 
25 QgsVectorLayerCache::QgsVectorLayerCache( QgsVectorLayer *layer, int cacheSize, QObject *parent )
26  : QObject( parent )
27  , mLayer( layer )
28 {
29  mCache.setMaxCost( cacheSize );
30 
31  connect( mLayer, &QgsVectorLayer::featureDeleted, this, &QgsVectorLayerCache::featureDeleted );
32  connect( mLayer, &QgsVectorLayer::featureAdded, this, &QgsVectorLayerCache::onFeatureAdded );
33  connect( mLayer, &QgsVectorLayer::destroyed, this, &QgsVectorLayerCache::layerDeleted );
34 
35  setCacheGeometry( true );
38 
39  connect( mLayer, &QgsVectorLayer::attributeDeleted, this, &QgsVectorLayerCache::attributeDeleted );
40  connect( mLayer, &QgsVectorLayer::updatedFields, this, &QgsVectorLayerCache::invalidate );
41  connect( mLayer, &QgsVectorLayer::dataChanged, this, &QgsVectorLayerCache::invalidate );
42  connect( mLayer, &QgsVectorLayer::attributeValueChanged, this, &QgsVectorLayerCache::onAttributeValueChanged );
43 
44  connectJoinedLayers();
45 }
46 
48 {
49  qDeleteAll( mCacheIndices );
50  mCacheIndices.clear();
51 }
52 
54 {
55  mCache.setMaxCost( cacheSize );
56 }
57 
59 {
60  return mCache.maxCost();
61 }
62 
64 {
65  bool shouldCacheGeometry = cacheGeometry && mLayer->isSpatial();
66  bool mustInvalidate = shouldCacheGeometry && !mCacheGeometry; // going from no geometry -> geometry, so have to clear existing cache entries
67  mCacheGeometry = shouldCacheGeometry;
68  if ( cacheGeometry )
69  {
70  connect( mLayer, &QgsVectorLayer::geometryChanged, this, &QgsVectorLayerCache::geometryChanged );
71  }
72  else
73  {
74  disconnect( mLayer, &QgsVectorLayer::geometryChanged, this, &QgsVectorLayerCache::geometryChanged );
75  }
76  if ( mustInvalidate )
77  {
78  invalidate();
79  }
80 }
81 
83 {
84  mCachedAttributes = attributes;
85 }
86 
87 void QgsVectorLayerCache::setFullCache( bool fullCache )
88 {
89  mFullCache = fullCache;
90 
91  if ( mFullCache )
92  {
93  // Add a little more than necessary...
94  setCacheSize( mLayer->featureCount() + 100 );
95 
96  // Initialize the cache...
98  .setSubsetOfAttributes( mCachedAttributes )
99  .setFlags( mCacheGeometry ? QgsFeatureRequest::NoFlags : QgsFeatureRequest::NoGeometry ) ) );
100 
101  int i = 0;
102 
103  QTime t;
104  t.start();
105 
106  QgsFeature f;
107  while ( it.nextFeature( f ) )
108  {
109  ++i;
110 
111  if ( t.elapsed() > 1000 )
112  {
113  bool cancel = false;
114  emit progress( i, cancel );
115  if ( cancel )
116  break;
117 
118  t.restart();
119  }
120  }
121 
122  it.close();
123 
124  emit finished();
125  }
126 }
127 
129 {
130  mCacheIndices.append( cacheIndex );
131 }
132 
133 void QgsVectorLayerCache::setCacheAddedAttributes( bool cacheAddedAttributes )
134 {
135  if ( cacheAddedAttributes )
136  {
137  connect( mLayer, &QgsVectorLayer::attributeAdded, this, &QgsVectorLayerCache::attributeAdded );
138  }
139  else
140  {
141  disconnect( mLayer, &QgsVectorLayer::attributeAdded, this, &QgsVectorLayerCache::attributeAdded );
142  }
143 }
144 
145 bool QgsVectorLayerCache::featureAtId( QgsFeatureId featureId, QgsFeature &feature, bool skipCache )
146 {
147  bool featureFound = false;
148 
149  QgsCachedFeature *cachedFeature = nullptr;
150 
151  if ( !skipCache )
152  {
153  cachedFeature = mCache[ featureId ];
154  }
155 
156  if ( cachedFeature )
157  {
158  feature = QgsFeature( *cachedFeature->feature() );
159  featureFound = true;
160  }
161  else if ( mLayer->getFeatures( QgsFeatureRequest()
162  .setFilterFid( featureId )
163  .setSubsetOfAttributes( mCachedAttributes )
164  .setFlags( !mCacheGeometry ? QgsFeatureRequest::NoGeometry : QgsFeatureRequest::Flags( nullptr ) ) )
165  .nextFeature( feature ) )
166  {
167  cacheFeature( feature );
168  featureFound = true;
169  }
170 
171  return featureFound;
172 }
173 
175 {
176  return mCache.remove( fid );
177 }
178 
180 {
181  return mLayer;
182 }
183 
185 {
186  return mLayer->crs();
187 }
188 
190 {
191  return mLayer->wkbType();
192 }
193 
195 {
196  return mLayer->fields();
197 }
198 
200 {
201  return mLayer->featureCount();
202 }
203 
205 {
206  // If a request is too large for the cache don't notify to prevent from indexing incomplete requests
207  if ( fids.count() <= mCache.size() )
208  {
209  for ( const auto &idx : qgis::as_const( mCacheIndices ) )
210  {
211  idx->requestCompleted( featureRequest, fids );
212  }
213  if ( featureRequest.filterType() == QgsFeatureRequest::FilterNone &&
214  ( featureRequest.filterRect().isNull() || featureRequest.filterRect().contains( mLayer->extent() ) ) )
215  {
216  mFullCache = true;
217  }
218  }
219 }
220 
222 {
223  Q_FOREACH ( QgsAbstractCacheIndex *idx, mCacheIndices )
224  {
225  idx->flushFeature( fid );
226  }
227 }
228 
229 void QgsVectorLayerCache::onAttributeValueChanged( QgsFeatureId fid, int field, const QVariant &value )
230 {
231  QgsCachedFeature *cachedFeat = mCache[ fid ];
232 
233  if ( cachedFeat )
234  {
235  cachedFeat->mFeature->setAttribute( field, value );
236  }
237 
238  emit attributeValueChanged( fid, field, value );
239 }
240 
241 void QgsVectorLayerCache::onJoinAttributeValueChanged( QgsFeatureId fid, int field, const QVariant &value )
242 {
243  const QgsVectorLayer *joinLayer = qobject_cast<const QgsVectorLayer *>( sender() );
244 
245  Q_FOREACH ( const QgsVectorLayerJoinInfo &info, mLayer->vectorJoins() )
246  {
247  if ( joinLayer == info.joinLayer() )
248  {
249  const QgsFeature feature = mLayer->joinBuffer()->targetedFeatureOf( &info, joinLayer->getFeature( fid ) );
250 
251  const QString fieldName = info.prefixedFieldName( joinLayer->fields().field( field ) );
252  const int fieldIndex = mLayer->fields().indexFromName( fieldName );
253 
254  if ( feature.isValid() && fieldIndex != -1 )
255  {
256  onAttributeValueChanged( feature.id(), fieldIndex, value );
257  return;
258  }
259  }
260  }
261 }
262 
263 void QgsVectorLayerCache::featureDeleted( QgsFeatureId fid )
264 {
265  mCache.remove( fid );
266 }
267 
268 void QgsVectorLayerCache::onFeatureAdded( QgsFeatureId fid )
269 {
270  if ( mFullCache )
271  {
272  if ( cacheSize() <= mLayer->featureCount() )
273  {
274  setCacheSize( mLayer->featureCount() + 100 );
275  }
276 
277  QgsFeature feat;
278  featureAtId( fid, feat );
279  }
280  emit featureAdded( fid );
281 }
282 
283 void QgsVectorLayerCache::attributeAdded( int field )
284 {
285  Q_UNUSED( field )
286  mCachedAttributes.append( field );
287  invalidate();
288 }
289 
290 void QgsVectorLayerCache::attributeDeleted( int field )
291 {
292  QgsAttributeList attrs = mCachedAttributes;
293  mCachedAttributes.clear();
294 
295  Q_FOREACH ( int attr, attrs )
296  {
297  if ( attr < field )
298  mCachedAttributes << attr;
299  else if ( attr > field )
300  mCachedAttributes << attr - 1;
301  }
302 }
303 
304 void QgsVectorLayerCache::geometryChanged( QgsFeatureId fid, const QgsGeometry &geom )
305 {
306  QgsCachedFeature *cachedFeat = mCache[ fid ];
307 
308  if ( cachedFeat )
309  {
310  cachedFeat->mFeature->setGeometry( geom );
311  }
312 }
313 
314 void QgsVectorLayerCache::layerDeleted()
315 {
316  emit cachedLayerDeleted();
317  mLayer = nullptr;
318 }
319 
320 void QgsVectorLayerCache::invalidate()
321 {
322  mCache.clear();
323  mFullCache = false;
324  emit invalidated();
325 }
326 
327 bool QgsVectorLayerCache::canUseCacheForRequest( const QgsFeatureRequest &featureRequest, QgsFeatureIterator &it )
328 {
329  // check first for available indices
330  Q_FOREACH ( QgsAbstractCacheIndex *idx, mCacheIndices )
331  {
332  if ( idx->getCacheIterator( it, featureRequest ) )
333  {
334  return true;
335  }
336  }
337 
338  // no indexes available, but maybe we have already cached all required features anyway?
339  switch ( featureRequest.filterType() )
340  {
342  {
343  if ( mCache.contains( featureRequest.filterFid() ) )
344  {
345  it = QgsFeatureIterator( new QgsCachedFeatureIterator( this, featureRequest ) );
346  return true;
347  }
348  break;
349  }
351  {
352  if ( mCache.keys().toSet().contains( featureRequest.filterFids() ) )
353  {
354  it = QgsFeatureIterator( new QgsCachedFeatureIterator( this, featureRequest ) );
355  return true;
356  }
357  break;
358  }
361  {
362  if ( mFullCache )
363  {
364  it = QgsFeatureIterator( new QgsCachedFeatureIterator( this, featureRequest ) );
365  return true;
366  }
367  break;
368  }
369 
370  }
371  return false;
372 }
373 
375 {
377  bool requiresWriterIt = true; // If a not yet cached, but cachable request is made, this stays true.
378 
379  if ( checkInformationCovered( featureRequest ) )
380  {
381  // If we have a full cache available, run on this
382  if ( mFullCache )
383  {
384  it = QgsFeatureIterator( new QgsCachedFeatureIterator( this, featureRequest ) );
385  requiresWriterIt = false;
386  }
387  else
388  {
389  // may still be able to satisfy request using cache
390  requiresWriterIt = !canUseCacheForRequest( featureRequest, it );
391  }
392  }
393  else
394  {
395  // Let the layer answer the request, so no caching of requests
396  // we don't want to cache is done
397  requiresWriterIt = false;
398  it = mLayer->getFeatures( featureRequest );
399  }
400 
401  if ( requiresWriterIt && mLayer->dataProvider() )
402  {
403  // No index was able to satisfy the request
404  QgsFeatureRequest myRequest = QgsFeatureRequest( featureRequest );
405 
406  // Make sure if we cache the geometry, it gets fetched
407  if ( mCacheGeometry && mLayer->isSpatial() )
408  myRequest.setFlags( featureRequest.flags() & ~QgsFeatureRequest::NoGeometry );
409 
410  // Make sure, all the cached attributes are requested as well
411  QSet<int> attrs = featureRequest.subsetOfAttributes().toSet() + mCachedAttributes.toSet();
412  myRequest.setSubsetOfAttributes( attrs.toList() );
413 
414  it = QgsFeatureIterator( new QgsCachedFeatureWriterIterator( this, myRequest ) );
415  }
416 
417  return it;
418 }
419 
421 {
422  return mCache.contains( fid );
423 }
424 
426 {
427  QgsAttributeList requestedAttributes;
428 
429  if ( !featureRequest.flags().testFlag( QgsFeatureRequest::SubsetOfAttributes ) )
430  {
431  requestedAttributes = mLayer->attributeList();
432  }
433  else
434  {
435  requestedAttributes = featureRequest.subsetOfAttributes();
436  }
437 
438  // Check if we even cache the information requested
439  Q_FOREACH ( int attr, requestedAttributes )
440  {
441  if ( !mCachedAttributes.contains( attr ) )
442  {
443  return false;
444  }
445  }
446 
447  // If the request needs geometry but we don't cache this...
448  return !( !featureRequest.flags().testFlag( QgsFeatureRequest::NoGeometry )
449  && !mCacheGeometry );
450 }
451 
452 void QgsVectorLayerCache::connectJoinedLayers() const
453 {
454  Q_FOREACH ( const QgsVectorLayerJoinInfo &info, mLayer->vectorJoins() )
455  {
456  const QgsVectorLayer *vl = info.joinLayer();
457  if ( vl )
458  connect( vl, &QgsVectorLayer::attributeValueChanged, this, &QgsVectorLayerCache::onJoinAttributeValueChanged );
459  }
460 }
QString prefixedFieldName(const QgsField &field) const
Returns the prefixed name of the field.
const QList< QgsVectorLayerJoinInfo > vectorJoins() const
QgsFeatureId id
Definition: qgsfeature.h:64
Wrapper for iterator of features from vector data provider or vector layer.
friend class QgsCachedFeatureWriterIterator
QgsWkbTypes::Type wkbType() const
Returns the geometry type for features in the cache.
QgsAttributeList attributeList() const
Returns list of attribute indexes.
Filter using feature ID.
const Flags & flags() const
Filter using feature IDs.
QSet< QgsFeatureId > QgsFeatureIds
Definition: qgsfeatureid.h:34
QgsVectorLayerCache(QgsVectorLayer *layer, int cacheSize, QObject *parent=nullptr)
QgsFeatureId filterFid() const
Gets the feature ID that should be fetched.
bool isValid() const
Returns the validity of this feature.
Definition: qgsfeature.cpp:183
void invalidated()
The cache has been invalidated and cleared.
const QgsRectangle & filterRect() const
Returns the rectangle from which features will be taken.
QgsWkbTypes::Type wkbType() const FINAL
Returns the WKBType or WKBUnknown in case of error.
bool removeCachedFeature(QgsFeatureId fid)
Removes the feature identified by fid from the cache if present.
const QgsFeatureIds & filterFids() const
Gets feature IDs that should be fetched.
bool contains(const QgsRectangle &rect) const
Returns true when rectangle contains other rectangle.
Definition: qgsrectangle.h:341
QgsAttributeList subsetOfAttributes() const
Returns the subset of attributes which at least need to be fetched.
bool isNull() const
Test if the rectangle is null (all coordinates zero or after call to setMinimal()).
Definition: qgsrectangle.h:435
qint64 QgsFeatureId
Definition: qgsfeatureid.h:25
QgsFeatureRequest & setSubsetOfAttributes(const QgsAttributeList &attrs)
Set a subset of attributes that will be fetched.
Container of fields for a vector layer.
Definition: qgsfields.h:42
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:106
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.
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:55
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.
bool isSpatial() const FINAL
Returns true if this is a geometry layer and false in case of NoGeometry (table only) or UnknownGeome...
QgsFields fields() const
Returns the fields associated with features in the cache.
void setCacheSubsetOfAttributes(const QgsAttributeList &attributes)
Set the subset of attributes to be cached.
void featureAdded(QgsFeatureId fid)
Is emitted, when a new feature has been added to the layer and this cache.
QgsFeature getFeature(QgsFeatureId fid) const
Query the layer for the feature with the given id.
Type
The WKB type describes the number of dimensions a geometry has.
Definition: qgswkbtypes.h:68
QgsFields fields() const FINAL
Returns the list of fields of this layer.
bool isFidCached(QgsFeatureId fid) const
Check if a certain feature id is cached.
void progress(int i, bool &cancel)
When filling the cache, this signal gets emitted periodically to notify about the progress and to be ...
Defines left outer join from our vector layer to some other vector layer.
This class wraps a request for features to a vector layer (or directly its vector data provider)...
QgsFeature targetedFeatureOf(const QgsVectorLayerJoinInfo *info, const QgsFeature &feature) const
Returns the targeted feature corresponding to the joined feature.
long featureCount(const QString &legendKey) const
Number of features rendered with specified legend key.
void geometryChanged(QgsFeatureId fid, const QgsGeometry &geometry)
Is emitted whenever a geometry change is done in the edit buffer.
FilterType filterType() const
Returns the filter type which is currently set on this request.
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.
QgsField field(int fieldIdx) const
Gets field at particular index (must be in range 0..N-1)
Definition: qgsfields.cpp:168
QgsRectangle extent() const FINAL
Returns the extent of the layer.
bool checkInformationCovered(const QgsFeatureRequest &featureRequest)
Checks if the information required to complete the request is cached.
QgsVectorLayerJoinBuffer * joinBuffer()
Returns the join buffer object.
int indexFromName(const QString &fieldName) const
Gets the field index from the field name.
Definition: qgsfields.cpp:202
QgsCoordinateReferenceSystem sourceCrs() const
Returns the coordinate reference system for features in the cache.
bool cacheGeometry() const
Returns true if the cache will fetch and cache feature geometries.
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:31
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.
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).
void dataChanged()
Data of layer changed.
QgsVectorLayer * joinLayer() const
Returns joined layer (may be null if the reference was set by layer ID and not resolved yet) ...
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const FINAL
Query the layer for features specified in request.
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() FINAL
Returns the layer&#39;s data provider.
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.
virtual void flushFeature(QgsFeatureId fid)=0
Is called, whenever a feature is removed from the cache.
void finished()
When filling the cache, this signal gets emitted once the cache is fully initialized.
friend class QgsCachedFeatureIterator
QgsCoordinateReferenceSystem crs
Definition: qgsmaplayer.h:70
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)
Sets flags that affect how features will be fetched.
long featureCount() const
Returns the number of features contained in the source, or -1 if the feature count is unknown...