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