QGIS API Documentation 4.3.0-Master (ffcfc20b9b4)
Loading...
Searching...
No Matches
qgsalgorithmconstantraster.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsalgorithmconstantraster.cpp
3 ---------------------
4 begin : November 2019
5 copyright : (C) 2019 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
27using namespace Qt::StringLiterals;
28
30
31QString QgsConstantRasterAlgorithm::name() const
32{
33 return u"createconstantrasterlayer"_s;
34}
35
36QString QgsConstantRasterAlgorithm::displayName() const
37{
38 return QObject::tr( "Create constant raster layer" );
39}
40
41QStringList QgsConstantRasterAlgorithm::tags() const
42{
43 return QObject::tr( "raster,create,constant" ).split( ',' );
44}
45
46QString QgsConstantRasterAlgorithm::group() const
47{
48 return QObject::tr( "Raster creation" );
49}
50
51QString QgsConstantRasterAlgorithm::groupId() const
52{
53 return u"rastercreation"_s;
54}
55
56QString QgsConstantRasterAlgorithm::shortHelpString() const
57{
58 return QObject::tr(
59 "This algorithm generates a raster layer for a given extent and cell "
60 "size filled with a single constant value.\n"
61 "Additionally an output data type can be specified. "
62 "The algorithm will abort if a value has been entered that "
63 "cannot be represented by the selected output raster data type."
64 );
65}
66
67QString QgsConstantRasterAlgorithm::shortDescription() const
68{
69 return QObject::tr( "Generates a raster layer for a given extent and cell size filled with a single constant value." );
70}
71
72QgsConstantRasterAlgorithm *QgsConstantRasterAlgorithm::createInstance() const
73{
74 return new QgsConstantRasterAlgorithm();
75}
76
77void QgsConstantRasterAlgorithm::initAlgorithm( const QVariantMap & )
78{
79 addParameter( new QgsProcessingParameterExtent( u"EXTENT"_s, QObject::tr( "Desired extent" ) ) );
80 addParameter( new QgsProcessingParameterCrs( u"TARGET_CRS"_s, QObject::tr( "Target CRS" ), u"ProjectCrs"_s ) );
81 addParameter( new QgsProcessingParameterNumber( u"PIXEL_SIZE"_s, QObject::tr( "Pixel size" ), Qgis::ProcessingNumberParameterType::Double, 1, false, 0 ) );
82 addParameter( new QgsProcessingParameterNumber( u"NUMBER"_s, QObject::tr( "Constant value" ), Qgis::ProcessingNumberParameterType::Double, 1, false ) );
83
84 QStringList rasterDataTypes; //currently supported raster data types that can be handled QgsRasterBlock::writeValue()
85 rasterDataTypes << u"Byte"_s << u"Integer16"_s << u"Unsigned Integer16"_s << u"Integer32"_s << u"Unsigned Integer32"_s << u"Float32"_s << u"Float64"_s;
86
87 //QGIS3: parameter set to Float32 by default so that existing models/scripts don't break
88 std::unique_ptr<QgsProcessingParameterDefinition> rasterTypeParameter
89 = std::make_unique<QgsProcessingParameterEnum>( u"OUTPUT_TYPE"_s, QObject::tr( "Output raster data type" ), rasterDataTypes, false, 5, false );
90 rasterTypeParameter->setFlags( Qgis::ProcessingParameterFlag::Advanced );
91 addParameter( rasterTypeParameter.release() );
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( "Constant" ) ) );
106}
107
108QVariantMap QgsConstantRasterAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
109{
110 QGS_MARK_ALGORITHM_SOURCE
111
112 const QgsCoordinateReferenceSystem crs = parameterAsCrs( parameters, u"TARGET_CRS"_s, context );
113 const QgsRectangle extent = parameterAsExtent( parameters, u"EXTENT"_s, context, crs );
114 const double pixelSize = parameterAsDouble( parameters, u"PIXEL_SIZE"_s, context );
115 const double value = parameterAsDouble( parameters, u"NUMBER"_s, context );
116 const int typeId = parameterAsInt( parameters, u"OUTPUT_TYPE"_s, context );
117
118 if ( pixelSize <= 0 )
119 {
120 throw QgsProcessingException( QObject::tr( "Pixel size must be greater than 0." ) );
121 }
122
123 //implement warning if input float has decimal places but is written to integer raster
124 double fractpart;
125 double intpart;
126 fractpart = abs( std::modf( value, &intpart ) ); //@abs: negative values may be entered
127
128 Qgis::DataType rasterDataType = Qgis::DataType::Float32; //standard output type
129 switch ( typeId )
130 {
131 case 0:
132 rasterDataType = Qgis::DataType::Byte;
133 if ( value < std::numeric_limits<quint8>::min() || value > std::numeric_limits<quint8>::max() )
135 QObject::tr( "Raster datasets of type %3 only accept positive values between %1 and %2" ).arg( std::numeric_limits<quint8>::min() ).arg( std::numeric_limits<quint8>::max() ).arg( "Byte"_L1 )
136 );
137 if ( fractpart > 0 )
138 feedback->reportError(
139 QObject::tr( "The entered constant value has decimals but will be written to a raster dataset of type %1. The decimals of the constant value will be omitted." ).arg( "Byte"_L1 )
140 );
141 break;
142 case 1:
143 rasterDataType = Qgis::DataType::Int16;
144 if ( value < std::numeric_limits<qint16>::min() || value > std::numeric_limits<qint16>::max() )
146 QObject::tr( "Raster datasets of type %3 only accept values between %1 and %2" ).arg( std::numeric_limits<qint16>::min() ).arg( std::numeric_limits<qint16>::max() ).arg( "Integer16"_L1 )
147 );
148 if ( fractpart > 0 )
149 feedback->reportError(
150 QObject::tr( "The entered constant value has decimals but will be written to a raster dataset of type %1. The decimals of the constant value will be omitted." ).arg( "Integer16"_L1 )
151 );
152 break;
153 case 2:
154 rasterDataType = Qgis::DataType::UInt16;
155 if ( value < std::numeric_limits<quint16>::min() || value > std::numeric_limits<quint16>::max() )
157 QObject::tr( "Raster datasets of type %3 only accept positive values between %1 and %2" ).arg( std::numeric_limits<quint16>::min() ).arg( std::numeric_limits<quint16>::max() ).arg( "Unsigned Integer16" )
158 );
159 if ( fractpart > 0 )
160 feedback->reportError(
161 QObject::tr( "The entered constant value has decimals but will be written to a raster dataset of type %1. The decimals of the constant value will be omitted." ).arg( "Unsigned Integer16"_L1 )
162 );
163 break;
164 case 3:
165 rasterDataType = Qgis::DataType::Int32;
166 if ( value < std::numeric_limits<qint32>::min() || value > std::numeric_limits<qint32>::max() )
168 QObject::tr( "Raster datasets of type %3 only accept values between %1 and %2" ).arg( std::numeric_limits<qint32>::min() ).arg( std::numeric_limits<qint32>::max() ).arg( "Integer32"_L1 )
169 );
170 if ( fractpart > 0 )
171 feedback->reportError(
172 QObject::tr( "The entered constant value has decimals but will be written to a raster dataset of type %1. The decimals of the constant value will be omitted." ).arg( "Integer32"_L1 )
173 );
174 break;
175 case 4:
176 rasterDataType = Qgis::DataType::UInt32;
177 if ( value < std::numeric_limits<quint32>::min() || value > std::numeric_limits<quint32>::max() )
179 QObject::tr( "Raster datasets of type %3 only accept positive values between %1 and %2" ).arg( std::numeric_limits<quint32>::min() ).arg( std::numeric_limits<quint32>::max() ).arg( "Unsigned Integer32"_L1 )
180 );
181 if ( fractpart > 0 )
182 feedback->reportError(
183 QObject::tr( "The entered constant value has decimals but will be written to a raster dataset of type %1. The decimals of the constant value will be omitted." ).arg( "Unsigned Integer32"_L1 )
184 );
185 break;
186 case 5:
187 rasterDataType = Qgis::DataType::Float32;
188 break;
189 case 6:
190 rasterDataType = Qgis::DataType::Float64;
191 break;
192 default:
193 break;
194 }
195
196 QString creationOptions = parameterAsString( parameters, u"CREATION_OPTIONS"_s, context ).trimmed();
197 // handle backwards compatibility parameter CREATE_OPTIONS
198 const QString optionsString = parameterAsString( parameters, u"CREATE_OPTIONS"_s, context );
199 if ( !optionsString.isEmpty() )
200 creationOptions = optionsString;
201
202 const QString outputFile = parameterAsOutputLayer( parameters, u"OUTPUT"_s, context );
203 const QString outputFormat = parameterAsOutputRasterFormat( parameters, u"OUTPUT"_s, context );
204
205 // round up width and height to the nearest integer as GDAL does (e.g. in gdal_rasterize)
206 // see https://github.com/qgis/QGIS/issues/43547
207 const int rows = static_cast<int>( 0.5 + extent.height() / pixelSize );
208 const int cols = static_cast<int>( 0.5 + extent.width() / pixelSize );
209
210 //build new raster extent based on number of columns and cellsize
211 //this prevents output cellsize being calculated too small
212 const QgsRectangle rasterExtent = QgsRectangle( extent.xMinimum(), extent.yMaximum() - ( rows * pixelSize ), extent.xMinimum() + ( cols * pixelSize ), extent.yMaximum() );
213
214 auto writer = std::make_unique<QgsRasterFileWriter>( outputFile );
215 writer->setOutputProviderKey( u"gdal"_s );
216 if ( !creationOptions.isEmpty() )
217 {
218 writer->setCreationOptions( creationOptions.split( '|' ) );
219 }
220 writer->setOutputFormat( outputFormat );
221 std::unique_ptr<QgsRasterDataProvider> provider( writer->createOneBandRaster( rasterDataType, cols, rows, rasterExtent, crs ) );
222 if ( !provider )
223 throw QgsProcessingException( QObject::tr( "Could not create raster output: %1" ).arg( outputFile ) );
224 if ( !provider->isValid() )
225 throw QgsProcessingException( QObject::tr( "Could not create raster output %1: %2" ).arg( outputFile, provider->error().message( QgsErrorMessage::Text ) ) );
226
227 //Thoughts on noData:
228 //Setting a noData value is disabled so that the user is protected from accidentally creating an empty raster (eg. when value is set to -9999)
229 //We could also allow creating empty rasters by exposing a noData value parameter (usecases?).
230
231 //prepare raw data depending on raster data type
232 QgsRasterBlock block( rasterDataType, cols, 1 );
233 block.fill( value );
234
235 const double step = rows > 0 ? 100.0 / rows : 1;
236
237 for ( int i = 0; i < rows; i++ )
238 {
239 if ( feedback->isCanceled() )
240 {
241 break;
242 }
243
244 if ( !provider->writeBlock( &block, 1, 0, i ) )
245 {
246 throw QgsProcessingException( QObject::tr( "Could not write raster block: %1" ).arg( provider->error().summary() ) );
247 }
248 feedback->setProgress( i * step );
249 }
250
251 QVariantMap outputs;
252 outputs.insert( u"OUTPUT"_s, outputFile );
253 return outputs;
254}
255
DataType
Raster data types.
Definition qgis.h:393
@ Float32
Thirty two bit floating point (float).
Definition qgis.h:401
@ Int16
Sixteen bit signed integer (qint16).
Definition qgis.h:398
@ UInt16
Sixteen bit unsigned integer (quint16).
Definition qgis.h:397
@ Byte
Eight bit unsigned integer (quint8).
Definition qgis.h:395
@ Int32
Thirty two bit signed integer (qint32).
Definition qgis.h:400
@ Float64
Sixty four bit floating point (double).
Definition qgis.h:402
@ UInt32
Thirty two bit unsigned integer (quint32).
Definition qgis.h:399
@ Hidden
Parameter is hidden and should not be shown to users.
Definition qgis.h:3983
@ Advanced
Parameter is an advanced parameter which should be hidden from users by default.
Definition qgis.h:3982
@ Double
Double/float values.
Definition qgis.h:4023
Represents a coordinate reference system (CRS).
@ Text
Plain text format.
Definition qgserror.h:40
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
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 coordinate reference system parameter for processing algorithms.
A rectangular map extent 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 ...
Raster data container.
A rectangle specified with double values.
double xMinimum
double yMaximum