diff --git a/python/PyQt6/core/auto_additions/qgis.py b/python/PyQt6/core/auto_additions/qgis.py index 157361db9bb0..ce54603fc041 100644 --- a/python/PyQt6/core/auto_additions/qgis.py +++ b/python/PyQt6/core/auto_additions/qgis.py @@ -2716,20 +2716,66 @@ # monkey patching scoped based enum QgsRasterPipe.ResampleFilter = Qgis.RasterResamplingStage.ResampleFilter QgsRasterPipe.ResampleFilter.is_monkey_patched = True -QgsRasterPipe.ResampleFilter.__doc__ = "" +QgsRasterPipe.ResampleFilter.__doc__ = "Resampling occurs in ResamplingFilter" QgsRasterPipe.Provider = Qgis.RasterResamplingStage.Provider QgsRasterPipe.Provider.is_monkey_patched = True -QgsRasterPipe.Provider.__doc__ = "" +QgsRasterPipe.Provider.__doc__ = "Resampling occurs in Provider" Qgis.RasterResamplingStage.__doc__ = """Stage at which raster resampling occurs. .. versionadded:: 3.22 -* ``ResampleFilter``: -* ``Provider``: +* ``ResampleFilter``: Resampling occurs in ResamplingFilter +* ``Provider``: Resampling occurs in Provider """ # -- Qgis.RasterResamplingStage.baseClass = Qgis +QgsRasterDataProvider.ResamplingMethod = Qgis.RasterResamplingMethod +# monkey patching scoped based enum +QgsRasterDataProvider.Nearest = Qgis.RasterResamplingMethod.Nearest +QgsRasterDataProvider.Nearest.is_monkey_patched = True +QgsRasterDataProvider.Nearest.__doc__ = "Nearest-neighbour resampling" +QgsRasterDataProvider.Bilinear = Qgis.RasterResamplingMethod.Bilinear +QgsRasterDataProvider.Bilinear.is_monkey_patched = True +QgsRasterDataProvider.Bilinear.__doc__ = "Bilinear (2x2 kernel) resampling" +QgsRasterDataProvider.Cubic = Qgis.RasterResamplingMethod.Cubic +QgsRasterDataProvider.Cubic.is_monkey_patched = True +QgsRasterDataProvider.Cubic.__doc__ = "Cubic Convolution Approximation (4x4 kernel) resampling" +QgsRasterDataProvider.CubicSpline = Qgis.RasterResamplingMethod.CubicSpline +QgsRasterDataProvider.CubicSpline.is_monkey_patched = True +QgsRasterDataProvider.CubicSpline.__doc__ = "Cubic B-Spline Approximation (4x4 kernel)" +QgsRasterDataProvider.Lanczos = Qgis.RasterResamplingMethod.Lanczos +QgsRasterDataProvider.Lanczos.is_monkey_patched = True +QgsRasterDataProvider.Lanczos.__doc__ = "Lanczos windowed sinc interpolation (6x6 kernel)" +QgsRasterDataProvider.Average = Qgis.RasterResamplingMethod.Average +QgsRasterDataProvider.Average.is_monkey_patched = True +QgsRasterDataProvider.Average.__doc__ = "Average resampling" +QgsRasterDataProvider.Mode = Qgis.RasterResamplingMethod.Mode +QgsRasterDataProvider.Mode.is_monkey_patched = True +QgsRasterDataProvider.Mode.__doc__ = "Mode (selects the value which appears most often of all the sampled points)" +QgsRasterDataProvider.Gauss = Qgis.RasterResamplingMethod.Gauss +QgsRasterDataProvider.Gauss.is_monkey_patched = True +QgsRasterDataProvider.Gauss.__doc__ = "Gauss blurring" +Qgis.RasterResamplingMethod.__doc__ = """Resampling method for raster provider-level resampling. + +.. note:: + + Prior to QGIS 3.42 this was available as :py:class:`QgsRasterDataProvider`.ResamplingMethod + +.. versionadded:: 3.42 + +* ``Nearest``: Nearest-neighbour resampling +* ``Bilinear``: Bilinear (2x2 kernel) resampling +* ``Cubic``: Cubic Convolution Approximation (4x4 kernel) resampling +* ``CubicSpline``: Cubic B-Spline Approximation (4x4 kernel) +* ``Lanczos``: Lanczos windowed sinc interpolation (6x6 kernel) +* ``Average``: Average resampling +* ``Mode``: Mode (selects the value which appears most often of all the sampled points) +* ``Gauss``: Gauss blurring + +""" +# -- +Qgis.RasterResamplingMethod.baseClass = Qgis # monkey patching scoped based enum Qgis.RasterRendererFlag.InternalLayerOpacityHandling.__doc__ = "The renderer internally handles the raster layer's opacity, so the default layer level opacity handling should not be applied." Qgis.RasterRendererFlag.UseNoDataForOutOfRangePixels.__doc__ = "Out of range pixels (eg those values outside of the rendered map's z range filter) should be set using additional nodata values instead of additional transparency values \n.. versionadded:: 3.38" diff --git a/python/PyQt6/core/auto_additions/qgsrasterdataprovider.py b/python/PyQt6/core/auto_additions/qgsrasterdataprovider.py index 1eaa7562ff06..d5e07960fbd0 100644 --- a/python/PyQt6/core/auto_additions/qgsrasterdataprovider.py +++ b/python/PyQt6/core/auto_additions/qgsrasterdataprovider.py @@ -1,30 +1,6 @@ # The following has been generated automatically from src/core/raster/qgsrasterdataprovider.h QgsRasterDataProvider.TransformImageToLayer = QgsRasterDataProvider.TransformType.TransformImageToLayer QgsRasterDataProvider.TransformLayerToImage = QgsRasterDataProvider.TransformType.TransformLayerToImage -# monkey patching scoped based enum -QgsRasterDataProvider.ResamplingMethod.Nearest.__doc__ = "Nearest-neighbour resampling" -QgsRasterDataProvider.ResamplingMethod.Bilinear.__doc__ = "Bilinear (2x2 kernel) resampling" -QgsRasterDataProvider.ResamplingMethod.Cubic.__doc__ = "Cubic Convolution Approximation (4x4 kernel) resampling" -QgsRasterDataProvider.ResamplingMethod.CubicSpline.__doc__ = "Cubic B-Spline Approximation (4x4 kernel)" -QgsRasterDataProvider.ResamplingMethod.Lanczos.__doc__ = "Lanczos windowed sinc interpolation (6x6 kernel)" -QgsRasterDataProvider.ResamplingMethod.Average.__doc__ = "Average resampling" -QgsRasterDataProvider.ResamplingMethod.Mode.__doc__ = "Mode (selects the value which appears most often of all the sampled points)" -QgsRasterDataProvider.ResamplingMethod.Gauss.__doc__ = "Gauss blurring" -QgsRasterDataProvider.ResamplingMethod.__doc__ = """Resampling method for provider-level resampling. - -.. versionadded:: 3.16 - -* ``Nearest``: Nearest-neighbour resampling -* ``Bilinear``: Bilinear (2x2 kernel) resampling -* ``Cubic``: Cubic Convolution Approximation (4x4 kernel) resampling -* ``CubicSpline``: Cubic B-Spline Approximation (4x4 kernel) -* ``Lanczos``: Lanczos windowed sinc interpolation (6x6 kernel) -* ``Average``: Average resampling -* ``Mode``: Mode (selects the value which appears most often of all the sampled points) -* ``Gauss``: Gauss blurring - -""" -# -- try: QgsImageFetcher.__attribute_docs__ = {'finish': 'Emitted when the download completes\n\n:param legend: The downloaded legend image\n', 'progress': 'Emitted to report progress\n', 'error': 'Emitted when an error occurs\n'} QgsImageFetcher.__signal_arguments__ = {'finish': ['legend: QImage'], 'progress': ['received: int', 'total: int'], 'error': ['msg: str']} diff --git a/python/PyQt6/core/auto_additions/qgsrasterlabeling.py b/python/PyQt6/core/auto_additions/qgsrasterlabeling.py new file mode 100644 index 000000000000..ac99c452057c --- /dev/null +++ b/python/PyQt6/core/auto_additions/qgsrasterlabeling.py @@ -0,0 +1,12 @@ +# The following has been generated automatically from src/core/raster/qgsrasterlabeling.h +try: + QgsAbstractRasterLayerLabeling.defaultLabelingForLayer = staticmethod(QgsAbstractRasterLayerLabeling.defaultLabelingForLayer) + QgsAbstractRasterLayerLabeling.createFromElement = staticmethod(QgsAbstractRasterLayerLabeling.createFromElement) + QgsAbstractRasterLayerLabeling.__group__ = ['raster'] +except (NameError, AttributeError): + pass +try: + QgsRasterLayerSimpleLabeling.create = staticmethod(QgsRasterLayerSimpleLabeling.create) + QgsRasterLayerSimpleLabeling.__group__ = ['raster'] +except (NameError, AttributeError): + pass diff --git a/python/PyQt6/core/auto_additions/qgsrasterviewport.py b/python/PyQt6/core/auto_additions/qgsrasterviewport.py index 1e07c0dac11c..9a3a3c6d535d 100644 --- a/python/PyQt6/core/auto_additions/qgsrasterviewport.py +++ b/python/PyQt6/core/auto_additions/qgsrasterviewport.py @@ -1,6 +1,6 @@ # The following has been generated automatically from src/core/raster/qgsrasterviewport.h try: - QgsRasterViewPort.__attribute_docs__ = {'mTopLeftPoint': 'Coordinate (in output device coordinate system) of top left corner\nof the part of the raster that is to be rendered.', 'mBottomRightPoint': 'Coordinate (in output device coordinate system) of bottom right corner\nof the part of the raster that is to be rendered.', 'mWidth': 'Width, number of columns to be rendered', 'mHeight': 'Height, number of rows to be rendered', 'mDrawnExtent': 'Intersection of current map extent and layer extent', 'mSrcCRS': 'Source coordinate system', 'mDestCRS': 'Target coordinate system', 'mTransformContext': 'Coordinate transform context'} + QgsRasterViewPort.__attribute_docs__ = {'mTopLeftPoint': 'Coordinate (in output device coordinate system) of top left corner\nof the part of the raster that is to be rendered.', 'mBottomRightPoint': 'Coordinate (in output device coordinate system) of bottom right corner\nof the part of the raster that is to be rendered.', 'mWidth': 'Width, number of columns to be rendered', 'mHeight': 'Height, number of rows to be rendered', 'mDrawnExtent': 'Intersection of current map extent and layer extent, in map (destination) CRS', 'mSrcCRS': 'Source (layer) coordinate system', 'mDestCRS': 'Target (map) coordinate system', 'mTransformContext': 'Coordinate transform context'} QgsRasterViewPort.__doc__ = """This class provides details of the viewable area that a raster will be rendered into. diff --git a/python/PyQt6/core/auto_generated/qgis.sip.in b/python/PyQt6/core/auto_generated/qgis.sip.in index 3dc2449a4749..5871b998f5e3 100644 --- a/python/PyQt6/core/auto_generated/qgis.sip.in +++ b/python/PyQt6/core/auto_generated/qgis.sip.in @@ -828,10 +828,20 @@ The development version enum class RasterResamplingStage /BaseType=IntEnum/ { - //! Resampling occurs in ResamplingFilter ResampleFilter, - //! Resampling occurs in Provider - Provider + Provider, + }; + + enum class RasterResamplingMethod /BaseType=IntEnum/ + { + Nearest, + Bilinear, + Cubic, + CubicSpline, + Lanczos, + Average, + Mode, + Gauss }; enum class RasterRendererFlag /BaseType=IntFlag/ diff --git a/python/PyQt6/core/auto_generated/raster/qgsrasterdataprovider.sip.in b/python/PyQt6/core/auto_generated/raster/qgsrasterdataprovider.sip.in index 58ebc0c3b1fa..2426d7e986a0 100644 --- a/python/PyQt6/core/auto_generated/raster/qgsrasterdataprovider.sip.in +++ b/python/PyQt6/core/auto_generated/raster/qgsrasterdataprovider.sip.in @@ -627,19 +627,7 @@ Returns whether provider-level resampling is enabled. .. versionadded:: 3.16 %End - enum class ResamplingMethod - { - Nearest, - Bilinear, - Cubic, - CubicSpline, - Lanczos, - Average, - Mode, - Gauss - }; - - virtual bool setZoomedInResamplingMethod( ResamplingMethod method ); + virtual bool setZoomedInResamplingMethod( Qgis::RasterResamplingMethod method ); %Docstring Set resampling method to apply for zoomed-in operations. @@ -648,14 +636,14 @@ Set resampling method to apply for zoomed-in operations. .. versionadded:: 3.16 %End - ResamplingMethod zoomedInResamplingMethod() const; + Qgis::RasterResamplingMethod zoomedInResamplingMethod() const; %Docstring Returns resampling method for zoomed-in operations. .. versionadded:: 3.16 %End - virtual bool setZoomedOutResamplingMethod( ResamplingMethod method ); + virtual bool setZoomedOutResamplingMethod( Qgis::RasterResamplingMethod method ); %Docstring Set resampling method to apply for zoomed-out operations. @@ -664,7 +652,7 @@ Set resampling method to apply for zoomed-out operations. .. versionadded:: 3.16 %End - ResamplingMethod zoomedOutResamplingMethod() const; + Qgis::RasterResamplingMethod zoomedOutResamplingMethod() const; %Docstring Returns resampling method for zoomed-out operations. diff --git a/python/PyQt6/core/auto_generated/raster/qgsrasterlabeling.sip.in b/python/PyQt6/core/auto_generated/raster/qgsrasterlabeling.sip.in new file mode 100644 index 000000000000..cea4ab1b3f4f --- /dev/null +++ b/python/PyQt6/core/auto_generated/raster/qgsrasterlabeling.sip.in @@ -0,0 +1,396 @@ +/************************************************************************ + * This file has been generated automatically from * + * * + * src/core/raster/qgsrasterlabeling.h * + * * + * Do not edit manually ! Edit header and run scripts/sipify.py again * + ************************************************************************/ + + + + + + + + + +class QgsAbstractRasterLayerLabeling /Abstract/ +{ +%Docstring(signature="appended") +Abstract base class for labeling settings for raster layers. + +.. versionadded:: 3.42 +%End + +%TypeHeaderCode +#include "qgsrasterlabeling.h" +%End + public: + +%ConvertToSubClassCode + if ( sipCpp->type() == "simple" ) + sipType = sipType_QgsRasterLayerSimpleLabeling; + else + sipType = 0; +%End + + QgsAbstractRasterLayerLabeling(); + virtual ~QgsAbstractRasterLayerLabeling(); + + static QgsAbstractRasterLayerLabeling *defaultLabelingForLayer( QgsRasterLayer *layer ) /Factory/; +%Docstring +Creates default labeling for a raster ``layer``. +%End + + virtual QString type() const = 0; +%Docstring +Unique type string of the labeling configuration implementation +%End + + virtual QgsAbstractRasterLayerLabeling *clone() const = 0 /Factory/; +%Docstring +Returns a new copy of the object +%End + + + virtual QDomElement save( QDomDocument &doc, const QgsReadWriteContext &context ) const = 0; +%Docstring +Saves the labeling configuration to an XML element. + +.. seealso:: :py:func:`createFromElement` +%End + + virtual bool requiresAdvancedEffects() const = 0; +%Docstring +Returns ``True`` if drawing labels requires advanced effects like composition +modes, which could prevent it being used as an isolated cached image +or exported to a vector format. +%End + + virtual void multiplyOpacity( double opacityFactor ); +%Docstring +Multiply opacity by ``opacityFactor``. + +This method multiplies the opacity of the labeling elements (text, shadow, buffer etc.) +by ``opacity`` effectively changing the opacity of the whole labeling elements. +%End + + virtual bool isInScaleRange( double scale ) const; +%Docstring +Tests whether the labels should be visible at the specified ``scale``. +The ``scale`` value indicates the scale denominator, e.g. 1000.0 for a 1:1000 map. + +:return: ``True`` if the labels are visible at the given scale. +%End + + + static QgsAbstractRasterLayerLabeling *createFromElement( const QDomElement &element, const QgsReadWriteContext &context ) /Factory/; +%Docstring +Tries to create an instance of an implementation based on the XML data. +%End + + virtual void toSld( QDomNode &parent, const QVariantMap &props ) const; +%Docstring +Writes the SE 1.1 TextSymbolizer element based on the current layer labeling settings +%End + + virtual bool accept( QgsStyleEntityVisitorInterface *visitor ) const; +%Docstring +Accepts the specified symbology ``visitor``, causing it to visit all symbols associated +with the labeling. + +Returns ``True`` if the visitor should continue visiting other objects, or ``False`` if visiting +should be canceled. +%End + + private: + QgsAbstractRasterLayerLabeling( const QgsAbstractRasterLayerLabeling &rhs ); +}; + + +class QgsRasterLayerSimpleLabeling : QgsAbstractRasterLayerLabeling +{ +%Docstring(signature="appended") +Basic implementation of the labeling interface for raster layers. + +.. versionadded:: 3.42 +%End + +%TypeHeaderCode +#include "qgsrasterlabeling.h" +%End + public: + + explicit QgsRasterLayerSimpleLabeling(); + ~QgsRasterLayerSimpleLabeling(); + + virtual QString type() const; + + virtual QgsRasterLayerSimpleLabeling *clone() const /Factory/; + + virtual QDomElement save( QDomDocument &doc, const QgsReadWriteContext &context ) const; + + virtual bool accept( QgsStyleEntityVisitorInterface *visitor ) const; + + virtual bool requiresAdvancedEffects() const; + + virtual void multiplyOpacity( double opacityFactor ); + + + static QgsRasterLayerSimpleLabeling *create( const QDomElement &element, const QgsReadWriteContext &context ) /Factory/; +%Docstring +Creates a QgsRasterLayerSimpleLabeling from a DOM element with saved configuration +%End + + QgsTextFormat textFormat() const; +%Docstring +Returns the text format used for rendering the labels. + +.. seealso:: :py:func:`setTextFormat` +%End + + void setTextFormat( const QgsTextFormat &format ); +%Docstring +Sets the text ``format`` used for rendering the labels. + +.. seealso:: :py:func:`textFormat` +%End + + const QgsNumericFormat *numericFormat() const; +%Docstring +Returns the numeric format used for the labels. + +.. seealso:: :py:func:`setNumericFormat` +%End + + void setNumericFormat( QgsNumericFormat *format /Transfer/ ); +%Docstring +Sets the numeric ``format`` used for the labels. + +Ownership of ``format`` is transferred to the labeling. + +.. seealso:: :py:func:`numericFormat` +%End + + int band() const; +%Docstring +Returns the raster band to use for label values. + +.. seealso:: :py:func:`setBand` +%End + + void setBand( int band ); +%Docstring +Sest the raster ``band`` to use for label values. + +.. seealso:: :py:func:`band` +%End + + double priority() const; +%Docstring +Returns the priority of labels. + +This is a value between 0 to 1, where 0 = highest priority and 1 = lowest priority. + +The default is 0.5. + +.. seealso:: :py:func:`setPriority` +%End + + void setPriority( double priority ); +%Docstring +Sets the ``priority`` of labels. + +This is a value between 0 to 1, where 0 = highest priority and 1 = lowest priority. + +.. seealso:: :py:func:`priority` +%End + + + QgsLabelPlacementSettings &placementSettings(); +%Docstring +Returns the label placement settings. + +.. seealso:: :py:func:`setPlacementSettings` +%End + + void setPlacementSettings( const QgsLabelPlacementSettings &settings ); +%Docstring +Sets the label placement ``settings``. + +.. seealso:: :py:func:`placementSettings` +%End + + + QgsLabelThinningSettings &thinningSettings(); +%Docstring +Returns the label thinning settings. + +.. seealso:: :py:func:`setThinningSettings` +%End + + void setThinningSettings( const QgsLabelThinningSettings &settings ); +%Docstring +Sets the label thinning ``settings``. + +.. seealso:: :py:func:`thinningSettings` +%End + + double zIndex() const; +%Docstring +Returns the Z-Index of the labels. + +Labels with a higher z-index are rendered on top of labels with a lower z-index. + +.. seealso:: :py:func:`setZIndex` +%End + + void setZIndex( double index ); +%Docstring +Sets the Z-Index of the labels. + +Labels with a higher z-index are rendered on top of labels with a lower z-index. + +.. seealso:: :py:func:`zIndex` +%End + + double maximumScale() const; +%Docstring +Returns the maximum map scale (i.e. most "zoomed in" scale) at which the labels will be visible. + +The scale value indicates the scale denominator, e.g. 1000.0 for a 1:1000 map. +A scale of 0 indicates no maximum scale visibility. + +This setting is only considered if :py:func:`~QgsRasterLayerSimpleLabeling.hasScaleBasedVisibility` is ``True``. + +.. seealso:: :py:func:`setMaximumScale` + +.. seealso:: :py:func:`minimumScale` + +.. seealso:: :py:func:`hasScaleBasedVisibility` +%End + + void setMaximumScale( double scale ); +%Docstring +Sets the maximum map ``scale`` (i.e. most "zoomed in" scale) at which the labels will be visible. + +The scale value indicates the scale denominator, e.g. 1000.0 for a 1:1000 map. +A scale of 0 indicates no maximum scale visibility. + +This setting is only considered if :py:func:`~QgsRasterLayerSimpleLabeling.hasScaleBasedVisibility` is ``True``. + +.. seealso:: :py:func:`maximumScale` + +.. seealso:: :py:func:`setMinimumScale` + +.. seealso:: :py:func:`setScaleBasedVisibility` +%End + + double minimumScale() const; +%Docstring +Returns the minimum map scale (i.e. most "zoomed out" scale) at which the labels will be visible. + +The scale value indicates the scale denominator, e.g. 1000.0 for a 1:1000 map. +A scale of 0 indicates no minimum scale visibility. + +This setting is only considered if :py:func:`~QgsRasterLayerSimpleLabeling.hasScaleBasedVisibility` is ``True``. + +.. seealso:: :py:func:`setMinimumScale` + +.. seealso:: :py:func:`maximumScale` + +.. seealso:: :py:func:`hasScaleBasedVisibility` +%End + + void setMinimumScale( double scale ); +%Docstring +Sets the minimum map ``scale`` (i.e. most "zoomed out" scale) at which the labels will be visible. + +The scale value indicates the scale denominator, e.g. 1000.0 for a 1:1000 map. +A scale of 0 indicates no minimum scale visibility. + +This setting is only considered if :py:func:`~QgsRasterLayerSimpleLabeling.hasScaleBasedVisibility` is ``True``. + +.. seealso:: :py:func:`minimumScale` + +.. seealso:: :py:func:`setMaximumScale` + +.. seealso:: :py:func:`hasScaleBasedVisibility` +%End + + void setScaleBasedVisibility( bool enabled ); +%Docstring +Sets whether scale based visibility is enabled for the labels. + +.. seealso:: :py:func:`setMinimumScale` + +.. seealso:: :py:func:`setMaximumScale` + +.. seealso:: :py:func:`hasScaleBasedVisibility` +%End + + bool hasScaleBasedVisibility() const; +%Docstring +Returns whether scale based visibility is enabled for the labels. + +.. seealso:: :py:func:`minimumScale` + +.. seealso:: :py:func:`maximumScale` + +.. seealso:: :py:func:`setScaleBasedVisibility` +%End + + virtual bool isInScaleRange( double scale ) const; + + + Qgis::RasterResamplingMethod resampleMethod() const; +%Docstring +Returns the resampling method used when the raster labels are being +resampled over neighboring pixels. + +.. seealso:: :py:func:`setResampleMethod` + +.. seealso:: :py:func:`resampleOver` +%End + + void setResampleMethod( Qgis::RasterResamplingMethod method ); +%Docstring +Sets the resampling ``method`` to use when the raster labels are being +resampled over neighboring pixels. + +.. seealso:: :py:func:`resampleMethod` + +.. seealso:: :py:func:`setResampleOver` +%End + + int resampleOver() const; +%Docstring +Returns the number of neighboring pixels to resample over, when labels are +showing values resampled over neighboring pixels. + +.. seealso:: :py:func:`setResampleOver` + +.. seealso:: :py:func:`resampleMethod` +%End + + void setResampleOver( int pixels ); +%Docstring +Sets the number of neighboring ``pixels`` to resample over, when labels are +showing values resampled over neighboring pixels. + +.. seealso:: :py:func:`resampleOver` + +.. seealso:: :py:func:`setResampleMethod` +%End + +}; + + + +/************************************************************************ + * This file has been generated automatically from * + * * + * src/core/raster/qgsrasterlabeling.h * + * * + * Do not edit manually ! Edit header and run scripts/sipify.py again * + ************************************************************************/ diff --git a/python/PyQt6/core/auto_generated/raster/qgsrasterlayer.sip.in b/python/PyQt6/core/auto_generated/raster/qgsrasterlayer.sip.in index edf4464c5b3b..31228974204b 100644 --- a/python/PyQt6/core/auto_generated/raster/qgsrasterlayer.sip.in +++ b/python/PyQt6/core/auto_generated/raster/qgsrasterlayer.sip.in @@ -400,6 +400,60 @@ Draws a preview of the rasterlayer into a QImage virtual bool accept( QgsStyleEntityVisitorInterface *visitor ) const; + bool labelsEnabled() const; +%Docstring +Returns whether the layer contains labels which are enabled and should be drawn. + +:return: ``True`` if layer contains enabled labels + +.. seealso:: :py:func:`setLabelsEnabled` + +.. seealso:: :py:func:`labeling` + +.. versionadded:: 3.42 +%End + + void setLabelsEnabled( bool enabled ); +%Docstring +Sets whether labels should be ``enabled`` for the layer. + +.. note:: + + Labels will only be rendered if :py:func:`~QgsRasterLayer.labelsEnabled` is ``True`` and a labeling + object is returned by :py:func:`~QgsRasterLayer.labeling`. + +.. seealso:: :py:func:`labelsEnabled` + +.. seealso:: :py:func:`labeling` + +.. versionadded:: 3.42 +%End + + + QgsAbstractRasterLayerLabeling *labeling(); +%Docstring +Access to labeling configuration. May be ``None`` if labeling is not used. + +.. note:: + + Labels will only be rendered if :py:func:`~QgsRasterLayer.labelsEnabled` returns ``True``. + +.. seealso:: :py:func:`labelsEnabled` + +.. seealso:: :py:func:`setLabeling` + +.. versionadded:: 3.42 +%End + + void setLabeling( QgsAbstractRasterLayerLabeling *labeling /Transfer/ ); +%Docstring +Sets labeling configuration. Takes ownership of the object. + +.. seealso:: :py:func:`labeling` + +.. versionadded:: 3.42 +%End + bool writeSld( QDomNode &node, QDomDocument &doc, QString &errorMessage, const QVariantMap &props = QVariantMap() ) const; %Docstring Writes the symbology of the layer into the document provided in SLD 1.0.0 format diff --git a/python/PyQt6/core/core_auto.sip b/python/PyQt6/core/core_auto.sip index cf68113b49dc..52d5f3e440b3 100644 --- a/python/PyQt6/core/core_auto.sip +++ b/python/PyQt6/core/core_auto.sip @@ -653,6 +653,7 @@ %Include auto_generated/raster/qgsrasteridentifyresult.sip %Include auto_generated/raster/qgsrasterinterface.sip %Include auto_generated/raster/qgsrasteriterator.sip +%Include auto_generated/raster/qgsrasterlabeling.sip %Include auto_generated/raster/qgsrasterlayer.sip %Include auto_generated/raster/qgsrasterlayerelevationproperties.sip %Include auto_generated/raster/qgsrasterlayertemporalproperties.sip diff --git a/python/PyQt6/gui/auto_generated/qgstextformatwidget.sip.in b/python/PyQt6/gui/auto_generated/qgstextformatwidget.sip.in index 2e1347dd52c7..3f6d455f9652 100644 --- a/python/PyQt6/gui/auto_generated/qgstextformatwidget.sip.in +++ b/python/PyQt6/gui/auto_generated/qgstextformatwidget.sip.in @@ -156,6 +156,14 @@ when registering labels for the labeling settings currently defined by the widge .. versionadded:: 3.16 %End + void setPropertyOverrideButtonsVisible( bool visible ); +%Docstring +Toggles whether data defined buttons should be shown in the widget. + +.. versionadded:: 3.42 +%End + + diff --git a/python/core/auto_additions/qgis.py b/python/core/auto_additions/qgis.py index 23385a874354..4c06ddf6f95b 100644 --- a/python/core/auto_additions/qgis.py +++ b/python/core/auto_additions/qgis.py @@ -2686,20 +2686,66 @@ # monkey patching scoped based enum QgsRasterPipe.ResampleFilter = Qgis.RasterResamplingStage.ResampleFilter QgsRasterPipe.ResampleFilter.is_monkey_patched = True -QgsRasterPipe.ResampleFilter.__doc__ = "" +QgsRasterPipe.ResampleFilter.__doc__ = "Resampling occurs in ResamplingFilter" QgsRasterPipe.Provider = Qgis.RasterResamplingStage.Provider QgsRasterPipe.Provider.is_monkey_patched = True -QgsRasterPipe.Provider.__doc__ = "" +QgsRasterPipe.Provider.__doc__ = "Resampling occurs in Provider" Qgis.RasterResamplingStage.__doc__ = """Stage at which raster resampling occurs. .. versionadded:: 3.22 -* ``ResampleFilter``: -* ``Provider``: +* ``ResampleFilter``: Resampling occurs in ResamplingFilter +* ``Provider``: Resampling occurs in Provider """ # -- Qgis.RasterResamplingStage.baseClass = Qgis +QgsRasterDataProvider.ResamplingMethod = Qgis.RasterResamplingMethod +# monkey patching scoped based enum +QgsRasterDataProvider.Nearest = Qgis.RasterResamplingMethod.Nearest +QgsRasterDataProvider.Nearest.is_monkey_patched = True +QgsRasterDataProvider.Nearest.__doc__ = "Nearest-neighbour resampling" +QgsRasterDataProvider.Bilinear = Qgis.RasterResamplingMethod.Bilinear +QgsRasterDataProvider.Bilinear.is_monkey_patched = True +QgsRasterDataProvider.Bilinear.__doc__ = "Bilinear (2x2 kernel) resampling" +QgsRasterDataProvider.Cubic = Qgis.RasterResamplingMethod.Cubic +QgsRasterDataProvider.Cubic.is_monkey_patched = True +QgsRasterDataProvider.Cubic.__doc__ = "Cubic Convolution Approximation (4x4 kernel) resampling" +QgsRasterDataProvider.CubicSpline = Qgis.RasterResamplingMethod.CubicSpline +QgsRasterDataProvider.CubicSpline.is_monkey_patched = True +QgsRasterDataProvider.CubicSpline.__doc__ = "Cubic B-Spline Approximation (4x4 kernel)" +QgsRasterDataProvider.Lanczos = Qgis.RasterResamplingMethod.Lanczos +QgsRasterDataProvider.Lanczos.is_monkey_patched = True +QgsRasterDataProvider.Lanczos.__doc__ = "Lanczos windowed sinc interpolation (6x6 kernel)" +QgsRasterDataProvider.Average = Qgis.RasterResamplingMethod.Average +QgsRasterDataProvider.Average.is_monkey_patched = True +QgsRasterDataProvider.Average.__doc__ = "Average resampling" +QgsRasterDataProvider.Mode = Qgis.RasterResamplingMethod.Mode +QgsRasterDataProvider.Mode.is_monkey_patched = True +QgsRasterDataProvider.Mode.__doc__ = "Mode (selects the value which appears most often of all the sampled points)" +QgsRasterDataProvider.Gauss = Qgis.RasterResamplingMethod.Gauss +QgsRasterDataProvider.Gauss.is_monkey_patched = True +QgsRasterDataProvider.Gauss.__doc__ = "Gauss blurring" +Qgis.RasterResamplingMethod.__doc__ = """Resampling method for raster provider-level resampling. + +.. note:: + + Prior to QGIS 3.42 this was available as :py:class:`QgsRasterDataProvider`.ResamplingMethod + +.. versionadded:: 3.42 + +* ``Nearest``: Nearest-neighbour resampling +* ``Bilinear``: Bilinear (2x2 kernel) resampling +* ``Cubic``: Cubic Convolution Approximation (4x4 kernel) resampling +* ``CubicSpline``: Cubic B-Spline Approximation (4x4 kernel) +* ``Lanczos``: Lanczos windowed sinc interpolation (6x6 kernel) +* ``Average``: Average resampling +* ``Mode``: Mode (selects the value which appears most often of all the sampled points) +* ``Gauss``: Gauss blurring + +""" +# -- +Qgis.RasterResamplingMethod.baseClass = Qgis # monkey patching scoped based enum Qgis.RasterRendererFlag.InternalLayerOpacityHandling.__doc__ = "The renderer internally handles the raster layer's opacity, so the default layer level opacity handling should not be applied." Qgis.RasterRendererFlag.UseNoDataForOutOfRangePixels.__doc__ = "Out of range pixels (eg those values outside of the rendered map's z range filter) should be set using additional nodata values instead of additional transparency values \n.. versionadded:: 3.38" diff --git a/python/core/auto_additions/qgsrasterdataprovider.py b/python/core/auto_additions/qgsrasterdataprovider.py index e618c3187367..86845d12e1e8 100644 --- a/python/core/auto_additions/qgsrasterdataprovider.py +++ b/python/core/auto_additions/qgsrasterdataprovider.py @@ -1,28 +1,4 @@ # The following has been generated automatically from src/core/raster/qgsrasterdataprovider.h -# monkey patching scoped based enum -QgsRasterDataProvider.ResamplingMethod.Nearest.__doc__ = "Nearest-neighbour resampling" -QgsRasterDataProvider.ResamplingMethod.Bilinear.__doc__ = "Bilinear (2x2 kernel) resampling" -QgsRasterDataProvider.ResamplingMethod.Cubic.__doc__ = "Cubic Convolution Approximation (4x4 kernel) resampling" -QgsRasterDataProvider.ResamplingMethod.CubicSpline.__doc__ = "Cubic B-Spline Approximation (4x4 kernel)" -QgsRasterDataProvider.ResamplingMethod.Lanczos.__doc__ = "Lanczos windowed sinc interpolation (6x6 kernel)" -QgsRasterDataProvider.ResamplingMethod.Average.__doc__ = "Average resampling" -QgsRasterDataProvider.ResamplingMethod.Mode.__doc__ = "Mode (selects the value which appears most often of all the sampled points)" -QgsRasterDataProvider.ResamplingMethod.Gauss.__doc__ = "Gauss blurring" -QgsRasterDataProvider.ResamplingMethod.__doc__ = """Resampling method for provider-level resampling. - -.. versionadded:: 3.16 - -* ``Nearest``: Nearest-neighbour resampling -* ``Bilinear``: Bilinear (2x2 kernel) resampling -* ``Cubic``: Cubic Convolution Approximation (4x4 kernel) resampling -* ``CubicSpline``: Cubic B-Spline Approximation (4x4 kernel) -* ``Lanczos``: Lanczos windowed sinc interpolation (6x6 kernel) -* ``Average``: Average resampling -* ``Mode``: Mode (selects the value which appears most often of all the sampled points) -* ``Gauss``: Gauss blurring - -""" -# -- try: QgsImageFetcher.__attribute_docs__ = {'finish': 'Emitted when the download completes\n\n:param legend: The downloaded legend image\n', 'progress': 'Emitted to report progress\n', 'error': 'Emitted when an error occurs\n'} QgsImageFetcher.__signal_arguments__ = {'finish': ['legend: QImage'], 'progress': ['received: int', 'total: int'], 'error': ['msg: str']} diff --git a/python/core/auto_additions/qgsrasterlabeling.py b/python/core/auto_additions/qgsrasterlabeling.py new file mode 100644 index 000000000000..ac99c452057c --- /dev/null +++ b/python/core/auto_additions/qgsrasterlabeling.py @@ -0,0 +1,12 @@ +# The following has been generated automatically from src/core/raster/qgsrasterlabeling.h +try: + QgsAbstractRasterLayerLabeling.defaultLabelingForLayer = staticmethod(QgsAbstractRasterLayerLabeling.defaultLabelingForLayer) + QgsAbstractRasterLayerLabeling.createFromElement = staticmethod(QgsAbstractRasterLayerLabeling.createFromElement) + QgsAbstractRasterLayerLabeling.__group__ = ['raster'] +except (NameError, AttributeError): + pass +try: + QgsRasterLayerSimpleLabeling.create = staticmethod(QgsRasterLayerSimpleLabeling.create) + QgsRasterLayerSimpleLabeling.__group__ = ['raster'] +except (NameError, AttributeError): + pass diff --git a/python/core/auto_additions/qgsrasterviewport.py b/python/core/auto_additions/qgsrasterviewport.py index 1e07c0dac11c..9a3a3c6d535d 100644 --- a/python/core/auto_additions/qgsrasterviewport.py +++ b/python/core/auto_additions/qgsrasterviewport.py @@ -1,6 +1,6 @@ # The following has been generated automatically from src/core/raster/qgsrasterviewport.h try: - QgsRasterViewPort.__attribute_docs__ = {'mTopLeftPoint': 'Coordinate (in output device coordinate system) of top left corner\nof the part of the raster that is to be rendered.', 'mBottomRightPoint': 'Coordinate (in output device coordinate system) of bottom right corner\nof the part of the raster that is to be rendered.', 'mWidth': 'Width, number of columns to be rendered', 'mHeight': 'Height, number of rows to be rendered', 'mDrawnExtent': 'Intersection of current map extent and layer extent', 'mSrcCRS': 'Source coordinate system', 'mDestCRS': 'Target coordinate system', 'mTransformContext': 'Coordinate transform context'} + QgsRasterViewPort.__attribute_docs__ = {'mTopLeftPoint': 'Coordinate (in output device coordinate system) of top left corner\nof the part of the raster that is to be rendered.', 'mBottomRightPoint': 'Coordinate (in output device coordinate system) of bottom right corner\nof the part of the raster that is to be rendered.', 'mWidth': 'Width, number of columns to be rendered', 'mHeight': 'Height, number of rows to be rendered', 'mDrawnExtent': 'Intersection of current map extent and layer extent, in map (destination) CRS', 'mSrcCRS': 'Source (layer) coordinate system', 'mDestCRS': 'Target (map) coordinate system', 'mTransformContext': 'Coordinate transform context'} QgsRasterViewPort.__doc__ = """This class provides details of the viewable area that a raster will be rendered into. diff --git a/python/core/auto_generated/qgis.sip.in b/python/core/auto_generated/qgis.sip.in index b6e2ee439302..7a33dc80aa1b 100644 --- a/python/core/auto_generated/qgis.sip.in +++ b/python/core/auto_generated/qgis.sip.in @@ -828,10 +828,20 @@ The development version enum class RasterResamplingStage { - //! Resampling occurs in ResamplingFilter ResampleFilter, - //! Resampling occurs in Provider - Provider + Provider, + }; + + enum class RasterResamplingMethod + { + Nearest, + Bilinear, + Cubic, + CubicSpline, + Lanczos, + Average, + Mode, + Gauss }; enum class RasterRendererFlag diff --git a/python/core/auto_generated/raster/qgsrasterdataprovider.sip.in b/python/core/auto_generated/raster/qgsrasterdataprovider.sip.in index ee34a4777952..fba67d93fe2a 100644 --- a/python/core/auto_generated/raster/qgsrasterdataprovider.sip.in +++ b/python/core/auto_generated/raster/qgsrasterdataprovider.sip.in @@ -627,19 +627,7 @@ Returns whether provider-level resampling is enabled. .. versionadded:: 3.16 %End - enum class ResamplingMethod - { - Nearest, - Bilinear, - Cubic, - CubicSpline, - Lanczos, - Average, - Mode, - Gauss - }; - - virtual bool setZoomedInResamplingMethod( ResamplingMethod method ); + virtual bool setZoomedInResamplingMethod( Qgis::RasterResamplingMethod method ); %Docstring Set resampling method to apply for zoomed-in operations. @@ -648,14 +636,14 @@ Set resampling method to apply for zoomed-in operations. .. versionadded:: 3.16 %End - ResamplingMethod zoomedInResamplingMethod() const; + Qgis::RasterResamplingMethod zoomedInResamplingMethod() const; %Docstring Returns resampling method for zoomed-in operations. .. versionadded:: 3.16 %End - virtual bool setZoomedOutResamplingMethod( ResamplingMethod method ); + virtual bool setZoomedOutResamplingMethod( Qgis::RasterResamplingMethod method ); %Docstring Set resampling method to apply for zoomed-out operations. @@ -664,7 +652,7 @@ Set resampling method to apply for zoomed-out operations. .. versionadded:: 3.16 %End - ResamplingMethod zoomedOutResamplingMethod() const; + Qgis::RasterResamplingMethod zoomedOutResamplingMethod() const; %Docstring Returns resampling method for zoomed-out operations. diff --git a/python/core/auto_generated/raster/qgsrasterlabeling.sip.in b/python/core/auto_generated/raster/qgsrasterlabeling.sip.in new file mode 100644 index 000000000000..cea4ab1b3f4f --- /dev/null +++ b/python/core/auto_generated/raster/qgsrasterlabeling.sip.in @@ -0,0 +1,396 @@ +/************************************************************************ + * This file has been generated automatically from * + * * + * src/core/raster/qgsrasterlabeling.h * + * * + * Do not edit manually ! Edit header and run scripts/sipify.py again * + ************************************************************************/ + + + + + + + + + +class QgsAbstractRasterLayerLabeling /Abstract/ +{ +%Docstring(signature="appended") +Abstract base class for labeling settings for raster layers. + +.. versionadded:: 3.42 +%End + +%TypeHeaderCode +#include "qgsrasterlabeling.h" +%End + public: + +%ConvertToSubClassCode + if ( sipCpp->type() == "simple" ) + sipType = sipType_QgsRasterLayerSimpleLabeling; + else + sipType = 0; +%End + + QgsAbstractRasterLayerLabeling(); + virtual ~QgsAbstractRasterLayerLabeling(); + + static QgsAbstractRasterLayerLabeling *defaultLabelingForLayer( QgsRasterLayer *layer ) /Factory/; +%Docstring +Creates default labeling for a raster ``layer``. +%End + + virtual QString type() const = 0; +%Docstring +Unique type string of the labeling configuration implementation +%End + + virtual QgsAbstractRasterLayerLabeling *clone() const = 0 /Factory/; +%Docstring +Returns a new copy of the object +%End + + + virtual QDomElement save( QDomDocument &doc, const QgsReadWriteContext &context ) const = 0; +%Docstring +Saves the labeling configuration to an XML element. + +.. seealso:: :py:func:`createFromElement` +%End + + virtual bool requiresAdvancedEffects() const = 0; +%Docstring +Returns ``True`` if drawing labels requires advanced effects like composition +modes, which could prevent it being used as an isolated cached image +or exported to a vector format. +%End + + virtual void multiplyOpacity( double opacityFactor ); +%Docstring +Multiply opacity by ``opacityFactor``. + +This method multiplies the opacity of the labeling elements (text, shadow, buffer etc.) +by ``opacity`` effectively changing the opacity of the whole labeling elements. +%End + + virtual bool isInScaleRange( double scale ) const; +%Docstring +Tests whether the labels should be visible at the specified ``scale``. +The ``scale`` value indicates the scale denominator, e.g. 1000.0 for a 1:1000 map. + +:return: ``True`` if the labels are visible at the given scale. +%End + + + static QgsAbstractRasterLayerLabeling *createFromElement( const QDomElement &element, const QgsReadWriteContext &context ) /Factory/; +%Docstring +Tries to create an instance of an implementation based on the XML data. +%End + + virtual void toSld( QDomNode &parent, const QVariantMap &props ) const; +%Docstring +Writes the SE 1.1 TextSymbolizer element based on the current layer labeling settings +%End + + virtual bool accept( QgsStyleEntityVisitorInterface *visitor ) const; +%Docstring +Accepts the specified symbology ``visitor``, causing it to visit all symbols associated +with the labeling. + +Returns ``True`` if the visitor should continue visiting other objects, or ``False`` if visiting +should be canceled. +%End + + private: + QgsAbstractRasterLayerLabeling( const QgsAbstractRasterLayerLabeling &rhs ); +}; + + +class QgsRasterLayerSimpleLabeling : QgsAbstractRasterLayerLabeling +{ +%Docstring(signature="appended") +Basic implementation of the labeling interface for raster layers. + +.. versionadded:: 3.42 +%End + +%TypeHeaderCode +#include "qgsrasterlabeling.h" +%End + public: + + explicit QgsRasterLayerSimpleLabeling(); + ~QgsRasterLayerSimpleLabeling(); + + virtual QString type() const; + + virtual QgsRasterLayerSimpleLabeling *clone() const /Factory/; + + virtual QDomElement save( QDomDocument &doc, const QgsReadWriteContext &context ) const; + + virtual bool accept( QgsStyleEntityVisitorInterface *visitor ) const; + + virtual bool requiresAdvancedEffects() const; + + virtual void multiplyOpacity( double opacityFactor ); + + + static QgsRasterLayerSimpleLabeling *create( const QDomElement &element, const QgsReadWriteContext &context ) /Factory/; +%Docstring +Creates a QgsRasterLayerSimpleLabeling from a DOM element with saved configuration +%End + + QgsTextFormat textFormat() const; +%Docstring +Returns the text format used for rendering the labels. + +.. seealso:: :py:func:`setTextFormat` +%End + + void setTextFormat( const QgsTextFormat &format ); +%Docstring +Sets the text ``format`` used for rendering the labels. + +.. seealso:: :py:func:`textFormat` +%End + + const QgsNumericFormat *numericFormat() const; +%Docstring +Returns the numeric format used for the labels. + +.. seealso:: :py:func:`setNumericFormat` +%End + + void setNumericFormat( QgsNumericFormat *format /Transfer/ ); +%Docstring +Sets the numeric ``format`` used for the labels. + +Ownership of ``format`` is transferred to the labeling. + +.. seealso:: :py:func:`numericFormat` +%End + + int band() const; +%Docstring +Returns the raster band to use for label values. + +.. seealso:: :py:func:`setBand` +%End + + void setBand( int band ); +%Docstring +Sest the raster ``band`` to use for label values. + +.. seealso:: :py:func:`band` +%End + + double priority() const; +%Docstring +Returns the priority of labels. + +This is a value between 0 to 1, where 0 = highest priority and 1 = lowest priority. + +The default is 0.5. + +.. seealso:: :py:func:`setPriority` +%End + + void setPriority( double priority ); +%Docstring +Sets the ``priority`` of labels. + +This is a value between 0 to 1, where 0 = highest priority and 1 = lowest priority. + +.. seealso:: :py:func:`priority` +%End + + + QgsLabelPlacementSettings &placementSettings(); +%Docstring +Returns the label placement settings. + +.. seealso:: :py:func:`setPlacementSettings` +%End + + void setPlacementSettings( const QgsLabelPlacementSettings &settings ); +%Docstring +Sets the label placement ``settings``. + +.. seealso:: :py:func:`placementSettings` +%End + + + QgsLabelThinningSettings &thinningSettings(); +%Docstring +Returns the label thinning settings. + +.. seealso:: :py:func:`setThinningSettings` +%End + + void setThinningSettings( const QgsLabelThinningSettings &settings ); +%Docstring +Sets the label thinning ``settings``. + +.. seealso:: :py:func:`thinningSettings` +%End + + double zIndex() const; +%Docstring +Returns the Z-Index of the labels. + +Labels with a higher z-index are rendered on top of labels with a lower z-index. + +.. seealso:: :py:func:`setZIndex` +%End + + void setZIndex( double index ); +%Docstring +Sets the Z-Index of the labels. + +Labels with a higher z-index are rendered on top of labels with a lower z-index. + +.. seealso:: :py:func:`zIndex` +%End + + double maximumScale() const; +%Docstring +Returns the maximum map scale (i.e. most "zoomed in" scale) at which the labels will be visible. + +The scale value indicates the scale denominator, e.g. 1000.0 for a 1:1000 map. +A scale of 0 indicates no maximum scale visibility. + +This setting is only considered if :py:func:`~QgsRasterLayerSimpleLabeling.hasScaleBasedVisibility` is ``True``. + +.. seealso:: :py:func:`setMaximumScale` + +.. seealso:: :py:func:`minimumScale` + +.. seealso:: :py:func:`hasScaleBasedVisibility` +%End + + void setMaximumScale( double scale ); +%Docstring +Sets the maximum map ``scale`` (i.e. most "zoomed in" scale) at which the labels will be visible. + +The scale value indicates the scale denominator, e.g. 1000.0 for a 1:1000 map. +A scale of 0 indicates no maximum scale visibility. + +This setting is only considered if :py:func:`~QgsRasterLayerSimpleLabeling.hasScaleBasedVisibility` is ``True``. + +.. seealso:: :py:func:`maximumScale` + +.. seealso:: :py:func:`setMinimumScale` + +.. seealso:: :py:func:`setScaleBasedVisibility` +%End + + double minimumScale() const; +%Docstring +Returns the minimum map scale (i.e. most "zoomed out" scale) at which the labels will be visible. + +The scale value indicates the scale denominator, e.g. 1000.0 for a 1:1000 map. +A scale of 0 indicates no minimum scale visibility. + +This setting is only considered if :py:func:`~QgsRasterLayerSimpleLabeling.hasScaleBasedVisibility` is ``True``. + +.. seealso:: :py:func:`setMinimumScale` + +.. seealso:: :py:func:`maximumScale` + +.. seealso:: :py:func:`hasScaleBasedVisibility` +%End + + void setMinimumScale( double scale ); +%Docstring +Sets the minimum map ``scale`` (i.e. most "zoomed out" scale) at which the labels will be visible. + +The scale value indicates the scale denominator, e.g. 1000.0 for a 1:1000 map. +A scale of 0 indicates no minimum scale visibility. + +This setting is only considered if :py:func:`~QgsRasterLayerSimpleLabeling.hasScaleBasedVisibility` is ``True``. + +.. seealso:: :py:func:`minimumScale` + +.. seealso:: :py:func:`setMaximumScale` + +.. seealso:: :py:func:`hasScaleBasedVisibility` +%End + + void setScaleBasedVisibility( bool enabled ); +%Docstring +Sets whether scale based visibility is enabled for the labels. + +.. seealso:: :py:func:`setMinimumScale` + +.. seealso:: :py:func:`setMaximumScale` + +.. seealso:: :py:func:`hasScaleBasedVisibility` +%End + + bool hasScaleBasedVisibility() const; +%Docstring +Returns whether scale based visibility is enabled for the labels. + +.. seealso:: :py:func:`minimumScale` + +.. seealso:: :py:func:`maximumScale` + +.. seealso:: :py:func:`setScaleBasedVisibility` +%End + + virtual bool isInScaleRange( double scale ) const; + + + Qgis::RasterResamplingMethod resampleMethod() const; +%Docstring +Returns the resampling method used when the raster labels are being +resampled over neighboring pixels. + +.. seealso:: :py:func:`setResampleMethod` + +.. seealso:: :py:func:`resampleOver` +%End + + void setResampleMethod( Qgis::RasterResamplingMethod method ); +%Docstring +Sets the resampling ``method`` to use when the raster labels are being +resampled over neighboring pixels. + +.. seealso:: :py:func:`resampleMethod` + +.. seealso:: :py:func:`setResampleOver` +%End + + int resampleOver() const; +%Docstring +Returns the number of neighboring pixels to resample over, when labels are +showing values resampled over neighboring pixels. + +.. seealso:: :py:func:`setResampleOver` + +.. seealso:: :py:func:`resampleMethod` +%End + + void setResampleOver( int pixels ); +%Docstring +Sets the number of neighboring ``pixels`` to resample over, when labels are +showing values resampled over neighboring pixels. + +.. seealso:: :py:func:`resampleOver` + +.. seealso:: :py:func:`setResampleMethod` +%End + +}; + + + +/************************************************************************ + * This file has been generated automatically from * + * * + * src/core/raster/qgsrasterlabeling.h * + * * + * Do not edit manually ! Edit header and run scripts/sipify.py again * + ************************************************************************/ diff --git a/python/core/auto_generated/raster/qgsrasterlayer.sip.in b/python/core/auto_generated/raster/qgsrasterlayer.sip.in index edf4464c5b3b..31228974204b 100644 --- a/python/core/auto_generated/raster/qgsrasterlayer.sip.in +++ b/python/core/auto_generated/raster/qgsrasterlayer.sip.in @@ -400,6 +400,60 @@ Draws a preview of the rasterlayer into a QImage virtual bool accept( QgsStyleEntityVisitorInterface *visitor ) const; + bool labelsEnabled() const; +%Docstring +Returns whether the layer contains labels which are enabled and should be drawn. + +:return: ``True`` if layer contains enabled labels + +.. seealso:: :py:func:`setLabelsEnabled` + +.. seealso:: :py:func:`labeling` + +.. versionadded:: 3.42 +%End + + void setLabelsEnabled( bool enabled ); +%Docstring +Sets whether labels should be ``enabled`` for the layer. + +.. note:: + + Labels will only be rendered if :py:func:`~QgsRasterLayer.labelsEnabled` is ``True`` and a labeling + object is returned by :py:func:`~QgsRasterLayer.labeling`. + +.. seealso:: :py:func:`labelsEnabled` + +.. seealso:: :py:func:`labeling` + +.. versionadded:: 3.42 +%End + + + QgsAbstractRasterLayerLabeling *labeling(); +%Docstring +Access to labeling configuration. May be ``None`` if labeling is not used. + +.. note:: + + Labels will only be rendered if :py:func:`~QgsRasterLayer.labelsEnabled` returns ``True``. + +.. seealso:: :py:func:`labelsEnabled` + +.. seealso:: :py:func:`setLabeling` + +.. versionadded:: 3.42 +%End + + void setLabeling( QgsAbstractRasterLayerLabeling *labeling /Transfer/ ); +%Docstring +Sets labeling configuration. Takes ownership of the object. + +.. seealso:: :py:func:`labeling` + +.. versionadded:: 3.42 +%End + bool writeSld( QDomNode &node, QDomDocument &doc, QString &errorMessage, const QVariantMap &props = QVariantMap() ) const; %Docstring Writes the symbology of the layer into the document provided in SLD 1.0.0 format diff --git a/python/core/core_auto.sip b/python/core/core_auto.sip index cf68113b49dc..52d5f3e440b3 100644 --- a/python/core/core_auto.sip +++ b/python/core/core_auto.sip @@ -653,6 +653,7 @@ %Include auto_generated/raster/qgsrasteridentifyresult.sip %Include auto_generated/raster/qgsrasterinterface.sip %Include auto_generated/raster/qgsrasteriterator.sip +%Include auto_generated/raster/qgsrasterlabeling.sip %Include auto_generated/raster/qgsrasterlayer.sip %Include auto_generated/raster/qgsrasterlayerelevationproperties.sip %Include auto_generated/raster/qgsrasterlayertemporalproperties.sip diff --git a/python/gui/auto_generated/qgstextformatwidget.sip.in b/python/gui/auto_generated/qgstextformatwidget.sip.in index 31575f017ffc..dfa996e537dc 100644 --- a/python/gui/auto_generated/qgstextformatwidget.sip.in +++ b/python/gui/auto_generated/qgstextformatwidget.sip.in @@ -156,6 +156,14 @@ when registering labels for the labeling settings currently defined by the widge .. versionadded:: 3.16 %End + void setPropertyOverrideButtonsVisible( bool visible ); +%Docstring +Toggles whether data defined buttons should be shown in the widget. + +.. versionadded:: 3.42 +%End + + diff --git a/scripts/includemocs.py b/scripts/includemocs.py old mode 100644 new mode 100755 diff --git a/src/app/qgsapplayertreeviewmenuprovider.cpp b/src/app/qgsapplayertreeviewmenuprovider.cpp index 5c9cee654862..c7b964b229fc 100644 --- a/src/app/qgsapplayertreeviewmenuprovider.cpp +++ b/src/app/qgsapplayertreeviewmenuprovider.cpp @@ -58,6 +58,7 @@ #include "qgsvectortiledataprovider.h" #include "qgsproviderregistry.h" #include "qgsprovidermetadata.h" +#include "qgsrasterlabeling.h" QgsAppLayerTreeViewMenuProvider::QgsAppLayerTreeViewMenuProvider( QgsLayerTreeView *view, QgsMapCanvas *canvas ) : mView( view ) @@ -197,7 +198,7 @@ QMenu *QgsAppLayerTreeViewMenuProvider::createContextMenu() showFeatureCount->setEnabled( vlayer->isValid() ); } - if ( vlayer || vectorTileLayer || meshLayer ) + if ( vlayer || vectorTileLayer || meshLayer || rlayer ) { const QString iconName = vectorTileLayer || ( vlayer && vlayer->labeling() && vlayer->labeling()->type() == QLatin1String( "rule-based" ) ) ? QStringLiteral( "labelingRuleBased.svg" ) @@ -205,6 +206,7 @@ QMenu *QgsAppLayerTreeViewMenuProvider::createContextMenu() QAction *actionShowLabels = new QAction( QgsApplication::getThemeIcon( iconName ), tr( "Show &Labels" ), menu ); actionShowLabels->setCheckable( true ); actionShowLabels->setChecked( vectorTileLayer ? vectorTileLayer->labelsEnabled() : meshLayer ? meshLayer->labelsEnabled() + : rlayer ? rlayer->labelsEnabled() : vlayer->labelsEnabled() ); connect( actionShowLabels, &QAction::toggled, this, &QgsAppLayerTreeViewMenuProvider::toggleLabels ); menu->addAction( actionShowLabels ); @@ -1419,5 +1421,21 @@ void QgsAppLayerTreeViewMenuProvider::toggleLabels( bool enabled ) meshLayer->emitStyleChanged(); meshLayer->triggerRepaint(); } + else if ( QgsRasterLayer *rasterLayer = qobject_cast( l->layer() ) ) + { + if ( enabled && !rasterLayer->labeling() ) + { + // no labeling setup - create default labeling for layer + rasterLayer->setLabeling( QgsAbstractRasterLayerLabeling::defaultLabelingForLayer( rasterLayer ) ); + rasterLayer->setLabelsEnabled( true ); + } + else + { + rasterLayer->setLabelsEnabled( enabled ); + } + + rasterLayer->emitStyleChanged(); + rasterLayer->triggerRepaint(); + } } } diff --git a/src/app/qgslayerstylingwidget.cpp b/src/app/qgslayerstylingwidget.cpp index af9ed23c38e1..b91b34f7d611 100644 --- a/src/app/qgslayerstylingwidget.cpp +++ b/src/app/qgslayerstylingwidget.cpp @@ -57,6 +57,7 @@ #include "qgisapp.h" #include "qgssymbolwidgetcontext.h" #include "qgsannotationlayer.h" +#include "qgsrasterlabelingwidget.h" #ifdef HAVE_3D #include "qgsvectorlayer3drendererwidget.h" @@ -230,6 +231,11 @@ void QgsLayerStylingWidget::setLayer( QgsMapLayer *layer ) transparencyItem->setData( Qt::UserRole, RasterTransparency ); mOptionsListWidget->addItem( transparencyItem ); + QListWidgetItem *labelItem = new QListWidgetItem( QgsApplication::getThemeIcon( QStringLiteral( "labelingSingle.svg" ) ), QString() ); + labelItem->setData( Qt::UserRole, VectorLabeling ); + labelItem->setToolTip( tr( "Labels" ) ); + mOptionsListWidget->addItem( labelItem ); + if ( static_cast( layer )->dataProvider() && static_cast( layer )->dataProvider()->capabilities() & Qgis::RasterInterfaceCapability::Size ) { QListWidgetItem *histogramItem = new QListWidgetItem( QgsApplication::getThemeIcon( QStringLiteral( "propertyicons/histogram.svg" ) ), QString() ); @@ -641,7 +647,24 @@ void QgsLayerStylingWidget::updateCurrentWidgetLayer() mWidgetStack->setMainPanel( transwidget ); break; } - case 2: // Histogram + + case 2: // Labeling + { + if ( !mRasterLabelingWidget ) + { + mRasterLabelingWidget = new QgsRasterLabelingWidget( rlayer, mMapCanvas, mWidgetStack, mMessageBar ); + mRasterLabelingWidget->setDockMode( true ); + connect( mRasterLabelingWidget, &QgsPanelWidget::widgetChanged, this, &QgsLayerStylingWidget::autoApply ); + } + else + { + mRasterLabelingWidget->setLayer( rlayer ); + } + mWidgetStack->setMainPanel( mRasterLabelingWidget ); + break; + } + + case 3: // Histogram { if ( rlayer->dataProvider()->capabilities() & Qgis::RasterInterfaceCapability::Size ) { @@ -662,7 +685,8 @@ void QgsLayerStylingWidget::updateCurrentWidgetLayer() } break; } - case 3: // Attribute Tables + + case 4: // Attribute Tables { if ( rlayer->attributeTableCount() > 0 ) { diff --git a/src/app/qgslayerstylingwidget.h b/src/app/qgslayerstylingwidget.h index 59a196d290b4..0e0b89f3af68 100644 --- a/src/app/qgslayerstylingwidget.h +++ b/src/app/qgslayerstylingwidget.h @@ -47,6 +47,7 @@ class QgsMapLayerStyleManagerWidget; class QgsVectorLayer3DRendererWidget; class QgsMeshLayer3DRendererWidget; class QgsMeshLabelingWidget; +class QgsRasterLabelingWidget; class QgsPointCloudLayer3DRendererWidget; class QgsMessageBar; class QgsVectorTileBasicRendererWidget; @@ -171,6 +172,7 @@ class APP_EXPORT QgsLayerStylingWidget : public QWidget, private Ui::QgsLayerSty QgsMapLayer *mCurrentLayer = nullptr; QgsLabelingWidget *mLabelingWidget = nullptr; QgsMeshLabelingWidget *mMeshLabelingWidget = nullptr; + QPointer mRasterLabelingWidget; QgsMaskingWidget *mMaskingWidget = nullptr; #ifdef HAVE_3D QgsVectorLayer3DRendererWidget *mVector3DWidget = nullptr; diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 75d9788205bd..a97f8f43aa74 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -790,6 +790,7 @@ set(QGIS_CORE_SRCS raster/qgsrasteridentifyresult.cpp raster/qgsrasterinterface.cpp raster/qgsrasteriterator.cpp + raster/qgsrasterlabeling.cpp raster/qgsrasterlayer.cpp raster/qgsrasterlayerelevationproperties.cpp raster/qgsrasterlayerprofilegenerator.cpp @@ -1924,6 +1925,7 @@ set(QGIS_CORE_HDRS raster/qgsrasteridentifyresult.h raster/qgsrasterinterface.h raster/qgsrasteriterator.h + raster/qgsrasterlabeling.h raster/qgsrasterlayer.h raster/qgsrasterlayerelevationproperties.h raster/qgsrasterlayerprofilegenerator.h diff --git a/src/core/labeling/qgslabelingengine.h b/src/core/labeling/qgslabelingengine.h index cd1568813e13..fb6141fce2c4 100644 --- a/src/core/labeling/qgslabelingengine.h +++ b/src/core/labeling/qgslabelingengine.h @@ -164,7 +164,11 @@ class CORE_EXPORT QgsAbstractLabelProvider //! What placement strategy to use for the labels Qgis::LabelPlacement placement() const { return mPlacement; } - //! Default priority of labels (may be overridden by individual labels) + /** + * Default priority of labels (may be overridden by individual labels). + * + * This is a value between 0 to 1, where 0 = highest priority and 1 = lowest priority. The default is 0.5. + */ double priority() const { return mPriority; } //! How the feature geometries will work as obstacles @@ -203,7 +207,7 @@ class CORE_EXPORT QgsAbstractLabelProvider Flags mFlags = DrawLabels; //! Placement strategy Qgis::LabelPlacement mPlacement = Qgis::LabelPlacement::AroundPoint; - //! Default priority of labels + //! Default priority of labels. 0 = highest priority, 1 = lowest priority double mPriority = 0.5; //! Type of the obstacle of feature geometries QgsLabelObstacleSettings::ObstacleType mObstacleType = QgsLabelObstacleSettings::ObstacleType::PolygonBoundary; diff --git a/src/core/labeling/qgspallabeling.cpp b/src/core/labeling/qgspallabeling.cpp index ecb22f13b963..fed2f7af2d9a 100644 --- a/src/core/labeling/qgspallabeling.cpp +++ b/src/core/labeling/qgspallabeling.cpp @@ -59,6 +59,7 @@ #include "qgsfontmanager.h" #include "qgsvariantutils.h" #include "qgsmeshlayer.h" +#include "qgsrasterlayer.h" using namespace pal; @@ -4096,6 +4097,11 @@ bool QgsPalLabeling::staticWillUseLayer( const QgsMapLayer *layer ) } case Qgis::LayerType::Raster: + { + const QgsRasterLayer *rl = qobject_cast< const QgsRasterLayer * >( layer ); + return rl->labeling() && rl->labelsEnabled(); + } + case Qgis::LayerType::Plugin: case Qgis::LayerType::PointCloud: case Qgis::LayerType::Annotation: @@ -4231,7 +4237,7 @@ QgsGeometry QgsPalLabeling::prepareGeometry( const QgsGeometry &geometry, QgsRen } } - // Rotate the geometry if needed, before clipping + // Rotate the geometry if needed const QgsMapToPixel &m2p = context.mapToPixel(); if ( !qgsDoubleNear( m2p.mapRotation(), 0 ) ) { diff --git a/src/core/maprenderer/qgsmaprendererjob.cpp b/src/core/maprenderer/qgsmaprendererjob.cpp index 1da7871e788f..7dcde5a53e50 100644 --- a/src/core/maprenderer/qgsmaprendererjob.cpp +++ b/src/core/maprenderer/qgsmaprendererjob.cpp @@ -50,6 +50,7 @@ #include "qgsruntimeprofiler.h" #include "qgsmeshlayer.h" #include "qgsmeshlayerlabeling.h" +#include "qgsrasterlabeling.h" #include "qgsgeos.h" const QgsSettingsEntryBool *QgsMapRendererJob::settingsLogCanvasRefreshEvent = new QgsSettingsEntryBool( QStringLiteral( "logCanvasRefreshEvent" ), QgsSettingsTree::sTreeMap, false ); @@ -268,13 +269,22 @@ bool QgsMapRendererJob::prepareLabelCache() const break; } + case Qgis::LayerType::Raster: + { + QgsRasterLayer *l = qobject_cast< QgsRasterLayer *>( ml ); + if ( l->labelsEnabled() && l->labeling()->requiresAdvancedEffects() ) + { + canCache = false; + } + break; + } + case Qgis::LayerType::VectorTile: { // TODO -- add detection of advanced labeling effects for vector tile layers break; } - case Qgis::LayerType::Raster: case Qgis::LayerType::Annotation: case Qgis::LayerType::Plugin: case Qgis::LayerType::PointCloud: diff --git a/src/core/providers/gdal/qgsgdalprovider.cpp b/src/core/providers/gdal/qgsgdalprovider.cpp index d91fd8ac6213..e9a399fbaac9 100644 --- a/src/core/providers/gdal/qgsgdalprovider.cpp +++ b/src/core/providers/gdal/qgsgdalprovider.cpp @@ -755,18 +755,18 @@ bool QgsGdalProvider::canDoResampling( if ( resamplingFactor < 1 ) { // upsampling - return mZoomedInResamplingMethod != QgsRasterDataProvider::ResamplingMethod::Nearest; + return mZoomedInResamplingMethod != Qgis::RasterResamplingMethod::Nearest; } if ( resamplingFactor < 1.1 ) { // very close to nominal resolution ==> check compatibility of zoom-in or zoom-out resampler with what GDAL can do - return mZoomedInResamplingMethod != QgsRasterDataProvider::ResamplingMethod::Nearest || - mZoomedOutResamplingMethod != QgsRasterDataProvider::ResamplingMethod::Nearest; + return mZoomedInResamplingMethod != Qgis::RasterResamplingMethod::Nearest || + mZoomedOutResamplingMethod != Qgis::RasterResamplingMethod::Nearest; } // if no zoom out resampling, exit now - if ( mZoomedOutResamplingMethod == QgsRasterDataProvider::ResamplingMethod::Nearest ) + if ( mZoomedOutResamplingMethod == Qgis::RasterResamplingMethod::Nearest ) { return false; } @@ -795,40 +795,40 @@ bool QgsGdalProvider::canDoResampling( return false; } -static GDALRIOResampleAlg getGDALResamplingAlg( QgsGdalProvider::ResamplingMethod method ) +static GDALRIOResampleAlg getGDALResamplingAlg( Qgis::RasterResamplingMethod method ) { GDALRIOResampleAlg eResampleAlg = GRIORA_NearestNeighbour; switch ( method ) { - case QgsGdalProvider::ResamplingMethod::Nearest: + case Qgis::RasterResamplingMethod::Nearest: eResampleAlg = GRIORA_NearestNeighbour; break; - case QgsGdalProvider::ResamplingMethod::Bilinear: + case Qgis::RasterResamplingMethod::Bilinear: eResampleAlg = GRIORA_Bilinear; break; - case QgsGdalProvider::ResamplingMethod::Cubic: + case Qgis::RasterResamplingMethod::Cubic: eResampleAlg = GRIORA_Cubic; break; - case QgsRasterDataProvider::ResamplingMethod::CubicSpline: + case Qgis::RasterResamplingMethod::CubicSpline: eResampleAlg = GRIORA_CubicSpline; break; - case QgsRasterDataProvider::ResamplingMethod::Lanczos: + case Qgis::RasterResamplingMethod::Lanczos: eResampleAlg = GRIORA_Lanczos; break; - case QgsRasterDataProvider::ResamplingMethod::Average: + case Qgis::RasterResamplingMethod::Average: eResampleAlg = GRIORA_Average; break; - case QgsRasterDataProvider::ResamplingMethod::Mode: + case Qgis::RasterResamplingMethod::Mode: eResampleAlg = GRIORA_Mode; break; - case QgsRasterDataProvider::ResamplingMethod::Gauss: + case Qgis::RasterResamplingMethod::Gauss: eResampleAlg = GRIORA_Gauss; break; } @@ -981,7 +981,7 @@ bool QgsGdalProvider::readBlock( int bandNo, QgsRectangle const &reqExtent, int GDALRasterIOExtraArg sExtraArg; INIT_RASTERIO_EXTRA_ARG( sExtraArg ); - ResamplingMethod method; + Qgis::RasterResamplingMethod method; if ( resamplingFactor < 1 ) { method = mZoomedInResamplingMethod; @@ -989,7 +989,7 @@ bool QgsGdalProvider::readBlock( int bandNo, QgsRectangle const &reqExtent, int else if ( resamplingFactor < 1.1 ) { // very close to nominal resolution ==> use either zoomed out resampler or zoomed in resampler - if ( mZoomedOutResamplingMethod != ResamplingMethod::Nearest ) + if ( mZoomedOutResamplingMethod != Qgis::RasterResamplingMethod::Nearest ) method = mZoomedOutResamplingMethod; else method = mZoomedInResamplingMethod; @@ -1037,7 +1037,7 @@ bool QgsGdalProvider::readBlock( int bandNo, QgsRectangle const &reqExtent, int // (too much downsampling compared to the allowed maximum resampling factor), // so fallback to something replicating QgsRasterResampleFilter behavior else if ( mProviderResamplingEnabled && - mZoomedOutResamplingMethod != QgsRasterDataProvider::ResamplingMethod::Nearest && + mZoomedOutResamplingMethod != Qgis::RasterResamplingMethod::Nearest && resamplingFactor > 1 ) { // Do the resampling in two steps: diff --git a/src/core/providers/gdal/qgsgdalprovider.h b/src/core/providers/gdal/qgsgdalprovider.h index 3bbff95a0e94..3f21fc288804 100644 --- a/src/core/providers/gdal/qgsgdalprovider.h +++ b/src/core/providers/gdal/qgsgdalprovider.h @@ -216,8 +216,8 @@ class QgsGdalProvider final: public QgsRasterDataProvider, QgsGdalProviderBase QgsPoint transformCoordinates( const QgsPoint &point, TransformType type ) override; bool enableProviderResampling( bool enable ) override { mProviderResamplingEnabled = enable; return true; } - bool setZoomedInResamplingMethod( ResamplingMethod method ) override { mZoomedInResamplingMethod = method; return true; } - bool setZoomedOutResamplingMethod( ResamplingMethod method ) override { mZoomedOutResamplingMethod = method; return true; } + bool setZoomedInResamplingMethod( Qgis::RasterResamplingMethod method ) override { mZoomedInResamplingMethod = method; return true; } + bool setZoomedOutResamplingMethod( Qgis::RasterResamplingMethod method ) override { mZoomedOutResamplingMethod = method; return true; } bool setMaxOversampling( double factor ) override { mMaxOversampling = factor; return true; } Qgis::ProviderStyleStorageCapabilities styleStorageCapabilities() const override; diff --git a/src/core/qgis.h b/src/core/qgis.h index 0020f4d06ae1..31d6fba5792a 100644 --- a/src/core/qgis.h +++ b/src/core/qgis.h @@ -1378,13 +1378,31 @@ class CORE_EXPORT Qgis */ enum class RasterResamplingStage SIP_MONKEYPATCH_SCOPEENUM_UNNEST( QgsRasterPipe, ResamplingStage ) : int { - //! Resampling occurs in ResamplingFilter - ResampleFilter, - //! Resampling occurs in Provider - Provider + ResampleFilter, //!< Resampling occurs in ResamplingFilter + Provider, //!< Resampling occurs in Provider }; Q_ENUM( RasterResamplingStage ) + /** + * Resampling method for raster provider-level resampling. + * + * \note Prior to QGIS 3.42 this was available as QgsRasterDataProvider::ResamplingMethod + * + * \since QGIS 3.42 + */ + enum class RasterResamplingMethod SIP_MONKEYPATCH_SCOPEENUM_UNNEST( QgsRasterDataProvider, ResamplingMethod ) : int + { + Nearest, //!< Nearest-neighbour resampling + Bilinear, //!< Bilinear (2x2 kernel) resampling + Cubic, //!< Cubic Convolution Approximation (4x4 kernel) resampling + CubicSpline, //!< Cubic B-Spline Approximation (4x4 kernel) + Lanczos, //!< Lanczos windowed sinc interpolation (6x6 kernel) + Average, //!< Average resampling + Mode, //!< Mode (selects the value which appears most often of all the sampled points) + Gauss //!< Gauss blurring + }; + Q_ENUM( RasterResamplingMethod ) + /** * Flags which control behavior of raster renderers. * diff --git a/src/core/qgsgdalutils.cpp b/src/core/qgsgdalutils.cpp index afe05bf06573..8d3310cf9911 100644 --- a/src/core/qgsgdalutils.cpp +++ b/src/core/qgsgdalutils.cpp @@ -636,37 +636,37 @@ GDALDataType QgsGdalUtils::gdalDataTypeFromQgisDataType( Qgis::DataType dataType return GDALDataType::GDT_Unknown; } -GDALResampleAlg QgsGdalUtils::gdalResamplingAlgorithm( QgsRasterDataProvider::ResamplingMethod method ) +GDALResampleAlg QgsGdalUtils::gdalResamplingAlgorithm( Qgis::RasterResamplingMethod method ) { GDALResampleAlg eResampleAlg = GRA_NearestNeighbour; switch ( method ) { - case QgsRasterDataProvider::ResamplingMethod::Nearest: - case QgsRasterDataProvider::ResamplingMethod::Gauss: // Gauss not available in GDALResampleAlg + case Qgis::RasterResamplingMethod::Nearest: + case Qgis::RasterResamplingMethod::Gauss: // Gauss not available in GDALResampleAlg eResampleAlg = GRA_NearestNeighbour; break; - case QgsRasterDataProvider::ResamplingMethod::Bilinear: + case Qgis::RasterResamplingMethod::Bilinear: eResampleAlg = GRA_Bilinear; break; - case QgsRasterDataProvider::ResamplingMethod::Cubic: + case Qgis::RasterResamplingMethod::Cubic: eResampleAlg = GRA_Cubic; break; - case QgsRasterDataProvider::ResamplingMethod::CubicSpline: + case Qgis::RasterResamplingMethod::CubicSpline: eResampleAlg = GRA_CubicSpline; break; - case QgsRasterDataProvider::ResamplingMethod::Lanczos: + case Qgis::RasterResamplingMethod::Lanczos: eResampleAlg = GRA_Lanczos; break; - case QgsRasterDataProvider::ResamplingMethod::Average: + case Qgis::RasterResamplingMethod::Average: eResampleAlg = GRA_Average; break; - case QgsRasterDataProvider::ResamplingMethod::Mode: + case Qgis::RasterResamplingMethod::Mode: eResampleAlg = GRA_Mode; break; } diff --git a/src/core/qgsgdalutils.h b/src/core/qgsgdalutils.h index bc7ca47d5e8b..8edc348d2226 100644 --- a/src/core/qgsgdalutils.h +++ b/src/core/qgsgdalutils.h @@ -22,7 +22,8 @@ #include #include "qgsogrutils.h" -#include "qgsrasterdataprovider.h" + +class QgsRasterBlock; /** * \ingroup core @@ -253,7 +254,7 @@ class CORE_EXPORT QgsGdalUtils * * \since QGIS 3.30 */ - static GDALResampleAlg gdalResamplingAlgorithm( QgsRasterDataProvider::ResamplingMethod method ); + static GDALResampleAlg gdalResamplingAlgorithm( Qgis::RasterResamplingMethod method ); #ifndef QT_NO_NETWORKPROXY //! Sets the gdal proxy variables diff --git a/src/core/raster/qgsrasterdataprovider.cpp b/src/core/raster/qgsrasterdataprovider.cpp index d58648ad68d8..4cdc3d136c5e 100644 --- a/src/core/raster/qgsrasterdataprovider.cpp +++ b/src/core/raster/qgsrasterdataprovider.cpp @@ -598,37 +598,37 @@ void QgsRasterDataProvider::copyBaseSettings( const QgsRasterDataProvider &other } } -static QgsRasterDataProvider::ResamplingMethod resamplingMethodFromString( const QString &str ) +static Qgis::RasterResamplingMethod resamplingMethodFromString( const QString &str ) { if ( str == QLatin1String( "bilinear" ) ) { - return QgsRasterDataProvider::ResamplingMethod::Bilinear; + return Qgis::RasterResamplingMethod::Bilinear; } else if ( str == QLatin1String( "cubic" ) ) { - return QgsRasterDataProvider::ResamplingMethod::Cubic; + return Qgis::RasterResamplingMethod::Cubic; } else if ( str == QLatin1String( "cubicSpline" ) ) { - return QgsRasterDataProvider::ResamplingMethod::CubicSpline; + return Qgis::RasterResamplingMethod::CubicSpline; } else if ( str == QLatin1String( "lanczos" ) ) { - return QgsRasterDataProvider::ResamplingMethod::Lanczos; + return Qgis::RasterResamplingMethod::Lanczos; } else if ( str == QLatin1String( "average" ) ) { - return QgsRasterDataProvider::ResamplingMethod::Average; + return Qgis::RasterResamplingMethod::Average; } else if ( str == QLatin1String( "mode" ) ) { - return QgsRasterDataProvider::ResamplingMethod::Mode; + return Qgis::RasterResamplingMethod::Mode; } else if ( str == QLatin1String( "gauss" ) ) { - return QgsRasterDataProvider::ResamplingMethod::Gauss; + return Qgis::RasterResamplingMethod::Gauss; } - return QgsRasterDataProvider::ResamplingMethod::Nearest; + return Qgis::RasterResamplingMethod::Nearest; } void QgsRasterDataProvider::readXml( const QDomElement &filterElem ) @@ -650,25 +650,25 @@ void QgsRasterDataProvider::readXml( const QDomElement &filterElem ) } } -static QString resamplingMethodToString( QgsRasterDataProvider::ResamplingMethod method ) +static QString resamplingMethodToString( Qgis::RasterResamplingMethod method ) { switch ( method ) { - case QgsRasterDataProvider::ResamplingMethod::Nearest: + case Qgis::RasterResamplingMethod::Nearest: return QStringLiteral( "nearestNeighbour" ); - case QgsRasterDataProvider::ResamplingMethod::Bilinear: + case Qgis::RasterResamplingMethod::Bilinear: return QStringLiteral( "bilinear" ); - case QgsRasterDataProvider::ResamplingMethod::Cubic: + case Qgis::RasterResamplingMethod::Cubic: return QStringLiteral( "cubic" ); - case QgsRasterDataProvider::ResamplingMethod::CubicSpline: + case Qgis::RasterResamplingMethod::CubicSpline: return QStringLiteral( "cubicSpline" ); - case QgsRasterDataProvider::ResamplingMethod::Lanczos: + case Qgis::RasterResamplingMethod::Lanczos: return QStringLiteral( "lanczos" ); - case QgsRasterDataProvider::ResamplingMethod::Average: + case Qgis::RasterResamplingMethod::Average: return QStringLiteral( "average" ); - case QgsRasterDataProvider::ResamplingMethod::Mode: + case Qgis::RasterResamplingMethod::Mode: return QStringLiteral( "mode" ); - case QgsRasterDataProvider::ResamplingMethod::Gauss: + case Qgis::RasterResamplingMethod::Gauss: return QStringLiteral( "gauss" ); } // should not happen diff --git a/src/core/raster/qgsrasterdataprovider.h b/src/core/raster/qgsrasterdataprovider.h index f747750664cd..8fd0b4f0f4a4 100644 --- a/src/core/raster/qgsrasterdataprovider.h +++ b/src/core/raster/qgsrasterdataprovider.h @@ -623,35 +623,19 @@ class CORE_EXPORT QgsRasterDataProvider : public QgsDataProvider, public QgsRast */ bool isProviderResamplingEnabled() const { return mProviderResamplingEnabled; } - /** - * Resampling method for provider-level resampling. - * \since QGIS 3.16 - */ - enum class ResamplingMethod - { - Nearest, //!< Nearest-neighbour resampling - Bilinear, //!< Bilinear (2x2 kernel) resampling - Cubic,//!< Cubic Convolution Approximation (4x4 kernel) resampling - CubicSpline, //!< Cubic B-Spline Approximation (4x4 kernel) - Lanczos, //!< Lanczos windowed sinc interpolation (6x6 kernel) - Average, //!< Average resampling - Mode, //!< Mode (selects the value which appears most often of all the sampled points) - Gauss //!< Gauss blurring - }; - /** * Set resampling method to apply for zoomed-in operations. * * \return TRUE if success * \since QGIS 3.16 */ - virtual bool setZoomedInResamplingMethod( ResamplingMethod method ) { Q_UNUSED( method ); return false; } + virtual bool setZoomedInResamplingMethod( Qgis::RasterResamplingMethod method ) { Q_UNUSED( method ); return false; } /** * Returns resampling method for zoomed-in operations. * \since QGIS 3.16 */ - ResamplingMethod zoomedInResamplingMethod() const { return mZoomedInResamplingMethod; } + Qgis::RasterResamplingMethod zoomedInResamplingMethod() const { return mZoomedInResamplingMethod; } /** * Set resampling method to apply for zoomed-out operations. @@ -659,13 +643,13 @@ class CORE_EXPORT QgsRasterDataProvider : public QgsDataProvider, public QgsRast * \return TRUE if success * \since QGIS 3.16 */ - virtual bool setZoomedOutResamplingMethod( ResamplingMethod method ) { Q_UNUSED( method ); return false; } + virtual bool setZoomedOutResamplingMethod( Qgis::RasterResamplingMethod method ) { Q_UNUSED( method ); return false; } /** * Returns resampling method for zoomed-out operations. * \since QGIS 3.16 */ - ResamplingMethod zoomedOutResamplingMethod() const { return mZoomedOutResamplingMethod; } + Qgis::RasterResamplingMethod zoomedOutResamplingMethod() const { return mZoomedOutResamplingMethod; } /** * Sets maximum oversampling factor for zoomed-out operations. @@ -827,10 +811,10 @@ class CORE_EXPORT QgsRasterDataProvider : public QgsDataProvider, public QgsRast bool mProviderResamplingEnabled = false; //! Resampling method for zoomed in pixel extraction - ResamplingMethod mZoomedInResamplingMethod = ResamplingMethod::Nearest; + Qgis::RasterResamplingMethod mZoomedInResamplingMethod = Qgis::RasterResamplingMethod::Nearest; //! Resampling method for zoomed out pixel extraction - ResamplingMethod mZoomedOutResamplingMethod = ResamplingMethod::Nearest; + Qgis::RasterResamplingMethod mZoomedOutResamplingMethod = Qgis::RasterResamplingMethod::Nearest; //! Maximum boundary for oversampling (to avoid too much data traffic). Default: 2.0 double mMaxOversampling = 2.0; diff --git a/src/core/raster/qgsrasterlabeling.cpp b/src/core/raster/qgsrasterlabeling.cpp new file mode 100644 index 000000000000..f00887b111d8 --- /dev/null +++ b/src/core/raster/qgsrasterlabeling.cpp @@ -0,0 +1,619 @@ +/*************************************************************************** + qgsrasterlabeling.cpp + --------------- + begin : December 2024 + copyright : (C) 2024 by Nyall Dawson + email : nyall dot dawson at gmail dot com + ***************************************************************************/ +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "qgsrasterlabeling.h" +#include "qgsrasterlayer.h" +#include "labelposition.h" +#include "feature.h" +#include "qgstextrenderer.h" +#include "qgstextlabelfeature.h" +#include "qgsstyleentityvisitor.h" +#include "qgsstyle.h" +#include "qgsnumericformat.h" +#include "qgsnumericformatregistry.h" +#include "qgsbasicnumericformat.h" +#include "qgsapplication.h" +#include "qgsscaleutils.h" +#include "qgsmessagelog.h" +#include "qgsrasterpipe.h" +#include "qgsrasterlayerrenderer.h" + +QgsRasterLayerLabelProvider::QgsRasterLayerLabelProvider( QgsRasterLayer *layer ) + : QgsAbstractLabelProvider( layer ) + , mNumericFormat( std::make_unique< QgsBasicNumericFormat >() ) +{ + mPlacement = Qgis::LabelPlacement::OverPoint; + mFlags |= DrawLabels; +} + +QgsRasterLayerLabelProvider::~QgsRasterLayerLabelProvider() +{ + qDeleteAll( mLabels ); +} + +void QgsRasterLayerLabelProvider::addLabel( const QgsPoint &mapPoint, const QString &text, QgsRenderContext &context ) +{ + QgsPoint geom = mapPoint; + const QgsTextDocument doc = QgsTextDocument::fromTextAndFormat( { text }, mFormat ); + QgsTextDocumentMetrics documentMetrics = QgsTextDocumentMetrics::calculateMetrics( doc, mFormat, context ); + const QSizeF size = documentMetrics.documentSize( Qgis::TextLayoutMode::Point, Qgis::TextOrientation::Horizontal ); + + + // Rotate the geometry if needed, before clipping + const QgsMapToPixel &m2p = context.mapToPixel(); + if ( !qgsDoubleNear( m2p.mapRotation(), 0 ) ) + { + QgsPointXY center = context.mapExtent().center(); + + QTransform t = QTransform::fromTranslate( center.x(), center.y() ); + t.rotate( - m2p.mapRotation() ); + t.translate( -center.x(), -center.y() ); + geom.transform( t ); + } + + const double uPP = m2p.mapUnitsPerPixel(); + std::unique_ptr< QgsTextLabelFeature > feature = std::make_unique< QgsTextLabelFeature >( mLabels.size(), + QgsGeos::asGeos( &geom ), + QSizeF( size.width() * uPP, + size.height() * uPP ) ); + + feature->setDocument( doc, documentMetrics ); + feature->setFixedAngle( 0 ); + feature->setHasFixedAngle( true ); + feature->setQuadOffset( QPointF( 0, 0 ) ); + feature->setZIndex( mZIndex ); + + feature->setOverlapHandling( mPlacementSettings.overlapHandling() ); + + mLabels.append( feature.release() ); +} + +void QgsRasterLayerLabelProvider::setTextFormat( const QgsTextFormat &format ) +{ + mFormat = format; +} + +void QgsRasterLayerLabelProvider::setNumericFormat( std::unique_ptr format ) +{ + mNumericFormat = std::move( format ); +} + +QgsNumericFormat *QgsRasterLayerLabelProvider::numericFormat() +{ + return mNumericFormat.get(); +} + +void QgsRasterLayerLabelProvider::setResampleMethod( Qgis::RasterResamplingMethod method ) +{ + mResampleMethod = method; +} + +void QgsRasterLayerLabelProvider::setResampleOver( int pixels ) +{ + mResampleOver = pixels; +} + +QList QgsRasterLayerLabelProvider::labelFeatures( QgsRenderContext & ) +{ + return mLabels; +} + +void QgsRasterLayerLabelProvider::drawLabel( QgsRenderContext &context, pal::LabelPosition *label ) const +{ + // as per vector label rendering... + QgsMapToPixel xform = context.mapToPixel(); + xform.setMapRotation( 0, 0, 0 ); + const QPointF outPt = xform.transform( label->getX(), label->getY() ).toQPointF(); + + QgsTextLabelFeature *lf = qgis::down_cast( label->getFeaturePart()->feature() ); + QgsTextRenderer::drawDocument( outPt, + mFormat, lf->document(), lf->documentMetrics(), context, Qgis::TextHorizontalAlignment::Left, + label->getAlpha(), Qgis::TextLayoutMode::Labeling ); +} + +void QgsRasterLayerLabelProvider::startRender( QgsRenderContext &context ) +{ + if ( mFormat.dataDefinedProperties().hasActiveProperties() ) + mFormat.updateDataDefinedProperties( context ); + QgsAbstractLabelProvider::startRender( context ); +} + +///@cond PRIVATE +// RAII properties restorer for QgsRasterDataProvider +struct RasterProviderSettingsRestorer +{ + QgsRasterDataProvider *mProvider; + const bool mProviderResampling; + const Qgis::RasterResamplingMethod mZoomedOutMethod; + const double mMaxOversampling; + + RasterProviderSettingsRestorer( QgsRasterDataProvider *provider ) + : mProvider( provider ) + , mProviderResampling( provider->isProviderResamplingEnabled() ) + , mZoomedOutMethod( provider->zoomedOutResamplingMethod() ) + , mMaxOversampling( provider->maxOversampling() ) {} + + ~RasterProviderSettingsRestorer() + { + mProvider->enableProviderResampling( mProviderResampling ); + mProvider->setZoomedOutResamplingMethod( mZoomedOutMethod ); + mProvider->setMaxOversampling( mMaxOversampling ); + } +}; +///@endcond + +void QgsRasterLayerLabelProvider::generateLabels( QgsRenderContext &context, QgsRasterPipe *pipe, QgsRasterViewPort *rasterViewPort, QgsRasterLayerRendererFeedback *feedback ) +{ + if ( !pipe ) + return; + + QgsRasterDataProvider *provider = pipe->provider(); + if ( !provider ) + return; + + if ( provider->xSize() == 0 || provider->ySize() == 0 ) + return; + + if ( !rasterViewPort ) + return; + + // iterate through blocks, directly over the provider. + QgsRasterIterator iterator( provider ); + + const QSize maxTileSize {provider->maximumTileSize()}; + iterator.setMaximumTileWidth( maxTileSize.width() ); + iterator.setMaximumTileHeight( maxTileSize.height() ); + iterator.setSnapToPixelFactor( mResampleOver ); + + // we need to calculate the visible portion of the layer, in the original (layer) CRS: + QgsCoordinateTransform layerToMapTransform = context.coordinateTransform(); + layerToMapTransform.setBallparkTransformsAreAppropriate( true ); + QgsRectangle layerVisibleExtent; + try + { + layerVisibleExtent = layerToMapTransform.transformBoundingBox( rasterViewPort->mDrawnExtent, Qgis::TransformDirection::Reverse ); + } + catch ( QgsCsException &cs ) + { + QgsMessageLog::logMessage( QObject::tr( "Could not reproject view extent: %1" ).arg( cs.what() ), QObject::tr( "Raster" ) ); + return; + } + + const int maxNumLabels = mThinningSettings.limitNumberOfLabelsEnabled() ? mThinningSettings.maximumNumberLabels() : 0; + + // calculate the portion of the raster which is actually visible in the map + int subRegionWidth = 0; + int subRegionHeight = 0; + int subRegionLeft = 0; + int subRegionTop = 0; + QgsRectangle rasterSubRegion = QgsRasterIterator::subRegion( + provider->extent(), + provider->xSize(), + provider->ySize(), + layerVisibleExtent, + subRegionWidth, + subRegionHeight, + subRegionLeft, + subRegionTop ); + + const double rasterUnitsPerPixelX = provider->extent().width() / provider->xSize() * mResampleOver; + const double rasterUnitsPerPixelY = provider->extent().height() / provider->ySize() * mResampleOver; + + const double minPixelSizePainterUnits = context.convertToPainterUnits( mThinningSettings.minimumFeatureSize(), Qgis::RenderUnit::Millimeters ); + if ( minPixelSizePainterUnits > 0 ) + { + // calculate size in painter units of one raster pixel + QgsPointXY p1( rasterSubRegion.xMinimum(), rasterSubRegion.yMinimum() ); + QgsPointXY p2( rasterSubRegion.xMinimum() + rasterSubRegion.width() / subRegionWidth, + rasterSubRegion.yMinimum() + rasterSubRegion.height() / subRegionHeight ); + try + { + p1 = context.coordinateTransform().transform( p1 ); + p2 = context.coordinateTransform().transform( p2 ); + } + catch ( QgsCsException & ) + { + QgsDebugError( QStringLiteral( "Could not transform raster pixel to map crs" ) ); + return; + } + const QgsPointXY p1PainterUnits = context.mapToPixel().transform( p1 ); + const QgsPointXY p2PainterUnits = context.mapToPixel().transform( p2 ); + const double painterUnitsPerRasterPixel = std::max( std::fabs( p1PainterUnits.x() - p2PainterUnits.x() ), + std::fabs( p1PainterUnits.y() - p2PainterUnits.y() ) ) * mResampleOver; + if ( painterUnitsPerRasterPixel < minPixelSizePainterUnits ) + return; + } + + iterator.startRasterRead( mBandNumber, subRegionWidth, subRegionHeight, rasterSubRegion, feedback ); + + const QgsNumericFormatContext numericContext; + QgsNumericFormat *numericFormat = mNumericFormat.get(); + + int iterLeft = 0; + int iterTop = 0; + int iterCols = 0; + int iterRows = 0; + QgsRectangle blockExtent; + std::unique_ptr< QgsRasterBlock > block; + bool isNoData = false; + int numberLabels = 0; + + RasterProviderSettingsRestorer restorer( provider ); + if ( mResampleOver > 1 ) + { + provider->enableProviderResampling( true ); + provider->setZoomedOutResamplingMethod( mResampleMethod ); + provider->setMaxOversampling( mResampleOver ); + } + + while ( iterator.next( mBandNumber, iterCols, iterRows, iterLeft, iterTop, blockExtent ) ) + { + if ( feedback && feedback->isCanceled() ) + return; + + const int resampledColumns = iterCols / mResampleOver; + const int resampledRows = iterRows / mResampleOver; + block.reset( provider->block( mBandNumber, blockExtent, resampledColumns, resampledRows, feedback ) ); + + double currentY = blockExtent.yMaximum() - 0.5 * rasterUnitsPerPixelY; + + for ( int row = 0; row < resampledRows; row++ ) + { + if ( feedback && feedback->isCanceled() ) + return; + + double currentX = blockExtent.xMinimum() + 0.5 * rasterUnitsPerPixelX; + + for ( int column = 0; column < resampledColumns; column++ ) + { + const double value = block->valueAndNoData( row, column, isNoData ); + if ( !isNoData ) + { + try + { + QgsPoint pixelCenter( currentX, currentY ); + pixelCenter.transform( context.coordinateTransform() ); + + addLabel( pixelCenter, + numericFormat->formatDouble( value, numericContext ), + context ); + numberLabels++; + if ( maxNumLabels > 0 && numberLabels >= maxNumLabels ) + return; + } + catch ( QgsCsException & ) + { + QgsDebugError( QStringLiteral( "Could not transform raster pixel center to map crs" ) ); + } + } + currentX += rasterUnitsPerPixelX; + } + currentY -= rasterUnitsPerPixelY; + } + } +} + +// +// QgsAbstractRasterLayerLabeling +// + +void QgsAbstractRasterLayerLabeling::multiplyOpacity( double ) +{ + +} + +bool QgsAbstractRasterLayerLabeling::isInScaleRange( double ) const +{ + return true; +} + +QgsAbstractRasterLayerLabeling *QgsAbstractRasterLayerLabeling::createFromElement( const QDomElement &element, const QgsReadWriteContext &context ) +{ + const QString type = element.attribute( QStringLiteral( "type" ) ); + if ( type == QLatin1String( "simple" ) ) + { + return QgsRasterLayerSimpleLabeling::create( element, context ); + } + else + { + return nullptr; + } +} + +void QgsAbstractRasterLayerLabeling::toSld( QDomNode &parent, const QVariantMap &props ) const +{ + Q_UNUSED( parent ) + Q_UNUSED( props ) + QDomDocument doc = parent.ownerDocument(); + parent.appendChild( doc.createComment( QStringLiteral( "SE Export for %1 not implemented yet" ).arg( type() ) ) ); +} + +bool QgsAbstractRasterLayerLabeling::accept( QgsStyleEntityVisitorInterface * ) const +{ + return true; +} + +// +// QgsRasterLayerSimpleLabeling +// + + +QgsRasterLayerSimpleLabeling::QgsRasterLayerSimpleLabeling() + : mNumericFormat( std::make_unique< QgsBasicNumericFormat >() ) +{ + mThinningSettings.setMaximumNumberLabels( 4000 ); + mThinningSettings.setLimitNumberLabelsEnabled( true ); + mThinningSettings.setMinimumFeatureSize( 8 ); +} + +QgsRasterLayerSimpleLabeling::~QgsRasterLayerSimpleLabeling() = default; + +QString QgsRasterLayerSimpleLabeling::type() const +{ + return QStringLiteral( "simple" ); +} + +QgsRasterLayerSimpleLabeling *QgsRasterLayerSimpleLabeling::clone() const +{ + std::unique_ptr< QgsRasterLayerSimpleLabeling > res = std::make_unique< QgsRasterLayerSimpleLabeling >(); + res->setTextFormat( mTextFormat ); + + if ( mNumericFormat ) + res->mNumericFormat.reset( mNumericFormat->clone() ); + + res->setBand( mBandNumber ); + res->setPriority( mPriority ); + res->setPlacementSettings( mPlacementSettings ); + res->setThinningSettings( mThinningSettings ); + res->setZIndex( mZIndex ); + res->setScaleBasedVisibility( mScaleVisibility ); + res->setMaximumScale( mMaximumScale ); + res->setMinimumScale( mMinimumScale ); + res->setResampleMethod( mResampleMethod ); + res->setResampleOver( mResampleOver ); + + return res.release(); +} + +std::unique_ptr< QgsRasterLayerLabelProvider > QgsRasterLayerSimpleLabeling::provider( QgsRasterLayer *layer ) const +{ + std::unique_ptr< QgsRasterLayerLabelProvider > res = std::make_unique< QgsRasterLayerLabelProvider >( layer ); + res->setTextFormat( mTextFormat ); + res->setBand( mBandNumber ); + res->setPriority( mPriority ); + res->setPlacementSettings( mPlacementSettings ); + res->setZIndex( mZIndex ); + res->setThinningSettings( mThinningSettings ); + res->setResampleMethod( mResampleMethod ); + res->setResampleOver( mResampleOver ); + if ( mNumericFormat ) + { + res->setNumericFormat( std::unique_ptr< QgsNumericFormat >( mNumericFormat->clone() ) ); + } + return res; +} + +QDomElement QgsRasterLayerSimpleLabeling::save( QDomDocument &doc, const QgsReadWriteContext &context ) const +{ + QDomElement elem = doc.createElement( QStringLiteral( "labeling" ) ); + elem.setAttribute( QStringLiteral( "type" ), QStringLiteral( "simple" ) ); + elem.setAttribute( QStringLiteral( "band" ), mBandNumber ); + elem.setAttribute( QStringLiteral( "priority" ), mPriority ); + elem.setAttribute( QStringLiteral( "zIndex" ), mZIndex ); + + if ( mResampleOver > 1 ) + { + elem.setAttribute( QStringLiteral( "resampleOver" ), mResampleOver ); + } + elem.setAttribute( QStringLiteral( "resampleMethod" ), qgsEnumValueToKey( mResampleMethod ) ); + + { + QDomElement textFormatElem = doc.createElement( QStringLiteral( "textFormat" ) ); + textFormatElem.appendChild( mTextFormat.writeXml( doc, context ) ); + elem.appendChild( textFormatElem ); + } + + { + QDomElement numericFormatElem = doc.createElement( QStringLiteral( "numericFormat" ) ); + mNumericFormat->writeXml( numericFormatElem, doc, context ); + elem.appendChild( numericFormatElem ); + } + + { + QDomElement placementElem = doc.createElement( QStringLiteral( "placement" ) ); + placementElem.setAttribute( QStringLiteral( "overlapHandling" ), qgsEnumValueToKey( mPlacementSettings.overlapHandling() ) ); + elem.appendChild( placementElem ); + } + + { + QDomElement thinningElem = doc.createElement( QStringLiteral( "thinning" ) ); + thinningElem.setAttribute( QStringLiteral( "maxNumLabels" ), mThinningSettings.maximumNumberLabels() ); + thinningElem.setAttribute( QStringLiteral( "limitNumLabels" ), mThinningSettings.limitNumberOfLabelsEnabled() ); + thinningElem.setAttribute( QStringLiteral( "minFeatureSize" ), mThinningSettings.minimumFeatureSize() ); + elem.appendChild( thinningElem ); + } + + { + QDomElement renderingElem = doc.createElement( QStringLiteral( "rendering" ) ); + renderingElem.setAttribute( QStringLiteral( "scaleVisibility" ), mScaleVisibility ); + // note the element names are "flipped" vs the member -- this is intentional, and done to match vector labeling + renderingElem.setAttribute( QStringLiteral( "scaleMin" ), mMaximumScale ); + renderingElem.setAttribute( QStringLiteral( "scaleMax" ), mMinimumScale ); + elem.appendChild( renderingElem ); + } + + return elem; +} + +bool QgsRasterLayerSimpleLabeling::accept( QgsStyleEntityVisitorInterface *visitor ) const +{ + QgsStyleTextFormatEntity entity( mTextFormat ); + if ( !visitor->visit( &entity ) ) + return false; + + return true; +} + +bool QgsRasterLayerSimpleLabeling::requiresAdvancedEffects() const +{ + return mTextFormat.containsAdvancedEffects(); +} + +QgsRasterLayerSimpleLabeling *QgsRasterLayerSimpleLabeling::create( const QDomElement &element, const QgsReadWriteContext &context ) +{ + std::unique_ptr< QgsRasterLayerSimpleLabeling > res = std::make_unique< QgsRasterLayerSimpleLabeling >(); + res->setBand( element.attribute( QStringLiteral( "band" ), QStringLiteral( "1" ) ).toInt() ); + res->setPriority( element.attribute( QStringLiteral( "priority" ), QStringLiteral( "0.5" ) ).toDouble() ); + res->setZIndex( element.attribute( QStringLiteral( "zIndex" ), QStringLiteral( "0" ) ).toDouble() ); + res->setResampleOver( element.attribute( QStringLiteral( "resampleOver" ), QStringLiteral( "1" ) ).toInt() ); + res->setResampleMethod( qgsEnumKeyToValue( element.attribute( QStringLiteral( "resampleMethod" ) ), Qgis::RasterResamplingMethod::Average ) ); + + const QDomElement textFormatElem = element.firstChildElement( QStringLiteral( "textFormat" ) ); + if ( !textFormatElem.isNull() ) + { + const QDomNodeList textFormatNodeList = textFormatElem.elementsByTagName( QStringLiteral( "text-style" ) ); + const QDomElement textFormatElem = textFormatNodeList.at( 0 ).toElement(); + QgsTextFormat format; + format.readXml( textFormatElem, context ); + res->setTextFormat( format ); + } + + const QDomNodeList numericFormatNodeList = element.elementsByTagName( QStringLiteral( "numericFormat" ) ); + if ( !numericFormatNodeList.isEmpty() ) + { + const QDomElement numericFormatElem = numericFormatNodeList.at( 0 ).toElement(); + res->mNumericFormat.reset( QgsApplication::numericFormatRegistry()->createFromXml( numericFormatElem, context ) ); + } + + QDomElement placementElem = element.firstChildElement( QStringLiteral( "placement" ) ); + res->mPlacementSettings.setOverlapHandling( qgsEnumKeyToValue( placementElem.attribute( QStringLiteral( "overlapHandling" ) ), Qgis::LabelOverlapHandling::PreventOverlap ) ); + + QDomElement thinningElem = element.firstChildElement( QStringLiteral( "thinning" ) ); + res->mThinningSettings.setMaximumNumberLabels( thinningElem.attribute( QStringLiteral( "maxNumLabels" ), QStringLiteral( "4000" ) ).toInt() ); + res->mThinningSettings.setLimitNumberLabelsEnabled( thinningElem.attribute( QStringLiteral( "limitNumLabels" ), QStringLiteral( "1" ) ).toInt() ); + res->mThinningSettings.setMinimumFeatureSize( thinningElem.attribute( QStringLiteral( "minFeatureSize" ), QStringLiteral( "8" ) ).toDouble() ); + + QDomElement renderingElem = element.firstChildElement( QStringLiteral( "rendering" ) ); + // note the element names are "flipped" vs the member -- this is intentional, and done to match vector labeling + res->mMaximumScale = renderingElem.attribute( QStringLiteral( "scaleMin" ), QStringLiteral( "0" ) ).toDouble(); + res->mMinimumScale = renderingElem.attribute( QStringLiteral( "scaleMax" ), QStringLiteral( "0" ) ).toDouble(); + res->mScaleVisibility = renderingElem.attribute( QStringLiteral( "scaleVisibility" ) ).toInt(); + + return res.release(); +} + +QgsTextFormat QgsRasterLayerSimpleLabeling::textFormat() const +{ + return mTextFormat; +} + +void QgsRasterLayerSimpleLabeling::setTextFormat( const QgsTextFormat &format ) +{ + mTextFormat = format; +} + +const QgsNumericFormat *QgsRasterLayerSimpleLabeling::numericFormat() const +{ + return mNumericFormat.get(); +} + +void QgsRasterLayerSimpleLabeling::setNumericFormat( QgsNumericFormat *format ) +{ + if ( format != mNumericFormat.get() ) + mNumericFormat.reset( format ); +} + +double QgsRasterLayerSimpleLabeling::zIndex() const +{ + return mZIndex; +} + +void QgsRasterLayerSimpleLabeling::setZIndex( double index ) +{ + mZIndex = index; +} + +double QgsRasterLayerSimpleLabeling::maximumScale() const +{ + return mMaximumScale; +} + +void QgsRasterLayerSimpleLabeling::setMaximumScale( double scale ) +{ + mMaximumScale = scale; +} + +double QgsRasterLayerSimpleLabeling::minimumScale() const +{ + return mMinimumScale; +} + +void QgsRasterLayerSimpleLabeling::setMinimumScale( double scale ) +{ + mMinimumScale = scale; +} + +bool QgsRasterLayerSimpleLabeling::hasScaleBasedVisibility() const +{ + return mScaleVisibility; +} + +bool QgsRasterLayerSimpleLabeling::isInScaleRange( double scale ) const +{ + // mMinScale (denominator!) is inclusive ( >= --> In range ) + // mMaxScale (denominator!) is exclusive ( < --> In range ) + return !mScaleVisibility + || ( ( mMinimumScale == 0 || !QgsScaleUtils::lessThanMaximumScale( scale, mMinimumScale ) ) + && ( mMaximumScale == 0 || !QgsScaleUtils::equalToOrGreaterThanMinimumScale( scale, mMaximumScale ) ) ); +} + +Qgis::RasterResamplingMethod QgsRasterLayerSimpleLabeling::resampleMethod() const +{ + return mResampleMethod; +} + +void QgsRasterLayerSimpleLabeling::setResampleMethod( Qgis::RasterResamplingMethod method ) +{ + mResampleMethod = method; +} + +int QgsRasterLayerSimpleLabeling::resampleOver() const +{ + return mResampleOver; +} + +void QgsRasterLayerSimpleLabeling::setResampleOver( int pixels ) +{ + mResampleOver = pixels; +} + +void QgsRasterLayerSimpleLabeling::setScaleBasedVisibility( bool enabled ) +{ + mScaleVisibility = enabled; +} + +void QgsRasterLayerSimpleLabeling::multiplyOpacity( double opacityFactor ) +{ + mTextFormat.multiplyOpacity( opacityFactor ); +} + +QgsAbstractRasterLayerLabeling *QgsAbstractRasterLayerLabeling::defaultLabelingForLayer( QgsRasterLayer *layer ) +{ + std::unique_ptr< QgsRasterLayerSimpleLabeling > res = std::make_unique< QgsRasterLayerSimpleLabeling >(); + res->setTextFormat( QgsStyle::defaultTextFormatForProject( layer->project() ) ); + res->setBand( 1 ); + return res.release(); +} diff --git a/src/core/raster/qgsrasterlabeling.h b/src/core/raster/qgsrasterlabeling.h new file mode 100644 index 000000000000..c27177a3ab82 --- /dev/null +++ b/src/core/raster/qgsrasterlabeling.h @@ -0,0 +1,548 @@ +/*************************************************************************** + qgsrasterlabeling.h + --------------- + begin : December 2024 + copyright : (C) 2024 by Nyall Dawson + email : nyall dot dawson at gmail dot com + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + + +#ifndef QGSRASTERLABELING_H +#define QGSRASTERLABELING_H + +#include "qgis_core.h" +#include "qgis_sip.h" +#include "qgslabelingengine.h" +#include "qgstextformat.h" +#include "qgslabelplacementsettings.h" +#include "qgslabelthinningsettings.h" + +class QgsRasterLayer; +class QgsNumericFormat; +class QgsRasterPipe; +struct QgsRasterViewPort; +class QgsRasterLayerRendererFeedback; + +#ifndef SIP_RUN + +/** + * \ingroup core + * \brief Implements labeling support for raster layers. + * + * \note Not available in Python bindings + * + * \since QGIS 3.42 + */ +class CORE_EXPORT QgsRasterLayerLabelProvider final : public QgsAbstractLabelProvider +{ + public: + + /** + * Constructor for QgsRasterLayerLabelProvider. + */ + explicit QgsRasterLayerLabelProvider( QgsRasterLayer *layer ); + + ~QgsRasterLayerLabelProvider() final; + QList labelFeatures( QgsRenderContext & ) final; + void drawLabel( QgsRenderContext &context, pal::LabelPosition *label ) const final; + void startRender( QgsRenderContext &context ) final; + + /** + * Generates the labels, given a render context and input pipe. + */ + void generateLabels( QgsRenderContext &context, QgsRasterPipe *pipe, QgsRasterViewPort *rasterViewPort, QgsRasterLayerRendererFeedback *feedback ); + + /** + * Adds a label at the specified point in map coordinates. + */ + void addLabel( const QgsPoint &mapPoint, const QString &text, QgsRenderContext &context ); + + /** + * Sets the text \a format used for rendering the labels. + */ + void setTextFormat( const QgsTextFormat &format ); + + /** + * Sets the numeric \a format used for the labels. + * + * \see numericFormat() + */ + void setNumericFormat( std::unique_ptr< QgsNumericFormat > format ); + + /** + * Returns the numeric format to be used for the labels. + * + * \see setNumericFormat() + */ + QgsNumericFormat *numericFormat(); + + /** + * Returns the raster band to use for label values. + * + * \see setBand() + */ + int band() const { return mBandNumber; } + + /** + * Sest the raster \a band to use for label values. + * + * \see band() + */ + void setBand( int band ) { mBandNumber = band; } + + /** + * Sets the \a priority of labels. + * + * This is a value between 0 to 1, where 0 = highest priority and 1 = lowest priority. + */ + void setPriority( double priority ) { mPriority = priority; } + + /** + * Sets the label placement \a settings. + */ + void setPlacementSettings( const QgsLabelPlacementSettings &settings ) { mPlacementSettings = settings; } + + /** + * Sets the Z-Index of the labels. + * + * Labels with a higher z-index are rendered on top of labels with a lower z-index. + */ + void setZIndex( double index ) { mZIndex = index; } + + /** + * Sets the label thinning \a settings. + */ + void setThinningSettings( const QgsLabelThinningSettings &settings ) { mThinningSettings = settings; } + + /** + * Sets the resampling \a method to use when the raster labels are being + * resampled over neighboring pixels. + * + * \see setResampleOver() + */ + void setResampleMethod( Qgis::RasterResamplingMethod method ); + + /** + * Sets the number of neighboring \a pixels to resample over, when labels are + * showing values resampled over neighboring pixels. + * + * \see setResampleMethod() + */ + void setResampleOver( int pixels ); + + private: + QgsTextFormat mFormat; + int mBandNumber = 1; + std::unique_ptr< QgsNumericFormat > mNumericFormat; + + QgsLabelPlacementSettings mPlacementSettings; + QgsLabelThinningSettings mThinningSettings; + double mZIndex = 0; + + Qgis::RasterResamplingMethod mResampleMethod = Qgis::RasterResamplingMethod::Average; + int mResampleOver = 1; + + QList mLabels; + +}; + +#endif + + + +/** + * \ingroup core + * \brief Abstract base class for labeling settings for raster layers. + * + * \since QGIS 3.42 + */ +class CORE_EXPORT QgsAbstractRasterLayerLabeling SIP_ABSTRACT +{ + public: + +#ifdef SIP_RUN + SIP_CONVERT_TO_SUBCLASS_CODE + if ( sipCpp->type() == "simple" ) + sipType = sipType_QgsRasterLayerSimpleLabeling; + else + sipType = 0; + SIP_END +#endif + + QgsAbstractRasterLayerLabeling() = default; + virtual ~QgsAbstractRasterLayerLabeling() = default; +#ifndef SIP_RUN + //! QgsAbstractRasterLayerLabeling cannot be copied, use clone() instead + QgsAbstractRasterLayerLabeling( const QgsAbstractRasterLayerLabeling &rhs ) = delete; + //! QgsAbstractRasterLayerLabeling cannot be copied, use clone() instead + QgsAbstractRasterLayerLabeling &operator=( const QgsAbstractRasterLayerLabeling &rhs ) = delete; +#endif + + /** + * Creates default labeling for a raster \a layer. + */ + static QgsAbstractRasterLayerLabeling *defaultLabelingForLayer( QgsRasterLayer *layer ) SIP_FACTORY; + + //! Unique type string of the labeling configuration implementation + virtual QString type() const = 0; + + //! Returns a new copy of the object + virtual QgsAbstractRasterLayerLabeling *clone() const = 0 SIP_FACTORY; + + /** + * Creates a raster label provider corresponding to this object's configuration. + * + * \note not available in Python bindings + */ + virtual std::unique_ptr< QgsRasterLayerLabelProvider > provider( QgsRasterLayer *layer ) const = 0 SIP_SKIP; + + /** + * Saves the labeling configuration to an XML element. + * + * \see createFromElement() + */ + virtual QDomElement save( QDomDocument &doc, const QgsReadWriteContext &context ) const = 0; + + /** + * Returns TRUE if drawing labels requires advanced effects like composition + * modes, which could prevent it being used as an isolated cached image + * or exported to a vector format. + */ + virtual bool requiresAdvancedEffects() const = 0; + + /** + * Multiply opacity by \a opacityFactor. + * + * This method multiplies the opacity of the labeling elements (text, shadow, buffer etc.) + * by \a opacity effectively changing the opacity of the whole labeling elements. + */ + virtual void multiplyOpacity( double opacityFactor ); + + /** + * Tests whether the labels should be visible at the specified \a scale. + * The \a scale value indicates the scale denominator, e.g. 1000.0 for a 1:1000 map. + * \returns TRUE if the labels are visible at the given scale. + */ + virtual bool isInScaleRange( double scale ) const; + + // static stuff + + /** + * Tries to create an instance of an implementation based on the XML data. + */ + static QgsAbstractRasterLayerLabeling *createFromElement( const QDomElement &element, const QgsReadWriteContext &context ) SIP_FACTORY; + + /** + * Writes the SE 1.1 TextSymbolizer element based on the current layer labeling settings + */ + virtual void toSld( QDomNode &parent, const QVariantMap &props ) const; + + /** + * Accepts the specified symbology \a visitor, causing it to visit all symbols associated + * with the labeling. + * + * Returns TRUE if the visitor should continue visiting other objects, or FALSE if visiting + * should be canceled. + */ + virtual bool accept( QgsStyleEntityVisitorInterface *visitor ) const; + + private: + +#ifdef SIP_RUN + QgsAbstractRasterLayerLabeling( const QgsAbstractRasterLayerLabeling &rhs ); +#endif + +}; + + +/** + * \ingroup core + * \brief Basic implementation of the labeling interface for raster layers. + * + * \since QGIS 3.42 + */ +class CORE_EXPORT QgsRasterLayerSimpleLabeling : public QgsAbstractRasterLayerLabeling +{ + public: + + explicit QgsRasterLayerSimpleLabeling(); + ~QgsRasterLayerSimpleLabeling() override; + + QString type() const override; + QgsRasterLayerSimpleLabeling *clone() const override SIP_FACTORY; + std::unique_ptr< QgsRasterLayerLabelProvider > provider( QgsRasterLayer *layer ) const override SIP_SKIP; + QDomElement save( QDomDocument &doc, const QgsReadWriteContext &context ) const override; + bool accept( QgsStyleEntityVisitorInterface *visitor ) const override; + bool requiresAdvancedEffects() const override; + void multiplyOpacity( double opacityFactor ) override; + + //! Creates a QgsRasterLayerSimpleLabeling from a DOM element with saved configuration + static QgsRasterLayerSimpleLabeling *create( const QDomElement &element, const QgsReadWriteContext &context ) SIP_FACTORY; + + /** + * Returns the text format used for rendering the labels. + * + * \see setTextFormat() + */ + QgsTextFormat textFormat() const; + + /** + * Sets the text \a format used for rendering the labels. + * + * \see textFormat() + */ + void setTextFormat( const QgsTextFormat &format ); + + /** + * Returns the numeric format used for the labels. + * + * \see setNumericFormat() + */ + const QgsNumericFormat *numericFormat() const; + + /** + * Sets the numeric \a format used for the labels. + * + * Ownership of \a format is transferred to the labeling. + * + * \see numericFormat() + */ + void setNumericFormat( QgsNumericFormat *format SIP_TRANSFER ); + + /** + * Returns the raster band to use for label values. + * + * \see setBand() + */ + int band() const { return mBandNumber; } + + /** + * Sest the raster \a band to use for label values. + * + * \see band() + */ + void setBand( int band ) { mBandNumber = band; } + + /** + * Returns the priority of labels. + * + * This is a value between 0 to 1, where 0 = highest priority and 1 = lowest priority. + * + * The default is 0.5. + * + * \see setPriority() + */ + double priority() const { return mPriority; } + + /** + * Sets the \a priority of labels. + * + * This is a value between 0 to 1, where 0 = highest priority and 1 = lowest priority. + * + * \see priority() + */ + void setPriority( double priority ) { mPriority = priority; } + + /** + * Returns the label placement settings. + * \see setPlacementSettings() + * \note Not available in Python bindings + */ + const QgsLabelPlacementSettings &placementSettings() const { return mPlacementSettings; } SIP_SKIP + + /** + * Returns the label placement settings. + * \see setPlacementSettings() + */ + QgsLabelPlacementSettings &placementSettings() { return mPlacementSettings; } + + /** + * Sets the label placement \a settings. + * \see placementSettings() + */ + void setPlacementSettings( const QgsLabelPlacementSettings &settings ) { mPlacementSettings = settings; } + + /** + * Returns the label thinning settings. + * \see setThinningSettings() + * \note Not available in Python bindings + */ + const QgsLabelThinningSettings &thinningSettings() const { return mThinningSettings; } SIP_SKIP + + /** + * Returns the label thinning settings. + * \see setThinningSettings() + */ + QgsLabelThinningSettings &thinningSettings() { return mThinningSettings; } + + /** + * Sets the label thinning \a settings. + * \see thinningSettings() + */ + void setThinningSettings( const QgsLabelThinningSettings &settings ) { mThinningSettings = settings; } + + /** + * Returns the Z-Index of the labels. + * + * Labels with a higher z-index are rendered on top of labels with a lower z-index. + * + * \see setZIndex() + */ + double zIndex() const; + + /** + * Sets the Z-Index of the labels. + * + * Labels with a higher z-index are rendered on top of labels with a lower z-index. + * + * \see zIndex() + */ + void setZIndex( double index ); + + /** + * Returns the maximum map scale (i.e. most "zoomed in" scale) at which the labels will be visible. + * + * The scale value indicates the scale denominator, e.g. 1000.0 for a 1:1000 map. + * A scale of 0 indicates no maximum scale visibility. + * + * This setting is only considered if hasScaleBasedVisibility() is TRUE. + * + * \see setMaximumScale() + * \see minimumScale() + * \see hasScaleBasedVisibility() + */ + double maximumScale() const; + + /** + * Sets the maximum map \a scale (i.e. most "zoomed in" scale) at which the labels will be visible. + * + * The scale value indicates the scale denominator, e.g. 1000.0 for a 1:1000 map. + * A scale of 0 indicates no maximum scale visibility. + * + * This setting is only considered if hasScaleBasedVisibility() is TRUE. + * + * \see maximumScale() + * \see setMinimumScale() + * \see setScaleBasedVisibility() + */ + void setMaximumScale( double scale ); + + /** + * Returns the minimum map scale (i.e. most "zoomed out" scale) at which the labels will be visible. + * + * The scale value indicates the scale denominator, e.g. 1000.0 for a 1:1000 map. + * A scale of 0 indicates no minimum scale visibility. + * + * This setting is only considered if hasScaleBasedVisibility() is TRUE. + * + * \see setMinimumScale() + * \see maximumScale() + * \see hasScaleBasedVisibility() + */ + double minimumScale() const; + + /** + * Sets the minimum map \a scale (i.e. most "zoomed out" scale) at which the labels will be visible. + * + * The scale value indicates the scale denominator, e.g. 1000.0 for a 1:1000 map. + * A scale of 0 indicates no minimum scale visibility. + * + * This setting is only considered if hasScaleBasedVisibility() is TRUE. + * + * \see minimumScale() + * \see setMaximumScale() + * \see hasScaleBasedVisibility() + */ + void setMinimumScale( double scale ); + + /** + * Sets whether scale based visibility is enabled for the labels. + * \see setMinimumScale() + * \see setMaximumScale() + * \see hasScaleBasedVisibility() + */ + void setScaleBasedVisibility( bool enabled ); + + /** + * Returns whether scale based visibility is enabled for the labels. + + * \see minimumScale() + * \see maximumScale() + * \see setScaleBasedVisibility() + */ + bool hasScaleBasedVisibility() const; + + bool isInScaleRange( double scale ) const override; + + /** + * Returns the resampling method used when the raster labels are being + * resampled over neighboring pixels. + * + * \see setResampleMethod() + * \see resampleOver() + */ + Qgis::RasterResamplingMethod resampleMethod() const; + + /** + * Sets the resampling \a method to use when the raster labels are being + * resampled over neighboring pixels. + * + * \see resampleMethod() + * \see setResampleOver() + */ + void setResampleMethod( Qgis::RasterResamplingMethod method ); + + /** + * Returns the number of neighboring pixels to resample over, when labels are + * showing values resampled over neighboring pixels. + * + * \see setResampleOver() + * \see resampleMethod() + */ + int resampleOver() const; + + /** + * Sets the number of neighboring \a pixels to resample over, when labels are + * showing values resampled over neighboring pixels. + * + * \see resampleOver() + * \see setResampleMethod() + */ + void setResampleOver( int pixels ); + + private: + int mBandNumber = 1; + + QgsTextFormat mTextFormat; + + std::unique_ptr< QgsNumericFormat > mNumericFormat; + + //! Priority of labels. 0 = highest priority, 1 = lowest priority + double mPriority = 0.5; + + QgsLabelPlacementSettings mPlacementSettings; + QgsLabelThinningSettings mThinningSettings; + + double mZIndex = 0; + + bool mScaleVisibility = false; + double mMaximumScale = 0; + double mMinimumScale = 0; + + Qgis::RasterResamplingMethod mResampleMethod = Qgis::RasterResamplingMethod::Average; + int mResampleOver = 1; + +}; + + + +#endif // QGSRASTERLABELING_H diff --git a/src/core/raster/qgsrasterlayer.cpp b/src/core/raster/qgsrasterlayer.cpp index cfbbb1d12187..ed2f42383cb7 100644 --- a/src/core/raster/qgsrasterlayer.cpp +++ b/src/core/raster/qgsrasterlayer.cpp @@ -61,6 +61,7 @@ email : tim at linfiniti.com #include "qgsthreadingutils.h" #include "qgssettingsentryimpl.h" #include "qgssettingstree.h" +#include "qgsrasterlabeling.h" #include #include @@ -156,6 +157,8 @@ QgsRasterLayer::~QgsRasterLayer() { emit willBeDeleted(); + mLabeling.reset(); + setValid( false ); // Note: provider and other interfaces are owned and deleted by pipe } @@ -190,6 +193,10 @@ QgsRasterLayer *QgsRasterLayer::clone() const layer->dataProvider()->setZoomedOutResamplingMethod( mDataProvider->zoomedOutResamplingMethod() ); } + layer->setLabelsEnabled( mLabelsEnabled ); + if ( mLabeling ) + layer->setLabeling( mLabeling->clone() ); + return layer.release(); } @@ -957,18 +964,18 @@ void QgsRasterLayer::setDataProvider( QString const &provider, const QgsDataProv if ( resampling == QLatin1String( "bilinear" ) ) { resampleFilter->setZoomedInResampler( new QgsBilinearRasterResampler() ); - mDataProvider->setZoomedInResamplingMethod( QgsRasterDataProvider::ResamplingMethod::Bilinear ); + mDataProvider->setZoomedInResamplingMethod( Qgis::RasterResamplingMethod::Bilinear ); } else if ( resampling == QLatin1String( "cubic" ) ) { resampleFilter->setZoomedInResampler( new QgsCubicRasterResampler() ); - mDataProvider->setZoomedInResamplingMethod( QgsRasterDataProvider::ResamplingMethod::Cubic ); + mDataProvider->setZoomedInResamplingMethod( Qgis::RasterResamplingMethod::Cubic ); } resampling = settings.value( QStringLiteral( "/Raster/defaultZoomedOutResampling" ), QStringLiteral( "nearest neighbour" ) ).toString(); if ( resampling == QLatin1String( "bilinear" ) ) { resampleFilter->setZoomedOutResampler( new QgsBilinearRasterResampler() ); - mDataProvider->setZoomedOutResamplingMethod( QgsRasterDataProvider::ResamplingMethod::Bilinear ); + mDataProvider->setZoomedOutResamplingMethod( Qgis::RasterResamplingMethod::Bilinear ); } const double maxOversampling = QgsRasterLayer::settingsRasterDefaultOversampling->value(); @@ -1727,6 +1734,44 @@ bool QgsRasterLayer::accept( QgsStyleEntityVisitorInterface *visitor ) const return true; } +bool QgsRasterLayer::labelsEnabled() const +{ + QGIS_PROTECT_QOBJECT_THREAD_ACCESS + + return mLabelsEnabled && static_cast< bool >( mLabeling ); +} + +void QgsRasterLayer::setLabelsEnabled( bool enabled ) +{ + QGIS_PROTECT_QOBJECT_THREAD_ACCESS + + mLabelsEnabled = enabled; +} + +const QgsAbstractRasterLayerLabeling *QgsRasterLayer::labeling() const +{ + QGIS_PROTECT_QOBJECT_THREAD_ACCESS + + return mLabeling.get(); +} + +QgsAbstractRasterLayerLabeling *QgsRasterLayer::labeling() +{ + QGIS_PROTECT_QOBJECT_THREAD_ACCESS + + return mLabeling.get(); +} + +void QgsRasterLayer::setLabeling( QgsAbstractRasterLayerLabeling *labeling ) +{ + QGIS_PROTECT_QOBJECT_THREAD_ACCESS + + if ( mLabeling.get() == labeling ) + return; + + mLabeling.reset( labeling ); + triggerRepaint(); +} bool QgsRasterLayer::writeSld( QDomNode &node, QDomDocument &doc, QString &errorMessage, const QVariantMap &props ) const { @@ -2195,6 +2240,20 @@ bool QgsRasterLayer::readSymbology( const QDomNode &layer_node, QString &errorMe mPipe->dataDefinedProperties().readXml( elemDataDefinedProperties, QgsRasterPipe::propertyDefinitions() ); } + // read labeling definition + if ( categories.testFlag( Labeling ) ) + { + QgsReadWriteContextCategoryPopper p = context.enterCategory( tr( "Labeling" ) ); + + QDomElement labelingElement = layer_node.firstChildElement( QStringLiteral( "labeling" ) ); + if ( !labelingElement.isNull() ) + { + QgsAbstractRasterLayerLabeling *labeling = QgsAbstractRasterLayerLabeling::createFromElement( labelingElement, context ); + mLabelsEnabled = layer_node.toElement().attribute( QStringLiteral( "labelsEnabled" ), QStringLiteral( "0" ) ).toInt(); + setLabeling( labeling ); + } + } + if ( categories.testFlag( MapTips ) ) { QDomElement mapTipElem = layer_node.namedItem( QStringLiteral( "mapTip" ) ).toElement(); @@ -2402,6 +2461,17 @@ bool QgsRasterLayer::writeSymbology( QDomNode &layer_node, QDomDocument &documen writeCommonStyle( layerElement, document, context, categories ); + if ( categories.testFlag( Labeling ) ) + { + if ( mLabeling ) + { + QDomElement labelingElement = mLabeling->save( document, context ); + layerElement.appendChild( labelingElement ); + } + if ( mLabelsEnabled ) + layerElement.setAttribute( QStringLiteral( "labelsEnabled" ), QStringLiteral( "1" ) ); + } + // save map tip if ( categories.testFlag( MapTips ) ) { diff --git a/src/core/raster/qgsrasterlayer.h b/src/core/raster/qgsrasterlayer.h index 30ff754ef3dd..873dbb12b197 100644 --- a/src/core/raster/qgsrasterlayer.h +++ b/src/core/raster/qgsrasterlayer.h @@ -50,6 +50,7 @@ class QgsRasterLayerElevationProperties; class QgsSettingsEntryBool; class QgsSettingsEntryDouble; class QgsRasterMinMaxOrigin; +class QgsAbstractRasterLayerLabeling; class QImage; class QPixmap; @@ -444,6 +445,60 @@ class CORE_EXPORT QgsRasterLayer : public QgsMapLayer, public QgsAbstractProfile QDateTime timestamp() const override; bool accept( QgsStyleEntityVisitorInterface *visitor ) const override; + /** + * Returns whether the layer contains labels which are enabled and should be drawn. + * \returns TRUE if layer contains enabled labels + * + * \see setLabelsEnabled() + * \see labeling() + * \since QGIS 3.42 + */ + bool labelsEnabled() const; + + /** + * Sets whether labels should be \a enabled for the layer. + * + * \note Labels will only be rendered if labelsEnabled() is TRUE and a labeling + * object is returned by labeling(). + * + * \see labelsEnabled() + * \see labeling() + * \since QGIS 3.42 + */ + void setLabelsEnabled( bool enabled ); + + /** + * Access to const labeling configuration. May be NULLPTR if labeling is not used. + * \note Labels will only be rendered if labelsEnabled() returns TRUE. + * + * \see labelsEnabled() + * \see setLabelsEnabled() + * \see setLabeling() + * + * \since QGIS 3.42 + */ + const QgsAbstractRasterLayerLabeling *labeling() const SIP_SKIP; + + /** + * Access to labeling configuration. May be NULLPTR if labeling is not used. + * \note Labels will only be rendered if labelsEnabled() returns TRUE. + * + * \see labelsEnabled() + * \see setLabeling() + * + * \since QGIS 3.42 + */ + QgsAbstractRasterLayerLabeling *labeling(); + + /** + * Sets labeling configuration. Takes ownership of the object. + * + * \see labeling() + * + * \since QGIS 3.42 + */ + void setLabeling( QgsAbstractRasterLayerLabeling *labeling SIP_TRANSFER ); + /** * Writes the symbology of the layer into the document provided in SLD 1.0.0 format * \param node the node that will have the style element added to it. @@ -574,6 +629,12 @@ class CORE_EXPORT QgsRasterLayer : public QgsMapLayer, public QgsAbstractProfile QgsRasterLayerElevationProperties *mElevationProperties = nullptr; + //! True if labels are enabled + bool mLabelsEnabled = false; + + //! Labeling configuration + std::unique_ptr< QgsAbstractRasterLayerLabeling > mLabeling; + //! [ data provider interface ] Timestamp, the last modified time of the data source when the layer was created QDateTime mLastModified; diff --git a/src/core/raster/qgsrasterlayerrenderer.cpp b/src/core/raster/qgsrasterlayerrenderer.cpp index 8edccc402672..46ec85f52d8e 100644 --- a/src/core/raster/qgsrasterlayerrenderer.cpp +++ b/src/core/raster/qgsrasterlayerrenderer.cpp @@ -40,6 +40,7 @@ #include "qgsunittypes.h" #include "qgsrasternuller.h" #include "qgsrenderedlayerstatistics.h" +#include "qgsrasterlabeling.h" #include #include @@ -448,6 +449,8 @@ QgsRasterLayerRenderer::QgsRasterLayerRenderer( QgsRasterLayer *layer, QgsRender } } + prepareLabeling( layer ); + mFeedback->setRenderContext( rendererContext ); mPipe->moveToThread( nullptr ); @@ -551,6 +554,18 @@ bool QgsRasterLayerRenderer::render() if ( mDrawElevationMap ) drawElevationMap(); + renderingProfile.reset(); + + if ( mLabelProvider && !renderContext()->renderingStopped() ) + { + std::unique_ptr< QgsScopedRuntimeProfile > labelingProfile; + if ( mEnableProfile ) + { + labelingProfile = std::make_unique< QgsScopedRuntimeProfile >( QObject::tr( "Labeling" ), QStringLiteral( "rendering" ) ); + } + drawLabeling(); + } + if ( restoreOldResamplingStage ) { mPipe->setResamplingStage( oldResamplingState ); @@ -594,6 +609,35 @@ bool QgsRasterLayerRenderer::forceRasterRender() const return false; } +void QgsRasterLayerRenderer::prepareLabeling( QgsRasterLayer *layer ) +{ + QgsRenderContext &context = *renderContext(); + + if ( QgsLabelingEngine *engine2 = context.labelingEngine() ) + { + if ( QgsAbstractRasterLayerLabeling *labeling = layer->labeling() ) + { + if ( layer->labelsEnabled() && labeling->isInScaleRange( context.rendererScale() ) ) + { + std::unique_ptr< QgsRasterLayerLabelProvider > provider = labeling->provider( layer ); + if ( provider ) + { + // engine takes ownership + mLabelProvider = provider.release(); + mLabelProvider->startRender( context ); + engine2->addProvider( mLabelProvider ); + } + } + } + } +} + +void QgsRasterLayerRenderer::drawLabeling() +{ + if ( mLabelProvider ) + mLabelProvider->generateLabels( *renderContext(), mPipe.get(), mRasterViewPort, mFeedback ); +} + void QgsRasterLayerRenderer::drawElevationMap() { QgsRasterDataProvider *dataProvider = mPipe->provider(); diff --git a/src/core/raster/qgsrasterlayerrenderer.h b/src/core/raster/qgsrasterlayerrenderer.h index af9e9e42f3d2..ac6460098a5a 100644 --- a/src/core/raster/qgsrasterlayerrenderer.h +++ b/src/core/raster/qgsrasterlayerrenderer.h @@ -30,6 +30,7 @@ struct QgsRasterViewPort; class QgsRenderContext; class QgsRasterLayerRenderer; +class QgsRasterLayerLabelProvider; #include "qgsrasterinterface.h" @@ -76,6 +77,9 @@ class CORE_EXPORT QgsRasterLayerRenderer : public QgsMapLayerRenderer private: + void prepareLabeling( QgsRasterLayer *layer ); + void drawLabeling(); + QString mLayerName; QgsRasterViewPort *mRasterViewPort = nullptr; @@ -98,6 +102,9 @@ class CORE_EXPORT QgsRasterLayerRenderer : public QgsMapLayerRenderer bool mEnableProfile = false; quint64 mPreparationTime = 0; + // may be NULLPTR. no need to delete: if exists it is owned by labeling engine + QgsRasterLayerLabelProvider *mLabelProvider = nullptr; + void drawElevationMap(); friend class QgsRasterLayerRendererFeedback; diff --git a/src/core/raster/qgsrasterviewport.h b/src/core/raster/qgsrasterviewport.h index 100e763b0200..0bca727b4af0 100644 --- a/src/core/raster/qgsrasterviewport.h +++ b/src/core/raster/qgsrasterviewport.h @@ -57,13 +57,13 @@ struct CORE_EXPORT QgsRasterViewPort //! \brief Height, number of rows to be rendered qgssize mHeight; - //! \brief Intersection of current map extent and layer extent + //! \brief Intersection of current map extent and layer extent, in map (destination) CRS QgsRectangle mDrawnExtent; - //! \brief Source coordinate system + //! \brief Source (layer) coordinate system QgsCoordinateReferenceSystem mSrcCRS; - //! \brief Target coordinate system + //! \brief Target (map) coordinate system QgsCoordinateReferenceSystem mDestCRS; /** diff --git a/src/core/textrenderer/qgstextrenderer.cpp b/src/core/textrenderer/qgstextrenderer.cpp index 4b3612ec667d..f3b9a11f261b 100644 --- a/src/core/textrenderer/qgstextrenderer.cpp +++ b/src/core/textrenderer/qgstextrenderer.cpp @@ -130,25 +130,28 @@ void QgsTextRenderer::drawText( QPointF point, double rotation, Qgis::TextHorizo drawDocument( point, lFormat, metrics.document(), metrics, context, alignment, rotation ); } -void QgsTextRenderer::drawDocument(QPointF point, const QgsTextFormat &format, const QgsTextDocument &document, const QgsTextDocumentMetrics &metrics, QgsRenderContext &context, Qgis::TextHorizontalAlignment alignment, double rotation , Qgis::TextLayoutMode mode) +void QgsTextRenderer::drawDocument( QPointF point, const QgsTextFormat &_format, const QgsTextDocument &document, const QgsTextDocumentMetrics &metrics, QgsRenderContext &context, Qgis::TextHorizontalAlignment alignment, double rotation, Qgis::TextLayoutMode mode ) { + const QgsTextFormat lFormat = updateShadowPosition( _format ); + // DO NOT USE _format in the following code, always use lFormat!! + Qgis::TextComponents components = Qgis::TextComponent::Text; - if ( format.background().enabled() ) + if ( lFormat.background().enabled() ) { components |= Qgis::TextComponent::Background; } - if ( format.shadow().enabled() ) + if ( lFormat.shadow().enabled() ) { components |= Qgis::TextComponent::Shadow; } - if ( format.buffer().enabled() ) + if ( lFormat.buffer().enabled() ) { components |= Qgis::TextComponent::Buffer; } - drawParts( point, rotation, alignment, document, metrics, context, format, components, mode ); + drawParts( point, rotation, alignment, document, metrics, context, lFormat, components, mode ); } void QgsTextRenderer::drawTextOnLine( const QPolygonF &line, const QString &text, QgsRenderContext &context, const QgsTextFormat &_format, double offsetAlongLine, double offsetFromLine ) diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index 3f72095f728f..e3a1cc9e40eb 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -18,6 +18,8 @@ set(QGIS_GUI_SRCS raster/qgsrasterbandcombobox.cpp raster/qgsrastercontourrendererwidget.cpp raster/qgsrasterhistogramwidget.cpp + raster/qgsrasterlabelingwidget.cpp + raster/qgsrasterlabelsettingswidget.cpp raster/qgsrasterlayerproperties.cpp raster/qgsrasterlayertemporalpropertieswidget.cpp raster/qgsrasterminmaxwidget.cpp @@ -1495,6 +1497,8 @@ set(QGIS_GUI_HDRS raster/qgsrasterbandcombobox.h raster/qgsrastercontourrendererwidget.h raster/qgsrasterhistogramwidget.h + raster/qgsrasterlabelingwidget.h + raster/qgsrasterlabelsettingswidget.h raster/qgsrasterminmaxwidget.h raster/qgsrasterrendererwidget.h raster/qgsrastertransparencywidget.h diff --git a/src/gui/labeling/qgslabelinggui.cpp b/src/gui/labeling/qgslabelinggui.cpp index b88f86b89453..034ceea9f032 100644 --- a/src/gui/labeling/qgslabelinggui.cpp +++ b/src/gui/labeling/qgslabelinggui.cpp @@ -32,6 +32,8 @@ #include "qgslabellineanchorwidget.h" #include "qgsprojectstylesettings.h" #include "qgsgui.h" +#include "qgsmeshlayer.h" +#include "qgsvectortilelayer.h" #include #include @@ -41,9 +43,9 @@ QgsExpressionContext QgsLabelingGui::createExpressionContext() const { QgsExpressionContext expContext; - if ( mCanvas ) + if ( mMapCanvas ) { - expContext = mCanvas->createExpressionContext(); + expContext = mMapCanvas->createExpressionContext(); } else { @@ -229,16 +231,66 @@ void QgsLabelingGui::showLineAnchorSettings() } } -QgsLabelingGui::QgsLabelingGui( QgsMapLayer *layer, QgsMapCanvas *mapCanvas, const QgsPalLayerSettings &layerSettings, QWidget *parent, Qgis::GeometryType geomType ) +QgsLabelingGui::QgsLabelingGui( QgsVectorLayer *layer, QgsMapCanvas *mapCanvas, const QgsPalLayerSettings &layerSettings, QWidget *parent, Qgis::GeometryType geomType ) : QgsTextFormatWidget( mapCanvas, parent, QgsTextFormatWidget::Labeling, layer ) + , mMode( NoLabels ) , mSettings( layerSettings ) +{ + mGeomType = geomType; + + init(); + + setLayer( layer ); +} + +QgsLabelingGui::QgsLabelingGui( QgsMeshLayer *layer, QgsMapCanvas *mapCanvas, const QgsPalLayerSettings &settings, QWidget *parent, Qgis::GeometryType geomType ) + : QgsTextFormatWidget( mapCanvas, parent, QgsTextFormatWidget::Labeling, layer ) , mMode( NoLabels ) - , mCanvas( mapCanvas ) + , mSettings( settings ) { - QgsGui::initCalloutWidgets(); + mGeomType = geomType; + + init(); + + setLayer( layer ); +} +QgsLabelingGui::QgsLabelingGui( QgsVectorTileLayer *layer, QgsMapCanvas *mapCanvas, const QgsPalLayerSettings &settings, QWidget *parent, Qgis::GeometryType geomType ) + : QgsTextFormatWidget( mapCanvas, parent, QgsTextFormatWidget::Labeling, layer ) + , mMode( NoLabels ) + , mSettings( settings ) +{ mGeomType = geomType; + init(); + + setLayer( layer ); +} + + +QgsLabelingGui::QgsLabelingGui( QgsMapCanvas *mapCanvas, QWidget *parent, QgsMapLayer *layer ) + : QgsTextFormatWidget( mapCanvas, parent, QgsTextFormatWidget::Labeling, layer ) + , mMode( NoLabels ) +{ +} + + +QgsLabelingGui::QgsLabelingGui( QgsMapCanvas *mapCanvas, const QgsPalLayerSettings &settings, QWidget *parent ) + : QgsTextFormatWidget( mapCanvas, parent, QgsTextFormatWidget::Labeling, nullptr ) + , mMode( NoLabels ) + , mSettings( settings ) +{ + init(); + + setLayer( nullptr ); +} + +void QgsLabelingGui::init() +{ + QgsGui::initCalloutWidgets(); + + mStackedWidgetLabelWith->setSizeMode( QgsStackedWidget::SizeMode::CurrentPageOnly ); + mFontMultiLineAlignComboBox->addItem( tr( "Left" ), static_cast( Qgis::LabelMultiLineAlignment::Left ) ); mFontMultiLineAlignComboBox->addItem( tr( "Center" ), static_cast( Qgis::LabelMultiLineAlignment::Center ) ); mFontMultiLineAlignComboBox->addItem( tr( "Right" ), static_cast( Qgis::LabelMultiLineAlignment::Right ) ); @@ -275,9 +327,9 @@ QgsLabelingGui::QgsLabelingGui( QgsMapLayer *layer, QgsMapCanvas *mapCanvas, con mFieldExpressionWidget->registerExpressionContextGenerator( this ); - mMinScaleWidget->setMapCanvas( mCanvas ); + mMinScaleWidget->setMapCanvas( mMapCanvas ); mMinScaleWidget->setShowCurrentScaleButton( true ); - mMaxScaleWidget->setMapCanvas( mCanvas ); + mMaxScaleWidget->setMapCanvas( mMapCanvas ); mMaxScaleWidget->setShowCurrentScaleButton( true ); mGeometryGeneratorExpressionButton->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum ); @@ -299,8 +351,6 @@ QgsLabelingGui::QgsLabelingGui( QgsMapLayer *layer, QgsMapCanvas *mapCanvas, con connect( mCalloutStyleComboBox, static_cast( &QComboBox::currentIndexChanged ), this, &QgsLabelingGui::calloutTypeChanged ); mLblNoObstacle1->installEventFilter( this ); - - setLayer( layer ); } void QgsLabelingGui::setLayer( QgsMapLayer *mapLayer ) diff --git a/src/gui/labeling/qgslabelinggui.h b/src/gui/labeling/qgslabelinggui.h index 99c17d13ce7d..6af4261b10a5 100644 --- a/src/gui/labeling/qgslabelinggui.h +++ b/src/gui/labeling/qgslabelinggui.h @@ -25,6 +25,9 @@ class QDialogButtonBox; +class QgsMeshLayer; +class QgsVectorTileLayer; + #define SIP_NO_FILE ///@cond PRIVATE @@ -34,7 +37,25 @@ class GUI_EXPORT QgsLabelingGui : public QgsTextFormatWidget Q_OBJECT public: - QgsLabelingGui( QgsMapLayer *layer, QgsMapCanvas *mapCanvas, const QgsPalLayerSettings &settings, QWidget *parent = nullptr, Qgis::GeometryType geomType = Qgis::GeometryType::Unknown ); + /** + * Constructor for QgsLabelingGui, for configuring a vector \a layer labeling. + */ + QgsLabelingGui( QgsVectorLayer *layer, QgsMapCanvas *mapCanvas, const QgsPalLayerSettings &settings, QWidget *parent = nullptr, Qgis::GeometryType geomType = Qgis::GeometryType::Unknown ); + + /** + * Constructor for QgsLabelingGui, for configuring a mesh \a layer labeling. + */ + QgsLabelingGui( QgsMeshLayer *layer, QgsMapCanvas *mapCanvas, const QgsPalLayerSettings &settings, QWidget *parent = nullptr, Qgis::GeometryType geomType = Qgis::GeometryType::Unknown ); + + /** + * Constructor for QgsLabelingGui, for configuring a vector tile \a layer labeling. + */ + QgsLabelingGui( QgsVectorTileLayer *layer, QgsMapCanvas *mapCanvas, const QgsPalLayerSettings &settings, QWidget *parent = nullptr, Qgis::GeometryType geomType = Qgis::GeometryType::Unknown ); + + /** + * Generic constructor for QgsLabelingGui, when no layer is available. + */ + QgsLabelingGui( QgsMapCanvas *mapCanvas, const QgsPalLayerSettings &settings, QWidget *parent = nullptr ); QgsPalLayerSettings layerSettings(); @@ -47,7 +68,7 @@ class GUI_EXPORT QgsLabelingGui : public QgsTextFormatWidget void setLabelMode( LabelMode mode ); - void setLayer( QgsMapLayer *layer ); + virtual void setLayer( QgsMapLayer *layer ); void setSettings( const QgsPalLayerSettings &settings ); @@ -62,10 +83,34 @@ class GUI_EXPORT QgsLabelingGui : public QgsTextFormatWidget void saveFormat() override; protected: + /** + * Constructor for QgsLabelingGui, for subclasses. + * + * \warning The subclass constructor must call the init() and setLayer() methods. + * + * \param mapCanvas associated map canvas + * \param parent parent widget + * \param layer associated layer + * + * \note Not available in Python bindings + * \since QGIS 3.42 + */ + QgsLabelingGui( QgsMapCanvas *mapCanvas, QWidget *parent, QgsMapLayer *layer ) SIP_SKIP; + + /** + * Initializes the widget. + * + * \since QGIS 3.42 + */ + void init(); + void blockInitSignals( bool block ); void syncDefinedCheckboxFrame( QgsPropertyOverrideButton *ddBtn, QCheckBox *chkBx, QFrame *f ); bool eventFilter( QObject *object, QEvent *event ) override; + //! Dialog mode + LabelMode mMode; + private slots: /** @@ -85,9 +130,7 @@ class GUI_EXPORT QgsLabelingGui : public QgsTextFormatWidget private: QgsPalLayerSettings mSettings; - LabelMode mMode; QgsFeature mPreviewFeature; - QgsMapCanvas *mCanvas = nullptr; QgsLabelObstacleSettings mObstacleSettings; QgsLabelLineSettings mLineSettings; diff --git a/src/gui/labeling/qgslabelingwidget.cpp b/src/gui/labeling/qgslabelingwidget.cpp index 55468267d2dd..805e822a0209 100644 --- a/src/gui/labeling/qgslabelingwidget.cpp +++ b/src/gui/labeling/qgslabelingwidget.cpp @@ -44,14 +44,15 @@ QgsLabelingWidget::QgsLabelingWidget( QgsVectorLayer *layer, QgsMapCanvas *canva mLabelModeComboBox->addItem( QgsApplication::getThemeIcon( QStringLiteral( "labelingRuleBased.svg" ) ), tr( "Rule-based Labeling" ), ModeRuleBased ); mLabelModeComboBox->addItem( QgsApplication::getThemeIcon( QStringLiteral( "labelingObstacle.svg" ) ), tr( "Blocking" ), ModeBlocking ); - connect( mLabelRulesButton, &QAbstractButton::clicked, this, &QgsLabelingWidget::showLabelingEngineRules ); - connect( mEngineSettingsButton, &QAbstractButton::clicked, this, &QgsLabelingWidget::showEngineConfigDialog ); + connect( mLabelRulesButton, &QAbstractButton::clicked, this, &QgsLabelingWidget::showLabelingEngineRulesPrivate ); + connect( mEngineSettingsButton, &QAbstractButton::clicked, this, &QgsLabelingWidget::showEngineConfigDialogPrivate ); connect( mLabelModeComboBox, static_cast( &QComboBox::currentIndexChanged ), this, &QgsLabelingWidget::labelModeChanged ); setLayer( layer ); const int iconSize16 = QgsGuiUtils::scaleIconSize( 16 ); mEngineSettingsButton->setIconSize( QSize( iconSize16, iconSize16 ) ); + mLabelRulesButton->setIconSize( QSize( iconSize16, iconSize16 ) ); } QgsLabelingGui *QgsLabelingWidget::labelingGui() @@ -285,9 +286,10 @@ void QgsLabelingWidget::labelModeChanged( int index ) emit widgetChanged(); } -void QgsLabelingWidget::showLabelingEngineRules() + +void QgsLabelingWidget::showLabelingEngineRules( QWidget *parent, QgsMapCanvas *canvas ) { - QgsPanelWidget *panel = QgsPanelWidget::findParentPanel( this ); + QgsPanelWidget *panel = QgsPanelWidget::findParentPanel( parent ); const QgsLabelingEngineSettings &settings = QgsProject::instance()->labelingEngineSettings(); QList rules; for ( const QgsAbstractLabelingEngineRule *rule : settings.rules() ) @@ -299,19 +301,19 @@ void QgsLabelingWidget::showLabelingEngineRules() { QgsLabelingEngineRulesWidget *widget = new QgsLabelingEngineRulesWidget(); widget->setRules( rules ); - connect( widget, &QgsLabelingEngineRulesWidget::changed, widget, [widget, this] { + connect( widget, &QgsLabelingEngineRulesWidget::changed, widget, [widget, canvas] { QgsLabelingEngineSettings settings = QgsProject::instance()->labelingEngineSettings(); settings.setRules( widget->rules() ); QgsProject::instance()->setLabelingEngineSettings( settings ); QgsProject::instance()->setDirty(); - if ( mCanvas ) - mCanvas->refreshAllLayers(); + if ( canvas ) + canvas->refreshAllLayers(); } ); panel->openPanel( widget ); } else { - QgsLabelingEngineRulesDialog dialog( this ); + QgsLabelingEngineRulesDialog dialog( parent ); dialog.setRules( rules ); if ( dialog.exec() ) { @@ -319,27 +321,37 @@ void QgsLabelingWidget::showLabelingEngineRules() settings.setRules( dialog.rules() ); QgsProject::instance()->setLabelingEngineSettings( settings ); QgsProject::instance()->setDirty(); - if ( mCanvas ) - mCanvas->refreshAllLayers(); + if ( canvas ) + canvas->refreshAllLayers(); } - activateWindow(); + parent->activateWindow(); } } -void QgsLabelingWidget::showEngineConfigDialog() +void QgsLabelingWidget::showLabelingEngineRulesPrivate() +{ + showLabelingEngineRules( this, mCanvas ); +} + +void QgsLabelingWidget::showEngineConfiguration( QWidget *parent, QgsMapCanvas *canvas ) { - QgsPanelWidget *panel = QgsPanelWidget::findParentPanel( this ); + QgsPanelWidget *panel = QgsPanelWidget::findParentPanel( parent ); if ( panel && panel->dockMode() ) { - QgsLabelEngineConfigWidget *widget = new QgsLabelEngineConfigWidget( mCanvas ); + QgsLabelEngineConfigWidget *widget = new QgsLabelEngineConfigWidget( canvas ); connect( widget, &QgsLabelEngineConfigWidget::widgetChanged, widget, &QgsLabelEngineConfigWidget::apply ); panel->openPanel( widget ); } else { - QgsLabelEngineConfigDialog dialog( mCanvas, this ); + QgsLabelEngineConfigDialog dialog( canvas, parent ); dialog.exec(); // reactivate button's window - activateWindow(); + parent->activateWindow(); } } + +void QgsLabelingWidget::showEngineConfigDialogPrivate() +{ + showEngineConfiguration( this, mCanvas ); +} diff --git a/src/gui/labeling/qgslabelingwidget.h b/src/gui/labeling/qgslabelingwidget.h index 9b6210326a35..98a39f6ea1c4 100644 --- a/src/gui/labeling/qgslabelingwidget.h +++ b/src/gui/labeling/qgslabelingwidget.h @@ -54,6 +54,24 @@ class GUI_EXPORT QgsLabelingWidget : public QgsMapLayerConfigWidget, private Ui: */ QgsLabelingGui *labelingGui(); + /** + * Shows the labeling engine rules. + * + * The rules widget will either be shown as a dialog or an inline panel, depending on the \a parent widget. + * + * \since QGIS 3.42 + */ + static void showLabelingEngineRules( QWidget *parent, QgsMapCanvas *canvas ); + + /** + * Shows the labeling engine configuration. + * + * The config widget will either be shown as a dialog or an inline panel, depending on the \a parent widget. + * + * \since QGIS 3.42 + */ + static void showEngineConfiguration( QWidget *parent, QgsMapCanvas *canvas ); + public slots: //! Sets the layer to configure void setLayer( QgsMapLayer *layer ); @@ -75,8 +93,8 @@ class GUI_EXPORT QgsLabelingWidget : public QgsMapLayerConfigWidget, private Ui: private slots: void labelModeChanged( int index ); - void showLabelingEngineRules(); - void showEngineConfigDialog(); + void showLabelingEngineRulesPrivate(); + void showEngineConfigDialogPrivate(); private: enum Mode diff --git a/src/gui/labeling/qgsrulebasedlabelingwidget.cpp b/src/gui/labeling/qgsrulebasedlabelingwidget.cpp index da28068853b8..f9c37695cdee 100644 --- a/src/gui/labeling/qgsrulebasedlabelingwidget.cpp +++ b/src/gui/labeling/qgsrulebasedlabelingwidget.cpp @@ -690,7 +690,7 @@ QgsLabelingRulePropsWidget::QgsLabelingRulePropsWidget( QgsRuleBasedLabeling::Ru mSettings = new QgsPalLayerSettings; } - mLabelingGui = new QgsLabelingGui( nullptr, mMapCanvas, *mSettings, this ); + mLabelingGui = new QgsLabelingGui( mMapCanvas, *mSettings, this ); mLabelingGui->layout()->setContentsMargins( 0, 0, 0, 0 ); QVBoxLayout *l = new QVBoxLayout; l->addWidget( mLabelingGui ); diff --git a/src/gui/qgstextformatwidget.cpp b/src/gui/qgstextformatwidget.cpp index f02f91367649..10016acf4500 100644 --- a/src/gui/qgstextformatwidget.cpp +++ b/src/gui/qgstextformatwidget.cpp @@ -79,6 +79,99 @@ void QgsTextFormatWidget::initWidget() mGeometryGeneratorGroupBox->setCollapsed( true ); + mTextItem = new QListWidgetItem( tr( "Text" ), mLabelingOptionsListWidget ); + mTextItem->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "propertyicons/labeltext.svg" ) ) ); + mTextItem->setToolTip( tr( "Text style" ) ); + + mFormattingItem = new QListWidgetItem( tr( "Formatting" ), mLabelingOptionsListWidget ); + mFormattingItem->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "propertyicons/labelformatting.svg" ) ) ); + mFormattingItem->setToolTip( tr( "Formatting" ) ); + + mBufferItem = new QListWidgetItem( tr( "Buffer" ), mLabelingOptionsListWidget ); + mBufferItem->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "propertyicons/labelbuffer.svg" ) ) ); + mBufferItem->setToolTip( tr( "Buffer" ) ); + + mMaskItem = new QListWidgetItem( tr( "Mask" ), mLabelingOptionsListWidget ); + mMaskItem->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "propertyicons/labelmask.svg" ) ) ); + mMaskItem->setToolTip( tr( "Mask" ) ); + + mBackgroundItem = new QListWidgetItem( tr( "Background" ), mLabelingOptionsListWidget ); + mBackgroundItem->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "propertyicons/labelbackground.svg" ) ) ); + mBackgroundItem->setToolTip( tr( "Background" ) ); + + mShadowItem = new QListWidgetItem( tr( "Shadow" ), mLabelingOptionsListWidget ); + mShadowItem->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "propertyicons/labelshadow.svg" ) ) ); + mShadowItem->setToolTip( tr( "Shadow" ) ); + + mCalloutItem = new QListWidgetItem( tr( "Callouts" ), mLabelingOptionsListWidget ); + mCalloutItem->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "propertyicons/labelcallout.svg" ) ) ); + mCalloutItem->setToolTip( tr( "Callouts" ) ); + + mPlacementItem = new QListWidgetItem( tr( "Placement" ), mLabelingOptionsListWidget ); + mPlacementItem->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "propertyicons/labelplacement.svg" ) ) ); + mPlacementItem->setToolTip( tr( "Placement" ) ); + + mRenderingItem = new QListWidgetItem( tr( "Rendering" ), mLabelingOptionsListWidget ); + mRenderingItem->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "propertyicons/render.svg" ) ) ); + mRenderingItem->setToolTip( tr( "Rendering" ) ); + + mLabelingOptionsListWidget->addItem( mTextItem ); + mLabelingOptionsListWidget->addItem( mFormattingItem ); + mLabelingOptionsListWidget->addItem( mBufferItem ); + mLabelingOptionsListWidget->addItem( mMaskItem ); + mLabelingOptionsListWidget->addItem( mBackgroundItem ); + mLabelingOptionsListWidget->addItem( mShadowItem ); + mLabelingOptionsListWidget->addItem( mCalloutItem ); + mLabelingOptionsListWidget->addItem( mPlacementItem ); + mLabelingOptionsListWidget->addItem( mRenderingItem ); + + QObject::connect( mOptionsTab, &QTabWidget::currentChanged, this, [this]( int index ) { + if ( index == mOptionsTab->indexOf( textTab ) ) + mLabelStackedWidget->setCurrentWidget( mLabelPage_Text ); + else if ( index == mOptionsTab->indexOf( formattingTab ) ) + mLabelStackedWidget->setCurrentWidget( mLabelPage_Formatting ); + else if ( index == mOptionsTab->indexOf( maskTab ) ) + mLabelStackedWidget->setCurrentWidget( mLabelPage_Mask ); + else if ( index == mOptionsTab->indexOf( bufferTab ) ) + mLabelStackedWidget->setCurrentWidget( mLabelPage_Buffer ); + else if ( index == mOptionsTab->indexOf( backgroundTab ) ) + mLabelStackedWidget->setCurrentWidget( mLabelPage_Background ); + else if ( index == mOptionsTab->indexOf( shadowTab ) ) + mLabelStackedWidget->setCurrentWidget( mLabelPage_Shadow ); + else if ( index == mOptionsTab->indexOf( calloutsTab ) ) + mLabelStackedWidget->setCurrentWidget( mLabelPage_Callouts ); + else if ( index == mOptionsTab->indexOf( placementTab ) ) + mLabelStackedWidget->setCurrentWidget( mLabelPage_Placement ); + else if ( index == mOptionsTab->indexOf( renderingTab ) ) + mLabelStackedWidget->setCurrentWidget( mLabelPage_Rendering ); + } ); + + QObject::connect( mLabelingOptionsListWidget, &QListWidget::currentRowChanged, this, [this]( int ) { + QListWidgetItem *currentItem = mLabelingOptionsListWidget->currentItem(); + if ( !currentItem ) + return; + if ( currentItem == mTextItem ) + mLabelStackedWidget->setCurrentWidget( mLabelPage_Text ); + else if ( currentItem == mFormattingItem ) + mLabelStackedWidget->setCurrentWidget( mLabelPage_Formatting ); + else if ( currentItem == mMaskItem ) + mLabelStackedWidget->setCurrentWidget( mLabelPage_Mask ); + else if ( currentItem == mBufferItem ) + mLabelStackedWidget->setCurrentWidget( mLabelPage_Buffer ); + else if ( currentItem == mBackgroundItem ) + mLabelStackedWidget->setCurrentWidget( mLabelPage_Background ); + else if ( currentItem == mShadowItem ) + mLabelStackedWidget->setCurrentWidget( mLabelPage_Shadow ); + else if ( currentItem == mCalloutItem ) + mLabelStackedWidget->setCurrentWidget( mLabelPage_Callouts ); + else if ( currentItem == mPlacementItem ) + mLabelStackedWidget->setCurrentWidget( mLabelPage_Placement ); + else if ( currentItem == mRenderingItem ) + mLabelStackedWidget->setCurrentWidget( mLabelPage_Rendering ); + } ); + + QObject::connect( mLabelingOptionsListWidget, &QListWidget::currentRowChanged, mLabelStackedWidget, &QStackedWidget::setCurrentIndex ); + connect( mShapeSVGPathLineEdit, &QLineEdit::textChanged, this, &QgsTextFormatWidget::mShapeSVGPathLineEdit_textChanged ); connect( mFontSizeSpinBox, static_cast( &QDoubleSpinBox::valueChanged ), this, &QgsTextFormatWidget::mFontSizeSpinBox_valueChanged ); connect( mFontFamilyCmbBx, &QFontComboBox::currentFontChanged, this, &QgsTextFormatWidget::mFontFamilyCmbBx_currentFontChanged ); @@ -544,7 +637,7 @@ void QgsTextFormatWidget::setWidgetMode( QgsTextFormatWidget::Mode mode ) switch ( mode ) { case Labeling: - toggleDDButtons( true ); + setPropertyOverrideButtonsVisible( true ); mTextFormatsListWidget->setEntityTypes( QList() << QgsStyle::TextFormatEntity << QgsStyle::LabelSettingsEntity ); mTextOrientationComboBox->addItem( tr( "Rotation-based" ), static_cast( Qgis::TextOrientation::RotationBased ) ); break; @@ -552,15 +645,19 @@ void QgsTextFormatWidget::setWidgetMode( QgsTextFormatWidget::Mode mode ) case Text: { const int prevIndex = mOptionsTab->currentIndex(); - toggleDDButtons( true ); - delete mLabelingOptionsListWidget->takeItem( 8 ); // rendering - delete mLabelingOptionsListWidget->takeItem( 7 ); // placement - delete mLabelingOptionsListWidget->takeItem( 6 ); // callouts - delete mLabelingOptionsListWidget->takeItem( 3 ); // mask - mOptionsTab->removeTab( 8 ); - mOptionsTab->removeTab( 7 ); - mOptionsTab->removeTab( 6 ); - mOptionsTab->removeTab( 3 ); + setPropertyOverrideButtonsVisible( true ); + delete mRenderingItem; + mRenderingItem = nullptr; + delete mPlacementItem; + mPlacementItem = nullptr; + delete mCalloutItem; + mCalloutItem = nullptr; + delete mMaskItem; + mMaskItem = nullptr; + mOptionsTab->removeTab( mOptionsTab->indexOf( renderingTab ) ); + mOptionsTab->removeTab( mOptionsTab->indexOf( placementTab ) ); + mOptionsTab->removeTab( mOptionsTab->indexOf( calloutsTab ) ); + mOptionsTab->removeTab( mOptionsTab->indexOf( maskTab ) ); mLabelStackedWidget->removeWidget( mLabelPage_Rendering ); mLabelStackedWidget->removeWidget( mLabelPage_Callouts ); mLabelStackedWidget->removeWidget( mLabelPage_Mask ); @@ -587,7 +684,7 @@ void QgsTextFormatWidget::setWidgetMode( QgsTextFormatWidget::Mode mode ) break; } - frameLabelWith->hide(); + mStackedWidgetLabelWith->hide(); mDirectSymbolsFrame->hide(); mFormatNumFrame->hide(); mFormatNumChkBx->hide(); @@ -611,7 +708,7 @@ void QgsTextFormatWidget::setWidgetMode( QgsTextFormatWidget::Mode mode ) } } -void QgsTextFormatWidget::toggleDDButtons( bool visible ) +void QgsTextFormatWidget::setPropertyOverrideButtonsVisible( bool visible ) { const auto buttons = findChildren(); for ( QgsPropertyOverrideButton *button : buttons ) @@ -629,15 +726,24 @@ void QgsTextFormatWidget::toggleDDButtons( bool visible ) void QgsTextFormatWidget::setDockMode( bool enabled ) { mOptionsTab->setVisible( enabled ); - mOptionsTab->setTabToolTip( 0, tr( "Text" ) ); - mOptionsTab->setTabToolTip( 1, tr( "Formatting" ) ); - mOptionsTab->setTabToolTip( 2, tr( "Buffer" ) ); - mOptionsTab->setTabToolTip( 3, tr( "Mask" ) ); - mOptionsTab->setTabToolTip( 4, tr( "Background" ) ); - mOptionsTab->setTabToolTip( 5, tr( "Shadow" ) ); - mOptionsTab->setTabToolTip( 6, tr( "Callouts" ) ); - mOptionsTab->setTabToolTip( 7, tr( "Placement" ) ); - mOptionsTab->setTabToolTip( 8, tr( "Rendering" ) ); + if ( int tabIndex = mOptionsTab->indexOf( textTab ); tabIndex >= 0 ) + mOptionsTab->setTabToolTip( tabIndex, tr( "Text" ) ); + if ( int tabIndex = mOptionsTab->indexOf( formattingTab ); tabIndex >= 0 ) + mOptionsTab->setTabToolTip( tabIndex, tr( "Formatting" ) ); + if ( int tabIndex = mOptionsTab->indexOf( bufferTab ); tabIndex >= 0 ) + mOptionsTab->setTabToolTip( tabIndex, tr( "Buffer" ) ); + if ( int tabIndex = mOptionsTab->indexOf( maskTab ); tabIndex >= 0 ) + mOptionsTab->setTabToolTip( tabIndex, tr( "Mask" ) ); + if ( int tabIndex = mOptionsTab->indexOf( backgroundTab ); tabIndex >= 0 ) + mOptionsTab->setTabToolTip( tabIndex, tr( "Background" ) ); + if ( int tabIndex = mOptionsTab->indexOf( shadowTab ); tabIndex >= 0 ) + mOptionsTab->setTabToolTip( tabIndex, tr( "Shadow" ) ); + if ( int tabIndex = mOptionsTab->indexOf( calloutsTab ); tabIndex >= 0 ) + mOptionsTab->setTabToolTip( tabIndex, tr( "Callouts" ) ); + if ( int tabIndex = mOptionsTab->indexOf( placementTab ); tabIndex >= 0 ) + mOptionsTab->setTabToolTip( tabIndex, tr( "Placement" ) ); + if ( int tabIndex = mOptionsTab->indexOf( renderingTab ); tabIndex >= 0 ) + mOptionsTab->setTabToolTip( tabIndex, tr( "Rendering" ) ); mLabelingOptionsListFrame->setVisible( !enabled ); groupBox_mPreview->setVisible( !enabled ); @@ -985,8 +1091,8 @@ void QgsTextFormatWidget::updateWidgetForFormat( const QgsTextFormat &format ) mFontMissingLabel->setText( missingTxt.arg( txtPrepend ) ); // ensure user is sent to 'Text style' section to see notice - mLabelingOptionsListWidget->setCurrentRow( 0 ); - whileBlocking( mOptionsTab )->setCurrentIndex( 0 ); + mLabelingOptionsListWidget->setCurrentItem( mTextItem ); + whileBlocking( mOptionsTab )->setCurrentIndex( mOptionsTab->indexOf( textTab ) ); } mFontLineHeightSpinBox->setValue( format.lineHeightUnit() == Qgis::RenderUnit::Percentage ? ( format.lineHeight() * 100 ) : format.lineHeight() ); mLineHeightUnitWidget->setUnit( format.lineHeightUnit() ); @@ -1237,10 +1343,28 @@ void QgsTextFormatWidget::deactivateField( QgsPalLayerSettings::Property key ) } } -void QgsTextFormatWidget::optionsStackedWidget_CurrentChanged( int indx ) +void QgsTextFormatWidget::optionsStackedWidget_CurrentChanged( int ) { mLabelingOptionsListWidget->blockSignals( true ); - mLabelingOptionsListWidget->setCurrentRow( indx ); + QWidget *currentPage = mLabelStackedWidget->currentWidget(); + if ( currentPage == mLabelPage_Text ) + mLabelingOptionsListWidget->setCurrentItem( mTextItem ); + else if ( currentPage == mLabelPage_Formatting ) + mLabelingOptionsListWidget->setCurrentItem( mFormattingItem ); + else if ( currentPage == mLabelPage_Mask ) + mLabelingOptionsListWidget->setCurrentItem( mMaskItem ); + if ( currentPage == mLabelPage_Buffer ) + mLabelingOptionsListWidget->setCurrentItem( mBufferItem ); + if ( currentPage == mLabelPage_Background ) + mLabelingOptionsListWidget->setCurrentItem( mBackgroundItem ); + if ( currentPage == mLabelPage_Shadow ) + mLabelingOptionsListWidget->setCurrentItem( mShadowItem ); + if ( currentPage == mLabelPage_Callouts ) + mLabelingOptionsListWidget->setCurrentItem( mCalloutItem ); + if ( currentPage == mLabelPage_Rendering ) + mLabelingOptionsListWidget->setCurrentItem( mRenderingItem ); + if ( currentPage == mLabelPage_Placement ) + mLabelingOptionsListWidget->setCurrentItem( mPlacementItem ); mLabelingOptionsListWidget->blockSignals( false ); } diff --git a/src/gui/qgstextformatwidget.h b/src/gui/qgstextformatwidget.h index 537b828bcc4d..b57d6c7e52f2 100644 --- a/src/gui/qgstextformatwidget.h +++ b/src/gui/qgstextformatwidget.h @@ -165,6 +165,13 @@ class GUI_EXPORT QgsTextFormatWidget : public QWidget, public QgsExpressionConte */ Qgis::GeometryType labelGeometryType() const; + /** + * Toggles whether data defined buttons should be shown in the widget. + * + * \since QGIS 3.42 + */ + void setPropertyOverrideButtonsVisible( bool visible ); + //! Text substitution list QgsStringReplacementCollection mSubstitutions; //! Tab positions @@ -196,6 +203,25 @@ class GUI_EXPORT QgsTextFormatWidget : public QWidget, public QgsExpressionConte //! Geometry type for layer, if known Qgis::GeometryType mGeomType = Qgis::GeometryType::Unknown; + //! Text widget item + QListWidgetItem *mTextItem = nullptr; + //! Formatting widget item + QListWidgetItem *mFormattingItem = nullptr; + //! Buffer widget item + QListWidgetItem *mBufferItem = nullptr; + //! Mask widget item + QListWidgetItem *mMaskItem = nullptr; + //! Background widget item + QListWidgetItem *mBackgroundItem = nullptr; + //! Shadow widget item + QListWidgetItem *mShadowItem = nullptr; + //! Callout widget item + QListWidgetItem *mCalloutItem = nullptr; + //! Placement widget item + QListWidgetItem *mPlacementItem = nullptr; + //! Rendering widget item + QListWidgetItem *mRenderingItem = nullptr; + protected slots: //! Updates line placement options to reflect current state of widget @@ -254,7 +280,6 @@ class GUI_EXPORT QgsTextFormatWidget : public QWidget, public QgsExpressionConte void initWidget(); void setWidgetMode( Mode mode ); - void toggleDDButtons( bool visible ); void blockFontChangeSignals( bool blk ); void populateFontCapitalsComboBox(); void populateFontStyleComboBox(); diff --git a/src/gui/raster/qgsrasterlabelingwidget.cpp b/src/gui/raster/qgsrasterlabelingwidget.cpp new file mode 100644 index 000000000000..ac94f9aea1ba --- /dev/null +++ b/src/gui/raster/qgsrasterlabelingwidget.cpp @@ -0,0 +1,181 @@ +/*************************************************************************** + qgsrasterlabelingwidget.cpp + --------------------------- + begin : December 2024 + copyright : (C) 2024 by Nyall Dawson + email : nyall dot dawson at gmail dot com + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "qgsrasterlabelingwidget.h" +#include "moc_qgsrasterlabelingwidget.cpp" + +#include "qgsrasterlayer.h" +#include "qgsproject.h" +#include "qgsapplication.h" +#include "qgsrasterlabelsettingswidget.h" +#include "qgslabelingwidget.h" + +QgsRasterLabelingWidget::QgsRasterLabelingWidget( QgsRasterLayer *layer, QgsMapCanvas *canvas, QWidget *parent, QgsMessageBar *messageBar ) + : QgsMapLayerConfigWidget( layer, canvas, parent ) + , mCanvas( canvas ) + , mMessageBar( messageBar ) + +{ + setupUi( this ); + + mLabelModeComboBox->addItem( QgsApplication::getThemeIcon( QStringLiteral( "labelingNone.svg" ) ), tr( "No Labels" ), QStringLiteral( "none" ) ); + mLabelModeComboBox->addItem( QgsApplication::getThemeIcon( QStringLiteral( "labelingSingle.svg" ) ), tr( "Label with Pixel Values" ), QStringLiteral( "simple" ) ); + + connect( mLabelModeComboBox, qOverload( &QComboBox::currentIndexChanged ), this, &QgsRasterLabelingWidget::labelModeChanged ); + setLayer( layer ); + + connect( mLabelRulesButton, &QAbstractButton::clicked, this, &QgsRasterLabelingWidget::showLabelingEngineRulesPrivate ); + connect( mEngineSettingsButton, &QAbstractButton::clicked, this, &QgsRasterLabelingWidget::showEngineConfigDialogPrivate ); + + const int iconSize16 = QgsGuiUtils::scaleIconSize( 16 ); + mEngineSettingsButton->setIconSize( QSize( iconSize16, iconSize16 ) ); + mLabelRulesButton->setIconSize( QSize( iconSize16, iconSize16 ) ); +} + +void QgsRasterLabelingWidget::setDockMode( bool dockMode ) +{ + QgsPanelWidget::setDockMode( dockMode ); + + if ( QgsRasterLabelSettingsWidget *l = qobject_cast( mWidget ) ) + { + l->setDockMode( dockMode ); + } +} + +void QgsRasterLabelingWidget::setLayer( QgsMapLayer *mapLayer ) +{ + if ( !mapLayer || mapLayer->type() != Qgis::LayerType::Raster ) + { + setEnabled( false ); + return; + } + else + { + setEnabled( true ); + } + + QgsRasterLayer *layer = qobject_cast( mapLayer ); + mLayer = layer; + + adaptToLayer(); +} + +void QgsRasterLabelingWidget::adaptToLayer() +{ + if ( !mLayer ) + return; + + int index = -1; + QgsAbstractRasterLayerLabeling *labeling = mLayer->labeling(); + if ( mLayer->labelsEnabled() && labeling ) + { + index = mLabelModeComboBox->findData( labeling->type() ); + if ( QgsRasterLabelSettingsWidget *settingsWidget = qobject_cast( mWidget ) ) + { + settingsWidget->setLayer( mLayer ); + settingsWidget->setLabeling( labeling ); + } + } + else + { + index = 0; + } + + if ( index != mLabelModeComboBox->currentIndex() ) + { + mLabelModeComboBox->setCurrentIndex( index ); + } +} + +void QgsRasterLabelingWidget::writeSettingsToLayer() +{ + const QString mode = mLabelModeComboBox->currentData().toString(); + if ( mode == QLatin1String( "simple" ) ) + { + std::unique_ptr labeling = std::make_unique(); + if ( QgsRasterLabelSettingsWidget *settingsWidget = qobject_cast( mWidget ) ) + { + settingsWidget->updateLabeling( labeling.get() ); + } + mLayer->setLabeling( labeling.release() ); + mLayer->setLabelsEnabled( true ); + } + else + { + mLayer->setLabelsEnabled( false ); + } +} + +void QgsRasterLabelingWidget::apply() +{ + writeSettingsToLayer(); + QgsProject::instance()->setDirty(); + // trigger refresh + mLayer->triggerRepaint(); +} + +void QgsRasterLabelingWidget::labelModeChanged( int index ) +{ + if ( mWidget ) + mStackedWidget->removeWidget( mWidget ); + + delete mWidget; + mWidget = nullptr; + + if ( index < 0 ) + return; + + const QString mode = mLabelModeComboBox->currentData().toString(); + if ( mode == QLatin1String( "simple" ) ) + { + QgsSymbolWidgetContext context; + context.setMapCanvas( mMapCanvas ); + context.setMessageBar( mMessageBar ); + + QgsRasterLabelSettingsWidget *settingsWidget = new QgsRasterLabelSettingsWidget( mLayer, mCanvas, this ); + settingsWidget->layout()->setContentsMargins( 0, 0, 0, 0 ); + settingsWidget->setContext( context ); + + settingsWidget->setDockMode( dockMode() ); + connect( settingsWidget, &QgsLabelingGui::widgetChanged, this, &QgsRasterLabelingWidget::widgetChanged ); + + mWidget = settingsWidget; + if ( !dynamic_cast( mLayer->labeling() ) ) + { + std::unique_ptr labeling = std::make_unique(); + settingsWidget->setLabeling( labeling.get() ); + mLayer->setLabeling( labeling.release() ); + } + else + { + settingsWidget->setLabeling( mLayer->labeling() ); + } + + mStackedWidget->addWidget( mWidget ); + mStackedWidget->setCurrentWidget( mWidget ); + } + + emit widgetChanged(); +} + +void QgsRasterLabelingWidget::showLabelingEngineRulesPrivate() +{ + QgsLabelingWidget::showLabelingEngineRules( this, mCanvas ); +} + +void QgsRasterLabelingWidget::showEngineConfigDialogPrivate() +{ + QgsLabelingWidget::showEngineConfiguration( this, mCanvas ); +} diff --git a/src/gui/raster/qgsrasterlabelingwidget.h b/src/gui/raster/qgsrasterlabelingwidget.h new file mode 100644 index 000000000000..637a8cdadebb --- /dev/null +++ b/src/gui/raster/qgsrasterlabelingwidget.h @@ -0,0 +1,82 @@ +/*************************************************************************** + qgsrasterlabelingwidget.h + ------------------------- + begin : December 2024 + copyright : (C) 2024 by Nyall Dawson + email : nyall dot dawson at gmail dot com + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ +#ifndef QGSRASTERLABELINGWIDGET_H +#define QGSRASTERLABELINGWIDGET_H + +// We don't want to expose this in the public API +#define SIP_NO_FILE + +#include + +#include "ui_qgsrasterlabelingwidgetbase.h" +#include "qgsrasterlabeling.h" +#include "qgis_gui.h" + +#include "qgsmaplayerconfigwidget.h" + +class QgsLabelingGui; +class QgsMapCanvas; +class QgsRasterLayer; +class QgsMapLayer; +class QgsMessageBar; + +/** + * \ingroup gui + * \brief Master widget for configuration of labeling of a raster layer + * \note Not available in Python bindings + * \since QGIS 3.42 + */ +class GUI_EXPORT QgsRasterLabelingWidget : public QgsMapLayerConfigWidget, private Ui::QgsRasterLabelingWidgetBase +{ + Q_OBJECT + public: + //! constructor + QgsRasterLabelingWidget( QgsRasterLayer *layer, QgsMapCanvas *canvas, QWidget *parent = nullptr, QgsMessageBar *messageBar = nullptr ); + + void setDockMode( bool dockMode ) override; + + public slots: + //! Sets the layer to configure + void setLayer( QgsMapLayer *layer ); + + /** + * Saves the labeling configuration to the destination layer. + */ + void writeSettingsToLayer(); + + //! Saves the labeling configuration and immediately updates the map canvas to reflect the changes + void apply() override; + + //! Reload the settings shown in the dialog from the current layer + void adaptToLayer(); + + signals: + //! Emitted when an auxiliary field is created + void auxiliaryFieldCreated(); + + private slots: + void labelModeChanged( int index ); + void showLabelingEngineRulesPrivate(); + void showEngineConfigDialogPrivate(); + + private: + QgsRasterLayer *mLayer = nullptr; + QgsMapCanvas *mCanvas = nullptr; + QgsMessageBar *mMessageBar = nullptr; + + QWidget *mWidget = nullptr; +}; + +#endif // QGSRASTERLABELINGWIDGET_H diff --git a/src/gui/raster/qgsrasterlabelsettingswidget.cpp b/src/gui/raster/qgsrasterlabelsettingswidget.cpp new file mode 100644 index 000000000000..5aba7ca58536 --- /dev/null +++ b/src/gui/raster/qgsrasterlabelsettingswidget.cpp @@ -0,0 +1,257 @@ +/*************************************************************************** + qgsrasterlabelsettingswidget.cpp + --------------------------- + begin : December 2024 + copyright : (C) 2024 by Nyall Dawson + email : nyall dot dawson at gmail dot com + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "qgsrasterlabelsettingswidget.h" +#include "moc_qgsrasterlabelsettingswidget.cpp" +#include "qgsrasterbandcombobox.h" +#include "qgsrasterlabeling.h" +#include "qgsnumericformatselectorwidget.h" +#include "qgsbasicnumericformat.h" + +#include + +QgsRasterLabelSettingsWidget::QgsRasterLabelSettingsWidget( QgsRasterLayer *layer, QgsMapCanvas *mapCanvas, QWidget *parent ) + : QgsLabelingGui( mapCanvas, parent, layer ) + , mNumberFormat( std::make_unique() ) +{ + mGeomType = Qgis::GeometryType::Point; + mMode = Labels; + + init(); + + QWidget *labelWithWidget = new QWidget(); + QGridLayout *gLayout = new QGridLayout(); + gLayout->setContentsMargins( 0, 0, 0, 0 ); + gLayout->addWidget( new QLabel( tr( "Value" ) ), 0, 0 ); + mBandCombo = new QgsRasterBandComboBox(); + mBandCombo->setLayer( layer ); + gLayout->addWidget( mBandCombo, 0, 1 ); + gLayout->setColumnStretch( 0, 1 ); + gLayout->setColumnStretch( 1, 2 ); + + gLayout->addWidget( new QLabel( tr( "Number format" ) ), 1, 0 ); + + QPushButton *numberFormatButton = new QPushButton( tr( "Customize" ) ); + connect( numberFormatButton, &QPushButton::clicked, this, &QgsRasterLabelSettingsWidget::changeNumberFormat ); + + gLayout->addWidget( numberFormatButton, 1, 1 ); + + gLayout->addWidget( new QLabel( tr( "Resample over" ) ), 2, 0 ); + mResampleOverSpin = new QgsSpinBox(); + mResampleOverSpin->setMinimum( 1 ); + mResampleOverSpin->setMaximum( 128 ); + mResampleOverSpin->setClearValue( 1 ); + mResampleOverSpin->setSuffix( tr( " pixels" ) ); + connect( mResampleOverSpin, qOverload( &QgsSpinBox::valueChanged ), this, &QgsRasterLabelSettingsWidget::widgetChanged ); + gLayout->addWidget( mResampleOverSpin, 2, 1 ); + + gLayout->addWidget( new QLabel( tr( "Resample using" ) ), 3, 0 ); + mResampleMethodComboBox = new QComboBox(); + mResampleMethodComboBox->addItem( QObject::tr( "Nearest Neighbour" ), QVariant::fromValue( Qgis::RasterResamplingMethod::Nearest ) ); + mResampleMethodComboBox->addItem( QObject::tr( "Bilinear (2x2 Kernel)" ), QVariant::fromValue( Qgis::RasterResamplingMethod::Bilinear ) ); + mResampleMethodComboBox->addItem( QObject::tr( "Cubic (4x4 Kernel)" ), QVariant::fromValue( Qgis::RasterResamplingMethod::Cubic ) ); + mResampleMethodComboBox->addItem( QObject::tr( "Cubic B-Spline (4x4 Kernel)" ), QVariant::fromValue( Qgis::RasterResamplingMethod::CubicSpline ) ); + mResampleMethodComboBox->addItem( QObject::tr( "Lanczos (6x6 Kernel)" ), QVariant::fromValue( Qgis::RasterResamplingMethod::Lanczos ) ); + mResampleMethodComboBox->addItem( QObject::tr( "Average" ), QVariant::fromValue( Qgis::RasterResamplingMethod::Average ) ); + mResampleMethodComboBox->addItem( QObject::tr( "Mode" ), QVariant::fromValue( Qgis::RasterResamplingMethod::Mode ) ); + mResampleMethodComboBox->addItem( QObject::tr( "Gauss" ), QVariant::fromValue( Qgis::RasterResamplingMethod::Gauss ) ); + connect( mResampleMethodComboBox, qOverload( &QComboBox::currentIndexChanged ), this, &QgsRasterLabelSettingsWidget::widgetChanged ); + gLayout->addWidget( mResampleMethodComboBox, 3, 1 ); + + labelWithWidget->setLayout( gLayout ); + + mStackedWidgetLabelWith->addWidget( labelWithWidget ); + mStackedWidgetLabelWith->setCurrentWidget( labelWithWidget ); + + setLayer( layer ); + + const int prevIndex = mOptionsTab->currentIndex(); + + setPropertyOverrideButtonsVisible( true ); + mTextFormatsListWidget->setEntityTypes( QList() << QgsStyle::TextFormatEntity ); + + delete mCalloutItem; + mCalloutItem = nullptr; + delete mMaskItem; + mMaskItem = nullptr; + + mOptionsTab->removeTab( mOptionsTab->indexOf( calloutsTab ) ); + mOptionsTab->removeTab( mOptionsTab->indexOf( maskTab ) ); + + mLabelStackedWidget->removeWidget( mLabelPage_Callouts ); + mLabelStackedWidget->removeWidget( mLabelPage_Mask ); + + switch ( prevIndex ) + { + case 0: + case 1: + case 2: + break; + + case 4: // background - account for removed mask tab + case 5: // shadow + mLabelStackedWidget->setCurrentIndex( prevIndex - 1 ); + mOptionsTab->setCurrentIndex( prevIndex - 1 ); + break; + + case 7: // background - account for removed mask & callouts tab + case 8: // shadow- account for removed mask & callouts tab + mLabelStackedWidget->setCurrentIndex( prevIndex - 2 ); + mOptionsTab->setCurrentIndex( prevIndex - 2 ); + break; + + case 3: // mask + case 6: // callouts + mLabelStackedWidget->setCurrentIndex( 0 ); + mOptionsTab->setCurrentIndex( 0 ); + break; + + default: + break; + } + + // hide settings which have no relevance to raster labeling + mDirectSymbolsFrame->hide(); + mFormatNumFrame->hide(); + mFormatNumChkBx->hide(); + mFormatNumDDBtn->hide(); + mCheckBoxSubstituteText->hide(); + mToolButtonConfigureSubstitutes->hide(); + mLabelWrapOnCharacter->hide(); + wrapCharacterEdit->hide(); + mWrapCharDDBtn->hide(); + mLabelWrapLinesTo->hide(); + mAutoWrapLengthSpinBox->hide(); + mAutoWrapLengthDDBtn->hide(); + mAutoWrapTypeComboBox->hide(); + mFontMultiLineLabel->hide(); + mFontMultiLineAlignComboBox->hide(); + mFontMultiLineAlignDDBtn->hide(); + mGeometryGeneratorGroupBox->hide(); + mObstaclesGroupBox->hide(); + mPlacementDDGroupBox->hide(); + mPlacementGroupBox->hide(); + mInferiorPlacementWidget->hide(); + mLabelRenderingDDFrame->hide(); + mUpsidedownFrame->hide(); + mLabelEveryPartWidget->hide(); + mFramePixelSizeVisibility->hide(); + line->hide(); + + mMinSizeFrame->show(); + mMinSizeLabel->setText( tr( "Suppress labeling of pixels smaller than" ) ); + + mLimitLabelChkBox->setText( tr( "Limit number of pixels to be labeled to" ) ); + + // fix precision for priority slider + mPrioritySlider->setRange( 0, 100 ); + mPrioritySlider->setTickInterval( 10 ); + + connect( mBandCombo, &QgsRasterBandComboBox::bandChanged, this, &QgsRasterLabelSettingsWidget::widgetChanged ); +} + +QgsRasterLabelSettingsWidget::~QgsRasterLabelSettingsWidget() = default; + +void QgsRasterLabelSettingsWidget::setLabeling( QgsAbstractRasterLayerLabeling *labeling ) +{ + if ( QgsRasterLayerSimpleLabeling *simpleLabeling = dynamic_cast( labeling ) ) + { + setFormat( simpleLabeling->textFormat() ); + mBandCombo->setBand( simpleLabeling->band() ); + mPrioritySlider->setValue( static_cast( 100 - simpleLabeling->priority() * 100 ) ); + + mComboOverlapHandling->setCurrentIndex( mComboOverlapHandling->findData( static_cast( simpleLabeling->placementSettings().overlapHandling() ) ) ); + mZIndexSpinBox->setValue( simpleLabeling->zIndex() ); + if ( const QgsNumericFormat *format = simpleLabeling->numericFormat() ) + mNumberFormat.reset( format->clone() ); + + mLimitLabelChkBox->setChecked( simpleLabeling->thinningSettings().limitNumberOfLabelsEnabled() ); + mLimitLabelSpinBox->setValue( simpleLabeling->thinningSettings().maximumNumberLabels() ); + mMinSizeSpinBox->setValue( simpleLabeling->thinningSettings().minimumFeatureSize() ); + + mScaleBasedVisibilityChkBx->setChecked( simpleLabeling->hasScaleBasedVisibility() ); + mMinScaleWidget->setScale( simpleLabeling->minimumScale() ); + mMaxScaleWidget->setScale( simpleLabeling->maximumScale() ); + + mResampleOverSpin->setValue( simpleLabeling->resampleOver() ); + mResampleMethodComboBox->setCurrentIndex( mResampleMethodComboBox->findData( QVariant::fromValue( simpleLabeling->resampleMethod() ) ) ); + + updateUi(); + } +} + +void QgsRasterLabelSettingsWidget::updateLabeling( QgsAbstractRasterLayerLabeling *labeling ) +{ + if ( QgsRasterLayerSimpleLabeling *simpleLabeling = dynamic_cast( labeling ) ) + { + simpleLabeling->setTextFormat( format() ); + simpleLabeling->setBand( mBandCombo->currentBand() ); + simpleLabeling->setPriority( 1.0 - mPrioritySlider->value() / 100.0 ); + simpleLabeling->placementSettings().setOverlapHandling( static_cast( mComboOverlapHandling->currentData().toInt() ) ); + simpleLabeling->setZIndex( mZIndexSpinBox->value() ); + simpleLabeling->setNumericFormat( mNumberFormat->clone() ); + + simpleLabeling->thinningSettings().setLimitNumberLabelsEnabled( mLimitLabelChkBox->isChecked() ); + simpleLabeling->thinningSettings().setMaximumNumberLabels( mLimitLabelSpinBox->value() ); + simpleLabeling->thinningSettings().setMinimumFeatureSize( mMinSizeSpinBox->value() ); + + simpleLabeling->setScaleBasedVisibility( mScaleBasedVisibilityChkBx->isChecked() ); + simpleLabeling->setMinimumScale( mMinScaleWidget->scale() ); + simpleLabeling->setMaximumScale( mMaxScaleWidget->scale() ); + + simpleLabeling->setResampleOver( mResampleOverSpin->value() ); + simpleLabeling->setResampleMethod( mResampleMethodComboBox->currentData().value() ); + } +} + +void QgsRasterLabelSettingsWidget::setLayer( QgsMapLayer *layer ) +{ + if ( mBandCombo ) + mBandCombo->setLayer( layer ); + + QgsLabelingGui::setLayer( layer ); + mMinSizeFrame->show(); +} + +void QgsRasterLabelSettingsWidget::changeNumberFormat() +{ + QgsPanelWidget *panel = QgsPanelWidget::findParentPanel( this ); + if ( panel && panel->dockMode() ) + { + QgsNumericFormatSelectorWidget *widget = new QgsNumericFormatSelectorWidget( this ); + widget->setPanelTitle( tr( "Number Format" ) ); + widget->setFormat( mNumberFormat.get() ); + widget->registerExpressionContextGenerator( this ); + connect( widget, &QgsNumericFormatSelectorWidget::changed, this, [=] { + if ( !mBlockChangesSignal ) + { + mNumberFormat.reset( widget->format() ); + emit widgetChanged(); + } + } ); + panel->openPanel( widget ); + } + else + { + QgsNumericFormatSelectorDialog dialog( this ); + dialog.setFormat( mNumberFormat.get() ); + dialog.registerExpressionContextGenerator( this ); + if ( dialog.exec() ) + { + mNumberFormat.reset( dialog.format() ); + emit widgetChanged(); + } + } +} diff --git a/src/gui/raster/qgsrasterlabelsettingswidget.h b/src/gui/raster/qgsrasterlabelsettingswidget.h new file mode 100644 index 000000000000..bbd562560294 --- /dev/null +++ b/src/gui/raster/qgsrasterlabelsettingswidget.h @@ -0,0 +1,70 @@ +/*************************************************************************** + qgsrasterlabelsettingswidget.h + ------------------------- + begin : December 2024 + copyright : (C) 2024 by Nyall Dawson + email : nyall dot dawson at gmail dot com + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ +#ifndef QGSRASTERLABELSETTINGSWIDGET_H +#define QGSRASTERLABELSETTINGSWIDGET_H + +#include "qgis_gui.h" +#include "qgslabelinggui.h" + +class QgsRasterLayer; +class QgsAbstractRasterLayerLabeling; +class QgsRasterBandComboBox; +class QgsNumericFormat; + +// We don't want to expose this in the public API +#define SIP_NO_FILE + +/** + * \class QgsRasterLabelSettingsWidget + * \ingroup gui + * \brief A widget for customizing settings for raster layer labeling. + * + * \since QGIS 3.42 + */ +class GUI_EXPORT QgsRasterLabelSettingsWidget : public QgsLabelingGui +{ + Q_OBJECT + public: + /** + * Constructor for QgsRasterLabelSettingsWidget, for configuring a raster \a layer labeling. + */ + QgsRasterLabelSettingsWidget( QgsRasterLayer *layer, QgsMapCanvas *mapCanvas, QWidget *parent = nullptr ); + ~QgsRasterLabelSettingsWidget() override; + + /** + * Sets the \a labeling settings to show in the widget. + */ + void setLabeling( QgsAbstractRasterLayerLabeling *labeling ); + + /** + * Updates \a labeling by setting properties to match the current state of the widget. + */ + void updateLabeling( QgsAbstractRasterLayerLabeling *labeling ); + + void setLayer( QgsMapLayer *layer ) final; + + private slots: + void changeNumberFormat(); + + private: + QgsRasterBandComboBox *mBandCombo = nullptr; + std::unique_ptr mNumberFormat; + int mBlockChangesSignal = 0; + + QgsSpinBox *mResampleOverSpin = nullptr; + QComboBox *mResampleMethodComboBox = nullptr; +}; + +#endif // QGSRASTERLABELSETTINGSWIDGET_H diff --git a/src/gui/raster/qgsrasterlayerproperties.cpp b/src/gui/raster/qgsrasterlayerproperties.cpp index 58fcdb826818..a8db645d107b 100644 --- a/src/gui/raster/qgsrasterlayerproperties.cpp +++ b/src/gui/raster/qgsrasterlayerproperties.cpp @@ -69,6 +69,7 @@ #include "qgswebframe.h" #include "qgsexpressionfinder.h" #include "qgsexpressionbuilderdialog.h" +#include "qgsrasterlabelingwidget.h" #if WITH_QTWEBKIT #include #endif @@ -120,6 +121,12 @@ QgsRasterLayerProperties::QgsRasterLayerProperties( QgsMapLayer *lyr, QgsMapCanv transparencyScrollArea->setWidget( mRasterTransparencyWidget ); + mLabelingWidget = new QgsRasterLabelingWidget( mRasterLayer, canvas, this ); + QVBoxLayout *vl = new QVBoxLayout(); + vl->setContentsMargins( 0, 0, 0, 0 ); + vl->addWidget( mLabelingWidget ); + mOptsPage_Labeling->setLayout( vl ); + connect( buttonBuildPyramids, &QPushButton::clicked, this, &QgsRasterLayerProperties::buttonBuildPyramids_clicked ); connect( mCrsSelector, &QgsProjectionSelectionWidget::crsChanged, this, &QgsRasterLayerProperties::mCrsSelector_crsChanged ); connect( mRenderTypeComboBox, static_cast( &QComboBox::currentIndexChanged ), this, &QgsRasterLayerProperties::mRenderTypeComboBox_currentIndexChanged ); @@ -743,6 +750,7 @@ void QgsRasterLayerProperties::sync() return; mRasterTransparencyWidget->syncToLayer(); + mLabelingWidget->syncToLayer( mRasterLayer ); if ( provider->dataType( 1 ) == Qgis::DataType::ARGB32 || provider->dataType( 1 ) == Qgis::DataType::ARGB32_Premultiplied ) @@ -954,6 +962,8 @@ void QgsRasterLayerProperties::apply() QgsRasterRenderer *rasterRenderer = mRasterLayer->renderer(); mRasterTransparencyWidget->applyToRasterRenderer( rasterRenderer ); + mLabelingWidget->apply(); + if ( rasterRenderer ) { // Sync the layer styling widget diff --git a/src/gui/raster/qgsrasterlayerproperties.h b/src/gui/raster/qgsrasterlayerproperties.h index 3c2c1852fa0a..70902c4f2d81 100644 --- a/src/gui/raster/qgsrasterlayerproperties.h +++ b/src/gui/raster/qgsrasterlayerproperties.h @@ -46,6 +46,7 @@ class QgsPropertyOverrideButton; class QgsRasterTransparencyWidget; class QgsRasterAttributeTableWidget; class QgsWebView; +class QgsRasterLabelingWidget; /** * \ingroup gui @@ -212,6 +213,8 @@ class GUI_EXPORT QgsRasterLayerProperties : public QgsLayerPropertiesDialog, pri QgsRasterTransparencyWidget *mRasterTransparencyWidget = nullptr; + QgsRasterLabelingWidget *mLabelingWidget = nullptr; + /** * Widget with temporal inputs, to be used by temporal based raster layers. */ diff --git a/src/gui/raster/qgsresamplingutils.cpp b/src/gui/raster/qgsresamplingutils.cpp index 22aafde982f1..1a41feeeb8b4 100644 --- a/src/gui/raster/qgsresamplingutils.cpp +++ b/src/gui/raster/qgsresamplingutils.cpp @@ -43,9 +43,9 @@ void QgsResamplingUtils::initWidgets( QgsRasterLayer *rasterLayer, QComboBox *zo for ( QComboBox *combo : { mZoomedInResamplingComboBox, mZoomedOutResamplingComboBox } ) { - combo->addItem( QObject::tr( "Nearest Neighbour" ), static_cast( QgsRasterDataProvider::ResamplingMethod::Nearest ) ); - combo->addItem( QObject::tr( "Bilinear (2x2 Kernel)" ), static_cast( QgsRasterDataProvider::ResamplingMethod::Bilinear ) ); - combo->addItem( QObject::tr( "Cubic (4x4 Kernel)" ), static_cast( QgsRasterDataProvider::ResamplingMethod::Cubic ) ); + combo->addItem( QObject::tr( "Nearest Neighbour" ), static_cast( Qgis::RasterResamplingMethod::Nearest ) ); + combo->addItem( QObject::tr( "Bilinear (2x2 Kernel)" ), static_cast( Qgis::RasterResamplingMethod::Bilinear ) ); + combo->addItem( QObject::tr( "Cubic (4x4 Kernel)" ), static_cast( Qgis::RasterResamplingMethod::Cubic ) ); } if ( mCbEarlyResampling->isChecked() ) @@ -97,16 +97,16 @@ void QgsResamplingUtils::refreshWidgetsFromLayer() { if ( zoomedInResampler->type() == QLatin1String( "bilinear" ) ) { - mZoomedInResamplingComboBox->setCurrentIndex( mZoomedInResamplingComboBox->findData( static_cast( QgsRasterDataProvider::ResamplingMethod::Bilinear ) ) ); + mZoomedInResamplingComboBox->setCurrentIndex( mZoomedInResamplingComboBox->findData( static_cast( Qgis::RasterResamplingMethod::Bilinear ) ) ); } else if ( zoomedInResampler->type() == QLatin1String( "cubic" ) ) { - mZoomedInResamplingComboBox->setCurrentIndex( mZoomedInResamplingComboBox->findData( static_cast( QgsRasterDataProvider::ResamplingMethod::Cubic ) ) ); + mZoomedInResamplingComboBox->setCurrentIndex( mZoomedInResamplingComboBox->findData( static_cast( Qgis::RasterResamplingMethod::Cubic ) ) ); } } else { - mZoomedInResamplingComboBox->setCurrentIndex( mZoomedInResamplingComboBox->findData( static_cast( QgsRasterDataProvider::ResamplingMethod::Nearest ) ) ); + mZoomedInResamplingComboBox->setCurrentIndex( mZoomedInResamplingComboBox->findData( static_cast( Qgis::RasterResamplingMethod::Nearest ) ) ); } const QgsRasterResampler *zoomedOutResampler = resampleFilter->zoomedOutResampler(); @@ -114,16 +114,16 @@ void QgsResamplingUtils::refreshWidgetsFromLayer() { if ( zoomedOutResampler->type() == QLatin1String( "bilinear" ) ) { - mZoomedOutResamplingComboBox->setCurrentIndex( mZoomedOutResamplingComboBox->findData( static_cast( QgsRasterDataProvider::ResamplingMethod::Bilinear ) ) ); + mZoomedOutResamplingComboBox->setCurrentIndex( mZoomedOutResamplingComboBox->findData( static_cast( Qgis::RasterResamplingMethod::Bilinear ) ) ); } else if ( zoomedOutResampler->type() == QLatin1String( "cubic" ) ) { - mZoomedOutResamplingComboBox->setCurrentIndex( mZoomedOutResamplingComboBox->findData( static_cast( QgsRasterDataProvider::ResamplingMethod::Cubic ) ) ); + mZoomedOutResamplingComboBox->setCurrentIndex( mZoomedOutResamplingComboBox->findData( static_cast( Qgis::RasterResamplingMethod::Cubic ) ) ); } } else { - mZoomedOutResamplingComboBox->setCurrentIndex( mZoomedOutResamplingComboBox->findData( static_cast( QgsRasterDataProvider::ResamplingMethod::Nearest ) ) ); + mZoomedOutResamplingComboBox->setCurrentIndex( mZoomedOutResamplingComboBox->findData( static_cast( Qgis::RasterResamplingMethod::Nearest ) ) ); } mMaximumOversamplingSpinBox->setValue( resampleFilter->maxOversampling() ); } @@ -133,10 +133,10 @@ void QgsResamplingUtils::refreshWidgetsFromLayer() void QgsResamplingUtils::refreshLayerFromWidgets() { - const QgsRasterDataProvider::ResamplingMethod zoomedInMethod = static_cast( + const Qgis::RasterResamplingMethod zoomedInMethod = static_cast( mZoomedInResamplingComboBox->itemData( mZoomedInResamplingComboBox->currentIndex() ).toInt() ); - const QgsRasterDataProvider::ResamplingMethod zoomedOutMethod = static_cast( + const Qgis::RasterResamplingMethod zoomedOutMethod = static_cast( mZoomedOutResamplingComboBox->itemData( mZoomedOutResamplingComboBox->currentIndex() ).toInt() ); @@ -155,56 +155,60 @@ void QgsResamplingUtils::refreshLayerFromWidgets() { std::unique_ptr zoomedInResampler; + // NOLINTBEGIN(bugprone-branch-clone) switch ( zoomedInMethod ) { - case QgsRasterDataProvider::ResamplingMethod::Nearest: + case Qgis::RasterResamplingMethod::Nearest: break; - case QgsRasterDataProvider::ResamplingMethod::Bilinear: + case Qgis::RasterResamplingMethod::Bilinear: zoomedInResampler = std::make_unique(); break; - case QgsRasterDataProvider::ResamplingMethod::Cubic: + case Qgis::RasterResamplingMethod::Cubic: zoomedInResampler = std::make_unique(); break; - case QgsRasterDataProvider::ResamplingMethod::CubicSpline: - case QgsRasterDataProvider::ResamplingMethod::Lanczos: - case QgsRasterDataProvider::ResamplingMethod::Average: - case QgsRasterDataProvider::ResamplingMethod::Mode: - case QgsRasterDataProvider::ResamplingMethod::Gauss: + case Qgis::RasterResamplingMethod::CubicSpline: + case Qgis::RasterResamplingMethod::Lanczos: + case Qgis::RasterResamplingMethod::Average: + case Qgis::RasterResamplingMethod::Mode: + case Qgis::RasterResamplingMethod::Gauss: // not supported as late resampler methods break; } + // NOLINTEND(bugprone-branch-clone) resampleFilter->setZoomedInResampler( zoomedInResampler.release() ); //raster resampling std::unique_ptr zoomedOutResampler; + // NOLINTBEGIN(bugprone-branch-clone) switch ( zoomedOutMethod ) { - case QgsRasterDataProvider::ResamplingMethod::Nearest: + case Qgis::RasterResamplingMethod::Nearest: break; - case QgsRasterDataProvider::ResamplingMethod::Bilinear: + case Qgis::RasterResamplingMethod::Bilinear: zoomedOutResampler = std::make_unique(); break; - case QgsRasterDataProvider::ResamplingMethod::Cubic: + case Qgis::RasterResamplingMethod::Cubic: zoomedOutResampler = std::make_unique(); break; - case QgsRasterDataProvider::ResamplingMethod::CubicSpline: - case QgsRasterDataProvider::ResamplingMethod::Lanczos: - case QgsRasterDataProvider::ResamplingMethod::Average: - case QgsRasterDataProvider::ResamplingMethod::Mode: - case QgsRasterDataProvider::ResamplingMethod::Gauss: + case Qgis::RasterResamplingMethod::CubicSpline: + case Qgis::RasterResamplingMethod::Lanczos: + case Qgis::RasterResamplingMethod::Average: + case Qgis::RasterResamplingMethod::Mode: + case Qgis::RasterResamplingMethod::Gauss: // not supported as late resampler methods break; } + // NOLINTEND(bugprone-branch-clone) resampleFilter->setZoomedOutResampler( zoomedOutResampler.release() ); @@ -214,33 +218,33 @@ void QgsResamplingUtils::refreshLayerFromWidgets() void QgsResamplingUtils::addExtraEarlyResamplingMethodsToCombos() { - if ( mZoomedInResamplingComboBox->findData( static_cast( QgsRasterDataProvider::ResamplingMethod::CubicSpline ) ) != -1 ) + if ( mZoomedInResamplingComboBox->findData( static_cast( Qgis::RasterResamplingMethod::CubicSpline ) ) != -1 ) return; // already present for ( QComboBox *combo : { mZoomedInResamplingComboBox, mZoomedOutResamplingComboBox } ) { - combo->addItem( QObject::tr( "Cubic B-Spline (4x4 Kernel)" ), static_cast( QgsRasterDataProvider::ResamplingMethod::CubicSpline ) ); - combo->addItem( QObject::tr( "Lanczos (6x6 Kernel)" ), static_cast( QgsRasterDataProvider::ResamplingMethod::Lanczos ) ); - combo->addItem( QObject::tr( "Average" ), static_cast( QgsRasterDataProvider::ResamplingMethod::Average ) ); - combo->addItem( QObject::tr( "Mode" ), static_cast( QgsRasterDataProvider::ResamplingMethod::Mode ) ); - combo->addItem( QObject::tr( "Gauss" ), static_cast( QgsRasterDataProvider::ResamplingMethod::Gauss ) ); + combo->addItem( QObject::tr( "Cubic B-Spline (4x4 Kernel)" ), static_cast( Qgis::RasterResamplingMethod::CubicSpline ) ); + combo->addItem( QObject::tr( "Lanczos (6x6 Kernel)" ), static_cast( Qgis::RasterResamplingMethod::Lanczos ) ); + combo->addItem( QObject::tr( "Average" ), static_cast( Qgis::RasterResamplingMethod::Average ) ); + combo->addItem( QObject::tr( "Mode" ), static_cast( Qgis::RasterResamplingMethod::Mode ) ); + combo->addItem( QObject::tr( "Gauss" ), static_cast( Qgis::RasterResamplingMethod::Gauss ) ); } } void QgsResamplingUtils::removeExtraEarlyResamplingMethodsFromCombos() { - if ( mZoomedInResamplingComboBox->findData( static_cast( QgsRasterDataProvider::ResamplingMethod::CubicSpline ) ) == -1 ) + if ( mZoomedInResamplingComboBox->findData( static_cast( Qgis::RasterResamplingMethod::CubicSpline ) ) == -1 ) return; // already removed for ( QComboBox *combo : { mZoomedInResamplingComboBox, mZoomedOutResamplingComboBox } ) { - for ( const QgsRasterDataProvider::ResamplingMethod method : + for ( const Qgis::RasterResamplingMethod method : { - QgsRasterDataProvider::ResamplingMethod::CubicSpline, - QgsRasterDataProvider::ResamplingMethod::Lanczos, - QgsRasterDataProvider::ResamplingMethod::Average, - QgsRasterDataProvider::ResamplingMethod::Mode, - QgsRasterDataProvider::ResamplingMethod::Gauss + Qgis::RasterResamplingMethod::CubicSpline, + Qgis::RasterResamplingMethod::Lanczos, + Qgis::RasterResamplingMethod::Average, + Qgis::RasterResamplingMethod::Mode, + Qgis::RasterResamplingMethod::Gauss } ) { combo->removeItem( combo->findData( static_cast( method ) ) ); diff --git a/src/providers/wms/qgswmsprovider.h b/src/providers/wms/qgswmsprovider.h index 0197a845c2dd..4f2c3593aca2 100644 --- a/src/providers/wms/qgswmsprovider.h +++ b/src/providers/wms/qgswmsprovider.h @@ -284,12 +284,12 @@ class QgsWmsProvider final : public QgsRasterDataProvider mProviderResamplingEnabled = enable; return true; } - bool setZoomedInResamplingMethod( ResamplingMethod method ) override + bool setZoomedInResamplingMethod( Qgis::RasterResamplingMethod method ) override { mZoomedInResamplingMethod = method; return true; } - bool setZoomedOutResamplingMethod( ResamplingMethod method ) override + bool setZoomedOutResamplingMethod( Qgis::RasterResamplingMethod method ) override { mZoomedOutResamplingMethod = method; return true; diff --git a/src/ui/qgsrasterlayerpropertiesbase.ui b/src/ui/qgsrasterlayerpropertiesbase.ui index c572982b5b32..c01ce6fb0b09 100644 --- a/src/ui/qgsrasterlayerpropertiesbase.ui +++ b/src/ui/qgsrasterlayerpropertiesbase.ui @@ -137,6 +137,18 @@ :/images/themes/default/propertyicons/transparency.svg:/images/themes/default/propertyicons/transparency.svg + + + Labels + + + Labels + + + + :/images/themes/default/mActionLabeling.svg:/images/themes/default/mActionLabeling.svg + + Histogram @@ -320,8 +332,8 @@ 0 0 - 354 - 457 + 320 + 455 @@ -350,7 +362,7 @@ false - + vectorgeneral @@ -358,7 +370,7 @@ 6 - + Qt::StrongFocus @@ -443,8 +455,8 @@ 0 0 - 665 - 365 + 643 + 680 @@ -471,7 +483,7 @@ Band Rendering - + rasterstyle @@ -516,13 +528,13 @@ Layer Rendering - + false - + rasterstyle - + true @@ -877,13 +889,13 @@ false - + false - + rasterstyle - + true @@ -985,6 +997,7 @@ + @@ -1012,8 +1025,8 @@ 0 0 - 643 - 681 + 98 + 39 @@ -1075,7 +1088,7 @@ false - + rastergeneral @@ -1095,7 +1108,7 @@ 6 - + @@ -1188,8 +1201,8 @@ 0 0 - 543 - 184 + 504 + 187 @@ -1256,10 +1269,13 @@ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> -<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +<html><head><meta name="qrichtext" content="1" /><meta charset="utf-8" /><style type="text/css"> p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'Ubuntu'; font-size:11pt; font-weight:400; font-style:normal;"> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Cantarell';"><br /></span></p></body></html> +hr { height: 1px; border-width: 0; } +li.unchecked::marker { content: "\2610"; } +li.checked::marker { content: "\2612"; } +</style></head><body style=" font-family:'Noto Sans'; font-size:10pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Cantarell'; font-size:11pt;"><br /></span></p></body></html> @@ -1622,8 +1638,8 @@ You can also import an existing raster attribute table from a VAT.DBF file and a 0 0 - 374 - 694 + 331 + 709 @@ -1644,7 +1660,7 @@ You can also import an existing raster attribute table from a VAT.DBF file and a Description - + rastermeta @@ -1780,7 +1796,7 @@ You can also import an existing raster attribute table from a VAT.DBF file and a Attribution - + vectormeta @@ -1826,7 +1842,7 @@ You can also import an existing raster attribute table from a VAT.DBF file and a Metadata URL - + vectormeta @@ -2030,11 +2046,16 @@ You can also import an existing raster attribute table from a VAT.DBF file and a - QgsCollapsibleGroupBox - QGroupBox -
qgscollapsiblegroupbox.h
+ QgsScrollArea + QScrollArea +
qgsscrollarea.h
1
+ + QgsDoubleSpinBox + QDoubleSpinBox +
qgsdoublespinbox.h
+
QgsColorButton QToolButton @@ -2042,9 +2063,21 @@ You can also import an existing raster attribute table from a VAT.DBF file and a 1 - QgsDoubleSpinBox - QDoubleSpinBox -
qgsdoublespinbox.h
+ QgsSpinBox + QSpinBox +
qgsspinbox.h
+ 1 +
+ + QgsCollapsibleGroupBox + QGroupBox +
qgscollapsiblegroupbox.h
+ 1 +
+ + QgsBlendModeComboBox + QComboBox +
qgsblendmodecombobox.h
QgsFilterLineEdit @@ -2062,18 +2095,6 @@ You can also import an existing raster attribute table from a VAT.DBF file and a QWidget
qgsscalerangewidget.h
- - QgsSpinBox - QSpinBox -
qgsspinbox.h
- 1 -
- - QgsScrollArea - QScrollArea -
qgsscrollarea.h
- 1 -
QgsLayerTreeEmbeddedConfigWidget QWidget @@ -2085,11 +2106,6 @@ You can also import an existing raster attribute table from a VAT.DBF file and a QWidget
qgscodeeditorhtml.h
- - QgsBlendModeComboBox - QComboBox -
qgsblendmodecombobox.h
-
QgsImageSourceLineEdit QWidget diff --git a/src/ui/qgstextformatwidgetbase.ui b/src/ui/qgstextformatwidgetbase.ui index aa7f39ad91e1..6135da442df0 100644 --- a/src/ui/qgstextformatwidgetbase.ui +++ b/src/ui/qgstextformatwidgetbase.ui @@ -13,7 +13,7 @@ Layer Labeling Settings - + 0 @@ -352,7 +352,7 @@
- + :/images/themes/default/propertyicons/labelformatting.svg:/images/themes/default/propertyicons/labelformatting.svg @@ -361,7 +361,7 @@ - + :/images/themes/default/propertyicons/labelbuffer.svg:/images/themes/default/propertyicons/labelbuffer.svg @@ -379,7 +379,7 @@ - + :/images/themes/default/propertyicons/labelbackground.svg:/images/themes/default/propertyicons/labelbackground.svg @@ -388,7 +388,7 @@ - + :/images/themes/default/propertyicons/labelshadow.svg:/images/themes/default/propertyicons/labelshadow.svg @@ -397,7 +397,7 @@ - + :/images/themes/default/propertyicons/labelcallout.svg:/images/themes/default/propertyicons/labelcallout.svg @@ -409,7 +409,7 @@ Callouts - + :/images/themes/default/propertyicons/labelplacement.svg:/images/themes/default/propertyicons/labelplacement.svg @@ -418,7 +418,7 @@ - + :/images/themes/default/propertyicons/render.svg:/images/themes/default/propertyicons/render.svg @@ -486,112 +486,6 @@ 20 - - - Text - - - Text style - - - - :/images/themes/default/propertyicons/labeltext.svg:/images/themes/default/propertyicons/labeltext.svg - - - - - Formatting - - - Formatting - - - - :/images/themes/default/propertyicons/labelformatting.svg:/images/themes/default/propertyicons/labelformatting.svg - - - - - Buffer - - - Buffer - - - - :/images/themes/default/propertyicons/labelbuffer.svg:/images/themes/default/propertyicons/labelbuffer.svg - - - - - Mask - - - - :/images/themes/default/propertyicons/labelmask.svg - - - - - - Background - - - Background - - - - :/images/themes/default/propertyicons/labelbackground.svg:/images/themes/default/propertyicons/labelbackground.svg - - - - - Shadow - - - Shadow - - - - :/images/themes/default/propertyicons/labelshadow.svg:/images/themes/default/propertyicons/labelshadow.svg - - - - - Callouts - - - Callouts - - - - :/images/themes/default/propertyicons/labelcallout.svg:/images/themes/default/propertyicons/labelcallout.svg - - - - - Placement - - - Placement - - - - :/images/themes/default/propertyicons/labelplacement.svg:/images/themes/default/propertyicons/labelplacement.svg - - - - - Rendering - - - Rendering - - - - :/images/themes/default/propertyicons/render.svg:/images/themes/default/propertyicons/render.svg - -
@@ -625,7 +519,7 @@ - 1 + 8 @@ -654,8 +548,8 @@ 0 0 - 485 - 410 + 305 + 307 @@ -1217,7 +1111,7 @@ font-style: italic; 0 0 - 471 + 350 755 @@ -2214,7 +2108,7 @@ font-style: italic; 0 0 - 273 + 276 299 @@ -2560,7 +2454,7 @@ font-style: italic; 0 0 - 273 + 276 284 @@ -2838,7 +2732,7 @@ font-style: italic; 0 0 - 418 + 471 731 @@ -4185,7 +4079,7 @@ font-style: italic; 0 - + General Settings @@ -5868,7 +5762,7 @@ font-style: italic; - + @@ -5882,7 +5776,7 @@ font-style: italic; Features act as obstacles - true + false mChkNoObstacle @@ -5974,8 +5868,8 @@ font-style: italic; 0 0 - 368 - 851 + 471 + 865 @@ -6132,131 +6026,133 @@ This limit is inclusive, that means the label will be displayed on this scale. - - - 6 - - - - - QFrame::NoFrame - - - - 20 - - - 0 + + + + 6 + + + + + QFrame::NoFrame - - 0 + + + 20 + + + 0 + + + 0 + + + 0 + + + 6 + + + + + + 0 + 0 + + + + Labels will not show if smaller than this on screen + + + px + + + Minimum + + + 1 + + + 1000 + + + 3 + + + false + + + + + + + + + + + + + + + 0 + 0 + + + + Labels will not show if larger than this on screen + + + px + + + Maximum + + + 1 + + + 10000 + + + 10000 + + + false + + + + + + + + + + + + + + + + + - - 0 + + + + + + + 0 + 0 + - - 6 + + Pixel size-based visibility (labels in map units) - - - - - 0 - 0 - - - - Labels will not show if smaller than this on screen - - - px - - - Minimum - - - 1 - - - 1000 - - - 3 - - - false - - - - - - - - - - - - - - - 0 - 0 - - - - Labels will not show if larger than this on screen - - - px - - - Maximum - - - 1 - - - 10000 - - - 10000 - - - false - - - - - - - - - - - - - - - - - - - - - - - - - 0 - 0 - - - - Pixel size-based visibility (labels in map units) - - - - + + + + @@ -6303,28 +6199,30 @@ This limit is inclusive, that means the label will be displayed on this scale. - - - 0 - - - - - If checked, then inferior placements are permitted when a label cannot otherwise be placed. For instance, it will permit a line label to be placed horizontal over the center of a line which is otherwise too short to fit the label text. - - - Allow inferior fallback placements - - - - - - - - - - - + + + + 1 + + + + + If checked, then inferior placements are permitted when a label cannot otherwise be placed. For instance, it will permit a line label to be placed horizontal over the center of a line which is otherwise too short to fit the label text. + + + Allow inferior fallback placements + + + + + + + + + + + + @@ -6583,58 +6481,6 @@ This limit is inclusive, that means the label will be displayed on this scale. 8 - - - - Label every part of multi-part features - - - - - - - - - - - - - - Merge connected lines to avoid duplicate labels - - - - - - - QFrame::NoFrame - - - QFrame::Raised - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Only draw labels which fit completely within feature - - - - - - @@ -6677,7 +6523,7 @@ This limit is inclusive, that means the label will be displayed on this scale. - + Suppress labeling of features smaller than @@ -6686,6 +6532,37 @@ This limit is inclusive, that means the label will be displayed on this scale. + + + + QFrame::NoFrame + + + QFrame::Raised + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Only draw labels which fit completely within feature + + + + + + @@ -6752,6 +6629,51 @@ This limit is inclusive, that means the label will be displayed on this scale. + + + + + 0 + 33 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Label every part of multi-part features + + + + + + + + + + + + + + + + + Merge connected lines to avoid duplicate labels + + + @@ -6788,63 +6710,65 @@ This limit is inclusive, that means the label will be displayed on this scale. - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Value - - - - - - - - 0 - 0 - - - - - 16777215 - 16777215 - - - - Qt::StrongFocus - - - - - - - Qt::Horizontal - - - QSizePolicy::Fixed - - - - 0 - 20 - - - - - + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Value + + + + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + Qt::StrongFocus + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 0 + 20 + + + + + + @@ -6945,6 +6869,12 @@ This limit is inclusive, that means the label will be displayed on this scale.qgsstyleitemslistwidget.h 1 + + QgsStackedWidget + QStackedWidget +
qgsstackedwidget.h
+ 1 +
mFieldExpressionWidget @@ -7230,38 +7160,5 @@ This limit is inclusive, that means the label will be displayed on this scale. - - - mOptionsTab - currentChanged(int) - mLabelStackedWidget - setCurrentIndex(int) - - - 164 - 205 - - - 689 - 277 - - - - - mLabelingOptionsListWidget - currentRowChanged(int) - mLabelStackedWidget - setCurrentIndex(int) - - - 34 - 599 - - - 633 - 411 - - - - + diff --git a/src/ui/raster/qgsrasterlabelingwidgetbase.ui b/src/ui/raster/qgsrasterlabelingwidgetbase.ui new file mode 100644 index 000000000000..16e165639f32 --- /dev/null +++ b/src/ui/raster/qgsrasterlabelingwidgetbase.ui @@ -0,0 +1,80 @@ + + + QgsRasterLabelingWidgetBase + + + + 0 + 0 + 572 + 300 + + + + + 6 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + 0 + 0 + + + + + + + + true + + + Configure project labeling rules + + + + :/images/themes/default/mIconLabelingRules.svg:/images/themes/default/mIconLabelingRules.svg + + + + + + + true + + + Automated placement settings (applies to all layers) + + + + :/images/themes/default/mIconAutoPlacementSettings.svg:/images/themes/default/mIconAutoPlacementSettings.svg + + + + + + + + + + + + + + + diff --git a/tests/src/core/testqgsmaprendererjob.cpp b/tests/src/core/testqgsmaprendererjob.cpp index 5f341731d643..40b56d36d89a 100644 --- a/tests/src/core/testqgsmaprendererjob.cpp +++ b/tests/src/core/testqgsmaprendererjob.cpp @@ -1080,7 +1080,7 @@ void TestQgsMapRendererJob::testMapShading() QVERIFY( rasterLayer->isValid() ); static_cast( rasterLayer->elevationProperties() )->setEnabled( true ); rasterLayer->dataProvider()->enableProviderResampling( true ); - rasterLayer->dataProvider()->setZoomedOutResamplingMethod( QgsRasterDataProvider::ResamplingMethod::Cubic ); + rasterLayer->dataProvider()->setZoomedOutResamplingMethod( Qgis::RasterResamplingMethod::Cubic ); std::unique_ptr meshLayer = std::make_unique( TEST_DATA_DIR + QStringLiteral( "/mesh/mesh_shading.nc" ), diff --git a/tests/src/providers/testqgswmsprovider.cpp b/tests/src/providers/testqgswmsprovider.cpp index d736bfcc38f7..48ac8fa4df27 100644 --- a/tests/src/providers/testqgswmsprovider.cpp +++ b/tests/src/providers/testqgswmsprovider.cpp @@ -566,8 +566,8 @@ void TestQgsWmsProvider::testResampling() QVERIFY( layer.dataProvider()->dataType( 1 ) == Qgis::DataType::Float32 ); QVERIFY( layer.dataProvider()->enableProviderResampling( true ) ); - QVERIFY( layer.dataProvider()->setZoomedInResamplingMethod( QgsRasterDataProvider::ResamplingMethod::Cubic ) ); - QVERIFY( layer.dataProvider()->setZoomedOutResamplingMethod( QgsRasterDataProvider::ResamplingMethod::Cubic ) ); + QVERIFY( layer.dataProvider()->setZoomedInResamplingMethod( Qgis::RasterResamplingMethod::Cubic ) ); + QVERIFY( layer.dataProvider()->setZoomedOutResamplingMethod( Qgis::RasterResamplingMethod::Cubic ) ); layer.setResamplingStage( Qgis::RasterResamplingStage::Provider ); std::unique_ptr hillshade = std::make_unique( layer.dataProvider(), 1, 315, 45 ); hillshade->setZFactor( 0.0005 ); diff --git a/tests/src/python/CMakeLists.txt b/tests/src/python/CMakeLists.txt index 4990500675fc..01ee8b5198d2 100644 --- a/tests/src/python/CMakeLists.txt +++ b/tests/src/python/CMakeLists.txt @@ -505,6 +505,7 @@ if (WITH_GUI) ADD_PYTHON_TEST(PyQgsRangeWidgets test_qgsrangewidgets.py) ADD_PYTHON_TEST(PyQgsRasterAttributeTableModel test_qgsrasterattributetablemodel.py) ADD_PYTHON_TEST(PyQgsRasterBandComboBox test_qgsrasterbandcombobox.py) + ADD_PYTHON_TEST(PyQgsRasterLabeling test_qgsrasterlabeling.py) ADD_PYTHON_TEST(PyQgsRasterLayerProperties test_qgsrasterlayerproperties.py) ADD_PYTHON_TEST(PyQgsRasterTransparencyWidget test_qgsrastertransparencywidget.py) ADD_PYTHON_TEST(PyQgsRatioLockButton test_qgsratiolockbutton.py) diff --git a/tests/src/python/test_qgsrasterlabeling.py b/tests/src/python/test_qgsrasterlabeling.py new file mode 100644 index 000000000000..830bce467b61 --- /dev/null +++ b/tests/src/python/test_qgsrasterlabeling.py @@ -0,0 +1,329 @@ +"""QGIS Unit tests for raster labeling + +.. note:: This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. +""" + +import unittest + +from qgis.PyQt.QtCore import QSize +from qgis.PyQt.QtGui import QColor +from qgis.PyQt.QtXml import QDomDocument +from qgis.core import ( + Qgis, + QgsRasterLayerSimpleLabeling, + QgsCoordinateReferenceSystem, + QgsTextFormat, + QgsMapSettings, + QgsRasterLayer, + QgsRectangle, + QgsReadWriteContext, + QgsAbstractRasterLayerLabeling, + QgsBasicNumericFormat, + QgsCurrencyNumericFormat, + QgsPercentageNumericFormat, +) +from qgis.testing import start_app, QgisTestCase + +from utilities import getTestFont + +# Convenience instances in case you may need them +# not used in this test +start_app() + + +class TestQgsRasterLabeling(QgisTestCase): + + @classmethod + def control_path_prefix(cls): + return "raster_labeling" + + def test_raster_labeling(self): + raster_layer = QgsRasterLayer( + self.get_test_data_path("rgb256x256.png").as_posix() + ) + self.assertTrue(raster_layer.isValid()) + self.assertFalse(raster_layer.labelsEnabled()) + self.assertIsNone(raster_layer.labeling()) + raster_layer.setLabelsEnabled(True) + # no labeling, so not enabled + self.assertFalse(raster_layer.labelsEnabled()) + raster_layer.setLabelsEnabled(False) + + labeling = QgsRasterLayerSimpleLabeling() + raster_layer.setLabeling(labeling) + self.assertEqual(raster_layer.labeling(), labeling) + self.assertFalse(raster_layer.labelsEnabled()) + + raster_layer.setLabelsEnabled(True) + self.assertTrue(raster_layer.labelsEnabled()) + + doc = QDomDocument() + context = QgsReadWriteContext() + element = doc.createElement("maplayer") + raster_layer.writeLayerXml(element, doc, context) + + raster_layer2 = QgsRasterLayer( + self.get_test_data_path("rgb256x256.png").as_posix() + ) + raster_layer2.readLayerXml(element, context) + + self.assertIsInstance(raster_layer2.labeling(), QgsRasterLayerSimpleLabeling) + self.assertTrue(raster_layer2.labelsEnabled()) + + def test_simple_labeling(self): + labeling = QgsRasterLayerSimpleLabeling() + labeling.setBand(3) + text_format = QgsTextFormat() + text_format.setSize(33) + labeling.setTextFormat(text_format) + labeling.setNumericFormat(QgsCurrencyNumericFormat()) + labeling.setPriority(0.2) + labeling.placementSettings().setOverlapHandling( + Qgis.LabelOverlapHandling.AllowOverlapIfRequired + ) + labeling.thinningSettings().setLimitNumberLabelsEnabled(True) + labeling.thinningSettings().setMaximumNumberLabels(123) + labeling.thinningSettings().setMinimumFeatureSize(16) + labeling.setZIndex(22) + labeling.setMaximumScale(12345) + labeling.setMinimumScale(123456) + labeling.setScaleBasedVisibility(True) + labeling.setResampleOver(3) + labeling.setResampleMethod(Qgis.RasterResamplingMethod.CubicSpline) + + self.assertEqual(labeling.band(), 3) + self.assertEqual(labeling.textFormat().size(), 33) + self.assertIsInstance(labeling.numericFormat(), QgsCurrencyNumericFormat) + self.assertEqual(labeling.priority(), 0.2) + self.assertEqual( + labeling.placementSettings().overlapHandling(), + Qgis.LabelOverlapHandling.AllowOverlapIfRequired, + ) + self.assertEqual(labeling.thinningSettings().limitNumberOfLabelsEnabled(), True) + self.assertEqual(labeling.thinningSettings().maximumNumberLabels(), 123) + self.assertEqual(labeling.thinningSettings().minimumFeatureSize(), 16) + self.assertEqual(labeling.zIndex(), 22) + self.assertEqual(labeling.maximumScale(), 12345) + self.assertEqual(labeling.minimumScale(), 123456) + self.assertEqual(labeling.hasScaleBasedVisibility(), True) + self.assertEqual(labeling.resampleOver(), 3) + self.assertEqual( + labeling.resampleMethod(), Qgis.RasterResamplingMethod.CubicSpline + ) + + labeling_clone = labeling.clone() + self.assertIsInstance(labeling_clone, QgsRasterLayerSimpleLabeling) + self.assertEqual(labeling_clone.band(), 3) + self.assertEqual(labeling_clone.textFormat().size(), 33) + self.assertIsInstance(labeling_clone.numericFormat(), QgsCurrencyNumericFormat) + self.assertEqual(labeling_clone.priority(), 0.2) + self.assertEqual( + labeling_clone.placementSettings().overlapHandling(), + Qgis.LabelOverlapHandling.AllowOverlapIfRequired, + ) + self.assertEqual( + labeling_clone.thinningSettings().limitNumberOfLabelsEnabled(), True + ) + self.assertEqual(labeling_clone.thinningSettings().maximumNumberLabels(), 123) + self.assertEqual(labeling_clone.thinningSettings().minimumFeatureSize(), 16) + self.assertEqual(labeling_clone.zIndex(), 22) + self.assertEqual(labeling_clone.maximumScale(), 12345) + self.assertEqual(labeling_clone.minimumScale(), 123456) + self.assertEqual(labeling_clone.hasScaleBasedVisibility(), True) + self.assertEqual(labeling_clone.resampleOver(), 3) + self.assertEqual( + labeling_clone.resampleMethod(), Qgis.RasterResamplingMethod.CubicSpline + ) + + doc = QDomDocument() + context = QgsReadWriteContext() + element = labeling.save(doc, context) + + labeling_from_xml = QgsAbstractRasterLayerLabeling.createFromElement( + element, context + ) + self.assertIsInstance(labeling_from_xml, QgsRasterLayerSimpleLabeling) + self.assertEqual(labeling_from_xml.band(), 3) + self.assertEqual(labeling_from_xml.textFormat().size(), 33) + self.assertIsInstance( + labeling_from_xml.numericFormat(), QgsCurrencyNumericFormat + ) + self.assertEqual(labeling_from_xml.priority(), 0.2) + self.assertEqual( + labeling_from_xml.placementSettings().overlapHandling(), + Qgis.LabelOverlapHandling.AllowOverlapIfRequired, + ) + self.assertEqual( + labeling_from_xml.thinningSettings().limitNumberOfLabelsEnabled(), True + ) + self.assertEqual( + labeling_from_xml.thinningSettings().maximumNumberLabels(), 123 + ) + self.assertEqual(labeling_from_xml.thinningSettings().minimumFeatureSize(), 16) + self.assertEqual(labeling_from_xml.zIndex(), 22) + self.assertEqual(labeling_from_xml.maximumScale(), 12345) + self.assertEqual(labeling_from_xml.minimumScale(), 123456) + self.assertEqual(labeling_from_xml.hasScaleBasedVisibility(), True) + self.assertEqual(labeling_from_xml.resampleOver(), 3) + self.assertEqual( + labeling_from_xml.resampleMethod(), Qgis.RasterResamplingMethod.CubicSpline + ) + + def test_render_int(self): + raster_layer = QgsRasterLayer( + self.get_test_data_path("raster/byte.tif").as_posix() + ) + self.assertTrue(raster_layer.isValid()) + + format = QgsTextFormat() + format.setFont(getTestFont("bold")) + format.setColor(QColor(255, 0, 0)) + format.setSize(30) + labeling = QgsRasterLayerSimpleLabeling() + labeling.setBand(1) + labeling.setTextFormat(format) + + raster_layer.setLabeling(labeling) + raster_layer.setLabelsEnabled(True) + + mapsettings = QgsMapSettings() + mapsettings.setOutputSize(QSize(400, 400)) + mapsettings.setOutputDpi(96) + mapsettings.setDestinationCrs(QgsCoordinateReferenceSystem("EPSG:3857")) + transform_context = mapsettings.transformContext() + transform_context.addCoordinateOperation( + raster_layer.crs(), + QgsCoordinateReferenceSystem("EPSG:3857"), + "+proj=pipeline +step +inv +proj=utm +zone=11 +ellps=clrk66 +step +proj=push +v_3 +step +proj=cart +ellps=clrk66 +step +proj=helmert +x=-10 +y=158 +z=187 +step +inv +proj=cart +ellps=WGS84 +step +proj=pop +v_3 +step +proj=webmerc +lat_0=0 +lon_0=0 +x_0=0 +y_0=0 +ellps=WGS84", + ) + mapsettings.setTransformContext(transform_context) + mapsettings.setExtent( + QgsRectangle(-13095009.8, 4014898.9, -13094808.4, 4015061.7) + ) + mapsettings.setLayers([raster_layer]) + + self.assertTrue( + self.render_map_settings_check("labeling_int", "labeling_int", mapsettings) + ) + + def test_render_rotated_map(self): + raster_layer = QgsRasterLayer( + self.get_test_data_path("raster/byte.tif").as_posix() + ) + self.assertTrue(raster_layer.isValid()) + + format = QgsTextFormat() + format.setFont(getTestFont("bold")) + format.setColor(QColor(255, 0, 0)) + format.setSize(30) + labeling = QgsRasterLayerSimpleLabeling() + labeling.setBand(1) + labeling.setTextFormat(format) + + raster_layer.setLabeling(labeling) + raster_layer.setLabelsEnabled(True) + + mapsettings = QgsMapSettings() + mapsettings.setOutputSize(QSize(400, 400)) + mapsettings.setOutputDpi(96) + mapsettings.setRotation(45) + mapsettings.setDestinationCrs(QgsCoordinateReferenceSystem("EPSG:3857")) + transform_context = mapsettings.transformContext() + transform_context.addCoordinateOperation( + raster_layer.crs(), + QgsCoordinateReferenceSystem("EPSG:3857"), + "+proj=pipeline +step +inv +proj=utm +zone=11 +ellps=clrk66 +step +proj=push +v_3 +step +proj=cart +ellps=clrk66 +step +proj=helmert +x=-10 +y=158 +z=187 +step +inv +proj=cart +ellps=WGS84 +step +proj=pop +v_3 +step +proj=webmerc +lat_0=0 +lon_0=0 +x_0=0 +y_0=0 +ellps=WGS84", + ) + mapsettings.setTransformContext(transform_context) + mapsettings.setExtent( + QgsRectangle(-13095009.8, 4014898.9, -13094808.4, 4015061.7) + ) + mapsettings.setLayers([raster_layer]) + + self.assertTrue( + self.render_map_settings_check( + "labeling_rotated", "labeling_rotated", mapsettings + ) + ) + + def test_render_numeric_format(self): + raster_layer = QgsRasterLayer( + self.get_test_data_path("raster/byte.tif").as_posix() + ) + self.assertTrue(raster_layer.isValid()) + + format = QgsTextFormat() + format.setFont(getTestFont("bold")) + format.setColor(QColor(255, 0, 0)) + format.setSize(24) + labeling = QgsRasterLayerSimpleLabeling() + labeling.setBand(1) + labeling.setTextFormat(format) + + labeling.setNumericFormat(QgsPercentageNumericFormat()) + + raster_layer.setLabeling(labeling) + raster_layer.setLabelsEnabled(True) + + mapsettings = QgsMapSettings() + transform_context = mapsettings.transformContext() + transform_context.addCoordinateOperation( + raster_layer.crs(), + QgsCoordinateReferenceSystem("EPSG:3857"), + "+proj=pipeline +step +inv +proj=utm +zone=11 +ellps=clrk66 +step +proj=push +v_3 +step +proj=cart +ellps=clrk66 +step +proj=helmert +x=-10 +y=158 +z=187 +step +inv +proj=cart +ellps=WGS84 +step +proj=pop +v_3 +step +proj=webmerc +lat_0=0 +lon_0=0 +x_0=0 +y_0=0 +ellps=WGS84", + ) + mapsettings.setTransformContext(transform_context) + mapsettings.setOutputSize(QSize(400, 400)) + mapsettings.setOutputDpi(96) + mapsettings.setDestinationCrs(QgsCoordinateReferenceSystem("EPSG:3857")) + mapsettings.setExtent( + QgsRectangle(-13095009.8, 4014898.9, -13094808.4, 4015061.7) + ) + mapsettings.setLayers([raster_layer]) + + self.assertTrue( + self.render_map_settings_check( + "numeric_format", "numeric_format", mapsettings + ) + ) + + def test_render_resampled(self): + raster_layer = QgsRasterLayer( + self.get_test_data_path("raster/dem.tif").as_posix() + ) + self.assertTrue(raster_layer.isValid()) + + format = QgsTextFormat() + format.setFont(getTestFont("bold")) + format.setColor(QColor(255, 0, 0)) + format.setSize(30) + labeling = QgsRasterLayerSimpleLabeling() + labeling.setBand(1) + labeling.setTextFormat(format) + labeling.setResampleOver(4) + labeling.setResampleMethod(Qgis.RasterResamplingMethod.Average) + + numeric_format = QgsBasicNumericFormat() + numeric_format.setNumberDecimalPlaces(1) + labeling.setNumericFormat(numeric_format) + + raster_layer.setLabeling(labeling) + raster_layer.setLabelsEnabled(True) + + mapsettings = QgsMapSettings() + mapsettings.setOutputSize(QSize(800, 800)) + mapsettings.setOutputDpi(96) + mapsettings.setDestinationCrs(QgsCoordinateReferenceSystem("EPSG:3857")) + mapsettings.setExtent(QgsRectangle(2080356.7, 5746858.1, 2080585.6, 5747055.3)) + mapsettings.setLayers([raster_layer]) + + self.assertTrue( + self.render_map_settings_check("resampling", "resampling", mapsettings) + ) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/testdata/control_images/raster_labeling/expected_labeling_int/expected_labeling_int.png b/tests/testdata/control_images/raster_labeling/expected_labeling_int/expected_labeling_int.png new file mode 100644 index 000000000000..8751728075d5 Binary files /dev/null and b/tests/testdata/control_images/raster_labeling/expected_labeling_int/expected_labeling_int.png differ diff --git a/tests/testdata/control_images/raster_labeling/expected_labeling_int/expected_labeling_int_mask.png b/tests/testdata/control_images/raster_labeling/expected_labeling_int/expected_labeling_int_mask.png new file mode 100644 index 000000000000..ad41b1a4d616 Binary files /dev/null and b/tests/testdata/control_images/raster_labeling/expected_labeling_int/expected_labeling_int_mask.png differ diff --git a/tests/testdata/control_images/raster_labeling/expected_labeling_rotated/expected_labeling_rotated.png b/tests/testdata/control_images/raster_labeling/expected_labeling_rotated/expected_labeling_rotated.png new file mode 100644 index 000000000000..2ec1934cf272 Binary files /dev/null and b/tests/testdata/control_images/raster_labeling/expected_labeling_rotated/expected_labeling_rotated.png differ diff --git a/tests/testdata/control_images/raster_labeling/expected_labeling_rotated/expected_labeling_rotated_mask.png b/tests/testdata/control_images/raster_labeling/expected_labeling_rotated/expected_labeling_rotated_mask.png new file mode 100644 index 000000000000..ef2095039b35 Binary files /dev/null and b/tests/testdata/control_images/raster_labeling/expected_labeling_rotated/expected_labeling_rotated_mask.png differ diff --git a/tests/testdata/control_images/raster_labeling/expected_numeric_format/expected_numeric_format.png b/tests/testdata/control_images/raster_labeling/expected_numeric_format/expected_numeric_format.png new file mode 100644 index 000000000000..14c1992a3d8c Binary files /dev/null and b/tests/testdata/control_images/raster_labeling/expected_numeric_format/expected_numeric_format.png differ diff --git a/tests/testdata/control_images/raster_labeling/expected_numeric_format/expected_numeric_format_mask.png b/tests/testdata/control_images/raster_labeling/expected_numeric_format/expected_numeric_format_mask.png new file mode 100644 index 000000000000..f3e79ccb8826 Binary files /dev/null and b/tests/testdata/control_images/raster_labeling/expected_numeric_format/expected_numeric_format_mask.png differ diff --git a/tests/testdata/control_images/raster_labeling/expected_resampling/expected_resampling.png b/tests/testdata/control_images/raster_labeling/expected_resampling/expected_resampling.png new file mode 100644 index 000000000000..aaf9ee79ed7a Binary files /dev/null and b/tests/testdata/control_images/raster_labeling/expected_resampling/expected_resampling.png differ diff --git a/tests/testdata/control_images/raster_labeling/expected_resampling/expected_resampling_mask.png b/tests/testdata/control_images/raster_labeling/expected_resampling/expected_resampling_mask.png new file mode 100644 index 000000000000..3932988340e9 Binary files /dev/null and b/tests/testdata/control_images/raster_labeling/expected_resampling/expected_resampling_mask.png differ