Skip to content
Permalink
Browse files
[api] Allow reference scale for vector layer renderer to be set
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 849819cd0d5d75df4a4c2138fae3ea8880cc2a58
@@ -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.
@@ -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;
@@ -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

@@ -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.
@@ -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 )
@@ -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" ) );
@@ -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 );
@@ -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
@@ -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
@@ -245,6 +245,7 @@ bool QgsVectorLayerRenderer::renderInternal( QgsFeatureRenderer *renderer )
}

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

QgsScopedQPainterState painterState( context.painter() );

@@ -30,7 +30,8 @@
QgsCategorizedSymbolRenderer,
QgsRendererCategory,
QgsCentroidFillSymbolLayer,
QgsMarkerSymbol
QgsMarkerSymbol,
QgsLineSymbol
)
from qgis.testing import start_app, unittest
from utilities import (unitTestDataPath)
@@ -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()
Binary file not shown.
Binary file not shown.
Binary file not shown.

0 comments on commit 849819c

Please sign in to comment.