33  , mCircleColor( QColor( 125, 125, 125 ) )
 
 
   62void QgsPointDisplacementRenderer::drawGroup( QPointF centerPoint, 
QgsRenderContext &context, 
const ClusteredGroup &group )
 const 
   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 )
 
  100    switch ( mPlacement )
 
  104        drawCircle( circleRadius, symbolContext, centerPoint, group.size() );
 
  107        drawGrid( gridSize, symbolContext, symbolPositions, group.size() );
 
  112  if ( group.size() > 1 )
 
  115    const QgsFeature firstFeature = group.at( 0 ).feature;
 
  118      mCenterSymbol->renderPoint( centerPoint, &firstFeature, context, -1, 
false );
 
  123      context.
painter()->drawRect( QRectF( centerPoint.x() - rectSize, centerPoint.y() - rectSize, rectSize * 2, rectSize * 2 ) );
 
  128  drawSymbols( group, context, symbolPositions );
 
  132    drawLabels( centerPoint, symbolContext, labelPositions, group );
 
  141    mCenterSymbol->startRender( context, fields );
 
 
  152    mCenterSymbol->stopRender( context );
 
 
  163    labelFont.fromString( symbologyElem.attribute( QStringLiteral( 
"labelFont" ), QString() ) );
 
  166  r->
setPlacement( 
static_cast< Placement >( symbologyElem.attribute( QStringLiteral( 
"placement" ), QStringLiteral( 
"0" ) ).toInt() ) );
 
  167  r->
setCircleWidth( symbologyElem.attribute( QStringLiteral( 
"circleWidth" ), QStringLiteral( 
"0.4" ) ).toDouble() );
 
  170  r->
setCircleRadiusAddition( symbologyElem.attribute( QStringLiteral( 
"circleRadiusAddition" ), QStringLiteral( 
"0.0" ) ).toDouble() );
 
  171  r->
setLabelDistanceFactor( symbologyElem.attribute( QStringLiteral( 
"labelDistanceFactor" ), QStringLiteral( 
"0.5" ) ).toDouble() );
 
  172  r->
setMinimumLabelScale( symbologyElem.attribute( QStringLiteral( 
"maxLabelScaleDenominator" ), QStringLiteral( 
"-1" ) ).toDouble() );
 
  173  r->
setTolerance( symbologyElem.attribute( QStringLiteral( 
"tolerance" ), QStringLiteral( 
"0.00001" ) ).toDouble() );
 
  178  QDomElement embeddedRendererElem = symbologyElem.firstChildElement( QStringLiteral( 
"renderer-v2" ) );
 
  179  if ( !embeddedRendererElem.isNull() )
 
  185  const QDomElement centerSymbolElem = symbologyElem.firstChildElement( QStringLiteral( 
"symbol" ) );
 
  186  if ( !centerSymbolElem.isNull() )
 
  188    r->
setCenterSymbol( QgsSymbolLayerUtils::loadSymbol<QgsMarkerSymbol>( centerSymbolElem, context ) );
 
 
  195  return mCenterSymbol.get();
 
 
  201  rendererElement.setAttribute( QStringLiteral( 
"type" ), QStringLiteral( 
"pointDisplacement" ) );
 
  202  rendererElement.setAttribute( QStringLiteral( 
"labelAttributeName" ), 
mLabelAttributeName );
 
  204  rendererElement.setAttribute( QStringLiteral( 
"circleWidth" ), QString::number( mCircleWidth ) );
 
  207  rendererElement.setAttribute( QStringLiteral( 
"circleRadiusAddition" ), QString::number( mCircleRadiusAddition ) );
 
  208  rendererElement.setAttribute( QStringLiteral( 
"labelDistanceFactor" ), QString::number( mLabelDistanceFactor ) );
 
  209  rendererElement.setAttribute( QStringLiteral( 
"placement" ), 
static_cast< int >( mPlacement ) );
 
  210  rendererElement.setAttribute( QStringLiteral( 
"maxLabelScaleDenominator" ), QString::number( 
mMinLabelScale ) );
 
  211  rendererElement.setAttribute( QStringLiteral( 
"tolerance" ), QString::number( 
mTolerance ) );
 
  217    const QDomElement embeddedRendererElem = 
mRenderer->save( doc, context );
 
  218    rendererElement.appendChild( embeddedRendererElem );
 
  223    rendererElement.appendChild( centerSymbolElem );
 
  228  return rendererElement;
 
 
  235    attr.unite( mCenterSymbol->usedAttributes( context ) );
 
 
  256  mCenterSymbol.reset( symbol );
 
 
  259void QgsPointDisplacementRenderer::calculateSymbolAndLabelPositions( 
QgsSymbolRenderContext &symbolContext, QPointF centerPoint, 
int nPosition,
 
  260    double symbolDiagonal, QList<QPointF> &symbolPositions, QList<QPointF> &labelShifts, 
double &circleRadius, 
double &gridRadius,
 
  261    int &gridSize, QVector<double> &diagonals )
 const 
  263  symbolPositions.clear();
 
  270  else if ( nPosition == 1 ) 
 
  272    const double side = std::sqrt( std::pow( symbolDiagonal, 2 ) / 2.0 );
 
  273    symbolPositions.append( centerPoint );
 
  274    labelShifts.append( QPointF( side * mLabelDistanceFactor, -side * mLabelDistanceFactor ) );
 
  280  switch ( mPlacement )
 
  284      const double minDiameterToFitSymbols = nPosition * symbolDiagonal / ( 2.0 * M_PI );
 
  285      const double radius = std::max( symbolDiagonal / 2, minDiameterToFitSymbols ) + circleAdditionPainterUnits;
 
  287      const double angleStep = 2 * M_PI / nPosition;
 
  288      double currentAngle = 0.0;
 
  289      for ( 
int featureIndex = 0; featureIndex < nPosition; currentAngle += angleStep, featureIndex++ )
 
  291        const double sinusCurrentAngle = std::sin( currentAngle );
 
  292        const double cosinusCurrentAngle = std::cos( currentAngle );
 
  293        const QPointF positionShift( radius * sinusCurrentAngle, radius * cosinusCurrentAngle );
 
  295        const QPointF labelShift( ( radius + diagonals.at( featureIndex ) * mLabelDistanceFactor ) * sinusCurrentAngle, ( radius + diagonals.at( featureIndex ) * mLabelDistanceFactor ) * cosinusCurrentAngle );
 
  296        symbolPositions.append( centerPoint + positionShift );
 
  297        labelShifts.append( labelShift );
 
  299      circleRadius = radius;
 
  304      const double centerDiagonal = mCenterSymbol->size( symbolContext.
renderContext() ) * M_SQRT2;
 
  306      int pointsRemaining = nPosition;
 
  308      const double firstRingRadius = centerDiagonal / 2.0 + symbolDiagonal / 2.0;
 
  309      int featureIndex = 0;
 
  310      while ( pointsRemaining > 0 )
 
  312        const double radiusCurrentRing = std::max( firstRingRadius + ( ringNumber - 1 ) * symbolDiagonal + ringNumber * circleAdditionPainterUnits, 0.0 );
 
  313        const int maxPointsCurrentRing = std::max( std::floor( 2 * M_PI * radiusCurrentRing / symbolDiagonal ), 1.0 );
 
  314        const int actualPointsCurrentRing = std::min( maxPointsCurrentRing, pointsRemaining );
 
  316        const double angleStep = 2 * M_PI / actualPointsCurrentRing;
 
  317        double currentAngle = 0.0;
 
  318        for ( 
int i = 0; i < actualPointsCurrentRing; ++i )
 
  320          const double sinusCurrentAngle = std::sin( currentAngle );
 
  321          const double cosinusCurrentAngle = std::cos( currentAngle );
 
  322          const QPointF positionShift( radiusCurrentRing * sinusCurrentAngle, radiusCurrentRing * cosinusCurrentAngle );
 
  323          const QPointF labelShift( ( radiusCurrentRing + diagonals.at( featureIndex ) * mLabelDistanceFactor ) * sinusCurrentAngle, ( radiusCurrentRing + diagonals.at( featureIndex ) * mLabelDistanceFactor ) * cosinusCurrentAngle );
 
  324          symbolPositions.append( centerPoint + positionShift );
 
  325          labelShifts.append( labelShift );
 
  326          currentAngle += angleStep;
 
  330        pointsRemaining -= actualPointsCurrentRing;
 
  332        circleRadius = radiusCurrentRing;
 
  338      const double centerDiagonal = mCenterSymbol->size( symbolContext.
renderContext() ) * M_SQRT2;
 
  339      int pointsRemaining = nPosition;
 
  340      gridSize = std::ceil( std::sqrt( pointsRemaining ) );
 
  341      if ( pointsRemaining - std::pow( gridSize - 1, 2 ) < gridSize )
 
  343      const double originalPointRadius = ( ( centerDiagonal / 2.0 + symbolDiagonal / 2.0 ) + symbolDiagonal ) / 2;
 
  344      const double userPointRadius =  originalPointRadius + circleAdditionPainterUnits;
 
  347      while ( pointsRemaining > 0 )
 
  349        for ( 
int xIndex = 0; xIndex < gridSize && pointsRemaining > 0; ++xIndex )
 
  351          const QPointF positionShift( userPointRadius * xIndex, userPointRadius * yIndex );
 
  352          symbolPositions.append( centerPoint + positionShift );
 
  358      centralizeGrid( symbolPositions, userPointRadius, gridSize );
 
  363      for ( 
int symbolIndex = 0; symbolIndex < symbolPositions.size(); ++symbolIndex )
 
  365        if ( symbolPositions.at( symbolIndex ).x() < centerPoint.x() )
 
  374        if ( symbolPositions.at( symbolIndex ).y() < centerPoint.y() )
 
  383        side = std::sqrt( std::pow( diagonals.at( symbolIndex ), 2 ) / 2.0 );
 
  384        const QPointF labelShift( ( side * mLabelDistanceFactor * xFactor ), ( -side * mLabelDistanceFactor * yFactor ) );
 
  385        labelShifts.append( symbolPositions.at( symbolIndex ) - centerPoint + labelShift );
 
  388      gridRadius = userPointRadius;
 
  394void QgsPointDisplacementRenderer::centralizeGrid( QList<QPointF> &pointSymbolPositions, 
double radius, 
int size )
 const 
  396  const double shiftAmount = -radius * ( size - 1.0 ) / 2.0;
 
  397  const QPointF centralShift( shiftAmount, shiftAmount );
 
  398  for ( 
int i = 0; i < pointSymbolPositions.size(); ++i )
 
  400    pointSymbolPositions[i] += centralShift;
 
  405    QList<QPointF> pointSymbolPositions, 
int nSymbols )
 const 
  408  if ( nSymbols < 2 || !p ) 
 
  413  QPen gridPen( mCircleColor );
 
  415  p->setPen( gridPen );
 
  417  for ( 
int i = 0; i < pointSymbolPositions.size(); ++i )
 
  419    if ( i + 1 < pointSymbolPositions.size() && 0 != ( i + 1 ) % gridSizeUnits )
 
  421      const QLineF gridLineRow( pointSymbolPositions[i], pointSymbolPositions[i + 1] );
 
  422      p->drawLine( gridLineRow );
 
  425    if ( i + gridSizeUnits < pointSymbolPositions.size() )
 
  427      const QLineF gridLineColumn( pointSymbolPositions[i], pointSymbolPositions[i + gridSizeUnits] );
 
  428      p->drawLine( gridLineColumn );
 
  433void QgsPointDisplacementRenderer::drawCircle( 
double radiusPainterUnits, 
QgsSymbolRenderContext &context, QPointF centerPoint, 
int nSymbols )
 const 
  436  if ( nSymbols < 2 || !p ) 
 
  442  QPen circlePen( mCircleColor );
 
  444  p->setPen( circlePen );
 
  445  p->drawArc( QRectF( centerPoint.x() - radiusPainterUnits, centerPoint.y() - radiusPainterUnits, 2 * radiusPainterUnits, 2 * radiusPainterUnits ), 0, 5760 );
 
  448void QgsPointDisplacementRenderer::drawSymbols( 
const ClusteredGroup &group, 
QgsRenderContext &context, 
const QList<QPointF> &symbolPositions )
 const 
  450  QList<QPointF>::const_iterator symbolPosIt = symbolPositions.constBegin();
 
  451  ClusteredGroup::const_iterator groupIt = group.constBegin();
 
  452  for ( ; symbolPosIt != symbolPositions.constEnd() && groupIt != group.constEnd();
 
  453        ++symbolPosIt, ++groupIt )
 
  456    groupIt->symbol()->startRender( context );
 
  457    groupIt->symbol()->renderPoint( *symbolPosIt, &( groupIt->feature ), context, -1, groupIt->isSelected );
 
  464        handler->handleRenderedFeature( groupIt->feature, bounds, featureContext );
 
  466    groupIt->symbol()->stopRender( context );
 
  472  if ( renderer->
type() == QLatin1String( 
"pointDisplacement" ) )
 
  476  else if ( renderer->
type() == QLatin1String( 
"singleSymbol" ) ||
 
  477            renderer->
type() == QLatin1String( 
"categorizedSymbol" ) ||
 
  478            renderer->
type() == QLatin1String( 
"graduatedSymbol" ) ||
 
  479            renderer->
type() == QLatin1String( 
"RuleRenderer" ) )
 
  484    return pointRenderer;
 
  486  else if ( renderer->
type() == QLatin1String( 
"pointCluster" ) )
 
  498    return pointRenderer;
 
 
@ Millimeters
Millimeters.
 
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.
 
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.
 
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)
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.
 
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
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).
 
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.
 
void setLabelFont(const QFont &font)
Sets the font used for labeling points.
 
void setToleranceUnit(Qgis::RenderUnit unit)
Sets the units for the tolerance distance.
 
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.
 
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...
 
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.
 
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 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 QgsMapUnitScale decodeMapUnitScale(const QString &str)
 
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 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.