32 , mCircleColor( QColor( 125, 125, 125 ) )
66 QVector<double> diagonals( group.size() );
67 double currentDiagonal;
69 int groupPosition = 0;
74 currentDiagonal = M_SQRT2 * symbol->size( context );
75 diagonals[groupPosition] = currentDiagonal;
76 diagonal = std::max( diagonal, currentDiagonal );
81 diagonals[groupPosition] = 0.0;
88 QList<QPointF> symbolPositions;
89 QList<QPointF> labelPositions;
90 double circleRadius = -1.0;
91 double gridRadius = -1.0;
94 calculateSymbolAndLabelPositions( symbolContext, centerPoint, group.size(), diagonal, symbolPositions, labelPositions, circleRadius, gridRadius, gridSize, diagonals );
97 if ( mCircleColor.isValid() && mCircleColor.alpha() > 0 )
100 if ( circleRadius > 0 )
101 drawCircle( circleRadius, symbolContext, centerPoint, group.size() );
104 drawGrid( gridSize, symbolContext, symbolPositions, group.size() );
107 if ( group.size() > 1 )
110 QgsFeature firstFeature = group.at( 0 ).feature;
113 mCenterSymbol->renderPoint( centerPoint, &firstFeature, context, -1,
false );
118 context.
painter()->drawRect( QRectF( centerPoint.x() - rectSize, centerPoint.y() - rectSize, rectSize * 2, rectSize * 2 ) );
123 drawSymbols( group, context, symbolPositions );
127 drawLabels( centerPoint, symbolContext, labelPositions, group );
136 mCenterSymbol->startRender( context, fields );
147 mCenterSymbol->stopRender( context );
158 labelFont.fromString( symbologyElem.attribute( QStringLiteral(
"labelFont" ), QString() ) );
161 r->
setPlacement( static_cast< Placement >( symbologyElem.attribute( QStringLiteral(
"placement" ), QStringLiteral(
"0" ) ).toInt() ) );
162 r->
setCircleWidth( symbologyElem.attribute( QStringLiteral(
"circleWidth" ), QStringLiteral(
"0.4" ) ).toDouble() );
165 r->
setCircleRadiusAddition( symbologyElem.attribute( QStringLiteral(
"circleRadiusAddition" ), QStringLiteral(
"0.0" ) ).toDouble() );
166 r->
setLabelDistanceFactor( symbologyElem.attribute( QStringLiteral(
"labelDistanceFactor" ), QStringLiteral(
"0.5" ) ).toDouble() );
167 r->
setMinimumLabelScale( symbologyElem.attribute( QStringLiteral(
"maxLabelScaleDenominator" ), QStringLiteral(
"-1" ) ).toDouble() );
168 r->
setTolerance( symbologyElem.attribute( QStringLiteral(
"tolerance" ), QStringLiteral(
"0.00001" ) ).toDouble() );
173 QDomElement embeddedRendererElem = symbologyElem.firstChildElement( QStringLiteral(
"renderer-v2" ) );
174 if ( !embeddedRendererElem.isNull() )
180 QDomElement centerSymbolElem = symbologyElem.firstChildElement( QStringLiteral(
"symbol" ) );
181 if ( !centerSymbolElem.isNull() )
183 r->
setCenterSymbol( QgsSymbolLayerUtils::loadSymbol<QgsMarkerSymbol>( centerSymbolElem, context ) );
190 return mCenterSymbol.get();
196 rendererElement.setAttribute( QStringLiteral(
"forceraster" ), (
mForceRaster ? QStringLiteral(
"1" ) : QStringLiteral(
"0" ) ) );
197 rendererElement.setAttribute( QStringLiteral(
"type" ), QStringLiteral(
"pointDisplacement" ) );
198 rendererElement.setAttribute( QStringLiteral(
"labelAttributeName" ),
mLabelAttributeName );
200 rendererElement.setAttribute( QStringLiteral(
"circleWidth" ), QString::number( mCircleWidth ) );
203 rendererElement.setAttribute( QStringLiteral(
"circleRadiusAddition" ), QString::number( mCircleRadiusAddition ) );
204 rendererElement.setAttribute( QStringLiteral(
"labelDistanceFactor" ), QString::number( mLabelDistanceFactor ) );
205 rendererElement.setAttribute( QStringLiteral(
"placement" ), static_cast< int >( mPlacement ) );
206 rendererElement.setAttribute( QStringLiteral(
"maxLabelScaleDenominator" ), QString::number(
mMinLabelScale ) );
207 rendererElement.setAttribute( QStringLiteral(
"tolerance" ), QString::number(
mTolerance ) );
213 QDomElement embeddedRendererElem =
mRenderer->save( doc, context );
214 rendererElement.appendChild( embeddedRendererElem );
219 rendererElement.appendChild( centerSymbolElem );
227 QDomElement
orderBy = doc.createElement( QStringLiteral(
"orderby" ) );
229 rendererElement.appendChild( orderBy );
231 rendererElement.setAttribute( QStringLiteral(
"enableorderby" ), (
mOrderByEnabled ? QStringLiteral(
"1" ) : QStringLiteral(
"0" ) ) );
233 return rendererElement;
240 attr.unite( mCenterSymbol->usedAttributes( context ) );
261 mCenterSymbol.reset( symbol );
264 void QgsPointDisplacementRenderer::calculateSymbolAndLabelPositions(
QgsSymbolRenderContext &symbolContext, QPointF centerPoint,
int nPosition,
265 double symbolDiagonal, QList<QPointF> &symbolPositions, QList<QPointF> &labelShifts,
double &circleRadius,
double &gridRadius,
266 int &gridSize, QVector<double> &diagonals )
const 268 symbolPositions.clear();
275 else if ( nPosition == 1 )
277 const double side = std::sqrt( std::pow( symbolDiagonal, 2 ) / 2.0 );
278 symbolPositions.append( centerPoint );
279 labelShifts.append( QPointF( side * mLabelDistanceFactor, -side * mLabelDistanceFactor ) );
285 switch ( mPlacement )
289 const double minDiameterToFitSymbols = nPosition * symbolDiagonal / ( 2.0 * M_PI );
290 const double radius = std::max( symbolDiagonal / 2, minDiameterToFitSymbols ) + circleAdditionPainterUnits;
292 const double angleStep = 2 * M_PI / nPosition;
293 double currentAngle = 0.0;
294 for (
int featureIndex = 0; featureIndex < nPosition; currentAngle += angleStep, featureIndex++ )
296 const double sinusCurrentAngle = std::sin( currentAngle );
297 const double cosinusCurrentAngle = std::cos( currentAngle );
298 const QPointF positionShift( radius * sinusCurrentAngle, radius * cosinusCurrentAngle );
300 const QPointF labelShift( ( radius + diagonals.at( featureIndex ) * mLabelDistanceFactor ) * sinusCurrentAngle, ( radius + diagonals.at( featureIndex ) * mLabelDistanceFactor ) * cosinusCurrentAngle );
301 symbolPositions.append( centerPoint + positionShift );
302 labelShifts.append( labelShift );
304 circleRadius = radius;
309 double centerDiagonal = mCenterSymbol->size( symbolContext.
renderContext() ) * M_SQRT2;
311 int pointsRemaining = nPosition;
313 double firstRingRadius = centerDiagonal / 2.0 + symbolDiagonal / 2.0;
314 int featureIndex = 0;
315 while ( pointsRemaining > 0 )
317 double radiusCurrentRing = std::max( firstRingRadius + ( ringNumber - 1 ) * symbolDiagonal + ringNumber * circleAdditionPainterUnits, 0.0 );
318 int maxPointsCurrentRing = std::max( std::floor( 2 * M_PI * radiusCurrentRing / symbolDiagonal ), 1.0 );
319 int actualPointsCurrentRing = std::min( maxPointsCurrentRing, pointsRemaining );
321 double angleStep = 2 * M_PI / actualPointsCurrentRing;
322 double currentAngle = 0.0;
323 for (
int i = 0; i < actualPointsCurrentRing; ++i )
325 double sinusCurrentAngle = std::sin( currentAngle );
326 double cosinusCurrentAngle = std::cos( currentAngle );
327 QPointF positionShift( radiusCurrentRing * sinusCurrentAngle, radiusCurrentRing * cosinusCurrentAngle );
328 QPointF labelShift( ( radiusCurrentRing + diagonals.at( featureIndex ) * mLabelDistanceFactor ) * sinusCurrentAngle, ( radiusCurrentRing + diagonals.at( featureIndex ) * mLabelDistanceFactor ) * cosinusCurrentAngle );
329 symbolPositions.append( centerPoint + positionShift );
330 labelShifts.append( labelShift );
331 currentAngle += angleStep;
335 pointsRemaining -= actualPointsCurrentRing;
337 circleRadius = radiusCurrentRing;
343 double centerDiagonal = mCenterSymbol->size( symbolContext.
renderContext() ) * M_SQRT2;
344 int pointsRemaining = nPosition;
345 gridSize = std::ceil( std::sqrt( pointsRemaining ) );
346 if ( pointsRemaining - std::pow( gridSize - 1, 2 ) < gridSize )
348 double originalPointRadius = ( ( centerDiagonal / 2.0 + symbolDiagonal / 2.0 ) + symbolDiagonal ) / 2;
349 double userPointRadius = originalPointRadius + circleAdditionPainterUnits;
352 while ( pointsRemaining > 0 )
354 for (
int xIndex = 0; xIndex < gridSize && pointsRemaining > 0; ++xIndex )
356 QPointF positionShift( userPointRadius * xIndex, userPointRadius * yIndex );
357 symbolPositions.append( centerPoint + positionShift );
363 centralizeGrid( symbolPositions, userPointRadius, gridSize );
368 for (
int symbolIndex = 0; symbolIndex < symbolPositions.size(); ++symbolIndex )
370 if ( symbolPositions.at( symbolIndex ).x() < centerPoint.x() )
379 if ( symbolPositions.at( symbolIndex ).y() < centerPoint.y() )
388 side = std::sqrt( std::pow( diagonals.at( symbolIndex ), 2 ) / 2.0 );
389 QPointF labelShift( ( side * mLabelDistanceFactor * xFactor ), ( -side * mLabelDistanceFactor * yFactor ) );
390 labelShifts.append( symbolPositions.at( symbolIndex ) - centerPoint + labelShift );
393 gridRadius = userPointRadius;
399 void QgsPointDisplacementRenderer::centralizeGrid( QList<QPointF> &pointSymbolPositions,
double radius,
int size )
const 401 double shiftAmount = -radius * ( size - 1.0 ) / 2.0;
402 QPointF centralShift( shiftAmount, shiftAmount );
403 for (
int i = 0; i < pointSymbolPositions.size(); ++i )
405 pointSymbolPositions[i] += centralShift;
410 QList<QPointF> pointSymbolPositions,
int nSymbols )
413 if ( nSymbols < 2 || !p )
418 QPen gridPen( mCircleColor );
420 p->setPen( gridPen );
422 for (
int i = 0; i < pointSymbolPositions.size(); ++i )
424 if ( i + 1 < pointSymbolPositions.size() && 0 != ( i + 1 ) % gridSizeUnits )
426 QLineF gridLineRow( pointSymbolPositions[i], pointSymbolPositions[i + 1] );
427 p->drawLine( gridLineRow );
430 if ( i + gridSizeUnits < pointSymbolPositions.size() )
432 QLineF gridLineColumn( pointSymbolPositions[i], pointSymbolPositions[i + gridSizeUnits] );
433 p->drawLine( gridLineColumn );
438 void QgsPointDisplacementRenderer::drawCircle(
double radiusPainterUnits,
QgsSymbolRenderContext &context, QPointF centerPoint,
int nSymbols )
441 if ( nSymbols < 2 || !p )
447 QPen circlePen( mCircleColor );
449 p->setPen( circlePen );
450 p->drawArc( QRectF( centerPoint.x() - radiusPainterUnits, centerPoint.y() - radiusPainterUnits, 2 * radiusPainterUnits, 2 * radiusPainterUnits ), 0, 5760 );
455 QList<QPointF>::const_iterator symbolPosIt = symbolPositions.constBegin();
456 ClusteredGroup::const_iterator groupIt = group.constBegin();
457 for ( ; symbolPosIt != symbolPositions.constEnd() && groupIt != group.constEnd();
458 ++symbolPosIt, ++groupIt )
461 groupIt->symbol()->startRender( context );
462 groupIt->symbol()->renderPoint( *symbolPosIt, &( groupIt->feature ), context, -1, groupIt->isSelected );
469 handler->handleRenderedFeature( groupIt->feature, bounds, featureContext );
471 groupIt->symbol()->stopRender( context );
477 if ( renderer->
type() == QLatin1String(
"pointDisplacement" ) )
481 else if ( renderer->
type() == QLatin1String(
"singleSymbol" ) ||
482 renderer->
type() == QLatin1String(
"categorizedSymbol" ) ||
483 renderer->
type() == QLatin1String(
"graduatedSymbol" ) ||
484 renderer->
type() == QLatin1String(
"RuleRenderer" ) )
488 return pointRenderer;
490 else if ( renderer->
type() == QLatin1String(
"pointCluster" ) )
499 if ( const_cast< QgsPointClusterRenderer * >( clusterRenderer )->clusterSymbol() )
500 pointRenderer->
setCenterSymbol( const_cast< QgsPointClusterRenderer * >( clusterRenderer )->clusterSymbol()->clone() );
501 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...
A rectangle specified with double values.
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.
A symbol entity for QgsStyle databases.
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
bool accept(QgsStyleEntityVisitorInterface *visitor) const override
Accepts the specified symbology visitor, causing it to visit all symbols associated with the renderer...
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.
QList< QgsRenderedFeatureHandlerInterface * > renderedFeatureHandlers() const
Returns the list of rendered feature handlers to use while rendering map layers.
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.
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.
A geometry is the spatial representation of a feature.
#define RENDERER_TAG_NAME
static QDomElement saveSymbol(const QString &symbolName, const QgsSymbol *symbol, QDomDocument &doc, const QgsReadWriteContext &context)
Writes a symbol definition to XML.
QgsPaintEffect * mPaintEffect
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
An interface for classes which can visit style entity (e.g.
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.
virtual bool visit(const QgsStyleEntityVisitorInterface::StyleLeaf &entity)
Called when the visitor will visit a style entity.
bool hasRenderedFeatureHandlers() const
Returns true if the context has any rendered feature handlers.
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.
static QgsGeometry fromRect(const QgsRectangle &rect)
Creates a new geometry from a QgsRectangle.
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.
An interface for classes which provider custom handlers for features rendered as part of a map render...
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.
void setLabelDistanceFactor(double factor)
Sets a factor for increasing the label distances from the symbol.
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.
double convertToPainterUnits(double size, QgsUnitTypes::RenderUnit unit, const QgsMapUnitScale &scale=QgsMapUnitScale()) const
Converts a size from the specified units to painter units (pixels).
QPainter * painter()
Returns the destination QPainter for the render operation.
bool accept(QgsStyleEntityVisitorInterface *visitor) const override
Accepts the specified symbology visitor, causing it to visit all symbols associated with the renderer...
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.
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.
Contains information relating to the style entity currently being visited.
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.