30 , mCircleColor( QColor( 125, 125, 125 ) )
64 QVector<double> diagonals( group.size() );
65 double currentDiagonal;
67 int groupPosition = 0;
72 currentDiagonal = M_SQRT2 * symbol->size( context );
73 diagonals[groupPosition] = currentDiagonal;
74 diagonal = std::max( diagonal, currentDiagonal );
79 diagonals[groupPosition] = 0.0;
86 QList<QPointF> symbolPositions;
87 QList<QPointF> labelPositions;
88 double circleRadius = -1.0;
89 double gridRadius = -1.0;
92 calculateSymbolAndLabelPositions( symbolContext, centerPoint, group.size(), diagonal, symbolPositions, labelPositions, circleRadius, gridRadius, gridSize, diagonals );
95 if ( mCircleColor.isValid() && mCircleColor.alpha() > 0 )
98 if ( circleRadius > 0 )
99 drawCircle( circleRadius, symbolContext, centerPoint, group.size() );
102 drawGrid( gridSize, symbolContext, symbolPositions, group.size() );
105 if ( group.size() > 1 )
108 QgsFeature firstFeature = group.at( 0 ).feature;
111 mCenterSymbol->renderPoint( centerPoint, &firstFeature, context, -1,
false );
116 context.
painter()->drawRect( QRectF( centerPoint.x() - rectSize, centerPoint.y() - rectSize, rectSize * 2, rectSize * 2 ) );
121 drawSymbols( group, context, symbolPositions );
125 drawLabels( centerPoint, symbolContext, labelPositions, group );
134 mCenterSymbol->startRender( context, fields );
145 mCenterSymbol->stopRender( context );
156 labelFont.fromString( symbologyElem.attribute( QStringLiteral(
"labelFont" ), QString() ) );
159 r->
setPlacement( static_cast< Placement >( symbologyElem.attribute( QStringLiteral(
"placement" ), QStringLiteral(
"0" ) ).toInt() ) );
160 r->
setCircleWidth( symbologyElem.attribute( QStringLiteral(
"circleWidth" ), QStringLiteral(
"0.4" ) ).toDouble() );
163 r->
setCircleRadiusAddition( symbologyElem.attribute( QStringLiteral(
"circleRadiusAddition" ), QStringLiteral(
"0.0" ) ).toDouble() );
164 r->
setLabelDistanceFactor( symbologyElem.attribute( QStringLiteral(
"labelDistanceFactor" ), QStringLiteral(
"0.5" ) ).toDouble() );
165 r->
setMinimumLabelScale( symbologyElem.attribute( QStringLiteral(
"maxLabelScaleDenominator" ), QStringLiteral(
"-1" ) ).toDouble() );
166 r->
setTolerance( symbologyElem.attribute( QStringLiteral(
"tolerance" ), QStringLiteral(
"0.00001" ) ).toDouble() );
171 QDomElement embeddedRendererElem = symbologyElem.firstChildElement( QStringLiteral(
"renderer-v2" ) );
172 if ( !embeddedRendererElem.isNull() )
178 QDomElement centerSymbolElem = symbologyElem.firstChildElement( QStringLiteral(
"symbol" ) );
179 if ( !centerSymbolElem.isNull() )
181 r->
setCenterSymbol( QgsSymbolLayerUtils::loadSymbol<QgsMarkerSymbol>( centerSymbolElem, context ) );
188 return mCenterSymbol.get();
194 rendererElement.setAttribute( QStringLiteral(
"forceraster" ), (
mForceRaster ? QStringLiteral(
"1" ) : QStringLiteral(
"0" ) ) );
195 rendererElement.setAttribute( QStringLiteral(
"type" ), QStringLiteral(
"pointDisplacement" ) );
196 rendererElement.setAttribute( QStringLiteral(
"labelAttributeName" ),
mLabelAttributeName );
198 rendererElement.setAttribute( QStringLiteral(
"circleWidth" ), QString::number( mCircleWidth ) );
201 rendererElement.setAttribute( QStringLiteral(
"circleRadiusAddition" ), QString::number( mCircleRadiusAddition ) );
202 rendererElement.setAttribute( QStringLiteral(
"labelDistanceFactor" ), QString::number( mLabelDistanceFactor ) );
203 rendererElement.setAttribute( QStringLiteral(
"placement" ), static_cast< int >( mPlacement ) );
204 rendererElement.setAttribute( QStringLiteral(
"maxLabelScaleDenominator" ), QString::number(
mMinLabelScale ) );
205 rendererElement.setAttribute( QStringLiteral(
"tolerance" ), QString::number(
mTolerance ) );
211 QDomElement embeddedRendererElem =
mRenderer->save( doc, context );
212 rendererElement.appendChild( embeddedRendererElem );
217 rendererElement.appendChild( centerSymbolElem );
225 QDomElement
orderBy = doc.createElement( QStringLiteral(
"orderby" ) );
227 rendererElement.appendChild( orderBy );
229 rendererElement.setAttribute( QStringLiteral(
"enableorderby" ), (
mOrderByEnabled ? QStringLiteral(
"1" ) : QStringLiteral(
"0" ) ) );
231 return rendererElement;
238 attr.unite( mCenterSymbol->usedAttributes( context ) );
244 mCenterSymbol.reset( symbol );
247 void QgsPointDisplacementRenderer::calculateSymbolAndLabelPositions(
QgsSymbolRenderContext &symbolContext, QPointF centerPoint,
int nPosition,
248 double symbolDiagonal, QList<QPointF> &symbolPositions, QList<QPointF> &labelShifts,
double &circleRadius,
double &gridRadius,
249 int &gridSize, QVector<double> &diagonals )
const 251 symbolPositions.clear();
258 else if ( nPosition == 1 )
260 const double side = std::sqrt( std::pow( symbolDiagonal, 2 ) / 2.0 );
261 symbolPositions.append( centerPoint );
262 labelShifts.append( QPointF( side * mLabelDistanceFactor, -side * mLabelDistanceFactor ) );
268 switch ( mPlacement )
272 double minDiameterToFitSymbols = nPosition * symbolDiagonal / ( 2.0 * M_PI );
273 double radius = std::max( symbolDiagonal / 2, minDiameterToFitSymbols ) + circleAdditionPainterUnits;
275 double fullPerimeter = 2 * M_PI;
276 double angleStep = fullPerimeter / nPosition;
280 for ( currentAngle = 0.0, featureIndex = 0; currentAngle < fullPerimeter; currentAngle += angleStep, featureIndex++ )
282 double sinusCurrentAngle = std::sin( currentAngle );
283 double cosinusCurrentAngle = std::cos( currentAngle );
284 QPointF positionShift( radius * sinusCurrentAngle, radius * cosinusCurrentAngle );
286 QPointF labelShift( ( radius + diagonals.at( featureIndex ) * mLabelDistanceFactor ) * sinusCurrentAngle, ( radius + diagonals.at( featureIndex ) * mLabelDistanceFactor ) * cosinusCurrentAngle );
287 symbolPositions.append( centerPoint + positionShift );
288 labelShifts.append( labelShift );
290 circleRadius = radius;
295 double centerDiagonal = mCenterSymbol->size( symbolContext.
renderContext() ) * M_SQRT2;
297 int pointsRemaining = nPosition;
299 double firstRingRadius = centerDiagonal / 2.0 + symbolDiagonal / 2.0;
300 int featureIndex = 0;
301 while ( pointsRemaining > 0 )
303 double radiusCurrentRing = std::max( firstRingRadius + ( ringNumber - 1 ) * symbolDiagonal + ringNumber * circleAdditionPainterUnits, 0.0 );
304 int maxPointsCurrentRing = std::max( std::floor( 2 * M_PI * radiusCurrentRing / symbolDiagonal ), 1.0 );
305 int actualPointsCurrentRing = std::min( maxPointsCurrentRing, pointsRemaining );
307 double angleStep = 2 * M_PI / actualPointsCurrentRing;
308 double currentAngle = 0.0;
309 for (
int i = 0; i < actualPointsCurrentRing; ++i )
311 double sinusCurrentAngle = std::sin( currentAngle );
312 double cosinusCurrentAngle = std::cos( currentAngle );
313 QPointF positionShift( radiusCurrentRing * sinusCurrentAngle, radiusCurrentRing * cosinusCurrentAngle );
314 QPointF labelShift( ( radiusCurrentRing + diagonals.at( featureIndex ) * mLabelDistanceFactor ) * sinusCurrentAngle, ( radiusCurrentRing + diagonals.at( featureIndex ) * mLabelDistanceFactor ) * cosinusCurrentAngle );
315 symbolPositions.append( centerPoint + positionShift );
316 labelShifts.append( labelShift );
317 currentAngle += angleStep;
321 pointsRemaining -= actualPointsCurrentRing;
323 circleRadius = radiusCurrentRing;
329 double centerDiagonal = mCenterSymbol->size( symbolContext.
renderContext() ) * M_SQRT2;
330 int pointsRemaining = nPosition;
331 gridSize = std::ceil( std::sqrt( pointsRemaining ) );
332 if ( pointsRemaining - std::pow( gridSize - 1, 2 ) < gridSize )
334 double originalPointRadius = ( ( centerDiagonal / 2.0 + symbolDiagonal / 2.0 ) + symbolDiagonal ) / 2;
335 double userPointRadius = originalPointRadius + circleAdditionPainterUnits;
338 while ( pointsRemaining > 0 )
340 for (
int xIndex = 0; xIndex < gridSize && pointsRemaining > 0; ++xIndex )
342 QPointF positionShift( userPointRadius * xIndex, userPointRadius * yIndex );
343 symbolPositions.append( centerPoint + positionShift );
349 centralizeGrid( symbolPositions, userPointRadius, gridSize );
354 for (
int symbolIndex = 0; symbolIndex < symbolPositions.size(); ++symbolIndex )
356 if ( symbolPositions.at( symbolIndex ).x() < centerPoint.x() )
365 if ( symbolPositions.at( symbolIndex ).y() < centerPoint.y() )
374 side = std::sqrt( std::pow( diagonals.at( symbolIndex ), 2 ) / 2.0 );
375 QPointF labelShift( ( side * mLabelDistanceFactor * xFactor ), ( -side * mLabelDistanceFactor * yFactor ) );
376 labelShifts.append( symbolPositions.at( symbolIndex ) - centerPoint + labelShift );
379 gridRadius = userPointRadius;
385 void QgsPointDisplacementRenderer::centralizeGrid( QList<QPointF> &pointSymbolPositions,
double radius,
int size )
const 387 double shiftAmount = -radius * ( size - 1.0 ) / 2.0;
388 QPointF centralShift( shiftAmount, shiftAmount );
389 for (
int i = 0; i < pointSymbolPositions.size(); ++i )
391 pointSymbolPositions[i] += centralShift;
396 QList<QPointF> pointSymbolPositions,
int nSymbols )
399 if ( nSymbols < 2 || !p )
404 QPen gridPen( mCircleColor );
406 p->setPen( gridPen );
408 for (
int i = 0; i < pointSymbolPositions.size(); ++i )
410 if ( i + 1 < pointSymbolPositions.size() && 0 != ( i + 1 ) % gridSizeUnits )
412 QLineF gridLineRow( pointSymbolPositions[i], pointSymbolPositions[i + 1] );
413 p->drawLine( gridLineRow );
416 if ( i + gridSizeUnits < pointSymbolPositions.size() )
418 QLineF gridLineColumn( pointSymbolPositions[i], pointSymbolPositions[i + gridSizeUnits] );
419 p->drawLine( gridLineColumn );
424 void QgsPointDisplacementRenderer::drawCircle(
double radiusPainterUnits,
QgsSymbolRenderContext &context, QPointF centerPoint,
int nSymbols )
427 if ( nSymbols < 2 || !p )
433 QPen circlePen( mCircleColor );
435 p->setPen( circlePen );
436 p->drawArc( QRectF( centerPoint.x() - radiusPainterUnits, centerPoint.y() - radiusPainterUnits, 2 * radiusPainterUnits, 2 * radiusPainterUnits ), 0, 5760 );
441 QList<QPointF>::const_iterator symbolPosIt = symbolPositions.constBegin();
442 ClusteredGroup::const_iterator groupIt = group.constBegin();
443 for ( ; symbolPosIt != symbolPositions.constEnd() && groupIt != group.constEnd();
444 ++symbolPosIt, ++groupIt )
447 groupIt->symbol()->startRender( context );
448 groupIt->symbol()->renderPoint( *symbolPosIt, &( groupIt->feature ), context, -1, groupIt->isSelected );
449 groupIt->symbol()->stopRender( context );
455 if ( renderer->
type() == QLatin1String(
"pointDisplacement" ) )
459 else if ( renderer->
type() == QLatin1String(
"singleSymbol" ) ||
460 renderer->
type() == QLatin1String(
"categorizedSymbol" ) ||
461 renderer->
type() == QLatin1String(
"graduatedSymbol" ) ||
462 renderer->
type() == QLatin1String(
"RuleRenderer" ) )
466 return pointRenderer;
468 else if ( renderer->
type() == QLatin1String(
"pointCluster" ) )
477 if ( const_cast< QgsPointClusterRenderer * >( clusterRenderer )->clusterSymbol() )
478 pointRenderer->
setCenterSymbol( const_cast< QgsPointClusterRenderer * >( clusterRenderer )->clusterSymbol()->clone() );
479 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.
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.
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.
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.