LCOV - code coverage report
Current view: top level - analysis/processing - qgsalgorithmconstantraster.cpp (source / functions) Hit Total Coverage
Test: coverage.info.cleaned Lines: 0 138 0.0 %
Date: 2021-04-10 08:29:14 Functions: 0 0 -
Branches: 0 0 -

           Branch data     Line data    Source code
       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"
      20                 :            : #include "qgsalgorithmconstantraster.h"
      21                 :            : #include "qgsrasterfilewriter.h"
      22                 :            : 
      23                 :            : ///@cond PRIVATE
      24                 :            : 
      25                 :          0 : QString QgsConstantRasterAlgorithm::name() const
      26                 :            : {
      27                 :          0 :   return QStringLiteral( "createconstantrasterlayer" );
      28                 :            : }
      29                 :            : 
      30                 :          0 : QString QgsConstantRasterAlgorithm::displayName() const
      31                 :            : {
      32                 :          0 :   return QObject::tr( "Create constant raster layer" );
      33                 :            : }
      34                 :            : 
      35                 :          0 : QStringList QgsConstantRasterAlgorithm::tags() const
      36                 :            : {
      37                 :          0 :   return QObject::tr( "raster,create,constant" ).split( ',' );
      38                 :          0 : }
      39                 :            : 
      40                 :          0 : QString QgsConstantRasterAlgorithm::group() const
      41                 :            : {
      42                 :          0 :   return QObject::tr( "Raster creation" );
      43                 :            : }
      44                 :            : 
      45                 :          0 : QString QgsConstantRasterAlgorithm::groupId() const
      46                 :            : {
      47                 :          0 :   return QStringLiteral( "rastercreation" );
      48                 :            : }
      49                 :            : 
      50                 :          0 : QString QgsConstantRasterAlgorithm::shortHelpString() const
      51                 :            : {
      52                 :          0 :   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                 :          0 : QgsConstantRasterAlgorithm *QgsConstantRasterAlgorithm::createInstance() const
      60                 :            : {
      61                 :          0 :   return new QgsConstantRasterAlgorithm();
      62                 :            : }
      63                 :            : 
      64                 :          0 : void QgsConstantRasterAlgorithm::initAlgorithm( const QVariantMap & )
      65                 :            : {
      66                 :          0 :   addParameter( new QgsProcessingParameterExtent( QStringLiteral( "EXTENT" ), QObject::tr( "Desired extent" ) ) );
      67                 :          0 :   addParameter( new QgsProcessingParameterCrs( QStringLiteral( "TARGET_CRS" ), QObject::tr( "Target CRS" ), QStringLiteral( "ProjectCrs" ) ) );
      68                 :          0 :   addParameter( new QgsProcessingParameterNumber( QStringLiteral( "PIXEL_SIZE" ), QObject::tr( "Pixel size" ),
      69                 :          0 :                 QgsProcessingParameterNumber::Double, 0.00001, false, 0.01 ) );
      70                 :          0 :   addParameter( new QgsProcessingParameterNumber( QStringLiteral( "NUMBER" ), QObject::tr( "Constant value" ),
      71                 :          0 :                 QgsProcessingParameterNumber::Double, 1, false ) );
      72                 :            : 
      73                 :          0 :   QStringList rasterDataTypes; //currently supported raster data types that can be handled QgsRasterBlock::writeValue()
      74                 :          0 :   rasterDataTypes << QStringLiteral( "Byte" )
      75                 :          0 :                   << QStringLiteral( "Integer16" )
      76                 :          0 :                   << QStringLiteral( "Unsigned Integer16" )
      77                 :          0 :                   << QStringLiteral( "Integer32" )
      78                 :          0 :                   << QStringLiteral( "Unsigned Integer32" )
      79                 :          0 :                   << QStringLiteral( "Float32" )
      80                 :          0 :                   << QStringLiteral( "Float64" );
      81                 :            : 
      82                 :            :   //QGIS3: parameter set to Float32 by default so that existing models/scripts don't break
      83                 :          0 :   std::unique_ptr< QgsProcessingParameterDefinition > rasterTypeParameter = std::make_unique< QgsProcessingParameterEnum >( QStringLiteral( "OUTPUT_TYPE" ), QObject::tr( "Output raster data type" ),  rasterDataTypes, false, 5, false );
      84                 :          0 :   rasterTypeParameter->setFlags( QgsProcessingParameterDefinition::FlagAdvanced );
      85                 :          0 :   addParameter( rasterTypeParameter.release() );
      86                 :            : 
      87                 :          0 :   addParameter( new QgsProcessingParameterRasterDestination( QStringLiteral( "OUTPUT" ), QObject::tr( "Constant" ) ) );
      88                 :          0 : }
      89                 :            : 
      90                 :          0 : QVariantMap QgsConstantRasterAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
      91                 :            : {
      92                 :          0 :   QgsCoordinateReferenceSystem crs = parameterAsCrs( parameters, QStringLiteral( "TARGET_CRS" ), context );
      93                 :          0 :   QgsRectangle extent = parameterAsExtent( parameters, QStringLiteral( "EXTENT" ), context, crs );
      94                 :          0 :   double pixelSize = parameterAsDouble( parameters, QStringLiteral( "PIXEL_SIZE" ), context );
      95                 :          0 :   double value = parameterAsDouble( parameters, QStringLiteral( "NUMBER" ), context );
      96                 :          0 :   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                 :          0 :   fractpart = abs( std::modf( value, &intpart ) ); //@abs: negative values may be entered
     102                 :            : 
     103                 :          0 :   Qgis::DataType rasterDataType = Qgis::Float32; //standard output type
     104                 :          0 :   switch ( typeId )
     105                 :            :   {
     106                 :            :     case 0:
     107                 :          0 :       rasterDataType = Qgis::Byte;
     108                 :          0 :       if ( value < std::numeric_limits<quint8>::min() || value > std::numeric_limits<quint8>::max() )
     109                 :          0 :         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                 :          0 :       if ( fractpart > 0 )
     111                 :          0 :         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                 :          0 :       break;
     113                 :            :     case 1:
     114                 :          0 :       rasterDataType = Qgis::Int16;
     115                 :          0 :       if ( value < std::numeric_limits<qint16>::min() || value > std::numeric_limits<qint16>::max() )
     116                 :          0 :         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                 :          0 :       if ( fractpart > 0 )
     118                 :          0 :         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                 :          0 :       break;
     120                 :            :     case 2:
     121                 :          0 :       rasterDataType = Qgis::UInt16;
     122                 :          0 :       if ( value < std::numeric_limits<quint16>::min() || value > std::numeric_limits<quint16>::max() )
     123                 :          0 :         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                 :          0 :       if ( fractpart > 0 )
     125                 :          0 :         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                 :          0 :       break;
     127                 :            :     case 3:
     128                 :          0 :       rasterDataType = Qgis::Int32;
     129                 :          0 :       if ( value < std::numeric_limits<qint32>::min() || value > std::numeric_limits<qint32>::max() )
     130                 :          0 :         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                 :          0 :       if ( fractpart > 0 )
     132                 :          0 :         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                 :          0 :       break;
     134                 :            :     case 4:
     135                 :          0 :       rasterDataType = Qgis::UInt32;
     136                 :          0 :       if ( value < std::numeric_limits<quint32>::min() || value > std::numeric_limits<quint32>::max() )
     137                 :          0 :         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                 :          0 :       if ( fractpart > 0 )
     139                 :          0 :         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                 :          0 :       break;
     141                 :            :     case 5:
     142                 :          0 :       rasterDataType = Qgis::Float32;
     143                 :          0 :       break;
     144                 :            :     case 6:
     145                 :          0 :       rasterDataType = Qgis::Float64;
     146                 :          0 :       break;
     147                 :            :     default:
     148                 :          0 :       break;
     149                 :            :   }
     150                 :          0 :   const QString outputFile = parameterAsOutputLayer( parameters, QStringLiteral( "OUTPUT" ), context );
     151                 :          0 :   QFileInfo fi( outputFile );
     152                 :          0 :   const QString outputFormat = QgsRasterFileWriter::driverForExtension( fi.suffix() );
     153                 :            : 
     154                 :          0 :   int rows = std::max( std::ceil( extent.height() / pixelSize ), 1.0 );
     155                 :          0 :   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                 :          0 :   QgsRectangle rasterExtent = QgsRectangle( extent.xMinimum(), extent.yMaximum() - ( rows * pixelSize ), extent.xMinimum() + ( cols * pixelSize ), extent.yMaximum() );
     160                 :            : 
     161                 :          0 :   std::unique_ptr< QgsRasterFileWriter > writer = std::make_unique< QgsRasterFileWriter >( outputFile );
     162                 :          0 :   writer->setOutputProviderKey( QStringLiteral( "gdal" ) );
     163                 :          0 :   writer->setOutputFormat( outputFormat );
     164                 :          0 :   std::unique_ptr<QgsRasterDataProvider > provider( writer->createOneBandRaster( rasterDataType, cols, rows, rasterExtent, crs ) );
     165                 :          0 :   if ( !provider )
     166                 :          0 :     throw QgsProcessingException( QObject::tr( "Could not create raster output: %1" ).arg( outputFile ) );
     167                 :          0 :   if ( !provider->isValid() )
     168                 :          0 :     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                 :          0 :   QgsRasterBlock block( rasterDataType, cols, 1 );
     176                 :          0 :   switch ( typeId )
     177                 :            :   {
     178                 :            :     case 0:
     179                 :            :     {
     180                 :          0 :       std::vector<quint8> byteRow( cols );
     181                 :          0 :       std::fill( byteRow.begin(), byteRow.end(), value );
     182                 :          0 :       block.setData( QByteArray::fromRawData( ( char * )&byteRow[0], QgsRasterBlock::typeSize( Qgis::Byte ) * cols ) );
     183                 :            :       break;
     184                 :          0 :     }
     185                 :            :     case 1:
     186                 :            :     {
     187                 :          0 :       std::vector<qint16> int16Row( cols );
     188                 :          0 :       std::fill( int16Row.begin(), int16Row.end(), value );
     189                 :          0 :       block.setData( QByteArray::fromRawData( ( char * )&int16Row[0], QgsRasterBlock::typeSize( Qgis::Int16 ) * cols ) );
     190                 :            :       break;
     191                 :          0 :     }
     192                 :            :     case 2:
     193                 :            :     {
     194                 :          0 :       std::vector<quint16> uInt16Row( cols );
     195                 :          0 :       std::fill( uInt16Row.begin(), uInt16Row.end(), value );
     196                 :          0 :       block.setData( QByteArray::fromRawData( ( char * )&uInt16Row[0], QgsRasterBlock::typeSize( Qgis::UInt16 ) * cols ) );
     197                 :            :       break;
     198                 :          0 :     }
     199                 :            :     case 3:
     200                 :            :     {
     201                 :          0 :       std::vector<qint32> int32Row( cols );
     202                 :          0 :       std::fill( int32Row.begin(), int32Row.end(), value );
     203                 :          0 :       block.setData( QByteArray::fromRawData( ( char * )&int32Row[0], QgsRasterBlock::typeSize( Qgis::Int32 ) * cols ) );
     204                 :            :       break;
     205                 :          0 :     }
     206                 :            :     case 4:
     207                 :            :     {
     208                 :          0 :       std::vector<quint32> uInt32Row( cols );
     209                 :          0 :       std::fill( uInt32Row.begin(), uInt32Row.end(), value );
     210                 :          0 :       block.setData( QByteArray::fromRawData( ( char * )&uInt32Row[0], QgsRasterBlock::typeSize( Qgis::UInt32 ) * cols ) );
     211                 :            :       break;
     212                 :          0 :     }
     213                 :            :     case 5:
     214                 :            :     {
     215                 :          0 :       std::vector<float> float32Row( cols );
     216                 :          0 :       std::fill( float32Row.begin(), float32Row.end(), value );
     217                 :          0 :       block.setData( QByteArray::fromRawData( ( char * )&float32Row[0], QgsRasterBlock::typeSize( Qgis::Float32 ) * cols ) );
     218                 :            :       break;
     219                 :          0 :     }
     220                 :            :     case 6:
     221                 :            :     {
     222                 :          0 :       std::vector<double> float64Row( cols );
     223                 :          0 :       std::fill( float64Row.begin(), float64Row.end(), value );
     224                 :          0 :       block.setData( QByteArray::fromRawData( ( char * )&float64Row[0], QgsRasterBlock::typeSize( Qgis::Float64 ) * cols ) );
     225                 :            :       break;
     226                 :          0 :     }
     227                 :            :     default:
     228                 :            :     {
     229                 :          0 :       std::vector<float> float32Row( cols );
     230                 :          0 :       std::fill( float32Row.begin(), float32Row.end(), value );
     231                 :          0 :       block.setData( QByteArray::fromRawData( ( char * )&float32Row[0], QgsRasterBlock::typeSize( Qgis::Float32 ) * cols ) );
     232                 :            :       break;
     233                 :          0 :     }
     234                 :            :   }
     235                 :            : 
     236                 :          0 :   double step = rows > 0 ? 100.0 / rows : 1;
     237                 :            : 
     238                 :          0 :   for ( int i = 0; i < rows ; i++ )
     239                 :            :   {
     240                 :          0 :     if ( feedback->isCanceled() )
     241                 :            :     {
     242                 :          0 :       break;
     243                 :            :     }
     244                 :            : 
     245                 :          0 :     provider->writeBlock( &block, 1, 0, i );
     246                 :          0 :     feedback->setProgress( i * step );
     247                 :          0 :   }
     248                 :            : 
     249                 :          0 :   QVariantMap outputs;
     250                 :          0 :   outputs.insert( QStringLiteral( "OUTPUT" ), outputFile );
     251                 :          0 :   return outputs;
     252                 :          0 : }
     253                 :            : 
     254                 :            : ///@endcond

Generated by: LCOV version 1.14