Branch data Line data Source code
1 : : /***************************************************************************
2 : : qgsvectorlayercache.cpp
3 : : Cache features of a vector layer
4 : : -------------------
5 : : begin : January 2013
6 : : copyright : (C) Matthias Kuhn
7 : : email : matthias at opengis 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"
20 : : #include "qgscachedfeatureiterator.h"
21 : : #include "qgsvectorlayerjoininfo.h"
22 : : #include "qgsvectorlayerjoinbuffer.h"
23 : : #include "qgsvectorlayer.h"
24 : :
25 : : #include <QElapsedTimer>
26 : :
27 : 0 : QgsVectorLayerCache::QgsVectorLayerCache( QgsVectorLayer *layer, int cacheSize, QObject *parent )
28 : 0 : : QObject( parent )
29 : 0 : , mLayer( layer )
30 : 0 : {
31 : 0 : mCache.setMaxCost( cacheSize );
32 : :
33 : 0 : connect( mLayer, &QgsVectorLayer::featureDeleted, this, &QgsVectorLayerCache::featureDeleted );
34 : 0 : connect( mLayer, &QgsVectorLayer::featureAdded, this, &QgsVectorLayerCache::onFeatureAdded );
35 : 0 : connect( mLayer, &QgsVectorLayer::destroyed, this, &QgsVectorLayerCache::layerDeleted );
36 : :
37 : 0 : setCacheGeometry( true );
38 : 0 : setCacheSubsetOfAttributes( mLayer->attributeList() );
39 : 0 : setCacheAddedAttributes( true );
40 : :
41 : 0 : connect( mLayer, &QgsVectorLayer::attributeDeleted, this, &QgsVectorLayerCache::attributeDeleted );
42 : 0 : connect( mLayer, &QgsVectorLayer::updatedFields, this, &QgsVectorLayerCache::invalidate );
43 : 0 : connect( mLayer, &QgsVectorLayer::dataChanged, this, &QgsVectorLayerCache::invalidate );
44 : 0 : connect( mLayer, &QgsVectorLayer::attributeValueChanged, this, &QgsVectorLayerCache::onAttributeValueChanged );
45 : :
46 : 0 : connectJoinedLayers();
47 : 0 : }
48 : :
49 : 0 : QgsVectorLayerCache::~QgsVectorLayerCache()
50 : 0 : {
51 : 0 : qDeleteAll( mCacheIndices );
52 : 0 : mCacheIndices.clear();
53 : 0 : }
54 : :
55 : 0 : void QgsVectorLayerCache::setCacheSize( int cacheSize )
56 : : {
57 : 0 : mCache.setMaxCost( cacheSize );
58 : 0 : }
59 : :
60 : 0 : int QgsVectorLayerCache::cacheSize()
61 : : {
62 : 0 : return mCache.maxCost();
63 : : }
64 : :
65 : 0 : void QgsVectorLayerCache::setCacheGeometry( bool cacheGeometry )
66 : : {
67 : 0 : bool shouldCacheGeometry = cacheGeometry && mLayer->isSpatial();
68 : 0 : bool mustInvalidate = shouldCacheGeometry && !mCacheGeometry; // going from no geometry -> geometry, so have to clear existing cache entries
69 : 0 : mCacheGeometry = shouldCacheGeometry;
70 : 0 : if ( cacheGeometry )
71 : : {
72 : 0 : connect( mLayer, &QgsVectorLayer::geometryChanged, this, &QgsVectorLayerCache::geometryChanged );
73 : 0 : }
74 : : else
75 : : {
76 : 0 : disconnect( mLayer, &QgsVectorLayer::geometryChanged, this, &QgsVectorLayerCache::geometryChanged );
77 : : }
78 : 0 : if ( mustInvalidate )
79 : : {
80 : 0 : invalidate();
81 : 0 : }
82 : 0 : }
83 : :
84 : 0 : void QgsVectorLayerCache::setCacheSubsetOfAttributes( const QgsAttributeList &attributes )
85 : : {
86 : 0 : mCachedAttributes = attributes;
87 : 0 : }
88 : :
89 : 0 : void QgsVectorLayerCache::setFullCache( bool fullCache )
90 : : {
91 : 0 : mFullCache = fullCache;
92 : :
93 : 0 : if ( mFullCache )
94 : : {
95 : : // Add a little more than necessary...
96 : 0 : setCacheSize( mLayer->featureCount() + 100 );
97 : :
98 : : // Initialize the cache...
99 : 0 : QgsFeatureIterator it( new QgsCachedFeatureWriterIterator( this, QgsFeatureRequest()
100 : 0 : .setSubsetOfAttributes( mCachedAttributes )
101 : 0 : .setFlags( mCacheGeometry ? QgsFeatureRequest::NoFlags : QgsFeatureRequest::NoGeometry ) ) );
102 : :
103 : 0 : int i = 0;
104 : :
105 : 0 : QElapsedTimer t;
106 : 0 : t.start();
107 : :
108 : 0 : QgsFeature f;
109 : 0 : while ( it.nextFeature( f ) )
110 : : {
111 : 0 : ++i;
112 : :
113 : 0 : if ( t.elapsed() > 1000 )
114 : : {
115 : 0 : bool cancel = false;
116 : 0 : emit progress( i, cancel );
117 : 0 : if ( cancel )
118 : 0 : break;
119 : :
120 : 0 : t.restart();
121 : 0 : }
122 : : }
123 : :
124 : 0 : it.close();
125 : :
126 : 0 : emit finished();
127 : 0 : }
128 : 0 : }
129 : :
130 : 0 : void QgsVectorLayerCache::addCacheIndex( QgsAbstractCacheIndex *cacheIndex )
131 : : {
132 : 0 : mCacheIndices.append( cacheIndex );
133 : 0 : }
134 : :
135 : 0 : void QgsVectorLayerCache::setCacheAddedAttributes( bool cacheAddedAttributes )
136 : : {
137 : 0 : if ( cacheAddedAttributes )
138 : : {
139 : 0 : connect( mLayer, &QgsVectorLayer::attributeAdded, this, &QgsVectorLayerCache::attributeAdded );
140 : 0 : }
141 : : else
142 : : {
143 : 0 : disconnect( mLayer, &QgsVectorLayer::attributeAdded, this, &QgsVectorLayerCache::attributeAdded );
144 : : }
145 : 0 : }
146 : :
147 : 0 : bool QgsVectorLayerCache::featureAtId( QgsFeatureId featureId, QgsFeature &feature, bool skipCache )
148 : : {
149 : 0 : bool featureFound = false;
150 : :
151 : 0 : QgsCachedFeature *cachedFeature = nullptr;
152 : :
153 : 0 : if ( !skipCache )
154 : : {
155 : 0 : cachedFeature = mCache[ featureId ];
156 : 0 : }
157 : :
158 : 0 : if ( cachedFeature )
159 : : {
160 : 0 : feature = QgsFeature( *cachedFeature->feature() );
161 : 0 : featureFound = true;
162 : 0 : }
163 : 0 : else if ( mLayer->getFeatures( QgsFeatureRequest()
164 : 0 : .setFilterFid( featureId )
165 : 0 : .setSubsetOfAttributes( mCachedAttributes )
166 : 0 : .setFlags( !mCacheGeometry ? QgsFeatureRequest::NoGeometry : QgsFeatureRequest::Flags() ) )
167 : 0 : .nextFeature( feature ) )
168 : : {
169 : 0 : cacheFeature( feature );
170 : 0 : featureFound = true;
171 : 0 : }
172 : :
173 : 0 : return featureFound;
174 : 0 : }
175 : :
176 : 0 : bool QgsVectorLayerCache::removeCachedFeature( QgsFeatureId fid )
177 : : {
178 : 0 : bool removed = mCache.remove( fid );
179 : 0 : if ( removed )
180 : 0 : mCacheOrderedKeys.removeOne( fid );
181 : 0 : return removed;
182 : : }
183 : :
184 : 0 : QgsVectorLayer *QgsVectorLayerCache::layer()
185 : : {
186 : 0 : return mLayer;
187 : : }
188 : :
189 : 0 : QgsCoordinateReferenceSystem QgsVectorLayerCache::sourceCrs() const
190 : : {
191 : 0 : return mLayer->crs();
192 : : }
193 : :
194 : 0 : QgsWkbTypes::Type QgsVectorLayerCache::wkbType() const
195 : : {
196 : 0 : return mLayer->wkbType();
197 : : }
198 : :
199 : 0 : QgsFields QgsVectorLayerCache::fields() const
200 : : {
201 : 0 : return mLayer->fields();
202 : : }
203 : :
204 : 0 : long QgsVectorLayerCache::featureCount() const
205 : : {
206 : 0 : return mLayer->featureCount();
207 : : }
208 : :
209 : 0 : void QgsVectorLayerCache::requestCompleted( const QgsFeatureRequest &featureRequest, const QgsFeatureIds &fids )
210 : : {
211 : : // If a request is too large for the cache don't notify to prevent from indexing incomplete requests
212 : 0 : if ( fids.count() <= mCache.size() )
213 : : {
214 : 0 : for ( const auto &idx : std::as_const( mCacheIndices ) )
215 : : {
216 : 0 : idx->requestCompleted( featureRequest, fids );
217 : : }
218 : 0 : if ( featureRequest.filterType() == QgsFeatureRequest::FilterNone &&
219 : 0 : ( featureRequest.filterRect().isNull() || featureRequest.filterRect().contains( mLayer->extent() ) ) )
220 : : {
221 : 0 : mFullCache = true;
222 : 0 : }
223 : 0 : }
224 : 0 : }
225 : :
226 : 0 : void QgsVectorLayerCache::featureRemoved( QgsFeatureId fid )
227 : : {
228 : 0 : const auto constMCacheIndices = mCacheIndices;
229 : 0 : for ( QgsAbstractCacheIndex *idx : constMCacheIndices )
230 : : {
231 : 0 : idx->flushFeature( fid );
232 : : }
233 : 0 : }
234 : :
235 : 0 : void QgsVectorLayerCache::onAttributeValueChanged( QgsFeatureId fid, int field, const QVariant &value )
236 : : {
237 : 0 : QgsCachedFeature *cachedFeat = mCache[ fid ];
238 : :
239 : 0 : if ( cachedFeat )
240 : : {
241 : 0 : cachedFeat->mFeature->setAttribute( field, value );
242 : 0 : }
243 : :
244 : 0 : emit attributeValueChanged( fid, field, value );
245 : 0 : }
246 : :
247 : 0 : void QgsVectorLayerCache::onJoinAttributeValueChanged( QgsFeatureId fid, int field, const QVariant &value )
248 : : {
249 : 0 : const QgsVectorLayer *joinLayer = qobject_cast<const QgsVectorLayer *>( sender() );
250 : :
251 : 0 : const auto constVectorJoins = mLayer->vectorJoins();
252 : 0 : for ( const QgsVectorLayerJoinInfo &info : constVectorJoins )
253 : : {
254 : 0 : if ( joinLayer == info.joinLayer() )
255 : : {
256 : 0 : const QgsFeature feature = mLayer->joinBuffer()->targetedFeatureOf( &info, joinLayer->getFeature( fid ) );
257 : :
258 : 0 : const QString fieldName = info.prefixedFieldName( joinLayer->fields().field( field ) );
259 : 0 : const int fieldIndex = mLayer->fields().indexFromName( fieldName );
260 : :
261 : 0 : if ( feature.isValid() && fieldIndex != -1 )
262 : : {
263 : 0 : onAttributeValueChanged( feature.id(), fieldIndex, value );
264 : 0 : return;
265 : : }
266 : 0 : }
267 : : }
268 : 0 : }
269 : :
270 : 0 : void QgsVectorLayerCache::featureDeleted( QgsFeatureId fid )
271 : : {
272 : 0 : mCache.remove( fid );
273 : 0 : mCacheOrderedKeys.removeOne( fid );
274 : 0 : }
275 : :
276 : 0 : void QgsVectorLayerCache::onFeatureAdded( QgsFeatureId fid )
277 : : {
278 : 0 : if ( mFullCache )
279 : : {
280 : 0 : if ( cacheSize() <= mLayer->featureCount() )
281 : : {
282 : 0 : setCacheSize( mLayer->featureCount() + 100 );
283 : 0 : }
284 : :
285 : 0 : QgsFeature feat;
286 : 0 : featureAtId( fid, feat );
287 : 0 : }
288 : 0 : emit featureAdded( fid );
289 : 0 : }
290 : :
291 : 0 : void QgsVectorLayerCache::attributeAdded( int field )
292 : : {
293 : : Q_UNUSED( field )
294 : 0 : mCachedAttributes.append( field );
295 : 0 : invalidate();
296 : 0 : }
297 : :
298 : 0 : void QgsVectorLayerCache::attributeDeleted( int field )
299 : : {
300 : 0 : QgsAttributeList attrs = mCachedAttributes;
301 : 0 : mCachedAttributes.clear();
302 : :
303 : 0 : const auto constAttrs = attrs;
304 : 0 : for ( int attr : constAttrs )
305 : : {
306 : 0 : if ( attr < field )
307 : 0 : mCachedAttributes << attr;
308 : 0 : else if ( attr > field )
309 : 0 : mCachedAttributes << attr - 1;
310 : : }
311 : 0 : }
312 : :
313 : 0 : void QgsVectorLayerCache::geometryChanged( QgsFeatureId fid, const QgsGeometry &geom )
314 : : {
315 : 0 : QgsCachedFeature *cachedFeat = mCache[ fid ];
316 : :
317 : 0 : if ( cachedFeat )
318 : : {
319 : 0 : cachedFeat->mFeature->setGeometry( geom );
320 : 0 : }
321 : 0 : }
322 : :
323 : 0 : void QgsVectorLayerCache::layerDeleted()
324 : : {
325 : 0 : emit cachedLayerDeleted();
326 : 0 : mLayer = nullptr;
327 : 0 : }
328 : :
329 : 0 : void QgsVectorLayerCache::invalidate()
330 : : {
331 : 0 : mCache.clear();
332 : 0 : mCacheOrderedKeys.clear();
333 : 0 : mFullCache = false;
334 : 0 : emit invalidated();
335 : 0 : }
336 : :
337 : 0 : bool QgsVectorLayerCache::canUseCacheForRequest( const QgsFeatureRequest &featureRequest, QgsFeatureIterator &it )
338 : : {
339 : : // check first for available indices
340 : 0 : const auto constMCacheIndices = mCacheIndices;
341 : 0 : for ( QgsAbstractCacheIndex *idx : constMCacheIndices )
342 : : {
343 : 0 : if ( idx->getCacheIterator( it, featureRequest ) )
344 : : {
345 : 0 : return true;
346 : : }
347 : : }
348 : :
349 : : // no indexes available, but maybe we have already cached all required features anyway?
350 : 0 : switch ( featureRequest.filterType() )
351 : : {
352 : : case QgsFeatureRequest::FilterFid:
353 : : {
354 : 0 : if ( mCache.contains( featureRequest.filterFid() ) )
355 : : {
356 : 0 : it = QgsFeatureIterator( new QgsCachedFeatureIterator( this, featureRequest ) );
357 : 0 : return true;
358 : : }
359 : 0 : break;
360 : : }
361 : : case QgsFeatureRequest::FilterFids:
362 : : {
363 : 0 : if ( qgis::listToSet( mCache.keys() ).contains( featureRequest.filterFids() ) )
364 : : {
365 : 0 : it = QgsFeatureIterator( new QgsCachedFeatureIterator( this, featureRequest ) );
366 : 0 : return true;
367 : : }
368 : 0 : break;
369 : : }
370 : : case QgsFeatureRequest::FilterNone:
371 : : case QgsFeatureRequest::FilterExpression:
372 : : {
373 : 0 : if ( mFullCache )
374 : : {
375 : 0 : it = QgsFeatureIterator( new QgsCachedFeatureIterator( this, featureRequest ) );
376 : 0 : return true;
377 : : }
378 : 0 : break;
379 : : }
380 : :
381 : : }
382 : 0 : return false;
383 : 0 : }
384 : :
385 : 0 : QgsFeatureIterator QgsVectorLayerCache::getFeatures( const QgsFeatureRequest &featureRequest )
386 : : {
387 : 0 : QgsFeatureIterator it;
388 : 0 : bool requiresWriterIt = true; // If a not yet cached, but cacheable request is made, this stays true.
389 : :
390 : 0 : if ( checkInformationCovered( featureRequest ) )
391 : : {
392 : : // If we have a full cache available, run on this
393 : 0 : if ( mFullCache )
394 : : {
395 : 0 : it = QgsFeatureIterator( new QgsCachedFeatureIterator( this, featureRequest ) );
396 : 0 : requiresWriterIt = false;
397 : 0 : }
398 : : else
399 : : {
400 : : // may still be able to satisfy request using cache
401 : 0 : requiresWriterIt = !canUseCacheForRequest( featureRequest, it );
402 : : }
403 : 0 : }
404 : 0 : else
405 : 0 : {
406 : : // Let the layer answer the request, so no caching of requests
407 : : // we don't want to cache is done
408 : 0 : requiresWriterIt = false;
409 : 0 : it = mLayer->getFeatures( featureRequest );
410 : : }
411 : :
412 : 0 : if ( requiresWriterIt && mLayer->dataProvider() )
413 : : {
414 : : // No index was able to satisfy the request
415 : 0 : QgsFeatureRequest myRequest = QgsFeatureRequest( featureRequest );
416 : :
417 : : // Make sure if we cache the geometry, it gets fetched
418 : 0 : if ( mCacheGeometry && mLayer->isSpatial() )
419 : 0 : myRequest.setFlags( featureRequest.flags() & ~QgsFeatureRequest::NoGeometry );
420 : :
421 : : // Make sure, all the cached attributes are requested as well
422 : 0 : QSet<int> attrs = qgis::listToSet( featureRequest.subsetOfAttributes() ) + qgis::listToSet( mCachedAttributes );
423 : 0 : myRequest.setSubsetOfAttributes( qgis::setToList( attrs ) );
424 : :
425 : 0 : it = QgsFeatureIterator( new QgsCachedFeatureWriterIterator( this, myRequest ) );
426 : 0 : }
427 : :
428 : 0 : return it;
429 : 0 : }
430 : :
431 : 0 : bool QgsVectorLayerCache::isFidCached( const QgsFeatureId fid ) const
432 : : {
433 : 0 : return mCache.contains( fid );
434 : : }
435 : :
436 : 0 : bool QgsVectorLayerCache::checkInformationCovered( const QgsFeatureRequest &featureRequest )
437 : : {
438 : 0 : QgsAttributeList requestedAttributes;
439 : :
440 : 0 : if ( !featureRequest.flags().testFlag( QgsFeatureRequest::SubsetOfAttributes ) )
441 : : {
442 : 0 : requestedAttributes = mLayer->attributeList();
443 : 0 : }
444 : : else
445 : : {
446 : 0 : requestedAttributes = featureRequest.subsetOfAttributes();
447 : : }
448 : :
449 : : // Check if we even cache the information requested
450 : 0 : const auto constRequestedAttributes = requestedAttributes;
451 : 0 : for ( int attr : constRequestedAttributes )
452 : : {
453 : 0 : if ( !mCachedAttributes.contains( attr ) )
454 : : {
455 : 0 : return false;
456 : : }
457 : : }
458 : :
459 : : // If the request needs geometry but we don't cache this...
460 : 0 : return !( !featureRequest.flags().testFlag( QgsFeatureRequest::NoGeometry )
461 : 0 : && !mCacheGeometry );
462 : 0 : }
463 : :
464 : 0 : void QgsVectorLayerCache::connectJoinedLayers() const
465 : : {
466 : 0 : const auto constVectorJoins = mLayer->vectorJoins();
467 : 0 : for ( const QgsVectorLayerJoinInfo &info : constVectorJoins )
468 : : {
469 : 0 : const QgsVectorLayer *vl = info.joinLayer();
470 : 0 : if ( vl )
471 : 0 : connect( vl, &QgsVectorLayer::attributeValueChanged, this, &QgsVectorLayerCache::onJoinAttributeValueChanged );
472 : : }
473 : 0 : }
|