Skip to content

Commit

Permalink
Support raster labels for resampled values across neighboring pixels
Browse files Browse the repository at this point in the history
  • Loading branch information
nyalldawson committed Dec 9, 2024
1 parent c607dad commit 6d556ba
Show file tree
Hide file tree
Showing 8 changed files with 305 additions and 9 deletions.
40 changes: 40 additions & 0 deletions python/PyQt6/core/auto_generated/raster/qgsrasterlabeling.sip.in
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,46 @@ Returns whether scale based visibility is enabled for the labels.
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

};


Expand Down
40 changes: 40 additions & 0 deletions python/core/auto_generated/raster/qgsrasterlabeling.sip.in
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,46 @@ Returns whether scale based visibility is enabled for the labels.
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

};


Expand Down
92 changes: 86 additions & 6 deletions src/core/raster/qgsrasterlabeling.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,16 @@ QgsNumericFormat *QgsRasterLayerLabelProvider::numericFormat()
return mNumericFormat.get();
}

void QgsRasterLayerLabelProvider::setResampleMethod( Qgis::RasterResamplingMethod method )
{
mResampleMethod = method;
}

void QgsRasterLayerLabelProvider::setResampleOver( int pixels )
{
mResampleOver = pixels;
}

QList<QgsLabelFeature *> QgsRasterLayerLabelProvider::labelFeatures( QgsRenderContext & )
{
return mLabels;
Expand All @@ -125,6 +135,30 @@ void QgsRasterLayerLabelProvider::startRender( QgsRenderContext &context )
QgsAbstractLabelProvider::startRender( context );
}

///@cond PRIVATE
// RAII properties restorer for QgsRasterDataProvider
struct ProviderSettingsRestorer
{
QgsRasterDataProvider *mProvider;
const bool mProviderResampling;
const Qgis::RasterResamplingMethod mZoomedOutMethod;
const double mMaxOversampling;

ProviderSettingsRestorer( QgsRasterDataProvider *provider )
: mProvider( provider )
, mProviderResampling( provider->isProviderResamplingEnabled() )
, mZoomedOutMethod( provider->zoomedOutResamplingMethod() )
, mMaxOversampling( provider->maxOversampling() ) {}

~ProviderSettingsRestorer()
{
mProvider->enableProviderResampling( mProviderResampling );
mProvider->setZoomedOutResamplingMethod( mZoomedOutMethod );
mProvider->setMaxOversampling( mMaxOversampling );
}
};
///@endcond

void QgsRasterLayerLabelProvider::generateLabels( QgsRenderContext &context, QgsRasterPipe *pipe, QgsRasterViewPort *rasterViewPort, QgsRasterLayerRendererFeedback *feedback )
{
if ( !pipe )
Expand All @@ -146,6 +180,7 @@ void QgsRasterLayerLabelProvider::generateLabels( QgsRenderContext &context, Qgs
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();
Expand Down Expand Up @@ -178,8 +213,8 @@ void QgsRasterLayerLabelProvider::generateLabels( QgsRenderContext &context, Qgs
subRegionLeft,
subRegionTop );

const double rasterUnitsPerPixelX = provider->extent().width() / provider->xSize();
const double rasterUnitsPerPixelY = provider->extent().height() / provider->ySize();
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 )
Expand All @@ -201,7 +236,7 @@ void QgsRasterLayerLabelProvider::generateLabels( QgsRenderContext &context, Qgs
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() ) );
std::fabs( p1PainterUnits.y() - p2PainterUnits.y() ) ) * mResampleOver;
if ( painterUnitsPerRasterPixel < minPixelSizePainterUnits )
return;
}
Expand All @@ -219,21 +254,34 @@ void QgsRasterLayerLabelProvider::generateLabels( QgsRenderContext &context, Qgs
std::unique_ptr< QgsRasterBlock > block;
bool isNoData = false;
int numberLabels = 0;
while ( iterator.readNextRasterPart( mBandNumber, iterCols, iterRows, block, iterLeft, iterTop, &blockExtent ) )

ProviderSettingsRestorer 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 < iterRows; row++ )
for ( int row = 0; row < resampledRows; row++ )
{
if ( feedback && feedback->isCanceled() )
return;

double currentX = blockExtent.xMinimum() + 0.5 * rasterUnitsPerPixelX;

for ( int column = 0; column < iterCols; column++ )
for ( int column = 0; column < resampledColumns; column++ )
{
const double value = block->valueAndNoData( row, column, isNoData );
if ( !isNoData )
Expand Down Expand Up @@ -338,6 +386,8 @@ QgsRasterLayerSimpleLabeling *QgsRasterLayerSimpleLabeling::clone() const
res->setScaleBasedVisibility( mScaleVisibility );
res->setMaximumScale( mMaximumScale );
res->setMinimumScale( mMinimumScale );
res->setResampleMethod( mResampleMethod );
res->setResampleOver( mResampleOver );

return res.release();
}
Expand All @@ -351,6 +401,8 @@ std::unique_ptr< QgsRasterLayerLabelProvider > QgsRasterLayerSimpleLabeling::pro
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() ) );
Expand All @@ -366,6 +418,12 @@ QDomElement QgsRasterLayerSimpleLabeling::save( QDomDocument &doc, const QgsRead
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 );
Expand Down Expand Up @@ -419,6 +477,8 @@ QgsRasterLayerSimpleLabeling *QgsRasterLayerSimpleLabeling::create( const QDomEl
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() )
Expand Down Expand Up @@ -518,6 +578,26 @@ bool QgsRasterLayerSimpleLabeling::isInScaleRange( double scale ) const
&& ( 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;
Expand Down
58 changes: 58 additions & 0 deletions src/core/raster/qgsrasterlabeling.h
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,22 @@ class CORE_EXPORT QgsRasterLayerLabelProvider final : public QgsAbstractLabelPro
*/
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;
Expand All @@ -132,6 +148,9 @@ class CORE_EXPORT QgsRasterLayerLabelProvider final : public QgsAbstractLabelPro
QgsLabelThinningSettings mThinningSettings;
double mZIndex = 0;

Qgis::RasterResamplingMethod mResampleMethod = Qgis::RasterResamplingMethod::Average;
int mResampleOver = 1;

QList<QgsLabelFeature *> mLabels;

};
Expand Down Expand Up @@ -464,6 +483,42 @@ class CORE_EXPORT QgsRasterLayerSimpleLabeling : public QgsAbstractRasterLayerLa

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;

Expand All @@ -483,6 +538,9 @@ class CORE_EXPORT QgsRasterLayerSimpleLabeling : public QgsAbstractRasterLayerLa
double mMaximumScale = 0;
double mMinimumScale = 0;

Qgis::RasterResamplingMethod mResampleMethod = Qgis::RasterResamplingMethod::Average;
int mResampleOver = 1;

};


Expand Down
Loading

0 comments on commit 6d556ba

Please sign in to comment.