30 , mCircleColor( QColor( 125, 125, 125 ) )
69 symbol->sizeUnit(), symbol->sizeMapUnitScale() ) );
75 QList<QPointF> symbolPositions;
76 QList<QPointF> labelPositions;
77 double circleRadius = -1.0;
78 double gridRadius = -1.0;
81 calculateSymbolAndLabelPositions( symbolContext, centerPoint, group.size(), diagonal, symbolPositions, labelPositions, circleRadius, gridRadius, gridSize );
84 if ( mCircleColor.isValid() && mCircleColor.alpha() > 0 )
87 if ( circleRadius > 0 )
88 drawCircle( circleRadius, symbolContext, centerPoint, group.size() );
91 drawGrid( gridSize, symbolContext, symbolPositions, group.size() );
94 if ( group.size() > 1 )
97 QgsFeature firstFeature = group.at( 0 ).feature;
100 mCenterSymbol->renderPoint( centerPoint, &firstFeature, context, -1,
false );
109 drawSymbols( group, context, symbolPositions );
113 drawLabels( centerPoint, symbolContext, labelPositions, group );
122 mCenterSymbol->startRender( context, fields );
133 mCenterSymbol->stopRender( context );
144 labelFont.fromString( symbologyElem.attribute( QStringLiteral(
"labelFont" ), QLatin1String(
"" ) ) );
147 r->
setPlacement( static_cast< Placement >( symbologyElem.attribute( QStringLiteral(
"placement" ), QStringLiteral(
"0" ) ).toInt() ) );
148 r->
setCircleWidth( symbologyElem.attribute( QStringLiteral(
"circleWidth" ), QStringLiteral(
"0.4" ) ).toDouble() );
151 r->
setCircleRadiusAddition( symbologyElem.attribute( QStringLiteral(
"circleRadiusAddition" ), QStringLiteral(
"0.0" ) ).toDouble() );
152 r->
setMinimumLabelScale( symbologyElem.attribute( QStringLiteral(
"maxLabelScaleDenominator" ), QStringLiteral(
"-1" ) ).toDouble() );
153 r->
setTolerance( symbologyElem.attribute( QStringLiteral(
"tolerance" ), QStringLiteral(
"0.00001" ) ).toDouble() );
158 QDomElement embeddedRendererElem = symbologyElem.firstChildElement( QStringLiteral(
"renderer-v2" ) );
159 if ( !embeddedRendererElem.isNull() )
165 QDomElement centerSymbolElem = symbologyElem.firstChildElement( QStringLiteral(
"symbol" ) );
166 if ( !centerSymbolElem.isNull() )
168 r->
setCenterSymbol( QgsSymbolLayerUtils::loadSymbol<QgsMarkerSymbol>( centerSymbolElem, context ) );
175 return mCenterSymbol.get();
181 rendererElement.setAttribute( QStringLiteral(
"forceraster" ), (
mForceRaster ? QStringLiteral(
"1" ) : QStringLiteral(
"0" ) ) );
182 rendererElement.setAttribute( QStringLiteral(
"type" ), QStringLiteral(
"pointDisplacement" ) );
183 rendererElement.setAttribute( QStringLiteral(
"labelAttributeName" ),
mLabelAttributeName );
185 rendererElement.setAttribute( QStringLiteral(
"circleWidth" ), QString::number( mCircleWidth ) );
188 rendererElement.setAttribute( QStringLiteral(
"circleRadiusAddition" ), QString::number( mCircleRadiusAddition ) );
189 rendererElement.setAttribute( QStringLiteral(
"placement" ), static_cast< int >( mPlacement ) );
190 rendererElement.setAttribute( QStringLiteral(
"maxLabelScaleDenominator" ), QString::number(
mMinLabelScale ) );
191 rendererElement.setAttribute( QStringLiteral(
"tolerance" ), QString::number(
mTolerance ) );
197 QDomElement embeddedRendererElem =
mRenderer->save( doc, context );
198 rendererElement.appendChild( embeddedRendererElem );
203 rendererElement.appendChild( centerSymbolElem );
211 QDomElement
orderBy = doc.createElement( QStringLiteral(
"orderby" ) );
213 rendererElement.appendChild( orderBy );
215 rendererElement.setAttribute( QStringLiteral(
"enableorderby" ), (
mOrderByEnabled ? QStringLiteral(
"1" ) : QStringLiteral(
"0" ) ) );
217 return rendererElement;
224 attr.unite( mCenterSymbol->usedAttributes( context ) );
230 mCenterSymbol.reset( symbol );
233 void QgsPointDisplacementRenderer::calculateSymbolAndLabelPositions(
QgsSymbolRenderContext &symbolContext, QPointF centerPoint,
int nPosition,
234 double symbolDiagonal, QList<QPointF> &symbolPositions, QList<QPointF> &labelShifts,
double &circleRadius,
double &gridRadius,
235 int &gridSize )
const 237 symbolPositions.clear();
244 else if ( nPosition == 1 )
246 symbolPositions.append( centerPoint );
247 labelShifts.append( QPointF( symbolDiagonal / 2.0, -symbolDiagonal / 2.0 ) );
251 double circleAdditionPainterUnits = symbolContext.
outputLineWidth( mCircleRadiusAddition );
253 switch ( mPlacement )
257 double minDiameterToFitSymbols = nPosition * symbolDiagonal / ( 2.0 * M_PI );
258 double radius = std::max( symbolDiagonal / 2, minDiameterToFitSymbols ) + circleAdditionPainterUnits;
260 double fullPerimeter = 2 * M_PI;
261 double angleStep = fullPerimeter / nPosition;
262 for (
double currentAngle = 0.0; currentAngle < fullPerimeter; currentAngle += angleStep )
264 double sinusCurrentAngle = std::sin( currentAngle );
265 double cosinusCurrentAngle = std::cos( currentAngle );
266 QPointF positionShift( radius * sinusCurrentAngle, radius * cosinusCurrentAngle );
267 QPointF labelShift( ( radius + symbolDiagonal / 2 ) * sinusCurrentAngle, ( radius + symbolDiagonal / 2 ) * cosinusCurrentAngle );
268 symbolPositions.append( centerPoint + positionShift );
269 labelShifts.append( labelShift );
272 circleRadius = radius;
278 mCenterSymbol->sizeUnit(), mCenterSymbol->sizeMapUnitScale() );
280 int pointsRemaining = nPosition;
282 double firstRingRadius = centerDiagonal / 2.0 + symbolDiagonal / 2.0;
283 while ( pointsRemaining > 0 )
285 double radiusCurrentRing = std::max( firstRingRadius + ( ringNumber - 1 ) * symbolDiagonal + ringNumber * circleAdditionPainterUnits, 0.0 );
286 int maxPointsCurrentRing = std::max( std::floor( 2 * M_PI * radiusCurrentRing / symbolDiagonal ), 1.0 );
287 int actualPointsCurrentRing = std::min( maxPointsCurrentRing, pointsRemaining );
289 double angleStep = 2 * M_PI / actualPointsCurrentRing;
290 double currentAngle = 0.0;
291 for (
int i = 0; i < actualPointsCurrentRing; ++i )
293 double sinusCurrentAngle = std::sin( currentAngle );
294 double cosinusCurrentAngle = std::cos( currentAngle );
295 QPointF positionShift( radiusCurrentRing * sinusCurrentAngle, radiusCurrentRing * cosinusCurrentAngle );
296 QPointF labelShift( ( radiusCurrentRing + symbolDiagonal / 2 ) * sinusCurrentAngle, ( radiusCurrentRing + symbolDiagonal / 2 ) * cosinusCurrentAngle );
297 symbolPositions.append( centerPoint + positionShift );
298 labelShifts.append( labelShift );
299 currentAngle += angleStep;
302 pointsRemaining -= actualPointsCurrentRing;
304 circleRadius = radiusCurrentRing;
311 mCenterSymbol->sizeUnit(), mCenterSymbol->sizeMapUnitScale() );
312 int pointsRemaining = nPosition;
313 gridSize = std::ceil( std::sqrt( pointsRemaining ) );
314 if ( pointsRemaining - std::pow( gridSize - 1, 2 ) < gridSize )
316 double originalPointRadius = ( ( centerDiagonal / 2.0 + symbolDiagonal / 2.0 ) + symbolDiagonal ) / 2;
317 double userPointRadius = originalPointRadius + circleAdditionPainterUnits;
320 while ( pointsRemaining > 0 )
322 for (
int xIndex = 0; xIndex < gridSize && pointsRemaining > 0; ++xIndex )
324 QPointF positionShift( userPointRadius * xIndex, userPointRadius * yIndex );
325 QPointF labelShift( ( userPointRadius + symbolDiagonal / 2 ) * xIndex, ( userPointRadius + symbolDiagonal / 2 ) * yIndex );
326 symbolPositions.append( centerPoint + positionShift );
327 labelShifts.append( labelShift );
333 centralizeGrid( symbolPositions, userPointRadius, gridSize );
334 centralizeGrid( labelShifts, userPointRadius, gridSize );
335 gridRadius = userPointRadius;
341 void QgsPointDisplacementRenderer::centralizeGrid( QList<QPointF> &pointSymbolPositions,
double radius,
int size )
const 343 double shiftAmount = -radius * ( size - 1.0 ) / 2.0;
344 QPointF centralShift( shiftAmount, shiftAmount );
345 for (
int i = 0; i < pointSymbolPositions.size(); ++i )
347 pointSymbolPositions[i] += centralShift;
352 QList<QPointF> pointSymbolPositions,
int nSymbols )
355 if ( nSymbols < 2 || !p )
360 QPen gridPen( mCircleColor );
362 p->setPen( gridPen );
364 for (
int i = 0; i < pointSymbolPositions.size(); ++i )
366 if ( i + 1 < pointSymbolPositions.size() && 0 != ( i + 1 ) % gridSizeUnits )
368 QLineF gridLineRow( pointSymbolPositions[i], pointSymbolPositions[i + 1] );
369 p->drawLine( gridLineRow );
372 if ( i + gridSizeUnits < pointSymbolPositions.size() )
374 QLineF gridLineColumn( pointSymbolPositions[i], pointSymbolPositions[i + gridSizeUnits] );
375 p->drawLine( gridLineColumn );
380 void QgsPointDisplacementRenderer::drawCircle(
double radiusPainterUnits,
QgsSymbolRenderContext &context, QPointF centerPoint,
int nSymbols )
383 if ( nSymbols < 2 || !p )
389 QPen circlePen( mCircleColor );
391 p->setPen( circlePen );
392 p->drawArc( QRectF( centerPoint.x() - radiusPainterUnits, centerPoint.y() - radiusPainterUnits, 2 * radiusPainterUnits, 2 * radiusPainterUnits ), 0, 5760 );
397 QList<QPointF>::const_iterator symbolPosIt = symbolPositions.constBegin();
398 ClusteredGroup::const_iterator groupIt = group.constBegin();
399 for ( ; symbolPosIt != symbolPositions.constEnd() && groupIt != group.constEnd();
400 ++symbolPosIt, ++groupIt )
403 groupIt->symbol()->startRender( context );
404 groupIt->symbol()->renderPoint( *symbolPosIt, &( groupIt->feature ), context, -1, groupIt->isSelected );
405 groupIt->symbol()->stopRender( context );
411 if ( renderer->
type() == QLatin1String(
"pointDisplacement" ) )
415 else if ( renderer->
type() == QLatin1String(
"singleSymbol" ) ||
416 renderer->
type() == QLatin1String(
"categorizedSymbol" ) ||
417 renderer->
type() == QLatin1String(
"graduatedSymbol" ) ||
418 renderer->
type() == QLatin1String(
"RuleRenderer" ) )
422 return pointRenderer;
424 else if ( renderer->
type() == QLatin1String(
"pointCluster" ) )
433 if ( const_cast< QgsPointClusterRenderer * >( clusterRenderer )->clusterSymbol() )
434 pointRenderer->
setCenterSymbol( const_cast< QgsPointClusterRenderer * >( clusterRenderer )->clusterSymbol()->clone() );
435 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
Get 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.
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. ...
QgsMarkerSymbol * symbol() const
Base symbol for rendering feature.
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
Return 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
Return 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.