QGIS API Documentation  2.8.2-Wien
 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 
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::NoFlags : QgsFeatureRequest::NoGeometry ) ) );
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 }