1 /***************************************************************************
2 qgsvectorlayerfeaturecounter.cpp
3 ---------------------
4 begin : May 2017
5 copyright : (C) 2017 by Matthias Kuhn
6 email : matthias at opengis dot ch
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15
16 #include "qgsvectorlayerfeaturecounter.h"
17 #include "qgsvectorlayer.h"
18 #include "qgsfeatureid.h"
19 #include "qgsfeedback.h"
20 #include "qgsrendercontext.h"
21
QgsVectorLayerFeatureCounter(QgsVectorLayer * layer,const QgsExpressionContext & context,bool storeSymbolFids)22 QgsVectorLayerFeatureCounter::QgsVectorLayerFeatureCounter( QgsVectorLayer *layer, const QgsExpressionContext &context, bool storeSymbolFids )
23 : QgsTask( tr( "Counting features in %1" ).arg( layer->name() ), QgsTask::CanCancel | QgsTask::CancelWithoutPrompt )
24 , mSource( new QgsVectorLayerFeatureSource( layer ) )
25 , mRenderer( layer->renderer()->clone() )
26 , mExpressionContext( context )
27 , mWithFids( storeSymbolFids )
28 , mFeatureCount( layer->featureCount() )
29 {
30 if ( !mExpressionContext.scopeCount() )
31 {
32 mExpressionContext = layer->createExpressionContext();
33 }
34 }
35
36 QgsVectorLayerFeatureCounter::~QgsVectorLayerFeatureCounter() = default;
37
run()38 bool QgsVectorLayerFeatureCounter::run()
39 {
40 mSymbolFeatureCountMap.clear();
41 mSymbolFeatureIdMap.clear();
42 const QgsLegendSymbolList symbolList = mRenderer->legendSymbolItems();
43 QgsLegendSymbolList::const_iterator symbolIt = symbolList.constBegin();
44
45 for ( ; symbolIt != symbolList.constEnd(); ++symbolIt )
46 {
47 mSymbolFeatureCountMap.insert( symbolIt->ruleKey(), 0 );
48 if ( mWithFids )
49 mSymbolFeatureIdMap.insert( symbolIt->ruleKey(), QgsFeatureIds() );
50 }
51
52 // If there are no features to be counted, we can spare us the trouble
53 if ( mFeatureCount > 0 )
54 {
55 mFeedback = std::make_unique< QgsFeedback >();
56
57 int featuresCounted = 0;
58
59 // Renderer (rule based) may depend on context scale, with scale is ignored if 0
60 QgsRenderContext renderContext;
61 renderContext.setRendererScale( 0 );
62 renderContext.setExpressionContext( mExpressionContext );
63
64 QgsFeatureRequest request;
65 if ( !mRenderer->filterNeedsGeometry() )
66 request.setFlags( QgsFeatureRequest::NoGeometry );
67 request.setSubsetOfAttributes( mRenderer->usedAttributes( renderContext ), mSource->fields() );
68
69 request.setFeedback( mFeedback.get() );
70 mExpressionContext.setFeedback( mFeedback.get() );
71
72 QgsFeatureIterator fit = mSource->getFeatures( request );
73
74 mRenderer->startRender( renderContext, mSource->fields() );
75
76 double progress = 0;
77 QgsFeature f;
78 while ( fit.nextFeature( f ) )
79 {
80 renderContext.expressionContext().setFeature( f );
81
82 const QSet<QString> featureKeyList = mRenderer->legendKeysForFeature( f, renderContext );
83 for ( const QString &key : featureKeyList )
84 {
85 mSymbolFeatureCountMap[key] += 1;
86 if ( mWithFids )
87 mSymbolFeatureIdMap[key].insert( f.id() );
88 }
89 ++featuresCounted;
90
91 const double p = ( static_cast< double >( featuresCounted ) / mFeatureCount ) * 100;
92 if ( p - progress > 1 )
93 {
94 progress = p;
95 setProgress( progress );
96 }
97
98 if ( isCanceled() )
99 {
100 mRenderer->stopRender( renderContext );
101 mExpressionContext.setFeedback( nullptr );
102 mFeedback.reset();
103 return false;
104 }
105 }
106 mRenderer->stopRender( renderContext );
107 mExpressionContext.setFeedback( nullptr );
108 mFeedback.reset();
109 }
110 setProgress( 100 );
111 emit symbolsCounted();
112 return true;
113 }
114
cancel()115 void QgsVectorLayerFeatureCounter::cancel()
116 {
117 if ( mFeedback )
118 mFeedback->cancel();
119 QgsTask::cancel();
120 }
121
symbolFeatureCountMap() const122 QHash<QString, long long> QgsVectorLayerFeatureCounter::symbolFeatureCountMap() const
123 {
124 return mSymbolFeatureCountMap;
125 }
126
featureCount(const QString & legendKey) const127 long long QgsVectorLayerFeatureCounter::featureCount( const QString &legendKey ) const
128 {
129 return mSymbolFeatureCountMap.value( legendKey, -1 );
130 }
131
symbolFeatureIdMap() const132 QHash<QString, QgsFeatureIds> QgsVectorLayerFeatureCounter::symbolFeatureIdMap() const
133 {
134 return mSymbolFeatureIdMap;
135 }
136
featureIds(const QString & symbolkey) const137 QgsFeatureIds QgsVectorLayerFeatureCounter::featureIds( const QString &symbolkey ) const
138 {
139 return mSymbolFeatureIdMap.value( symbolkey, QgsFeatureIds() );
140 }
141