Skip to content

Commit

Permalink
[api] Allow reference scale for vector layer renderer to be set
Browse files Browse the repository at this point in the history
Gives a means of setting the reference scale for a vector layer
renderer, so that symbol sizes in the rendered layer will be
scaled accordingly depending on the actual scale of the
rendered map.
  • Loading branch information
nyalldawson committed Jun 28, 2021
1 parent c8da0c4 commit 849819c
Show file tree
Hide file tree
Showing 10 changed files with 128 additions and 3 deletions.
2 changes: 1 addition & 1 deletion python/core/auto_generated/qgsrendercontext.sip.in
Original file line number Diff line number Diff line change
Expand Up @@ -337,7 +337,7 @@ for the rendered map, eg 1000.0 for a 1:1000 map render.

double symbologyReferenceScale() const;
%Docstring
Returns the symbology reference ``scale``.
Returns the symbology reference scale.

This represents the desired scale denominator for the rendered map, eg 1000.0 for a 1:1000 map render.
A value of -1 indicates that symbology scaling by reference scale is disabled.
Expand Down
35 changes: 35 additions & 0 deletions python/core/auto_generated/symbology/qgsrenderer.sip.in
Original file line number Diff line number Diff line change
Expand Up @@ -421,6 +421,40 @@ Sets whether the renderer should be rendered to a raster destination.
.. seealso:: :py:func:`forceRasterRender`

.. versionadded:: 2.12
%End

double referenceScale() const;
%Docstring
Returns the symbology reference scale.

This represents the desired scale denominator for the rendered map, eg 1000.0 for a 1:1000 map render.
A value of -1 indicates that symbology scaling by reference scale is disabled.

The symbology reference scale is an optional property which specifies the reference
scale at which symbology in paper units (such a millimeters or points) is fixed
to. For instance, if the scale is 1000 then a 2mm thick line will be rendered at
exactly 2mm thick when a map is rendered at 1:1000, or 1mm thick when rendered at 1:2000, or 4mm thick at 1:500.

.. seealso:: :py:func:`setReferenceScale`

.. versionadded:: 3.22
%End

void setReferenceScale( double scale );
%Docstring
Sets the symbology reference ``scale``.

This should match the desired scale denominator for the rendered map, eg 1000.0 for a 1:1000 map render.
Set to -1 to disable symbology scaling by reference scale.

The symbology reference scale is an optional property which specifies the reference
scale at which symbology in paper units (such a millimeters or points) is fixed
to. For instance, if ``scale`` is set to 1000 then a 2mm thick line will be rendered at
exactly 2mm thick when a map is rendered at 1:1000, or 1mm thick when rendered at 1:2000, or 4mm thick at 1:500.

.. seealso:: :py:func:`referenceScale`

.. versionadded:: 3.22
%End

QgsFeatureRequest::OrderBy orderBy() const;
Expand Down Expand Up @@ -562,6 +596,7 @@ to store all common base class properties in the DOM ``element``.




static void convertSymbolSizeScale( QgsSymbol *symbol, Qgis::ScaleMethod method, const QString &field );
%Docstring

Expand Down
2 changes: 1 addition & 1 deletion src/core/qgsrendercontext.h
Original file line number Diff line number Diff line change
Expand Up @@ -386,7 +386,7 @@ class CORE_EXPORT QgsRenderContext : public QgsTemporalRangeObject


/**
* Returns the symbology reference \a scale.
* Returns the symbology reference scale.
*
* This represents the desired scale denominator for the rendered map, eg 1000.0 for a 1:1000 map render.
* A value of -1 indicates that symbology scaling by reference scale is disabled.
Expand Down
3 changes: 3 additions & 0 deletions src/core/symbology/qgsrenderer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ void QgsFeatureRenderer::copyRendererData( QgsFeatureRenderer *destRenderer ) co
destRenderer->setUsingSymbolLevels( mUsingSymbolLevels );
destRenderer->mOrderBy = mOrderBy;
destRenderer->mOrderByEnabled = mOrderByEnabled;
destRenderer->mReferenceScale = mReferenceScale;
}

QgsFeatureRenderer::QgsFeatureRenderer( const QString &type )
Expand Down Expand Up @@ -171,6 +172,7 @@ QgsFeatureRenderer *QgsFeatureRenderer::load( QDomElement &element, const QgsRea
{
r->setUsingSymbolLevels( element.attribute( QStringLiteral( "symbollevels" ), QStringLiteral( "0" ) ).toInt() );
r->setForceRasterRender( element.attribute( QStringLiteral( "forceraster" ), QStringLiteral( "0" ) ).toInt() );
r->setReferenceScale( element.attribute( QStringLiteral( "referencescale" ), QStringLiteral( "-1" ) ).toDouble() );

//restore layer effect
QDomElement effectElem = element.firstChildElement( QStringLiteral( "effect" ) );
Expand Down Expand Up @@ -202,6 +204,7 @@ void QgsFeatureRenderer::saveRendererData( QDomDocument &doc, QDomElement &rende
{
rendererElem.setAttribute( QStringLiteral( "forceraster" ), ( mForceRaster ? QStringLiteral( "1" ) : QStringLiteral( "0" ) ) );
rendererElem.setAttribute( QStringLiteral( "symbollevels" ), ( mUsingSymbolLevels ? QStringLiteral( "1" ) : QStringLiteral( "0" ) ) );
rendererElem.setAttribute( QStringLiteral( "referencescale" ), mReferenceScale );

if ( mPaintEffect && !QgsPaintEffectRegistry::isDefaultStack( mPaintEffect ) )
mPaintEffect->saveProperties( doc, rendererElem );
Expand Down
34 changes: 34 additions & 0 deletions src/core/symbology/qgsrenderer.h
Original file line number Diff line number Diff line change
Expand Up @@ -434,6 +434,38 @@ class CORE_EXPORT QgsFeatureRenderer
*/
void setForceRasterRender( bool forceRaster ) { mForceRaster = forceRaster; }

/**
* Returns the symbology reference scale.
*
* This represents the desired scale denominator for the rendered map, eg 1000.0 for a 1:1000 map render.
* A value of -1 indicates that symbology scaling by reference scale is disabled.
*
* The symbology reference scale is an optional property which specifies the reference
* scale at which symbology in paper units (such a millimeters or points) is fixed
* to. For instance, if the scale is 1000 then a 2mm thick line will be rendered at
* exactly 2mm thick when a map is rendered at 1:1000, or 1mm thick when rendered at 1:2000, or 4mm thick at 1:500.
*
* \see setReferenceScale()
* \since QGIS 3.22
*/
double referenceScale() const { return mReferenceScale; }

/**
* Sets the symbology reference \a scale.
*
* This should match the desired scale denominator for the rendered map, eg 1000.0 for a 1:1000 map render.
* Set to -1 to disable symbology scaling by reference scale.
*
* The symbology reference scale is an optional property which specifies the reference
* scale at which symbology in paper units (such a millimeters or points) is fixed
* to. For instance, if \a scale is set to 1000 then a 2mm thick line will be rendered at
* exactly 2mm thick when a map is rendered at 1:1000, or 1mm thick when rendered at 1:2000, or 4mm thick at 1:500.
*
* \see referenceScale()
* \since QGIS 3.22
*/
void setReferenceScale( double scale ) { mReferenceScale = scale; }

/**
* Gets the order in which features shall be processed by this renderer.
* \note this property has no effect if orderByEnabled() is FALSE
Expand Down Expand Up @@ -553,6 +585,8 @@ class CORE_EXPORT QgsFeatureRenderer

bool mForceRaster = false;

double mReferenceScale = -1.0;

/**
* \note this function is used to convert old sizeScale expressions to symbol
* level DataDefined size
Expand Down
1 change: 1 addition & 0 deletions src/core/vector/qgsvectorlayerrenderer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,7 @@ bool QgsVectorLayerRenderer::renderInternal( QgsFeatureRenderer *renderer )
}

QgsRenderContext &context = *renderContext();
context.setSymbologyReferenceScale( renderer->referenceScale() );

QgsScopedQPainterState painterState( context.painter() );

Expand Down
54 changes: 53 additions & 1 deletion tests/src/python/test_qgsvectorlayerrenderer.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@
QgsCategorizedSymbolRenderer,
QgsRendererCategory,
QgsCentroidFillSymbolLayer,
QgsMarkerSymbol
QgsMarkerSymbol,
QgsLineSymbol
)
from qgis.testing import start_app, unittest
from utilities import (unitTestDataPath)
Expand Down Expand Up @@ -666,6 +667,57 @@ def createRenderer(self):
self.report += renderchecker.report()
self.assertTrue(result)

def test_reference_scale(self):
"""
Test rendering a layer with a reference scale set
"""
layer = QgsVectorLayer(os.path.join(TEST_DATA_DIR, 'lines.shp'), 'Lines', 'ogr')
self.assertTrue(layer.isValid())

sym1 = QgsLineSymbol.createSimple({'line_color': '#4dbf6f', 'line_width': 4, 'line_width_unit': "points"})

renderer = QgsSingleSymbolRenderer(sym1)
layer.setRenderer(renderer)

mapsettings = QgsMapSettings()
mapsettings.setDestinationCrs(layer.crs())
mapsettings.setOutputSize(QSize(400, 400))
mapsettings.setOutputDpi(96)
mapsettings.setExtent(layer.extent())
mapsettings.setLayers([layer])
self.assertAlmostEqual(mapsettings.scale(), 22738556, -5)

# Setup rendering check
renderchecker = QgsMultiRenderChecker()
renderchecker.setMapSettings(mapsettings)
renderchecker.setControlPathPrefix('vectorlayerrenderer')
renderchecker.setControlName('expected_reference_scale_not_set')
result = renderchecker.runTest('expected_reference_scale_not_set')
self.report += renderchecker.report()
self.assertTrue(result)

# Set the reference scale as half the map scale -- the lines should be double as wide
# as their preset width
renderer.setReferenceScale(22738556 * 2)
renderchecker = QgsMultiRenderChecker()
renderchecker.setMapSettings(mapsettings)
renderchecker.setControlPathPrefix('vectorlayerrenderer')
renderchecker.setControlName('expected_reference_scale_double')
result = renderchecker.runTest('expected_reference_scale_double')
self.report += renderchecker.report()
self.assertTrue(result)

# Set the reference scale as double the map scale -- the lines should be half as wide
# as their preset width
renderer.setReferenceScale(22738556 / 2)
renderchecker = QgsMultiRenderChecker()
renderchecker.setMapSettings(mapsettings)
renderchecker.setControlPathPrefix('vectorlayerrenderer')
renderchecker.setControlName('expected_reference_scale_half')
result = renderchecker.runTest('expected_reference_scale_half')
self.report += renderchecker.report()
self.assertTrue(result)


if __name__ == '__main__':
unittest.main()
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 849819c

Please sign in to comment.