Skip to content

Commit

Permalink
[FEATURE] Point cluster renderer
Browse files Browse the repository at this point in the history
Groups nearby points into a single rendered marker symbol.

QgsPointDisplacementRenderer has been split into a new
pure virtual QgsPointDistanceRenderer base class which handles the
detection of clusters and grouping of points. The new cluster
renderer reuses this base class to avoid code duplication.

Additionally, some improvements have been made to the
displacement renderer, specifically:
- points are now assigned to the group which is "nearest" them,
rather then just assigning them first group within the search
distance. In some cases this was assigning features to a more
distant cluster, resulting in less predictable cluster patterns
- individual points are now correctly shown in their own
selection state

Lots of code cleanup + documentation too.

Sponsored by:
- Andreas Neumann
- Qtibia Engineering (Tudor Barascu)
- Karl-Magnus Jönsson
- Geonesia (Nicolas Ponzo)
- Plus numerous additional anonymous backers whose generous
contributions are also highly valued!
  • Loading branch information
nyalldawson committed Sep 29, 2016
1 parent 19585ee commit 36e276f
Show file tree
Hide file tree
Showing 25 changed files with 1,826 additions and 735 deletions.
6 changes: 6 additions & 0 deletions doc/api_break.dox
Expand Up @@ -1084,6 +1084,12 @@ be used instead of a null pointer if no transformation is required.</li>
<li>createMapRenderer(): default implementation (which called plugin's draw() method) has been removed. Plugin layers must implement createMapRenderer().</li>
</ul>

\subsection qgis_api_break_3_0_QgsPointDisplacementRenderer QgsPointDisplacementRenderer

<ul>
<li>The deprecated method setDisplacementGroups() has been removed. This method has had no effect since QGIS 2.4</li>
</ul>

\subsection qgis_api_break_3_0_QgsPointLocator QgsPointLocator

<ul>
Expand Down
2 changes: 2 additions & 0 deletions python/core/core.sip
Expand Up @@ -294,7 +294,9 @@
%Include symbology-ng/qgsinvertedpolygonrenderer.sip
%Include symbology-ng/qgslegendsymbolitem.sip
%Include symbology-ng/qgsnullsymbolrenderer.sip
%Include symbology-ng/qgspointclusterrenderer.sip
%Include symbology-ng/qgspointdisplacementrenderer.sip
%Include symbology-ng/qgspointdistancerenderer.sip
%Include symbology-ng/qgsrenderer.sip
%Include symbology-ng/qgsrendererregistry.sip
%Include symbology-ng/qgsrulebasedrenderer.sip
Expand Down
45 changes: 45 additions & 0 deletions python/core/symbology-ng/qgspointclusterrenderer.sip
@@ -0,0 +1,45 @@
/** \class QgsPointClusterRenderer
* \ingroup core
* A renderer that automatically clusters points with the same geographic position.
* \note added in QGIS 3.0
*/
class QgsPointClusterRenderer : QgsPointDistanceRenderer
{
%TypeHeaderCode
#include <qgspointclusterrenderer.h>
%End
public:

QgsPointClusterRenderer();

virtual QgsPointClusterRenderer* clone() const /Factory/;
virtual void startRender( QgsRenderContext& context, const QgsFields& fields );
void stopRender( QgsRenderContext& context );
QDomElement save( QDomDocument& doc );

//! Create a renderer from XML element
static QgsFeatureRenderer* create( QDomElement& symbologyElem ) /Factory/;

/** Returns the symbol used for rendering clustered groups (but not ownership of the symbol).
* @see setClusterSymbol()
*/
QgsMarkerSymbol* clusterSymbol();

/** Sets the symbol for rendering clustered groups.
* @param symbol new cluster symbol. Ownership is transferred to the renderer.
* @see clusterSymbol()
*/
void setClusterSymbol( QgsMarkerSymbol* symbol /Transfer/ );

/** Creates a QgsPointDisplacementRenderer from an existing renderer.
* @note added in 2.5
* @returns a new renderer if the conversion was possible, otherwise nullptr.
*/
static QgsPointClusterRenderer* convertFromRenderer( const QgsFeatureRenderer *renderer ) /Factory/;

private:
QgsPointClusterRenderer( const QgsPointClusterRenderer & );
QgsPointClusterRenderer & operator=( const QgsPointClusterRenderer & );

void drawGroup( QPointF centerPoint, QgsRenderContext& context, const QgsPointDistanceRenderer::ClusteredGroup& group );
};
173 changes: 55 additions & 118 deletions python/core/symbology-ng/qgspointdisplacementrenderer.sip
@@ -1,4 +1,8 @@
class QgsPointDisplacementRenderer : QgsFeatureRenderer
/** \class QgsPointDisplacementRenderer
* \ingroup core
* A renderer that automatically displaces points with the same geographic location.
*/
class QgsPointDisplacementRenderer : QgsPointDistanceRenderer
{
%TypeHeaderCode
#include <qgspointdisplacementrenderer.h>
Expand All @@ -13,88 +17,56 @@ class QgsPointDisplacementRenderer : QgsFeatureRenderer
ConcentricRings /*!< Place points in concentric rings around group*/
};

QgsPointDisplacementRenderer( const QString& labelAttributeName = "" );
~QgsPointDisplacementRenderer();

virtual QgsPointDisplacementRenderer* clone() const /Factory/;

virtual void toSld( QDomDocument& doc, QDomElement &element ) const;

/** Reimplemented from QgsFeatureRenderer*/
bool renderFeature( QgsFeature& feature, QgsRenderContext& context, int layer = -1, bool selected = false, bool drawVertexMarker = false );

/** Partial proxy that will call this method on the embedded renderer. */
virtual QList<QString> usedAttributes();
/** Proxy that will call this method on the embedded renderer. */
virtual QgsFeatureRenderer::Capabilities capabilities();
/** Proxy that will call this method on the embedded renderer.
@note available in python as symbols2
*/
virtual QgsSymbolList symbols( QgsRenderContext& context );
/** Proxy that will call this method on the embedded renderer.
@note available in python as symbolForFeature2
/** Constructor for QgsPointDisplacementRenderer.
* @param labelAttributeName optional attribute name for labeling points
*/
virtual QgsSymbol* symbolForFeature( QgsFeature& feature, QgsRenderContext& context );
/** Proxy that will call this method on the embedded renderer.
@note available in python as originalSymbolForFeature2
*/
virtual QgsSymbol* originalSymbolForFeature( QgsFeature& feat, QgsRenderContext& context );
/** Proxy that will call this method on the embedded renderer.
@note available in python as symbolsForFeature2
*/
virtual QgsSymbolList symbolsForFeature( QgsFeature& feat, QgsRenderContext& context );
/** Proxy that will call this method on the embedded renderer.
@note available in python as originalSymbolsForFeature2
*/
virtual QgsSymbolList originalSymbolsForFeature( QgsFeature& feat, QgsRenderContext& context );
/** Proxy that will call this method on the embedded renderer.
@note available in python as willRenderFeature2
*/
virtual bool willRenderFeature( QgsFeature& feat, QgsRenderContext& context );
QgsPointDisplacementRenderer( const QString& labelAttributeName = QString() );

virtual QgsPointDisplacementRenderer* clone() const /Factory/;
virtual void startRender( QgsRenderContext& context, const QgsFields& fields );

void stopRender( QgsRenderContext& context );

//! create a renderer from XML element
static QgsFeatureRenderer* create( QDomElement& symbologyElem ) /Factory/;
QDomElement save( QDomDocument& doc );

QgsLegendSymbologyList legendSymbologyItems( QSize iconSize );

//! @note not available in python bindings
// QgsLegendSymbolList legendSymbolItems();

void setLabelAttributeName( const QString& name );
QString labelAttributeName() const;

void setEmbeddedRenderer( QgsFeatureRenderer* r /Transfer/ );
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 );
//! Create a renderer from XML element
static QgsFeatureRenderer* create( QDomElement& symbologyElem ) /Factory/;

void setLabelFont( const QFont& f );
QFont labelFont() const;
/** Sets the line width for the displacement group circle.
* @param width line width in mm
* @see circleWidth()
* @see setCircleColor()
*/
void setCircleWidth( double width );

void setCircleWidth( double w );
/** Returns the line width for the displacement group circle in mm.
* @see setCircleWidth()
* @see circleColor()
*/
double circleWidth() const;

void setCircleColor( const QColor& c );
/** Sets the color used for drawing the displacement group circle.
* @param color circle color
* @see circleColor()
* @see setCircleWidth()
*/
void setCircleColor( const QColor& color );

/** Returns the color used for drawing the displacement group circle.
* @see setCircleColor()
* @see circleWidth()
*/
QColor circleColor() const;

void setLabelColor( const QColor& c );
QColor labelColor() const;
/** Sets a factor for increasing the ring size of displacement groups.
* @param distance addition factor
* @see circleRadiusAddition()
*/
void setCircleRadiusAddition( double distance );

void setCircleRadiusAddition( double d );
/** Returns the factor for increasing the ring size of displacement groups.
* @see setCircleRadiusAddition()
*/
double circleRadiusAddition() const;

void setMaxLabelScaleDenominator( double d );
double maxLabelScaleDenominator() const;

/** Returns the placement method used for dispersing the points.
* @see setPlacement()
* @note added in QGIS 2.12
Expand All @@ -108,62 +80,27 @@ class QgsPointDisplacementRenderer : QgsFeatureRenderer
*/
void setPlacement( Placement placement );

/** Returns the symbol for the center of a displacement group (but _not_ ownership of the symbol)*/
/** Returns the symbol for the center of a displacement group (but not ownership of the symbol).
* @see setCenterSymbol()
*/
QgsMarkerSymbol* centerSymbol();
/** Sets the center symbol (takes ownership)*/
void setCenterSymbol( QgsMarkerSymbol* symbol /Transfer/ );

/** Sets the tolerance distance for grouping points. Units are specified using
* setToleranceUnit().
* @param t tolerance distance
* @see tolerance()
* @see setToleranceUnit()
*/
void setTolerance( double t );

/** Returns the tolerance distance for grouping points. Units are retrieved using
* toleranceUnit().
* @see setTolerance()
* @see toleranceUnit()
*/
double tolerance() const;

/** Sets the units for the tolerance distance.
* @param unit tolerance distance units
* @see setTolerance()
* @see toleranceUnit()
* @note added in QGIS 2.12
*/
void setToleranceUnit( QgsUnitTypes::RenderUnit unit );

/** Returns the units for the tolerance distance.
* @see tolerance()
* @see setToleranceUnit()
* @note added in QGIS 2.12
*/
QgsUnitTypes::RenderUnit toleranceUnit() const;

/** Sets the map unit scale object for the distance tolerance. This is only used if the
* toleranceUnit() is set to QgsSymbol::MapUnit.
* @param scale scale for distance tolerance
* @see toleranceMapUnitScale()
* @see setToleranceUnit()
*/
void setToleranceMapUnitScale( const QgsMapUnitScale& scale );
/** Sets the center symbol for a displacement group.
* @param symbol new center symbol. Ownership is transferred to the renderer.
* @see centerSymbol()
*/
void setCenterSymbol( QgsMarkerSymbol* symbol /Transfer/ );

/** Returns the map unit scale object for the distance tolerance. This is only used if the
* toleranceUnit() is set to QgsSymbol::MapUnit.
* @see setToleranceMapUnitScale()
* @see toleranceUnit()
/** Creates a QgsPointDisplacementRenderer from an existing renderer.
* @note added in 2.5
* @returns a new renderer if the conversion was possible, otherwise nullptr.
*/
const QgsMapUnitScale& toleranceMapUnitScale() const;

//! creates a QgsPointDisplacementRenderer from an existing renderer.
//! @note added in 2.5
//! @returns a new renderer if the conversion was possible, otherwise 0.
static QgsPointDisplacementRenderer* convertFromRenderer(const QgsFeatureRenderer *renderer ) /Factory/;
static QgsPointDisplacementRenderer* convertFromRenderer( const QgsFeatureRenderer *renderer ) /Factory/;

private:
QgsPointDisplacementRenderer( const QgsPointDisplacementRenderer & );
QgsPointDisplacementRenderer & operator=( const QgsPointDisplacementRenderer & );

void drawGroup( QPointF centerPoint, QgsRenderContext& context, const QgsPointDistanceRenderer::ClusteredGroup& group );

};

0 comments on commit 36e276f

Please sign in to comment.