1 /***************************************************************************
2          qgspointdisplacementrenderer.cpp
3          --------------------------------
4   begin                : January 26, 2010
5   copyright            : (C) 2010 by Marco Hugentobler
6   email                : marco at hugis dot net
7  ***************************************************************************/
8 
9 /***************************************************************************
10  *                                                                         *
11  *   This program is free software; you can redistribute it and/or modify  *
12  *   it under the terms of the GNU General Public License as published by  *
13  *   the Free Software Foundation; either version 2 of the License, or     *
14  *   (at your option) any later version.                                   *
15  *                                                                         *
16  ***************************************************************************/
17 
18 #include "qgspointdisplacementrenderer.h"
19 #include "qgssymbollayerutils.h"
20 #include "qgsfontutils.h"
21 #include "qgspainteffectregistry.h"
22 #include "qgspainteffect.h"
23 #include "qgspointclusterrenderer.h"
24 #include "qgsstyleentityvisitor.h"
25 #include "qgsrenderedfeaturehandlerinterface.h"
26 
27 #include <QPainter>
28 #include <cmath>
29 
QgsPointDisplacementRenderer(const QString & labelAttributeName)30 QgsPointDisplacementRenderer::QgsPointDisplacementRenderer( const QString &labelAttributeName )
31   : QgsPointDistanceRenderer( QStringLiteral( "pointDisplacement" ), labelAttributeName )
32   , mCircleColor( QColor( 125, 125, 125 ) )
33 {
34   mCenterSymbol.reset( new QgsMarkerSymbol() );
35 }
36 
clone() const37 QgsPointDisplacementRenderer *QgsPointDisplacementRenderer::clone() const
38 {
39   QgsPointDisplacementRenderer *r = new QgsPointDisplacementRenderer( mLabelAttributeName );
40   if ( mRenderer )
41     r->setEmbeddedRenderer( mRenderer->clone() );
42   r->setCircleWidth( mCircleWidth );
43   r->setCircleColor( mCircleColor );
44   r->setLabelFont( mLabelFont );
45   r->setLabelColor( mLabelColor );
46   r->setPlacement( mPlacement );
47   r->setCircleRadiusAddition( mCircleRadiusAddition );
48   r->setLabelDistanceFactor( mLabelDistanceFactor );
49   r->setMinimumLabelScale( mMinLabelScale );
50   r->setTolerance( mTolerance );
51   r->setToleranceUnit( mToleranceUnit );
52   r->setToleranceMapUnitScale( mToleranceMapUnitScale );
53   if ( mCenterSymbol )
54   {
55     r->setCenterSymbol( mCenterSymbol->clone() );
56   }
57   copyRendererData( r );
58   return r;
59 }
60 
drawGroup(QPointF centerPoint,QgsRenderContext & context,const ClusteredGroup & group)61 void QgsPointDisplacementRenderer::drawGroup( QPointF centerPoint, QgsRenderContext &context, const ClusteredGroup &group )
62 {
63 
64   //calculate max diagonal size from all symbols in group
65   double diagonal = 0;
66   QVector<double> diagonals( group.size() );
67   double currentDiagonal;
68 
69   int groupPosition = 0;
70   for ( const GroupedFeature &feature : group )
71   {
72     if ( QgsMarkerSymbol *symbol = feature.symbol() )
73     {
74       currentDiagonal = M_SQRT2 * symbol->size( context );
75       diagonals[groupPosition] = currentDiagonal;
76       diagonal = std::max( diagonal, currentDiagonal );
77 
78     }
79     else
80     {
81       diagonals[groupPosition] = 0.0;
82     }
83     groupPosition++;
84   }
85 
86   QgsSymbolRenderContext symbolContext( context, QgsUnitTypes::RenderMillimeters, 1.0, false );
87 
88   QList<QPointF> symbolPositions;
89   QList<QPointF> labelPositions;
90   double circleRadius = -1.0;
91   double gridRadius = -1.0;
92   int gridSize = -1;
93 
94   calculateSymbolAndLabelPositions( symbolContext, centerPoint, group.size(), diagonal, symbolPositions, labelPositions, circleRadius, gridRadius, gridSize, diagonals );
95 
96   //only draw circle/grid if there's a pen present - otherwise skip drawing transparent grids
97   if ( mCircleColor.isValid() && mCircleColor.alpha() > 0 )
98   {
99     //draw circle
100     if ( circleRadius > 0 )
101       drawCircle( circleRadius, symbolContext, centerPoint, group.size() );
102     //draw grid
103     else
104       drawGrid( gridSize, symbolContext, symbolPositions, group.size() );
105   }
106 
107   if ( group.size() > 1 )
108   {
109     //draw mid point
110     QgsFeature firstFeature = group.at( 0 ).feature;
111     if ( mCenterSymbol )
112     {
113       mCenterSymbol->renderPoint( centerPoint, &firstFeature, context, -1, false );
114     }
115     else
116     {
117       const double rectSize = symbolContext.renderContext().convertToPainterUnits( 1, QgsUnitTypes::RenderMillimeters );
118       context.painter()->drawRect( QRectF( centerPoint.x() - rectSize, centerPoint.y() - rectSize, rectSize * 2, rectSize * 2 ) );
119     }
120   }
121 
122   //draw symbols on the circle
123   drawSymbols( group, context, symbolPositions );
124   //and also the labels
125   if ( mLabelIndex >= 0 )
126   {
127     drawLabels( centerPoint, symbolContext, labelPositions, group );
128   }
129 }
130 
131 
startRender(QgsRenderContext & context,const QgsFields & fields)132 void QgsPointDisplacementRenderer::startRender( QgsRenderContext &context, const QgsFields &fields )
133 {
134   if ( mCenterSymbol )
135   {
136     mCenterSymbol->startRender( context, fields );
137   }
138 
139   QgsPointDistanceRenderer::startRender( context, fields );
140 }
141 
stopRender(QgsRenderContext & context)142 void QgsPointDisplacementRenderer::stopRender( QgsRenderContext &context )
143 {
144   QgsPointDistanceRenderer::stopRender( context );
145   if ( mCenterSymbol )
146   {
147     mCenterSymbol->stopRender( context );
148   }
149 }
150 
create(QDomElement & symbologyElem,const QgsReadWriteContext & context)151 QgsFeatureRenderer *QgsPointDisplacementRenderer::create( QDomElement &symbologyElem, const QgsReadWriteContext &context )
152 {
153   QgsPointDisplacementRenderer *r = new QgsPointDisplacementRenderer();
154   r->setLabelAttributeName( symbologyElem.attribute( QStringLiteral( "labelAttributeName" ) ) );
155   QFont labelFont;
156   if ( !QgsFontUtils::setFromXmlChildNode( labelFont, symbologyElem, QStringLiteral( "labelFontProperties" ) ) )
157   {
158     labelFont.fromString( symbologyElem.attribute( QStringLiteral( "labelFont" ), QString() ) );
159   }
160   r->setLabelFont( labelFont );
161   r->setPlacement( static_cast< Placement >( symbologyElem.attribute( QStringLiteral( "placement" ), QStringLiteral( "0" ) ).toInt() ) );
162   r->setCircleWidth( symbologyElem.attribute( QStringLiteral( "circleWidth" ), QStringLiteral( "0.4" ) ).toDouble() );
163   r->setCircleColor( QgsSymbolLayerUtils::decodeColor( symbologyElem.attribute( QStringLiteral( "circleColor" ), QString() ) ) );
164   r->setLabelColor( QgsSymbolLayerUtils::decodeColor( symbologyElem.attribute( QStringLiteral( "labelColor" ), QString() ) ) );
165   r->setCircleRadiusAddition( symbologyElem.attribute( QStringLiteral( "circleRadiusAddition" ), QStringLiteral( "0.0" ) ).toDouble() );
166   r->setLabelDistanceFactor( symbologyElem.attribute( QStringLiteral( "labelDistanceFactor" ), QStringLiteral( "0.5" ) ).toDouble() );
167   r->setMinimumLabelScale( symbologyElem.attribute( QStringLiteral( "maxLabelScaleDenominator" ), QStringLiteral( "-1" ) ).toDouble() );
168   r->setTolerance( symbologyElem.attribute( QStringLiteral( "tolerance" ), QStringLiteral( "0.00001" ) ).toDouble() );
169   r->setToleranceUnit( QgsUnitTypes::decodeRenderUnit( symbologyElem.attribute( QStringLiteral( "toleranceUnit" ), QStringLiteral( "MapUnit" ) ) ) );
170   r->setToleranceMapUnitScale( QgsSymbolLayerUtils::decodeMapUnitScale( symbologyElem.attribute( QStringLiteral( "toleranceUnitScale" ) ) ) );
171 
172   //look for an embedded renderer <renderer-v2>
173   QDomElement embeddedRendererElem = symbologyElem.firstChildElement( QStringLiteral( "renderer-v2" ) );
174   if ( !embeddedRendererElem.isNull() )
175   {
176     r->setEmbeddedRenderer( QgsFeatureRenderer::load( embeddedRendererElem, context ) );
177   }
178 
179   //center symbol
180   QDomElement centerSymbolElem = symbologyElem.firstChildElement( QStringLiteral( "symbol" ) );
181   if ( !centerSymbolElem.isNull() )
182   {
183     r->setCenterSymbol( QgsSymbolLayerUtils::loadSymbol<QgsMarkerSymbol>( centerSymbolElem, context ) );
184   }
185   return r;
186 }
187 
centerSymbol()188 QgsMarkerSymbol *QgsPointDisplacementRenderer::centerSymbol()
189 {
190   return mCenterSymbol.get();
191 }
192 
save(QDomDocument & doc,const QgsReadWriteContext & context)193 QDomElement QgsPointDisplacementRenderer::save( QDomDocument &doc, const QgsReadWriteContext &context )
194 {
195   QDomElement rendererElement = doc.createElement( RENDERER_TAG_NAME );
196   rendererElement.setAttribute( QStringLiteral( "forceraster" ), ( mForceRaster ? QStringLiteral( "1" ) : QStringLiteral( "0" ) ) );
197   rendererElement.setAttribute( QStringLiteral( "type" ), QStringLiteral( "pointDisplacement" ) );
198   rendererElement.setAttribute( QStringLiteral( "labelAttributeName" ), mLabelAttributeName );
199   rendererElement.appendChild( QgsFontUtils::toXmlElement( mLabelFont, doc, QStringLiteral( "labelFontProperties" ) ) );
200   rendererElement.setAttribute( QStringLiteral( "circleWidth" ), QString::number( mCircleWidth ) );
201   rendererElement.setAttribute( QStringLiteral( "circleColor" ), QgsSymbolLayerUtils::encodeColor( mCircleColor ) );
202   rendererElement.setAttribute( QStringLiteral( "labelColor" ), QgsSymbolLayerUtils::encodeColor( mLabelColor ) );
203   rendererElement.setAttribute( QStringLiteral( "circleRadiusAddition" ), QString::number( mCircleRadiusAddition ) );
204   rendererElement.setAttribute( QStringLiteral( "labelDistanceFactor" ), QString::number( mLabelDistanceFactor ) );
205   rendererElement.setAttribute( QStringLiteral( "placement" ), static_cast< int >( mPlacement ) );
206   rendererElement.setAttribute( QStringLiteral( "maxLabelScaleDenominator" ), QString::number( mMinLabelScale ) );
207   rendererElement.setAttribute( QStringLiteral( "tolerance" ), QString::number( mTolerance ) );
208   rendererElement.setAttribute( QStringLiteral( "toleranceUnit" ), QgsUnitTypes::encodeUnit( mToleranceUnit ) );
209   rendererElement.setAttribute( QStringLiteral( "toleranceUnitScale" ), QgsSymbolLayerUtils::encodeMapUnitScale( mToleranceMapUnitScale ) );
210 
211   if ( mRenderer )
212   {
213     QDomElement embeddedRendererElem = mRenderer->save( doc, context );
214     rendererElement.appendChild( embeddedRendererElem );
215   }
216   if ( mCenterSymbol )
217   {
218     QDomElement centerSymbolElem = QgsSymbolLayerUtils::saveSymbol( QStringLiteral( "centerSymbol" ), mCenterSymbol.get(), doc, context );
219     rendererElement.appendChild( centerSymbolElem );
220   }
221 
222   if ( mPaintEffect && !QgsPaintEffectRegistry::isDefaultStack( mPaintEffect ) )
223     mPaintEffect->saveProperties( doc, rendererElement );
224 
225   if ( !mOrderBy.isEmpty() )
226   {
227     QDomElement orderBy = doc.createElement( QStringLiteral( "orderby" ) );
228     mOrderBy.save( orderBy );
229     rendererElement.appendChild( orderBy );
230   }
231   rendererElement.setAttribute( QStringLiteral( "enableorderby" ), ( mOrderByEnabled ? QStringLiteral( "1" ) : QStringLiteral( "0" ) ) );
232 
233   return rendererElement;
234 }
235 
usedAttributes(const QgsRenderContext & context) const236 QSet<QString> QgsPointDisplacementRenderer::usedAttributes( const QgsRenderContext &context ) const
237 {
238   QSet<QString> attr = QgsPointDistanceRenderer::usedAttributes( context );
239   if ( mCenterSymbol )
240     attr.unite( mCenterSymbol->usedAttributes( context ) );
241   return attr;
242 }
243 
accept(QgsStyleEntityVisitorInterface * visitor) const244 bool QgsPointDisplacementRenderer::accept( QgsStyleEntityVisitorInterface *visitor ) const
245 {
246   if ( !QgsPointDistanceRenderer::accept( visitor ) )
247     return false;
248 
249   if ( mCenterSymbol )
250   {
251     QgsStyleSymbolEntity entity( mCenterSymbol.get() );
252     if ( !visitor->visit( QgsStyleEntityVisitorInterface::StyleLeaf( &entity, QStringLiteral( "center" ), QObject::tr( "Center Symbol" ) ) ) )
253       return false;
254   }
255 
256   return true;
257 }
258 
setCenterSymbol(QgsMarkerSymbol * symbol)259 void QgsPointDisplacementRenderer::setCenterSymbol( QgsMarkerSymbol *symbol )
260 {
261   mCenterSymbol.reset( symbol );
262 }
263 
calculateSymbolAndLabelPositions(QgsSymbolRenderContext & symbolContext,QPointF centerPoint,int nPosition,double symbolDiagonal,QList<QPointF> & symbolPositions,QList<QPointF> & labelShifts,double & circleRadius,double & gridRadius,int & gridSize,QVector<double> & diagonals) const264 void QgsPointDisplacementRenderer::calculateSymbolAndLabelPositions( QgsSymbolRenderContext &symbolContext, QPointF centerPoint, int nPosition,
265     double symbolDiagonal, QList<QPointF> &symbolPositions, QList<QPointF> &labelShifts, double &circleRadius, double &gridRadius,
266     int &gridSize, QVector<double> &diagonals ) const
267 {
268   symbolPositions.clear();
269   labelShifts.clear();
270 
271   if ( nPosition < 1 )
272   {
273     return;
274   }
275   else if ( nPosition == 1 ) //If there is only one feature, draw it exactly at the center position
276   {
277     const double side = std::sqrt( std::pow( symbolDiagonal, 2 ) / 2.0 );
278     symbolPositions.append( centerPoint );
279     labelShifts.append( QPointF( side * mLabelDistanceFactor, -side * mLabelDistanceFactor ) );
280     return;
281   }
282 
283   double circleAdditionPainterUnits = symbolContext.renderContext().convertToPainterUnits( mCircleRadiusAddition, QgsUnitTypes::RenderMillimeters );
284 
285   switch ( mPlacement )
286   {
287     case Ring:
288     {
289       const double minDiameterToFitSymbols = nPosition * symbolDiagonal / ( 2.0 * M_PI );
290       const double radius = std::max( symbolDiagonal / 2, minDiameterToFitSymbols ) + circleAdditionPainterUnits;
291 
292       const double angleStep = 2 * M_PI / nPosition;
293       double currentAngle = 0.0;
294       for ( int featureIndex = 0; featureIndex < nPosition; currentAngle += angleStep, featureIndex++ )
295       {
296         const double sinusCurrentAngle = std::sin( currentAngle );
297         const double cosinusCurrentAngle = std::cos( currentAngle );
298         const QPointF positionShift( radius * sinusCurrentAngle, radius * cosinusCurrentAngle );
299 
300         const QPointF labelShift( ( radius + diagonals.at( featureIndex ) * mLabelDistanceFactor ) * sinusCurrentAngle, ( radius + diagonals.at( featureIndex ) * mLabelDistanceFactor ) * cosinusCurrentAngle );
301         symbolPositions.append( centerPoint + positionShift );
302         labelShifts.append( labelShift );
303       }
304       circleRadius = radius;
305       break;
306     }
307     case ConcentricRings:
308     {
309       double centerDiagonal = mCenterSymbol->size( symbolContext.renderContext() ) * M_SQRT2;
310 
311       int pointsRemaining = nPosition;
312       int ringNumber = 1;
313       double firstRingRadius = centerDiagonal / 2.0 + symbolDiagonal / 2.0;
314       int featureIndex = 0;
315       while ( pointsRemaining > 0 )
316       {
317         double radiusCurrentRing = std::max( firstRingRadius + ( ringNumber - 1 ) * symbolDiagonal + ringNumber * circleAdditionPainterUnits, 0.0 );
318         int maxPointsCurrentRing = std::max( std::floor( 2 * M_PI * radiusCurrentRing / symbolDiagonal ), 1.0 );
319         int actualPointsCurrentRing = std::min( maxPointsCurrentRing, pointsRemaining );
320 
321         double angleStep = 2 * M_PI / actualPointsCurrentRing;
322         double currentAngle = 0.0;
323         for ( int i = 0; i < actualPointsCurrentRing; ++i )
324         {
325           double sinusCurrentAngle = std::sin( currentAngle );
326           double cosinusCurrentAngle = std::cos( currentAngle );
327           QPointF positionShift( radiusCurrentRing * sinusCurrentAngle, radiusCurrentRing * cosinusCurrentAngle );
328           QPointF labelShift( ( radiusCurrentRing + diagonals.at( featureIndex ) * mLabelDistanceFactor ) * sinusCurrentAngle, ( radiusCurrentRing + diagonals.at( featureIndex ) * mLabelDistanceFactor ) * cosinusCurrentAngle );
329           symbolPositions.append( centerPoint + positionShift );
330           labelShifts.append( labelShift );
331           currentAngle += angleStep;
332           featureIndex++;
333         }
334 
335         pointsRemaining -= actualPointsCurrentRing;
336         ringNumber++;
337         circleRadius = radiusCurrentRing;
338       }
339       break;
340     }
341     case Grid:
342     {
343       double centerDiagonal = mCenterSymbol->size( symbolContext.renderContext() ) * M_SQRT2;
344       int pointsRemaining = nPosition;
345       gridSize = std::ceil( std::sqrt( pointsRemaining ) );
346       if ( pointsRemaining - std::pow( gridSize - 1, 2 ) < gridSize )
347         gridSize -= 1;
348       double originalPointRadius = ( ( centerDiagonal / 2.0 + symbolDiagonal / 2.0 ) + symbolDiagonal ) / 2;
349       double userPointRadius =  originalPointRadius + circleAdditionPainterUnits;
350 
351       int yIndex = 0;
352       while ( pointsRemaining > 0 )
353       {
354         for ( int xIndex = 0; xIndex < gridSize && pointsRemaining > 0; ++xIndex )
355         {
356           QPointF positionShift( userPointRadius * xIndex, userPointRadius * yIndex );
357           symbolPositions.append( centerPoint + positionShift );
358           pointsRemaining--;
359         }
360         yIndex++;
361       }
362 
363       centralizeGrid( symbolPositions, userPointRadius, gridSize );
364 
365       int xFactor;
366       int yFactor;
367       double side = 0;
368       for ( int symbolIndex = 0; symbolIndex < symbolPositions.size(); ++symbolIndex )
369       {
370         if ( symbolPositions.at( symbolIndex ).x() < centerPoint.x() )
371         {
372           xFactor = -1;
373         }
374         else
375         {
376           xFactor = 1;
377         }
378 
379         if ( symbolPositions.at( symbolIndex ).y() < centerPoint.y() )
380         {
381           yFactor = 1;
382         }
383         else
384         {
385           yFactor = -1;
386         }
387 
388         side = std::sqrt( std::pow( diagonals.at( symbolIndex ), 2 ) / 2.0 );
389         QPointF labelShift( ( side * mLabelDistanceFactor * xFactor ), ( -side * mLabelDistanceFactor * yFactor ) );
390         labelShifts.append( symbolPositions.at( symbolIndex ) - centerPoint + labelShift );
391       }
392 
393       gridRadius = userPointRadius;
394       break;
395     }
396   }
397 }
398 
centralizeGrid(QList<QPointF> & pointSymbolPositions,double radius,int size) const399 void QgsPointDisplacementRenderer::centralizeGrid( QList<QPointF> &pointSymbolPositions, double radius, int size ) const
400 {
401   double shiftAmount = -radius * ( size - 1.0 ) / 2.0;
402   QPointF centralShift( shiftAmount, shiftAmount );
403   for ( int i = 0; i < pointSymbolPositions.size(); ++i )
404   {
405     pointSymbolPositions[i] += centralShift;
406   }
407 }
408 
drawGrid(int gridSizeUnits,QgsSymbolRenderContext & context,QList<QPointF> pointSymbolPositions,int nSymbols)409 void QgsPointDisplacementRenderer::drawGrid( int gridSizeUnits, QgsSymbolRenderContext &context,
410     QList<QPointF> pointSymbolPositions, int nSymbols )
411 {
412   QPainter *p = context.renderContext().painter();
413   if ( nSymbols < 2 || !p ) //draw grid only if multiple features
414   {
415     return;
416   }
417 
418   QPen gridPen( mCircleColor );
419   gridPen.setWidthF( context.renderContext().convertToPainterUnits( mCircleWidth, QgsUnitTypes::RenderMillimeters ) );
420   p->setPen( gridPen );
421 
422   for ( int i = 0; i < pointSymbolPositions.size(); ++i )
423   {
424     if ( i + 1 < pointSymbolPositions.size() && 0 != ( i + 1 ) % gridSizeUnits )
425     {
426       QLineF gridLineRow( pointSymbolPositions[i], pointSymbolPositions[i + 1] );
427       p->drawLine( gridLineRow );
428     }
429 
430     if ( i + gridSizeUnits < pointSymbolPositions.size() )
431     {
432       QLineF gridLineColumn( pointSymbolPositions[i], pointSymbolPositions[i + gridSizeUnits] );
433       p->drawLine( gridLineColumn );
434     }
435   }
436 }
437 
drawCircle(double radiusPainterUnits,QgsSymbolRenderContext & context,QPointF centerPoint,int nSymbols)438 void QgsPointDisplacementRenderer::drawCircle( double radiusPainterUnits, QgsSymbolRenderContext &context, QPointF centerPoint, int nSymbols )
439 {
440   QPainter *p = context.renderContext().painter();
441   if ( nSymbols < 2 || !p ) //draw circle only if multiple features
442   {
443     return;
444   }
445 
446   //draw Circle
447   QPen circlePen( mCircleColor );
448   circlePen.setWidthF( context.renderContext().convertToPainterUnits( mCircleWidth, QgsUnitTypes::RenderMillimeters ) );
449   p->setPen( circlePen );
450   p->drawArc( QRectF( centerPoint.x() - radiusPainterUnits, centerPoint.y() - radiusPainterUnits, 2 * radiusPainterUnits, 2 * radiusPainterUnits ), 0, 5760 );
451 }
452 
drawSymbols(const ClusteredGroup & group,QgsRenderContext & context,const QList<QPointF> & symbolPositions)453 void QgsPointDisplacementRenderer::drawSymbols( const ClusteredGroup &group, QgsRenderContext &context, const QList<QPointF> &symbolPositions )
454 {
455   QList<QPointF>::const_iterator symbolPosIt = symbolPositions.constBegin();
456   ClusteredGroup::const_iterator groupIt = group.constBegin();
457   for ( ; symbolPosIt != symbolPositions.constEnd() && groupIt != group.constEnd();
458         ++symbolPosIt, ++groupIt )
459   {
460     context.expressionContext().setFeature( groupIt->feature );
461     groupIt->symbol()->startRender( context );
462     groupIt->symbol()->renderPoint( *symbolPosIt, &( groupIt->feature ), context, -1, groupIt->isSelected );
463     if ( context.hasRenderedFeatureHandlers() )
464     {
465       const QgsGeometry bounds( QgsGeometry::fromRect( QgsRectangle( groupIt->symbol()->bounds( *symbolPosIt, context, groupIt->feature ) ) ) );
466       const QList< QgsRenderedFeatureHandlerInterface * > handlers = context.renderedFeatureHandlers();
467       QgsRenderedFeatureHandlerInterface::RenderedFeatureContext featureContext( context );
468       for ( QgsRenderedFeatureHandlerInterface *handler : handlers )
469         handler->handleRenderedFeature( groupIt->feature, bounds, featureContext );
470     }
471     groupIt->symbol()->stopRender( context );
472   }
473 }
474 
convertFromRenderer(const QgsFeatureRenderer * renderer)475 QgsPointDisplacementRenderer *QgsPointDisplacementRenderer::convertFromRenderer( const QgsFeatureRenderer *renderer )
476 {
477   if ( renderer->type() == QLatin1String( "pointDisplacement" ) )
478   {
479     return dynamic_cast<QgsPointDisplacementRenderer *>( renderer->clone() );
480   }
481   else if ( renderer->type() == QLatin1String( "singleSymbol" ) ||
482             renderer->type() == QLatin1String( "categorizedSymbol" ) ||
483             renderer->type() == QLatin1String( "graduatedSymbol" ) ||
484             renderer->type() == QLatin1String( "RuleRenderer" ) )
485   {
486     QgsPointDisplacementRenderer *pointRenderer = new QgsPointDisplacementRenderer();
487     pointRenderer->setEmbeddedRenderer( renderer->clone() );
488     return pointRenderer;
489   }
490   else if ( renderer->type() == QLatin1String( "pointCluster" ) )
491   {
492     QgsPointDisplacementRenderer *pointRenderer = new QgsPointDisplacementRenderer();
493     const QgsPointClusterRenderer *clusterRenderer = static_cast< const QgsPointClusterRenderer * >( renderer );
494     if ( clusterRenderer->embeddedRenderer() )
495       pointRenderer->setEmbeddedRenderer( clusterRenderer->embeddedRenderer()->clone() );
496     pointRenderer->setTolerance( clusterRenderer->tolerance() );
497     pointRenderer->setToleranceUnit( clusterRenderer->toleranceUnit() );
498     pointRenderer->setToleranceMapUnitScale( clusterRenderer->toleranceMapUnitScale() );
499     if ( const_cast< QgsPointClusterRenderer * >( clusterRenderer )->clusterSymbol() )
500       pointRenderer->setCenterSymbol( const_cast< QgsPointClusterRenderer * >( clusterRenderer )->clusterSymbol()->clone() );
501     return pointRenderer;
502   }
503   else
504   {
505     return nullptr;
506   }
507 }
508