New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[feature] add native Round raster algorithm #35694
Conversation
Isn't that smooth? And also looking at the code, this made me think. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice addition! You've nailed the implementation too (like usual 😉 )
images/images.qrc
Outdated
@@ -116,6 +116,7 @@ | |||
<file>themes/default/algorithms/mAlgorithmRandomPointsWithinPolygon.svg</file> | |||
<file>themes/default/algorithms/mAlgorithmRandomPointsWithinExtent.svg</file> | |||
<file>themes/default/algorithms/mAlgorithmRegularPoints.svg</file> | |||
<file>themes/default/algorithms/mAlgorithmRoundRastervalues.svg</file> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
indentation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed
|
||
QString QgsRoundRasterValuesAlgorithm::group() const | ||
{ | ||
return QObject::tr( "Raster tools" ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure Raster Tools is the right group for this. I think it fits better in the current Raster Analysis group, but ideally we'd split that group into purely analysis tools vs "manipulation" tools.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree. One question on adding new processing algorithm groups between major releases: Is this considered as an API break? I plan to add more raster creation algorithms and would put them in one new group. Is it problematic to regroup algorithms/add new groups between major releases?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's not an API break, I think it's perfectly acceptable for a minor release!
addParameter( new QgsProcessingParameterRasterLayer( QStringLiteral( "INPUT" ), QStringLiteral( "Input raster" ) ) ); | ||
addParameter( new QgsProcessingParameterBand( QStringLiteral( "BAND" ), QObject::tr( "Band number" ), 1, QStringLiteral( "INPUT" ) ) ); | ||
addParameter( new QgsProcessingParameterEnum( QStringLiteral( "ROUNDING_DIRECTION" ), QObject::tr( "Rounding direction" ), QStringList() << QObject::tr( "Round up" ) << QObject::tr( "Round to nearest" ) << QObject::tr( "Round down" ), false, 1 ) ); | ||
addParameter( new QgsProcessingParameterNumber( QStringLiteral( "DECIMAL_PLACES" ), QObject::tr( "Number of decimals places (use negative values to round cell values to a multiple of a base n, see advanced parameters)" ), QgsProcessingParameterNumber::Integer, 2 ) ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This description is very long compared with the usual parameter description. I'd shorten to just "number of decimal places" and leave the full explanation for the help string.
I keep thinking we need api to support extended descriptions for parameters, which we could show in a tooltip...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed - this would be very nice!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed
|
||
QString QgsRoundRasterValuesAlgorithm::shortHelpString() const | ||
{ | ||
return QObject::tr( "This algorithm rounds the cell values of a rasterdataset according to the specified number of decimals.\n " |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
return QObject::tr( "This algorithm rounds the cell values of a rasterdataset according to the specified number of decimals.\n " | |
return QObject::tr( "This algorithm rounds the cell values of a raster dataset according to the specified number of decimals.\n " |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed
"Alternatively, a negative number of decimal places may be used to round values to powers of a base n " | ||
"(specified in the advanced parameter Base n). For example, with a Base value n of 10 and Decimal places of -1 " | ||
"the algorithm rounds cell values to multiples of 10, -2 rounds to multiples of 100, and so on. Arbitrary base values " | ||
"may be chosen, the algorithm applies the same multiplicative principle.Rounding cell values to multiples of " |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"may be chosen, the algorithm applies the same multiplicative principle.Rounding cell values to multiples of " | |
"may be chosen, the algorithm applies the same multiplicative principle. Rounding cell values to multiples of " |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed
double roundUpBaseN( double &value, int &multipleOfBaseN ); | ||
double roundDownBaseN( double &value, int &multipleOfBaseN ); | ||
|
||
QgsRasterLayer *mInputRaster; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Better not to store this as a member -- it potentially risks someone using it from processAlgorithm later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you mean the QgsRasterLayer member variable? If so, it is fixed.
break; | ||
for ( int column = 0; column < iterCols; column++ ) | ||
{ | ||
if ( analysisRasterBlock->isNoData( row, column ) ) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Calling valueAndNoData once here instead of separately calling isNoData and the value is more efficient.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed
double QgsRoundRasterValuesAlgorithm::roundNearest( double &value, int &decimals ) | ||
{ | ||
double m = ( value < 0.0 ) ? -1.0 : 1.0; | ||
double scaleFactor = std::pow( 10.0, decimals ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This scalefactor is getting calculated once per pixel -- it'd be preferable to calculate it once in processAlgorithm and pass it to this function.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, I think you misunderstood me -- it's "scale factor" which is a constant value and should only be calculated once
That's exactly what QgsRasterBlock is |
@nyalldawson thank you for the fast review :) |
@nyalldawson I just find it strange that values must be handled and set one at the time instead of using broadcasting. Though it still might me more efficient in c++ than numpy. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nearly there!
addParameter( new QgsProcessingParameterEnum( QStringLiteral( "ROUNDING_DIRECTION" ), QObject::tr( "Rounding direction" ), QStringList() << QObject::tr( "Round up" ) << QObject::tr( "Round to nearest" ) << QObject::tr( "Round down" ), false, 1 ) ); | ||
addParameter( new QgsProcessingParameterNumber( QStringLiteral( "DECIMAL_PLACES" ), QObject::tr( "Number of decimals places" ), QgsProcessingParameterNumber::Integer, 2 ) ); | ||
addParameter( new QgsProcessingParameterRasterDestination( QStringLiteral( "OUTPUT" ), QObject::tr( "Output raster" ) ) ); | ||
std::unique_ptr< QgsProcessingParameterDefinition > baseParameter = qgis::make_unique< QgsProcessingParameterNumber >( QStringLiteral( "BASE_N" ), QObject::tr( "Base n (provides the base rounding raster values near/up/down to multiples of n when Decimal parameter is negative)" ), QgsProcessingParameterNumber::Integer, 10, true, 1 ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Better shorten this one too
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I condensed the parameter description a little.
double QgsRoundRasterValuesAlgorithm::roundNearest( double &value, int &decimals ) | ||
{ | ||
double m = ( value < 0.0 ) ? -1.0 : 1.0; | ||
double scaleFactor = std::pow( 10.0, decimals ); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, I think you misunderstood me -- it's "scale factor" which is a constant value and should only be calculated once
return ( std::round( value * m * scaleFactor ) / scaleFactor ) * m; | ||
} | ||
|
||
double QgsRoundRasterValuesAlgorithm::roundUp( double &value, double &m, int &decimals ) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These arguments should be passed by value, not reference (by reference is slower then by value for small trivially copied types like int and double)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed
Perfect! |
Description
This PR proposes a new processing algorithm to round Raster values (thanks @nyalldawson, your algorithm list triggered me ;) ). The algorithm is a reimplementation of the FME counterpart RasterCellValueRounder.
The main functionality of the algorithm is either standard up/nearest/down-rounding of floating point cell values or rounding to powers of a user specified base n. By exposing the base as parameter to the user, the algorithm now is even a bit more versatile than the FME counterpart and allows for some interesting raster generalization (see example). The algorithm can be used with rasters of all types (even byte/integer rasters when rounding to powers of n) and outputs the same raster data type, a standard floating point rounding of an integer raster will copy it and raise a warning. A detailed description of the parameters interplay can be found in the algorithm helpstring.
The PR also contains a test of the algorithm.
Example:
Input Raster file
Output raster rounded to multiples of 10
Please feel free to review/comment!