mirror of
https://github.com/qgis/QGIS.git
synced 2025-02-25 00:58:06 -05:00
Correctly set transform for QgsHighlight
When we are highlighting a feature, we skip the early transformation and instead defer to the layer's renderer to handle this for us. By correctly setting up the render context with the correct transform and transformed map extent, we ensure that the highlight will render correctly even for complex map symbols (eg geometry generators) Fixes #48439
This commit is contained in:
parent
e67cd8c71c
commit
88ebb3f2f5
@ -83,35 +83,21 @@ void QgsHighlight::init()
|
||||
|
||||
void QgsHighlight::updateTransformedGeometry()
|
||||
{
|
||||
QgsCoordinateTransform ct = mMapCanvas->mapSettings().layerTransform( mLayer );
|
||||
if ( ct.isValid() )
|
||||
const QgsCoordinateTransform ct = mMapCanvas->mapSettings().layerTransform( mLayer );
|
||||
|
||||
// we don't auto-transform if we are highlighting a feature -- the renderer will take care
|
||||
// of that for us
|
||||
if ( ct.isValid() && !mGeometry.isNull() )
|
||||
{
|
||||
// reset to original geometry and transform
|
||||
if ( !mGeometry.isNull() )
|
||||
mGeometry = mOriginalGeometry;
|
||||
try
|
||||
{
|
||||
mGeometry = mOriginalGeometry;
|
||||
try
|
||||
{
|
||||
mGeometry.transform( ct );
|
||||
}
|
||||
catch ( QgsCsException & )
|
||||
{
|
||||
QgsDebugMsg( QStringLiteral( "Could not transform highlight geometry to canvas CRS" ) );
|
||||
}
|
||||
mGeometry.transform( ct );
|
||||
}
|
||||
else if ( mFeature.hasGeometry() )
|
||||
catch ( QgsCsException & )
|
||||
{
|
||||
mFeature.setGeometry( mOriginalGeometry );
|
||||
QgsGeometry g = mFeature.geometry();
|
||||
try
|
||||
{
|
||||
g.transform( ct );
|
||||
mFeature.setGeometry( g );
|
||||
}
|
||||
catch ( QgsCsException & )
|
||||
{
|
||||
QgsDebugMsg( QStringLiteral( "Could not transform highlight geometry to canvas CRS" ) );
|
||||
}
|
||||
QgsDebugMsg( QStringLiteral( "Could not transform highlight geometry to canvas CRS" ) );
|
||||
}
|
||||
}
|
||||
updateRect();
|
||||
@ -329,6 +315,27 @@ void QgsHighlight::paint( QPainter *p )
|
||||
return;
|
||||
|
||||
QgsRenderContext context = createRenderContext();
|
||||
const QgsCoordinateTransform layerToCanvasTransform = mMapCanvas->mapSettings().layerTransform( mLayer );
|
||||
context.setCoordinateTransform( layerToCanvasTransform );
|
||||
QgsRectangle mapExtentInLayerCrs = mMapCanvas->mapSettings().visibleExtent();
|
||||
if ( layerToCanvasTransform.isValid() )
|
||||
{
|
||||
QgsCoordinateTransform approxTransform = layerToCanvasTransform;
|
||||
approxTransform.setBallparkTransformsAreAppropriate( true );
|
||||
try
|
||||
{
|
||||
mapExtentInLayerCrs = approxTransform.transformBoundingBox( mapExtentInLayerCrs, Qgis::TransformDirection::Reverse );
|
||||
}
|
||||
catch ( QgsCsException & )
|
||||
{
|
||||
QgsDebugMsg( QStringLiteral( "Error transforming canvas extent to layer CRS" ) );
|
||||
}
|
||||
}
|
||||
if ( !mapExtentInLayerCrs.isFinite() )
|
||||
{
|
||||
return;
|
||||
}
|
||||
context.setExtent( mapExtentInLayerCrs );
|
||||
|
||||
// Because lower level outlines must be covered by upper level fill color
|
||||
// we render first with temporary opaque color, which is then replaced
|
||||
|
@ -17,26 +17,38 @@ import shutil
|
||||
|
||||
from qgis.PyQt.QtCore import (
|
||||
QSize,
|
||||
Qt
|
||||
Qt,
|
||||
QDir,
|
||||
|
||||
)
|
||||
from qgis.PyQt.QtGui import (
|
||||
QColor,
|
||||
QImage,
|
||||
QPainter,
|
||||
QResizeEvent
|
||||
QResizeEvent,
|
||||
QPixmap
|
||||
)
|
||||
from qgis.core import (
|
||||
QgsVectorLayer,
|
||||
QgsProject,
|
||||
QgsRectangle,
|
||||
QgsRenderChecker
|
||||
QgsRenderChecker,
|
||||
QgsCoordinateReferenceSystem,
|
||||
QgsMultiRenderChecker,
|
||||
QgsGeometryGeneratorSymbolLayer,
|
||||
QgsFillSymbol,
|
||||
QgsSingleSymbolRenderer,
|
||||
QgsSymbol
|
||||
)
|
||||
from qgis.gui import (
|
||||
QgsHighlight,
|
||||
QgsMapCanvas
|
||||
)
|
||||
from qgis.gui import QgsHighlight
|
||||
from qgis.testing import start_app, unittest
|
||||
from qgis.testing.mocked import get_iface
|
||||
from utilities import unitTestDataPath
|
||||
|
||||
start_app()
|
||||
app = start_app()
|
||||
TEST_DATA_DIR = unitTestDataPath()
|
||||
|
||||
|
||||
@ -48,8 +60,13 @@ class TestQgsHighlight(unittest.TestCase):
|
||||
self.iface.mapCanvas().viewport().resize(400, 400)
|
||||
# For some reason the resizeEvent is not delivered, fake it
|
||||
self.iface.mapCanvas().resizeEvent(QResizeEvent(QSize(400, 400), self.iface.mapCanvas().size()))
|
||||
self.report = "<h1>Python QgsMapCanvas Tests</h1>\n"
|
||||
|
||||
def tearDown(self):
|
||||
report_file_path = "%s/qgistest.html" % QDir.tempPath()
|
||||
with open(report_file_path, 'a') as report_file:
|
||||
report_file.write(self.report)
|
||||
|
||||
QgsProject.instance().removeAllMapLayers()
|
||||
|
||||
def runTestForLayer(self, layer, testname):
|
||||
@ -115,6 +132,70 @@ class TestQgsHighlight(unittest.TestCase):
|
||||
finally:
|
||||
self.iface.mapCanvas().scene().removeItem(highlight)
|
||||
|
||||
def test_feature_transformation(self):
|
||||
poly_shp = os.path.join(TEST_DATA_DIR, 'polys.shp')
|
||||
layer = QgsVectorLayer(poly_shp, 'Layer', 'ogr')
|
||||
|
||||
sub_symbol = QgsFillSymbol.createSimple({'color': '#8888ff', 'outline_style': 'no'})
|
||||
|
||||
sym = QgsFillSymbol()
|
||||
buffer_layer = QgsGeometryGeneratorSymbolLayer.create(
|
||||
{'geometryModifier': 'buffer($geometry, -0.4)'})
|
||||
buffer_layer.setSymbolType(QgsSymbol.Fill)
|
||||
buffer_layer.setSubSymbol(sub_symbol)
|
||||
sym.changeSymbolLayer(0, buffer_layer)
|
||||
layer.setRenderer(QgsSingleSymbolRenderer(sym))
|
||||
|
||||
canvas = QgsMapCanvas()
|
||||
canvas.setDestinationCrs(QgsCoordinateReferenceSystem('EPSG:3857'))
|
||||
canvas.setFrameStyle(0)
|
||||
canvas.resize(600, 400)
|
||||
self.assertEqual(canvas.width(), 600)
|
||||
self.assertEqual(canvas.height(), 400)
|
||||
|
||||
canvas.setLayers([layer])
|
||||
canvas.setExtent(QgsRectangle(-11960254, 4247568, -11072454, 4983088))
|
||||
canvas.show()
|
||||
|
||||
# need to wait until first redraw can occur (note that we first need to wait till drawing starts!)
|
||||
while not canvas.isDrawing():
|
||||
app.processEvents()
|
||||
canvas.waitWhileRendering()
|
||||
|
||||
feature = layer.getFeature(1)
|
||||
self.assertTrue(feature.isValid())
|
||||
|
||||
highlight = QgsHighlight(canvas, feature, layer)
|
||||
color = QColor(Qt.red)
|
||||
highlight.setColor(color)
|
||||
color.setAlpha(50)
|
||||
highlight.setFillColor(color)
|
||||
highlight.show()
|
||||
highlight.show()
|
||||
|
||||
self.assertTrue(self.canvasImageCheck('highlight_transform', 'highlight_transform', canvas))
|
||||
|
||||
def canvasImageCheck(self, name, reference_image, canvas):
|
||||
self.report += "<h2>Render {}</h2>\n".format(name)
|
||||
temp_dir = QDir.tempPath() + '/'
|
||||
file_name = temp_dir + 'rendered_' + name + ".png"
|
||||
|
||||
image = QImage(canvas.size(), QImage.Format_ARGB32)
|
||||
painter = QPainter(image)
|
||||
canvas.render(painter)
|
||||
painter.end()
|
||||
image.save(file_name)
|
||||
|
||||
checker = QgsMultiRenderChecker()
|
||||
checker.setControlPathPrefix("highlight")
|
||||
checker.setControlName("expected_" + reference_image)
|
||||
checker.setRenderedImage(file_name)
|
||||
checker.setColorTolerance(2)
|
||||
result = checker.runTest(name, 20)
|
||||
self.report += checker.report()
|
||||
print((self.report))
|
||||
return result
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
Binary file not shown.
After Width: | Height: | Size: 8.0 KiB |
Loading…
x
Reference in New Issue
Block a user