Use direct QPainterPaths when clipping map renders using a QPainter based clip

Allows correct clipping to multipolygons, polygons with holes, etc
This commit is contained in:
Nyall Dawson 2020-07-03 09:53:29 +10:00
parent 205273e7cd
commit 040dd56b80
6 changed files with 52 additions and 9 deletions

View File

@ -772,11 +772,17 @@ QPainterPath QgsCurvePolygon::asQPainterPath() const
{
QPainterPath p;
if ( mExteriorRing )
mExteriorRing->addToPainterPath( p );
{
QPainterPath ring = mExteriorRing->asQPainterPath();
ring.closeSubpath();
p.addPath( ring );
}
for ( const QgsCurve *ring : mInteriorRings )
{
p.addPath( ring->asQPainterPath() );
QPainterPath ringPath = ring->asQPainterPath();
ringPath.closeSubpath();
p.addPath( ringPath );
}
return p;

View File

@ -169,15 +169,10 @@ QPainterPath QgsMapClippingUtils::calculatePainterClipRegion( const QList<QgsMap
if ( !shouldClip )
return QPainterPath();
// filter out polygon parts from result only
result.convertGeometryCollectionToSubclass( QgsWkbTypes::PolygonGeometry );
// transform to painter coordinates
result.mapToPixel( context.mapToPixel() );
QPainterPath path;
path.addPolygon( result.asQPolygonF() );
return path;
return result.constGet()->asQPainterPath();
}
QgsGeometry QgsMapClippingUtils::calculateLabelIntersectionGeometry( const QList<QgsMapClippingRegion> &regions, const QgsRenderContext &context, bool &shouldClip )

View File

@ -5902,7 +5902,6 @@ class TestQgsGeometry(unittest.TestCase):
rendered_image = self.renderGeometry(geom, False, True)
assert self.imageCheck(test['name'] + '_aspolygon', test['as_polygon_reference_image'], rendered_image)
def testGeometryAsQPainterPath(self):
'''Tests conversion of different geometries to QPainterPath, including bad/odd geometries.'''
@ -5980,6 +5979,9 @@ class TestQgsGeometry(unittest.TestCase):
{'name': 'Collection Mixed',
'wkt': 'GeometryCollection(Point(1 2), MultiPoint(3 3, 2 3), LineString (0 0,3 4,4 3), MultiLineString((3 1, 3 2, 4 2)), Polygon((0 0, 1 0, 1 1, 2 1, 2 2, 0 2, 0 0)), MultiPolygon(((4 0, 5 0, 5 1, 6 1, 6 2, 4 2, 4 0)),(( 1 4, 2 4, 1 5, 1 4))))',
'reference_image': 'collection_mixed'},
{'name': 'MultiCurvePolygon',
'wkt': 'MultiSurface (CurvePolygon (CompoundCurve (CircularString (-12942312 4593500, -11871048 5481118, -11363838 5065730, -11551856 4038191, -12133399 4130014),(-12133399 4130014, -12942312 4593500)),(-12120281 5175043, -12456964 4694067, -11752991 4256817, -11569346 4943300, -12120281 5175043)),Polygon ((-10856627 5625411, -11083997 4995770, -10887235 4357384, -9684796 4851477, -10069576 5428648, -10856627 5625411)))',
'reference_image': 'multicurvepolygon_with_rings'}
]
for test in tests:

View File

@ -229,6 +229,46 @@ class TestQgsVectorLayerRenderer(unittest.TestCase):
self.report += renderchecker.report()
self.assertTrue(result)
def testRenderWithPainterClipRegionsMultiPolygon(self):
poly_layer = QgsVectorLayer(os.path.join(TEST_DATA_DIR, 'polys.shp'))
self.assertTrue(poly_layer.isValid())
sym1 = QgsFillSymbol.createSimple({'color': '#ff00ff', 'outline_color': '#000000', 'outline_width': '1'})
renderer = QgsSingleSymbolRenderer(sym1)
poly_layer.setRenderer(renderer)
mapsettings = QgsMapSettings()
mapsettings.setOutputSize(QSize(400, 400))
mapsettings.setOutputDpi(96)
mapsettings.setDestinationCrs(QgsCoordinateReferenceSystem('EPSG:3857'))
mapsettings.setExtent(QgsRectangle(-13875783.2, 2266009.4, -8690110.7, 6673344.5))
mapsettings.setLayers([poly_layer])
region = QgsMapClippingRegion(QgsGeometry.fromWkt(
'MultiSurface (Polygon ((-10856627.66351187042891979 5625411.45629768911749125, -11083997.96136780828237534 4995770.63146586995571852, -10887235.20360786281526089 4357384.79517805296927691, -9684796.12840820662677288 4851477.9424419105052948, -10069576.63247209787368774 5428648.69853774644434452, -10856627.66351187042891979 5625411.45629768911749125)),Polygon ((-12045949.22152753174304962 5533588.83600971661508083, -12758667.65519132651388645 4868967.96535390708595514, -12478827.28859940730035305 4296169.71498607192188501, -11783598.87784760631620884 4077544.42858613422140479, -11223918.14466376602649689 4715930.26487395167350769, -11127723.01864779368042946 5673509.01930567622184753, -11359465.8222317285835743 5809056.69687363691627979, -12045949.22152753174304962 5533588.83600971661508083),(-11341975.79931973293423653 4790262.86224992945790291, -11722383.7976556234061718 4318032.24362606555223465, -12019714.18715953826904297 4606617.62167398259043694, -11757363.84347961470484734 4908320.51690589636564255, -11341975.79931973293423653 4790262.86224992945790291)))'))
region.setFeatureClip(QgsMapClippingRegion.FeatureClippingType.ClipPainterOnly)
mapsettings.addClippingRegion(region)
renderchecker = QgsMultiRenderChecker()
renderchecker.setMapSettings(mapsettings)
renderchecker.setControlPathPrefix('vectorlayerrenderer')
renderchecker.setControlName('expected_painterclip_region_multi')
result = renderchecker.runTest('expected_painterclip_region_multi')
self.report += renderchecker.report()
self.assertTrue(result)
# also try with symbol levels
renderer.setUsingSymbolLevels(True)
poly_layer.setRenderer(renderer)
renderchecker = QgsMultiRenderChecker()
renderchecker.setMapSettings(mapsettings)
renderchecker.setControlPathPrefix('vectorlayerrenderer')
renderchecker.setControlName('expected_painterclip_region_multi')
result = renderchecker.runTest('expected_painterclip_region_multi')
self.report += renderchecker.report()
self.assertTrue(result)
if __name__ == '__main__':
unittest.main()

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 460 KiB