Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
[FEATURE]: Add possibility to show number of features in legend class…
…es. Accessible via right click legend menu

git-svn-id: http://svn.osgeo.org/qgis/trunk@14714 c8812cc2-4d05-0410-92ff-de0c093fc19c
  • Loading branch information
mhugent committed Nov 19, 2010
1 parent 2f0c705 commit a8c75f9
Show file tree
Hide file tree
Showing 10 changed files with 230 additions and 21 deletions.
3 changes: 3 additions & 0 deletions src/app/legend/qgslegend.cpp
Expand Up @@ -849,6 +849,7 @@ bool QgsLegend::writeXML( QList<QTreeWidgetItem *> items, QDomNode &node, QDomDo
{
QDomElement legendlayernode = document.createElement( "legendlayer" );
legendlayernode.setAttribute( "open", isItemExpanded( item ) ? "true" : "false" );

Qt::CheckState cstate = item->checkState( 0 );
if ( cstate == Qt::Checked )
{
Expand Down Expand Up @@ -900,6 +901,7 @@ bool QgsLegend::writeXML( QList<QTreeWidgetItem *> items, QDomNode &node, QDomDo
// to keep it compatible with older projects
QgsLegendLayer *ll = dynamic_cast<QgsLegendLayer *>( item );
QgsMapLayer* layer = ll->layer();
legendlayernode.setAttribute( "showFeatureCount", ll->showFeatureCount() );

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

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

// load layer's visibility and 'show in overview' flag
ll->setInOverview( atoi( fileElem.attribute( "isInOverview" ).toUtf8() ) );
Expand Down
192 changes: 185 additions & 7 deletions src/app/legend/qgslegendlayer.cpp
Expand Up @@ -27,6 +27,7 @@

#include "qgsapplication.h"
#include "qgsfield.h"
#include "qgsmapcanvasmap.h"
#include "qgsmaplayerregistry.h"
#include "qgsrasterlayer.h"
#include "qgsrenderer.h"
Expand All @@ -50,11 +51,12 @@
#include <QPainter>
#include <QSettings>
#include <QFileDialog>
#include <QProgressDialog>


QgsLegendLayer::QgsLegendLayer( QgsMapLayer* layer )
: QgsLegendItem( ),
mLyr( layer )
mLyr( layer ), mShowFeatureCount( false )
{
mType = LEGEND_LAYER;

Expand Down Expand Up @@ -91,6 +93,7 @@ QgsLegendLayer::QgsLegendLayer( QgsMapLayer* layer )
QgsDebugMsg( "Connecting signals for updating icons, layer " + layer->name() );
connect( layer, SIGNAL( editingStarted() ), this, SLOT( updateIcon() ) );
connect( layer, SIGNAL( editingStopped() ), this, SLOT( updateIcon() ) );
connect( layer, SIGNAL( layerModified( bool ) ), this, SLOT( updateAfterLayerModification( bool ) ) );
}
connect( layer, SIGNAL( layerNameChanged() ), this, SLOT( layerNameChanged() ) );

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



void QgsLegendLayer::vectorLayerSymbology( const QgsVectorLayer* layer, double widthScale )
void QgsLegendLayer::vectorLayerSymbology( QgsVectorLayer* layer, double widthScale )
{
if ( !layer )
{
Expand Down Expand Up @@ -219,6 +222,12 @@ void QgsLegendLayer::vectorLayerSymbology( const QgsVectorLayer* layer, double w
}
}

QMap< QgsSymbol*, int > featureCountMap;
if ( mShowFeatureCount )
{
updateItemListCount( layer, sym, featureCountMap );
}

for ( QList<QgsSymbol*>::const_iterator it = sym.begin(); it != sym.end(); ++it )
{
QImage img;
Expand Down Expand Up @@ -258,6 +267,15 @@ void QgsLegendLayer::vectorLayerSymbology( const QgsVectorLayer* layer, double w
values += label;
}

if ( mShowFeatureCount )
{
int fCount = featureCountMap[*it];
if ( fCount >= 0 )
{
values += ( " [" + QString::number( fCount ) + "]" );
}
}

QPixmap pix = QPixmap::fromImage( img ); // convert to pixmap
itemList.append( qMakePair( values, pix ) );
}
Expand All @@ -270,12 +288,11 @@ void QgsLegendLayer::vectorLayerSymbologyV2( QgsVectorLayer* layer )
{
QSize iconSize( 16, 16 );

#if 0 // unused
QSettings settings;
bool showClassifiers = settings.value( "/qgis/showLegendClassifiers", false ).toBool();
#endif

SymbologyList itemList = layer->rendererV2()->legendSymbologyItems( iconSize );
if ( mShowFeatureCount )
{
updateItemListCountV2( itemList, layer );
}

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

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

Expand Down Expand Up @@ -503,3 +526,158 @@ void QgsLegendLayer::layerNameChanged()
QString name = mLyr.layer()->name();
setText( 0, name );
}

void QgsLegendLayer::updateAfterLayerModification( bool onlyGeomChanged )
{
if ( onlyGeomChanged )
{
return;
}

double widthScale = 1.0;
QgsMapCanvas* canvas = QgisApp::instance()->mapCanvas();
if ( canvas && canvas->map() )
{
widthScale = canvas->map()->paintDevice().logicalDpiX() / 25.4;
}
refreshSymbology( mLyr.layer()->getLayerID(), widthScale );
}

void QgsLegendLayer::updateItemListCountV2( SymbologyList& itemList, QgsVectorLayer* layer )
{
if ( !layer )
{
return;
}

QgsFeatureRendererV2* renderer = layer->rendererV2();
if ( !renderer )
{
return;
}
QgsRenderContext dummyContext;
renderer->startRender( dummyContext, layer );

//create map holding the symbol count
QMap< QgsSymbolV2*, int > mSymbolCountMap;
QgsLegendSymbolList symbolList = renderer->legendSymbolItems();
QgsLegendSymbolList::const_iterator symbolIt = symbolList.constBegin();
for ( ; symbolIt != symbolList.constEnd(); ++symbolIt )
{
mSymbolCountMap.insert( symbolIt->second, 0 );
}

//go through all features and count the number of occurrences
int nFeatures = layer->pendingFeatureCount();
QProgressDialog p( tr( "Updating feature count for layer " ) + layer->name(), tr( "Abort" ), 0, nFeatures );
p.setWindowModality( Qt::WindowModal );
int featuresCounted = 0;


layer->select( layer->pendingAllAttributesList(), QgsRectangle(), false, false );
QgsFeature f;
QgsSymbolV2* currentSymbol = 0;

while ( layer->nextFeature( f ) )
{
currentSymbol = renderer->symbolForFeature( f );
mSymbolCountMap[currentSymbol] += 1;
++featuresCounted;
if ( featuresCounted % 50 == 0 )
{
if ( featuresCounted > nFeatures ) //sometimes the feature count is not correct
{
p.setMaximum( 0 );
}
p.setValue( featuresCounted );
if ( p.wasCanceled() )
{
return;
}
}
}
p.setValue( nFeatures );

QMap<QString, QPixmap> itemMap;
SymbologyList::const_iterator symbologyIt = itemList.constBegin();
for ( ; symbologyIt != itemList.constEnd(); ++ symbologyIt )
{
itemMap.insert( symbologyIt->first, symbologyIt->second );
}
itemList.clear();

//
symbolIt = symbolList.constBegin();
for ( ; symbolIt != symbolList.constEnd(); ++symbolIt )
{
QgsSymbolV2* debug = symbolIt->second;
itemList.push_back( qMakePair( symbolIt->first + " [" + QString::number( mSymbolCountMap[symbolIt->second] ) + "]", itemMap[symbolIt->first] ) );
}
}

void QgsLegendLayer::updateItemListCount( QgsVectorLayer* layer, const QList<QgsSymbol*>& sym, QMap< QgsSymbol*, int >& featureCountMap )
{
featureCountMap.clear();
QList<QgsSymbol*>::const_iterator symbolIt = sym.constBegin();
for ( ; symbolIt != sym.constEnd(); ++symbolIt )
{
featureCountMap.insert( *symbolIt, 0 );
}

QgsRenderer* renderer = const_cast<QgsRenderer*>( layer->renderer() );
if ( !renderer )
{
return;
}

//go through all features and count the number of occurrences
int nFeatures = layer->pendingFeatureCount();
QProgressDialog p( tr( "Updating feature count for layer " ) + layer->name(), tr( "Abort" ), 0, nFeatures );
p.setWindowModality( Qt::WindowModal );
int featuresCounted = 0;

layer->select( layer->pendingAllAttributesList(), QgsRectangle(), false, false );
QgsFeature f;
QgsSymbol* currentSymbol = 0;

while ( layer->nextFeature( f ) )
{
currentSymbol = renderer->symbolForFeature( &f );
if ( currentSymbol )
{
featureCountMap[currentSymbol] += 1;
}
++featuresCounted;

if ( featuresCounted % 50 == 0 )
{
if ( featuresCounted > nFeatures ) //sometimes the feature count is not correct
{
p.setMaximum( 0 );
}
p.setValue( featuresCounted );
if ( p.wasCanceled() ) //set all entries to -1 (= invalid)
{
QMap< QgsSymbol*, int >::iterator cIt = featureCountMap.begin();
for ( ; cIt != featureCountMap.end(); ++cIt )
{
cIt.value() = -1;
}
return;
}
}
}
p.setValue( nFeatures );
}

void QgsLegendLayer::setShowFeatureCount( bool show, bool update )
{
if ( show != mShowFeatureCount )
{
mShowFeatureCount = show;
if ( update )
{
updateAfterLayerModification( false );
}
}
}
17 changes: 16 additions & 1 deletion src/app/legend/qgslegendlayer.h
Expand Up @@ -29,6 +29,7 @@ class QgsLegendLayer;
class QgsLegendPropertyGroup;
class QgsMapLayer;
class QgsRasterLayer;
class QgsSymbol;
class QgsVectorLayer;

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

/**Update symbology (e.g. to update feature count in the legend after editing operations)*/
void updateAfterLayerModification( bool onlyGeomChanged );

void setShowFeatureCount( bool show, bool update = true );
bool showFeatureCount() const { return mShowFeatureCount; }

protected:

/** Prepare and change symbology for vector layer */
void vectorLayerSymbology( const QgsVectorLayer* mapLayer, double widthScale = 1.0 );
void vectorLayerSymbology( QgsVectorLayer* mapLayer, double widthScale = 1.0 );

void vectorLayerSymbologyV2( QgsVectorLayer* vlayer );

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

/**Adds feature counts to the symbology items (for symbology v2)*/
void updateItemListCountV2( SymbologyList& itemList, QgsVectorLayer* layer );
/**Calculates feature count for the individual symbols (old symbology)*/
void updateItemListCount( QgsVectorLayer* layer, const QList<QgsSymbol*>& sym, QMap< QgsSymbol*, int >& featureCountMap );

QPixmap getOriginalPixmap();

private:
Expand All @@ -113,6 +125,9 @@ class QgsLegendLayer : public QgsLegendItem

/** layer identified by its layer id */
QgsMapCanvasLayer mLyr;

/**True if number of features per legend class should is shown in the legend items*/
bool mShowFeatureCount;
};

#endif
6 changes: 2 additions & 4 deletions src/core/renderer/qgsgraduatedsymbolrenderer.h
Expand Up @@ -107,6 +107,8 @@ class CORE_EXPORT QgsGraduatedSymbolRenderer: public QgsRenderer
/**Returns a copy of the renderer (a deep copy on the heap)*/
QgsRenderer* clone() const;

QgsSymbol *symbolForFeature( const QgsFeature* f );

protected:
/** The graduation mode */
Mode mMode;
Expand All @@ -117,12 +119,8 @@ class CORE_EXPORT QgsGraduatedSymbolRenderer: public QgsRenderer
/**List holding the symbols for the individual classes*/
QList<QgsSymbol*> mSymbols;

QgsSymbol *symbolForFeature( const QgsFeature* f );

/**Cached copy of all underlying symbols required attribute fields*/
QgsAttributeList mSymbolAttributes;


};

inline void QgsGraduatedSymbolRenderer::addSymbol( QgsSymbol* sy )
Expand Down
4 changes: 4 additions & 0 deletions src/core/renderer/qgsrenderer.h
Expand Up @@ -112,6 +112,10 @@ class CORE_EXPORT QgsRenderer
This is a hint for QgsVectorLayer to not use the transparency setting on layer level in this cases*/
virtual bool usesTransparency() const {return false;}

/**Returns renderer symbol for a feature.
@note: this method was added in version 1.6*/
virtual QgsSymbol* symbolForFeature( const QgsFeature* f ) { return 0;}

/**Scales a brush to a given raster scale factor (e.g. for printing)*/
static void scaleBrush( QBrush& b, double rasterScaleFactor );

Expand Down
5 changes: 5 additions & 0 deletions src/core/renderer/qgssinglesymbolrenderer.h
Expand Up @@ -64,6 +64,11 @@ class CORE_EXPORT QgsSingleSymbolRenderer: public QgsRenderer
const QList<QgsSymbol*> symbols() const;
/**Returns a deep copy of this renderer*/
QgsRenderer* clone() const;

/**Returns renderer symbol for a feature
@note: this method was added in version 1.6*/
QgsSymbol* symbolForFeature( const QgsFeature* f ) { return mSymbol0; }

protected:
/**Object containing symbology information*/
QgsSymbol *mSymbol0;
Expand Down
7 changes: 5 additions & 2 deletions src/core/renderer/qgsuniquevaluerenderer.h
Expand Up @@ -67,13 +67,16 @@ class CORE_EXPORT QgsUniqueValueRenderer: public QgsRenderer
@note added in 1.4 */
const QMap<QString, QgsSymbol*> symbolMap() const { return mSymbols; }
QgsRenderer* clone() const;

/**Returns the symbol for a feature or 0 if there isn't any*/
QgsSymbol *symbolForFeature( const QgsFeature* f );

protected:
/**Field index used for classification*/
int mClassificationField;
/**Symbols for the unique values*/
QMap<QString, QgsSymbol*> mSymbols;
/**Returns the symbol for a feature or 0 if there isn't any*/
QgsSymbol *symbolForFeature( const QgsFeature* f );

/**Cached copy of all underlying symbols required attribute fields*/
QgsAttributeList mSymbolAttributes;
bool mSymbolAttributesDirty; // insertValue was called
Expand Down

0 comments on commit a8c75f9

Please sign in to comment.