[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.
This commit is contained in:
Nyall Dawson 2021-06-20 11:32:51 +10:00
parent c8da0c43f9
commit 849819cd0d
10 changed files with 128 additions and 3 deletions

View File

@ -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.

View File

@ -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

View File

@ -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.

View File

@ -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 );

View File

@ -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

View File

@ -245,6 +245,7 @@ bool QgsVectorLayerRenderer::renderInternal( QgsFeatureRenderer *renderer )
}
QgsRenderContext &context = *renderContext();
context.setSymbologyReferenceScale( renderer->referenceScale() );
QgsScopedQPainterState painterState( context.painter() );

View File

@ -30,7 +30,8 @@ from qgis.core import (QgsVectorLayer,
QgsCategorizedSymbolRenderer,
QgsRendererCategory,
QgsCentroidFillSymbolLayer,
QgsMarkerSymbol
QgsMarkerSymbol,
QgsLineSymbol
)
from qgis.testing import start_app, unittest
from utilities import (unitTestDataPath)
@ -666,6 +667,57 @@ class TestQgsVectorLayerRenderer(unittest.TestCase):
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.

After

Width:  |  Height:  |  Size: 460 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 460 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 460 KiB