Skip to content

Commit

Permalink
[FEATURE] Add concentric ring placement mode for displacement renderer
Browse files Browse the repository at this point in the history
  • Loading branch information
nyalldawson committed Sep 18, 2015
1 parent 0af4246 commit 73d838f
Show file tree
Hide file tree
Showing 6 changed files with 244 additions and 96 deletions.
22 changes: 22 additions & 0 deletions python/core/symbology-ng/qgspointdisplacementrenderer.sip
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,15 @@ class QgsPointDisplacementRenderer : QgsFeatureRendererV2
#include <qgspointdisplacementrenderer.h>
%End
public:

/** Placement methods for dispersing points
*/
enum Placement
{
Ring, /*!< Place points in a single ring around group*/
ConcentricRings /*!< Place points in concentric rings around group*/
};

QgsPointDisplacementRenderer( const QString& labelAttributeName = "" );
~QgsPointDisplacementRenderer();

Expand Down Expand Up @@ -73,6 +82,19 @@ class QgsPointDisplacementRenderer : QgsFeatureRendererV2
void setMaxLabelScaleDenominator( double d );
double maxLabelScaleDenominator() const;

/** Returns the placement method used for dispersing the points.
* @see setPlacement()
* @note added in QGIS 2.12
*/
Placement placement() const;

/** Sets the placement method used for dispersing the points.
* @param placement placement method
* @see placement()
* @note added in QGIS 2.12
*/
void setPlacement( Placement placement );

/** Returns the symbol for the center of a displacement group (but _not_ ownership of the symbol)*/
QgsMarkerSymbolV2* centerSymbol();
/** Sets the center symbol (takes ownership)*/
Expand Down
87 changes: 68 additions & 19 deletions src/core/symbology-ng/qgspointdisplacementrenderer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ QgsPointDisplacementRenderer::QgsPointDisplacementRenderer( const QString& label
, mLabelIndex( -1 )
, mTolerance( 3 )
, mToleranceUnit( QgsSymbolV2::MM )
, mPlacement( Ring )
, mCircleWidth( 0.4 )
, mCircleColor( QColor( 125, 125, 125 ) )
, mCircleRadiusAddition( 0 )
Expand All @@ -68,6 +69,7 @@ QgsFeatureRendererV2* QgsPointDisplacementRenderer::clone() const
r->setCircleColor( mCircleColor );
r->setLabelFont( mLabelFont );
r->setLabelColor( mLabelColor );
r->setPlacement( mPlacement );
r->setCircleRadiusAddition( mCircleRadiusAddition );
r->setMaxLabelScaleDenominator( mMaxLabelScaleDenominator );
r->setTolerance( mTolerance );
Expand Down Expand Up @@ -181,16 +183,15 @@ void QgsPointDisplacementRenderer::drawGroup( const DisplacementGroup& group, Qg
}

QgsSymbolV2RenderContext symbolContext( context, QgsSymbolV2::MM, 1.0, selected );
double circleAdditionPainterUnits = symbolContext.outputLineWidth( mCircleRadiusAddition );
double minDiameterToFitSymbols = symbolList.size() * diagonal / ( 2.0 * M_PI );
double radius = qMax( diagonal / 2, minDiameterToFitSymbols ) + circleAdditionPainterUnits;

//draw Circle
drawCircle( radius, symbolContext, pt, symbolList.size() );

QList<QPointF> symbolPositions;
QList<QPointF> labelPositions;
calculateSymbolAndLabelPositions( pt, labelAttributeList.size(), radius, diagonal, symbolPositions, labelPositions );
double circleRadius = -1.0;
calculateSymbolAndLabelPositions( symbolContext, pt, symbolList.size(), diagonal, symbolPositions, labelPositions, circleRadius );

//draw Circle
if ( circleRadius > 0 )
drawCircle( circleRadius, symbolContext, pt, symbolList.size() );

//draw mid point
if ( labelAttributeList.size() > 1 )
Expand Down Expand Up @@ -357,6 +358,7 @@ QgsFeatureRendererV2* QgsPointDisplacementRenderer::create( QDomElement& symbolo
labelFont.fromString( symbologyElem.attribute( "labelFont", "" ) );
}
r->setLabelFont( labelFont );
r->setPlacement(( Placement )symbologyElem.attribute( "placement", "0" ).toInt() );
r->setCircleWidth( symbologyElem.attribute( "circleWidth", "0.4" ).toDouble() );
r->setCircleColor( QgsSymbolLayerV2Utils::decodeColor( symbologyElem.attribute( "circleColor", "" ) ) );
r->setLabelColor( QgsSymbolLayerV2Utils::decodeColor( symbologyElem.attribute( "labelColor", "" ) ) );
Expand Down Expand Up @@ -393,6 +395,7 @@ QDomElement QgsPointDisplacementRenderer::save( QDomDocument& doc )
rendererElement.setAttribute( "circleColor", QgsSymbolLayerV2Utils::encodeColor( mCircleColor ) );
rendererElement.setAttribute( "labelColor", QgsSymbolLayerV2Utils::encodeColor( mLabelColor ) );
rendererElement.setAttribute( "circleRadiusAddition", QString::number( mCircleRadiusAddition ) );
rendererElement.setAttribute( "placement", ( int )mPlacement );
rendererElement.setAttribute( "maxLabelScaleDenominator", QString::number( mMaxLabelScaleDenominator ) );
rendererElement.setAttribute( "tolerance", QString::number( mTolerance ) );
rendererElement.setAttribute( "toleranceUnit", QgsSymbolLayerV2Utils::encodeOutputUnit( mToleranceUnit ) );
Expand Down Expand Up @@ -473,8 +476,8 @@ void QgsPointDisplacementRenderer::setCenterSymbol( QgsMarkerSymbolV2* symbol )



void QgsPointDisplacementRenderer::calculateSymbolAndLabelPositions( const QPointF& centerPoint, int nPosition, double radius,
double symbolDiagonal, QList<QPointF>& symbolPositions, QList<QPointF>& labelShifts ) const
void QgsPointDisplacementRenderer::calculateSymbolAndLabelPositions( QgsSymbolV2RenderContext& symbolContext, const QPointF& centerPoint, int nPosition,
double symbolDiagonal, QList<QPointF>& symbolPositions, QList<QPointF>& labelShifts, double& circleRadius ) const
{
symbolPositions.clear();
labelShifts.clear();
Expand All @@ -490,18 +493,64 @@ void QgsPointDisplacementRenderer::calculateSymbolAndLabelPositions( const QPoin
return;
}

double fullPerimeter = 2 * M_PI;
double angleStep = fullPerimeter / nPosition;
double currentAngle;
double circleAdditionPainterUnits = symbolContext.outputLineWidth( mCircleRadiusAddition );

for ( currentAngle = 0.0; currentAngle < fullPerimeter; currentAngle += angleStep )
switch ( mPlacement )
{
double sinusCurrentAngle = sin( currentAngle );
double cosinusCurrentAngle = cos( currentAngle );
QPointF positionShift( radius * sinusCurrentAngle, radius * cosinusCurrentAngle );
QPointF labelShift(( radius + symbolDiagonal / 2 ) * sinusCurrentAngle, ( radius + symbolDiagonal / 2 ) * cosinusCurrentAngle );
symbolPositions.append( centerPoint + positionShift );
labelShifts.append( labelShift );
case Ring:
{
double minDiameterToFitSymbols = nPosition * symbolDiagonal / ( 2.0 * M_PI );
double radius = qMax( symbolDiagonal / 2, minDiameterToFitSymbols ) + circleAdditionPainterUnits;

double fullPerimeter = 2 * M_PI;
double angleStep = fullPerimeter / nPosition;
for ( double currentAngle = 0.0; currentAngle < fullPerimeter; currentAngle += angleStep )
{
double sinusCurrentAngle = sin( currentAngle );
double cosinusCurrentAngle = cos( currentAngle );
QPointF positionShift( radius * sinusCurrentAngle, radius * cosinusCurrentAngle );
QPointF labelShift(( radius + symbolDiagonal / 2 ) * sinusCurrentAngle, ( radius + symbolDiagonal / 2 ) * cosinusCurrentAngle );
symbolPositions.append( centerPoint + positionShift );
labelShifts.append( labelShift );
}

circleRadius = radius;
break;
}
case ConcentricRings:
{
double centerDiagonal = QgsSymbolLayerV2Utils::convertToPainterUnits( symbolContext.renderContext(),
M_SQRT2 * mCenterSymbol->size(),
mCenterSymbol->outputUnit(), mCenterSymbol->mapUnitScale() );

int pointsRemaining = nPosition;
int ringNumber = 1;
double firstRingRadius = centerDiagonal / 2.0 + symbolDiagonal / 2.0;
while ( pointsRemaining > 0 )
{
double radiusCurrentRing = qMax( firstRingRadius + ( ringNumber - 1 ) * symbolDiagonal + ringNumber * circleAdditionPainterUnits, 0.0 );
int maxPointsCurrentRing = qMax( floor( 2 * M_PI * radiusCurrentRing / symbolDiagonal ), 1.0 );
int actualPointsCurrentRing = qMin( maxPointsCurrentRing, pointsRemaining );

double angleStep = 2 * M_PI / actualPointsCurrentRing;
double currentAngle = 0.0;
for ( int i = 0; i < actualPointsCurrentRing; ++i )
{
double sinusCurrentAngle = sin( currentAngle );
double cosinusCurrentAngle = cos( currentAngle );
QPointF positionShift( radiusCurrentRing * sinusCurrentAngle, radiusCurrentRing * cosinusCurrentAngle );
QPointF labelShift(( radiusCurrentRing + symbolDiagonal / 2 ) * sinusCurrentAngle, ( radiusCurrentRing + symbolDiagonal / 2 ) * cosinusCurrentAngle );
symbolPositions.append( centerPoint + positionShift );
labelShifts.append( labelShift );
currentAngle += angleStep;
}

pointsRemaining -= actualPointsCurrentRing;
ringNumber++;
circleRadius = radiusCurrentRing;
}
break;
}
}
}

Expand Down
26 changes: 25 additions & 1 deletion src/core/symbology-ng/qgspointdisplacementrenderer.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,15 @@ class QgsSpatialIndex;
class CORE_EXPORT QgsPointDisplacementRenderer: public QgsFeatureRendererV2
{
public:

/** Placement methods for dispersing points
*/
enum Placement
{
Ring, /*!< Place points in a single ring around group*/
ConcentricRings /*!< Place points in concentric rings around group*/
};

QgsPointDisplacementRenderer( const QString& labelAttributeName = "" );
~QgsPointDisplacementRenderer();

Expand Down Expand Up @@ -100,6 +109,19 @@ class CORE_EXPORT QgsPointDisplacementRenderer: public QgsFeatureRendererV2
void setMaxLabelScaleDenominator( double d ) { mMaxLabelScaleDenominator = d; }
double maxLabelScaleDenominator() const { return mMaxLabelScaleDenominator; }

/** Returns the placement method used for dispersing the points.
* @see setPlacement()
* @note added in QGIS 2.12
*/
Placement placement() const { return mPlacement; }

/** Sets the placement method used for dispersing the points.
* @param placement placement method
* @see placement()
* @note added in QGIS 2.12
*/
void setPlacement( Placement placement ) { mPlacement = placement; }

/** Returns the symbol for the center of a displacement group (but _not_ ownership of the symbol)*/
QgsMarkerSymbolV2* centerSymbol() { return mCenterSymbol;}
/** Sets the center symbol (takes ownership)*/
Expand Down Expand Up @@ -173,6 +195,8 @@ class CORE_EXPORT QgsPointDisplacementRenderer: public QgsFeatureRendererV2
QgsSymbolV2::OutputUnit mToleranceUnit;
QgsMapUnitScale mToleranceMapUnitScale;

Placement mPlacement;

/** Font that is passed to the renderer*/
QFont mLabelFont;
QColor mLabelColor;
Expand Down Expand Up @@ -210,7 +234,7 @@ class CORE_EXPORT QgsPointDisplacementRenderer: public QgsFeatureRendererV2
const QStringList& labels );

//helper functions
void calculateSymbolAndLabelPositions( const QPointF& centerPoint, int nPosition, double radius, double symbolDiagonal, QList<QPointF>& symbolPositions, QList<QPointF>& labelShifts ) const;
void calculateSymbolAndLabelPositions( QgsSymbolV2RenderContext &symbolContext, const QPointF& centerPoint, int nPosition, double symbolDiagonal, QList<QPointF>& symbolPositions, QList<QPointF>& labelShifts , double &circleRadius ) const;
void drawGroup( const DisplacementGroup& group, QgsRenderContext& context );
void drawCircle( double radiusPainterUnits, QgsSymbolV2RenderContext& context, const QPointF& centerPoint, int nSymbols );
void drawSymbols( const QgsFeature& f, QgsRenderContext& context, const QList<QgsMarkerSymbolV2*>& symbolList, const QList<QPointF>& symbolPositions, bool selected = false );
Expand Down
17 changes: 17 additions & 0 deletions src/gui/symbology-ng/qgspointdisplacementrendererwidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,9 @@ QgsPointDisplacementRendererWidget::QgsPointDisplacementRendererWidget( QgsVecto

blockAllSignals( true );

mPlacementComboBox->addItem( tr( "Ring" ), QgsPointDisplacementRenderer::Ring );
mPlacementComboBox->addItem( tr( "Concentric rings" ), QgsPointDisplacementRenderer::ConcentricRings );

//insert attributes into combo box
if ( layer )
{
Expand Down Expand Up @@ -98,18 +101,23 @@ QgsPointDisplacementRendererWidget::QgsPointDisplacementRendererWidget( QgsVecto
mCircleColorButton->setColorDialogTitle( tr( "Select color" ) );
mCircleColorButton->setContext( "symbology" );
mCircleColorButton->setAllowAlpha( true );
mCircleColorButton->setShowNoColor( true );
mCircleColorButton->setNoColorString( tr( "No outline" ) );
mLabelColorButton->setContext( "symbology" );
mLabelColorButton->setColorDialogTitle( tr( "Select color" ) );
mLabelColorButton->setAllowAlpha( true );

mCircleWidthSpinBox->setValue( mRenderer->circleWidth() );
mCircleColorButton->setColor( mRenderer->circleColor() );
mLabelColorButton->setColor( mRenderer->labelColor() );
mCircleModificationSpinBox->setClearValue( 0.0 );
mCircleModificationSpinBox->setValue( mRenderer->circleRadiusAddition() );
mDistanceSpinBox->setValue( mRenderer->tolerance() );
mDistanceUnitWidget->setUnit( mRenderer->toleranceUnit() );
mDistanceUnitWidget->setMapUnitScale( mRenderer->toleranceMapUnitScale() );

mPlacementComboBox->setCurrentIndex( mPlacementComboBox->findData( mRenderer->placement() ) );

//scale dependent labelling
mMaxScaleDenominatorEdit->setText( QString::number( mRenderer->maxLabelScaleDenominator() ) );
mMaxScaleDenominatorEdit->setValidator( new QDoubleValidator( mMaxScaleDenominatorEdit ) );
Expand Down Expand Up @@ -194,6 +202,14 @@ void QgsPointDisplacementRendererWidget::on_mRendererComboBox_currentIndexChange
}
}

void QgsPointDisplacementRendererWidget::on_mPlacementComboBox_currentIndexChanged( int index )
{
if ( !mRenderer )
return;

mRenderer->setPlacement(( QgsPointDisplacementRenderer::Placement )mPlacementComboBox->itemData( index ).toInt() );
}

void QgsPointDisplacementRendererWidget::on_mRendererSettingsButton_clicked()
{
if ( mEmbeddedRendererWidget )
Expand Down Expand Up @@ -330,6 +346,7 @@ void QgsPointDisplacementRendererWidget::blockAllSignals( bool block )
mCenterSymbolPushButton->blockSignals( block );
mDistanceSpinBox->blockSignals( block );
mDistanceUnitWidget->blockSignals( block );
mPlacementComboBox->blockSignals( block );
}

void QgsPointDisplacementRendererWidget::on_mCenterSymbolPushButton_clicked()
Expand Down
1 change: 1 addition & 0 deletions src/gui/symbology-ng/qgspointdisplacementrendererwidget.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ class GUI_EXPORT QgsPointDisplacementRendererWidget: public QgsRendererV2Widget,
private slots:
void on_mLabelFieldComboBox_currentIndexChanged( const QString& text );
void on_mRendererComboBox_currentIndexChanged( int index );
void on_mPlacementComboBox_currentIndexChanged( int index );
void on_mLabelFontButton_clicked();
void on_mCircleWidthSpinBox_valueChanged( double d );
void on_mCircleColorButton_colorChanged( const QColor& newColor );
Expand Down
Loading

0 comments on commit 73d838f

Please sign in to comment.