33  , mCircleColor( QColor( 125, 125, 125 ) )
 
 
   72void QgsPointDisplacementRenderer::drawGroup( QPointF centerPoint, 
QgsRenderContext &context, 
const ClusteredGroup &group )
 const 
   77  QVector<double> diagonals( group.size() );
 
   78  double currentDiagonal;
 
   80  int groupPosition = 0;
 
   81  for ( 
const GroupedFeature &feature : group )
 
   85      currentDiagonal = M_SQRT2 * symbol->size( context );
 
   86      diagonals[groupPosition] = currentDiagonal;
 
   87      diagonal = std::max( diagonal, currentDiagonal );
 
   92      diagonals[groupPosition] = 0.0;
 
   99  QList<QPointF> symbolPositions;
 
  100  QList<QPointF> labelPositions;
 
  101  double circleRadius = -1.0;
 
  102  double gridRadius = -1.0;
 
  105  calculateSymbolAndLabelPositions( symbolContext, centerPoint, group.size(), diagonal, symbolPositions, labelPositions, circleRadius, gridRadius, gridSize, diagonals );
 
  108  if ( mCircleColor.isValid() && mCircleColor.alpha() > 0 )
 
  110    switch ( mPlacement )
 
  114        drawCircle( circleRadius, symbolContext, centerPoint, group.size() );
 
  117        drawGrid( gridSize, symbolContext, symbolPositions, group.size() );
 
  122  if ( group.size() > 1 )
 
  125    const QgsFeature firstFeature = group.at( 0 ).feature;
 
  128      mCenterSymbol->renderPoint( centerPoint, &firstFeature, context, -1, 
false );
 
  133      context.
painter()->drawRect( QRectF( centerPoint.x() - rectSize, centerPoint.y() - rectSize, rectSize * 2, rectSize * 2 ) );
 
  138  drawSymbols( group, context, symbolPositions );
 
  142    drawLabels( centerPoint, symbolContext, labelPositions, group );
 
  151    mCenterSymbol->startRender( context, fields );
 
 
  162    mCenterSymbol->stopRender( context );
 
 
  173    labelFont.fromString( symbologyElem.attribute( QStringLiteral( 
"labelFont" ), QString() ) );
 
  176  r->
setPlacement( 
static_cast< Placement >( symbologyElem.attribute( QStringLiteral( 
"placement" ), QStringLiteral( 
"0" ) ).toInt() ) );
 
  177  r->
setCircleWidth( symbologyElem.attribute( QStringLiteral( 
"circleWidth" ), QStringLiteral( 
"0.4" ) ).toDouble() );
 
  180  r->
setCircleRadiusAddition( symbologyElem.attribute( QStringLiteral( 
"circleRadiusAddition" ), QStringLiteral( 
"0.0" ) ).toDouble() );
 
  181  r->
setLabelDistanceFactor( symbologyElem.attribute( QStringLiteral( 
"labelDistanceFactor" ), QStringLiteral( 
"0.5" ) ).toDouble() );
 
  182  r->
setMinimumLabelScale( symbologyElem.attribute( QStringLiteral( 
"maxLabelScaleDenominator" ), QStringLiteral( 
"-1" ) ).toDouble() );
 
  183  r->
setTolerance( symbologyElem.attribute( QStringLiteral( 
"tolerance" ), QStringLiteral( 
"0.00001" ) ).toDouble() );
 
  188  QDomElement embeddedRendererElem = symbologyElem.firstChildElement( QStringLiteral( 
"renderer-v2" ) );
 
  189  if ( !embeddedRendererElem.isNull() )
 
  195  const QDomElement centerSymbolElem = symbologyElem.firstChildElement( QStringLiteral( 
"symbol" ) );
 
  196  if ( !centerSymbolElem.isNull() )
 
  198    r->
setCenterSymbol( QgsSymbolLayerUtils::loadSymbol<QgsMarkerSymbol>( centerSymbolElem, context ).release() );
 
 
  205  return mCenterSymbol.get();
 
 
  211  rendererElement.setAttribute( QStringLiteral( 
"type" ), QStringLiteral( 
"pointDisplacement" ) );
 
  212  rendererElement.setAttribute( QStringLiteral( 
"labelAttributeName" ), 
mLabelAttributeName );
 
  214  rendererElement.setAttribute( QStringLiteral( 
"circleWidth" ), QString::number( mCircleWidth ) );
 
  217  rendererElement.setAttribute( QStringLiteral( 
"circleRadiusAddition" ), QString::number( mCircleRadiusAddition ) );
 
  218  rendererElement.setAttribute( QStringLiteral( 
"labelDistanceFactor" ), QString::number( mLabelDistanceFactor ) );
 
  219  rendererElement.setAttribute( QStringLiteral( 
"placement" ), 
static_cast< int >( mPlacement ) );
 
  220  rendererElement.setAttribute( QStringLiteral( 
"maxLabelScaleDenominator" ), QString::number( 
mMinLabelScale ) );
 
  221  rendererElement.setAttribute( QStringLiteral( 
"tolerance" ), QString::number( 
mTolerance ) );
 
  227    const QDomElement embeddedRendererElem = 
mRenderer->save( doc, context );
 
  228    rendererElement.appendChild( embeddedRendererElem );
 
  233    rendererElement.appendChild( centerSymbolElem );
 
  238  return rendererElement;
 
 
  245    attr.unite( mCenterSymbol->usedAttributes( context ) );
 
 
  266  mCenterSymbol.reset( symbol );
 
 
  269void QgsPointDisplacementRenderer::calculateSymbolAndLabelPositions( 
QgsSymbolRenderContext &symbolContext, QPointF centerPoint, 
int nPosition,
 
  270    double symbolDiagonal, QList<QPointF> &symbolPositions, QList<QPointF> &labelShifts, 
double &circleRadius, 
double &gridRadius,
 
  271    int &gridSize, QVector<double> &diagonals )
 const 
  273  symbolPositions.clear();
 
  280  else if ( nPosition == 1 ) 
 
  282    const double side = std::sqrt( std::pow( symbolDiagonal, 2 ) / 2.0 );
 
  283    symbolPositions.append( centerPoint );
 
  284    labelShifts.append( QPointF( side * mLabelDistanceFactor, -side * mLabelDistanceFactor ) );
 
  290  switch ( mPlacement )
 
  294      const double minDiameterToFitSymbols = nPosition * symbolDiagonal / ( 2.0 * M_PI );
 
  295      const double radius = std::max( symbolDiagonal / 2, minDiameterToFitSymbols ) + circleAdditionPainterUnits;
 
  297      const double angleStep = 2 * M_PI / nPosition;
 
  298      double currentAngle = 0.0;
 
  299      for ( 
int featureIndex = 0; featureIndex < nPosition; currentAngle += angleStep, featureIndex++ )
 
  301        const double sinusCurrentAngle = std::sin( currentAngle );
 
  302        const double cosinusCurrentAngle = std::cos( currentAngle );
 
  303        const QPointF positionShift( radius * sinusCurrentAngle, radius * cosinusCurrentAngle );
 
  305        const QPointF labelShift( ( radius + diagonals.at( featureIndex ) * mLabelDistanceFactor ) * sinusCurrentAngle, ( radius + diagonals.at( featureIndex ) * mLabelDistanceFactor ) * cosinusCurrentAngle );
 
  306        symbolPositions.append( centerPoint + positionShift );
 
  307        labelShifts.append( labelShift );
 
  309      circleRadius = radius;
 
  314      const double centerDiagonal = mCenterSymbol->size( symbolContext.
renderContext() ) * M_SQRT2;
 
  316      int pointsRemaining = nPosition;
 
  318      const double firstRingRadius = centerDiagonal / 2.0 + symbolDiagonal / 2.0;
 
  319      int featureIndex = 0;
 
  320      while ( pointsRemaining > 0 )
 
  322        const double radiusCurrentRing = std::max( firstRingRadius + ( ringNumber - 1 ) * symbolDiagonal + ringNumber * circleAdditionPainterUnits, 0.0 );
 
  323        const int maxPointsCurrentRing = std::max( std::floor( 2 * M_PI * radiusCurrentRing / symbolDiagonal ), 1.0 );
 
  324        const int actualPointsCurrentRing = std::min( maxPointsCurrentRing, pointsRemaining );
 
  326        const double angleStep = 2 * M_PI / actualPointsCurrentRing;
 
  327        double currentAngle = 0.0;
 
  328        for ( 
int i = 0; i < actualPointsCurrentRing; ++i )
 
  330          const double sinusCurrentAngle = std::sin( currentAngle );
 
  331          const double cosinusCurrentAngle = std::cos( currentAngle );
 
  332          const QPointF positionShift( radiusCurrentRing * sinusCurrentAngle, radiusCurrentRing * cosinusCurrentAngle );
 
  333          const QPointF labelShift( ( radiusCurrentRing + diagonals.at( featureIndex ) * mLabelDistanceFactor ) * sinusCurrentAngle, ( radiusCurrentRing + diagonals.at( featureIndex ) * mLabelDistanceFactor ) * cosinusCurrentAngle );
 
  334          symbolPositions.append( centerPoint + positionShift );
 
  335          labelShifts.append( labelShift );
 
  336          currentAngle += angleStep;
 
  340        pointsRemaining -= actualPointsCurrentRing;
 
  342        circleRadius = radiusCurrentRing;
 
  348      const double centerDiagonal = mCenterSymbol->size( symbolContext.
renderContext() ) * M_SQRT2;
 
  349      int pointsRemaining = nPosition;
 
  350      gridSize = std::ceil( std::sqrt( pointsRemaining ) );
 
  351      if ( pointsRemaining - std::pow( gridSize - 1, 2 ) < gridSize )
 
  353      const double originalPointRadius = ( ( centerDiagonal / 2.0 + symbolDiagonal / 2.0 ) + symbolDiagonal ) / 2;
 
  354      const double userPointRadius =  originalPointRadius + circleAdditionPainterUnits;
 
  357      while ( pointsRemaining > 0 )
 
  359        for ( 
int xIndex = 0; xIndex < gridSize && pointsRemaining > 0; ++xIndex )
 
  361          const QPointF positionShift( userPointRadius * xIndex, userPointRadius * yIndex );
 
  362          symbolPositions.append( centerPoint + positionShift );
 
  368      centralizeGrid( symbolPositions, userPointRadius, gridSize );
 
  373      for ( 
int symbolIndex = 0; symbolIndex < symbolPositions.size(); ++symbolIndex )
 
  375        if ( symbolPositions.at( symbolIndex ).x() < centerPoint.x() )
 
  384        if ( symbolPositions.at( symbolIndex ).y() < centerPoint.y() )
 
  393        side = std::sqrt( std::pow( diagonals.at( symbolIndex ), 2 ) / 2.0 );
 
  394        const QPointF labelShift( ( side * mLabelDistanceFactor * xFactor ), ( -side * mLabelDistanceFactor * yFactor ) );
 
  395        labelShifts.append( symbolPositions.at( symbolIndex ) - centerPoint + labelShift );
 
  398      gridRadius = userPointRadius;
 
  404void QgsPointDisplacementRenderer::centralizeGrid( QList<QPointF> &pointSymbolPositions, 
double radius, 
int size )
 const 
  406  const double shiftAmount = -radius * ( size - 1.0 ) / 2.0;
 
  407  const QPointF centralShift( shiftAmount, shiftAmount );
 
  408  for ( 
int i = 0; i < pointSymbolPositions.size(); ++i )
 
  410    pointSymbolPositions[i] += centralShift;
 
  415    QList<QPointF> pointSymbolPositions, 
int nSymbols )
 const 
  418  if ( nSymbols < 2 || !p ) 
 
  423  QPen gridPen( mCircleColor );
 
  425  p->setPen( gridPen );
 
  427  for ( 
int i = 0; i < pointSymbolPositions.size(); ++i )
 
  429    if ( i + 1 < pointSymbolPositions.size() && 0 != ( i + 1 ) % gridSizeUnits )
 
  431      const QLineF gridLineRow( pointSymbolPositions[i], pointSymbolPositions[i + 1] );
 
  432      p->drawLine( gridLineRow );
 
  435    if ( i + gridSizeUnits < pointSymbolPositions.size() )
 
  437      const QLineF gridLineColumn( pointSymbolPositions[i], pointSymbolPositions[i + gridSizeUnits] );
 
  438      p->drawLine( gridLineColumn );
 
  443void QgsPointDisplacementRenderer::drawCircle( 
double radiusPainterUnits, 
QgsSymbolRenderContext &context, QPointF centerPoint, 
int nSymbols )
 const 
  446  if ( nSymbols < 2 || !p ) 
 
  452  QPen circlePen( mCircleColor );
 
  454  p->setPen( circlePen );
 
  455  p->drawArc( QRectF( centerPoint.x() - radiusPainterUnits, centerPoint.y() - radiusPainterUnits, 2 * radiusPainterUnits, 2 * radiusPainterUnits ), 0, 5760 );
 
  458void QgsPointDisplacementRenderer::drawSymbols( 
const ClusteredGroup &group, 
QgsRenderContext &context, 
const QList<QPointF> &symbolPositions )
 const 
  460  QList<QPointF>::const_iterator symbolPosIt = symbolPositions.constBegin();
 
  461  ClusteredGroup::const_iterator groupIt = group.constBegin();
 
  462  for ( ; symbolPosIt != symbolPositions.constEnd() && groupIt != group.constEnd();
 
  463        ++symbolPosIt, ++groupIt )
 
  466    groupIt->symbol()->startRender( context );
 
  467    groupIt->symbol()->renderPoint( *symbolPosIt, &( groupIt->feature ), context, -1, groupIt->isSelected );
 
  474        handler->handleRenderedFeature( groupIt->feature, bounds, featureContext );
 
  476    groupIt->symbol()->stopRender( context );
 
  482  if ( renderer->
type() == QLatin1String( 
"pointDisplacement" ) )
 
  486  else if ( renderer->
type() == QLatin1String( 
"singleSymbol" ) ||
 
  487            renderer->
type() == QLatin1String( 
"categorizedSymbol" ) ||
 
  488            renderer->
type() == QLatin1String( 
"graduatedSymbol" ) ||
 
  489            renderer->
type() == QLatin1String( 
"RuleRenderer" ) )
 
  494    return pointRenderer;
 
  496  else if ( renderer->
type() == QLatin1String( 
"pointCluster" ) )
 
  506    return pointRenderer;
 
 
QFlags< FeatureRendererFlag > FeatureRendererFlags
Flags controlling behavior of vector feature renderers.
 
@ AffectsLabeling
If present, indicates that the renderer will participate in the map labeling problem.
 
@ Millimeters
Millimeters.
 
@ AffectsLabeling
If present, indicates that the symbol will participate in the map labeling problem.
 
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.
 
Abstract base class for all 2D vector feature renderers.
 
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.
 
A renderer that automatically clusters points with the same geographic position.
 
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).
 
Qgis::FeatureRendererFlags flags() const override
Returns flags associated with the renderer.
 
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...
 
A container for the context for various read/write operations on 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 provide 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.
 
Encapsulates the context in which a symbol is being rendered.
 
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.