QGIS API Documentation  3.22.4-Białowieża (ce8e65e95e)
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 
25 QString QgsConstantRasterAlgorithm::name() const
26 {
27  return QStringLiteral( "createconstantrasterlayer" );
28 }
29 
30 QString QgsConstantRasterAlgorithm::displayName() const
31 {
32  return QObject::tr( "Create constant raster layer" );
33 }
34 
35 QStringList QgsConstantRasterAlgorithm::tags() const
36 {
37  return QObject::tr( "raster,create,constant" ).split( ',' );
38 }
39 
40 QString QgsConstantRasterAlgorithm::group() const
41 {
42  return QObject::tr( "Raster creation" );
43 }
44 
45 QString QgsConstantRasterAlgorithm::groupId() const
46 {
47  return QStringLiteral( "rastercreation" );
48 }
49 
50 QString 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 
59 QgsConstantRasterAlgorithm *QgsConstantRasterAlgorithm::createInstance() const
60 {
61  return new QgsConstantRasterAlgorithm();
62 }
63 
64 void 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" ),
69  QgsProcessingParameterNumber::Double, 0.00001, false, 0.01 ) );
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( QgsProcessingParameterDefinition::FlagAdvanced );
85  addParameter( rasterTypeParameter.release() );
86 
87  addParameter( new QgsProcessingParameterRasterDestination( QStringLiteral( "OUTPUT" ), QObject::tr( "Constant" ) ) );
88 }
89 
90 QVariantMap 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  //implement warning if input float has decimal places but is written to integer raster
99  double fractpart;
100  double intpart;
101  fractpart = abs( std::modf( value, &intpart ) ); //@abs: negative values may be entered
102 
103  Qgis::DataType rasterDataType = Qgis::DataType::Float32; //standard output type
104  switch ( typeId )
105  {
106  case 0:
107  rasterDataType = Qgis::DataType::Byte;
108  if ( value < std::numeric_limits<quint8>::min() || value > std::numeric_limits<quint8>::max() )
109  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" ) ) );
110  if ( fractpart > 0 )
111  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" ) ) );
112  break;
113  case 1:
114  rasterDataType = Qgis::DataType::Int16;
115  if ( value < std::numeric_limits<qint16>::min() || value > std::numeric_limits<qint16>::max() )
116  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" ) ) );
117  if ( fractpart > 0 )
118  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" ) ) );
119  break;
120  case 2:
121  rasterDataType = Qgis::DataType::UInt16;
122  if ( value < std::numeric_limits<quint16>::min() || value > std::numeric_limits<quint16>::max() )
123  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" ) );
124  if ( fractpart > 0 )
125  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" ) ) );
126  break;
127  case 3:
128  rasterDataType = Qgis::DataType::Int32;
129  if ( value < std::numeric_limits<qint32>::min() || value > std::numeric_limits<qint32>::max() )
130  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" ) ) );
131  if ( fractpart > 0 )
132  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" ) ) );
133  break;
134  case 4:
135  rasterDataType = Qgis::DataType::UInt32;
136  if ( value < std::numeric_limits<quint32>::min() || value > std::numeric_limits<quint32>::max() )
137  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" ) ) );
138  if ( fractpart > 0 )
139  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" ) ) );
140  break;
141  case 5:
142  rasterDataType = Qgis::DataType::Float32;
143  break;
144  case 6:
145  rasterDataType = Qgis::DataType::Float64;
146  break;
147  default:
148  break;
149  }
150  const QString outputFile = parameterAsOutputLayer( parameters, QStringLiteral( "OUTPUT" ), context );
151  const QFileInfo fi( outputFile );
152  const QString outputFormat = QgsRasterFileWriter::driverForExtension( fi.suffix() );
153 
154  const int rows = std::max( std::ceil( extent.height() / pixelSize ), 1.0 );
155  const int cols = std::max( std::ceil( extent.width() / pixelSize ), 1.0 );
156 
157  //build new raster extent based on number of columns and cellsize
158  //this prevents output cellsize being calculated too small
159  const QgsRectangle rasterExtent = QgsRectangle( extent.xMinimum(), extent.yMaximum() - ( rows * pixelSize ), extent.xMinimum() + ( cols * pixelSize ), extent.yMaximum() );
160 
161  std::unique_ptr< QgsRasterFileWriter > writer = std::make_unique< QgsRasterFileWriter >( outputFile );
162  writer->setOutputProviderKey( QStringLiteral( "gdal" ) );
163  writer->setOutputFormat( outputFormat );
164  std::unique_ptr<QgsRasterDataProvider > provider( writer->createOneBandRaster( rasterDataType, cols, rows, rasterExtent, crs ) );
165  if ( !provider )
166  throw QgsProcessingException( QObject::tr( "Could not create raster output: %1" ).arg( outputFile ) );
167  if ( !provider->isValid() )
168  throw QgsProcessingException( QObject::tr( "Could not create raster output %1: %2" ).arg( outputFile, provider->error().message( QgsErrorMessage::Text ) ) );
169 
170  //Thoughts on noData:
171  //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)
172  //We could also allow creating empty rasters by exposing a noData value parameter (usecases?).
173 
174  //prepare raw data depending on raster data type
175  QgsRasterBlock block( rasterDataType, cols, 1 );
176  switch ( typeId )
177  {
178  case 0:
179  {
180  std::vector<quint8> byteRow( cols );
181  std::fill( byteRow.begin(), byteRow.end(), value );
182  block.setData( QByteArray::fromRawData( ( char * )&byteRow[0], QgsRasterBlock::typeSize( Qgis::DataType::Byte ) * cols ) );
183  break;
184  }
185  case 1:
186  {
187  std::vector<qint16> int16Row( cols );
188  std::fill( int16Row.begin(), int16Row.end(), value );
189  block.setData( QByteArray::fromRawData( ( char * )&int16Row[0], QgsRasterBlock::typeSize( Qgis::DataType::Int16 ) * cols ) );
190  break;
191  }
192  case 2:
193  {
194  std::vector<quint16> uInt16Row( cols );
195  std::fill( uInt16Row.begin(), uInt16Row.end(), value );
196  block.setData( QByteArray::fromRawData( ( char * )&uInt16Row[0], QgsRasterBlock::typeSize( Qgis::DataType::UInt16 ) * cols ) );
197  break;
198  }
199  case 3:
200  {
201  std::vector<qint32> int32Row( cols );
202  std::fill( int32Row.begin(), int32Row.end(), value );
203  block.setData( QByteArray::fromRawData( ( char * )&int32Row[0], QgsRasterBlock::typeSize( Qgis::DataType::Int32 ) * cols ) );
204  break;
205  }
206  case 4:
207  {
208  std::vector<quint32> uInt32Row( cols );
209  std::fill( uInt32Row.begin(), uInt32Row.end(), value );
210  block.setData( QByteArray::fromRawData( ( char * )&uInt32Row[0], QgsRasterBlock::typeSize( Qgis::DataType::UInt32 ) * cols ) );
211  break;
212  }
213  case 5:
214  {
215  std::vector<float> float32Row( cols );
216  std::fill( float32Row.begin(), float32Row.end(), value );
217  block.setData( QByteArray::fromRawData( ( char * )&float32Row[0], QgsRasterBlock::typeSize( Qgis::DataType::Float32 ) * cols ) );
218  break;
219  }
220  case 6:
221  {
222  std::vector<double> float64Row( cols );
223  std::fill( float64Row.begin(), float64Row.end(), value );
224  block.setData( QByteArray::fromRawData( ( char * )&float64Row[0], QgsRasterBlock::typeSize( Qgis::DataType::Float64 ) * cols ) );
225  break;
226  }
227  default:
228  {
229  std::vector<float> float32Row( cols );
230  std::fill( float32Row.begin(), float32Row.end(), value );
231  block.setData( QByteArray::fromRawData( ( char * )&float32Row[0], QgsRasterBlock::typeSize( Qgis::DataType::Float32 ) * cols ) );
232  break;
233  }
234  }
235 
236  const double step = rows > 0 ? 100.0 / rows : 1;
237 
238  for ( int i = 0; i < rows ; i++ )
239  {
240  if ( feedback->isCanceled() )
241  {
242  break;
243  }
244 
245  provider->writeBlock( &block, 1, 0, i );
246  feedback->setProgress( i * step );
247  }
248 
249  QVariantMap outputs;
250  outputs.insert( QStringLiteral( "OUTPUT" ), outputFile );
251  return outputs;
252 }
253 
DataType
Raster data types.
Definition: qgis.h:120
@ 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)
This class represents a coordinate reference system (CRS).
bool isCanceled() const SIP_HOLDGIL
Tells whether the operation has been canceled already.
Definition: qgsfeedback.h:54
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.
Definition: qgsexception.h:83
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.
@ FlagAdvanced
Parameter is an advanced parameter which should be hidden from users by default.
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) SIP_HOLDGIL
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.
Definition: qgsrectangle.h:42
double yMaximum() const SIP_HOLDGIL
Returns the y maximum value (top side of rectangle).
Definition: qgsrectangle.h:193
double xMinimum() const SIP_HOLDGIL
Returns the x minimum value (left side of rectangle).
Definition: qgsrectangle.h:188
double height() const SIP_HOLDGIL
Returns the height of the rectangle.
Definition: qgsrectangle.h:230
double width() const SIP_HOLDGIL
Returns the width of the rectangle.
Definition: qgsrectangle.h:223
const QgsCoordinateReferenceSystem & crs