From dd36eb724921d0d2809a18939e19dd4784a2b6fc Mon Sep 17 00:00:00 2001 From: Jean Felder Date: Wed, 20 Nov 2024 16:04:59 +0100 Subject: [PATCH] qgsrasterrenderer: Introduce refresh This is similar to what is achieved in `QgsRasterLayer::refreshRenderer()` to refresh the renderer according to an extent. Contrary to the first one, this method does not perform any GUI update or emit any signal. It is not used at the moment. This will replace the logic to refresh a renderer in the following commits. --- .../raster/qgsmultibandcolorrenderer.sip.in | 1 + .../raster/qgsrasterrenderer.sip.in | 1 + .../raster/qgssinglebandgrayrenderer.sip.in | 1 + .../qgssinglebandpseudocolorrenderer.sip.in | 1 + .../raster/qgsmultibandcolorrenderer.sip.in | 1 + .../raster/qgsrasterrenderer.sip.in | 1 + .../raster/qgssinglebandgrayrenderer.sip.in | 1 + .../qgssinglebandpseudocolorrenderer.sip.in | 1 + src/core/raster/qgsmultibandcolorrenderer.cpp | 40 +++++++++++++++++++ src/core/raster/qgsmultibandcolorrenderer.h | 16 ++++++++ src/core/raster/qgsrasterrenderer.cpp | 11 +++++ src/core/raster/qgsrasterrenderer.h | 11 +++++ src/core/raster/qgssinglebandgrayrenderer.cpp | 20 ++++++++++ src/core/raster/qgssinglebandgrayrenderer.h | 11 +++++ .../qgssinglebandpseudocolorrenderer.cpp | 39 ++++++++++++++++++ .../raster/qgssinglebandpseudocolorrenderer.h | 12 ++++++ 16 files changed, 168 insertions(+) diff --git a/python/PyQt6/core/auto_generated/raster/qgsmultibandcolorrenderer.sip.in b/python/PyQt6/core/auto_generated/raster/qgsmultibandcolorrenderer.sip.in index f85580c5485..e2fc92ae04a 100644 --- a/python/PyQt6/core/auto_generated/raster/qgsmultibandcolorrenderer.sip.in +++ b/python/PyQt6/core/auto_generated/raster/qgsmultibandcolorrenderer.sip.in @@ -129,6 +129,7 @@ Ownership of the enhancement is transferred. virtual void toSld( QDomDocument &doc, QDomElement &element, const QVariantMap &props = QVariantMap() ) const; + private: QgsMultiBandColorRenderer( const QgsMultiBandColorRenderer & ); const QgsMultiBandColorRenderer &operator=( const QgsMultiBandColorRenderer & ); diff --git a/python/PyQt6/core/auto_generated/raster/qgsrasterrenderer.sip.in b/python/PyQt6/core/auto_generated/raster/qgsrasterrenderer.sip.in index c8649e2d4fe..5be053014c4 100644 --- a/python/PyQt6/core/auto_generated/raster/qgsrasterrenderer.sip.in +++ b/python/PyQt6/core/auto_generated/raster/qgsrasterrenderer.sip.in @@ -222,6 +222,7 @@ should be canceled. %End + protected: void _writeXml( QDomDocument &doc, QDomElement &rasterRendererElem ) const; diff --git a/python/PyQt6/core/auto_generated/raster/qgssinglebandgrayrenderer.sip.in b/python/PyQt6/core/auto_generated/raster/qgssinglebandgrayrenderer.sip.in index 40dce2da98b..01304ad39f5 100644 --- a/python/PyQt6/core/auto_generated/raster/qgssinglebandgrayrenderer.sip.in +++ b/python/PyQt6/core/auto_generated/raster/qgssinglebandgrayrenderer.sip.in @@ -107,6 +107,7 @@ Ownership of ``settings`` is transferred. .. versionadded:: 3.18 %End + private: QgsSingleBandGrayRenderer( const QgsSingleBandGrayRenderer & ); const QgsSingleBandGrayRenderer &operator=( const QgsSingleBandGrayRenderer & ); diff --git a/python/PyQt6/core/auto_generated/raster/qgssinglebandpseudocolorrenderer.sip.in b/python/PyQt6/core/auto_generated/raster/qgssinglebandpseudocolorrenderer.sip.in index 1bbc68755ef..e8f055992e2 100644 --- a/python/PyQt6/core/auto_generated/raster/qgssinglebandpseudocolorrenderer.sip.in +++ b/python/PyQt6/core/auto_generated/raster/qgssinglebandpseudocolorrenderer.sip.in @@ -120,6 +120,7 @@ Sets the band used by the renderer. void setClassificationMin( double min ); void setClassificationMax( double max ); + private: QgsSingleBandPseudoColorRenderer( const QgsSingleBandPseudoColorRenderer & ); const QgsSingleBandPseudoColorRenderer &operator=( const QgsSingleBandPseudoColorRenderer & ); diff --git a/python/core/auto_generated/raster/qgsmultibandcolorrenderer.sip.in b/python/core/auto_generated/raster/qgsmultibandcolorrenderer.sip.in index f85580c5485..e2fc92ae04a 100644 --- a/python/core/auto_generated/raster/qgsmultibandcolorrenderer.sip.in +++ b/python/core/auto_generated/raster/qgsmultibandcolorrenderer.sip.in @@ -129,6 +129,7 @@ Ownership of the enhancement is transferred. virtual void toSld( QDomDocument &doc, QDomElement &element, const QVariantMap &props = QVariantMap() ) const; + private: QgsMultiBandColorRenderer( const QgsMultiBandColorRenderer & ); const QgsMultiBandColorRenderer &operator=( const QgsMultiBandColorRenderer & ); diff --git a/python/core/auto_generated/raster/qgsrasterrenderer.sip.in b/python/core/auto_generated/raster/qgsrasterrenderer.sip.in index c8649e2d4fe..5be053014c4 100644 --- a/python/core/auto_generated/raster/qgsrasterrenderer.sip.in +++ b/python/core/auto_generated/raster/qgsrasterrenderer.sip.in @@ -222,6 +222,7 @@ should be canceled. %End + protected: void _writeXml( QDomDocument &doc, QDomElement &rasterRendererElem ) const; diff --git a/python/core/auto_generated/raster/qgssinglebandgrayrenderer.sip.in b/python/core/auto_generated/raster/qgssinglebandgrayrenderer.sip.in index 8aa75b0d569..9f2a05ab963 100644 --- a/python/core/auto_generated/raster/qgssinglebandgrayrenderer.sip.in +++ b/python/core/auto_generated/raster/qgssinglebandgrayrenderer.sip.in @@ -107,6 +107,7 @@ Ownership of ``settings`` is transferred. .. versionadded:: 3.18 %End + private: QgsSingleBandGrayRenderer( const QgsSingleBandGrayRenderer & ); const QgsSingleBandGrayRenderer &operator=( const QgsSingleBandGrayRenderer & ); diff --git a/python/core/auto_generated/raster/qgssinglebandpseudocolorrenderer.sip.in b/python/core/auto_generated/raster/qgssinglebandpseudocolorrenderer.sip.in index 1bbc68755ef..e8f055992e2 100644 --- a/python/core/auto_generated/raster/qgssinglebandpseudocolorrenderer.sip.in +++ b/python/core/auto_generated/raster/qgssinglebandpseudocolorrenderer.sip.in @@ -120,6 +120,7 @@ Sets the band used by the renderer. void setClassificationMin( double min ); void setClassificationMax( double max ); + private: QgsSingleBandPseudoColorRenderer( const QgsSingleBandPseudoColorRenderer & ); const QgsSingleBandPseudoColorRenderer &operator=( const QgsSingleBandPseudoColorRenderer & ); diff --git a/src/core/raster/qgsmultibandcolorrenderer.cpp b/src/core/raster/qgsmultibandcolorrenderer.cpp index cf8ecc82d02..584c61f54db 100644 --- a/src/core/raster/qgsmultibandcolorrenderer.cpp +++ b/src/core/raster/qgsmultibandcolorrenderer.cpp @@ -589,3 +589,43 @@ void QgsMultiBandColorRenderer::toSld( QDomDocument &doc, QDomElement &element, } } } + +bool QgsMultiBandColorRenderer::refresh( const QgsRectangle &extent, const QList &min, const QList &max, bool forceRefresh ) +{ + if ( !needsRefresh( extent ) && !forceRefresh ) + { + return false; + } + + bool refreshed = false; + if ( min.size() >= 3 && max.size() >= 3 ) + { + mLastRectangleUsedByRefreshContrastEnhancementIfNeeded = extent; + + if ( mRedContrastEnhancement && mRedContrastEnhancement->contrastEnhancementAlgorithm() != QgsContrastEnhancement::NoEnhancement && + !std::isnan( min[0] ) && !std::isnan( max[0] ) ) + { + mRedContrastEnhancement->setMinimumValue( min[0] ); + mRedContrastEnhancement->setMaximumValue( max[0] ); + refreshed = true; + } + + if ( mGreenContrastEnhancement && mGreenContrastEnhancement->contrastEnhancementAlgorithm() != QgsContrastEnhancement::NoEnhancement && + !std::isnan( min[1] ) && !std::isnan( max[1] ) ) + { + mGreenContrastEnhancement->setMinimumValue( min[1] ); + mGreenContrastEnhancement->setMaximumValue( max[1] ); + refreshed = true; + } + + if ( mBlueContrastEnhancement && mBlueContrastEnhancement->contrastEnhancementAlgorithm() != QgsContrastEnhancement::NoEnhancement && + !std::isnan( min[2] ) && !std::isnan( max[2] ) ) + { + mBlueContrastEnhancement->setMinimumValue( min[2] ); + mBlueContrastEnhancement->setMaximumValue( max[2] ); + refreshed = true; + } + } + + return refreshed; +} diff --git a/src/core/raster/qgsmultibandcolorrenderer.h b/src/core/raster/qgsmultibandcolorrenderer.h index 462dd5cda0f..6d145f39cf3 100644 --- a/src/core/raster/qgsmultibandcolorrenderer.h +++ b/src/core/raster/qgsmultibandcolorrenderer.h @@ -123,6 +123,22 @@ class CORE_EXPORT QgsMultiBandColorRenderer: public QgsRasterRenderer void toSld( QDomDocument &doc, QDomElement &element, const QVariantMap &props = QVariantMap() ) const override; + /** + * \brief Refreshes the renderer according to the \a min and \a max values associated with the \a extent. + * \a min and \a max size need to be at least 3. + * If \a min or \a max size is greater than 3, the last values are ignored. + * The first value is associated with the red contrast. + * The second value is associated with the green contrast. + * The third value is associated with the blue contrast. + * NaN values are ignored. + * If \a forceRefresh is TRUE, this will force the refresh even if needsRefresh() returns FALSE. + * \returns TRUE if the renderer has been refreshed + * \note not available in Python bindings + * + * \since QGIS 3.42 + */ + bool refresh( const QgsRectangle &extent, const QList &min, const QList &max, bool forceRefresh = false ) override SIP_SKIP; + private: #ifdef SIP_RUN QgsMultiBandColorRenderer( const QgsMultiBandColorRenderer & ); diff --git a/src/core/raster/qgsrasterrenderer.cpp b/src/core/raster/qgsrasterrenderer.cpp index 5b91f4b59be..287d8ad4d0e 100644 --- a/src/core/raster/qgsrasterrenderer.cpp +++ b/src/core/raster/qgsrasterrenderer.cpp @@ -256,3 +256,14 @@ bool QgsRasterRenderer::needsRefresh( const QgsRectangle &extent ) const return false; } + +bool QgsRasterRenderer::refresh( const QgsRectangle &extent, const QList &, const QList &, bool forceRefresh ) +{ + if ( !needsRefresh( extent ) && !forceRefresh ) + { + return false; + } + + mLastRectangleUsedByRefreshContrastEnhancementIfNeeded = extent; + return true; +} diff --git a/src/core/raster/qgsrasterrenderer.h b/src/core/raster/qgsrasterrenderer.h index eb24b8f22c3..f03937f75e9 100644 --- a/src/core/raster/qgsrasterrenderer.h +++ b/src/core/raster/qgsrasterrenderer.h @@ -231,6 +231,16 @@ class CORE_EXPORT QgsRasterRenderer : public QgsRasterInterface */ bool needsRefresh( const QgsRectangle &extent ) const SIP_SKIP; + /** + * \brief Refreshes the renderer according to the \a min and \a max values associated with the \a extent. + * If \a forceRefresh is TRUE, this will force the refresh even if needsRefresh() returns FALSE. + * \returns TRUE if the renderer has been refreshed + * \note not available in Python bindings + * + * \since QGIS 3.42 + */ + virtual bool refresh( const QgsRectangle &extent, const QList &min, const QList &max, bool forceRefresh = false ) SIP_SKIP; + protected: //! Write upper class info into rasterrenderer element (called by writeXml method of subclasses) @@ -262,6 +272,7 @@ class CORE_EXPORT QgsRasterRenderer : public QgsRasterInterface */ QRgb renderColorForNodataPixel() const; + //! To save computations and possible infinite cycle of notifications QgsRectangle mLastRectangleUsedByRefreshContrastEnhancementIfNeeded; private: diff --git a/src/core/raster/qgssinglebandgrayrenderer.cpp b/src/core/raster/qgssinglebandgrayrenderer.cpp index 35c85d9fb7f..3e1a78b787a 100644 --- a/src/core/raster/qgssinglebandgrayrenderer.cpp +++ b/src/core/raster/qgssinglebandgrayrenderer.cpp @@ -458,3 +458,23 @@ void QgsSingleBandGrayRenderer::setLegendSettings( QgsColorRampLegendNodeSetting return; mLegendSettings.reset( settings ); } + +bool QgsSingleBandGrayRenderer::refresh( const QgsRectangle &extent, const QList &min, const QList &max, bool forceRefresh ) +{ + if ( !needsRefresh( extent ) && !forceRefresh ) + { + return false; + } + + bool refreshed = false; + if ( mContrastEnhancement && mContrastEnhancement->contrastEnhancementAlgorithm() != QgsContrastEnhancement::NoEnhancement && + min.size() >= 1 && max.size() >= 1 ) + { + mLastRectangleUsedByRefreshContrastEnhancementIfNeeded = extent; + mContrastEnhancement->setMinimumValue( min[0] ); + mContrastEnhancement->setMaximumValue( max[0] ); + refreshed = true; + } + + return refreshed; +} diff --git a/src/core/raster/qgssinglebandgrayrenderer.h b/src/core/raster/qgssinglebandgrayrenderer.h index bcd11675d24..b620f573537 100644 --- a/src/core/raster/qgssinglebandgrayrenderer.h +++ b/src/core/raster/qgssinglebandgrayrenderer.h @@ -102,6 +102,17 @@ class CORE_EXPORT QgsSingleBandGrayRenderer: public QgsRasterRenderer */ void setLegendSettings( QgsColorRampLegendNodeSettings *settings SIP_TRANSFER ); + /** + * \brief Refreshes the renderer according to the \a min and \a max values associated with the \a extent. + * If \a min or \a max size is greater than 1, the last values are ignored. + * If \a forceRefresh is TRUE, this will force the refresh even if needsRefresh() returns FALSE. + * \returns TRUE if the renderer has been refreshed + * \note not available in Python bindings + * + * \since QGIS 3.42 + */ + bool refresh( const QgsRectangle &extent, const QList &min, const QList &max, bool forceRefresh = false ) override SIP_SKIP; + private: #ifdef SIP_RUN QgsSingleBandGrayRenderer( const QgsSingleBandGrayRenderer & ); diff --git a/src/core/raster/qgssinglebandpseudocolorrenderer.cpp b/src/core/raster/qgssinglebandpseudocolorrenderer.cpp index 66525329f51..faeb463d37c 100644 --- a/src/core/raster/qgssinglebandpseudocolorrenderer.cpp +++ b/src/core/raster/qgssinglebandpseudocolorrenderer.cpp @@ -493,3 +493,42 @@ bool QgsSingleBandPseudoColorRenderer::canCreateRasterAttributeTable() const return true; } +bool QgsSingleBandPseudoColorRenderer::refresh( const QgsRectangle &extent, const QList &min, const QList &max, bool force ) +{ + if ( !needsRefresh( extent ) && !force ) + { + return false; + } + + bool refreshed = false; + if ( min.size() >= 1 && max.size() >= 1 ) + { + mLastRectangleUsedByRefreshContrastEnhancementIfNeeded = extent; + + // Do not overwrite min/max with NaN if they were already set, + // for example when the style was already loaded from a raster attribute table + // in that case we need to respect the style from the attribute table and do + // not perform any reclassification. + bool refreshed = false; + + if ( !std::isnan( min[0] ) ) + { + setClassificationMin( min[0] ); + refreshed = true; + } + + if ( !std::isnan( max[0] ) ) + { + setClassificationMax( max[0] ); + refreshed = true; + } + + QgsColorRampShader *rampShader = dynamic_cast( mShader->rasterShaderFunction() ); + if ( rampShader && refreshed ) + { + rampShader->classifyColorRamp( mBand, extent, input() ); + } + } + + return refreshed; +} diff --git a/src/core/raster/qgssinglebandpseudocolorrenderer.h b/src/core/raster/qgssinglebandpseudocolorrenderer.h index 39d802e20f3..cfaa6973845 100644 --- a/src/core/raster/qgssinglebandpseudocolorrenderer.h +++ b/src/core/raster/qgssinglebandpseudocolorrenderer.h @@ -107,6 +107,18 @@ class CORE_EXPORT QgsSingleBandPseudoColorRenderer: public QgsRasterRenderer void setClassificationMin( double min ); void setClassificationMax( double max ); + /** + * \brief Refreshes the renderer according to the \a min and \a max values associated with the \a extent. + * If \a min or \a max size is greater than 1, the last values are ignored. + * NaN values are ignored. + * If \a forceRefresh is TRUE, this will force the refresh even if needsRefresh() returns FALSE. + * \returns TRUE if the renderer has been refreshed + * \note not available in Python bindings + * + * \since QGIS 3.42 + */ + bool refresh( const QgsRectangle &extent, const QList &min, const QList &max, bool forceRefresh = false ) override SIP_SKIP; + private: #ifdef SIP_RUN QgsSingleBandPseudoColorRenderer( const QgsSingleBandPseudoColorRenderer & );