QGIS API Documentation  2.3.0-Master
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups Pages
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 dot kuhn at gmx 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, 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( updatedFields() ) );
39  connect( mLayer, SIGNAL( attributeValueChanged( QgsFeatureId, int, const QVariant& ) ), SLOT( onAttributeValueChanged( QgsFeatureId, int, const QVariant& ) ) );
40 }
41 
42 void QgsVectorLayerCache::setCacheSize( int cacheSize )
43 {
44  mCache.setMaxCost( cacheSize );
45 }
46 
48 {
49  return mCache.maxCost();
50 }
51 
52 void QgsVectorLayerCache::setCacheGeometry( bool cacheGeometry )
53 {
54  mCacheGeometry = cacheGeometry && mLayer->hasGeometryType();
55  if ( cacheGeometry )
56  {
58  }
59  else
60  {
61  disconnect( mLayer, SIGNAL( geometryChanged( QgsFeatureId, QgsGeometry& ) ), this, SLOT( geometryChanged( QgsFeatureId, QgsGeometry& ) ) );
62  }
63 }
64 
66 {
67  mCachedAttributes = attributes;
68 }
69 
70 void QgsVectorLayerCache::setFullCache( bool fullCache )
71 {
72  mFullCache = fullCache;
73 
74  if ( mFullCache )
75  {
76  // Add a little more than necessary...
77  setCacheSize( mLayer->featureCount() + 100 );
78 
79  // Initialize the cache...
81  .setSubsetOfAttributes( mCachedAttributes )
82  .setFlags( !mCacheGeometry ? QgsFeatureRequest::NoGeometry : QgsFeatureRequest::Flags( 0 ) ) ) );
83 
84  int i = 0;
85 
86  QTime t;
87  t.start();
88 
89  QgsFeature f;
90  while ( it.nextFeature( f ) )
91  {
92  ++i;
93 
94  if ( t.elapsed() > 1000 )
95  {
96  bool cancel = false;
97  emit progress( i, cancel );
98  if ( cancel )
99  break;
100 
101  t.restart();
102  }
103  }
104 
105  it.close();
106 
107  emit finished();
108  }
109 }
110 
112 {
113  mCacheIndices.append( cacheIndex );
114 }
115 
116 void QgsVectorLayerCache::setCacheAddedAttributes( bool cacheAddedAttributes )
117 {
118  if ( cacheAddedAttributes )
119  {
120  connect( mLayer, SIGNAL( attributeAdded( int ) ), SLOT( attributeAdded( int ) ) );
121  }
122  else
123  {
124  disconnect( mLayer, SIGNAL( attributeAdded( int ) ), this, SLOT( attributeAdded( int ) ) );
125  }
126 }
127 
128 bool QgsVectorLayerCache::featureAtId( QgsFeatureId featureId, QgsFeature& feature, bool skipCache )
129 {
130  bool featureFound = false;
131 
132  QgsCachedFeature* cachedFeature = NULL;
133 
134  if ( !skipCache )
135  {
136  cachedFeature = mCache[ featureId ];
137  }
138 
139  if ( cachedFeature != NULL )
140  {
141  feature = QgsFeature( *cachedFeature->feature() );
142  featureFound = true;
143  }
144  else if ( mLayer->getFeatures( QgsFeatureRequest()
145  .setFilterFid( featureId )
146  .setSubsetOfAttributes( mCachedAttributes )
147  .setFlags( !mCacheGeometry ? QgsFeatureRequest::NoGeometry : QgsFeatureRequest::Flags( 0 ) ) )
148  .nextFeature( feature ) )
149  {
150  cacheFeature( feature );
151  featureFound = true;
152  }
153 
154  return featureFound;
155 }
156 
158 {
159  return mCache.remove( fid );
160 }
161 
163 {
164  return mLayer;
165 }
166 
168 {
169  // If a request is too large for the cache don't notify to prevent from indexing incomplete requests
170  if ( fids.count() < mCache.size() )
171  {
172  foreach ( QgsAbstractCacheIndex* idx, mCacheIndices )
173  {
174  idx->requestCompleted( featureRequest, fids );
175  }
176  }
177 }
178 
180 {
181  foreach ( QgsAbstractCacheIndex* idx, mCacheIndices )
182  {
183  idx->flushFeature( fid );
184  }
185 }
186 
187 void QgsVectorLayerCache::onAttributeValueChanged( QgsFeatureId fid, int field, const QVariant& value )
188 {
189  QgsCachedFeature* cachedFeat = mCache[ fid ];
190 
191  if ( NULL != cachedFeat )
192  {
193  cachedFeat->mFeature->setAttribute( field, value );
194  }
195 
196  emit attributeValueChanged( fid, field, value );
197 }
198 
200 {
201  mCache.remove( fid );
202 }
203 
205 {
206  if ( mFullCache )
207  {
208  if ( cacheSize() <= mLayer->featureCount() )
209  {
210  setCacheSize( mLayer->featureCount() + 100 );
211  }
212 
213  QgsFeature feat;
214  featureAtId( fid, feat );
215  }
216  emit( featureAdded( fid ) );
217 }
218 
220 {
221  Q_UNUSED( field )
222  mCachedAttributes.append( field );
223  mCache.clear();
224 }
225 
227 {
228  foreach ( QgsFeatureId fid, mCache.keys() )
229  {
230  mCache[ fid ]->mFeature->deleteAttribute( field );
231  }
232 }
233 
235 {
236  QgsCachedFeature* cachedFeat = mCache[ fid ];
237 
238  if ( cachedFeat != NULL )
239  {
240  cachedFeat->mFeature->setGeometry( geom );
241  }
242 }
243 
245 {
246  emit cachedLayerDeleted();
247  mLayer = NULL;
248 }
249 
251 {
252  mCache.clear();
253 }
254 
256 {
258  bool requiresWriterIt = true; // If a not yet cached, but cachable request is made, this stays true.
259 
260  if ( checkInformationCovered( featureRequest ) )
261  {
262  // If we have a full cache available, run on this
263  if ( mFullCache )
264  {
265  it = QgsFeatureIterator( new QgsCachedFeatureIterator( this, featureRequest ) );
266  requiresWriterIt = false;
267  }
268  else
269  {
270  // Check if an index is able to deliver the requested features
271  foreach ( QgsAbstractCacheIndex *idx, mCacheIndices )
272  {
273  if ( idx->getCacheIterator( it, featureRequest ) )
274  {
275  requiresWriterIt = false;
276  break;
277  }
278  }
279  }
280  }
281  else
282  {
283  // Let the layer answer the request, so no caching of requests
284  // we don't want to cache is done
285  requiresWriterIt = false;
286  it = mLayer->getFeatures( featureRequest );
287  }
288 
289  if ( requiresWriterIt && mLayer->dataProvider() )
290  {
291  // No index was able to satisfy the request
292  QgsFeatureRequest myRequest = QgsFeatureRequest( featureRequest );
293 
294  // Make sure if we cache the geometry, it gets fetched
296  myRequest.setFlags( featureRequest.flags() & ~QgsFeatureRequest::NoGeometry );
297 
298  // Make sure, all the cached attributes are requested as well
299  QSet<int> attrs = featureRequest.subsetOfAttributes().toSet() + mCachedAttributes.toSet();
300  myRequest.setSubsetOfAttributes( attrs.toList() );
301 
302  it = QgsFeatureIterator( new QgsCachedFeatureWriterIterator( this, myRequest ) );
303  }
304 
305  return it;
306 }
307 
309 {
310  return mCache.contains( fid );
311 }
312 
314 {
315  QgsAttributeList requestedAttributes;
316 
317  if ( !featureRequest.flags().testFlag( QgsFeatureRequest::SubsetOfAttributes ) )
318  {
319  requestedAttributes = mLayer->pendingAllAttributesList();
320  }
321  else
322  {
323  requestedAttributes = featureRequest.subsetOfAttributes();
324  }
325 
326  // Check if we even cache the information requested
327  foreach ( int attr, requestedAttributes )
328  {
329  if ( !mCachedAttributes.contains( attr ) )
330  {
331  return false;
332  }
333  }
334 
335  // If the request needs geometry but we don't cache this...
336  if ( !featureRequest.flags().testFlag( QgsFeatureRequest::NoGeometry )
337  && !mCacheGeometry )
338  {
339  return false;
340  }
341 
342  return true;
343 }
Wrapper for iterator of features from vector data provider or vector layer.
friend class QgsCachedFeatureWriterIterator
const Flags & flags() const
QList< QgsAbstractCacheIndex * > mCacheIndices
QgsAttributeList mCachedAttributes
QSet< QgsFeatureId > QgsFeatureIds
Definition: qgsfeature.h:326
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
bool setAttribute(int field, const QVariant &attr)
Set an attribute by id.
Definition: qgsfeature.cpp:190
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:114
QCache< QgsFeatureId, QgsCachedFeature > mCache
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.
void attributeAdded(int field)
void setGeometry(const QgsGeometry &geom)
Set this feature's geometry from another QgsGeometry object (deep copy)
Definition: qgsfeature.cpp:134
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
void featureDeleted(QgsFeatureId fid)
Fetch only a subset of attributes (setSubsetOfAttributes sets this flag)
void cachedLayerDeleted()
Is emitted when the cached layer is deleted.
void geometryChanged(QgsFeatureId fid, QgsGeometry &geom)
bool checkInformationCovered(const QgsFeatureRequest &featureRequest)
Checks if the information required to complete the request is cached.
void attributeValueChanged(const QgsFeatureId &fid, const int &field, const QVariant &value)
Is emitted when an attribute is changed.
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...
virtual long featureCount() const
Number of features in the layer.
Abstract base class for cache indices.
Definition: qgscacheindex.h:29
QgsAttributeList pendingAllAttributesList()
returns list of attributes
QgsVectorLayer * mLayer
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 cacheFeature(QgsFeature &feat)
void setCacheSize(int cacheSize)
Sets the maximum number of features to keep in the cache.
void requestCompleted(QgsFeatureRequest featureRequest, QgsFeatureIds fids)
Gets called, whenever the full list of feature ids for a certain request is known.
qint64 QgsFeatureId
Definition: qgsfeature.h:30
This is a wrapper class around a cached QgsFeature, which will inform the cache, when it has been del...
QgsVectorLayerCache(QgsVectorLayer *layer, int cacheSize, QObject *parent=NULL)
void featureRemoved(QgsFeatureId fid)
Gets called, whenever a feature has been removed.
int cacheSize()
Returns the maximum number of features this cache will hold.
void onAttributeValueChanged(QgsFeatureId fid, int field, const QVariant &value)
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.
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.
QgsFeatureRequest & setFlags(Flags flags)
Set flags that affect how features will be fetched.
void finished()
When filling the cache, this signal gets emitted once the cache is fully initialized.
friend class QgsCachedFeatureIterator
virtual void requestCompleted(QgsFeatureRequest featureRequest, QgsFeatureIds fids)
Implement this method to update the the indices, in case you need information contained by the reques...
void onFeatureAdded(QgsFeatureId fid)
void setCacheAddedAttributes(bool cacheAddedAttributes)
If this is enabled, the subset of cached attributes will automatically be extended to also include ne...
void attributeDeleted(int field)