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 : return mCache.remove( fid );
179 : : }
180 : :
181 : 0 : QgsVectorLayer *QgsVectorLayerCache::layer()
182 : : {
183 : 0 : return mLayer;
184 : : }
185 : :
186 : 0 : QgsCoordinateReferenceSystem QgsVectorLayerCache::sourceCrs() const
187 : : {
188 : 0 : return mLayer->crs();
189 : : }
190 : :
191 : 0 : QgsWkbTypes::Type QgsVectorLayerCache::wkbType() const
192 : : {
193 : 0 : return mLayer->wkbType();
194 : : }
195 : :
196 : 0 : QgsFields QgsVectorLayerCache::fields() const
197 : : {
198 : 0 : return mLayer->fields();
199 : : }
200 : :
201 : 0 : long QgsVectorLayerCache::featureCount() const
202 : : {
203 : 0 : return mLayer->featureCount();
204 : : }
205 : :
206 : 0 : void QgsVectorLayerCache::requestCompleted( const QgsFeatureRequest &featureRequest, const QgsFeatureIds &fids )
207 : : {
208 : : // If a request is too large for the cache don't notify to prevent from indexing incomplete requests
209 : 0 : if ( fids.count() <= mCache.size() )
210 : : {
211 : 0 : for ( const auto &idx : std::as_const( mCacheIndices ) )
212 : : {
213 : 0 : idx->requestCompleted( featureRequest, fids );
214 : : }
215 : 0 : if ( featureRequest.filterType() == QgsFeatureRequest::FilterNone &&
216 : 0 : ( featureRequest.filterRect().isNull() || featureRequest.filterRect().contains( mLayer->extent() ) ) )
217 : : {
218 : 0 : mFullCache = true;
219 : 0 : }
220 : 0 : }
221 : 0 : }
222 : :
223 : 0 : void QgsVectorLayerCache::featureRemoved( QgsFeatureId fid )
224 : : {
225 : 0 : const auto constMCacheIndices = mCacheIndices;
226 : 0 : for ( QgsAbstractCacheIndex *idx : constMCacheIndices )
227 : : {
228 : 0 : idx->flushFeature( fid );
229 : : }
230 : 0 : }
231 : :
232 : 0 : void QgsVectorLayerCache::onAttributeValueChanged( QgsFeatureId fid, int field, const QVariant &value )
233 : : {
234 : 0 : QgsCachedFeature *cachedFeat = mCache[ fid ];
235 : :
236 : 0 : if ( cachedFeat )
237 : : {
238 : 0 : cachedFeat->mFeature->setAttribute( field, value );
239 : 0 : }
240 : :
241 : 0 : emit attributeValueChanged( fid, field, value );
242 : 0 : }
243 : :
244 : 0 : void QgsVectorLayerCache::onJoinAttributeValueChanged( QgsFeatureId fid, int field, const QVariant &value )
245 : : {
246 : 0 : const QgsVectorLayer *joinLayer = qobject_cast<const QgsVectorLayer *>( sender() );
247 : :
248 : 0 : const auto constVectorJoins = mLayer->vectorJoins();
249 : 0 : for ( const QgsVectorLayerJoinInfo &info : constVectorJoins )
250 : : {
251 : 0 : if ( joinLayer == info.joinLayer() )
252 : : {
253 : 0 : const QgsFeature feature = mLayer->joinBuffer()->targetedFeatureOf( &info, joinLayer->getFeature( fid ) );
254 : :
255 : 0 : const QString fieldName = info.prefixedFieldName( joinLayer->fields().field( field ) );
256 : 0 : const int fieldIndex = mLayer->fields().indexFromName( fieldName );
257 : :
258 : 0 : if ( feature.isValid() && fieldIndex != -1 )
259 : : {
260 : 0 : onAttributeValueChanged( feature.id(), fieldIndex, value );
261 : 0 : return;
262 : : }
263 : 0 : }
264 : : }
265 : 0 : }
266 : :
267 : 0 : void QgsVectorLayerCache::featureDeleted( QgsFeatureId fid )
268 : : {
269 : 0 : mCache.remove( fid );
270 : 0 : }
271 : :
272 : 0 : void QgsVectorLayerCache::onFeatureAdded( QgsFeatureId fid )
273 : : {
274 : 0 : if ( mFullCache )
275 : : {
276 : 0 : if ( cacheSize() <= mLayer->featureCount() )
277 : : {
278 : 0 : setCacheSize( mLayer->featureCount() + 100 );
279 : 0 : }
280 : :
281 : 0 : QgsFeature feat;
282 : 0 : featureAtId( fid, feat );
283 : 0 : }
284 : 0 : emit featureAdded( fid );
285 : 0 : }
286 : :
287 : 0 : void QgsVectorLayerCache::attributeAdded( int field )
288 : : {
289 : : Q_UNUSED( field )
290 : 0 : mCachedAttributes.append( field );
291 : 0 : invalidate();
292 : 0 : }
293 : :
294 : 0 : void QgsVectorLayerCache::attributeDeleted( int field )
295 : : {
296 : 0 : QgsAttributeList attrs = mCachedAttributes;
297 : 0 : mCachedAttributes.clear();
298 : :
299 : 0 : const auto constAttrs = attrs;
300 : 0 : for ( int attr : constAttrs )
301 : : {
302 : 0 : if ( attr < field )
303 : 0 : mCachedAttributes << attr;
304 : 0 : else if ( attr > field )
305 : 0 : mCachedAttributes << attr - 1;
306 : : }
307 : 0 : }
308 : :
309 : 0 : void QgsVectorLayerCache::geometryChanged( QgsFeatureId fid, const QgsGeometry &geom )
310 : : {
311 : 0 : QgsCachedFeature *cachedFeat = mCache[ fid ];
312 : :
313 : 0 : if ( cachedFeat )
314 : : {
315 : 0 : cachedFeat->mFeature->setGeometry( geom );
316 : 0 : }
317 : 0 : }
318 : :
319 : 0 : void QgsVectorLayerCache::layerDeleted()
320 : : {
321 : 0 : emit cachedLayerDeleted();
322 : 0 : mLayer = nullptr;
323 : 0 : }
324 : :
325 : 0 : void QgsVectorLayerCache::invalidate()
326 : : {
327 : 0 : mCache.clear();
328 : 0 : mFullCache = false;
329 : 0 : emit invalidated();
330 : 0 : }
331 : :
332 : 0 : bool QgsVectorLayerCache::canUseCacheForRequest( const QgsFeatureRequest &featureRequest, QgsFeatureIterator &it )
333 : : {
334 : : // check first for available indices
335 : 0 : const auto constMCacheIndices = mCacheIndices;
336 : 0 : for ( QgsAbstractCacheIndex *idx : constMCacheIndices )
337 : : {
338 : 0 : if ( idx->getCacheIterator( it, featureRequest ) )
339 : : {
340 : 0 : return true;
341 : : }
342 : : }
343 : :
344 : : // no indexes available, but maybe we have already cached all required features anyway?
345 : 0 : switch ( featureRequest.filterType() )
346 : : {
347 : : case QgsFeatureRequest::FilterFid:
348 : : {
349 : 0 : if ( mCache.contains( featureRequest.filterFid() ) )
350 : : {
351 : 0 : it = QgsFeatureIterator( new QgsCachedFeatureIterator( this, featureRequest ) );
352 : 0 : return true;
353 : : }
354 : 0 : break;
355 : : }
356 : : case QgsFeatureRequest::FilterFids:
357 : : {
358 : 0 : if ( qgis::listToSet( mCache.keys() ).contains( featureRequest.filterFids() ) )
359 : : {
360 : 0 : it = QgsFeatureIterator( new QgsCachedFeatureIterator( this, featureRequest ) );
361 : 0 : return true;
362 : : }
363 : 0 : break;
364 : : }
365 : : case QgsFeatureRequest::FilterNone:
366 : : case QgsFeatureRequest::FilterExpression:
367 : : {
368 : 0 : if ( mFullCache )
369 : : {
370 : 0 : it = QgsFeatureIterator( new QgsCachedFeatureIterator( this, featureRequest ) );
371 : 0 : return true;
372 : : }
373 : 0 : break;
374 : : }
375 : :
376 : : }
377 : 0 : return false;
378 : 0 : }
379 : :
380 : 0 : QgsFeatureIterator QgsVectorLayerCache::getFeatures( const QgsFeatureRequest &featureRequest )
381 : : {
382 : 0 : QgsFeatureIterator it;
383 : 0 : bool requiresWriterIt = true; // If a not yet cached, but cacheable request is made, this stays true.
384 : :
385 : 0 : if ( checkInformationCovered( featureRequest ) )
386 : : {
387 : : // If we have a full cache available, run on this
388 : 0 : if ( mFullCache )
389 : : {
390 : 0 : it = QgsFeatureIterator( new QgsCachedFeatureIterator( this, featureRequest ) );
391 : 0 : requiresWriterIt = false;
392 : 0 : }
393 : : else
394 : : {
395 : : // may still be able to satisfy request using cache
396 : 0 : requiresWriterIt = !canUseCacheForRequest( featureRequest, it );
397 : : }
398 : 0 : }
399 : : else
400 : : {
401 : 0 : // Let the layer answer the request, so no caching of requests
402 : 0 : // we don't want to cache is done
403 : 0 : requiresWriterIt = false;
404 : 0 : it = mLayer->getFeatures( featureRequest );
405 : : }
406 : :
407 : 0 : if ( requiresWriterIt && mLayer->dataProvider() )
408 : : {
409 : : // No index was able to satisfy the request
410 : 0 : QgsFeatureRequest myRequest = QgsFeatureRequest( featureRequest );
411 : :
412 : : // Make sure if we cache the geometry, it gets fetched
413 : 0 : if ( mCacheGeometry && mLayer->isSpatial() )
414 : 0 : myRequest.setFlags( featureRequest.flags() & ~QgsFeatureRequest::NoGeometry );
415 : :
416 : : // Make sure, all the cached attributes are requested as well
417 : 0 : QSet<int> attrs = qgis::listToSet( featureRequest.subsetOfAttributes() ) + qgis::listToSet( mCachedAttributes );
418 : 0 : myRequest.setSubsetOfAttributes( qgis::setToList( attrs ) );
419 : :
420 : 0 : it = QgsFeatureIterator( new QgsCachedFeatureWriterIterator( this, myRequest ) );
421 : 0 : }
422 : :
423 : 0 : return it;
424 : 0 : }
425 : :
426 : 0 : bool QgsVectorLayerCache::isFidCached( const QgsFeatureId fid ) const
427 : : {
428 : 0 : return mCache.contains( fid );
429 : : }
430 : :
431 : 0 : bool QgsVectorLayerCache::checkInformationCovered( const QgsFeatureRequest &featureRequest )
432 : : {
433 : 0 : QgsAttributeList requestedAttributes;
434 : :
435 : 0 : if ( !featureRequest.flags().testFlag( QgsFeatureRequest::SubsetOfAttributes ) )
436 : : {
437 : 0 : requestedAttributes = mLayer->attributeList();
438 : 0 : }
439 : : else
440 : : {
441 : 0 : requestedAttributes = featureRequest.subsetOfAttributes();
442 : : }
443 : :
444 : : // Check if we even cache the information requested
445 : 0 : const auto constRequestedAttributes = requestedAttributes;
446 : 0 : for ( int attr : constRequestedAttributes )
447 : : {
448 : 0 : if ( !mCachedAttributes.contains( attr ) )
449 : : {
450 : 0 : return false;
451 : : }
452 : : }
453 : :
454 : : // If the request needs geometry but we don't cache this...
455 : 0 : return !( !featureRequest.flags().testFlag( QgsFeatureRequest::NoGeometry )
456 : 0 : && !mCacheGeometry );
457 : 0 : }
458 : :
459 : 0 : void QgsVectorLayerCache::connectJoinedLayers() const
460 : : {
461 : 0 : const auto constVectorJoins = mLayer->vectorJoins();
462 : 0 : for ( const QgsVectorLayerJoinInfo &info : constVectorJoins )
463 : : {
464 : 0 : const QgsVectorLayer *vl = info.joinLayer();
465 : 0 : if ( vl )
466 : 0 : connect( vl, &QgsVectorLayer::attributeValueChanged, this, &QgsVectorLayerCache::onJoinAttributeValueChanged );
467 : : }
468 : 0 : }
|