QGIS API Documentation 4.1.0-Master (5bf3c20f3c9)
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 const QgsCoordinateReferenceSystem crs = parameterAsCrs( parameters, u"TARGET_CRS"_s, context );
111 const QgsRectangle extent = parameterAsExtent( parameters, u"EXTENT"_s, context, crs );
112 const double pixelSize = parameterAsDouble( parameters, u"PIXEL_SIZE"_s, context );
113 const double value = parameterAsDouble( parameters, u"NUMBER"_s, context );
114 const int typeId = parameterAsInt( parameters, u"OUTPUT_TYPE"_s, context );
115
116 if ( pixelSize <= 0 )
117 {
118 throw QgsProcessingException( QObject::tr( "Pixel size must be greater than 0." ) );
119 }
120
121 //implement warning if input float has decimal places but is written to integer raster
122 double fractpart;
123 double intpart;
124 fractpart = abs( std::modf( value, &intpart ) ); //@abs: negative values may be entered
125
126 Qgis::DataType rasterDataType = Qgis::DataType::Float32; //standard output type
127 switch ( typeId )
128 {
129 case 0:
130 rasterDataType = Qgis::DataType::Byte;
131 if ( value < std::numeric_limits<quint8>::min() || value > std::numeric_limits<quint8>::max() )
133 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 )
134 );
135 if ( fractpart > 0 )
136 feedback->reportError(
137 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 );
139 break;
140 case 1:
141 rasterDataType = Qgis::DataType::Int16;
142 if ( value < std::numeric_limits<qint16>::min() || value > std::numeric_limits<qint16>::max() )
144 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 )
145 );
146 if ( fractpart > 0 )
147 feedback->reportError(
148 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 )
149 );
150 break;
151 case 2:
152 rasterDataType = Qgis::DataType::UInt16;
153 if ( value < std::numeric_limits<quint16>::min() || value > std::numeric_limits<quint16>::max() )
155 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" )
156 );
157 if ( fractpart > 0 )
158 feedback->reportError(
159 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 )
160 );
161 break;
162 case 3:
163 rasterDataType = Qgis::DataType::Int32;
164 if ( value < std::numeric_limits<qint32>::min() || value > std::numeric_limits<qint32>::max() )
166 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 )
167 );
168 if ( fractpart > 0 )
169 feedback->reportError(
170 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 )
171 );
172 break;
173 case 4:
174 rasterDataType = Qgis::DataType::UInt32;
175 if ( value < std::numeric_limits<quint32>::min() || value > std::numeric_limits<quint32>::max() )
177 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 )
178 );
179 if ( fractpart > 0 )
180 feedback->reportError(
181 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 )
182 );
183 break;
184 case 5:
185 rasterDataType = Qgis::DataType::Float32;
186 break;
187 case 6:
188 rasterDataType = Qgis::DataType::Float64;
189 break;
190 default:
191 break;
192 }
193
194 QString creationOptions = parameterAsString( parameters, u"CREATION_OPTIONS"_s, context ).trimmed();
195 // handle backwards compatibility parameter CREATE_OPTIONS
196 const QString optionsString = parameterAsString( parameters, u"CREATE_OPTIONS"_s, context );
197 if ( !optionsString.isEmpty() )
198 creationOptions = optionsString;
199
200 const QString outputFile = parameterAsOutputLayer( parameters, u"OUTPUT"_s, context );
201 const QString outputFormat = parameterAsOutputRasterFormat( parameters, u"OUTPUT"_s, context );
202
203 // round up width and height to the nearest integer as GDAL does (e.g. in gdal_rasterize)
204 // see https://github.com/qgis/QGIS/issues/43547
205 const int rows = static_cast<int>( 0.5 + extent.height() / pixelSize );
206 const int cols = static_cast<int>( 0.5 + extent.width() / pixelSize );
207
208 //build new raster extent based on number of columns and cellsize
209 //this prevents output cellsize being calculated too small
210 const QgsRectangle rasterExtent = QgsRectangle( extent.xMinimum(), extent.yMaximum() - ( rows * pixelSize ), extent.xMinimum() + ( cols * pixelSize ), extent.yMaximum() );
211
212 auto writer = std::make_unique<QgsRasterFileWriter>( outputFile );
213 writer->setOutputProviderKey( u"gdal"_s );
214 if ( !creationOptions.isEmpty() )
215 {
216 writer->setCreationOptions( creationOptions.split( '|' ) );
217 }
218 writer->setOutputFormat( outputFormat );
219 std::unique_ptr<QgsRasterDataProvider> provider( writer->createOneBandRaster( rasterDataType, cols, rows, rasterExtent, crs ) );
220 if ( !provider )
221 throw QgsProcessingException( QObject::tr( "Could not create raster output: %1" ).arg( outputFile ) );
222 if ( !provider->isValid() )
223 throw QgsProcessingException( QObject::tr( "Could not create raster output %1: %2" ).arg( outputFile, provider->error().message( QgsErrorMessage::Text ) ) );
224
225 //Thoughts on noData:
226 //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)
227 //We could also allow creating empty rasters by exposing a noData value parameter (usecases?).
228
229 //prepare raw data depending on raster data type
230 QgsRasterBlock block( rasterDataType, cols, 1 );
231 block.fill( value );
232
233 const double step = rows > 0 ? 100.0 / rows : 1;
234
235 for ( int i = 0; i < rows; i++ )
236 {
237 if ( feedback->isCanceled() )
238 {
239 break;
240 }
241
242 if ( !provider->writeBlock( &block, 1, 0, i ) )
243 {
244 throw QgsProcessingException( QObject::tr( "Could not write raster block: %1" ).arg( provider->error().summary() ) );
245 }
246 feedback->setProgress( i * step );
247 }
248
249 QVariantMap outputs;
250 outputs.insert( u"OUTPUT"_s, outputFile );
251 return outputs;
252}
253
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:3881
@ Advanced
Parameter is an advanced parameter which should be hidden from users by default.
Definition qgis.h:3880
@ Double
Double/float values.
Definition qgis.h:3921
Represents a coordinate reference system (CRS).
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