QGIS API Documentation 3.38.0-Grenoble (exported)
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
18#include <limits>
19#include "math.h"
21#include "qgsrasterfilewriter.h"
22
24
25QString QgsConstantRasterAlgorithm::name() const
26{
27 return QStringLiteral( "createconstantrasterlayer" );
28}
29
30QString QgsConstantRasterAlgorithm::displayName() const
31{
32 return QObject::tr( "Create constant raster layer" );
33}
34
35QStringList QgsConstantRasterAlgorithm::tags() const
36{
37 return QObject::tr( "raster,create,constant" ).split( ',' );
38}
39
40QString QgsConstantRasterAlgorithm::group() const
41{
42 return QObject::tr( "Raster creation" );
43}
44
45QString QgsConstantRasterAlgorithm::groupId() const
46{
47 return QStringLiteral( "rastercreation" );
48}
49
50QString QgsConstantRasterAlgorithm::shortHelpString() const
51{
52 return QObject::tr( "Generates raster layer for given extent and cell "
53 "size filled with the specified value.\n"
54 "Additionally an output data type can be specified. "
55 "The algorithm will abort if a value has been entered that "
56 "cannot be represented by the selected output raster data type." );
57}
58
59QgsConstantRasterAlgorithm *QgsConstantRasterAlgorithm::createInstance() const
60{
61 return new QgsConstantRasterAlgorithm();
62}
63
64void QgsConstantRasterAlgorithm::initAlgorithm( const QVariantMap & )
65{
66 addParameter( new QgsProcessingParameterExtent( QStringLiteral( "EXTENT" ), QObject::tr( "Desired extent" ) ) );
67 addParameter( new QgsProcessingParameterCrs( QStringLiteral( "TARGET_CRS" ), QObject::tr( "Target CRS" ), QStringLiteral( "ProjectCrs" ) ) );
68 addParameter( new QgsProcessingParameterNumber( QStringLiteral( "PIXEL_SIZE" ), QObject::tr( "Pixel size" ),
70 addParameter( new QgsProcessingParameterNumber( QStringLiteral( "NUMBER" ), QObject::tr( "Constant value" ),
72
73 QStringList rasterDataTypes; //currently supported raster data types that can be handled QgsRasterBlock::writeValue()
74 rasterDataTypes << QStringLiteral( "Byte" )
75 << QStringLiteral( "Integer16" )
76 << QStringLiteral( "Unsigned Integer16" )
77 << QStringLiteral( "Integer32" )
78 << QStringLiteral( "Unsigned Integer32" )
79 << QStringLiteral( "Float32" )
80 << QStringLiteral( "Float64" );
81
82 //QGIS3: parameter set to Float32 by default so that existing models/scripts don't break
83 std::unique_ptr< QgsProcessingParameterDefinition > rasterTypeParameter = std::make_unique< QgsProcessingParameterEnum >( QStringLiteral( "OUTPUT_TYPE" ), QObject::tr( "Output raster data type" ), rasterDataTypes, false, 5, false );
84 rasterTypeParameter->setFlags( Qgis::ProcessingParameterFlag::Advanced );
85 addParameter( rasterTypeParameter.release() );
86
87 addParameter( new QgsProcessingParameterRasterDestination( QStringLiteral( "OUTPUT" ), QObject::tr( "Constant" ) ) );
88}
89
90QVariantMap QgsConstantRasterAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
91{
92 const QgsCoordinateReferenceSystem crs = parameterAsCrs( parameters, QStringLiteral( "TARGET_CRS" ), context );
93 const QgsRectangle extent = parameterAsExtent( parameters, QStringLiteral( "EXTENT" ), context, crs );
94 const double pixelSize = parameterAsDouble( parameters, QStringLiteral( "PIXEL_SIZE" ), context );
95 const double value = parameterAsDouble( parameters, QStringLiteral( "NUMBER" ), context );
96 const int typeId = parameterAsInt( parameters, QStringLiteral( "OUTPUT_TYPE" ), context );
97
98 if ( pixelSize <= 0 )
99 {
100 throw QgsProcessingException( QObject::tr( "Pixel size must be greater than 0." ) );
101 }
102
103 //implement warning if input float has decimal places but is written to integer raster
104 double fractpart;
105 double intpart;
106 fractpart = abs( std::modf( value, &intpart ) ); //@abs: negative values may be entered
107
108 Qgis::DataType rasterDataType = Qgis::DataType::Float32; //standard output type
109 switch ( typeId )
110 {
111 case 0:
112 rasterDataType = Qgis::DataType::Byte;
113 if ( value < std::numeric_limits<quint8>::min() || value > std::numeric_limits<quint8>::max() )
114 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" ) ) );
115 if ( fractpart > 0 )
116 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" ) ) );
117 break;
118 case 1:
119 rasterDataType = Qgis::DataType::Int16;
120 if ( value < std::numeric_limits<qint16>::min() || value > std::numeric_limits<qint16>::max() )
121 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" ) ) );
122 if ( fractpart > 0 )
123 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" ) ) );
124 break;
125 case 2:
126 rasterDataType = Qgis::DataType::UInt16;
127 if ( value < std::numeric_limits<quint16>::min() || value > std::numeric_limits<quint16>::max() )
128 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" ) );
129 if ( fractpart > 0 )
130 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" ) ) );
131 break;
132 case 3:
133 rasterDataType = Qgis::DataType::Int32;
134 if ( value < std::numeric_limits<qint32>::min() || value > std::numeric_limits<qint32>::max() )
135 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" ) ) );
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( QLatin1String( "Integer32" ) ) );
138 break;
139 case 4:
140 rasterDataType = Qgis::DataType::UInt32;
141 if ( value < std::numeric_limits<quint32>::min() || value > std::numeric_limits<quint32>::max() )
142 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" ) ) );
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( QLatin1String( "Unsigned Integer32" ) ) );
145 break;
146 case 5:
147 rasterDataType = Qgis::DataType::Float32;
148 break;
149 case 6:
150 rasterDataType = Qgis::DataType::Float64;
151 break;
152 default:
153 break;
154 }
155 const QString outputFile = parameterAsOutputLayer( parameters, QStringLiteral( "OUTPUT" ), context );
156 const QFileInfo fi( outputFile );
157 const QString outputFormat = QgsRasterFileWriter::driverForExtension( fi.suffix() );
158
159 const int rows = std::max( std::ceil( extent.height() / pixelSize ), 1.0 );
160 const int cols = std::max( std::ceil( extent.width() / pixelSize ), 1.0 );
161
162 //build new raster extent based on number of columns and cellsize
163 //this prevents output cellsize being calculated too small
164 const QgsRectangle rasterExtent = QgsRectangle( extent.xMinimum(), extent.yMaximum() - ( rows * pixelSize ), extent.xMinimum() + ( cols * pixelSize ), extent.yMaximum() );
165
166 std::unique_ptr< QgsRasterFileWriter > writer = std::make_unique< QgsRasterFileWriter >( outputFile );
167 writer->setOutputProviderKey( QStringLiteral( "gdal" ) );
168 writer->setOutputFormat( outputFormat );
169 std::unique_ptr<QgsRasterDataProvider > provider( writer->createOneBandRaster( rasterDataType, cols, rows, rasterExtent, crs ) );
170 if ( !provider )
171 throw QgsProcessingException( QObject::tr( "Could not create raster output: %1" ).arg( outputFile ) );
172 if ( !provider->isValid() )
173 throw QgsProcessingException( QObject::tr( "Could not create raster output %1: %2" ).arg( outputFile, provider->error().message( QgsErrorMessage::Text ) ) );
174
175 //Thoughts on noData:
176 //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)
177 //We could also allow creating empty rasters by exposing a noData value parameter (usecases?).
178
179 //prepare raw data depending on raster data type
180 QgsRasterBlock block( rasterDataType, cols, 1 );
181 switch ( typeId )
182 {
183 case 0:
184 {
185 std::vector<quint8> byteRow( cols );
186 std::fill( byteRow.begin(), byteRow.end(), value );
187 block.setData( QByteArray::fromRawData( ( char * )&byteRow[0], QgsRasterBlock::typeSize( Qgis::DataType::Byte ) * cols ) );
188 break;
189 }
190 case 1:
191 {
192 std::vector<qint16> int16Row( cols );
193 std::fill( int16Row.begin(), int16Row.end(), value );
194 block.setData( QByteArray::fromRawData( ( char * )&int16Row[0], QgsRasterBlock::typeSize( Qgis::DataType::Int16 ) * cols ) );
195 break;
196 }
197 case 2:
198 {
199 std::vector<quint16> uInt16Row( cols );
200 std::fill( uInt16Row.begin(), uInt16Row.end(), value );
201 block.setData( QByteArray::fromRawData( ( char * )&uInt16Row[0], QgsRasterBlock::typeSize( Qgis::DataType::UInt16 ) * cols ) );
202 break;
203 }
204 case 3:
205 {
206 std::vector<qint32> int32Row( cols );
207 std::fill( int32Row.begin(), int32Row.end(), value );
208 block.setData( QByteArray::fromRawData( ( char * )&int32Row[0], QgsRasterBlock::typeSize( Qgis::DataType::Int32 ) * cols ) );
209 break;
210 }
211 case 4:
212 {
213 std::vector<quint32> uInt32Row( cols );
214 std::fill( uInt32Row.begin(), uInt32Row.end(), value );
215 block.setData( QByteArray::fromRawData( ( char * )&uInt32Row[0], QgsRasterBlock::typeSize( Qgis::DataType::UInt32 ) * cols ) );
216 break;
217 }
218 case 5:
219 {
220 std::vector<float> float32Row( cols );
221 std::fill( float32Row.begin(), float32Row.end(), value );
222 block.setData( QByteArray::fromRawData( ( char * )&float32Row[0], QgsRasterBlock::typeSize( Qgis::DataType::Float32 ) * cols ) );
223 break;
224 }
225 case 6:
226 {
227 std::vector<double> float64Row( cols );
228 std::fill( float64Row.begin(), float64Row.end(), value );
229 block.setData( QByteArray::fromRawData( ( char * )&float64Row[0], QgsRasterBlock::typeSize( Qgis::DataType::Float64 ) * cols ) );
230 break;
231 }
232 default:
233 {
234 std::vector<float> float32Row( cols );
235 std::fill( float32Row.begin(), float32Row.end(), value );
236 block.setData( QByteArray::fromRawData( ( char * )&float32Row[0], QgsRasterBlock::typeSize( Qgis::DataType::Float32 ) * cols ) );
237 break;
238 }
239 }
240
241 const double step = rows > 0 ? 100.0 / rows : 1;
242
243 for ( int i = 0; i < rows ; i++ )
244 {
245 if ( feedback->isCanceled() )
246 {
247 break;
248 }
249
250 provider->writeBlock( &block, 1, 0, i );
251 feedback->setProgress( i * step );
252 }
253
254 QVariantMap outputs;
255 outputs.insert( QStringLiteral( "OUTPUT" ), outputFile );
256 return outputs;
257}
258
DataType
Raster data types.
Definition qgis.h:288
@ Float32
Thirty two bit floating point (float)
@ Int16
Sixteen bit signed integer (qint16)
@ UInt16
Sixteen bit unsigned integer (quint16)
@ Byte
Eight bit unsigned integer (quint8)
@ Int32
Thirty two bit signed integer (qint32)
@ Float64
Sixty four bit floating point (double)
@ UInt32
Thirty two bit unsigned integer (quint32)
@ Advanced
Parameter is an advanced parameter which should be hidden from users by default.
This class 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 int typeSize(Qgis::DataType dataType)
Returns the size in bytes for the specified dataType.
static QString driverForExtension(const QString &extension)
Returns the GDAL driver name for a specified file extension.
A rectangle specified with double values.
double xMinimum() const
Returns the x minimum value (left side of rectangle).
double width() const
Returns the width of the rectangle.
double yMaximum() const
Returns the y maximum value (top side of rectangle).
double height() const
Returns the height of the rectangle.
const QgsCoordinateReferenceSystem & crs