33   , mCircleColor( QColor( 125, 125, 125 ) )
 
   62 void QgsPointDisplacementRenderer::drawGroup( QPointF centerPoint, 
QgsRenderContext &context, 
const ClusteredGroup &group )
 
   67   QVector<double> diagonals( group.size() );
 
   68   double currentDiagonal;
 
   70   int groupPosition = 0;
 
   71   for ( 
const GroupedFeature &feature : group )
 
   75       currentDiagonal = M_SQRT2 * symbol->size( context );
 
   76       diagonals[groupPosition] = currentDiagonal;
 
   77       diagonal = std::max( diagonal, currentDiagonal );
 
   82       diagonals[groupPosition] = 0.0;
 
   89   QList<QPointF> symbolPositions;
 
   90   QList<QPointF> labelPositions;
 
   91   double circleRadius = -1.0;
 
   92   double gridRadius = -1.0;
 
   95   calculateSymbolAndLabelPositions( symbolContext, centerPoint, group.size(), diagonal, symbolPositions, labelPositions, circleRadius, gridRadius, gridSize, diagonals );
 
   98   if ( mCircleColor.isValid() && mCircleColor.alpha() > 0 )
 
  101     if ( circleRadius > 0 )
 
  102       drawCircle( circleRadius, symbolContext, centerPoint, group.size() );
 
  105       drawGrid( gridSize, symbolContext, symbolPositions, group.size() );
 
  108   if ( group.size() > 1 )
 
  111     QgsFeature firstFeature = group.at( 0 ).feature;
 
  114       mCenterSymbol->renderPoint( centerPoint, &firstFeature, context, -1, 
false );
 
  119       context.
painter()->drawRect( QRectF( centerPoint.x() - rectSize, centerPoint.y() - rectSize, rectSize * 2, rectSize * 2 ) );
 
  124   drawSymbols( group, context, symbolPositions );
 
  128     drawLabels( centerPoint, symbolContext, labelPositions, group );
 
  137     mCenterSymbol->startRender( context, fields );
 
  148     mCenterSymbol->stopRender( context );
 
  159     labelFont.fromString( symbologyElem.attribute( QStringLiteral( 
"labelFont" ), QString() ) );
 
  162   r->
setPlacement( 
static_cast< Placement >( symbologyElem.attribute( QStringLiteral( 
"placement" ), QStringLiteral( 
"0" ) ).toInt() ) );
 
  163   r->
setCircleWidth( symbologyElem.attribute( QStringLiteral( 
"circleWidth" ), QStringLiteral( 
"0.4" ) ).toDouble() );
 
  166   r->
setCircleRadiusAddition( symbologyElem.attribute( QStringLiteral( 
"circleRadiusAddition" ), QStringLiteral( 
"0.0" ) ).toDouble() );
 
  167   r->
setLabelDistanceFactor( symbologyElem.attribute( QStringLiteral( 
"labelDistanceFactor" ), QStringLiteral( 
"0.5" ) ).toDouble() );
 
  168   r->
setMinimumLabelScale( symbologyElem.attribute( QStringLiteral( 
"maxLabelScaleDenominator" ), QStringLiteral( 
"-1" ) ).toDouble() );
 
  169   r->
setTolerance( symbologyElem.attribute( QStringLiteral( 
"tolerance" ), QStringLiteral( 
"0.00001" ) ).toDouble() );
 
  174   QDomElement embeddedRendererElem = symbologyElem.firstChildElement( QStringLiteral( 
"renderer-v2" ) );
 
  175   if ( !embeddedRendererElem.isNull() )
 
  181   QDomElement centerSymbolElem = symbologyElem.firstChildElement( QStringLiteral( 
"symbol" ) );
 
  182   if ( !centerSymbolElem.isNull() )
 
  184     r->
setCenterSymbol( QgsSymbolLayerUtils::loadSymbol<QgsMarkerSymbol>( centerSymbolElem, context ) );
 
  191   return mCenterSymbol.get();
 
  197   rendererElement.setAttribute( QStringLiteral( 
"forceraster" ), ( 
mForceRaster ? QStringLiteral( 
"1" ) : QStringLiteral( 
"0" ) ) );
 
  198   rendererElement.setAttribute( QStringLiteral( 
"type" ), QStringLiteral( 
"pointDisplacement" ) );
 
  199   rendererElement.setAttribute( QStringLiteral( 
"labelAttributeName" ), 
mLabelAttributeName );
 
  201   rendererElement.setAttribute( QStringLiteral( 
"circleWidth" ), QString::number( mCircleWidth ) );
 
  204   rendererElement.setAttribute( QStringLiteral( 
"circleRadiusAddition" ), QString::number( mCircleRadiusAddition ) );
 
  205   rendererElement.setAttribute( QStringLiteral( 
"labelDistanceFactor" ), QString::number( mLabelDistanceFactor ) );
 
  206   rendererElement.setAttribute( QStringLiteral( 
"placement" ), 
static_cast< int >( mPlacement ) );
 
  207   rendererElement.setAttribute( QStringLiteral( 
"maxLabelScaleDenominator" ), QString::number( 
mMinLabelScale ) );
 
  208   rendererElement.setAttribute( QStringLiteral( 
"tolerance" ), QString::number( 
mTolerance ) );
 
  214     QDomElement embeddedRendererElem = 
mRenderer->save( doc, context );
 
  215     rendererElement.appendChild( embeddedRendererElem );
 
  220     rendererElement.appendChild( centerSymbolElem );
 
  228     QDomElement 
orderBy = doc.createElement( QStringLiteral( 
"orderby" ) );
 
  230     rendererElement.appendChild( 
orderBy );
 
  232   rendererElement.setAttribute( QStringLiteral( 
"enableorderby" ), ( 
mOrderByEnabled ? QStringLiteral( 
"1" ) : QStringLiteral( 
"0" ) ) );
 
  234   return rendererElement;
 
  241     attr.unite( mCenterSymbol->usedAttributes( context ) );
 
  262   mCenterSymbol.reset( symbol );
 
  265 void QgsPointDisplacementRenderer::calculateSymbolAndLabelPositions( 
QgsSymbolRenderContext &symbolContext, QPointF centerPoint, 
int nPosition,
 
  266     double symbolDiagonal, QList<QPointF> &symbolPositions, QList<QPointF> &labelShifts, 
double &circleRadius, 
double &gridRadius,
 
  267     int &gridSize, QVector<double> &diagonals )
 const 
  269   symbolPositions.clear();
 
  276   else if ( nPosition == 1 ) 
 
  278     const double side = std::sqrt( std::pow( symbolDiagonal, 2 ) / 2.0 );
 
  279     symbolPositions.append( centerPoint );
 
  280     labelShifts.append( QPointF( side * mLabelDistanceFactor, -side * mLabelDistanceFactor ) );
 
  286   switch ( mPlacement )
 
  290       const double minDiameterToFitSymbols = nPosition * symbolDiagonal / ( 2.0 * M_PI );
 
  291       const double radius = std::max( symbolDiagonal / 2, minDiameterToFitSymbols ) + circleAdditionPainterUnits;
 
  293       const double angleStep = 2 * M_PI / nPosition;
 
  294       double currentAngle = 0.0;
 
  295       for ( 
int featureIndex = 0; featureIndex < nPosition; currentAngle += angleStep, featureIndex++ )
 
  297         const double sinusCurrentAngle = std::sin( currentAngle );
 
  298         const double cosinusCurrentAngle = std::cos( currentAngle );
 
  299         const QPointF positionShift( radius * sinusCurrentAngle, radius * cosinusCurrentAngle );
 
  301         const QPointF labelShift( ( radius + diagonals.at( featureIndex ) * mLabelDistanceFactor ) * sinusCurrentAngle, ( radius + diagonals.at( featureIndex ) * mLabelDistanceFactor ) * cosinusCurrentAngle );
 
  302         symbolPositions.append( centerPoint + positionShift );
 
  303         labelShifts.append( labelShift );
 
  305       circleRadius = radius;
 
  310       double centerDiagonal = mCenterSymbol->size( symbolContext.
renderContext() ) * M_SQRT2;
 
  312       int pointsRemaining = nPosition;
 
  314       double firstRingRadius = centerDiagonal / 2.0 + symbolDiagonal / 2.0;
 
  315       int featureIndex = 0;
 
  316       while ( pointsRemaining > 0 )
 
  318         double radiusCurrentRing = std::max( firstRingRadius + ( ringNumber - 1 ) * symbolDiagonal + ringNumber * circleAdditionPainterUnits, 0.0 );
 
  319         int maxPointsCurrentRing = std::max( std::floor( 2 * M_PI * radiusCurrentRing / symbolDiagonal ), 1.0 );
 
  320         int actualPointsCurrentRing = std::min( maxPointsCurrentRing, pointsRemaining );
 
  322         double angleStep = 2 * M_PI / actualPointsCurrentRing;
 
  323         double currentAngle = 0.0;
 
  324         for ( 
int i = 0; i < actualPointsCurrentRing; ++i )
 
  326           double sinusCurrentAngle = std::sin( currentAngle );
 
  327           double cosinusCurrentAngle = std::cos( currentAngle );
 
  328           QPointF positionShift( radiusCurrentRing * sinusCurrentAngle, radiusCurrentRing * cosinusCurrentAngle );
 
  329           QPointF labelShift( ( radiusCurrentRing + diagonals.at( featureIndex ) * mLabelDistanceFactor ) * sinusCurrentAngle, ( radiusCurrentRing + diagonals.at( featureIndex ) * mLabelDistanceFactor ) * cosinusCurrentAngle );
 
  330           symbolPositions.append( centerPoint + positionShift );
 
  331           labelShifts.append( labelShift );
 
  332           currentAngle += angleStep;
 
  336         pointsRemaining -= actualPointsCurrentRing;
 
  338         circleRadius = radiusCurrentRing;
 
  344       double centerDiagonal = mCenterSymbol->size( symbolContext.
renderContext() ) * M_SQRT2;
 
  345       int pointsRemaining = nPosition;
 
  346       gridSize = std::ceil( std::sqrt( pointsRemaining ) );
 
  347       if ( pointsRemaining - std::pow( gridSize - 1, 2 ) < gridSize )
 
  349       double originalPointRadius = ( ( centerDiagonal / 2.0 + symbolDiagonal / 2.0 ) + symbolDiagonal ) / 2;
 
  350       double userPointRadius =  originalPointRadius + circleAdditionPainterUnits;
 
  353       while ( pointsRemaining > 0 )
 
  355         for ( 
int xIndex = 0; xIndex < gridSize && pointsRemaining > 0; ++xIndex )
 
  357           QPointF positionShift( userPointRadius * xIndex, userPointRadius * yIndex );
 
  358           symbolPositions.append( centerPoint + positionShift );
 
  364       centralizeGrid( symbolPositions, userPointRadius, gridSize );
 
  369       for ( 
int symbolIndex = 0; symbolIndex < symbolPositions.size(); ++symbolIndex )
 
  371         if ( symbolPositions.at( symbolIndex ).x() < centerPoint.x() )
 
  380         if ( symbolPositions.at( symbolIndex ).y() < centerPoint.y() )
 
  389         side = std::sqrt( std::pow( diagonals.at( symbolIndex ), 2 ) / 2.0 );
 
  390         QPointF labelShift( ( side * mLabelDistanceFactor * xFactor ), ( -side * mLabelDistanceFactor * yFactor ) );
 
  391         labelShifts.append( symbolPositions.at( symbolIndex ) - centerPoint + labelShift );
 
  394       gridRadius = userPointRadius;
 
  400 void QgsPointDisplacementRenderer::centralizeGrid( QList<QPointF> &pointSymbolPositions, 
double radius, 
int size )
 const 
  402   double shiftAmount = -radius * ( size - 1.0 ) / 2.0;
 
  403   QPointF centralShift( shiftAmount, shiftAmount );
 
  404   for ( 
int i = 0; i < pointSymbolPositions.size(); ++i )
 
  406     pointSymbolPositions[i] += centralShift;
 
  411     QList<QPointF> pointSymbolPositions, 
int nSymbols )
 
  414   if ( nSymbols < 2 || !p ) 
 
  419   QPen gridPen( mCircleColor );
 
  421   p->setPen( gridPen );
 
  423   for ( 
int i = 0; i < pointSymbolPositions.size(); ++i )
 
  425     if ( i + 1 < pointSymbolPositions.size() && 0 != ( i + 1 ) % gridSizeUnits )
 
  427       QLineF gridLineRow( pointSymbolPositions[i], pointSymbolPositions[i + 1] );
 
  428       p->drawLine( gridLineRow );
 
  431     if ( i + gridSizeUnits < pointSymbolPositions.size() )
 
  433       QLineF gridLineColumn( pointSymbolPositions[i], pointSymbolPositions[i + gridSizeUnits] );
 
  434       p->drawLine( gridLineColumn );
 
  439 void QgsPointDisplacementRenderer::drawCircle( 
double radiusPainterUnits, 
QgsSymbolRenderContext &context, QPointF centerPoint, 
int nSymbols )
 
  442   if ( nSymbols < 2 || !p ) 
 
  448   QPen circlePen( mCircleColor );
 
  450   p->setPen( circlePen );
 
  451   p->drawArc( QRectF( centerPoint.x() - radiusPainterUnits, centerPoint.y() - radiusPainterUnits, 2 * radiusPainterUnits, 2 * radiusPainterUnits ), 0, 5760 );
 
  454 void QgsPointDisplacementRenderer::drawSymbols( 
const ClusteredGroup &group, 
QgsRenderContext &context, 
const QList<QPointF> &symbolPositions )
 
  456   QList<QPointF>::const_iterator symbolPosIt = symbolPositions.constBegin();
 
  457   ClusteredGroup::const_iterator groupIt = group.constBegin();
 
  458   for ( ; symbolPosIt != symbolPositions.constEnd() && groupIt != group.constEnd();
 
  459         ++symbolPosIt, ++groupIt )
 
  462     groupIt->symbol()->startRender( context );
 
  463     groupIt->symbol()->renderPoint( *symbolPosIt, &( groupIt->feature ), context, -1, groupIt->isSelected );
 
  470         handler->handleRenderedFeature( groupIt->feature, bounds, featureContext );
 
  472     groupIt->symbol()->stopRender( context );
 
  478   if ( renderer->
type() == QLatin1String( 
"pointDisplacement" ) )
 
  482   else if ( renderer->
type() == QLatin1String( 
"singleSymbol" ) ||
 
  483             renderer->
type() == QLatin1String( 
"categorizedSymbol" ) ||
 
  484             renderer->
type() == QLatin1String( 
"graduatedSymbol" ) ||
 
  485             renderer->
type() == QLatin1String( 
"RuleRenderer" ) )
 
  489     return pointRenderer;
 
  491   else if ( renderer->
type() == QLatin1String( 
"pointCluster" ) )
 
  502     return pointRenderer;
 
void setFeature(const QgsFeature &feature)
Convenience function for setting a feature for the context.
virtual QgsFeatureRenderer * clone() const =0
Create a deep copy of this renderer.
QgsPaintEffect * mPaintEffect
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
QgsFeatureRequest::OrderBy mOrderBy
QgsFeatureRequest::OrderBy orderBy() const
Gets the order in which features shall be processed by this renderer.
void CORE_EXPORT save(QDomElement &elem) const
Serialize to XML.
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
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.
A geometry is the spatial representation of a feature.
static QgsGeometry fromRect(const QgsRectangle &rect) SIP_HOLDGIL
Creates a new geometry from a QgsRectangle.
A marker symbol type, for rendering Point and MultiPoint geometries.
QgsMarkerSymbol * clone() const override
Returns a deep copy of this symbol.
static bool isDefaultStack(QgsPaintEffect *effect)
Tests whether a paint effect matches the default effects stack.
virtual bool saveProperties(QDomDocument &doc, QDomElement &element) const
Saves the current state of the effect to a DOM element.
A renderer that automatically clusters points with the same geographic position.
QgsMarkerSymbol * clusterSymbol()
Returns the symbol used for rendering clustered groups (but not ownership of the symbol).
A renderer that automatically displaces points with the same geographic location.
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
store renderer info to 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).
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...
An abstract base class for distance based point renderers (e.g., clusterer and displacement renderers...
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.
QgsUnitTypes::RenderUnit toleranceUnit() const
Returns the units for the tolerance distance.
void setLabelFont(const QFont &font)
Sets the font used for labeling points.
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.
const QgsMapUnitScale & toleranceMapUnitScale() const
Returns the map unit scale object for the distance tolerance.
void startRender(QgsRenderContext &context, const QgsFields &fields) override
Must be called when a new render cycle is started.
void setEmbeddedRenderer(QgsFeatureRenderer *r) override
Sets an embedded renderer (subrenderer) for this feature renderer.
void setToleranceUnit(QgsUnitTypes::RenderUnit unit)
Sets the units for the tolerance distance.
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 drawLabels(QPointF centerPoint, QgsSymbolRenderContext &context, const QList< QPointF > &labelShifts, const ClusteredGroup &group)
Renders the labels for a group.
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.
QgsUnitTypes::RenderUnit mToleranceUnit
Unit for distance tolerance.
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.
bool accept(QgsStyleEntityVisitorInterface *visitor) const override
Accepts the specified symbology visitor, causing it to visit all symbols associated with the renderer...
The class is used as a container of context for various read/write operations on other objects.
A rectangle specified with double values.
Contains information about the context of a rendering operation.
bool hasRenderedFeatureHandlers() const
Returns true if the context has any rendered feature handlers.
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.
double convertToPainterUnits(double size, QgsUnitTypes::RenderUnit unit, const QgsMapUnitScale &scale=QgsMapUnitScale()) const
Converts a size from the specified units to painter units (pixels).
An interface for classes which provider custom handlers for features rendered as part of a map render...
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 QColor decodeColor(const QString &str)
static QgsMapUnitScale decodeMapUnitScale(const QString &str)
static QString encodeColor(const QColor &color)
static QDomElement saveSymbol(const QString &symbolName, const QgsSymbol *symbol, QDomDocument &doc, const QgsReadWriteContext &context)
Writes a symbol definition to XML.
QgsRenderContext & renderContext()
Returns a reference to the context's render context.
static Q_INVOKABLE QString encodeUnit(QgsUnitTypes::DistanceUnit unit)
Encodes a distance unit to a string.
static Q_INVOKABLE QgsUnitTypes::RenderUnit decodeRenderUnit(const QString &string, bool *ok=nullptr)
Decodes a render unit from a string.
@ RenderMillimeters
Millimeters.
#define RENDERER_TAG_NAME
Contains information relating to the style entity currently being visited.