QGIS API Documentation 4.3.0-Master (45633be667c)
Loading...
Searching...
No Matches
qgsalgorithmrescaleraster.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsalgorithmrescaleraster.cpp
3 ---------------------
4 begin : July 2020
5 copyright : (C) 2020 by Alexander Bruy
6 email : alexander dot bruy 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
20#include <limits>
21#include <math.h>
22
23#include "qgsrasterfilewriter.h"
24
25#include <QString>
26
27// this file breaks cppcheck ast parsing
28#define EXCLUDE_CPPCHECK
29#ifdef EXCLUDE_CPPCHECK
30
31using namespace Qt::StringLiterals;
32
34
35QString QgsRescaleRasterAlgorithm::name() const
36{
37 return u"rescaleraster"_s;
38}
39
40QString QgsRescaleRasterAlgorithm::displayName() const
41{
42 return QObject::tr( "Rescale raster" );
43}
44
45QStringList QgsRescaleRasterAlgorithm::tags() const
46{
47 return QObject::tr( "raster,rescale,minimum,maximum,range" ).split( ',' );
48}
49
50QString QgsRescaleRasterAlgorithm::group() const
51{
52 return QObject::tr( "Raster analysis" );
53}
54
55QString QgsRescaleRasterAlgorithm::groupId() const
56{
57 return u"rasteranalysis"_s;
58}
59
60QString QgsRescaleRasterAlgorithm::shortHelpString() const
61{
62 return QObject::tr(
63 "This algorithm rescales a raster layer to a new value range, while preserving the shape "
64 "(distribution) of the raster's histogram (pixel values). Input values "
65 "are mapped using a linear interpolation from the source raster's minimum "
66 "and maximum pixel values to the destination minimum and maximum pixel range.\n\n"
67 "By default the algorithm preserves the original NoData value, but there is "
68 "an option to override it."
69 );
70}
71
72QString QgsRescaleRasterAlgorithm::shortDescription() const
73{
74 return QObject::tr(
75 "Rescales a raster layer to a new value range, while preserving the shape "
76 "(distribution) of the raster's histogram (pixel values)."
77 );
78}
79
80QgsRescaleRasterAlgorithm *QgsRescaleRasterAlgorithm::createInstance() const
81{
82 return new QgsRescaleRasterAlgorithm();
83}
84
85void QgsRescaleRasterAlgorithm::initAlgorithm( const QVariantMap & )
86{
87 addParameter( new QgsProcessingParameterRasterLayer( u"INPUT"_s, u"Input raster"_s ) );
88 addParameter( new QgsProcessingParameterBand( u"BAND"_s, QObject::tr( "Band number" ), 1, u"INPUT"_s ) );
89 addParameter( new QgsProcessingParameterNumber( u"MINIMUM"_s, QObject::tr( "New minimum value" ), Qgis::ProcessingNumberParameterType::Double, 0 ) );
90 addParameter( new QgsProcessingParameterNumber( u"MAXIMUM"_s, QObject::tr( "New maximum value" ), Qgis::ProcessingNumberParameterType::Double, 255 ) );
91 addParameter( new QgsProcessingParameterNumber( u"NODATA"_s, QObject::tr( "New NoData value" ), Qgis::ProcessingNumberParameterType::Double, QVariant(), true ) );
92
93 // backwards compatibility parameter
94 // TODO QGIS 5: remove parameter and related logic
95 auto createOptsParam = std::make_unique<QgsProcessingParameterString>( u"CREATE_OPTIONS"_s, QObject::tr( "Creation options" ), QVariant(), false, true );
96 createOptsParam->setMetadata( QVariantMap( { { u"widget_wrapper"_s, QVariantMap( { { u"widget_type"_s, u"rasteroptions"_s } } ) } } ) );
97 createOptsParam->setFlags( createOptsParam->flags() | Qgis::ProcessingParameterFlag::Hidden );
98 addParameter( createOptsParam.release() );
99
100 auto creationOptsParam = std::make_unique<QgsProcessingParameterString>( u"CREATION_OPTIONS"_s, QObject::tr( "Creation options" ), QVariant(), false, true );
101 creationOptsParam->setMetadata( QVariantMap( { { u"widget_wrapper"_s, QVariantMap( { { u"widget_type"_s, u"rasteroptions"_s } } ) } } ) );
102 creationOptsParam->setFlags( creationOptsParam->flags() | Qgis::ProcessingParameterFlag::Advanced );
103 addParameter( creationOptsParam.release() );
104
105 addParameter( new QgsProcessingParameterRasterDestination( u"OUTPUT"_s, QObject::tr( "Rescaled" ) ) );
106}
107
108bool QgsRescaleRasterAlgorithm::prepareAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
109{
110 Q_UNUSED( feedback );
111
112 QgsRasterLayer *layer = parameterAsRasterLayer( parameters, u"INPUT"_s, context );
113 if ( !layer )
114 throw QgsProcessingException( invalidRasterError( parameters, u"INPUT"_s ) );
115
116 mBand = parameterAsInt( parameters, u"BAND"_s, context );
117 if ( mBand < 1 || mBand > layer->bandCount() )
118 throw QgsProcessingException( QObject::tr( "Invalid band number for BAND (%1): Valid values for input raster are 1 to %2" ).arg( mBand ).arg( layer->bandCount() ) );
119
120 mMinimum = parameterAsDouble( parameters, u"MINIMUM"_s, context );
121 mMaximum = parameterAsDouble( parameters, u"MAXIMUM"_s, context );
122
123 mInterface.reset( layer->dataProvider()->clone() );
124
125 mCrs = layer->crs();
126 mLayerWidth = layer->width();
127 mLayerHeight = layer->height();
128 mExtent = layer->extent();
129 if ( parameters.value( u"NODATA"_s ).isValid() )
130 {
131 mNoData = parameterAsDouble( parameters, u"NODATA"_s, context );
132 }
133 else
134 {
135 mNoData = layer->dataProvider()->sourceNoDataValue( mBand );
136 }
137
138 if ( std::isfinite( mNoData ) )
139 {
140 // Clamp nodata to float32 range, since that's the type of the raster
141 if ( mNoData < std::numeric_limits<float>::lowest() )
142 mNoData = std::numeric_limits<float>::lowest();
143 else if ( mNoData > std::numeric_limits<float>::max() )
144 mNoData = std::numeric_limits<float>::max();
145 }
146
147 mXSize = mInterface->xSize();
148 mYSize = mInterface->ySize();
149
150 return true;
151}
152
153QVariantMap QgsRescaleRasterAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
154{
155 QGS_MARK_ALGORITHM_SOURCE
156
157 feedback->pushInfo( QObject::tr( "Calculating raster minimum and maximum values…" ) );
158 const QgsRasterBandStats stats = mInterface->bandStatistics( mBand, Qgis::RasterBandStatistic::Min | Qgis::RasterBandStatistic::Max, QgsRectangle(), 0 );
159
160 feedback->pushInfo( QObject::tr( "Rescaling values…" ) );
161
162 QString creationOptions = parameterAsString( parameters, u"CREATION_OPTIONS"_s, context ).trimmed();
163 // handle backwards compatibility parameter CREATE_OPTIONS
164 const QString optionsString = parameterAsString( parameters, u"CREATE_OPTIONS"_s, context );
165 if ( !optionsString.isEmpty() )
166 creationOptions = optionsString;
167
168 const QString outputFile = parameterAsOutputLayer( parameters, u"OUTPUT"_s, context );
169 const QString outputFormat = parameterAsOutputRasterFormat( parameters, u"OUTPUT"_s, context );
170 auto writer = std::make_unique<QgsRasterFileWriter>( outputFile );
171 writer->setOutputProviderKey( u"gdal"_s );
172 if ( !creationOptions.isEmpty() )
173 {
174 writer->setCreationOptions( creationOptions.split( '|' ) );
175 }
176
177 writer->setOutputFormat( outputFormat );
178 std::unique_ptr<QgsRasterDataProvider> provider( writer->createOneBandRaster( Qgis::DataType::Float32, mXSize, mYSize, mExtent, mCrs ) );
179 if ( !provider )
180 throw QgsProcessingException( QObject::tr( "Could not create raster output: %1" ).arg( outputFile ) );
181 if ( !provider->isValid() )
182 throw QgsProcessingException( QObject::tr( "Could not create raster output %1: %2" ).arg( outputFile, provider->error().message( QgsErrorMessage::Text ) ) );
183
184 QgsRasterDataProvider *destProvider = provider.get();
185 destProvider->setEditable( true );
186 destProvider->setNoDataValue( 1, mNoData );
187
188 const bool hasReportsDuringClose = provider->hasReportsDuringClose();
189 const double maxProgressDuringBlockWriting = hasReportsDuringClose ? 50.0 : 100.0;
190
191 QgsRasterIterator iter( mInterface.get() );
192 iter.startRasterRead( mBand, mLayerWidth, mLayerHeight, mExtent );
193 int iterLeft = 0;
194 int iterTop = 0;
195 int iterCols = 0;
196 int iterRows = 0;
197 std::unique_ptr<QgsRasterBlock> inputBlock;
198 while ( iter.readNextRasterPart( mBand, iterCols, iterRows, inputBlock, iterLeft, iterTop ) )
199 {
200 auto outputBlock = std::make_unique<QgsRasterBlock>( destProvider->dataType( 1 ), iterCols, iterRows );
201 feedback->setProgress( maxProgressDuringBlockWriting * iter.progress( mBand ) );
202
203 for ( int row = 0; row < iterRows; row++ )
204 {
205 if ( feedback->isCanceled() )
206 break;
207
208 for ( int col = 0; col < iterCols; col++ )
209 {
210 bool isNoData = false;
211 const double val = inputBlock->valueAndNoData( row, col, isNoData );
212 if ( isNoData )
213 {
214 outputBlock->setValue( row, col, mNoData );
215 }
216 else
217 {
218 const double newValue = ( ( val - stats.minimumValue ) * ( mMaximum - mMinimum ) / ( stats.maximumValue - stats.minimumValue ) ) + mMinimum;
219 outputBlock->setValue( row, col, newValue );
220 }
221 }
222 }
223 if ( !destProvider->writeBlock( outputBlock.get(), mBand, iterLeft, iterTop ) )
224 {
225 throw QgsProcessingException( QObject::tr( "Could not write raster block: %1" ).arg( destProvider->error().summary() ) );
226 }
227 }
228 destProvider->setEditable( false );
229
230 if ( hasReportsDuringClose )
231 {
232 std::unique_ptr<QgsFeedback> scaledFeedback( QgsFeedback::createScaledFeedback( feedback, maxProgressDuringBlockWriting, 100.0 ) );
233 if ( !provider->closeWithProgress( scaledFeedback.get() ) )
234 {
235 if ( feedback->isCanceled() )
236 return {};
237 throw QgsProcessingException( QObject::tr( "Could not write raster dataset" ) );
238 }
239 }
240
241 QVariantMap outputs;
242 outputs.insert( u"OUTPUT"_s, outputFile );
243 return outputs;
244}
245
246#endif
247
@ Float32
Thirty two bit floating point (float).
Definition qgis.h:401
@ Hidden
Parameter is hidden and should not be shown to users.
Definition qgis.h:4007
@ Advanced
Parameter is an advanced parameter which should be hidden from users by default.
Definition qgis.h:4006
@ Double
Double/float values.
Definition qgis.h:4047
virtual QgsError error() const
Gets current status error.
@ Text
Plain text format.
Definition qgserror.h:40
QString summary() const
Short error description, usually the first error in chain, the real error.
Definition qgserror.cpp:132
bool isCanceled() const
Tells whether the operation has been canceled already.
Definition qgsfeedback.h:56
void setProgress(double progress)
Sets the current progress for the feedback object.
Definition qgsfeedback.h:65
static std::unique_ptr< QgsFeedback > createScaledFeedback(QgsFeedback *parentFeedback, double startPercentage, double endPercentage)
Returns a feedback object whose [0, 100] progression range will be mapped to parentFeedback [startPer...
virtual Q_INVOKABLE QgsRectangle extent() const
Returns the extent of the layer.
QgsCoordinateReferenceSystem crs
Definition qgsmaplayer.h:90
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 pushInfo(const QString &info)
Pushes a general informational message from the algorithm.
A raster band parameter for Processing algorithms.
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.
The RasterBandStats struct is a container for statistics about a single raster band.
double minimumValue
The minimum cell value in the raster band.
double maximumValue
The maximum cell value in the raster band.
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.
Qgis::DataType dataType(int bandNo) const override=0
Returns data type for the band specified by number.
virtual bool setEditable(bool enabled)
Turns on/off editing mode of the provider.
Iterator for sequentially processing raster cells.
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.
A rectangle specified with double values.