QGIS API Documentation 3.99.0-Master (09f76ad7019)
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( "This algorithm generates a raster layer for a given extent and cell "
59 "size filled with a single constant value.\n"
60 "Additionally an output data type can be specified. "
61 "The algorithm will abort if a value has been entered that "
62 "cannot be represented by the selected output raster data type." );
63}
64
65QString QgsConstantRasterAlgorithm::shortDescription() const
66{
67 return QObject::tr( "Generates a raster layer for a given extent and cell size filled with a single constant value." );
68}
69
70QgsConstantRasterAlgorithm *QgsConstantRasterAlgorithm::createInstance() const
71{
72 return new QgsConstantRasterAlgorithm();
73}
74
75void QgsConstantRasterAlgorithm::initAlgorithm( const QVariantMap & )
76{
77 addParameter( new QgsProcessingParameterExtent( u"EXTENT"_s, QObject::tr( "Desired extent" ) ) );
78 addParameter( new QgsProcessingParameterCrs( u"TARGET_CRS"_s, QObject::tr( "Target CRS" ), u"ProjectCrs"_s ) );
79 addParameter( new QgsProcessingParameterNumber( u"PIXEL_SIZE"_s, QObject::tr( "Pixel size" ), Qgis::ProcessingNumberParameterType::Double, 1, false, 0 ) );
80 addParameter( new QgsProcessingParameterNumber( u"NUMBER"_s, QObject::tr( "Constant value" ), Qgis::ProcessingNumberParameterType::Double, 1, false ) );
81
82 QStringList rasterDataTypes; //currently supported raster data types that can be handled QgsRasterBlock::writeValue()
83 rasterDataTypes << u"Byte"_s
84 << u"Integer16"_s
85 << u"Unsigned Integer16"_s
86 << u"Integer32"_s
87 << u"Unsigned Integer32"_s
88 << u"Float32"_s
89 << u"Float64"_s;
90
91 //QGIS3: parameter set to Float32 by default so that existing models/scripts don't break
92 std::unique_ptr<QgsProcessingParameterDefinition> rasterTypeParameter = std::make_unique<QgsProcessingParameterEnum>( u"OUTPUT_TYPE"_s, QObject::tr( "Output raster data type" ), rasterDataTypes, false, 5, false );
93 rasterTypeParameter->setFlags( Qgis::ProcessingParameterFlag::Advanced );
94 addParameter( rasterTypeParameter.release() );
95
96 // backwards compatibility parameter
97 // TODO QGIS 5: remove parameter and related logic
98 auto createOptsParam = std::make_unique<QgsProcessingParameterString>( u"CREATE_OPTIONS"_s, QObject::tr( "Creation options" ), QVariant(), false, true );
99 createOptsParam->setMetadata( QVariantMap( { { u"widget_wrapper"_s, QVariantMap( { { u"widget_type"_s, u"rasteroptions"_s } } ) } } ) );
100 createOptsParam->setFlags( createOptsParam->flags() | Qgis::ProcessingParameterFlag::Hidden );
101 addParameter( createOptsParam.release() );
102
103 auto creationOptsParam = std::make_unique<QgsProcessingParameterString>( u"CREATION_OPTIONS"_s, QObject::tr( "Creation options" ), QVariant(), false, true );
104 creationOptsParam->setMetadata( QVariantMap( { { u"widget_wrapper"_s, QVariantMap( { { u"widget_type"_s, u"rasteroptions"_s } } ) } } ) );
105 creationOptsParam->setFlags( creationOptsParam->flags() | Qgis::ProcessingParameterFlag::Advanced );
106 addParameter( creationOptsParam.release() );
107
108 addParameter( new QgsProcessingParameterRasterDestination( u"OUTPUT"_s, QObject::tr( "Constant" ) ) );
109}
110
111QVariantMap QgsConstantRasterAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
112{
113 const QgsCoordinateReferenceSystem crs = parameterAsCrs( parameters, u"TARGET_CRS"_s, context );
114 const QgsRectangle extent = parameterAsExtent( parameters, u"EXTENT"_s, context, crs );
115 const double pixelSize = parameterAsDouble( parameters, u"PIXEL_SIZE"_s, context );
116 const double value = parameterAsDouble( parameters, u"NUMBER"_s, context );
117 const int typeId = parameterAsInt( parameters, u"OUTPUT_TYPE"_s, context );
118
119 if ( pixelSize <= 0 )
120 {
121 throw QgsProcessingException( QObject::tr( "Pixel size must be greater than 0." ) );
122 }
123
124 //implement warning if input float has decimal places but is written to integer raster
125 double fractpart;
126 double intpart;
127 fractpart = abs( std::modf( value, &intpart ) ); //@abs: negative values may be entered
128
129 Qgis::DataType rasterDataType = Qgis::DataType::Float32; //standard output type
130 switch ( typeId )
131 {
132 case 0:
133 rasterDataType = Qgis::DataType::Byte;
134 if ( value < std::numeric_limits<quint8>::min() || value > std::numeric_limits<quint8>::max() )
135 throw QgsProcessingException( 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 if ( fractpart > 0 )
137 feedback->reportError( 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 ) );
138 break;
139 case 1:
140 rasterDataType = Qgis::DataType::Int16;
141 if ( value < std::numeric_limits<qint16>::min() || value > std::numeric_limits<qint16>::max() )
142 throw QgsProcessingException( 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 ) );
143 if ( fractpart > 0 )
144 feedback->reportError( 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 ) );
145 break;
146 case 2:
147 rasterDataType = Qgis::DataType::UInt16;
148 if ( value < std::numeric_limits<quint16>::min() || value > std::numeric_limits<quint16>::max() )
149 throw QgsProcessingException( 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" ) );
150 if ( fractpart > 0 )
151 feedback->reportError( 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 ) );
152 break;
153 case 3:
154 rasterDataType = Qgis::DataType::Int32;
155 if ( value < std::numeric_limits<qint32>::min() || value > std::numeric_limits<qint32>::max() )
156 throw QgsProcessingException( 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 ) );
157 if ( fractpart > 0 )
158 feedback->reportError( 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 ) );
159 break;
160 case 4:
161 rasterDataType = Qgis::DataType::UInt32;
162 if ( value < std::numeric_limits<quint32>::min() || value > std::numeric_limits<quint32>::max() )
163 throw QgsProcessingException( 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 ) );
164 if ( fractpart > 0 )
165 feedback->reportError( 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 ) );
166 break;
167 case 5:
168 rasterDataType = Qgis::DataType::Float32;
169 break;
170 case 6:
171 rasterDataType = Qgis::DataType::Float64;
172 break;
173 default:
174 break;
175 }
176
177 QString creationOptions = parameterAsString( parameters, u"CREATION_OPTIONS"_s, context ).trimmed();
178 // handle backwards compatibility parameter CREATE_OPTIONS
179 const QString optionsString = parameterAsString( parameters, u"CREATE_OPTIONS"_s, context );
180 if ( !optionsString.isEmpty() )
181 creationOptions = optionsString;
182
183 const QString outputFile = parameterAsOutputLayer( parameters, u"OUTPUT"_s, context );
184 const QString outputFormat = parameterAsOutputRasterFormat( parameters, u"OUTPUT"_s, context );
185
186 // round up width and height to the nearest integer as GDAL does (e.g. in gdal_rasterize)
187 // see https://github.com/qgis/QGIS/issues/43547
188 const int rows = static_cast<int>( 0.5 + extent.height() / pixelSize );
189 const int cols = static_cast<int>( 0.5 + extent.width() / pixelSize );
190
191 //build new raster extent based on number of columns and cellsize
192 //this prevents output cellsize being calculated too small
193 const QgsRectangle rasterExtent = QgsRectangle( extent.xMinimum(), extent.yMaximum() - ( rows * pixelSize ), extent.xMinimum() + ( cols * pixelSize ), extent.yMaximum() );
194
195 auto writer = std::make_unique<QgsRasterFileWriter>( outputFile );
196 writer->setOutputProviderKey( u"gdal"_s );
197 if ( !creationOptions.isEmpty() )
198 {
199 writer->setCreationOptions( creationOptions.split( '|' ) );
200 }
201 writer->setOutputFormat( outputFormat );
202 std::unique_ptr<QgsRasterDataProvider> provider( writer->createOneBandRaster( rasterDataType, cols, rows, rasterExtent, crs ) );
203 if ( !provider )
204 throw QgsProcessingException( QObject::tr( "Could not create raster output: %1" ).arg( outputFile ) );
205 if ( !provider->isValid() )
206 throw QgsProcessingException( QObject::tr( "Could not create raster output %1: %2" ).arg( outputFile, provider->error().message( QgsErrorMessage::Text ) ) );
207
208 //Thoughts on noData:
209 //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)
210 //We could also allow creating empty rasters by exposing a noData value parameter (usecases?).
211
212 //prepare raw data depending on raster data type
213 QgsRasterBlock block( rasterDataType, cols, 1 );
214 block.fill( value );
215
216 const double step = rows > 0 ? 100.0 / rows : 1;
217
218 for ( int i = 0; i < rows; i++ )
219 {
220 if ( feedback->isCanceled() )
221 {
222 break;
223 }
224
225 if ( !provider->writeBlock( &block, 1, 0, i ) )
226 {
227 throw QgsProcessingException( QObject::tr( "Could not write raster block: %1" ).arg( provider->error().summary() ) );
228 }
229 feedback->setProgress( i * step );
230 }
231
232 QVariantMap outputs;
233 outputs.insert( u"OUTPUT"_s, outputFile );
234 return outputs;
235}
236
DataType
Raster data types.
Definition qgis.h:379
@ Float32
Thirty two bit floating point (float).
Definition qgis.h:387
@ Int16
Sixteen bit signed integer (qint16).
Definition qgis.h:384
@ UInt16
Sixteen bit unsigned integer (quint16).
Definition qgis.h:383
@ Byte
Eight bit unsigned integer (quint8).
Definition qgis.h:381
@ Int32
Thirty two bit signed integer (qint32).
Definition qgis.h:386
@ Float64
Sixty four bit floating point (double).
Definition qgis.h:388
@ UInt32
Thirty two bit unsigned integer (quint32).
Definition qgis.h:385
@ Hidden
Parameter is hidden and should not be shown to users.
Definition qgis.h:3835
@ Advanced
Parameter is an advanced parameter which should be hidden from users by default.
Definition qgis.h:3834
@ Double
Double/float values.
Definition qgis.h:3875
Represents a coordinate reference system (CRS).
bool isCanceled() const
Tells whether the operation has been canceled already.
Definition qgsfeedback.h:55
void setProgress(double progress)
Sets the current progress for the feedback object.
Definition qgsfeedback.h:63
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