Skip to content
Permalink
Browse files

[FEATURE] New "Merged feature" renderer for polygon and line layers

This renderer merges (or unions/dissolves) the line or polygon features
from a layer prior to rendering them. It's useful for rendering
a polygon layer with overlapping features as one single "coverage" feature,
or a line layer consisting of many smaller component line features
using a regularly spaced marker line or similar.

Internally, this just moves the guts of the existing inverted polygons
renderer to a new base class, as that renderer already had an option
to merge features prior to rendering. Basically it just exposes a new
renderer to users which is the inverted polygon renderer without
the inversion step!
  • Loading branch information
nyalldawson committed Jan 4, 2021
1 parent b8e025b commit 7fba697bf390c8f87c9f0711078c049c6bd79a60
Showing with 1,433 additions and 582 deletions.
  1. +1 −64 python/core/auto_generated/symbology/qgsinvertedpolygonrenderer.sip.in
  2. +147 −0 python/core/auto_generated/symbology/qgsmergedfeaturerenderer.sip.in
  3. +1 −0 python/core/core_auto.sip
  4. +58 −0 python/gui/auto_generated/symbology/qgsmergedfeaturerendererwidget.sip.in
  5. +1 −0 python/gui/gui_auto.sip
  6. +2 −0 src/core/CMakeLists.txt
  7. +2 −1 src/core/symbology/qgsgraduatedsymbolrenderer.cpp
  8. +11 −422 src/core/symbology/qgsinvertedpolygonrenderer.cpp
  9. +4 −91 src/core/symbology/qgsinvertedpolygonrenderer.h
  10. +576 −0 src/core/symbology/qgsmergedfeaturerenderer.cpp
  11. +176 −0 src/core/symbology/qgsmergedfeaturerenderer.h
  12. +8 −0 src/core/symbology/qgsrendererregistry.cpp
  13. +7 −4 src/core/symbology/qgsrulebasedrenderer.cpp
  14. +2 −0 src/gui/CMakeLists.txt
  15. +147 −0 src/gui/symbology/qgsmergedfeaturerendererwidget.cpp
  16. +69 −0 src/gui/symbology/qgsmergedfeaturerendererwidget.h
  17. +2 −0 src/gui/symbology/qgsrendererpropertiesdialog.cpp
  18. +38 −0 src/ui/qgsmergedfeaturerendererwidgetbase.ui
  19. +1 −0 tests/src/python/CMakeLists.txt
  20. +176 −0 tests/src/python/test_qgsmergedfeaturerenderer.py
  21. BIN ...featurerenderer/expected_lines_categorized_subrenderer/expected_lines_categorized_subrenderer.png
  22. BIN ...ges/mergedfeaturerenderer/expected_lines_single_subrenderer/expected_lines_single_subrenderer.png
  23. BIN ...s/mergedfeaturerenderer/expected_polys_categorizedrenderer/expected_polys_categorizedrenderer.png
  24. BIN .../control_images/mergedfeaturerenderer/expected_single_subrenderer/expected_single_subrenderer.png
  25. +1 −0 tests/testdata/lines_touching.cpg
  26. BIN tests/testdata/lines_touching.dbf
  27. +1 −0 tests/testdata/lines_touching.prj
  28. BIN tests/testdata/lines_touching.shp
  29. BIN tests/testdata/lines_touching.shx
  30. +1 −0 tests/testdata/polys_overlapping_with_cat.cpg
  31. BIN tests/testdata/polys_overlapping_with_cat.dbf
  32. +1 −0 tests/testdata/polys_overlapping_with_cat.prj
  33. BIN tests/testdata/polys_overlapping_with_cat.shp
  34. BIN tests/testdata/polys_overlapping_with_cat.shx
@@ -7,7 +7,7 @@
************************************************************************/


class QgsInvertedPolygonRenderer : QgsFeatureRenderer
class QgsInvertedPolygonRenderer : QgsMergedFeatureRenderer
{
%Docstring
QgsInvertedPolygonRenderer is a polygon-only feature renderer used to
@@ -42,56 +42,10 @@ Constructor

%Docstring
Direct copies are forbidden. Use :py:func:`~QgsInvertedPolygonRenderer.clone` instead.
%End
virtual void startRender( QgsRenderContext &context, const QgsFields &fields );


virtual bool renderFeature( const QgsFeature &feature, QgsRenderContext &context, int layer = -1, bool selected = false, bool drawVertexMarker = false ) throw( QgsCsException );

%Docstring
Renders a given feature.
This will here collect features. The actual rendering will be postponed to :py:func:`~QgsInvertedPolygonRenderer.stopRender`

:param feature: the feature to render
:param context: the rendering context
:param layer: the symbol layer to render, if that makes sense
:param selected: whether this feature has been selected (this will add decorations)
:param drawVertexMarker: whether this feature has vertex markers (in edit mode usually)

:return: ``True`` if the rendering was OK
%End

virtual void stopRender( QgsRenderContext &context );

%Docstring
The actual rendering will take place here.
Features collected during :py:func:`~QgsInvertedPolygonRenderer.renderFeature` are rendered using the embedded feature renderer
%End

virtual QString dump() const;

virtual QSet<QString> usedAttributes( const QgsRenderContext &context ) const;

virtual bool filterNeedsGeometry() const;

virtual QgsFeatureRenderer::Capabilities capabilities();

virtual QgsSymbolList symbols( QgsRenderContext &context ) const;

virtual QgsSymbol *symbolForFeature( const QgsFeature &feature, QgsRenderContext &context ) const;

virtual QgsSymbol *originalSymbolForFeature( const QgsFeature &feature, QgsRenderContext &context ) const;

virtual QgsSymbolList symbolsForFeature( const QgsFeature &feature, QgsRenderContext &context ) const;

virtual QgsSymbolList originalSymbolsForFeature( const QgsFeature &feature, QgsRenderContext &context ) const;

virtual QSet< QString > legendKeysForFeature( const QgsFeature &feature, QgsRenderContext &context ) const;

virtual QgsLegendSymbolList legendSymbolItems() const;

virtual bool willRenderFeature( const QgsFeature &feature, QgsRenderContext &context ) const;


static QgsFeatureRenderer *create( QDomElement &element, const QgsReadWriteContext &context ) /Factory/;
%Docstring
@@ -101,23 +55,6 @@ Creates a renderer out of an XML, for loading
virtual QDomElement save( QDomDocument &doc, const QgsReadWriteContext &context );


virtual void setEmbeddedRenderer( QgsFeatureRenderer *subRenderer /Transfer/ );

virtual const QgsFeatureRenderer *embeddedRenderer() const;


virtual void setLegendSymbolItem( const QString &key, QgsSymbol *symbol );


virtual bool legendSymbolItemsCheckable() const;

virtual bool legendSymbolItemChecked( const QString &key );

virtual void checkLegendSymbolItem( const QString &key, bool state = true );

virtual bool accept( QgsStyleEntityVisitorInterface *visitor ) const;


bool preprocessingEnabled() const;
%Docstring

@@ -0,0 +1,147 @@
/************************************************************************
* This file has been generated automatically from *
* *
* src/core/symbology/qgsmergedfeaturerenderer.h *
* *
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
************************************************************************/


class QgsMergedFeatureRenderer : QgsFeatureRenderer
{
%Docstring
QgsMergedFeatureRenderer is a polygon or line-only feature renderer used to
renderer a set of features merged (or dissolved) into a single geometry.

It is designed on top of another feature renderer, which is called "embedded"
Most of the methods are then only proxies to the embedded renderer. E.g. if
the embedded renderer is a categorized renderer, then all the matching features
for each categorized class will be dissolved together. Features from different
classes will NOT be dissolved together.

.. versionadded:: 3.18
%End

%TypeHeaderCode
#include "qgsmergedfeaturerenderer.h"
%End
public:

QgsMergedFeatureRenderer( QgsFeatureRenderer *embeddedRenderer /Transfer/ );
%Docstring
Constructor for QgsMergedFeatureRenderer.

:param embeddedRenderer: optional embeddedRenderer. Ownership will be transferred.
%End


static QgsFeatureRenderer *create( QDomElement &element, const QgsReadWriteContext &context ) /Factory/;
%Docstring
Creates a renderer out of an XML, for loading
%End

virtual QgsMergedFeatureRenderer *clone() const /Factory/;

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


virtual bool renderFeature( const QgsFeature &feature, QgsRenderContext &context, int layer = -1, bool selected = false, bool drawVertexMarker = false ) throw( QgsCsException );

%Docstring
Renders a given feature.
This will here collect features. The actual rendering will be postponed to :py:func:`~QgsMergedFeatureRenderer.stopRender`

:param feature: the feature to render
:param context: the rendering context
:param layer: the symbol layer to render, if that makes sense
:param selected: whether this feature has been selected (this will add decorations)
:param drawVertexMarker: whether this feature has vertex markers (in edit mode usually)

:return: ``True`` if the rendering was OK
%End

virtual void stopRender( QgsRenderContext &context );

%Docstring
The actual rendering will take place here.
Features collected during :py:func:`~QgsMergedFeatureRenderer.renderFeature` are rendered using the embedded feature renderer
%End

virtual QString dump() const;

virtual QSet<QString> usedAttributes( const QgsRenderContext &context ) const;

virtual bool filterNeedsGeometry() const;

virtual QgsFeatureRenderer::Capabilities capabilities();

virtual QgsSymbolList symbols( QgsRenderContext &context ) const;

virtual QgsSymbol *symbolForFeature( const QgsFeature &feature, QgsRenderContext &context ) const;

virtual QgsSymbol *originalSymbolForFeature( const QgsFeature &feature, QgsRenderContext &context ) const;

virtual QgsSymbolList symbolsForFeature( const QgsFeature &feature, QgsRenderContext &context ) const;

virtual QgsSymbolList originalSymbolsForFeature( const QgsFeature &feature, QgsRenderContext &context ) const;

virtual QSet< QString > legendKeysForFeature( const QgsFeature &feature, QgsRenderContext &context ) const;

virtual QgsLegendSymbolList legendSymbolItems() const;

virtual bool willRenderFeature( const QgsFeature &feature, QgsRenderContext &context ) const;

virtual QDomElement save( QDomDocument &doc, const QgsReadWriteContext &context );

virtual void setEmbeddedRenderer( QgsFeatureRenderer *subRenderer /Transfer/ );

virtual const QgsFeatureRenderer *embeddedRenderer() const;

virtual void setLegendSymbolItem( const QString &key, QgsSymbol *symbol );

virtual bool legendSymbolItemsCheckable() const;

virtual bool legendSymbolItemChecked( const QString &key );

virtual void checkLegendSymbolItem( const QString &key, bool state = true );

virtual bool accept( QgsStyleEntityVisitorInterface *visitor ) const;


static QgsMergedFeatureRenderer *convertFromRenderer( const QgsFeatureRenderer *renderer ) /Factory/;
%Docstring
Creates a QgsMergedFeatureRenderer by a conversion from an existing renderer.

:return: a new renderer if the conversion was possible, otherwise 0.

.. versionadded:: 2.5
%End

protected:

QgsMergedFeatureRenderer( const QString &type, QgsFeatureRenderer *embeddedRenderer /Transfer/ );
%Docstring
Constructor for QgsMergedFeatureRenderer.

:param embeddedRenderer: optional embeddedRenderer. Ownership will be transferred.
%End

enum GeometryOperation
{
Merge,
InvertOnly,
MergeAndInvert,
};



};


/************************************************************************
* This file has been generated automatically from *
* *
* src/core/symbology/qgsmergedfeaturerenderer.h *
* *
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
************************************************************************/
@@ -556,6 +556,7 @@
%Include auto_generated/symbology/qgslegendsymbolitem.sip
%Include auto_generated/symbology/qgslinesymbollayer.sip
%Include auto_generated/symbology/qgsmarkersymbollayer.sip
%Include auto_generated/symbology/qgsmergedfeaturerenderer.sip
%Include auto_generated/symbology/qgsnullsymbolrenderer.sip
%Include auto_generated/symbology/qgspointclusterrenderer.sip
%Include auto_generated/symbology/qgspointdisplacementrenderer.sip
@@ -0,0 +1,58 @@
/************************************************************************
* This file has been generated automatically from *
* *
* src/gui/symbology/qgsmergedfeaturerendererwidget.h *
* *
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
************************************************************************/



class QgsMergedFeatureRendererWidget : QgsRendererWidget
{
%Docstring
A widget used represent options of a QgsMergedFeatureRenderer

.. versionadded:: 3.18
%End

%TypeHeaderCode
#include "qgsmergedfeaturerendererwidget.h"
%End
public:

static QgsRendererWidget *create( QgsVectorLayer *layer, QgsStyle *style, QgsFeatureRenderer *renderer ) /Factory/;
%Docstring
Static creation method

:param layer: the layer where this renderer is applied
:param style:
:param renderer: the merged feature renderer (will not take ownership)
%End

QgsMergedFeatureRendererWidget( QgsVectorLayer *layer, QgsStyle *style, QgsFeatureRenderer *renderer );
%Docstring
Constructor

:param layer: the layer where this renderer is applied
:param style:
:param renderer: the merged feature renderer (will not take ownership)
%End

virtual QgsFeatureRenderer *renderer();

virtual void setContext( const QgsSymbolWidgetContext &context );

virtual void setDockMode( bool dockMode );


};


/************************************************************************
* This file has been generated automatically from *
* *
* src/gui/symbology/qgsmergedfeaturerendererwidget.h *
* *
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
************************************************************************/
@@ -393,6 +393,7 @@
%Include auto_generated/symbology/qgsheatmaprendererwidget.sip
%Include auto_generated/symbology/qgsinvertedpolygonrendererwidget.sip
%Include auto_generated/symbology/qgslayerpropertieswidget.sip
%Include auto_generated/symbology/qgsmergedfeaturerendererwidget.sip
%Include auto_generated/symbology/qgsnullsymbolrendererwidget.sip
%Include auto_generated/symbology/qgsmasksymbollayerwidget.sip
%Include auto_generated/symbology/qgspenstylecombobox.sip
@@ -56,6 +56,7 @@ set(QGIS_CORE_SRCS
symbology/qgslinesymbollayer.cpp
symbology/qgsmarkersymbollayer.cpp
symbology/qgsmasksymbollayer.cpp
symbology/qgsmergedfeaturerenderer.cpp
symbology/qgspainterswapper.cpp
symbology/qgsnullsymbolrenderer.cpp
symbology/qgspointclusterrenderer.cpp
@@ -1487,6 +1488,7 @@ set(QGIS_CORE_HDRS
symbology/qgslegendsymbolitem.h
symbology/qgslinesymbollayer.h
symbology/qgsmarkersymbollayer.h
symbology/qgsmergedfeaturerenderer.h
symbology/qgsnullsymbolrenderer.h
symbology/qgspointclusterrenderer.h
symbology/qgspointdisplacementrenderer.h
@@ -1250,7 +1250,8 @@ QgsGraduatedSymbolRenderer *QgsGraduatedSymbolRenderer::convertFromRenderer( con
if ( categorizedSymbolRenderer )
{
r = qgis::make_unique< QgsGraduatedSymbolRenderer >( QString(), QgsRangeList() );
r->setSourceSymbol( categorizedSymbolRenderer->sourceSymbol()->clone() );
if ( categorizedSymbolRenderer->sourceSymbol() )
r->setSourceSymbol( categorizedSymbolRenderer->sourceSymbol()->clone() );
if ( categorizedSymbolRenderer->sourceColorRamp() )
{
bool isRandom = dynamic_cast<const QgsRandomColorRamp *>( categorizedSymbolRenderer->sourceColorRamp() ) ||

0 comments on commit 7fba697

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