Skip to content
Permalink
Browse files

[FEATURE] Legend filtering based on map content (in main window, comp…

…oser, WMS)

There is new "filter" button in layers panel that toggles this functionality
and in composer legend widget.

Related feature is that layer tree now shows symbols in map units with correct size
(even when filtering is not enabled) so as the map view changes the legend node icons
are updated too (if they use map units).

GetLegendGraphics in WMS server
-------------------------------

This is an extension of standard GetLegendGraphics request according to MapServer RFC 101.
See the document for more details: http://mapserver.org/development/rfc/ms-rfc-101.html

In summary, clients need to add BBOX and CRS/SRS parameters to get appropriate legend based on the given map view.
Parameters WIDTH and HEIGHT are also taken into account as they specify map view image size for correct calculation
of size of legend symbols (if they are based on map units).

--

This software has been commissioned by Tuscany Region (Italy),
co-funded by the European Commission and developed under the project LIFE12 ENV/IT/001054 LIFE + IMAGINE.
The software has been realized by Gis3W s.a.s.

Questo software è stato commissionato da Regione Toscana (Italia),
cofinanziato dalla Commissione Europea e sviluppato nell'ambito del progetto LIFE12 ENV/IT/001054 LIFE + IMAGINE.
Il software è stato realizzato da Gis3W s.a.s.
  • Loading branch information
wonder-sk committed Sep 25, 2014
1 parent a111c85 commit e37a5ad8df3ebe4a2a6fee6aeb8f7cfc434d62e4
Showing with 893 additions and 124 deletions.
  1. +7 −0 python/core/composer/qgscomposerlegend.sip
  2. +8 −0 python/core/composer/qgscomposermap.sip
  3. +18 −1 python/core/layertree/qgslayertreemodel.sip
  4. +13 −3 python/core/layertree/qgslayertreemodellegendnode.sip
  5. +8 −7 python/core/qgsmaprenderer.sip
  6. +2 −0 python/core/symbology-ng/qgscategorizedsymbolrendererv2.sip
  7. +2 −0 python/core/symbology-ng/qgsgraduatedsymbolrendererv2.sip
  8. +4 −0 python/core/symbology-ng/qgsinvertedpolygonrenderer.sip
  9. +13 −0 python/core/symbology-ng/qgsrendererv2.sip
  10. +2 −0 python/core/symbology-ng/qgsrulebasedrendererv2.sip
  11. +2 −0 python/core/symbology-ng/qgssinglesymbolrendererv2.sip
  12. +2 −1 python/core/symbology-ng/qgssymbollayerv2utils.sip
  13. +20 −9 src/app/composer/qgscomposerlegendwidget.cpp
  14. +1 −0 src/app/composer/qgscomposerlegendwidget.h
  15. +50 −0 src/app/qgisapp.cpp
  16. +7 −0 src/app/qgisapp.h
  17. +2 −0 src/core/CMakeLists.txt
  18. +42 −0 src/core/composer/qgscomposerlegend.cpp
  19. +12 −0 src/core/composer/qgscomposerlegend.h
  20. +13 −7 src/core/composer/qgscomposermap.cpp
  21. +8 −2 src/core/composer/qgscomposermap.h
  22. +92 −11 src/core/layertree/qgslayertreemodel.cpp
  23. +27 −1 src/core/layertree/qgslayertreemodel.h
  24. +52 −13 src/core/layertree/qgslayertreemodellegendnode.cpp
  25. +15 −4 src/core/layertree/qgslayertreemodellegendnode.h
  26. +2 −2 src/core/qgslegendrenderer.cpp
  27. +72 −0 src/core/qgsmaphittest.cpp
  28. +39 −0 src/core/qgsmaphittest.h
  29. +8 −9 src/core/qgsmaprenderer.h
  30. +1 −1 src/core/qgsvectorlayer.cpp
  31. +35 −24 src/core/symbology-ng/qgscategorizedsymbolrendererv2.cpp
  32. +3 −1 src/core/symbology-ng/qgscategorizedsymbolrendererv2.h
  33. +22 −17 src/core/symbology-ng/qgsgraduatedsymbolrendererv2.cpp
  34. +2 −0 src/core/symbology-ng/qgsgraduatedsymbolrendererv2.h
  35. +14 −0 src/core/symbology-ng/qgsinvertedpolygonrenderer.cpp
  36. +4 −0 src/core/symbology-ng/qgsinvertedpolygonrenderer.h
  37. +8 −0 src/core/symbology-ng/qgsrendererv2.cpp
  38. +13 −0 src/core/symbology-ng/qgsrendererv2.h
  39. +5 −0 src/core/symbology-ng/qgsrulebasedrendererv2.cpp
  40. +2 −0 src/core/symbology-ng/qgsrulebasedrendererv2.h
  41. +6 −0 src/core/symbology-ng/qgssinglesymbolrendererv2.cpp
  42. +2 −0 src/core/symbology-ng/qgssinglesymbolrendererv2.h
  43. +4 −2 src/core/symbology-ng/qgssymbollayerv2utils.cpp
  44. +2 −1 src/core/symbology-ng/qgssymbollayerv2utils.h
  45. +2 −2 src/gui/layertree/qgslayertreeview.cpp
  46. +153 −2 src/mapserver/qgswmsserver.cpp
  47. +13 −2 src/mapserver/qgswmsserver.h
  48. +59 −2 src/ui/qgscomposerlegendwidgetbase.ui
@@ -58,6 +58,13 @@ class QgsComposerLegend : QgsComposerItem
//! @note added in 2.6
bool autoUpdateModel() const;

//! Set whether legend items should be filtered to show just the ones visible in the associated map
//! @note added in 2.6
void setLegendFilterByMapEnabled( bool enabled );
//! Find out whether legend items are filtered to show just the ones visible in the associated map
//! @note added in 2.6
bool legendFilterByMapEnabled() const;

//setters and getters
void setTitle( const QString& t );
QString title() const;
@@ -111,6 +111,10 @@ class QgsComposerMap : QgsComposerItem
/** \brief Create cache image */
void cache();

/** Return map settings that would be used for drawing of the map
* @note added in 2.6 */
QgsMapSettings mapSettings( const QgsRectangle& extent, const QSizeF& size, int dpi ) const;

/** \brief Get identification number*/
int id() const;

@@ -737,6 +741,10 @@ class QgsComposerMap : QgsComposerItem

void connectMapOverviewSignals() /Deprecated/;

/**Calculates the extent to request and the yShift of the top-left point in case of rotation.
* @note added in 2.6 */
void requestedExtent( QgsRectangle& extent ) const;

signals:
void extentChanged();

@@ -85,7 +85,8 @@ class QgsLayerTreeModel : QAbstractItemModel
//! Return legend node for given index. Returns null for invalid index
//! @note added in 2.6
static QgsLayerTreeModelLegendNode* index2legendNode( const QModelIndex& index );
//! Return index for a given legend node. If the legend node does not belong to the layer tree, the result is undefined
//! Return index for a given legend node. If the legend node does not belong to the layer tree, the result is undefined.
//! If the legend node is belongs to the tree but it is filtered out, invalid model index is returned.
//! @note added in 2.6
QModelIndex legendNode2index( QgsLayerTreeModelLegendNode* legendNode );

@@ -124,6 +125,22 @@ class QgsLayerTreeModel : QAbstractItemModel
void setLegendFilterByScale( double scaleDenominator );
double legendFilterByScale() const;

//! Force only display of legend nodes which are valid for given map settings.
//! Setting null pointer or invalid map settings will disable the functionality.
//! Ownership of map settings pointer does not change.
//! @note added in 2.6
void setLegendFilterByMap( const QgsMapSettings* settings );
const QgsMapSettings* legendFilterByMap() const;

//! Give the layer tree model hints about the currently associated map view
//! so that legend nodes that use map units can be scaled currectly
//! @note added in 2.6
void setLegendMapViewData( double mapUnitsPerPixel, int dpi, double scale );
//! Get hints about map view - to be used in legend nodes. Arguments that are not null will receive values.
//! If there are no valid map view data (from previous call to setLegendMapViewData()), returned values are zeros.
//! @note added in 2.6
void legendMapViewData( double *mapUnitsPerPixel /Out/, int *dpi /Out/, double *scale /Out/ );

//! Return true if index represents a legend node (instead of layer node)
//! @deprecated use index2legendNode()
bool isIndexSymbologyNode( const QModelIndex& index ) const /Deprecated/;
@@ -19,11 +19,15 @@ class QgsLayerTreeModelLegendNode : QObject

enum LegendNodeRoles
{
RuleKeyRole
RuleKeyRole, //!< rule key of the node (QString)
SymbolV2LegacyRuleKeyRole //!< for QgsSymbolV2LegendNode only - legacy rule key (void ptr, to be cast to QgsSymbolV2 ptr)
};

/** Return pointer to the parent layer node */
QgsLayerTreeLayer* parent() const;
QgsLayerTreeLayer* layerNode() const;

/** Return pointer to model owning this legend node */
QgsLayerTreeModel* model() const;

/** Return item flags associated with the item. Default implementation returns Qt::ItemIsEnabled. */
virtual Qt::ItemFlags flags() const;
@@ -42,6 +46,10 @@ class QgsLayerTreeModelLegendNode : QObject

virtual bool isScaleOK( double scale ) const;

/** Notification from model that information from associated map view has changed.
* Default implementation does nothing. */
virtual void invalidateMapBasedData();

struct ItemContext
{
//! Painter
@@ -109,7 +117,7 @@ class QgsSymbolV2LegendNode : QgsLayerTreeModelLegendNode
#include <qgslayertreemodellegendnode.h>
%End
public:
QgsSymbolV2LegendNode( QgsLayerTreeLayer* nodeLayer, const QgsLegendSymbolItemV2& item );
QgsSymbolV2LegendNode( QgsLayerTreeLayer* nodeLayer, const QgsLegendSymbolItemV2& item, QObject* parent /TransferThis/ = 0 );
~QgsSymbolV2LegendNode();

virtual Qt::ItemFlags flags() const;
@@ -123,6 +131,8 @@ class QgsSymbolV2LegendNode : QgsLayerTreeModelLegendNode
void setUserLabel( const QString& userLabel );

virtual bool isScaleOK( double scale ) const;

virtual void invalidateMapBasedData();
};


@@ -287,6 +287,14 @@ class QgsMapRenderer : QObject
//! @note added in 2.4
const QgsMapSettings& mapSettings();

/** Convenience function to project an extent into the layer source
* CRS, but also split it into two extents if it crosses
* the +/- 180 degree line. Modifies the given extent to be in the
* source CRS coordinates, and if it was split, returns true, and
* also sets the contents of the r2 parameter
*/
bool splitLayersExtent( QgsMapLayer* layer, QgsRectangle& extent /In,Out/, QgsRectangle& r2 /Out/ );

signals:

//! @deprecated in 2.4 - not emitted anymore
@@ -331,11 +339,4 @@ class QgsMapRenderer : QObject
//! adjust extent to fit the pixmap size
void adjustExtentToSize();

/** Convenience function to project an extent into the layer source
* CRS, but also split it into two extents if it crosses
* the +/- 180 degree line. Modifies the given extent to be in the
* source CRS coordinates, and if it was split, returns true, and
* also sets the contents of the r2 parameter
*/
bool splitLayersExtent( QgsMapLayer* layer, QgsRectangle& extent, QgsRectangle& r2 );
};
@@ -47,6 +47,8 @@ class QgsCategorizedSymbolRendererV2 : QgsFeatureRendererV2

virtual QgsSymbolV2* symbolForFeature( QgsFeature& feature );

virtual QgsSymbolV2* originalSymbolForFeature( QgsFeature& feature );

virtual void startRender( QgsRenderContext& context, const QgsFields& fields );

virtual void stopRender( QgsRenderContext& context );
@@ -51,6 +51,8 @@ class QgsGraduatedSymbolRendererV2 : QgsFeatureRendererV2

virtual QgsSymbolV2* symbolForFeature( QgsFeature& feature );

virtual QgsSymbolV2* originalSymbolForFeature( QgsFeature& feature );

virtual void startRender( QgsRenderContext& context, const QgsFields& fields );

virtual void stopRender( QgsRenderContext& context );
@@ -45,8 +45,12 @@ class QgsInvertedPolygonRenderer : QgsFeatureRendererV2
/** Proxy that will call this method on the embedded renderer. */
virtual QgsSymbolV2* symbolForFeature( QgsFeature& feature );
/** Proxy that will call this method on the embedded renderer. */
virtual QgsSymbolV2* originalSymbolForFeature( QgsFeature& feat );
/** Proxy that will call this method on the embedded renderer. */
virtual QgsSymbolV2List symbolsForFeature( QgsFeature& feat );
/** Proxy that will call this method on the embedded renderer. */
virtual QgsSymbolV2List originalSymbolsForFeature( QgsFeature& feat );
/** Proxy that will call this method on the embedded renderer. */
virtual QgsLegendSymbologyList legendSymbologyItems( QSize iconSize );
/** Proxy that will call this method on the embedded renderer.
@note not available in python bindings
@@ -60,6 +60,14 @@ class QgsFeatureRendererV2
*/
virtual QgsSymbolV2* symbolForFeature( QgsFeature& feature ) = 0;

/**
* Return symbol for feature. The difference compared to symbolForFeature() is that it returns original
* symbol which can be used as an identifier for renderer's rule - the former may return a temporary replacement
* of a symbol for use in rendering.
* @note added in 2.6
*/
virtual QgsSymbolV2* originalSymbolForFeature( QgsFeature& feature );

virtual void startRender( QgsRenderContext& context, const QgsFields& fields ) = 0;

//! @deprecated since 2.4 - not using QgsVectorLayer directly anymore
@@ -175,6 +183,11 @@ class QgsFeatureRendererV2
//! @note added in 1.9
virtual QgsSymbolV2List symbolsForFeature( QgsFeature& feat );

//! Equivalent of originalSymbolsForFeature() call
//! extended to support renderers that may use more symbols per feature - similar to symbolsForFeature()
//! @note added in 2.6
virtual QgsSymbolV2List originalSymbolsForFeature( QgsFeature& feat );

protected:
QgsFeatureRendererV2( QString type );

@@ -216,6 +216,8 @@ class QgsRuleBasedRendererV2 : QgsFeatureRendererV2
//! @note added in 1.9
virtual QgsSymbolV2List symbolsForFeature( QgsFeature& feat );

virtual QgsSymbolV2List originalSymbolsForFeature( QgsFeature& feat );

//! returns bitwise OR-ed capabilities of the renderer
//! \note added in 2.0
virtual int capabilities();
@@ -11,6 +11,8 @@ class QgsSingleSymbolRendererV2 : QgsFeatureRendererV2

virtual QgsSymbolV2* symbolForFeature( QgsFeature& feature );

virtual QgsSymbolV2* originalSymbolForFeature( QgsFeature& feature );

virtual void startRender( QgsRenderContext& context, const QgsFields& fields );

virtual void stopRender( QgsRenderContext& context );
@@ -68,7 +68,8 @@ class QgsSymbolLayerV2Utils

static void drawStippledBackround( QPainter* painter, QRect rect );

static QPixmap symbolPreviewPixmap( QgsSymbolV2* symbol, QSize size );
//! @note customContext parameter added in 2.6
static QPixmap symbolPreviewPixmap( QgsSymbolV2* symbol, QSize size, QgsRenderContext* customContext = 0 );
static QPixmap colorRampPreviewPixmap( QgsVectorColorRampV2* ramp, QSize size );

/**Returns the maximum estimated bleed for the symbol */
@@ -145,6 +145,7 @@ void QgsComposerLegendWidget::setGuiElements()
blockAllSignals( true );
mTitleLineEdit->setText( mLegend->title() );
mTitleAlignCombo->setCurrentIndex( alignment );
mFilterByMapToolButton->setChecked( mLegend->legendFilterByMapEnabled() );
mColumnCountSpinBox->setValue( mLegend->columnCount() );
mSplitLayerCheckBox->setChecked( mLegend->splitLayer() );
mEqualColumnWidthCheckBox->setChecked( mLegend->equalColumnWidth() );
@@ -531,8 +532,8 @@ void QgsComposerLegendWidget::on_mMoveDownToolButton_clicked()
}
else // legend node
{
_moveLegendNode( legendNode->parent(), index.row(), 1 );
mItemTreeView->layerTreeModel()->refreshLayerLegend( legendNode->parent() );
_moveLegendNode( legendNode->layerNode(), index.row(), 1 );
mItemTreeView->layerTreeModel()->refreshLayerLegend( legendNode->layerNode() );
}

mItemTreeView->setCurrentIndex( mItemTreeView->layerTreeModel()->index( index.row() + 1, 0, parentIndex ) );
@@ -568,8 +569,8 @@ void QgsComposerLegendWidget::on_mMoveUpToolButton_clicked()
}
else // legend node
{
_moveLegendNode( legendNode->parent(), index.row(), -1 );
mItemTreeView->layerTreeModel()->refreshLayerLegend( legendNode->parent() );
_moveLegendNode( legendNode->layerNode(), index.row(), -1 );
mItemTreeView->layerTreeModel()->refreshLayerLegend( legendNode->layerNode() );
}

mItemTreeView->setCurrentIndex( mItemTreeView->layerTreeModel()->index( index.row() - 1, 0, parentIndex ) );
@@ -689,7 +690,7 @@ void QgsComposerLegendWidget::on_mRemoveToolButton_clicked()
{
if ( QgsLayerTreeModelLegendNode* legendNode = mItemTreeView->layerTreeModel()->index2legendNode( index ) )
{
QgsLayerTreeLayer* nodeLayer = legendNode->parent();
QgsLayerTreeLayer* nodeLayer = legendNode->layerNode();
nodesWithRemoval[nodeLayer].append( index.row() );
}
}
@@ -773,10 +774,10 @@ void QgsComposerLegendWidget::on_mEditPushButton_clicked()
}
else if ( legendNode )
{
QList<int> order = QgsMapLayerLegendUtils::legendNodeOrder( legendNode->parent() );
QList<int> order = QgsMapLayerLegendUtils::legendNodeOrder( legendNode->layerNode() );
int originalIndex = ( idx.row() >= 0 && idx.row() < order.count() ? order[idx.row()] : -1 );
QgsMapLayerLegendUtils::setLegendNodeUserLabel( legendNode->parent(), originalIndex, newText );
model->refreshLayerLegend( legendNode->parent() );
QgsMapLayerLegendUtils::setLegendNodeUserLabel( legendNode->layerNode(), originalIndex, newText );
model->refreshLayerLegend( legendNode->layerNode() );
}

mLegend->adjustBoxSize();
@@ -806,7 +807,7 @@ void QgsComposerLegendWidget::resetLayerNodeToDefaults()
}
if ( QgsLayerTreeModelLegendNode* legendNode = mItemTreeView->layerTreeModel()->index2legendNode( currentIndex ) )
{
nodeLayer = legendNode->parent();
nodeLayer = legendNode->layerNode();
}

if ( !nodeLayer )
@@ -853,6 +854,15 @@ void QgsComposerLegendWidget::on_mCountToolButton_clicked( bool checked )
mLegend->endCommand();
}

void QgsComposerLegendWidget::on_mFilterByMapToolButton_clicked( bool checked )
{
mLegend->beginCommand( tr( "Legend updated" ) );
mLegend->setLegendFilterByMapEnabled( checked );
mLegend->update();
mLegend->adjustBoxSize();
mLegend->endCommand();
}

void QgsComposerLegendWidget::on_mUpdateAllPushButton_clicked()
{
updateLegend();
@@ -891,6 +901,7 @@ void QgsComposerLegendWidget::blockAllSignals( bool b )
mItemTreeView->blockSignals( b );
mCheckBoxAutoUpdate->blockSignals( b );
mMapComboBox->blockSignals( b );
mFilterByMapToolButton->blockSignals( b );
mColumnCountSpinBox->blockSignals( b );
mSplitLayerCheckBox->blockSignals( b );
mEqualColumnWidthCheckBox->blockSignals( b );
@@ -76,6 +76,7 @@ class QgsComposerLegendWidget: public QgsComposerItemBaseWidget, private Ui::Qgs
void on_mAddToolButton_clicked();
void on_mEditPushButton_clicked();
void on_mCountToolButton_clicked( bool checked );
void on_mFilterByMapToolButton_clicked( bool checked );
void resetLayerNodeToDefaults();
void on_mUpdateAllPushButton_clicked();
void on_mAddGroupToolButton_clicked();

2 comments on commit e37a5ad

@NathanW2

This comment has been minimized.

Copy link
Member

@NathanW2 NathanW2 replied Sep 25, 2014

oooooohhhh fancy!

@SrNetoChan

This comment has been minimized.

Copy link
Member

@SrNetoChan SrNetoChan replied Sep 25, 2014

Sweet!!! Again, it seems like if you guys can read my mind, and guess what my wishes are!

Please sign in to comment.
You can’t perform that action at this time.