Skip to content

Commit a8c75f9

Browse files
author
mhugent
committed
[FEATURE]: Add possibility to show number of features in legend classes. Accessible via right click legend menu
git-svn-id: http://svn.osgeo.org/qgis/trunk@14714 c8812cc2-4d05-0410-92ff-de0c093fc19c
1 parent 2f0c705 commit a8c75f9

10 files changed

+230
-21
lines changed

src/app/legend/qgslegend.cpp

+3
Original file line numberDiff line numberDiff line change
@@ -849,6 +849,7 @@ bool QgsLegend::writeXML( QList<QTreeWidgetItem *> items, QDomNode &node, QDomDo
849849
{
850850
QDomElement legendlayernode = document.createElement( "legendlayer" );
851851
legendlayernode.setAttribute( "open", isItemExpanded( item ) ? "true" : "false" );
852+
852853
Qt::CheckState cstate = item->checkState( 0 );
853854
if ( cstate == Qt::Checked )
854855
{
@@ -900,6 +901,7 @@ bool QgsLegend::writeXML( QList<QTreeWidgetItem *> items, QDomNode &node, QDomDo
900901
// to keep it compatible with older projects
901902
QgsLegendLayer *ll = dynamic_cast<QgsLegendLayer *>( item );
902903
QgsMapLayer* layer = ll->layer();
904+
legendlayernode.setAttribute( "showFeatureCount", ll->showFeatureCount() );
903905

904906
QDomElement layerfilegroupnode = document.createElement( "filegroup" );
905907
layerfilegroupnode.setAttribute( "open", isItemExpanded( item ) ? "true" : "false" );
@@ -1060,6 +1062,7 @@ QgsLegendLayer* QgsLegend::readLayerFromXML( QDomElement& childelem, bool& isOpe
10601062

10611063
// create the item
10621064
QgsLegendLayer* ll = new QgsLegendLayer( theMapLayer );
1065+
ll->setShowFeatureCount( childelem.attribute( "showFeatureCount", "0" ).toInt(), false );
10631066

10641067
// load layer's visibility and 'show in overview' flag
10651068
ll->setInOverview( atoi( fileElem.attribute( "isInOverview" ).toUtf8() ) );

src/app/legend/qgslegendlayer.cpp

+185-7
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727

2828
#include "qgsapplication.h"
2929
#include "qgsfield.h"
30+
#include "qgsmapcanvasmap.h"
3031
#include "qgsmaplayerregistry.h"
3132
#include "qgsrasterlayer.h"
3233
#include "qgsrenderer.h"
@@ -50,11 +51,12 @@
5051
#include <QPainter>
5152
#include <QSettings>
5253
#include <QFileDialog>
54+
#include <QProgressDialog>
5355

5456

5557
QgsLegendLayer::QgsLegendLayer( QgsMapLayer* layer )
5658
: QgsLegendItem( ),
57-
mLyr( layer )
59+
mLyr( layer ), mShowFeatureCount( false )
5860
{
5961
mType = LEGEND_LAYER;
6062

@@ -91,6 +93,7 @@ QgsLegendLayer::QgsLegendLayer( QgsMapLayer* layer )
9193
QgsDebugMsg( "Connecting signals for updating icons, layer " + layer->name() );
9294
connect( layer, SIGNAL( editingStarted() ), this, SLOT( updateIcon() ) );
9395
connect( layer, SIGNAL( editingStopped() ), this, SLOT( updateIcon() ) );
96+
connect( layer, SIGNAL( layerModified( bool ) ), this, SLOT( updateAfterLayerModification( bool ) ) );
9497
}
9598
connect( layer, SIGNAL( layerNameChanged() ), this, SLOT( layerNameChanged() ) );
9699

@@ -184,7 +187,7 @@ void QgsLegendLayer::changeSymbologySettings( const QgsMapLayer* theMapLayer,
184187

185188

186189

187-
void QgsLegendLayer::vectorLayerSymbology( const QgsVectorLayer* layer, double widthScale )
190+
void QgsLegendLayer::vectorLayerSymbology( QgsVectorLayer* layer, double widthScale )
188191
{
189192
if ( !layer )
190193
{
@@ -219,6 +222,12 @@ void QgsLegendLayer::vectorLayerSymbology( const QgsVectorLayer* layer, double w
219222
}
220223
}
221224

225+
QMap< QgsSymbol*, int > featureCountMap;
226+
if ( mShowFeatureCount )
227+
{
228+
updateItemListCount( layer, sym, featureCountMap );
229+
}
230+
222231
for ( QList<QgsSymbol*>::const_iterator it = sym.begin(); it != sym.end(); ++it )
223232
{
224233
QImage img;
@@ -258,6 +267,15 @@ void QgsLegendLayer::vectorLayerSymbology( const QgsVectorLayer* layer, double w
258267
values += label;
259268
}
260269

270+
if ( mShowFeatureCount )
271+
{
272+
int fCount = featureCountMap[*it];
273+
if ( fCount >= 0 )
274+
{
275+
values += ( " [" + QString::number( fCount ) + "]" );
276+
}
277+
}
278+
261279
QPixmap pix = QPixmap::fromImage( img ); // convert to pixmap
262280
itemList.append( qMakePair( values, pix ) );
263281
}
@@ -270,12 +288,11 @@ void QgsLegendLayer::vectorLayerSymbologyV2( QgsVectorLayer* layer )
270288
{
271289
QSize iconSize( 16, 16 );
272290

273-
#if 0 // unused
274-
QSettings settings;
275-
bool showClassifiers = settings.value( "/qgis/showLegendClassifiers", false ).toBool();
276-
#endif
277-
278291
SymbologyList itemList = layer->rendererV2()->legendSymbologyItems( iconSize );
292+
if ( mShowFeatureCount )
293+
{
294+
updateItemListCountV2( itemList, layer );
295+
}
279296

280297
changeSymbologySettings( layer, itemList );
281298
}
@@ -436,6 +453,12 @@ void QgsLegendLayer::addToPopupMenu( QMenu& theMenu )
436453
if ( !vlayer->isEditable() && vlayer->dataProvider()->supportsSubsetString() )
437454
theMenu.addAction( tr( "&Query..." ), QgisApp::instance(), SLOT( layerSubsetString() ) );
438455

456+
//show number of features in legend if requested
457+
QAction* showNFeaturesAction = new QAction( tr( "Show feature count" ), &theMenu );
458+
showNFeaturesAction->setCheckable( true );
459+
showNFeaturesAction->setChecked( mShowFeatureCount );
460+
QObject::connect( showNFeaturesAction, SIGNAL( toggled( bool ) ), this, SLOT( setShowFeatureCount( bool ) ) );
461+
theMenu.addAction( showNFeaturesAction );
439462
theMenu.addSeparator();
440463
}
441464

@@ -503,3 +526,158 @@ void QgsLegendLayer::layerNameChanged()
503526
QString name = mLyr.layer()->name();
504527
setText( 0, name );
505528
}
529+
530+
void QgsLegendLayer::updateAfterLayerModification( bool onlyGeomChanged )
531+
{
532+
if ( onlyGeomChanged )
533+
{
534+
return;
535+
}
536+
537+
double widthScale = 1.0;
538+
QgsMapCanvas* canvas = QgisApp::instance()->mapCanvas();
539+
if ( canvas && canvas->map() )
540+
{
541+
widthScale = canvas->map()->paintDevice().logicalDpiX() / 25.4;
542+
}
543+
refreshSymbology( mLyr.layer()->getLayerID(), widthScale );
544+
}
545+
546+
void QgsLegendLayer::updateItemListCountV2( SymbologyList& itemList, QgsVectorLayer* layer )
547+
{
548+
if ( !layer )
549+
{
550+
return;
551+
}
552+
553+
QgsFeatureRendererV2* renderer = layer->rendererV2();
554+
if ( !renderer )
555+
{
556+
return;
557+
}
558+
QgsRenderContext dummyContext;
559+
renderer->startRender( dummyContext, layer );
560+
561+
//create map holding the symbol count
562+
QMap< QgsSymbolV2*, int > mSymbolCountMap;
563+
QgsLegendSymbolList symbolList = renderer->legendSymbolItems();
564+
QgsLegendSymbolList::const_iterator symbolIt = symbolList.constBegin();
565+
for ( ; symbolIt != symbolList.constEnd(); ++symbolIt )
566+
{
567+
mSymbolCountMap.insert( symbolIt->second, 0 );
568+
}
569+
570+
//go through all features and count the number of occurrences
571+
int nFeatures = layer->pendingFeatureCount();
572+
QProgressDialog p( tr( "Updating feature count for layer " ) + layer->name(), tr( "Abort" ), 0, nFeatures );
573+
p.setWindowModality( Qt::WindowModal );
574+
int featuresCounted = 0;
575+
576+
577+
layer->select( layer->pendingAllAttributesList(), QgsRectangle(), false, false );
578+
QgsFeature f;
579+
QgsSymbolV2* currentSymbol = 0;
580+
581+
while ( layer->nextFeature( f ) )
582+
{
583+
currentSymbol = renderer->symbolForFeature( f );
584+
mSymbolCountMap[currentSymbol] += 1;
585+
++featuresCounted;
586+
if ( featuresCounted % 50 == 0 )
587+
{
588+
if ( featuresCounted > nFeatures ) //sometimes the feature count is not correct
589+
{
590+
p.setMaximum( 0 );
591+
}
592+
p.setValue( featuresCounted );
593+
if ( p.wasCanceled() )
594+
{
595+
return;
596+
}
597+
}
598+
}
599+
p.setValue( nFeatures );
600+
601+
QMap<QString, QPixmap> itemMap;
602+
SymbologyList::const_iterator symbologyIt = itemList.constBegin();
603+
for ( ; symbologyIt != itemList.constEnd(); ++ symbologyIt )
604+
{
605+
itemMap.insert( symbologyIt->first, symbologyIt->second );
606+
}
607+
itemList.clear();
608+
609+
//
610+
symbolIt = symbolList.constBegin();
611+
for ( ; symbolIt != symbolList.constEnd(); ++symbolIt )
612+
{
613+
QgsSymbolV2* debug = symbolIt->second;
614+
itemList.push_back( qMakePair( symbolIt->first + " [" + QString::number( mSymbolCountMap[symbolIt->second] ) + "]", itemMap[symbolIt->first] ) );
615+
}
616+
}
617+
618+
void QgsLegendLayer::updateItemListCount( QgsVectorLayer* layer, const QList<QgsSymbol*>& sym, QMap< QgsSymbol*, int >& featureCountMap )
619+
{
620+
featureCountMap.clear();
621+
QList<QgsSymbol*>::const_iterator symbolIt = sym.constBegin();
622+
for ( ; symbolIt != sym.constEnd(); ++symbolIt )
623+
{
624+
featureCountMap.insert( *symbolIt, 0 );
625+
}
626+
627+
QgsRenderer* renderer = const_cast<QgsRenderer*>( layer->renderer() );
628+
if ( !renderer )
629+
{
630+
return;
631+
}
632+
633+
//go through all features and count the number of occurrences
634+
int nFeatures = layer->pendingFeatureCount();
635+
QProgressDialog p( tr( "Updating feature count for layer " ) + layer->name(), tr( "Abort" ), 0, nFeatures );
636+
p.setWindowModality( Qt::WindowModal );
637+
int featuresCounted = 0;
638+
639+
layer->select( layer->pendingAllAttributesList(), QgsRectangle(), false, false );
640+
QgsFeature f;
641+
QgsSymbol* currentSymbol = 0;
642+
643+
while ( layer->nextFeature( f ) )
644+
{
645+
currentSymbol = renderer->symbolForFeature( &f );
646+
if ( currentSymbol )
647+
{
648+
featureCountMap[currentSymbol] += 1;
649+
}
650+
++featuresCounted;
651+
652+
if ( featuresCounted % 50 == 0 )
653+
{
654+
if ( featuresCounted > nFeatures ) //sometimes the feature count is not correct
655+
{
656+
p.setMaximum( 0 );
657+
}
658+
p.setValue( featuresCounted );
659+
if ( p.wasCanceled() ) //set all entries to -1 (= invalid)
660+
{
661+
QMap< QgsSymbol*, int >::iterator cIt = featureCountMap.begin();
662+
for ( ; cIt != featureCountMap.end(); ++cIt )
663+
{
664+
cIt.value() = -1;
665+
}
666+
return;
667+
}
668+
}
669+
}
670+
p.setValue( nFeatures );
671+
}
672+
673+
void QgsLegendLayer::setShowFeatureCount( bool show, bool update )
674+
{
675+
if ( show != mShowFeatureCount )
676+
{
677+
mShowFeatureCount = show;
678+
if ( update )
679+
{
680+
updateAfterLayerModification( false );
681+
}
682+
}
683+
}

src/app/legend/qgslegendlayer.h

+16-1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ class QgsLegendLayer;
2929
class QgsLegendPropertyGroup;
3030
class QgsMapLayer;
3131
class QgsRasterLayer;
32+
class QgsSymbol;
3233
class QgsVectorLayer;
3334

3435
class QTreeWidget;
@@ -87,10 +88,16 @@ class QgsLegendLayer : public QgsLegendItem
8788
/**Layer name has changed - set it also in legend*/
8889
void layerNameChanged();
8990

91+
/**Update symbology (e.g. to update feature count in the legend after editing operations)*/
92+
void updateAfterLayerModification( bool onlyGeomChanged );
93+
94+
void setShowFeatureCount( bool show, bool update = true );
95+
bool showFeatureCount() const { return mShowFeatureCount; }
96+
9097
protected:
9198

9299
/** Prepare and change symbology for vector layer */
93-
void vectorLayerSymbology( const QgsVectorLayer* mapLayer, double widthScale = 1.0 );
100+
void vectorLayerSymbology( QgsVectorLayer* mapLayer, double widthScale = 1.0 );
94101

95102
void vectorLayerSymbologyV2( QgsVectorLayer* vlayer );
96103

@@ -100,6 +107,11 @@ class QgsLegendLayer : public QgsLegendItem
100107
/** Removes the symbology items of a layer and adds new ones. */
101108
void changeSymbologySettings( const QgsMapLayer* mapLayer, const SymbologyList& newSymbologyItems );
102109

110+
/**Adds feature counts to the symbology items (for symbology v2)*/
111+
void updateItemListCountV2( SymbologyList& itemList, QgsVectorLayer* layer );
112+
/**Calculates feature count for the individual symbols (old symbology)*/
113+
void updateItemListCount( QgsVectorLayer* layer, const QList<QgsSymbol*>& sym, QMap< QgsSymbol*, int >& featureCountMap );
114+
103115
QPixmap getOriginalPixmap();
104116

105117
private:
@@ -113,6 +125,9 @@ class QgsLegendLayer : public QgsLegendItem
113125

114126
/** layer identified by its layer id */
115127
QgsMapCanvasLayer mLyr;
128+
129+
/**True if number of features per legend class should is shown in the legend items*/
130+
bool mShowFeatureCount;
116131
};
117132

118133
#endif

src/core/renderer/qgsgraduatedsymbolrenderer.h

+2-4
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,8 @@ class CORE_EXPORT QgsGraduatedSymbolRenderer: public QgsRenderer
107107
/**Returns a copy of the renderer (a deep copy on the heap)*/
108108
QgsRenderer* clone() const;
109109

110+
QgsSymbol *symbolForFeature( const QgsFeature* f );
111+
110112
protected:
111113
/** The graduation mode */
112114
Mode mMode;
@@ -117,12 +119,8 @@ class CORE_EXPORT QgsGraduatedSymbolRenderer: public QgsRenderer
117119
/**List holding the symbols for the individual classes*/
118120
QList<QgsSymbol*> mSymbols;
119121

120-
QgsSymbol *symbolForFeature( const QgsFeature* f );
121-
122122
/**Cached copy of all underlying symbols required attribute fields*/
123123
QgsAttributeList mSymbolAttributes;
124-
125-
126124
};
127125

128126
inline void QgsGraduatedSymbolRenderer::addSymbol( QgsSymbol* sy )

src/core/renderer/qgsrenderer.h

+4
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,10 @@ class CORE_EXPORT QgsRenderer
112112
This is a hint for QgsVectorLayer to not use the transparency setting on layer level in this cases*/
113113
virtual bool usesTransparency() const {return false;}
114114

115+
/**Returns renderer symbol for a feature.
116+
@note: this method was added in version 1.6*/
117+
virtual QgsSymbol* symbolForFeature( const QgsFeature* f ) { return 0;}
118+
115119
/**Scales a brush to a given raster scale factor (e.g. for printing)*/
116120
static void scaleBrush( QBrush& b, double rasterScaleFactor );
117121

src/core/renderer/qgssinglesymbolrenderer.h

+5
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,11 @@ class CORE_EXPORT QgsSingleSymbolRenderer: public QgsRenderer
6464
const QList<QgsSymbol*> symbols() const;
6565
/**Returns a deep copy of this renderer*/
6666
QgsRenderer* clone() const;
67+
68+
/**Returns renderer symbol for a feature
69+
@note: this method was added in version 1.6*/
70+
QgsSymbol* symbolForFeature( const QgsFeature* f ) { return mSymbol0; }
71+
6772
protected:
6873
/**Object containing symbology information*/
6974
QgsSymbol *mSymbol0;

src/core/renderer/qgsuniquevaluerenderer.h

+5-2
Original file line numberDiff line numberDiff line change
@@ -67,13 +67,16 @@ class CORE_EXPORT QgsUniqueValueRenderer: public QgsRenderer
6767
@note added in 1.4 */
6868
const QMap<QString, QgsSymbol*> symbolMap() const { return mSymbols; }
6969
QgsRenderer* clone() const;
70+
71+
/**Returns the symbol for a feature or 0 if there isn't any*/
72+
QgsSymbol *symbolForFeature( const QgsFeature* f );
73+
7074
protected:
7175
/**Field index used for classification*/
7276
int mClassificationField;
7377
/**Symbols for the unique values*/
7478
QMap<QString, QgsSymbol*> mSymbols;
75-
/**Returns the symbol for a feature or 0 if there isn't any*/
76-
QgsSymbol *symbolForFeature( const QgsFeature* f );
79+
7780
/**Cached copy of all underlying symbols required attribute fields*/
7881
QgsAttributeList mSymbolAttributes;
7982
bool mSymbolAttributesDirty; // insertValue was called

0 commit comments

Comments
 (0)