QGIS API Documentation  2.7.0-Master
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules 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 
43 {
44  qDeleteAll( mCacheIndices );
45  mCacheIndices.clear();
46 }
47 
48 void QgsVectorLayerCache::setCacheSize( int cacheSize )
49 {
50  mCache.setMaxCost( cacheSize );
51 }
52 
54 {
55  return mCache.maxCost();
56 }
57 
58 void QgsVectorLayerCache::setCacheGeometry( bool cacheGeometry )
59 {
60  mCacheGeometry = cacheGeometry && mLayer->hasGeometryType();
61  if ( cacheGeometry )
62  {
63  connect( mLayer, SIGNAL( geometryChanged( QgsFeatureId, QgsGeometry& ) ), SLOT( geometryChanged( QgsFeatureId, QgsGeometry& ) ) );
64  }
65  else
66  {
67  disconnect( mLayer, SIGNAL( geometryChanged( QgsFeatureId, QgsGeometry& ) ), this, SLOT( geometryChanged( QgsFeatureId, QgsGeometry& ) ) );
68  }
69 }
70 
72 {
73  mCachedAttributes = attributes;
74 }
75 
76 void QgsVectorLayerCache::setFullCache( bool fullCache )
77 {
78  mFullCache = fullCache;
79 
80  if ( mFullCache )
81  {
82  // Add a little more than necessary...
83  setCacheSize( mLayer->featureCount() + 100 );
84 
85  // Initialize the cache...
87  .setSubsetOfAttributes( mCachedAttributes )
88  .setFlags( !mCacheGeometry ? QgsFeatureRequest::NoGeometry : QgsFeatureRequest::Flags( 0 ) ) ) );
89 
90  int i = 0;
91 
92  QTime t;
93  t.start();
94 
95  QgsFeature f;
96  while ( it.nextFeature( f ) )
97  {
98  ++i;
99 
100  if ( t.elapsed() > 1000 )
101  {
102  bool cancel = false;
103  emit progress( i, cancel );
104  if ( cancel )
105  break;
106 
107  t.restart();
108  }
109  }
110 
111  it.close();
112 
113  emit finished();
114  }
115 }
116 
118 {
119  mCacheIndices.append( cacheIndex );
120 }
121 
122 void QgsVectorLayerCache::setCacheAddedAttributes( bool cacheAddedAttributes )
123 {
124  if ( cacheAddedAttributes )
125  {
126  connect( mLayer, SIGNAL( attributeAdded( int ) ), SLOT( attributeAdded( int ) ) );
127  }
128  else
129  {
130  disconnect( mLayer, SIGNAL( attributeAdded( int ) ), this, SLOT( attributeAdded( int ) ) );
131  }
132 }
133 
134 bool QgsVectorLayerCache::featureAtId( QgsFeatureId featureId, QgsFeature& feature, bool skipCache )
135 {
136  bool featureFound = false;
137 
138  QgsCachedFeature* cachedFeature = NULL;
139 
140  if ( !skipCache )
141  {
142  cachedFeature = mCache[ featureId ];
143  }
144 
145  if ( cachedFeature != NULL )
146  {
147  feature = QgsFeature( *cachedFeature->feature() );
148  featureFound = true;
149  }
150  else if ( mLayer->getFeatures( QgsFeatureRequest()
151  .setFilterFid( featureId )
152  .setSubsetOfAttributes( mCachedAttributes )
153  .setFlags( !mCacheGeometry ? QgsFeatureRequest::NoGeometry : QgsFeatureRequest::Flags( 0 ) ) )
154  .nextFeature( feature ) )
155  {
156  cacheFeature( feature );
157  featureFound = true;
158  }
159 
160  return featureFound;
161 }
162 
164 {
165  return mCache.remove( fid );
166 }
167 
169 {
170  return mLayer;
171 }
172 
174 {
175  // If a request is too large for the cache don't notify to prevent from indexing incomplete requests
176  if ( fids.count() < mCache.size() )
177  {
178  foreach ( QgsAbstractCacheIndex* idx, mCacheIndices )
179  {
180  idx->requestCompleted( featureRequest, fids );
181  }
182  }
183 }
184 
186 {
187  Q_FOREACH ( QgsAbstractCacheIndex* idx, mCacheIndices )
188  {
189  idx->flushFeature( fid );
190  }
191 }
192 
193 void QgsVectorLayerCache::onAttributeValueChanged( QgsFeatureId fid, int field, const QVariant& value )
194 {
195  QgsCachedFeature* cachedFeat = mCache[ fid ];
196 
197  if ( NULL != cachedFeat )
198  {
199  cachedFeat->mFeature->setAttribute( field, value );
200  }
201 
202  emit attributeValueChanged( fid, field, value );
203 }
204 
205 void QgsVectorLayerCache::featureDeleted( QgsFeatureId fid )
206 {
207  mCache.remove( fid );
208 }
209 
210 void QgsVectorLayerCache::onFeatureAdded( QgsFeatureId fid )
211 {
212  if ( mFullCache )
213  {
214  if ( cacheSize() <= mLayer->featureCount() )
215  {
216  setCacheSize( mLayer->featureCount() + 100 );
217  }
218 
219  QgsFeature feat;
220  featureAtId( fid, feat );
221  }
222  emit( featureAdded( fid ) );
223 }
224 
225 void QgsVectorLayerCache::attributeAdded( int field )
226 {
227  Q_UNUSED( field )
228  mCachedAttributes.append( field );
229  mCache.clear();
230 }
231 
232 void QgsVectorLayerCache::attributeDeleted( int field )
233 {
234  foreach ( QgsFeatureId fid, mCache.keys() )
235  {
236  mCache[ fid ]->mFeature->deleteAttribute( field );
237  }
238 }
239 
240 void QgsVectorLayerCache::geometryChanged( QgsFeatureId fid, QgsGeometry& geom )
241 {
242  QgsCachedFeature* cachedFeat = mCache[ fid ];
243 
244  if ( cachedFeat != NULL )
245  {
246  cachedFeat->mFeature->setGeometry( geom );
247  }
248 }
249 
250 void QgsVectorLayerCache::layerDeleted()
251 {
252  emit cachedLayerDeleted();
253  mLayer = NULL;
254 }
255 
256 void QgsVectorLayerCache::updatedFields()
257 {
258  mCache.clear();
259 }
260 
262 {
264  bool requiresWriterIt = true; // If a not yet cached, but cachable request is made, this stays true.
265 
266  if ( checkInformationCovered( featureRequest ) )
267  {
268  // If we have a full cache available, run on this
269  if ( mFullCache )
270  {
271  it = QgsFeatureIterator( new QgsCachedFeatureIterator( this, featureRequest ) );
272  requiresWriterIt = false;
273  }
274  else
275  {
276  // Check if an index is able to deliver the requested features
277  foreach ( QgsAbstractCacheIndex *idx, mCacheIndices )
278  {
279  if ( idx->getCacheIterator( it, featureRequest ) )
280  {
281  requiresWriterIt = false;
282  break;
283  }
284  }
285  }
286  }
287  else
288  {
289  // Let the layer answer the request, so no caching of requests
290  // we don't want to cache is done
291  requiresWriterIt = false;
292  it = mLayer->getFeatures( featureRequest );
293  }
294 
295  if ( requiresWriterIt && mLayer->dataProvider() )
296  {
297  // No index was able to satisfy the request
298  QgsFeatureRequest myRequest = QgsFeatureRequest( featureRequest );
299 
300  // Make sure if we cache the geometry, it gets fetched
301  if ( mCacheGeometry && mLayer->hasGeometryType() )
302  myRequest.setFlags( featureRequest.flags() & ~QgsFeatureRequest::NoGeometry );
303 
304  // Make sure, all the cached attributes are requested as well
305  QSet<int> attrs = featureRequest.subsetOfAttributes().toSet() + mCachedAttributes.toSet();
306  myRequest.setSubsetOfAttributes( attrs.toList() );
307 
308  it = QgsFeatureIterator( new QgsCachedFeatureWriterIterator( this, myRequest ) );
309  }
310 
311  return it;
312 }
313 
315 {
316  return mCache.contains( fid );
317 }
318 
320 {
321  QgsAttributeList requestedAttributes;
322 
323  if ( !featureRequest.flags().testFlag( QgsFeatureRequest::SubsetOfAttributes ) )
324  {
325  requestedAttributes = mLayer->pendingAllAttributesList();
326  }
327  else
328  {
329  requestedAttributes = featureRequest.subsetOfAttributes();
330  }
331 
332  // Check if we even cache the information requested
333  foreach ( int attr, requestedAttributes )
334  {
335  if ( !mCachedAttributes.contains( attr ) )
336  {
337  return false;
338  }
339  }
340 
341  // If the request needs geometry but we don't cache this...
342  if ( !featureRequest.flags().testFlag( QgsFeatureRequest::NoGeometry )
343  && !mCacheGeometry )
344  {
345  return false;
346  }
347 
348  return true;
349 }
Wrapper for iterator of features from vector data provider or vector layer.
friend class QgsCachedFeatureWriterIterator
const Flags & flags() const
QSet< QgsFeatureId > QgsFeatureIds
Definition: qgsfeature.h:317
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
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:113
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 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
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.
This class caches features of a given QgsVectorLayer.
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
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.
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
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.
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 setCacheAddedAttributes(bool cacheAddedAttributes)
If this is enabled, the subset of cached attributes will automatically be extended to also include ne...