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