QGIS API Documentation 3.39.0-Master (67e056379ed)
Loading...
Searching...
No Matches
qgsalgorithmroundrastervalues.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsalgorithmroundrastervalues.cpp
3 ---------------------
4 begin : April 2020
5 copyright : (C) 2020 by Clemens Raffler
6 email : clemens dot raffler at gmail dot com
7 ***************************************************************************/
8
9/***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
17
19#include "qgsrasterfilewriter.h"
20
22
23QString QgsRoundRasterValuesAlgorithm::name() const
24{
25 return QStringLiteral( "roundrastervalues" );
26}
27
28QString QgsRoundRasterValuesAlgorithm::displayName() const
29{
30 return QObject::tr( "Round raster" );
31}
32
33QStringList QgsRoundRasterValuesAlgorithm::tags() const
34{
35 return QObject::tr( "data,cells,round,truncate" ).split( ',' );
36}
37
38QString QgsRoundRasterValuesAlgorithm::group() const
39{
40 return QObject::tr( "Raster analysis" );
41}
42
43QString QgsRoundRasterValuesAlgorithm::groupId() const
44{
45 return QStringLiteral( "rasteranalysis" );
46}
47
48void QgsRoundRasterValuesAlgorithm::initAlgorithm( const QVariantMap & )
49{
50 addParameter( new QgsProcessingParameterRasterLayer( QStringLiteral( "INPUT" ), QStringLiteral( "Input raster" ) ) );
51 addParameter( new QgsProcessingParameterBand( QStringLiteral( "BAND" ), QObject::tr( "Band number" ), 1, QStringLiteral( "INPUT" ) ) );
52 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 ) );
53 addParameter( new QgsProcessingParameterNumber( QStringLiteral( "DECIMAL_PLACES" ), QObject::tr( "Number of decimals places" ), Qgis::ProcessingNumberParameterType::Integer, 2 ) );
54 std::unique_ptr< QgsProcessingParameterDefinition > baseParameter = std::make_unique< QgsProcessingParameterNumber >( QStringLiteral( "BASE_N" ), QObject::tr( "Base n for rounding to multiples of n" ), Qgis::ProcessingNumberParameterType::Integer, 10, true, 1 );
55 baseParameter->setFlags( Qgis::ProcessingParameterFlag::Advanced );
56 addParameter( baseParameter.release() );
57 std::unique_ptr< QgsProcessingParameterString > createOptsParam = std::make_unique< QgsProcessingParameterString >( QStringLiteral( "CREATE_OPTIONS" ), QObject::tr( "Creation options" ), QVariant(), false, true );
58 createOptsParam->setMetadata( QVariantMap( {{QStringLiteral( "widget_wrapper" ), QVariantMap( {{QStringLiteral( "widget_type" ), QStringLiteral( "rasteroptions" ) }} ) }} ) );
59 createOptsParam->setFlags( createOptsParam->flags() | Qgis::ProcessingParameterFlag::Advanced );
60 addParameter( createOptsParam.release() );
61
62 addParameter( new QgsProcessingParameterRasterDestination( QStringLiteral( "OUTPUT" ), QObject::tr( "Output raster" ) ) );
63}
64
65QString QgsRoundRasterValuesAlgorithm::shortHelpString() const
66{
67 return QObject::tr( "This algorithm rounds the cell values of a raster dataset according to the specified number of decimals.\n "
68 "Alternatively, a negative number of decimal places may be used to round values to powers of a base n "
69 "(specified in the advanced parameter Base n). For example, with a Base value n of 10 and Decimal places of -1 "
70 "the algorithm rounds cell values to multiples of 10, -2 rounds to multiples of 100, and so on. Arbitrary base values "
71 "may be chosen, the algorithm applies the same multiplicative principle. Rounding cell values to multiples of "
72 "a base n may be used to generalize raster layers.\n"
73 "The algorithm preserves the data type of the input raster. Therefore byte/integer rasters can only be rounded "
74 "to multiples of a base n, otherwise a warning is raised and the raster gets copied as byte/integer raster" );
75}
76
77QgsRoundRasterValuesAlgorithm *QgsRoundRasterValuesAlgorithm::createInstance() const
78{
79 return new QgsRoundRasterValuesAlgorithm();
80}
81
82bool QgsRoundRasterValuesAlgorithm::prepareAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
83{
84 Q_UNUSED( feedback );
85 QgsRasterLayer *inputRaster = parameterAsRasterLayer( parameters, QStringLiteral( "INPUT" ), context );
86 mDecimalPrecision = parameterAsInt( parameters, QStringLiteral( "DECIMAL_PLACES" ), context );
87 mBaseN = parameterAsInt( parameters, QStringLiteral( "BASE_N" ), context );
88 mMultipleOfBaseN = pow( mBaseN, abs( mDecimalPrecision ) );
89 mScaleFactor = std::pow( 10.0, mDecimalPrecision );
90
91 if ( !inputRaster )
92 throw QgsProcessingException( invalidRasterError( parameters, QStringLiteral( "INPUT" ) ) );
93
94 mBand = parameterAsInt( parameters, QStringLiteral( "BAND" ), context );
95 if ( mBand < 1 || mBand > inputRaster->bandCount() )
96 throw QgsProcessingException( QObject::tr( "Invalid band number for BAND (%1): Valid values for input raster are 1 to %2" ).arg( mBand ).arg( inputRaster->bandCount() ) );
97
98 mRoundingDirection = parameterAsEnum( parameters, QStringLiteral( "ROUNDING_DIRECTION" ), context );
99
100 mInterface.reset( inputRaster->dataProvider()->clone() );
101 mDataType = mInterface->dataType( mBand );
102
103 switch ( mDataType )
104 {
110 mIsInteger = true;
111 if ( mDecimalPrecision > -1 )
112 feedback->reportError( QObject::tr( "Input raster is of byte or integer type. The cell values cannot be rounded and will be output using the same data type." ), false );
113 break;
114 default:
115 mIsInteger = false;
116 break;
117 }
118
119 mInputNoDataValue = inputRaster->dataProvider()->sourceNoDataValue( mBand );
120 mExtent = inputRaster->extent();
121 mLayerWidth = inputRaster->width();
122 mLayerHeight = inputRaster->height();
123 mCrs = inputRaster->crs();
124 mNbCellsXProvider = mInterface->xSize();
125 mNbCellsYProvider = mInterface->ySize();
126 return true;
127}
128
129QVariantMap QgsRoundRasterValuesAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
130{
131 //prepare output dataset
132 const QString createOptions = parameterAsString( parameters, QStringLiteral( "CREATE_OPTIONS" ), context ).trimmed();
133 const QString outputFile = parameterAsOutputLayer( parameters, QStringLiteral( "OUTPUT" ), context );
134 const QFileInfo fi( outputFile );
135 const QString outputFormat = QgsRasterFileWriter::driverForExtension( fi.suffix() );
136 std::unique_ptr< QgsRasterFileWriter > writer = std::make_unique< QgsRasterFileWriter >( outputFile );
137 writer->setOutputProviderKey( QStringLiteral( "gdal" ) );
138 if ( !createOptions.isEmpty() )
139 {
140 writer->setCreateOptions( createOptions.split( '|' ) );
141 }
142 writer->setOutputFormat( outputFormat );
143 std::unique_ptr<QgsRasterDataProvider > provider( writer->createOneBandRaster( mInterface->dataType( mBand ), mNbCellsXProvider, mNbCellsYProvider, mExtent, mCrs ) );
144 if ( !provider )
145 throw QgsProcessingException( QObject::tr( "Could not create raster output: %1" ).arg( outputFile ) );
146 if ( !provider->isValid() )
147 throw QgsProcessingException( QObject::tr( "Could not create raster output %1: %2" ).arg( outputFile, provider->error().message( QgsErrorMessage::Text ) ) );
148
149 //prepare output provider
150 QgsRasterDataProvider *destinationRasterProvider;
151 destinationRasterProvider = provider.get();
152 destinationRasterProvider->setEditable( true );
153 destinationRasterProvider->setNoDataValue( 1, mInputNoDataValue );
154
157 const int nbBlocksWidth = static_cast< int >( std::ceil( 1.0 * mLayerWidth / maxWidth ) );
158 const int nbBlocksHeight = static_cast< int >( std::ceil( 1.0 * mLayerHeight / maxHeight ) );
159 const int nbBlocks = nbBlocksWidth * nbBlocksHeight;
160
161 QgsRasterIterator iter( mInterface.get() );
162 iter.startRasterRead( mBand, mLayerWidth, mLayerHeight, mExtent );
163 int iterLeft = 0;
164 int iterTop = 0;
165 int iterCols = 0;
166 int iterRows = 0;
167 std::unique_ptr< QgsRasterBlock > analysisRasterBlock;
168 while ( iter.readNextRasterPart( mBand, iterCols, iterRows, analysisRasterBlock, iterLeft, iterTop ) )
169 {
170 if ( feedback )
171 feedback->setProgress( 100 * ( ( iterTop / maxHeight * nbBlocksWidth ) + iterLeft / maxWidth ) / nbBlocks );
172 if ( mIsInteger && mDecimalPrecision > -1 )
173 {
174 //nothing to round, just write raster block
175 analysisRasterBlock->setNoDataValue( mInputNoDataValue );
176 destinationRasterProvider->writeBlock( analysisRasterBlock.get(), mBand, iterLeft, iterTop );
177 }
178 else
179 {
180 for ( int row = 0; row < iterRows; row++ )
181 {
182 if ( feedback && feedback->isCanceled() )
183 break;
184 for ( int column = 0; column < iterCols; column++ )
185 {
186 bool isNoData = false;
187 const double val = analysisRasterBlock->valueAndNoData( row, column, isNoData );
188 if ( isNoData )
189 {
190 analysisRasterBlock->setValue( row, column, mInputNoDataValue );
191 }
192 else
193 {
194 double roundedVal = mInputNoDataValue;
195 if ( mRoundingDirection == 0 && mDecimalPrecision < 0 )
196 {
197 roundedVal = roundUpBaseN( val );
198 }
199 else if ( mRoundingDirection == 0 && mDecimalPrecision > -1 )
200 {
201 const double m = ( val < 0.0 ) ? -1.0 : 1.0;
202 roundedVal = roundUp( val, m );
203 }
204 else if ( mRoundingDirection == 1 && mDecimalPrecision < 0 )
205 {
206 roundedVal = roundNearestBaseN( val );
207 }
208 else if ( mRoundingDirection == 1 && mDecimalPrecision > -1 )
209 {
210 const double m = ( val < 0.0 ) ? -1.0 : 1.0;
211 roundedVal = roundNearest( val, m );
212 }
213 else if ( mRoundingDirection == 2 && mDecimalPrecision < 0 )
214 {
215 roundedVal = roundDownBaseN( val );
216 }
217 else
218 {
219 const double m = ( val < 0.0 ) ? -1.0 : 1.0;
220 roundedVal = roundDown( val, m );
221 }
222 //integer values get automatically cast to double when reading and back to int when writing
223 analysisRasterBlock->setValue( row, column, roundedVal );
224 }
225 }
226 }
227 destinationRasterProvider->writeBlock( analysisRasterBlock.get(), mBand, iterLeft, iterTop );
228 }
229 }
230 destinationRasterProvider->setEditable( false );
231
232 QVariantMap outputs;
233 outputs.insert( QStringLiteral( "OUTPUT" ), outputFile );
234 return outputs;
235}
236
237double QgsRoundRasterValuesAlgorithm::roundNearest( double value, double m )
238{
239 return ( std::round( value * m * mScaleFactor ) / mScaleFactor ) * m;
240}
241
242double QgsRoundRasterValuesAlgorithm::roundUp( double value, double m )
243{
244 return ( std::ceil( value * m * mScaleFactor ) / mScaleFactor ) * m;
245}
246
247double QgsRoundRasterValuesAlgorithm::roundDown( double value, double m )
248{
249 return ( std::floor( value * m * mScaleFactor ) / mScaleFactor ) * m;
250}
251
252double QgsRoundRasterValuesAlgorithm::roundNearestBaseN( double value )
253{
254 return static_cast<double>( mMultipleOfBaseN * round( value / mMultipleOfBaseN ) );
255}
256
257double QgsRoundRasterValuesAlgorithm::roundUpBaseN( double value )
258{
259 return static_cast<double>( mMultipleOfBaseN * ceil( value / mMultipleOfBaseN ) );
260}
261
262double QgsRoundRasterValuesAlgorithm::roundDownBaseN( double value )
263{
264 return static_cast<double>( mMultipleOfBaseN * floor( value / mMultipleOfBaseN ) );
265}
266
@ Int16
Sixteen bit signed integer (qint16)
@ UInt16
Sixteen bit unsigned integer (quint16)
@ Byte
Eight bit unsigned integer (quint8)
@ Int32
Thirty two bit signed integer (qint32)
@ UInt32
Thirty two bit unsigned integer (quint32)
@ Advanced
Parameter is an advanced parameter which should be hidden from users by default.
bool isCanceled() const
Tells whether the operation has been canceled already.
Definition qgsfeedback.h:53
void setProgress(double progress)
Sets the current progress for the feedback object.
Definition qgsfeedback.h:61
virtual QgsRectangle extent() const
Returns the extent of the layer.
QgsCoordinateReferenceSystem crs
Definition qgsmaplayer.h:82
Contains information about the context in which a processing algorithm is executed.
Custom exception class for processing related exceptions.
Base class for providing feedback from a processing algorithm.
virtual void reportError(const QString &error, bool fatalError=false)
Reports that the algorithm encountered an error while executing.
A raster band parameter for Processing algorithms.
An enum based parameter for processing algorithms, allowing for selection from predefined values.
A numeric parameter for processing algorithms.
A raster layer destination parameter, for specifying the destination path for a raster layer created ...
A raster layer parameter for processing algorithms.
Base class for raster data providers.
QgsRasterDataProvider * clone() const override=0
Clone itself, create deep copy.
virtual bool setNoDataValue(int bandNo, double noDataValue)
Set no data value on created dataset.
virtual double sourceNoDataValue(int bandNo) const
Value representing no data value.
bool writeBlock(QgsRasterBlock *block, int band, int xOffset=0, int yOffset=0)
Writes pixel data from a raster block into the provider data source.
virtual bool setEditable(bool enabled)
Turns on/off editing mode of the provider.
static QString driverForExtension(const QString &extension)
Returns the GDAL driver name for a specified file extension.
Iterator for sequentially processing raster cells.
static const int DEFAULT_MAXIMUM_TILE_WIDTH
Default maximum tile width.
static const int DEFAULT_MAXIMUM_TILE_HEIGHT
Default maximum tile height.
Represents a raster layer.
int height() const
Returns the height of the (unclipped) raster.
int bandCount() const
Returns the number of bands in this layer.
QgsRasterDataProvider * dataProvider() override
Returns the source data provider.
int width() const
Returns the width of the (unclipped) raster.