LCOV - code coverage report
Current view: top level - analysis/processing - qgsalgorithmreclassifybylayer.cpp (source / functions) Hit Total Coverage
Test: coverage.info.cleaned Lines: 0 205 0.0 %
Date: 2021-03-26 12:19:53 Functions: 0 0 -
Branches: 0 0 -

           Branch data     Line data    Source code
       1                 :            : /***************************************************************************
       2                 :            :                          qgsalgorithmreclassifybylayer.cpp
       3                 :            :                          ---------------------
       4                 :            :     begin                : June, 2018
       5                 :            :     copyright            : (C) 2018 by Nyall Dawson
       6                 :            :     email                : nyall dot dawson 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 "qgsalgorithmreclassifybylayer.h"
      19                 :            : #include "qgsrasterfilewriter.h"
      20                 :            : #include "qgsreclassifyutils.h"
      21                 :            : #include "qgsrasteranalysisutils.h"
      22                 :            : #include "qgis.h"
      23                 :            : 
      24                 :            : ///@cond PRIVATE
      25                 :            : 
      26                 :            : //
      27                 :            : // QgsReclassifyAlgorithmBase
      28                 :            : //
      29                 :            : 
      30                 :            : 
      31                 :          0 : QString QgsReclassifyAlgorithmBase::group() const
      32                 :            : {
      33                 :          0 :   return QObject::tr( "Raster analysis" );
      34                 :            : }
      35                 :            : 
      36                 :          0 : QString QgsReclassifyAlgorithmBase::groupId() const
      37                 :            : {
      38                 :          0 :   return QStringLiteral( "rasteranalysis" );
      39                 :            : }
      40                 :            : 
      41                 :          0 : void QgsReclassifyAlgorithmBase::initAlgorithm( const QVariantMap & )
      42                 :            : {
      43                 :          0 :   addParameter( new QgsProcessingParameterRasterLayer( QStringLiteral( "INPUT_RASTER" ),
      44                 :          0 :                 QObject::tr( "Raster layer" ) ) );
      45                 :          0 :   addParameter( new QgsProcessingParameterBand( QStringLiteral( "RASTER_BAND" ),
      46                 :          0 :                 QObject::tr( "Band number" ), 1, QStringLiteral( "INPUT_RASTER" ) ) );
      47                 :            : 
      48                 :          0 :   addAlgorithmParams();
      49                 :            : 
      50                 :          0 :   std::unique_ptr< QgsProcessingParameterNumber > noDataValueParam = std::make_unique< QgsProcessingParameterNumber >( QStringLiteral( "NO_DATA" ),
      51                 :          0 :       QObject::tr( "Output no data value" ), QgsProcessingParameterNumber::Double, -9999 );
      52                 :          0 :   noDataValueParam->setFlags( QgsProcessingParameterDefinition::FlagAdvanced );
      53                 :          0 :   addParameter( noDataValueParam.release() );
      54                 :            : 
      55                 :          0 :   std::unique_ptr< QgsProcessingParameterEnum > boundsHandling = std::make_unique< QgsProcessingParameterEnum >( QStringLiteral( "RANGE_BOUNDARIES" ),
      56                 :          0 :       QObject::tr( "Range boundaries" ), QStringList() << QObject::tr( "min < value <= max" )
      57                 :          0 :       << QObject::tr( "min <= value < max" )
      58                 :          0 :       << QObject::tr( "min <= value <= max" )
      59                 :          0 :       << QObject::tr( "min < value < max" ),
      60                 :          0 :       false, 0 );
      61                 :          0 :   boundsHandling->setFlags( QgsProcessingParameterDefinition::FlagAdvanced );
      62                 :          0 :   addParameter( boundsHandling.release() );
      63                 :            : 
      64                 :          0 :   std::unique_ptr< QgsProcessingParameterBoolean > missingValuesParam = std::make_unique< QgsProcessingParameterBoolean >( QStringLiteral( "NODATA_FOR_MISSING" ),
      65                 :          0 :       QObject::tr( "Use no data when no range matches value" ), false, false );
      66                 :          0 :   missingValuesParam->setFlags( QgsProcessingParameterDefinition::FlagAdvanced );
      67                 :          0 :   addParameter( missingValuesParam.release() );
      68                 :            : 
      69                 :          0 :   std::unique_ptr< QgsProcessingParameterDefinition > typeChoice = QgsRasterAnalysisUtils::createRasterTypeParameter( QStringLiteral( "DATA_TYPE" ), QObject::tr( "Output data type" ), Qgis::Float32 );
      70                 :          0 :   typeChoice->setFlags( QgsProcessingParameterDefinition::FlagAdvanced );
      71                 :          0 :   addParameter( typeChoice.release() );
      72                 :            : 
      73                 :          0 :   addParameter( new QgsProcessingParameterRasterDestination( QStringLiteral( "OUTPUT" ), QObject::tr( "Reclassified raster" ) ) );
      74                 :          0 : }
      75                 :            : 
      76                 :          0 : bool QgsReclassifyAlgorithmBase::prepareAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
      77                 :            : {
      78                 :          0 :   mDataType = QgsRasterAnalysisUtils::rasterTypeChoiceToDataType( parameterAsEnum( parameters, QStringLiteral( "DATA_TYPE" ), context ) );
      79                 :          0 :   QgsRasterLayer *layer = parameterAsRasterLayer( parameters, QStringLiteral( "INPUT_RASTER" ), context );
      80                 :            : 
      81                 :          0 :   if ( !layer )
      82                 :          0 :     throw QgsProcessingException( invalidRasterError( parameters, QStringLiteral( "INPUT_RASTER" ) ) );
      83                 :            : 
      84                 :          0 :   mBand = parameterAsInt( parameters, QStringLiteral( "RASTER_BAND" ), context );
      85                 :          0 :   if ( mBand < 1 || mBand > layer->bandCount() )
      86                 :          0 :     throw QgsProcessingException( QObject::tr( "Invalid band number for RASTER_BAND (%1): Valid values for input raster are 1 to %2" ).arg( mBand )
      87                 :          0 :                                   .arg( layer->bandCount() ) );
      88                 :            : 
      89                 :          0 :   mInterface.reset( layer->dataProvider()->clone() );
      90                 :          0 :   mExtent = layer->extent();
      91                 :          0 :   mCrs = layer->crs();
      92                 :          0 :   mRasterUnitsPerPixelX = std::abs( layer->rasterUnitsPerPixelX() );
      93                 :          0 :   mRasterUnitsPerPixelY = std::abs( layer->rasterUnitsPerPixelY() );
      94                 :          0 :   mNbCellsXProvider = mInterface->xSize();
      95                 :          0 :   mNbCellsYProvider = mInterface->ySize();
      96                 :            : 
      97                 :          0 :   mNoDataValue = parameterAsDouble( parameters, QStringLiteral( "NO_DATA" ), context );
      98                 :          0 :   mUseNoDataForMissingValues = parameterAsBoolean( parameters, QStringLiteral( "NODATA_FOR_MISSING" ), context );
      99                 :            : 
     100                 :          0 :   int boundsType = parameterAsEnum( parameters, QStringLiteral( "RANGE_BOUNDARIES" ), context );
     101                 :          0 :   switch ( boundsType )
     102                 :            :   {
     103                 :            :     case 0:
     104                 :          0 :       mBoundsType = QgsReclassifyUtils::RasterClass::IncludeMax;
     105                 :          0 :       break;
     106                 :            : 
     107                 :            :     case 1:
     108                 :          0 :       mBoundsType = QgsReclassifyUtils::RasterClass::IncludeMin;
     109                 :          0 :       break;
     110                 :            : 
     111                 :            :     case 2:
     112                 :          0 :       mBoundsType = QgsReclassifyUtils::RasterClass::IncludeMinAndMax;
     113                 :          0 :       break;
     114                 :            : 
     115                 :            :     case 3:
     116                 :          0 :       mBoundsType = QgsReclassifyUtils::RasterClass::Exclusive;
     117                 :          0 :       break;
     118                 :            :   }
     119                 :            : 
     120                 :          0 :   return _prepareAlgorithm( parameters, context, feedback );
     121                 :          0 : }
     122                 :            : 
     123                 :          0 : QVariantMap QgsReclassifyAlgorithmBase::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
     124                 :            : {
     125                 :          0 :   QVector< QgsReclassifyUtils::RasterClass > classes = createClasses( mBoundsType, parameters, context, feedback );
     126                 :            : 
     127                 :          0 :   QgsReclassifyUtils::reportClasses( classes, feedback );
     128                 :          0 :   QgsReclassifyUtils::checkForOverlaps( classes, feedback );
     129                 :            : 
     130                 :          0 :   const QString outputFile = parameterAsOutputLayer( parameters, QStringLiteral( "OUTPUT" ), context );
     131                 :          0 :   QFileInfo fi( outputFile );
     132                 :          0 :   const QString outputFormat = QgsRasterFileWriter::driverForExtension( fi.suffix() );
     133                 :            : 
     134                 :          0 :   std::unique_ptr< QgsRasterFileWriter > writer = std::make_unique< QgsRasterFileWriter >( outputFile );
     135                 :          0 :   writer->setOutputProviderKey( QStringLiteral( "gdal" ) );
     136                 :          0 :   writer->setOutputFormat( outputFormat );
     137                 :          0 :   std::unique_ptr<QgsRasterDataProvider > provider( writer->createOneBandRaster( mDataType, mNbCellsXProvider, mNbCellsYProvider, mExtent, mCrs ) );
     138                 :          0 :   if ( !provider )
     139                 :          0 :     throw QgsProcessingException( QObject::tr( "Could not create raster output: %1" ).arg( outputFile ) );
     140                 :          0 :   if ( !provider->isValid() )
     141                 :          0 :     throw QgsProcessingException( QObject::tr( "Could not create raster output %1: %2" ).arg( outputFile, provider->error().message( QgsErrorMessage::Text ) ) );
     142                 :            : 
     143                 :          0 :   provider->setNoDataValue( 1, mNoDataValue );
     144                 :            : 
     145                 :          0 :   QgsReclassifyUtils::reclassify( classes, mInterface.get(), mBand, mExtent, mNbCellsXProvider, mNbCellsYProvider, provider.get(), mNoDataValue, mUseNoDataForMissingValues,
     146                 :          0 :                                   feedback );
     147                 :            : 
     148                 :          0 :   QVariantMap outputs;
     149                 :          0 :   outputs.insert( QStringLiteral( "OUTPUT" ), outputFile );
     150                 :          0 :   return outputs;
     151                 :          0 : }
     152                 :            : 
     153                 :            : 
     154                 :            : //
     155                 :            : // QgsReclassifyByLayerAlgorithm
     156                 :            : //
     157                 :            : 
     158                 :          0 : QString QgsReclassifyByLayerAlgorithm::name() const
     159                 :            : {
     160                 :          0 :   return QStringLiteral( "reclassifybylayer" );
     161                 :            : }
     162                 :            : 
     163                 :          0 : QString QgsReclassifyByLayerAlgorithm::displayName() const
     164                 :            : {
     165                 :          0 :   return QObject::tr( "Reclassify by layer" );
     166                 :            : }
     167                 :            : 
     168                 :          0 : QStringList QgsReclassifyByLayerAlgorithm::tags() const
     169                 :            : {
     170                 :          0 :   return QObject::tr( "raster,reclassify,classes,calculator" ).split( ',' );
     171                 :          0 : }
     172                 :            : 
     173                 :          0 : QString QgsReclassifyByLayerAlgorithm::shortHelpString() const
     174                 :            : {
     175                 :          0 :   return QObject::tr( "This algorithm reclassifies a raster band by assigning new class values based on the ranges specified in a vector table." );
     176                 :            : }
     177                 :            : 
     178                 :          0 : QgsReclassifyByLayerAlgorithm *QgsReclassifyByLayerAlgorithm::createInstance() const
     179                 :            : {
     180                 :          0 :   return new QgsReclassifyByLayerAlgorithm();
     181                 :          0 : }
     182                 :            : 
     183                 :          0 : void QgsReclassifyByLayerAlgorithm::addAlgorithmParams()
     184                 :            : {
     185                 :          0 :   addParameter( new QgsProcessingParameterFeatureSource( QStringLiteral( "INPUT_TABLE" ),
     186                 :          0 :                 QObject::tr( "Layer containing class breaks" ), QList< int >() << QgsProcessing::TypeVector ) );
     187                 :          0 :   addParameter( new QgsProcessingParameterField( QStringLiteral( "MIN_FIELD" ),
     188                 :          0 :                 QObject::tr( "Minimum class value field" ), QVariant(), QStringLiteral( "INPUT_TABLE" ), QgsProcessingParameterField::Numeric ) );
     189                 :          0 :   addParameter( new QgsProcessingParameterField( QStringLiteral( "MAX_FIELD" ),
     190                 :          0 :                 QObject::tr( "Maximum class value field" ), QVariant(), QStringLiteral( "INPUT_TABLE" ), QgsProcessingParameterField::Numeric ) );
     191                 :          0 :   addParameter( new QgsProcessingParameterField( QStringLiteral( "VALUE_FIELD" ),
     192                 :          0 :                 QObject::tr( "Output value field" ), QVariant(), QStringLiteral( "INPUT_TABLE" ), QgsProcessingParameterField::Numeric ) );
     193                 :          0 : }
     194                 :            : 
     195                 :          0 : bool QgsReclassifyByLayerAlgorithm::_prepareAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback * )
     196                 :            : {
     197                 :          0 :   std::unique_ptr< QgsFeatureSource >tableSource( parameterAsSource( parameters, QStringLiteral( "INPUT_TABLE" ), context ) );
     198                 :          0 :   if ( !tableSource )
     199                 :          0 :     throw QgsProcessingException( invalidSourceError( parameters, QStringLiteral( "INPUT_TABLE" ) ) );
     200                 :            : 
     201                 :          0 :   QString fieldMin = parameterAsString( parameters, QStringLiteral( "MIN_FIELD" ), context );
     202                 :          0 :   mMinFieldIdx = tableSource->fields().lookupField( fieldMin );
     203                 :          0 :   if ( mMinFieldIdx < 0 )
     204                 :          0 :     throw QgsProcessingException( QObject::tr( "Invalid field specified for MIN_FIELD: %1" ).arg( fieldMin ) );
     205                 :          0 :   QString fieldMax = parameterAsString( parameters, QStringLiteral( "MAX_FIELD" ), context );
     206                 :          0 :   mMaxFieldIdx = tableSource->fields().lookupField( fieldMax );
     207                 :          0 :   if ( mMaxFieldIdx < 0 )
     208                 :          0 :     throw QgsProcessingException( QObject::tr( "Invalid field specified for MAX_FIELD: %1" ).arg( fieldMax ) );
     209                 :          0 :   QString fieldValue = parameterAsString( parameters, QStringLiteral( "VALUE_FIELD" ), context );
     210                 :          0 :   mValueFieldIdx = tableSource->fields().lookupField( fieldValue );
     211                 :          0 :   if ( mValueFieldIdx < 0 )
     212                 :          0 :     throw QgsProcessingException( QObject::tr( "Invalid field specified for VALUE_FIELD: %1" ).arg( fieldValue ) );
     213                 :            : 
     214                 :          0 :   QgsFeatureRequest request;
     215                 :          0 :   request.setFlags( QgsFeatureRequest::NoGeometry );
     216                 :          0 :   request.setSubsetOfAttributes( QgsAttributeList() << mMinFieldIdx << mMaxFieldIdx << mValueFieldIdx );
     217                 :          0 :   mTableIterator = tableSource->getFeatures( request );
     218                 :            : 
     219                 :            :   return true;
     220                 :          0 : }
     221                 :            : 
     222                 :          0 : QVector<QgsReclassifyUtils::RasterClass> QgsReclassifyByLayerAlgorithm::createClasses( QgsRasterRange::BoundsType boundsType, const QVariantMap &, QgsProcessingContext &, QgsProcessingFeedback * )
     223                 :            : {
     224                 :          0 :   QVector< QgsReclassifyUtils::RasterClass > classes;
     225                 :          0 :   QgsFeature f;
     226                 :          0 :   while ( mTableIterator.nextFeature( f ) )
     227                 :            :   {
     228                 :          0 :     bool ok = false;
     229                 :            : 
     230                 :            :     // null values map to nan, which corresponds to a range extended to +/- infinity....
     231                 :          0 :     const QVariant minVariant = f.attribute( mMinFieldIdx );
     232                 :            :     double minValue;
     233                 :          0 :     if ( minVariant.isNull() || minVariant.toString().isEmpty() )
     234                 :            :     {
     235                 :          0 :       minValue = std::numeric_limits<double>::quiet_NaN();
     236                 :          0 :     }
     237                 :            :     else
     238                 :            :     {
     239                 :          0 :       minValue = minVariant.toDouble( &ok );
     240                 :          0 :       if ( !ok )
     241                 :          0 :         throw QgsProcessingException( QObject::tr( "Invalid value for minimum: %1" ).arg( minVariant.toString() ) );
     242                 :            :     }
     243                 :          0 :     const QVariant maxVariant = f.attribute( mMaxFieldIdx );
     244                 :            :     double maxValue;
     245                 :          0 :     if ( maxVariant.isNull() || maxVariant.toString().isEmpty() )
     246                 :            :     {
     247                 :          0 :       maxValue = std::numeric_limits<double>::quiet_NaN();
     248                 :          0 :       ok = true;
     249                 :          0 :     }
     250                 :            :     else
     251                 :            :     {
     252                 :          0 :       maxValue = maxVariant.toDouble( &ok );
     253                 :          0 :       if ( !ok )
     254                 :          0 :         throw QgsProcessingException( QObject::tr( "Invalid value for maximum: %1" ).arg( maxVariant.toString() ) );
     255                 :            :     }
     256                 :            : 
     257                 :          0 :     const double value = f.attribute( mValueFieldIdx ).toDouble( &ok );
     258                 :          0 :     if ( !ok )
     259                 :          0 :       throw QgsProcessingException( QObject::tr( "Invalid output value: %1" ).arg( f.attribute( mValueFieldIdx ).toString() ) );
     260                 :            : 
     261                 :          0 :     classes << QgsReclassifyUtils::RasterClass( minValue, maxValue, boundsType, value );
     262                 :          0 :   }
     263                 :          0 :   return classes;
     264                 :          0 : }
     265                 :            : 
     266                 :            : 
     267                 :            : //
     268                 :            : // QgsReclassifyByTableAlgorithm
     269                 :            : //
     270                 :            : 
     271                 :          0 : QString QgsReclassifyByTableAlgorithm::name() const
     272                 :            : {
     273                 :          0 :   return QStringLiteral( "reclassifybytable" );
     274                 :            : }
     275                 :            : 
     276                 :          0 : QString QgsReclassifyByTableAlgorithm::displayName() const
     277                 :            : {
     278                 :          0 :   return QObject::tr( "Reclassify by table" );
     279                 :            : }
     280                 :            : 
     281                 :          0 : QStringList QgsReclassifyByTableAlgorithm::tags() const
     282                 :            : {
     283                 :          0 :   return QObject::tr( "raster,reclassify,classes,calculator" ).split( ',' );
     284                 :          0 : }
     285                 :            : 
     286                 :          0 : QString QgsReclassifyByTableAlgorithm::shortHelpString() const
     287                 :            : {
     288                 :          0 :   return QObject::tr( "This algorithm reclassifies a raster band by assigning new class values based on the ranges specified in a fixed table." );
     289                 :            : }
     290                 :            : 
     291                 :          0 : QgsReclassifyByTableAlgorithm *QgsReclassifyByTableAlgorithm::createInstance() const
     292                 :            : {
     293                 :          0 :   return new QgsReclassifyByTableAlgorithm();
     294                 :          0 : }
     295                 :            : 
     296                 :          0 : void QgsReclassifyByTableAlgorithm::addAlgorithmParams()
     297                 :            : {
     298                 :          0 :   addParameter( new QgsProcessingParameterMatrix( QStringLiteral( "TABLE" ),
     299                 :          0 :                 QObject::tr( "Reclassification table" ),
     300                 :          0 :                 1, false, QStringList() << QObject::tr( "Minimum" )
     301                 :          0 :                 << QObject::tr( "Maximum" )
     302                 :          0 :                 << QObject::tr( "Value" ) ) );
     303                 :          0 : }
     304                 :            : 
     305                 :          0 : bool QgsReclassifyByTableAlgorithm::_prepareAlgorithm( const QVariantMap &, QgsProcessingContext &, QgsProcessingFeedback * )
     306                 :            : {
     307                 :          0 :   return true;
     308                 :            : }
     309                 :            : 
     310                 :          0 : QVector<QgsReclassifyUtils::RasterClass> QgsReclassifyByTableAlgorithm::createClasses( QgsReclassifyUtils::RasterClass::BoundsType boundsType, const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback * )
     311                 :            : {
     312                 :          0 :   const QVariantList table = parameterAsMatrix( parameters, QStringLiteral( "TABLE" ), context );
     313                 :          0 :   if ( table.count() % 3 != 0 )
     314                 :          0 :     throw QgsProcessingException( QObject::tr( "Invalid value for TABLE: list must contain a multiple of 3 elements (found %1)" ).arg( table.count() ) );
     315                 :            : 
     316                 :          0 :   const int rows = table.count() / 3;
     317                 :          0 :   QVector< QgsReclassifyUtils::RasterClass > classes;
     318                 :          0 :   classes.reserve( rows );
     319                 :          0 :   for ( int row = 0; row < rows; ++row )
     320                 :            :   {
     321                 :          0 :     bool ok = false;
     322                 :            : 
     323                 :            :     // null values map to nan, which corresponds to a range extended to +/- infinity....
     324                 :          0 :     const QVariant minVariant = table.at( row * 3 );
     325                 :            :     double minValue;
     326                 :          0 :     if ( minVariant.isNull()  || minVariant.toString().isEmpty() )
     327                 :            :     {
     328                 :          0 :       minValue = std::numeric_limits<double>::quiet_NaN();
     329                 :          0 :     }
     330                 :            :     else
     331                 :            :     {
     332                 :          0 :       minValue = minVariant.toDouble( &ok );
     333                 :          0 :       if ( !ok )
     334                 :          0 :         throw QgsProcessingException( QObject::tr( "Invalid value for minimum: %1" ).arg( table.at( row * 3 ).toString() ) );
     335                 :            :     }
     336                 :          0 :     const QVariant maxVariant = table.at( row * 3 + 1 );
     337                 :            :     double maxValue;
     338                 :          0 :     if ( maxVariant.isNull() || maxVariant.toString().isEmpty() )
     339                 :            :     {
     340                 :          0 :       maxValue = std::numeric_limits<double>::quiet_NaN();
     341                 :          0 :       ok = true;
     342                 :          0 :     }
     343                 :            :     else
     344                 :            :     {
     345                 :          0 :       maxValue = maxVariant.toDouble( &ok );
     346                 :          0 :       if ( !ok )
     347                 :          0 :         throw QgsProcessingException( QObject::tr( "Invalid value for maximum: %1" ).arg( table.at( row * 3 + 1 ).toString() ) );
     348                 :            :     }
     349                 :            : 
     350                 :          0 :     const double value = table.at( row * 3 + 2 ).toDouble( &ok );
     351                 :          0 :     if ( !ok )
     352                 :          0 :       throw QgsProcessingException( QObject::tr( "Invalid output value: %1" ).arg( table.at( row * 3 + 2 ).toString() ) );
     353                 :            : 
     354                 :          0 :     classes << QgsReclassifyUtils::RasterClass( minValue, maxValue, boundsType, value );
     355                 :          0 :   }
     356                 :          0 :   return classes;
     357                 :          0 : }
     358                 :            : 
     359                 :            : ///@endcond
     360                 :            : 
     361                 :            : 

Generated by: LCOV version 1.14