diff --git a/python/core/auto_generated/qgsmapsettings.sip.in b/python/core/auto_generated/qgsmapsettings.sip.in index 6576ad48a05..e25384c5577 100644 --- a/python/core/auto_generated/qgsmapsettings.sip.in +++ b/python/core/auto_generated/qgsmapsettings.sip.in @@ -667,6 +667,24 @@ Returns the list of regions to avoid placing labels within. .. seealso:: :py:func:`labelBoundaryGeometry` .. versionadded:: 3.6 +%End + + void addClippingRegion( const QgsMapClippingRegion ®ion ); +%Docstring +Adds a new clipping ``region`` to the map settings. + +.. seealso:: :py:func:`clippingRegions` + +.. versionadded:: 3.16 +%End + + QList< QgsMapClippingRegion > clippingRegions() const; +%Docstring +Returns the list of clipping regions to apply to the map. + +.. seealso:: :py:func:`addClippingRegion` + +.. versionadded:: 3.16 %End void setSimplifyMethod( const QgsVectorSimplifyMethod &method ); diff --git a/python/core/auto_generated/qgsrendercontext.sip.in b/python/core/auto_generated/qgsrendercontext.sip.in index e17e0a2a61e..e435b6f7bef 100644 --- a/python/core/auto_generated/qgsrendercontext.sip.in +++ b/python/core/auto_generated/qgsrendercontext.sip.in @@ -12,7 +12,6 @@ - class QgsRenderContext : QgsTemporalRangeObject { %Docstring @@ -27,6 +26,7 @@ to be rendered etc. %End public: QgsRenderContext(); + ~QgsRenderContext(); QgsRenderContext( const QgsRenderContext &rh ); @@ -787,6 +787,15 @@ Clears the specified custom rendering flag. .. seealso:: :py:func:`setCustomRenderingFlag` .. versionadded:: 3.12 +%End + + QList< QgsMapClippingRegion > clippingRegions() const; +%Docstring +Returns the list of clipping regions to apply during the render. + +These regions are always in the final destination CRS for the map. + +.. versionadded:: 3.16 %End }; diff --git a/src/core/qgsmapsettings.cpp b/src/core/qgsmapsettings.cpp index c8dd38abdaf..0e7aad0d2fa 100644 --- a/src/core/qgsmapsettings.cpp +++ b/src/core/qgsmapsettings.cpp @@ -682,6 +682,16 @@ void QgsMapSettings::setLabelBoundaryGeometry( const QgsGeometry &boundary ) mLabelBoundaryGeometry = boundary; } +void QgsMapSettings::addClippingRegion( const QgsMapClippingRegion ®ion ) +{ + mClippingRegions.append( region ); +} + +QList QgsMapSettings::clippingRegions() const +{ + return mClippingRegions; +} + void QgsMapSettings::addRenderedFeatureHandler( QgsRenderedFeatureHandlerInterface *handler ) { mRenderedFeatureHandlers.append( handler ); diff --git a/src/core/qgsmapsettings.h b/src/core/qgsmapsettings.h index 763b0e26ad8..f3a3bfc5374 100644 --- a/src/core/qgsmapsettings.h +++ b/src/core/qgsmapsettings.h @@ -33,6 +33,7 @@ #include "qgsmaplayer.h" #include "qgsgeometry.h" #include "qgstemporalrangeobject.h" +#include "qgsmapclippingregion.h" class QPainter; @@ -578,6 +579,24 @@ class CORE_EXPORT QgsMapSettings : public QgsTemporalRangeObject */ QList< QgsLabelBlockingRegion > labelBlockingRegions() const { return mLabelBlockingRegions; } + /** + * Adds a new clipping \a region to the map settings. + * + * \see clippingRegions() + * + * \since QGIS 3.16 + */ + void addClippingRegion( const QgsMapClippingRegion ®ion ); + + /** + * Returns the list of clipping regions to apply to the map. + * + * \see addClippingRegion() + * + * \since QGIS 3.16 + */ + QList< QgsMapClippingRegion > clippingRegions() const; + /** * Sets the simplification setting to use when rendering vector layers. * @@ -692,6 +711,7 @@ class CORE_EXPORT QgsMapSettings : public QgsTemporalRangeObject private: QList< QgsLabelBlockingRegion > mLabelBlockingRegions; + QList< QgsMapClippingRegion > mClippingRegions; QList< QgsRenderedFeatureHandlerInterface * > mRenderedFeatureHandlers; }; diff --git a/src/core/qgsrendercontext.cpp b/src/core/qgsrendercontext.cpp index b9a34ab432b..89c8c9a5c7a 100644 --- a/src/core/qgsrendercontext.cpp +++ b/src/core/qgsrendercontext.cpp @@ -37,6 +37,8 @@ QgsRenderContext::QgsRenderContext() mDistanceArea.setEllipsoid( mDistanceArea.sourceCrs().ellipsoidAcronym() ); } +QgsRenderContext::~QgsRenderContext() = default; + QgsRenderContext::QgsRenderContext( const QgsRenderContext &rh ) : QgsTemporalRangeObject( rh ) , mFlags( rh.mFlags ) @@ -65,6 +67,7 @@ QgsRenderContext::QgsRenderContext( const QgsRenderContext &rh ) , mHasRenderedFeatureHandlers( rh.mHasRenderedFeatureHandlers ) , mCustomRenderingFlags( rh.mCustomRenderingFlags ) , mDisabledSymbolLayers() + , mClippingRegions( rh.mClippingRegions ) #ifdef QGISDEBUG , mHasTransformContext( rh.mHasTransformContext ) #endif @@ -98,6 +101,7 @@ QgsRenderContext &QgsRenderContext::operator=( const QgsRenderContext &rh ) mRenderedFeatureHandlers = rh.mRenderedFeatureHandlers; mHasRenderedFeatureHandlers = rh.mHasRenderedFeatureHandlers; mCustomRenderingFlags = rh.mCustomRenderingFlags; + mClippingRegions = rh.mClippingRegions; setIsTemporal( rh.isTemporal() ); if ( isTemporal() ) setTemporalRange( rh.temporalRange() ); @@ -209,6 +213,8 @@ QgsRenderContext QgsRenderContext::fromMapSettings( const QgsMapSettings &mapSet if ( ctx.isTemporal() ) ctx.setTemporalRange( mapSettings.temporalRange() ); + ctx.mClippingRegions = mapSettings.clippingRegions(); + return ctx; } @@ -491,4 +497,9 @@ QList QgsRenderContext::renderedFeatureHan return mRenderedFeatureHandlers; } +QList QgsRenderContext::clippingRegions() const +{ + return mClippingRegions; +} + diff --git a/src/core/qgsrendercontext.h b/src/core/qgsrendercontext.h index f07942a8082..4e2afecc5ff 100644 --- a/src/core/qgsrendercontext.h +++ b/src/core/qgsrendercontext.h @@ -43,8 +43,8 @@ class QgsLabelingEngine; class QgsMapSettings; class QgsRenderedFeatureHandlerInterface; class QgsSymbolLayer; - class QgsMaskIdProvider; +class QgsMapClippingRegion; /** @@ -58,6 +58,7 @@ class CORE_EXPORT QgsRenderContext : public QgsTemporalRangeObject { public: QgsRenderContext(); + ~QgsRenderContext() override; QgsRenderContext( const QgsRenderContext &rh ); QgsRenderContext &operator=( const QgsRenderContext &rh ); @@ -786,6 +787,15 @@ class CORE_EXPORT QgsRenderContext : public QgsTemporalRangeObject */ void clearCustomRenderingFlag( const QString &flag ) { mCustomRenderingFlags.remove( flag ); } + /** + * Returns the list of clipping regions to apply during the render. + * + * These regions are always in the final destination CRS for the map. + * + * \since QGIS 3.16 + */ + QList< QgsMapClippingRegion > clippingRegions() const; + private: Flags mFlags; @@ -877,6 +887,8 @@ class CORE_EXPORT QgsRenderContext : public QgsTemporalRangeObject QSet mDisabledSymbolLayers; + QList< QgsMapClippingRegion > mClippingRegions; + #ifdef QGISDEBUG bool mHasTransformContext = false; #endif diff --git a/tests/src/core/testqgsmapsettings.cpp b/tests/src/core/testqgsmapsettings.cpp index f915eeac720..b188cc35861 100644 --- a/tests/src/core/testqgsmapsettings.cpp +++ b/tests/src/core/testqgsmapsettings.cpp @@ -61,6 +61,7 @@ class TestQgsMapSettings: public QObject void testExpressionContext(); void testRenderedFeatureHandlers(); void testCustomRenderingFlags(); + void testClippingRegions(); private: QString toString( const QPolygonF &p, int decimalPlaces = 2 ) const; @@ -588,5 +589,32 @@ void TestQgsMapSettings::testCustomRenderingFlags() Q_NOWARN_DEPRECATED_POP } +void TestQgsMapSettings::testClippingRegions() +{ + QgsMapSettings settings; + QVERIFY( settings.clippingRegions().isEmpty() ); + + QgsMapClippingRegion region( QgsGeometry::fromWkt( QStringLiteral( "Polygon(( 0 0, 1 0 , 1 1 , 0 1, 0 0 ))" ) ) ); + settings.addClippingRegion( region ); + QCOMPARE( settings.clippingRegions().size(), 1 ); + QCOMPARE( settings.clippingRegions().at( 0 ).geometry().asWkt(), QStringLiteral( "Polygon ((0 0, 1 0, 1 1, 0 1, 0 0))" ) ); + QgsMapClippingRegion region2( QgsGeometry::fromWkt( QStringLiteral( "Polygon(( 10 0, 11 0 , 11 1 , 10 1, 10 0 ))" ) ) ); + settings.addClippingRegion( region2 ); + QCOMPARE( settings.clippingRegions().size(), 2 ); + QCOMPARE( settings.clippingRegions().at( 0 ).geometry().asWkt(), QStringLiteral( "Polygon ((0 0, 1 0, 1 1, 0 1, 0 0))" ) ); + QCOMPARE( settings.clippingRegions().at( 1 ).geometry().asWkt(), QStringLiteral( "Polygon ((10 0, 11 0, 11 1, 10 1, 10 0))" ) ); + + QgsMapSettings settings2( settings ); + QCOMPARE( settings2.clippingRegions().size(), 2 ); + QCOMPARE( settings2.clippingRegions().at( 0 ).geometry().asWkt(), QStringLiteral( "Polygon ((0 0, 1 0, 1 1, 0 1, 0 0))" ) ); + QCOMPARE( settings2.clippingRegions().at( 1 ).geometry().asWkt(), QStringLiteral( "Polygon ((10 0, 11 0, 11 1, 10 1, 10 0))" ) ); + + QgsMapSettings settings3; + settings3 = settings; + QCOMPARE( settings3.clippingRegions().size(), 2 ); + QCOMPARE( settings3.clippingRegions().at( 0 ).geometry().asWkt(), QStringLiteral( "Polygon ((0 0, 1 0, 1 1, 0 1, 0 0))" ) ); + QCOMPARE( settings3.clippingRegions().at( 1 ).geometry().asWkt(), QStringLiteral( "Polygon ((10 0, 11 0, 11 1, 10 1, 10 0))" ) ) ; +} + QGSTEST_MAIN( TestQgsMapSettings ) #include "testqgsmapsettings.moc" diff --git a/tests/src/python/test_qgsrendercontext.py b/tests/src/python/test_qgsrendercontext.py index 59475f99889..6cfdab563c3 100644 --- a/tests/src/python/test_qgsrendercontext.py +++ b/tests/src/python/test_qgsrendercontext.py @@ -23,7 +23,9 @@ from qgis.core import (QgsRenderContext, QgsRectangle, QgsVectorSimplifyMethod, QgsRenderedFeatureHandlerInterface, - QgsDateTimeRange) + QgsDateTimeRange, + QgsMapClippingRegion, + QgsGeometry) from qgis.PyQt.QtCore import QSize, QDateTime from qgis.PyQt.QtGui import QPainter, QImage from qgis.testing import start_app, unittest @@ -507,6 +509,17 @@ class TestQgsRenderContext(unittest.TestCase): self.assertEqual(rc.isTemporal(), False) self.assertIsNotNone(rc.temporalRange()) + def testClippingRegion(self): + ms = QgsMapSettings() + rc = QgsRenderContext.fromMapSettings(ms) + self.assertFalse(rc.clippingRegions()) + ms.addClippingRegion(QgsMapClippingRegion(QgsGeometry.fromWkt('Polygon(( 0 0, 1 0 , 1 1 , 0 1, 0 0 ))'))) + ms.addClippingRegion(QgsMapClippingRegion(QgsGeometry.fromWkt('Polygon(( 10 0, 11 0 , 11 1 , 10 1, 10 0 ))'))) + rc = QgsRenderContext.fromMapSettings(ms) + self.assertEqual(len(rc.clippingRegions()), 2) + self.assertEqual(rc.clippingRegions()[0].geometry().asWkt(), 'Polygon ((0 0, 1 0, 1 1, 0 1, 0 0))') + self.assertEqual(rc.clippingRegions()[1].geometry().asWkt(), 'Polygon ((10 0, 11 0, 11 1, 10 1, 10 0))') + if __name__ == '__main__': unittest.main()