QGIS API Documentation  2.2.0-Valmiera
 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 }