30 , mCircleColor( QColor( 125, 125, 125 ) )
68 diagonal = std::max( diagonal, M_SQRT2 * symbol->size( context ) );
74 QList<QPointF> symbolPositions;
75 QList<QPointF> labelPositions;
76 double circleRadius = -1.0;
77 double gridRadius = -1.0;
80 calculateSymbolAndLabelPositions( symbolContext, centerPoint, group.size(), diagonal, symbolPositions, labelPositions, circleRadius, gridRadius, gridSize );
83 if ( mCircleColor.isValid() && mCircleColor.alpha() > 0 )
86 if ( circleRadius > 0 )
87 drawCircle( circleRadius, symbolContext, centerPoint, group.size() );
90 drawGrid( gridSize, symbolContext, symbolPositions, group.size() );
93 if ( group.size() > 1 )
96 QgsFeature firstFeature = group.at( 0 ).feature;
99 mCenterSymbol->renderPoint( centerPoint, &firstFeature, context, -1,
false );
103 context.
painter()->drawRect( QRectF( centerPoint.x() - symbolContext.outputLineWidth( 1 ), centerPoint.y() - symbolContext.outputLineWidth( 1 ), symbolContext.outputLineWidth( 2 ), symbolContext.outputLineWidth( 2 ) ) );
108 drawSymbols( group, context, symbolPositions );
112 drawLabels( centerPoint, symbolContext, labelPositions, group );
121 mCenterSymbol->startRender( context, fields );
132 mCenterSymbol->stopRender( context );
143 labelFont.fromString( symbologyElem.attribute( QStringLiteral(
"labelFont" ), QString() ) );
146 r->
setPlacement( static_cast< Placement >( symbologyElem.attribute( QStringLiteral(
"placement" ), QStringLiteral(
"0" ) ).toInt() ) );
147 r->
setCircleWidth( symbologyElem.attribute( QStringLiteral(
"circleWidth" ), QStringLiteral(
"0.4" ) ).toDouble() );
150 r->
setCircleRadiusAddition( symbologyElem.attribute( QStringLiteral(
"circleRadiusAddition" ), QStringLiteral(
"0.0" ) ).toDouble() );
151 r->
setMinimumLabelScale( symbologyElem.attribute( QStringLiteral(
"maxLabelScaleDenominator" ), QStringLiteral(
"-1" ) ).toDouble() );
152 r->
setTolerance( symbologyElem.attribute( QStringLiteral(
"tolerance" ), QStringLiteral(
"0.00001" ) ).toDouble() );
157 QDomElement embeddedRendererElem = symbologyElem.firstChildElement( QStringLiteral(
"renderer-v2" ) );
158 if ( !embeddedRendererElem.isNull() )
164 QDomElement centerSymbolElem = symbologyElem.firstChildElement( QStringLiteral(
"symbol" ) );
165 if ( !centerSymbolElem.isNull() )
167 r->
setCenterSymbol( QgsSymbolLayerUtils::loadSymbol<QgsMarkerSymbol>( centerSymbolElem, context ) );
174 return mCenterSymbol.get();
180 rendererElement.setAttribute( QStringLiteral(
"forceraster" ), (
mForceRaster ? QStringLiteral(
"1" ) : QStringLiteral(
"0" ) ) );
181 rendererElement.setAttribute( QStringLiteral(
"type" ), QStringLiteral(
"pointDisplacement" ) );
182 rendererElement.setAttribute( QStringLiteral(
"labelAttributeName" ),
mLabelAttributeName );
184 rendererElement.setAttribute( QStringLiteral(
"circleWidth" ), QString::number( mCircleWidth ) );
187 rendererElement.setAttribute( QStringLiteral(
"circleRadiusAddition" ), QString::number( mCircleRadiusAddition ) );
188 rendererElement.setAttribute( QStringLiteral(
"placement" ), static_cast< int >( mPlacement ) );
189 rendererElement.setAttribute( QStringLiteral(
"maxLabelScaleDenominator" ), QString::number(
mMinLabelScale ) );
190 rendererElement.setAttribute( QStringLiteral(
"tolerance" ), QString::number(
mTolerance ) );
196 QDomElement embeddedRendererElem =
mRenderer->save( doc, context );
197 rendererElement.appendChild( embeddedRendererElem );
202 rendererElement.appendChild( centerSymbolElem );
210 QDomElement
orderBy = doc.createElement( QStringLiteral(
"orderby" ) );
212 rendererElement.appendChild( orderBy );
214 rendererElement.setAttribute( QStringLiteral(
"enableorderby" ), (
mOrderByEnabled ? QStringLiteral(
"1" ) : QStringLiteral(
"0" ) ) );
216 return rendererElement;
223 attr.unite( mCenterSymbol->usedAttributes( context ) );
229 mCenterSymbol.reset( symbol );
232 void QgsPointDisplacementRenderer::calculateSymbolAndLabelPositions(
QgsSymbolRenderContext &symbolContext, QPointF centerPoint,
int nPosition,
233 double symbolDiagonal, QList<QPointF> &symbolPositions, QList<QPointF> &labelShifts,
double &circleRadius,
double &gridRadius,
234 int &gridSize )
const 236 symbolPositions.clear();
243 else if ( nPosition == 1 )
245 symbolPositions.append( centerPoint );
246 labelShifts.append( QPointF( symbolDiagonal / 2.0, -symbolDiagonal / 2.0 ) );
250 double circleAdditionPainterUnits = symbolContext.
outputLineWidth( mCircleRadiusAddition );
252 switch ( mPlacement )
256 double minDiameterToFitSymbols = nPosition * symbolDiagonal / ( 2.0 * M_PI );
257 double radius = std::max( symbolDiagonal / 2, minDiameterToFitSymbols ) + circleAdditionPainterUnits;
259 double fullPerimeter = 2 * M_PI;
260 double angleStep = fullPerimeter / nPosition;
261 for (
double currentAngle = 0.0; currentAngle < fullPerimeter; currentAngle += angleStep )
263 double sinusCurrentAngle = std::sin( currentAngle );
264 double cosinusCurrentAngle = std::cos( currentAngle );
265 QPointF positionShift( radius * sinusCurrentAngle, radius * cosinusCurrentAngle );
266 QPointF labelShift( ( radius + symbolDiagonal / 2 ) * sinusCurrentAngle, ( radius + symbolDiagonal / 2 ) * cosinusCurrentAngle );
267 symbolPositions.append( centerPoint + positionShift );
268 labelShifts.append( labelShift );
271 circleRadius = radius;
276 double centerDiagonal = mCenterSymbol->size( symbolContext.
renderContext() ) * M_SQRT2;
278 int pointsRemaining = nPosition;
280 double firstRingRadius = centerDiagonal / 2.0 + symbolDiagonal / 2.0;
281 while ( pointsRemaining > 0 )
283 double radiusCurrentRing = std::max( firstRingRadius + ( ringNumber - 1 ) * symbolDiagonal + ringNumber * circleAdditionPainterUnits, 0.0 );
284 int maxPointsCurrentRing = std::max( std::floor( 2 * M_PI * radiusCurrentRing / symbolDiagonal ), 1.0 );
285 int actualPointsCurrentRing = std::min( maxPointsCurrentRing, pointsRemaining );
287 double angleStep = 2 * M_PI / actualPointsCurrentRing;
288 double currentAngle = 0.0;
289 for (
int i = 0; i < actualPointsCurrentRing; ++i )
291 double sinusCurrentAngle = std::sin( currentAngle );
292 double cosinusCurrentAngle = std::cos( currentAngle );
293 QPointF positionShift( radiusCurrentRing * sinusCurrentAngle, radiusCurrentRing * cosinusCurrentAngle );
294 QPointF labelShift( ( radiusCurrentRing + symbolDiagonal / 2 ) * sinusCurrentAngle, ( radiusCurrentRing + symbolDiagonal / 2 ) * cosinusCurrentAngle );
295 symbolPositions.append( centerPoint + positionShift );
296 labelShifts.append( labelShift );
297 currentAngle += angleStep;
300 pointsRemaining -= actualPointsCurrentRing;
302 circleRadius = radiusCurrentRing;
308 double centerDiagonal = mCenterSymbol->size( symbolContext.
renderContext() ) * M_SQRT2;
309 int pointsRemaining = nPosition;
310 gridSize = std::ceil( std::sqrt( pointsRemaining ) );
311 if ( pointsRemaining - std::pow( gridSize - 1, 2 ) < gridSize )
313 double originalPointRadius = ( ( centerDiagonal / 2.0 + symbolDiagonal / 2.0 ) + symbolDiagonal ) / 2;
314 double userPointRadius = originalPointRadius + circleAdditionPainterUnits;
317 while ( pointsRemaining > 0 )
319 for (
int xIndex = 0; xIndex < gridSize && pointsRemaining > 0; ++xIndex )
321 QPointF positionShift( userPointRadius * xIndex, userPointRadius * yIndex );
322 QPointF labelShift( ( userPointRadius + symbolDiagonal / 2 ) * xIndex, ( userPointRadius + symbolDiagonal / 2 ) * yIndex );
323 symbolPositions.append( centerPoint + positionShift );
324 labelShifts.append( labelShift );
330 centralizeGrid( symbolPositions, userPointRadius, gridSize );
331 centralizeGrid( labelShifts, userPointRadius, gridSize );
332 gridRadius = userPointRadius;
338 void QgsPointDisplacementRenderer::centralizeGrid( QList<QPointF> &pointSymbolPositions,
double radius,
int size )
const 340 double shiftAmount = -radius * ( size - 1.0 ) / 2.0;
341 QPointF centralShift( shiftAmount, shiftAmount );
342 for (
int i = 0; i < pointSymbolPositions.size(); ++i )
344 pointSymbolPositions[i] += centralShift;
349 QList<QPointF> pointSymbolPositions,
int nSymbols )
352 if ( nSymbols < 2 || !p )
357 QPen gridPen( mCircleColor );
359 p->setPen( gridPen );
361 for (
int i = 0; i < pointSymbolPositions.size(); ++i )
363 if ( i + 1 < pointSymbolPositions.size() && 0 != ( i + 1 ) % gridSizeUnits )
365 QLineF gridLineRow( pointSymbolPositions[i], pointSymbolPositions[i + 1] );
366 p->drawLine( gridLineRow );
369 if ( i + gridSizeUnits < pointSymbolPositions.size() )
371 QLineF gridLineColumn( pointSymbolPositions[i], pointSymbolPositions[i + gridSizeUnits] );
372 p->drawLine( gridLineColumn );
377 void QgsPointDisplacementRenderer::drawCircle(
double radiusPainterUnits,
QgsSymbolRenderContext &context, QPointF centerPoint,
int nSymbols )
380 if ( nSymbols < 2 || !p )
386 QPen circlePen( mCircleColor );
388 p->setPen( circlePen );
389 p->drawArc( QRectF( centerPoint.x() - radiusPainterUnits, centerPoint.y() - radiusPainterUnits, 2 * radiusPainterUnits, 2 * radiusPainterUnits ), 0, 5760 );
394 QList<QPointF>::const_iterator symbolPosIt = symbolPositions.constBegin();
395 ClusteredGroup::const_iterator groupIt = group.constBegin();
396 for ( ; symbolPosIt != symbolPositions.constEnd() && groupIt != group.constEnd();
397 ++symbolPosIt, ++groupIt )
400 groupIt->symbol()->startRender( context );
401 groupIt->symbol()->renderPoint( *symbolPosIt, &( groupIt->feature ), context, -1, groupIt->isSelected );
402 groupIt->symbol()->stopRender( context );
408 if ( renderer->
type() == QLatin1String(
"pointDisplacement" ) )
412 else if ( renderer->
type() == QLatin1String(
"singleSymbol" ) ||
413 renderer->
type() == QLatin1String(
"categorizedSymbol" ) ||
414 renderer->
type() == QLatin1String(
"graduatedSymbol" ) ||
415 renderer->
type() == QLatin1String(
"RuleRenderer" ) )
419 return pointRenderer;
421 else if ( renderer->
type() == QLatin1String(
"pointCluster" ) )
430 if ( const_cast< QgsPointClusterRenderer * >( clusterRenderer )->clusterSymbol() )
431 pointRenderer->
setCenterSymbol( const_cast< QgsPointClusterRenderer * >( clusterRenderer )->clusterSymbol()->clone() );
432 return pointRenderer;
An abstract base class for distance based point renderers (e.g., clusterer and displacement renderers...
The class is used as a container of context for various read/write operations on other objects...
QString mLabelAttributeName
Attribute name for labeling. An empty string indicates that no labels should be rendered.
static QgsFeatureRenderer * create(QDomElement &symbologyElem, const QgsReadWriteContext &context)
Create a renderer from XML element.
Place points in a grid around group.
QgsMarkerSymbol * centerSymbol()
Returns the symbol for the center of a displacement group (but not ownership of the symbol)...
QgsPointDisplacementRenderer * clone() const override
Create a deep copy of this renderer.
QgsUnitTypes::RenderUnit mToleranceUnit
Unit for distance tolerance.
QgsFeatureRequest::OrderBy mOrderBy
void setFeature(const QgsFeature &feature)
Convenience function for setting a feature for the context.
void setLabelFont(const QFont &font)
Sets the font used for labeling points.
QgsFeatureRequest::OrderBy orderBy() const
Gets the order in which features shall be processed by this renderer.
void setTolerance(double distance)
Sets the tolerance distance for grouping points.
static bool isDefaultStack(QgsPaintEffect *effect)
Tests whether a paint effect matches the default effects stack.
void setLabelColor(const QColor &color)
Sets the color to use for for labeling points.
void setToleranceMapUnitScale(const QgsMapUnitScale &scale)
Sets the map unit scale object for the distance tolerance.
void setToleranceUnit(QgsUnitTypes::RenderUnit unit)
Sets the units for the tolerance distance.
double outputLineWidth(double width) const
static QString encodeMapUnitScale(const QgsMapUnitScale &mapUnitScale)
Container of fields for a vector layer.
void stopRender(QgsRenderContext &context) override
Must be called when a render cycle has finished, to allow the renderer to clean up.
#define RENDERER_TAG_NAME
QgsPaintEffect * mPaintEffect
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
void startRender(QgsRenderContext &context, const QgsFields &fields) override
Must be called when a new render cycle is started.
void setCenterSymbol(QgsMarkerSymbol *symbol)
Sets the center symbol for a displacement group.
QgsUnitTypes::RenderUnit toleranceUnit() const
Returns the units for the tolerance distance.
A marker symbol type, for rendering Point and MultiPoint geometries.
double mMinLabelScale
Maximum scale denominator for label display. A zero value indicates no scale limitation.
void drawLabels(QPointF centerPoint, QgsSymbolRenderContext &context, const QList< QPointF > &labelShifts, const ClusteredGroup &group)
Renders the labels for a group.
static QString encodeColor(const QColor &color)
QgsPointDisplacementRenderer(const QString &labelAttributeName=QString())
Constructor for QgsPointDisplacementRenderer.
QFont labelFont() const
Returns the font used for labeling points.
QFont mLabelFont
Label font.
double mTolerance
Distance tolerance. Points that are closer together than this distance are considered clustered...
QDomElement save(QDomDocument &doc, const QgsReadWriteContext &context) override
store renderer info to XML element
static QgsFeatureRenderer * load(QDomElement &symbologyElem, const QgsReadWriteContext &context)
create a renderer from XML element
void setCircleColor(const QColor &color)
Sets the color used for drawing the displacement group circle.
void setLabelAttributeName(const QString &name)
Sets the attribute name for labeling points.
A renderer that automatically clusters points with the same geographic position.
Place points in concentric rings around group.
static bool setFromXmlChildNode(QFont &font, const QDomElement &element, const QString &childNode)
Sets the properties of a font to match the properties stored in an XML child node.
void setPlacement(Placement placement)
Sets the placement method used for dispersing the points.
QColor mLabelColor
Label text color.
void startRender(QgsRenderContext &context, const QgsFields &fields) override
Must be called when a new render cycle is started.
static Q_INVOKABLE QgsUnitTypes::RenderUnit decodeRenderUnit(const QString &string, bool *ok=nullptr)
Decodes a render unit from a string.
QgsRenderContext & renderContext()
Returns a reference to the context's render context.
QgsMapUnitScale mToleranceMapUnitScale
Map unit scale for distance tolerance.
double tolerance() const
Returns the tolerance distance for grouping points.
void stopRender(QgsRenderContext &context) override
Must be called when a render cycle has finished, to allow the renderer to clean up.
const QgsMapUnitScale & toleranceMapUnitScale() const
Returns the map unit scale object for the distance tolerance.
static Q_INVOKABLE QString encodeUnit(QgsUnitTypes::DistanceUnit unit)
Encodes a distance unit to a string.
Place points in a single ring around group.
QgsExpressionContext & expressionContext()
Gets the expression context.
Contains properties for a feature within a clustered group.
A renderer that automatically displaces points with the same geographic location. ...
const QgsFeatureRenderer * embeddedRenderer() const override
Returns the current embedded renderer (subrenderer) for this feature renderer.
Contains information about the context of a rendering operation.
QPainter * painter()
Returns the destination QPainter for the render operation.
void setMinimumLabelScale(double scale)
Sets the minimum map scale (i.e.
std::unique_ptr< QgsFeatureRenderer > mRenderer
Embedded base renderer. This can be used for rendering individual, isolated points.
void CORE_EXPORT save(QDomElement &elem) const
Serialize to XML.
static QDomElement saveSymbol(const QString &symbolName, QgsSymbol *symbol, QDomDocument &doc, const QgsReadWriteContext &context)
Writes a symbol definition to XML.
void copyRendererData(QgsFeatureRenderer *destRenderer) const
Clones generic renderer data to another renderer.
QList< QgsPointDistanceRenderer::GroupedFeature > ClusteredGroup
A group of clustered points (ie features within the distance tolerance).
static QgsMapUnitScale decodeMapUnitScale(const QString &str)
static QDomElement toXmlElement(const QFont &font, QDomDocument &document, const QString &elementName)
Returns a DOM element containing the properties of the font.
int mLabelIndex
Label attribute index (or -1 if none). This index is not stored, it is requested in the startRender()...
QSet< QString > usedAttributes(const QgsRenderContext &context) const override
Returns a list of attributes required by this renderer.
void setCircleRadiusAddition(double distance)
Sets a factor for increasing the ring size of displacement groups.
void setEmbeddedRenderer(QgsFeatureRenderer *r) override
Sets an embedded renderer (subrenderer) for this feature renderer.
static QgsPointDisplacementRenderer * convertFromRenderer(const QgsFeatureRenderer *renderer)
Creates a QgsPointDisplacementRenderer from an existing renderer.
virtual QgsFeatureRenderer * clone() const =0
Create a deep copy of this renderer.
void setCircleWidth(double width)
Sets the line width for the displacement group circle.
static QColor decodeColor(const QString &str)
QSet< QString > usedAttributes(const QgsRenderContext &context) const override
Returns a list of attributes required by this renderer.
virtual bool saveProperties(QDomDocument &doc, QDomElement &element) const
Saves the current state of the effect to a DOM element.