35using namespace Qt::StringLiterals;
39 , mCircleColor( QColor( 125, 125, 125 ) )
41 mCenterSymbol = std::make_unique<QgsMarkerSymbol>();
78void QgsPointDisplacementRenderer::drawGroup( QPointF centerPoint,
QgsRenderContext &context,
const ClusteredGroup &group )
const
82 QVector<double> diagonals( group.size() );
83 double currentDiagonal;
85 int groupPosition = 0;
86 for (
const GroupedFeature &feature : group )
90 currentDiagonal = M_SQRT2 * symbol->size( context );
91 diagonals[groupPosition] = currentDiagonal;
92 diagonal = std::max( diagonal, currentDiagonal );
96 diagonals[groupPosition] = 0.0;
103 QList<QPointF> symbolPositions;
104 QList<QPointF> labelPositions;
105 double circleRadius = -1.0;
106 double gridRadius = -1.0;
109 calculateSymbolAndLabelPositions( symbolContext, centerPoint, group.size(), diagonal, symbolPositions, labelPositions, circleRadius, gridRadius, gridSize, diagonals );
112 if ( mCircleColor.isValid() && mCircleColor.alpha() > 0 )
114 switch ( mPlacement )
118 drawCircle( circleRadius, symbolContext, centerPoint, group.size() );
121 drawGrid( gridSize, symbolContext, symbolPositions, group.size() );
126 if ( group.size() > 1 )
129 const QgsFeature firstFeature = group.at( 0 ).feature;
132 mCenterSymbol->renderPoint( centerPoint, &firstFeature, context, -1,
false );
137 context.
painter()->drawRect( QRectF( centerPoint.x() - rectSize, centerPoint.y() - rectSize, rectSize * 2, rectSize * 2 ) );
142 drawSymbols( group, context, symbolPositions );
146 drawLabels( centerPoint, symbolContext, labelPositions, group );
155 mCenterSymbol->startRender( context, fields );
166 mCenterSymbol->stopRender( context );
177 labelFont.fromString( symbologyElem.attribute( u
"labelFont"_s, QString() ) );
180 r->
setPlacement(
static_cast< Placement >( symbologyElem.attribute( u
"placement"_s, u
"0"_s ).toInt() ) );
181 r->
setCircleWidth( symbologyElem.attribute( u
"circleWidth"_s, u
"0.4"_s ).toDouble() );
186 r->
setMinimumLabelScale( symbologyElem.attribute( u
"maxLabelScaleDenominator"_s, u
"-1"_s ).toDouble() );
187 r->
setTolerance( symbologyElem.attribute( u
"tolerance"_s, u
"0.00001"_s ).toDouble() );
192 QDomElement embeddedRendererElem = symbologyElem.firstChildElement( u
"renderer-v2"_s );
193 if ( !embeddedRendererElem.isNull() )
199 const QDomElement centerSymbolElem = symbologyElem.firstChildElement( u
"symbol"_s );
200 if ( !centerSymbolElem.isNull() )
209 return mCenterSymbol.get();
215 rendererElement.setAttribute( u
"type"_s, u
"pointDisplacement"_s );
218 rendererElement.setAttribute( u
"circleWidth"_s, QString::number( mCircleWidth ) );
221 rendererElement.setAttribute( u
"circleRadiusAddition"_s, QString::number( mCircleRadiusAddition ) );
222 rendererElement.setAttribute( u
"labelDistanceFactor"_s, QString::number( mLabelDistanceFactor ) );
223 rendererElement.setAttribute( u
"placement"_s,
static_cast< int >( mPlacement ) );
224 rendererElement.setAttribute( u
"maxLabelScaleDenominator"_s, QString::number(
mMinLabelScale ) );
225 rendererElement.setAttribute( u
"tolerance"_s, QString::number(
mTolerance ) );
231 const QDomElement embeddedRendererElem =
mRenderer->save( doc, context );
232 rendererElement.appendChild( embeddedRendererElem );
237 rendererElement.appendChild( centerSymbolElem );
242 return rendererElement;
249 attr.unite( mCenterSymbol->usedAttributes( context ) );
270 mCenterSymbol.reset( symbol );
273void QgsPointDisplacementRenderer::calculateSymbolAndLabelPositions(
277 double symbolDiagonal,
278 QList<QPointF> &symbolPositions,
279 QList<QPointF> &labelShifts,
280 double &circleRadius,
283 QVector<double> &diagonals
286 symbolPositions.clear();
293 else if ( nPosition == 1 )
295 const double side = std::sqrt( std::pow( symbolDiagonal, 2 ) / 2.0 );
296 symbolPositions.append( centerPoint );
297 labelShifts.append( QPointF( side * mLabelDistanceFactor, -side * mLabelDistanceFactor ) );
303 switch ( mPlacement )
307 const double minDiameterToFitSymbols = nPosition * symbolDiagonal / ( 2.0 * M_PI );
308 const double radius = std::max( symbolDiagonal / 2, minDiameterToFitSymbols ) + circleAdditionPainterUnits;
310 const double angleStep = 2 * M_PI / nPosition;
311 double currentAngle = 0.0;
312 for (
int featureIndex = 0; featureIndex < nPosition; currentAngle += angleStep, featureIndex++ )
314 const double sinusCurrentAngle = std::sin( currentAngle );
315 const double cosinusCurrentAngle = std::cos( currentAngle );
316 const QPointF positionShift( radius * sinusCurrentAngle, radius * cosinusCurrentAngle );
318 const QPointF labelShift( ( radius + diagonals.at( featureIndex ) * mLabelDistanceFactor ) * sinusCurrentAngle, ( radius + diagonals.at( featureIndex ) * mLabelDistanceFactor ) * cosinusCurrentAngle );
319 symbolPositions.append( centerPoint + positionShift );
320 labelShifts.append( labelShift );
322 circleRadius = radius;
327 const double centerDiagonal = mCenterSymbol->size( symbolContext.
renderContext() ) * M_SQRT2;
329 int pointsRemaining = nPosition;
331 const double firstRingRadius = centerDiagonal / 2.0 + symbolDiagonal / 2.0;
332 int featureIndex = 0;
333 while ( pointsRemaining > 0 )
335 const double radiusCurrentRing = std::max( firstRingRadius + ( ringNumber - 1 ) * symbolDiagonal + ringNumber * circleAdditionPainterUnits, 0.0 );
336 const int maxPointsCurrentRing = std::max( std::floor( 2 * M_PI * radiusCurrentRing / symbolDiagonal ), 1.0 );
337 const int actualPointsCurrentRing = std::min( maxPointsCurrentRing, pointsRemaining );
339 const double angleStep = 2 * M_PI / actualPointsCurrentRing;
340 double currentAngle = 0.0;
341 for (
int i = 0; i < actualPointsCurrentRing; ++i )
343 const double sinusCurrentAngle = std::sin( currentAngle );
344 const double cosinusCurrentAngle = std::cos( currentAngle );
345 const QPointF positionShift( radiusCurrentRing * sinusCurrentAngle, radiusCurrentRing * cosinusCurrentAngle );
347 labelShift( ( radiusCurrentRing + diagonals.at( featureIndex ) * mLabelDistanceFactor ) * sinusCurrentAngle, ( radiusCurrentRing + diagonals.at( featureIndex ) * mLabelDistanceFactor ) * cosinusCurrentAngle );
348 symbolPositions.append( centerPoint + positionShift );
349 labelShifts.append( labelShift );
350 currentAngle += angleStep;
354 pointsRemaining -= actualPointsCurrentRing;
356 circleRadius = radiusCurrentRing;
362 const double centerDiagonal = mCenterSymbol->size( symbolContext.
renderContext() ) * M_SQRT2;
363 int pointsRemaining = nPosition;
364 gridSize = std::ceil( std::sqrt( pointsRemaining ) );
365 if ( pointsRemaining - std::pow( gridSize - 1, 2 ) < gridSize )
367 const double originalPointRadius = ( ( centerDiagonal / 2.0 + symbolDiagonal / 2.0 ) + symbolDiagonal ) / 2;
368 const double userPointRadius = originalPointRadius + circleAdditionPainterUnits;
371 while ( pointsRemaining > 0 )
373 for (
int xIndex = 0; xIndex < gridSize && pointsRemaining > 0; ++xIndex )
375 const QPointF positionShift( userPointRadius * xIndex, userPointRadius * yIndex );
376 symbolPositions.append( centerPoint + positionShift );
382 centralizeGrid( symbolPositions, userPointRadius, gridSize );
387 for (
int symbolIndex = 0; symbolIndex < symbolPositions.size(); ++symbolIndex )
389 if ( symbolPositions.at( symbolIndex ).x() < centerPoint.x() )
398 if ( symbolPositions.at( symbolIndex ).y() < centerPoint.y() )
407 side = std::sqrt( std::pow( diagonals.at( symbolIndex ), 2 ) / 2.0 );
408 const QPointF labelShift( ( side * mLabelDistanceFactor * xFactor ), ( -side * mLabelDistanceFactor * yFactor ) );
409 labelShifts.append( symbolPositions.at( symbolIndex ) - centerPoint + labelShift );
412 gridRadius = userPointRadius;
418void QgsPointDisplacementRenderer::centralizeGrid( QList<QPointF> &pointSymbolPositions,
double radius,
int size )
const
420 const double shiftAmount = -radius * ( size - 1.0 ) / 2.0;
421 const QPointF centralShift( shiftAmount, shiftAmount );
422 for (
int i = 0; i < pointSymbolPositions.size(); ++i )
424 pointSymbolPositions[i] += centralShift;
428void QgsPointDisplacementRenderer::drawGrid(
int gridSizeUnits,
QgsSymbolRenderContext &context, QList<QPointF> pointSymbolPositions,
int nSymbols )
const
431 if ( nSymbols < 2 || !p )
436 QPen gridPen( mCircleColor );
438 p->setPen( gridPen );
440 for (
int i = 0; i < pointSymbolPositions.size(); ++i )
442 if ( i + 1 < pointSymbolPositions.size() && 0 != ( i + 1 ) % gridSizeUnits )
444 const QLineF gridLineRow( pointSymbolPositions[i], pointSymbolPositions[i + 1] );
445 p->drawLine( gridLineRow );
448 if ( i + gridSizeUnits < pointSymbolPositions.size() )
450 const QLineF gridLineColumn( pointSymbolPositions[i], pointSymbolPositions[i + gridSizeUnits] );
451 p->drawLine( gridLineColumn );
456void QgsPointDisplacementRenderer::drawCircle(
double radiusPainterUnits,
QgsSymbolRenderContext &context, QPointF centerPoint,
int nSymbols )
const
459 if ( nSymbols < 2 || !p )
465 QPen circlePen( mCircleColor );
467 p->setPen( circlePen );
468 p->drawArc( QRectF( centerPoint.x() - radiusPainterUnits, centerPoint.y() - radiusPainterUnits, 2 * radiusPainterUnits, 2 * radiusPainterUnits ), 0, 5760 );
471void QgsPointDisplacementRenderer::drawSymbols(
const ClusteredGroup &group,
QgsRenderContext &context,
const QList<QPointF> &symbolPositions )
const
473 QList<QPointF>::const_iterator symbolPosIt = symbolPositions.constBegin();
474 ClusteredGroup::const_iterator groupIt = group.constBegin();
475 for ( ; symbolPosIt != symbolPositions.constEnd() && groupIt != group.constEnd(); ++symbolPosIt, ++groupIt )
478 groupIt->symbol()->startRender( context );
479 groupIt->symbol()->renderPoint( *symbolPosIt, &( groupIt->feature ), context, -1, groupIt->isSelected );
482 const QgsGeometry bounds(
QgsGeometry::fromRect( QgsRectangle( groupIt->symbol()->bounds( *symbolPosIt, context, groupIt->feature ) ) ) );
484 const QgsRenderedFeatureHandlerInterface::RenderedFeatureContext featureContext( context );
485 for ( QgsRenderedFeatureHandlerInterface *handler : handlers )
486 handler->handleRenderedFeature( groupIt->feature, bounds, featureContext );
488 groupIt->symbol()->stopRender( context );
494 if ( renderer->
type() ==
"pointDisplacement"_L1 )
498 else if ( renderer->
type() ==
"singleSymbol"_L1 || renderer->
type() ==
"categorizedSymbol"_L1 || renderer->
type() ==
"graduatedSymbol"_L1 || renderer->
type() ==
"RuleRenderer"_L1 )
503 return pointRenderer;
505 else if ( renderer->
type() ==
"pointCluster"_L1 )
515 return pointRenderer;
QFlags< FeatureRendererFlag > FeatureRendererFlags
Flags controlling behavior of vector feature renderers.
@ AffectsLabeling
If present, indicates that the renderer will participate in the map labeling problem.
@ Millimeters
Millimeters.
@ AffectsLabeling
If present, indicates that the symbol will participate in the map labeling problem.
static QColor colorFromString(const QString &string)
Decodes a string into a color value.
static QString colorToString(const QColor &color)
Encodes a color into a string value.
void setFeature(const QgsFeature &feature)
Convenience function for setting a feature for the context.
QgsFeatureRenderer(const QString &type)
void copyRendererData(QgsFeatureRenderer *destRenderer) const
Clones generic renderer data to another renderer.
static QgsFeatureRenderer * load(QDomElement &symbologyElem, const QgsReadWriteContext &context)
create a renderer from XML element
void saveRendererData(QDomDocument &doc, QDomElement &element, const QgsReadWriteContext &context)
Saves generic renderer data into the specified element.
virtual QgsFeatureRenderer * clone() const =0
Create a deep copy of this renderer.
Container of fields for a vector layer.
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.
static QDomElement toXmlElement(const QFont &font, QDomDocument &document, const QString &elementName)
Returns a DOM element containing the properties of the font.
static QgsGeometry fromRect(const QgsRectangle &rect)
Creates a new geometry from a QgsRectangle.
A marker symbol type, for rendering Point and MultiPoint geometries.
A renderer that automatically clusters points with the same geographic position.
QgsPointDisplacementRenderer(const QString &labelAttributeName=QString())
Constructor for QgsPointDisplacementRenderer.
void setPlacement(Placement placement)
Sets the placement method used for dispersing the points.
void setLabelDistanceFactor(double factor)
Sets a factor for increasing the label distances from the symbol.
QDomElement save(QDomDocument &doc, const QgsReadWriteContext &context) override
Stores renderer properties to an XML element.
QgsPointDisplacementRenderer * clone() const override
Create a deep copy of this renderer.
void setCircleColor(const QColor &color)
Sets the color used for drawing the displacement group circle.
void startRender(QgsRenderContext &context, const QgsFields &fields) override
Must be called when a new render cycle is started.
void stopRender(QgsRenderContext &context) override
Must be called when a render cycle has finished, to allow the renderer to clean up.
static QgsFeatureRenderer * create(QDomElement &symbologyElem, const QgsReadWriteContext &context)
Create a renderer from XML element.
void setCenterSymbol(QgsMarkerSymbol *symbol)
Sets the center symbol for a displacement group.
QSet< QString > usedAttributes(const QgsRenderContext &context) const override
Returns a list of attributes required by this renderer.
Placement
Placement methods for dispersing points.
@ ConcentricRings
Place points in concentric rings around group.
@ Ring
Place points in a single ring around group.
@ Grid
Place points in a grid around group.
void setCircleRadiusAddition(double distance)
Sets a factor for increasing the ring size of displacement groups.
QgsMarkerSymbol * centerSymbol()
Returns the symbol for the center of a displacement group (but not ownership of the symbol).
Qgis::FeatureRendererFlags flags() const override
Returns flags associated with the renderer.
void setCircleWidth(double width)
Sets the line width for the displacement group circle.
static QgsPointDisplacementRenderer * convertFromRenderer(const QgsFeatureRenderer *renderer)
Creates a QgsPointDisplacementRenderer from an existing renderer.
bool accept(QgsStyleEntityVisitorInterface *visitor) const override
Accepts the specified symbology visitor, causing it to visit all symbols associated with the renderer...
void setLabelColor(const QColor &color)
Sets the color to use for for labeling points.
double mMinLabelScale
Maximum scale denominator for label display. A zero value indicates no scale limitation.
int mLabelIndex
Label attribute index (or -1 if none). This index is not stored, it is requested in the startRender()...
QColor mLabelColor
Label text color.
QgsMapUnitScale mToleranceMapUnitScale
Map unit scale for distance tolerance.
void setLabelFont(const QFont &font)
Sets the font used for labeling points.
void setToleranceUnit(Qgis::RenderUnit unit)
Sets the units for the tolerance distance.
QgsPointDistanceRenderer(const QString &rendererName, const QString &labelAttributeName=QString())
Constructor for QgsPointDistanceRenderer.
void drawLabels(QPointF centerPoint, QgsSymbolRenderContext &context, const QList< QPointF > &labelShifts, const ClusteredGroup &group) const
Renders the labels for a group.
void setMinimumLabelScale(double scale)
Sets the minimum map scale (i.e.
void stopRender(QgsRenderContext &context) override
Must be called when a render cycle has finished, to allow the renderer to clean up.
double tolerance() const
Returns the tolerance distance for grouping points.
QFont labelFont() const
Returns the font used for labeling points.
QString labelAttributeName() const
Returns the attribute name used for labeling points, or an empty string if no labeling will be done b...
void startRender(QgsRenderContext &context, const QgsFields &fields) override
Must be called when a new render cycle is started.
const QgsMapUnitScale & toleranceMapUnitScale() const
Returns the map unit scale object for the distance tolerance.
void setEmbeddedRenderer(QgsFeatureRenderer *r) override
Sets an embedded renderer (subrenderer) for this feature renderer.
const QgsFeatureRenderer * embeddedRenderer() const override
Returns the current embedded renderer (subrenderer) for this feature renderer.
QString mLabelAttributeName
Attribute name for labeling. An empty string indicates that no labels should be rendered.
void setLabelAttributeName(const QString &name)
Sets the attribute name for labeling points.
double mTolerance
Distance tolerance. Points that are closer together than this distance are considered clustered.
QSet< QString > usedAttributes(const QgsRenderContext &context) const override
Returns a list of attributes required by this renderer.
QFont mLabelFont
Label font.
std::unique_ptr< QgsFeatureRenderer > mRenderer
Embedded base renderer. This can be used for rendering individual, isolated points.
Qgis::RenderUnit toleranceUnit() const
Returns the units for the tolerance distance.
void setToleranceMapUnitScale(const QgsMapUnitScale &scale)
Sets the map unit scale object for the distance tolerance.
void setTolerance(double distance)
Sets the tolerance distance for grouping points.
Qgis::RenderUnit mToleranceUnit
Unit for distance tolerance.
bool accept(QgsStyleEntityVisitorInterface *visitor) const override
Accepts the specified symbology visitor, causing it to visit all symbols associated with the renderer...
A container for the context for various read/write operations on objects.
Contains information about the context of a rendering operation.
bool hasRenderedFeatureHandlers() const
Returns true if the context has any rendered feature handlers.
double convertToPainterUnits(double size, Qgis::RenderUnit unit, const QgsMapUnitScale &scale=QgsMapUnitScale(), Qgis::RenderSubcomponentProperty property=Qgis::RenderSubcomponentProperty::Generic) const
Converts a size from the specified units to painter units (pixels).
QPainter * painter()
Returns the destination QPainter for the render operation.
QgsExpressionContext & expressionContext()
Gets the expression context.
QList< QgsRenderedFeatureHandlerInterface * > renderedFeatureHandlers() const
Returns the list of rendered feature handlers to use while rendering map layers.
An interface for classes which can visit style entity (e.g.
virtual bool visit(const QgsStyleEntityVisitorInterface::StyleLeaf &entity)
Called when the visitor will visit a style entity.
A symbol entity for QgsStyle databases.
static QString encodeMapUnitScale(const QgsMapUnitScale &mapUnitScale)
static QgsMapUnitScale decodeMapUnitScale(const QString &str)
static std::unique_ptr< QgsSymbol > loadSymbol(const QDomElement &element, const QgsReadWriteContext &context)
Attempts to load a symbol from a DOM element.
static QDomElement saveSymbol(const QString &symbolName, const QgsSymbol *symbol, QDomDocument &doc, const QgsReadWriteContext &context)
Writes a symbol definition to XML.
Encapsulates the context in which a symbol is being rendered.
QgsRenderContext & renderContext()
Returns a reference to the context's render context.
static Q_INVOKABLE Qgis::RenderUnit decodeRenderUnit(const QString &string, bool *ok=nullptr)
Decodes a render unit from a string.
static Q_INVOKABLE QString encodeUnit(Qgis::DistanceUnit unit)
Encodes a distance unit to a string.
#define RENDERER_TAG_NAME
Contains information relating to the style entity currently being visited.