32 , mCircleColor( QColor( 125, 125, 125 ) )
61 void QgsPointDisplacementRenderer::drawGroup( QPointF centerPoint,
QgsRenderContext &context,
const ClusteredGroup &group )
66 QVector<double> diagonals( group.size() );
67 double currentDiagonal;
69 int groupPosition = 0;
70 for (
const GroupedFeature &feature : group )
74 currentDiagonal = M_SQRT2 * symbol->size( context );
75 diagonals[groupPosition] = currentDiagonal;
76 diagonal = std::max( diagonal, currentDiagonal );
81 diagonals[groupPosition] = 0.0;
88 QList<QPointF> symbolPositions;
89 QList<QPointF> labelPositions;
90 double circleRadius = -1.0;
91 double gridRadius = -1.0;
94 calculateSymbolAndLabelPositions( symbolContext, centerPoint, group.size(), diagonal, symbolPositions, labelPositions, circleRadius, gridRadius, gridSize, diagonals );
97 if ( mCircleColor.isValid() && mCircleColor.alpha() > 0 )
100 if ( circleRadius > 0 )
101 drawCircle( circleRadius, symbolContext, centerPoint, group.size() );
104 drawGrid( gridSize, symbolContext, symbolPositions, group.size() );
107 if ( group.size() > 1 )
110 QgsFeature firstFeature = group.at( 0 ).feature;
113 mCenterSymbol->renderPoint( centerPoint, &firstFeature, context, -1,
false );
118 context.
painter()->drawRect( QRectF( centerPoint.x() - rectSize, centerPoint.y() - rectSize, rectSize * 2, rectSize * 2 ) );
123 drawSymbols( group, context, symbolPositions );
127 drawLabels( centerPoint, symbolContext, labelPositions, group );
136 mCenterSymbol->startRender( context, fields );
147 mCenterSymbol->stopRender( context );
158 labelFont.fromString( symbologyElem.attribute( QStringLiteral(
"labelFont" ), QString() ) );
161 r->
setPlacement(
static_cast< Placement >( symbologyElem.attribute( QStringLiteral(
"placement" ), QStringLiteral(
"0" ) ).toInt() ) );
162 r->
setCircleWidth( symbologyElem.attribute( QStringLiteral(
"circleWidth" ), QStringLiteral(
"0.4" ) ).toDouble() );
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() );
173 QDomElement embeddedRendererElem = symbologyElem.firstChildElement( QStringLiteral(
"renderer-v2" ) );
174 if ( !embeddedRendererElem.isNull() )
180 QDomElement centerSymbolElem = symbologyElem.firstChildElement( QStringLiteral(
"symbol" ) );
181 if ( !centerSymbolElem.isNull() )
183 r->
setCenterSymbol( QgsSymbolLayerUtils::loadSymbol<QgsMarkerSymbol>( centerSymbolElem, context ) );
190 return mCenterSymbol.get();
196 rendererElement.setAttribute( QStringLiteral(
"forceraster" ), (
mForceRaster ? QStringLiteral(
"1" ) : QStringLiteral(
"0" ) ) );
197 rendererElement.setAttribute( QStringLiteral(
"type" ), QStringLiteral(
"pointDisplacement" ) );
198 rendererElement.setAttribute( QStringLiteral(
"labelAttributeName" ),
mLabelAttributeName );
200 rendererElement.setAttribute( QStringLiteral(
"circleWidth" ), QString::number( mCircleWidth ) );
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 ) );
213 QDomElement embeddedRendererElem =
mRenderer->save( doc, context );
214 rendererElement.appendChild( embeddedRendererElem );
219 rendererElement.appendChild( centerSymbolElem );
227 QDomElement
orderBy = doc.createElement( QStringLiteral(
"orderby" ) );
229 rendererElement.appendChild(
orderBy );
231 rendererElement.setAttribute( QStringLiteral(
"enableorderby" ), (
mOrderByEnabled ? QStringLiteral(
"1" ) : QStringLiteral(
"0" ) ) );
233 return rendererElement;
240 attr.unite( mCenterSymbol->usedAttributes( context ) );
261 mCenterSymbol.reset( symbol );
264 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
268 symbolPositions.clear();
275 else if ( nPosition == 1 )
277 const double side = std::sqrt( std::pow( symbolDiagonal, 2 ) / 2.0 );
278 symbolPositions.append( centerPoint );
279 labelShifts.append( QPointF( side * mLabelDistanceFactor, -side * mLabelDistanceFactor ) );
285 switch ( mPlacement )
289 const double minDiameterToFitSymbols = nPosition * symbolDiagonal / ( 2.0 * M_PI );
290 const double radius = std::max( symbolDiagonal / 2, minDiameterToFitSymbols ) + circleAdditionPainterUnits;
292 const double angleStep = 2 * M_PI / nPosition;
293 double currentAngle = 0.0;
294 for (
int featureIndex = 0; featureIndex < nPosition; currentAngle += angleStep, featureIndex++ )
296 const double sinusCurrentAngle = std::sin( currentAngle );
297 const double cosinusCurrentAngle = std::cos( currentAngle );
298 const QPointF positionShift( radius * sinusCurrentAngle, radius * cosinusCurrentAngle );
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 );
304 circleRadius = radius;
309 double centerDiagonal = mCenterSymbol->size( symbolContext.
renderContext() ) * M_SQRT2;
311 int pointsRemaining = nPosition;
313 double firstRingRadius = centerDiagonal / 2.0 + symbolDiagonal / 2.0;
314 int featureIndex = 0;
315 while ( pointsRemaining > 0 )
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 );
321 double angleStep = 2 * M_PI / actualPointsCurrentRing;
322 double currentAngle = 0.0;
323 for (
int i = 0; i < actualPointsCurrentRing; ++i )
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;
335 pointsRemaining -= actualPointsCurrentRing;
337 circleRadius = radiusCurrentRing;
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 )
348 double originalPointRadius = ( ( centerDiagonal / 2.0 + symbolDiagonal / 2.0 ) + symbolDiagonal ) / 2;
349 double userPointRadius = originalPointRadius + circleAdditionPainterUnits;
352 while ( pointsRemaining > 0 )
354 for (
int xIndex = 0; xIndex < gridSize && pointsRemaining > 0; ++xIndex )
356 QPointF positionShift( userPointRadius * xIndex, userPointRadius * yIndex );
357 symbolPositions.append( centerPoint + positionShift );
363 centralizeGrid( symbolPositions, userPointRadius, gridSize );
368 for (
int symbolIndex = 0; symbolIndex < symbolPositions.size(); ++symbolIndex )
370 if ( symbolPositions.at( symbolIndex ).x() < centerPoint.x() )
379 if ( symbolPositions.at( symbolIndex ).y() < centerPoint.y() )
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 );
393 gridRadius = userPointRadius;
399 void QgsPointDisplacementRenderer::centralizeGrid( QList<QPointF> &pointSymbolPositions,
double radius,
int size )
const
401 double shiftAmount = -radius * ( size - 1.0 ) / 2.0;
402 QPointF centralShift( shiftAmount, shiftAmount );
403 for (
int i = 0; i < pointSymbolPositions.size(); ++i )
405 pointSymbolPositions[i] += centralShift;
410 QList<QPointF> pointSymbolPositions,
int nSymbols )
413 if ( nSymbols < 2 || !p )
418 QPen gridPen( mCircleColor );
420 p->setPen( gridPen );
422 for (
int i = 0; i < pointSymbolPositions.size(); ++i )
424 if ( i + 1 < pointSymbolPositions.size() && 0 != ( i + 1 ) % gridSizeUnits )
426 QLineF gridLineRow( pointSymbolPositions[i], pointSymbolPositions[i + 1] );
427 p->drawLine( gridLineRow );
430 if ( i + gridSizeUnits < pointSymbolPositions.size() )
432 QLineF gridLineColumn( pointSymbolPositions[i], pointSymbolPositions[i + gridSizeUnits] );
433 p->drawLine( gridLineColumn );
438 void QgsPointDisplacementRenderer::drawCircle(
double radiusPainterUnits,
QgsSymbolRenderContext &context, QPointF centerPoint,
int nSymbols )
441 if ( nSymbols < 2 || !p )
447 QPen circlePen( mCircleColor );
449 p->setPen( circlePen );
450 p->drawArc( QRectF( centerPoint.x() - radiusPainterUnits, centerPoint.y() - radiusPainterUnits, 2 * radiusPainterUnits, 2 * radiusPainterUnits ), 0, 5760 );
453 void QgsPointDisplacementRenderer::drawSymbols(
const ClusteredGroup &group,
QgsRenderContext &context,
const QList<QPointF> &symbolPositions )
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 )
461 groupIt->symbol()->startRender( context );
462 groupIt->symbol()->renderPoint( *symbolPosIt, &( groupIt->feature ), context, -1, groupIt->isSelected );
469 handler->handleRenderedFeature( groupIt->feature, bounds, featureContext );
471 groupIt->symbol()->stopRender( context );
477 if ( renderer->
type() == QLatin1String(
"pointDisplacement" ) )
481 else if ( renderer->
type() == QLatin1String(
"singleSymbol" ) ||
482 renderer->
type() == QLatin1String(
"categorizedSymbol" ) ||
483 renderer->
type() == QLatin1String(
"graduatedSymbol" ) ||
484 renderer->
type() == QLatin1String(
"RuleRenderer" ) )
488 return pointRenderer;
490 else if ( renderer->
type() == QLatin1String(
"pointCluster" ) )
501 return pointRenderer;