QGIS API Documentation  2.14.0-Essen
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 
23  : QObject( parent )
24  , mLayer( layer )
25  , mFullCache( false )
26 {
27  mCache.setMaxCost( cacheSize );
28 
29  connect( mLayer, SIGNAL( featureDeleted( QgsFeatureId ) ), SLOT( featureDeleted( QgsFeatureId ) ) );
30  connect( mLayer, SIGNAL( featureAdded( QgsFeatureId ) ), SLOT( onFeatureAdded( QgsFeatureId ) ) );
31  connect( mLayer, SIGNAL( layerDeleted() ), SLOT( layerDeleted() ) );
32 
33  setCacheGeometry( true );
36 
37  connect( mLayer, SIGNAL( attributeDeleted( int ) ), SLOT( attributeDeleted( int ) ) );
38  connect( mLayer, SIGNAL( updatedFields() ), SLOT( invalidate() ) );
39  connect( mLayer, SIGNAL( dataChanged() ), SLOT( invalidate() ) );
40  connect( mLayer, SIGNAL( attributeValueChanged( QgsFeatureId, int, const QVariant& ) ), SLOT( onAttributeValueChanged( QgsFeatureId, int, const QVariant& ) ) );
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, SIGNAL( geometryChanged( QgsFeatureId, QgsGeometry& ) ), SLOT( geometryChanged( QgsFeatureId, QgsGeometry& ) ) );
65  }
66  else
67  {
68  disconnect( mLayer, SIGNAL( geometryChanged( QgsFeatureId, QgsGeometry& ) ), this, SLOT( geometryChanged( QgsFeatureId, QgsGeometry& ) ) );
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, SIGNAL( attributeAdded( int ) ), SLOT( attributeAdded( int ) ) );
128  }
129  else
130  {
131  disconnect( mLayer, SIGNAL( attributeAdded( int ) ), this, SLOT( attributeAdded( int ) ) );
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  }
184 }
185 
187 {
188  Q_FOREACH ( QgsAbstractCacheIndex* idx, mCacheIndices )
189  {
190  idx->flushFeature( fid );
191  }
192 }
193 
194 void QgsVectorLayerCache::onAttributeValueChanged( QgsFeatureId fid, int field, const QVariant& value )
195 {
196  QgsCachedFeature* cachedFeat = mCache[ fid ];
197 
198  if ( cachedFeat )
199  {
200  cachedFeat->mFeature->setAttribute( field, value );
201  }
202 
203  emit attributeValueChanged( fid, field, value );
204 }
205 
206 void QgsVectorLayerCache::featureDeleted( QgsFeatureId fid )
207 {
208  mCache.remove( fid );
209 }
210 
211 void QgsVectorLayerCache::onFeatureAdded( QgsFeatureId fid )
212 {
213  if ( mFullCache )
214  {
215  if ( cacheSize() <= mLayer->featureCount() )
216  {
217  setCacheSize( mLayer->featureCount() + 100 );
218  }
219 
220  QgsFeature feat;
221  featureAtId( fid, feat );
222  }
223  emit featureAdded( fid );
224 }
225 
226 void QgsVectorLayerCache::attributeAdded( int field )
227 {
228  Q_UNUSED( field )
229  mCachedAttributes.append( field );
230  mCache.clear();
231 }
232 
233 void QgsVectorLayerCache::attributeDeleted( int field )
234 {
235  QgsAttributeList attrs = mCachedAttributes;
236  mCachedAttributes.clear();
237 
238  Q_FOREACH ( int attr, attrs )
239  {
240  if ( attr < field )
241  mCachedAttributes << attr;
242  else if ( attr > field )
243  mCachedAttributes << attr - 1;
244  }
245 }
246 
247 void QgsVectorLayerCache::geometryChanged( QgsFeatureId fid, QgsGeometry& geom )
248 {
249  QgsCachedFeature* cachedFeat = mCache[ fid ];
250 
251  if ( cachedFeat )
252  {
253  cachedFeat->mFeature->setGeometry( geom );
254  }
255 }
256 
257 void QgsVectorLayerCache::layerDeleted()
258 {
259  emit cachedLayerDeleted();
260  mLayer = nullptr;
261 }
262 
263 void QgsVectorLayerCache::invalidate()
264 {
265  mCache.clear();
266  emit invalidated();
267 }
268 
270 {
272  bool requiresWriterIt = true; // If a not yet cached, but cachable request is made, this stays true.
273 
274  if ( checkInformationCovered( featureRequest ) )
275  {
276  // If we have a full cache available, run on this
277  if ( mFullCache )
278  {
279  it = QgsFeatureIterator( new QgsCachedFeatureIterator( this, featureRequest ) );
280  requiresWriterIt = false;
281  }
282  else
283  {
284  // Check if an index is able to deliver the requested features
285  Q_FOREACH ( QgsAbstractCacheIndex *idx, mCacheIndices )
286  {
287  if ( idx->getCacheIterator( it, featureRequest ) )
288  {
289  requiresWriterIt = false;
290  break;
291  }
292  }
293  }
294  }
295  else
296  {
297  // Let the layer answer the request, so no caching of requests
298  // we don't want to cache is done
299  requiresWriterIt = false;
300  it = mLayer->getFeatures( featureRequest );
301  }
302 
303  if ( requiresWriterIt && mLayer->dataProvider() )
304  {
305  // No index was able to satisfy the request
306  QgsFeatureRequest myRequest = QgsFeatureRequest( featureRequest );
307 
308  // Make sure if we cache the geometry, it gets fetched
309  if ( mCacheGeometry && mLayer->hasGeometryType() )
310  myRequest.setFlags( featureRequest.flags() & ~QgsFeatureRequest::NoGeometry );
311 
312  // Make sure, all the cached attributes are requested as well
313  QSet<int> attrs = featureRequest.subsetOfAttributes().toSet() + mCachedAttributes.toSet();
314  myRequest.setSubsetOfAttributes( attrs.toList() );
315 
316  it = QgsFeatureIterator( new QgsCachedFeatureWriterIterator( this, myRequest ) );
317  }
318 
319  return it;
320 }
321 
323 {
324  return mCache.contains( fid );
325 }
326 
328 {
329  QgsAttributeList requestedAttributes;
330 
331  if ( !featureRequest.flags().testFlag( QgsFeatureRequest::SubsetOfAttributes ) )
332  {
333  requestedAttributes = mLayer->attributeList();
334  }
335  else
336  {
337  requestedAttributes = featureRequest.subsetOfAttributes();
338  }
339 
340  // Check if we even cache the information requested
341  Q_FOREACH ( int attr, requestedAttributes )
342  {
343  if ( !mCachedAttributes.contains( attr ) )
344  {
345  return false;
346  }
347  }
348 
349  // If the request needs geometry but we don't cache this...
350  if ( !featureRequest.flags().testFlag( QgsFeatureRequest::NoGeometry )
351  && !mCacheGeometry )
352  {
353  return false;
354  }
355 
356  return true;
357 }
void clear()
Wrapper for iterator of features from vector data provider or vector layer.
friend class QgsCachedFeatureWriterIterator
QgsAttributeList attributeList() const
Returns list of attribute indexes.
const Flags & flags() const
QgsVectorLayerCache(QgsVectorLayer *layer, int cacheSize, QObject *parent=nullptr)
int size() const
void invalidated()
The cache has been invalidated and cleared.
bool contains(const Key &key) const
bool removeCachedFeature(QgsFeatureId fid)
Removes the feature identified by fid from the cache if present.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest())
Query the provider for features specified in request.
bool isFidCached(const QgsFeatureId fid)
Check if a certain feature id is cached.
QgsFeatureRequest & setSubsetOfAttributes(const QgsAttributeList &attrs)
Set a subset of attributes that will be fetched.
const QgsAttributeList & subsetOfAttributes() const
Return the subset of attributes which at least need to be fetched.
A geometry is the spatial representation of a feature.
Definition: qgsgeometry.h:76
void requestCompleted(const QgsFeatureRequest &featureRequest, const QgsFeatureIds &fids)
Gets called, whenever the full list of feature ids for a certain request is known.
int maxCost() const
QSet< T > toSet() const
void setMaxCost(int cost)
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:187
void attributeValueChanged(QgsFeatureId fid, int field, const QVariant &value)
Is emitted when an attribute is changed.
bool disconnect(const QObject *sender, const char *signal, const QObject *receiver, const char *method)
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...
long featureCount(QgsSymbolV2 *symbol)
Number of features rendered with specified symbol.
void featureAdded(QgsFeatureId fid)
Is emitted, when a new feature has been added to the layer and this cache.
int elapsed() const
void append(const T &value)
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)...
int restart()
QgsFeatureRequest & setFlags(const QgsFeatureRequest::Flags &flags)
Set flags that affect how features will be fetched.
Fetch only a subset of attributes (setSubsetOfAttributes sets this flag)
int count() const
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.
bool contains(const T &value) const
virtual bool getCacheIterator(QgsFeatureIterator &featureIterator, const QgsFeatureRequest &featureRequest)=0
Is called, when a feature request is issued on a cached layer.
bool hasGeometryType() const
Returns true if this is a geometry layer and false in case of NoGeometry (table only) or UnknownGeome...
Abstract base class for cache indices.
Definition: qgscacheindex.h:29
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:31
QList< T > toList() const
void start()
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)
Geometry is not required. It may still be returned if e.g. required for a filter condition.
bool connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
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.
bool remove(const Key &key)
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...
void clear()