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