From a8c75f92f7da6f50615fd0725c3eaf11fe3d9233 Mon Sep 17 00:00:00 2001 From: mhugent Date: Fri, 19 Nov 2010 13:24:20 +0000 Subject: [PATCH] [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 --- src/app/legend/qgslegend.cpp | 3 + src/app/legend/qgslegendlayer.cpp | 192 +++++++++++++++++- src/app/legend/qgslegendlayer.h | 17 +- .../renderer/qgsgraduatedsymbolrenderer.h | 6 +- src/core/renderer/qgsrenderer.h | 4 + src/core/renderer/qgssinglesymbolrenderer.h | 5 + src/core/renderer/qgsuniquevaluerenderer.h | 7 +- .../qgscategorizedsymbolrendererv2.cpp | 4 +- .../qgsgraduatedsymbolrendererv2.cpp | 10 +- .../qgssinglesymbolrendererv2.cpp | 3 +- 10 files changed, 230 insertions(+), 21 deletions(-) diff --git a/src/app/legend/qgslegend.cpp b/src/app/legend/qgslegend.cpp index 159a205206dd..68f4b0b0bffe 100644 --- a/src/app/legend/qgslegend.cpp +++ b/src/app/legend/qgslegend.cpp @@ -849,6 +849,7 @@ bool QgsLegend::writeXML( QList 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 ) { @@ -900,6 +901,7 @@ bool QgsLegend::writeXML( QList items, QDomNode &node, QDomDo // to keep it compatible with older projects QgsLegendLayer *ll = dynamic_cast( item ); QgsMapLayer* layer = ll->layer(); + legendlayernode.setAttribute( "showFeatureCount", ll->showFeatureCount() ); QDomElement layerfilegroupnode = document.createElement( "filegroup" ); layerfilegroupnode.setAttribute( "open", isItemExpanded( item ) ? "true" : "false" ); @@ -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() ) ); diff --git a/src/app/legend/qgslegendlayer.cpp b/src/app/legend/qgslegendlayer.cpp index 2432c20e45ad..2aecf4ca7ce6 100644 --- a/src/app/legend/qgslegendlayer.cpp +++ b/src/app/legend/qgslegendlayer.cpp @@ -27,6 +27,7 @@ #include "qgsapplication.h" #include "qgsfield.h" +#include "qgsmapcanvasmap.h" #include "qgsmaplayerregistry.h" #include "qgsrasterlayer.h" #include "qgsrenderer.h" @@ -50,11 +51,12 @@ #include #include #include +#include QgsLegendLayer::QgsLegendLayer( QgsMapLayer* layer ) : QgsLegendItem( ), - mLyr( layer ) + mLyr( layer ), mShowFeatureCount( false ) { mType = LEGEND_LAYER; @@ -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() ) ); @@ -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 ) { @@ -219,6 +222,12 @@ void QgsLegendLayer::vectorLayerSymbology( const QgsVectorLayer* layer, double w } } + QMap< QgsSymbol*, int > featureCountMap; + if ( mShowFeatureCount ) + { + updateItemListCount( layer, sym, featureCountMap ); + } + for ( QList::const_iterator it = sym.begin(); it != sym.end(); ++it ) { QImage img; @@ -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 ) ); } @@ -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 ); } @@ -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(); } @@ -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 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& sym, QMap< QgsSymbol*, int >& featureCountMap ) +{ + featureCountMap.clear(); + QList::const_iterator symbolIt = sym.constBegin(); + for ( ; symbolIt != sym.constEnd(); ++symbolIt ) + { + featureCountMap.insert( *symbolIt, 0 ); + } + + QgsRenderer* renderer = const_cast( 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 ); + } + } +} diff --git a/src/app/legend/qgslegendlayer.h b/src/app/legend/qgslegendlayer.h index c5a53a2ebcae..424473f08980 100644 --- a/src/app/legend/qgslegendlayer.h +++ b/src/app/legend/qgslegendlayer.h @@ -29,6 +29,7 @@ class QgsLegendLayer; class QgsLegendPropertyGroup; class QgsMapLayer; class QgsRasterLayer; +class QgsSymbol; class QgsVectorLayer; class QTreeWidget; @@ -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 ); @@ -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& sym, QMap< QgsSymbol*, int >& featureCountMap ); + QPixmap getOriginalPixmap(); private: @@ -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 diff --git a/src/core/renderer/qgsgraduatedsymbolrenderer.h b/src/core/renderer/qgsgraduatedsymbolrenderer.h index bb41c11d2eec..a1717f5aafae 100644 --- a/src/core/renderer/qgsgraduatedsymbolrenderer.h +++ b/src/core/renderer/qgsgraduatedsymbolrenderer.h @@ -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; @@ -117,12 +119,8 @@ class CORE_EXPORT QgsGraduatedSymbolRenderer: public QgsRenderer /**List holding the symbols for the individual classes*/ QList mSymbols; - QgsSymbol *symbolForFeature( const QgsFeature* f ); - /**Cached copy of all underlying symbols required attribute fields*/ QgsAttributeList mSymbolAttributes; - - }; inline void QgsGraduatedSymbolRenderer::addSymbol( QgsSymbol* sy ) diff --git a/src/core/renderer/qgsrenderer.h b/src/core/renderer/qgsrenderer.h index 41ff7fa6393c..22670fcfb748 100644 --- a/src/core/renderer/qgsrenderer.h +++ b/src/core/renderer/qgsrenderer.h @@ -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 ); diff --git a/src/core/renderer/qgssinglesymbolrenderer.h b/src/core/renderer/qgssinglesymbolrenderer.h index 246400b75ae3..0b01daf89b85 100644 --- a/src/core/renderer/qgssinglesymbolrenderer.h +++ b/src/core/renderer/qgssinglesymbolrenderer.h @@ -64,6 +64,11 @@ class CORE_EXPORT QgsSingleSymbolRenderer: public QgsRenderer const QList 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; diff --git a/src/core/renderer/qgsuniquevaluerenderer.h b/src/core/renderer/qgsuniquevaluerenderer.h index 9192e652826e..39829808bf3f 100644 --- a/src/core/renderer/qgsuniquevaluerenderer.h +++ b/src/core/renderer/qgsuniquevaluerenderer.h @@ -67,13 +67,16 @@ class CORE_EXPORT QgsUniqueValueRenderer: public QgsRenderer @note added in 1.4 */ const QMap 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 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 diff --git a/src/core/symbology-ng/qgscategorizedsymbolrendererv2.cpp b/src/core/symbology-ng/qgscategorizedsymbolrendererv2.cpp index 4e90eb873bfe..d46ef9ecaa1b 100644 --- a/src/core/symbology-ng/qgscategorizedsymbolrendererv2.cpp +++ b/src/core/symbology-ng/qgscategorizedsymbolrendererv2.cpp @@ -75,7 +75,9 @@ QgsCategorizedSymbolRendererV2::QgsCategorizedSymbolRendererV2( QString attrName mAttrName( attrName ), mCategories( categories ), mSourceSymbol( NULL ), - mSourceColorRamp( NULL ) + mSourceColorRamp( NULL ), + mRotationFieldIdx( -1 ), + mSizeScaleFieldIdx( -1 ) { for ( int i = 0; i < mCategories.count(); ++i ) { diff --git a/src/core/symbology-ng/qgsgraduatedsymbolrendererv2.cpp b/src/core/symbology-ng/qgsgraduatedsymbolrendererv2.cpp index 45b327ea82ec..58155fa63594 100644 --- a/src/core/symbology-ng/qgsgraduatedsymbolrendererv2.cpp +++ b/src/core/symbology-ng/qgsgraduatedsymbolrendererv2.cpp @@ -100,7 +100,9 @@ QgsGraduatedSymbolRendererV2::QgsGraduatedSymbolRendererV2( QString attrName, Qg mRanges( ranges ), mMode( Custom ), mSourceSymbol( NULL ), - mSourceColorRamp( NULL ) + mSourceColorRamp( NULL ), + mRotationFieldIdx( -1 ), + mSizeScaleFieldIdx( -1 ) { // TODO: check ranges for sanity (NULL symbols, invalid ranges) } @@ -421,13 +423,13 @@ static QList _calcPrettyBreaks( double minimum, double maximum, int clas double base = pow( 10.0, floor( log10( cell ) ) ); double unit = base; - if (( 2 * base ) - cell < h * ( cell - unit ) ) + if (( 2 * base ) - cell < h *( cell - unit ) ) { unit = 2.0 * base; - if (( 5 * base ) - cell < adjustBias * ( cell - unit ) ) + if (( 5 * base ) - cell < adjustBias *( cell - unit ) ) { unit = 5.0 * base; - if (( 10.0 * base ) - cell < h * ( cell - unit ) ) + if (( 10.0 * base ) - cell < h *( cell - unit ) ) { unit = 10.0 * base; } diff --git a/src/core/symbology-ng/qgssinglesymbolrendererv2.cpp b/src/core/symbology-ng/qgssinglesymbolrendererv2.cpp index f3b4b33b0c1a..dded791949ac 100644 --- a/src/core/symbology-ng/qgssinglesymbolrendererv2.cpp +++ b/src/core/symbology-ng/qgssinglesymbolrendererv2.cpp @@ -12,8 +12,7 @@ #include QgsSingleSymbolRendererV2::QgsSingleSymbolRendererV2( QgsSymbolV2* symbol ) - : QgsFeatureRendererV2( "singleSymbol" ) - , mTempSymbol( NULL ) + : QgsFeatureRendererV2( "singleSymbol" ), mRotationFieldIdx( -1 ), mSizeScaleFieldIdx( -1 ), mTempSymbol( NULL ) { Q_ASSERT( symbol ); mSymbol = symbol;