mirror of
https://github.com/qgis/QGIS.git
synced 2025-12-30 00:29:39 -05:00
Allow access to feature geometry in generator subsymbols via
geometry($currentfeature)
When used in a geometry generator subsymbol, the expression
geometry($currentfeature)
should refer to the original feature's geometry, not the geometry
generated by the generator symbol (which is accessible through
$geometry)
Fixes #46455
This commit is contained in:
parent
84f3b42861
commit
914dccbd5b
@ -305,7 +305,7 @@ bool QgsGeometryGeneratorSymbolLayer::isCompatibleWithSymbol( QgsSymbol *symbol
|
||||
return true;
|
||||
}
|
||||
|
||||
QgsGeometry QgsGeometryGeneratorSymbolLayer::evaluateGeometryInPainterUnits( const QgsGeometry &input, const QgsFeature &feature, const QgsRenderContext &renderContext, QgsExpressionContext &expressionContext ) const
|
||||
QgsGeometry QgsGeometryGeneratorSymbolLayer::evaluateGeometryInPainterUnits( const QgsGeometry &input, const QgsFeature &, const QgsRenderContext &renderContext, QgsExpressionContext &expressionContext ) const
|
||||
{
|
||||
QgsGeometry drawGeometry( input );
|
||||
// step 1 - scale the draw geometry from PAINTER units to target units (e.g. millimeters)
|
||||
@ -314,11 +314,9 @@ QgsGeometry QgsGeometryGeneratorSymbolLayer::evaluateGeometryInPainterUnits( con
|
||||
drawGeometry.transform( painterToTargetUnits );
|
||||
|
||||
// step 2 - set the feature to use the new scaled geometry, and inject it into the expression context
|
||||
QgsFeature f( feature );
|
||||
f.setGeometry( drawGeometry );
|
||||
QgsExpressionContextScope *generatorScope = new QgsExpressionContextScope();
|
||||
QgsExpressionContextScopePopper popper( expressionContext, generatorScope );
|
||||
generatorScope->setFeature( f );
|
||||
generatorScope->setGeometry( drawGeometry );
|
||||
|
||||
// step 3 - evaluate the new generated geometry.
|
||||
QgsGeometry geom = mExpression->evaluate( &expressionContext ).value<QgsGeometry>();
|
||||
@ -483,8 +481,8 @@ void QgsGeometryGeneratorSymbolLayer::render( QgsSymbolRenderContext &context, Q
|
||||
}
|
||||
|
||||
QgsExpressionContextScope *subSymbolExpressionContextScope = mSymbol->symbolRenderContext()->expressionContextScope();
|
||||
|
||||
subSymbolExpressionContextScope->setFeature( f );
|
||||
// override the $geometry value for all subsymbols -- this should be the generated geometry
|
||||
subSymbolExpressionContextScope->setGeometry( f.geometry() );
|
||||
|
||||
const bool prevIsSubsymbol = context.renderContext().flags() & Qgis::RenderContextFlag::RenderingSubSymbol;
|
||||
context.renderContext().setFlag( Qgis::RenderContextFlag::RenderingSubSymbol );
|
||||
|
||||
@ -47,7 +47,11 @@ from qgis.core import (
|
||||
QgsCoordinateTransform,
|
||||
QgsArrowSymbolLayer,
|
||||
QgsFeature,
|
||||
QgsGeometry
|
||||
QgsGeometry,
|
||||
QgsFontMarkerSymbolLayer,
|
||||
QgsFontUtils,
|
||||
QgsSymbolLayer,
|
||||
QgsProperty
|
||||
)
|
||||
|
||||
from qgis.testing import start_app, unittest
|
||||
@ -410,6 +414,78 @@ class TestQgsGeometryGeneratorSymbolLayerV2(unittest.TestCase):
|
||||
self.report += renderchecker.report()
|
||||
self.assertTrue(res)
|
||||
|
||||
def test_geometry_function(self):
|
||||
"""
|
||||
The $geometry function used in a subsymbol should refer to the generated geometry
|
||||
"""
|
||||
points = QgsVectorLayer('Point?crs=epsg:4326', 'Points', 'memory')
|
||||
self.assertTrue(points.isValid())
|
||||
f = QgsFeature()
|
||||
f.setGeometry(QgsGeometry.fromWkt('Point(1 2)'))
|
||||
points.dataProvider().addFeature(f)
|
||||
|
||||
font = QgsFontUtils.getStandardTestFont('Bold')
|
||||
font_marker = QgsFontMarkerSymbolLayer(font.family(), 'x', 16)
|
||||
font_marker.setDataDefinedProperty(QgsSymbolLayer.PropertyCharacter, QgsProperty.fromExpression('geom_to_wkt($geometry)'))
|
||||
subsymbol = QgsMarkerSymbol()
|
||||
subsymbol.changeSymbolLayer(0, font_marker)
|
||||
|
||||
parent_generator = QgsGeometryGeneratorSymbolLayer.create({'geometryModifier': 'translate($geometry, 1, 2)'})
|
||||
parent_generator.setSymbolType(QgsSymbol.Marker)
|
||||
|
||||
parent_generator.setSubSymbol(subsymbol)
|
||||
|
||||
geom_symbol = QgsMarkerSymbol()
|
||||
geom_symbol.changeSymbolLayer(0, parent_generator)
|
||||
points.renderer().setSymbol(geom_symbol)
|
||||
|
||||
mapsettings = QgsMapSettings(self.mapsettings)
|
||||
mapsettings.setExtent(QgsRectangle(0, 0, 5, 5))
|
||||
mapsettings.setLayers([points])
|
||||
|
||||
renderchecker = QgsMultiRenderChecker()
|
||||
renderchecker.setMapSettings(mapsettings)
|
||||
renderchecker.setControlName('expected_geometrygenerator_function_geometry')
|
||||
res = renderchecker.runTest('geometrygenerator_function_geometry')
|
||||
self.report += renderchecker.report()
|
||||
self.assertTrue(res)
|
||||
|
||||
def test_feature_geometry(self):
|
||||
"""
|
||||
The geometry($currentfeature) expression used in a subsymbol should refer to the original FEATURE geometry
|
||||
"""
|
||||
points = QgsVectorLayer('Point?crs=epsg:4326', 'Points', 'memory')
|
||||
self.assertTrue(points.isValid())
|
||||
f = QgsFeature()
|
||||
f.setGeometry(QgsGeometry.fromWkt('Point(1 2)'))
|
||||
points.dataProvider().addFeature(f)
|
||||
|
||||
font = QgsFontUtils.getStandardTestFont('Bold')
|
||||
font_marker = QgsFontMarkerSymbolLayer(font.family(), 'x', 16)
|
||||
font_marker.setDataDefinedProperty(QgsSymbolLayer.PropertyCharacter, QgsProperty.fromExpression('geom_to_wkt(geometry($currentfeature))'))
|
||||
subsymbol = QgsMarkerSymbol()
|
||||
subsymbol.changeSymbolLayer(0, font_marker)
|
||||
|
||||
parent_generator = QgsGeometryGeneratorSymbolLayer.create({'geometryModifier': 'translate($geometry, 1, 2)'})
|
||||
parent_generator.setSymbolType(QgsSymbol.Marker)
|
||||
|
||||
parent_generator.setSubSymbol(subsymbol)
|
||||
|
||||
geom_symbol = QgsMarkerSymbol()
|
||||
geom_symbol.changeSymbolLayer(0, parent_generator)
|
||||
points.renderer().setSymbol(geom_symbol)
|
||||
|
||||
mapsettings = QgsMapSettings(self.mapsettings)
|
||||
mapsettings.setExtent(QgsRectangle(0, 0, 5, 5))
|
||||
mapsettings.setLayers([points])
|
||||
|
||||
renderchecker = QgsMultiRenderChecker()
|
||||
renderchecker.setMapSettings(mapsettings)
|
||||
renderchecker.setControlName('expected_geometrygenerator_feature_geometry')
|
||||
res = renderchecker.runTest('geometrygenerator_feature_geometry')
|
||||
self.report += renderchecker.report()
|
||||
self.assertTrue(res)
|
||||
|
||||
def imageCheck(self, name, reference_image, image):
|
||||
self.report += "<h2>Render {}</h2>\n".format(name)
|
||||
temp_dir = QDir.tempPath() + '/'
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 3.4 MiB |
Binary file not shown.
|
After Width: | Height: | Size: 3.4 MiB |
Loading…
x
Reference in New Issue
Block a user