1 /***************************************************************************
2                               qgspointdisplacementrenderer.cpp
3                               --------------------------------
4   begin                : January 26, 2010
5   copyright            : (C) 2010 by Marco Hugentobler
6   email                : marco at hugis dot net
7  ***************************************************************************/
8 
9 /***************************************************************************
10  *                                                                         *
11  *   This program is free software; you can redistribute it and/or modify  *
12  *   it under the terms of the GNU General Public License as published by  *
13  *   the Free Software Foundation; either version 2 of the License, or     *
14  *   (at your option) any later version.                                   *
15  *                                                                         *
16  ***************************************************************************/
17 
18 #ifndef QGSPOINTDISPLACEMENTRENDERER_H
19 #define QGSPOINTDISPLACEMENTRENDERER_H
20 
21 #include "qgis_core.h"
22 #include "qgis.h"
23 #include "qgspointdistancerenderer.h"
24 
25 /**
26  * \class QgsPointDisplacementRenderer
27  * \ingroup core
28  * \brief A renderer that automatically displaces points with the same geographic location.
29 */
30 class CORE_EXPORT QgsPointDisplacementRenderer: public QgsPointDistanceRenderer
31 {
32   public:
33 
34     /**
35      * Placement methods for dispersing points
36      */
37     enum Placement
38     {
39       Ring, //!< Place points in a single ring around group
40       ConcentricRings, //!< Place points in concentric rings around group
41       Grid //!< Place points in a grid around group
42     };
43 
44     /**
45      * Constructor for QgsPointDisplacementRenderer.
46      * \param labelAttributeName optional attribute name for labeling points
47      */
48     QgsPointDisplacementRenderer( const QString &labelAttributeName = QString() );
49 
50     QgsPointDisplacementRenderer *clone() const override SIP_FACTORY;
51     void startRender( QgsRenderContext &context, const QgsFields &fields ) override;
52     void stopRender( QgsRenderContext &context ) override;
53     QDomElement save( QDomDocument &doc, const QgsReadWriteContext &context ) override;
54     QSet<QString> usedAttributes( const QgsRenderContext &context ) const override;
55     bool accept( QgsStyleEntityVisitorInterface *visitor ) const override;
56 
57     //! Create a renderer from XML element
58     static QgsFeatureRenderer *create( QDomElement &symbologyElem, const QgsReadWriteContext &context ) SIP_FACTORY;
59 
60     /**
61      * Sets the line width for the displacement group circle.
62      * \param width line width in mm
63      * \see circleWidth()
64      * \see setCircleColor()
65      */
setCircleWidth(double width)66     void setCircleWidth( double width ) { mCircleWidth = width; }
67 
68     /**
69      * Returns the line width for the displacement group circle in mm.
70      * \see setCircleWidth()
71      * \see circleColor()
72      */
circleWidth()73     double circleWidth() const { return mCircleWidth; }
74 
75     /**
76      * Sets the color used for drawing the displacement group circle.
77      * \param color circle color
78      * \see circleColor()
79      * \see setCircleWidth()
80      */
setCircleColor(const QColor & color)81     void setCircleColor( const QColor &color ) { mCircleColor = color; }
82 
83     /**
84      * Returns the color used for drawing the displacement group circle.
85      * \see setCircleColor()
86      * \see circleWidth()
87      */
circleColor()88     QColor circleColor() const { return mCircleColor; }
89 
90     /**
91      * Sets a factor for increasing the ring size of displacement groups.
92      * \param distance addition factor
93      * \see circleRadiusAddition()
94      */
setCircleRadiusAddition(double distance)95     void setCircleRadiusAddition( double distance ) { mCircleRadiusAddition = distance; }
96 
97     /**
98      * Returns the factor for increasing the ring size of displacement groups.
99      * \see setCircleRadiusAddition()
100      */
circleRadiusAddition()101     double circleRadiusAddition() const { return mCircleRadiusAddition; }
102 
103     /**
104      * Sets a factor for increasing the label distances from the symbol.
105      * \param factor addition factor
106      * \see labelDistanceFactor()
107      * \since QGIS 3.8
108      */
setLabelDistanceFactor(double factor)109     void setLabelDistanceFactor( double factor ) { mLabelDistanceFactor = factor; }
110 
111     /**
112      * Returns the factor for label distance from the symbol.
113      * \see setLabelDistanceFactor()
114      * \since QGIS 3.8
115      */
labelDistanceFactor()116     double labelDistanceFactor() const { return mLabelDistanceFactor; }
117 
118     /**
119      * Returns the placement method used for dispersing the points.
120      * \see setPlacement()
121      * \since QGIS 2.12
122      */
placement()123     Placement placement() const { return mPlacement; }
124 
125     /**
126      * Sets the placement method used for dispersing the points.
127      * \param placement placement method
128      * \see placement()
129      * \since QGIS 2.12
130      */
setPlacement(Placement placement)131     void setPlacement( Placement placement ) { mPlacement = placement; }
132 
133     /**
134      * Returns the symbol for the center of a displacement group (but not ownership of the symbol).
135      * \see setCenterSymbol()
136     */
137     QgsMarkerSymbol *centerSymbol();
138 
139     /**
140      * Sets the center symbol for a displacement group.
141      * \param symbol new center symbol. Ownership is transferred to the renderer.
142      * \see centerSymbol()
143     */
144     void setCenterSymbol( QgsMarkerSymbol *symbol SIP_TRANSFER );
145 
146     /**
147      * Creates a QgsPointDisplacementRenderer from an existing renderer.
148      * \returns a new renderer if the conversion was possible, otherwise NULLPTR.
149      * \since QGIS 2.5
150      */
151     static QgsPointDisplacementRenderer *convertFromRenderer( const QgsFeatureRenderer *renderer ) SIP_FACTORY;
152 
153   private:
154 #ifdef SIP_RUN
155     QgsPointDisplacementRenderer( const QgsPointDisplacementRenderer & );
156     QgsPointDisplacementRenderer &operator=( const QgsPointDisplacementRenderer & );
157 #endif
158 
159     //! Center symbol for a displacement group
160     std::unique_ptr< QgsMarkerSymbol > mCenterSymbol;
161 
162     //! Displacement placement mode
163     Placement mPlacement = Ring;
164 
165     //! Line width for the circle
166     double mCircleWidth = 0.4;
167     //! Color to draw the circle
168     QColor mCircleColor;
169     //! Addition to the default circle radius
170     double mCircleRadiusAddition = 0;
171     //! Factor for label distance
172     double mLabelDistanceFactor = 0.5;
173 
174     void drawGroup( QPointF centerPoint, QgsRenderContext &context, const QgsPointDistanceRenderer::ClusteredGroup &group ) override SIP_FORCE;
175 
176     //helper functions
177     void calculateSymbolAndLabelPositions( QgsSymbolRenderContext &symbolContext, QPointF centerPoint, int nPosition, double symbolDiagonal, QList<QPointF> &symbolPositions, QList<QPointF> &labelShifts, double &circleRadius,
178                                            double &gridRadius, int &gridSize, QVector<double> &diagonals ) const;
179     void drawCircle( double radiusPainterUnits, QgsSymbolRenderContext &context, QPointF centerPoint, int nSymbols );
180     void drawSymbols( const ClusteredGroup &group, QgsRenderContext &context, const QList<QPointF> &symbolPositions );
181     void drawGrid( int gridSizeUnits, QgsSymbolRenderContext &context,
182                    QList<QPointF> pointSymbolPositions, int nSymbols );
183     void centralizeGrid( QList<QPointF> &pointSymbolPositions, double radius, int size ) const;
184 };
185 
186 #endif // QGSPOINTDISPLACEMENTRENDERER_H
187