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

           Branch data     Line data    Source code
       1                 :            : /***************************************************************************
       2                 :            :                          qgsalgorithmcellstatistics.cpp
       3                 :            :                          ---------------------
       4                 :            :     begin                : May 2020
       5                 :            :     copyright            : (C) 2020 by Clemens Raffler
       6                 :            :     email                : clemens dot raffler 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 "qgsalgorithmcellstatistics.h"
      19                 :            : #include "qgsrasterprojector.h"
      20                 :            : #include "qgsrasterfilewriter.h"
      21                 :            : #include "qgsrasteranalysisutils.h"
      22                 :            : 
      23                 :            : ///@cond PRIVATE
      24                 :            : 
      25                 :            : 
      26                 :          0 : QString QgsCellStatisticsAlgorithmBase::group() const
      27                 :            : {
      28                 :          0 :   return QObject::tr( "Raster analysis" );
      29                 :            : }
      30                 :            : 
      31                 :          0 : QString QgsCellStatisticsAlgorithmBase::groupId() const
      32                 :            : {
      33                 :          0 :   return QStringLiteral( "rasteranalysis" );
      34                 :            : }
      35                 :            : 
      36                 :          0 : void QgsCellStatisticsAlgorithmBase::initAlgorithm( const QVariantMap & )
      37                 :            : {
      38                 :          0 :   addParameter( new QgsProcessingParameterMultipleLayers( QStringLiteral( "INPUT" ),
      39                 :          0 :                 QObject::tr( "Input layers" ), QgsProcessing::TypeRaster ) );
      40                 :            : 
      41                 :          0 :   addSpecificAlgorithmParams();
      42                 :            : 
      43                 :          0 :   addParameter( new QgsProcessingParameterBoolean( QStringLiteral( "IGNORE_NODATA" ), QObject::tr( "Ignore NoData values" ), true ) );
      44                 :            : 
      45                 :          0 :   addParameter( new QgsProcessingParameterRasterLayer( QStringLiteral( "REFERENCE_LAYER" ), QObject::tr( "Reference layer" ) ) );
      46                 :            : 
      47                 :          0 :   std::unique_ptr< QgsProcessingParameterNumber > output_nodata_parameter = std::make_unique< QgsProcessingParameterNumber >( QStringLiteral( "OUTPUT_NODATA_VALUE" ), QObject::tr( "Output NoData value" ), QgsProcessingParameterNumber::Double, -9999, false );
      48                 :          0 :   output_nodata_parameter->setFlags( output_nodata_parameter->flags() | QgsProcessingParameterDefinition::FlagAdvanced );
      49                 :          0 :   addParameter( output_nodata_parameter.release() );
      50                 :            : 
      51                 :          0 :   addParameter( new QgsProcessingParameterRasterDestination( QStringLiteral( "OUTPUT" ),
      52                 :          0 :                 QObject::tr( "Output layer" ) ) );
      53                 :            : 
      54                 :          0 :   addOutput( new QgsProcessingOutputString( QStringLiteral( "EXTENT" ), QObject::tr( "Extent" ) ) );
      55                 :          0 :   addOutput( new QgsProcessingOutputString( QStringLiteral( "CRS_AUTHID" ), QObject::tr( "CRS authority identifier" ) ) );
      56                 :          0 :   addOutput( new QgsProcessingOutputNumber( QStringLiteral( "WIDTH_IN_PIXELS" ), QObject::tr( "Width in pixels" ) ) );
      57                 :          0 :   addOutput( new QgsProcessingOutputNumber( QStringLiteral( "HEIGHT_IN_PIXELS" ), QObject::tr( "Height in pixels" ) ) );
      58                 :          0 :   addOutput( new QgsProcessingOutputNumber( QStringLiteral( "TOTAL_PIXEL_COUNT" ), QObject::tr( "Total pixel count" ) ) );
      59                 :          0 : }
      60                 :            : 
      61                 :          0 : bool QgsCellStatisticsAlgorithmBase::prepareAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
      62                 :            : {
      63                 :          0 :   QgsRasterLayer *referenceLayer = parameterAsRasterLayer( parameters, QStringLiteral( "REFERENCE_LAYER" ), context );
      64                 :          0 :   if ( !referenceLayer )
      65                 :          0 :     throw QgsProcessingException( invalidRasterError( parameters, QStringLiteral( "REFERENCE_LAYER" ) ) );
      66                 :            : 
      67                 :          0 :   mIgnoreNoData = parameterAsBool( parameters, QStringLiteral( "IGNORE_NODATA" ), context );
      68                 :          0 :   mNoDataValue = parameterAsDouble( parameters, QStringLiteral( "OUTPUT_NODATA_VALUE" ), context );
      69                 :          0 :   mCrs = referenceLayer->crs();
      70                 :          0 :   mRasterUnitsPerPixelX = referenceLayer->rasterUnitsPerPixelX();
      71                 :          0 :   mRasterUnitsPerPixelY = referenceLayer->rasterUnitsPerPixelY();
      72                 :          0 :   mLayerWidth = referenceLayer->width();
      73                 :          0 :   mLayerHeight = referenceLayer->height();
      74                 :          0 :   mExtent = referenceLayer->extent();
      75                 :            : 
      76                 :          0 :   const QList< QgsMapLayer * > layers = parameterAsLayerList( parameters, QStringLiteral( "INPUT" ), context );
      77                 :          0 :   QList< QgsRasterLayer * > rasterLayers;
      78                 :          0 :   rasterLayers.reserve( layers.count() );
      79                 :          0 :   for ( QgsMapLayer *l : layers )
      80                 :            :   {
      81                 :          0 :     if ( feedback->isCanceled() )
      82                 :          0 :       break; //in case some slow data sources are loaded
      83                 :            : 
      84                 :          0 :     if ( l->type() == QgsMapLayerType::RasterLayer )
      85                 :            :     {
      86                 :          0 :       QgsRasterLayer *layer = qobject_cast< QgsRasterLayer * >( l );
      87                 :          0 :       QgsRasterAnalysisUtils::RasterLogicInput input;
      88                 :          0 :       const int band = 1; //could be made dynamic
      89                 :          0 :       input.hasNoDataValue = layer->dataProvider()->sourceHasNoDataValue( band );
      90                 :          0 :       input.sourceDataProvider.reset( layer->dataProvider()->clone() );
      91                 :          0 :       input.interface = input.sourceDataProvider.get();
      92                 :            :       // add projector if necessary
      93                 :          0 :       if ( layer->crs() != mCrs )
      94                 :            :       {
      95                 :          0 :         input.projector = std::make_unique< QgsRasterProjector >();
      96                 :          0 :         input.projector->setInput( input.sourceDataProvider.get() );
      97                 :          0 :         input.projector->setCrs( layer->crs(), mCrs, context.transformContext() );
      98                 :          0 :         input.interface = input.projector.get();
      99                 :          0 :       }
     100                 :          0 :       mInputs.emplace_back( std::move( input ) );
     101                 :          0 :     }
     102                 :            :   }
     103                 :            : 
     104                 :            :   //determine output raster data type
     105                 :            :   //initially raster data type to most primitive data type that is possible
     106                 :          0 :   mDataType = Qgis::Byte;
     107                 :          0 :   for ( const QgsRasterAnalysisUtils::RasterLogicInput &i : std::as_const( mInputs ) )
     108                 :            :   {
     109                 :          0 :     for ( int band : i.bands )
     110                 :            :     {
     111                 :          0 :       Qgis::DataType inputDataType = i.interface->dataType( band );
     112                 :          0 :       if ( static_cast<int>( mDataType ) < static_cast<int>( inputDataType ) )
     113                 :          0 :         mDataType = inputDataType; //if raster data type is more potent, set it as new data type
     114                 :            :     }
     115                 :            :   }
     116                 :            : 
     117                 :          0 :   prepareSpecificAlgorithmParameters( parameters, context, feedback );
     118                 :            : 
     119                 :            :   return true;
     120                 :          0 : }
     121                 :            : 
     122                 :            : 
     123                 :          0 : QVariantMap QgsCellStatisticsAlgorithmBase::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
     124                 :            : {
     125                 :          0 :   const QString outputFile = parameterAsOutputLayer( parameters, QStringLiteral( "OUTPUT" ), context );
     126                 :          0 :   QFileInfo fi( outputFile );
     127                 :          0 :   const QString outputFormat = QgsRasterFileWriter::driverForExtension( fi.suffix() );
     128                 :            : 
     129                 :          0 :   std::unique_ptr< QgsRasterFileWriter > writer = std::make_unique< QgsRasterFileWriter >( outputFile );
     130                 :          0 :   writer->setOutputProviderKey( QStringLiteral( "gdal" ) );
     131                 :          0 :   writer->setOutputFormat( outputFormat );
     132                 :          0 :   mOutputRasterDataProvider = writer->createOneBandRaster( mDataType, mLayerWidth, mLayerHeight, mExtent, mCrs );
     133                 :          0 :   if ( !mOutputRasterDataProvider )
     134                 :          0 :     throw QgsProcessingException( QObject::tr( "Could not create raster output: %1" ).arg( outputFile ) );
     135                 :          0 :   if ( !mOutputRasterDataProvider->isValid() )
     136                 :          0 :     throw QgsProcessingException( QObject::tr( "Could not create raster output %1: %2" ).arg( outputFile, mOutputRasterDataProvider->error().message( QgsErrorMessage::Text ) ) );
     137                 :            : 
     138                 :          0 :   mOutputRasterDataProvider->setNoDataValue( 1, mNoDataValue );
     139                 :          0 :   qgssize layerSize = static_cast< qgssize >( mLayerWidth ) * static_cast< qgssize >( mLayerHeight );
     140                 :            : 
     141                 :            :   //call child statistics method
     142                 :          0 :   processRasterStack( feedback );
     143                 :            : 
     144                 :          0 :   QVariantMap outputs;
     145                 :          0 :   outputs.insert( QStringLiteral( "EXTENT" ), mExtent.toString() );
     146                 :          0 :   outputs.insert( QStringLiteral( "CRS_AUTHID" ), mCrs.authid() );
     147                 :          0 :   outputs.insert( QStringLiteral( "WIDTH_IN_PIXELS" ), mLayerWidth );
     148                 :          0 :   outputs.insert( QStringLiteral( "HEIGHT_IN_PIXELS" ), mLayerHeight );
     149                 :          0 :   outputs.insert( QStringLiteral( "TOTAL_PIXEL_COUNT" ), layerSize );
     150                 :          0 :   outputs.insert( QStringLiteral( "OUTPUT" ), outputFile );
     151                 :            : 
     152                 :          0 :   return outputs;
     153                 :          0 : }
     154                 :            : 
     155                 :            : 
     156                 :            : //
     157                 :            : //QgsCellStatisticsAlgorithm
     158                 :            : //
     159                 :          0 : QString QgsCellStatisticsAlgorithm::displayName() const
     160                 :            : {
     161                 :          0 :   return QObject::tr( "Cell statistics" );
     162                 :            : }
     163                 :            : 
     164                 :          0 : QString QgsCellStatisticsAlgorithm::name() const
     165                 :            : {
     166                 :          0 :   return QStringLiteral( "cellstatistics" );
     167                 :            : }
     168                 :            : 
     169                 :          0 : QStringList QgsCellStatisticsAlgorithm::tags() const
     170                 :            : {
     171                 :          0 :   return QObject::tr( "cell,pixel,statistic,count,mean,sum,majority,minority,variance,variety,range,median,minimum,maximum" ).split( ',' );
     172                 :          0 : }
     173                 :            : 
     174                 :          0 : QString QgsCellStatisticsAlgorithm::shortHelpString() const
     175                 :            : {
     176                 :          0 :   return QObject::tr( "The Cell statistics algorithm computes a value for each cell of the "
     177                 :            :                       "output raster. At each cell location, "
     178                 :            :                       "the output value is defined as a function of all overlaid cell values of the "
     179                 :            :                       "input rasters.\n\n"
     180                 :            :                       "The output raster's extent and resolution is defined by a reference "
     181                 :            :                       "raster. The following functions can be applied on the input "
     182                 :            :                       "raster cells per output raster cell location:\n"
     183                 :            :                       "<ul> "
     184                 :            :                       "   <li>Sum</li>"
     185                 :            :                       "   <li>Count</li>"
     186                 :            :                       "   <li>Mean</li>"
     187                 :            :                       "   <li>Median</li>"
     188                 :            :                       "   <li>Standard deviation</li>"
     189                 :            :                       "   <li>Variance</li>"
     190                 :            :                       "   <li>Minimum</li>"
     191                 :            :                       "   <li>Maximum</li>"
     192                 :            :                       "   <li>Minority (least frequent value)</li>"
     193                 :            :                       "   <li>Majority (most frequent value)</li>"
     194                 :            :                       "   <li>Range (max-min)</li>"
     195                 :            :                       "   <li>Variety (count of unique values)</li>"
     196                 :            :                       "</ul> "
     197                 :            :                       "Input raster layers that do not match the cell size of the reference raster layer will be "
     198                 :            :                       "resampled using nearest neighbor resampling. The output raster data type will be set to "
     199                 :            :                       "the most complex data type present in the input datasets except when using the functions "
     200                 :            :                       "Mean, Standard deviation and Variance (data type is always Float32/Float64 depending on input float type) or Count and Variety (data type is always Int32).\n"
     201                 :            :                       "<i>Calculation details - general:</i> NoData values in any of the input layers will result in a NoData cell output if the Ignore NoData parameter is not set.\n"
     202                 :            :                       "<i>Calculation details - Count:</i> Count will always result in the number of cells without NoData values at the current cell location.\n"
     203                 :            :                       "<i>Calculation details - Median:</i> If the number of input layers is even, the median will be calculated as the "
     204                 :            :                       "arithmetic mean of the two middle values of the ordered cell input values. In this case the output data type is Float32.\n"
     205                 :            :                       "<i>Calculation details - Minority/Majority:</i> If no unique minority or majority could be found, the result is NoData, except all "
     206                 :            :                       "input cell values are equal." );
     207                 :            : }
     208                 :            : 
     209                 :          0 : QgsCellStatisticsAlgorithm *QgsCellStatisticsAlgorithm::createInstance() const
     210                 :            : {
     211                 :          0 :   return new QgsCellStatisticsAlgorithm();
     212                 :          0 : }
     213                 :            : 
     214                 :          0 : void QgsCellStatisticsAlgorithm::addSpecificAlgorithmParams()
     215                 :            : {
     216                 :          0 :   QStringList statistics = QStringList();
     217                 :          0 :   statistics << QObject::tr( "Sum" )
     218                 :          0 :              << QObject::tr( "Count" )
     219                 :          0 :              << QObject::tr( "Mean" )
     220                 :          0 :              << QObject::tr( "Median" )
     221                 :          0 :              << QObject::tr( "Standard deviation" )
     222                 :          0 :              << QObject::tr( "Variance" )
     223                 :          0 :              << QObject::tr( "Minimum" )
     224                 :          0 :              << QObject::tr( "Maximum" )
     225                 :          0 :              << QObject::tr( "Minority" )
     226                 :          0 :              << QObject::tr( "Majority" )
     227                 :          0 :              << QObject::tr( "Range" )
     228                 :          0 :              << QObject::tr( "Variety" );
     229                 :            : 
     230                 :          0 :   addParameter( new QgsProcessingParameterEnum( QStringLiteral( "STATISTIC" ), QObject::tr( "Statistic" ),  statistics, false, 0, false ) );
     231                 :          0 : }
     232                 :            : 
     233                 :          0 : bool QgsCellStatisticsAlgorithm::prepareSpecificAlgorithmParameters( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
     234                 :            : {
     235                 :            :   Q_UNUSED( feedback )
     236                 :            :   //obtain statistic method
     237                 :          0 :   mMethod = static_cast<QgsRasterAnalysisUtils::CellValueStatisticMethods>( parameterAsEnum( parameters, QStringLiteral( "STATISTIC" ), context ) );
     238                 :            : 
     239                 :            :   //force data types on specific functions in the cellstatistics alg if input data types don't match
     240                 :            :   if (
     241                 :          0 :     mMethod == QgsRasterAnalysisUtils::Mean ||
     242                 :          0 :     mMethod == QgsRasterAnalysisUtils::StandardDeviation ||
     243                 :          0 :     mMethod == QgsRasterAnalysisUtils::Variance ||
     244                 :          0 :     ( mMethod == QgsRasterAnalysisUtils::Median && ( mInputs.size() % 2 == 0 ) )
     245                 :            :   )
     246                 :            :   {
     247                 :          0 :     if ( static_cast<int>( mDataType ) < 6 )
     248                 :          0 :       mDataType = Qgis::Float32; //force float on mean, stddev and median with equal number of input layers if all inputs are integer
     249                 :          0 :   }
     250                 :          0 :   else if ( mMethod == QgsRasterAnalysisUtils::Count || mMethod == QgsRasterAnalysisUtils::Variety ) //count, variety
     251                 :            :   {
     252                 :          0 :     if ( static_cast<int>( mDataType ) > 5 ) //if is floating point type
     253                 :          0 :       mDataType = Qgis::Int32; //force integer on variety if all inputs are float or complex
     254                 :          0 :   }
     255                 :          0 :   return true;
     256                 :          0 : }
     257                 :            : 
     258                 :          0 : void QgsCellStatisticsAlgorithm::processRasterStack( QgsProcessingFeedback *feedback )
     259                 :            : {
     260                 :          0 :   int maxWidth = QgsRasterIterator::DEFAULT_MAXIMUM_TILE_WIDTH;
     261                 :          0 :   int maxHeight = QgsRasterIterator::DEFAULT_MAXIMUM_TILE_HEIGHT;
     262                 :          0 :   int nbBlocksWidth = static_cast< int>( std::ceil( 1.0 * mLayerWidth / maxWidth ) );
     263                 :          0 :   int nbBlocksHeight = static_cast< int >( std::ceil( 1.0 * mLayerHeight / maxHeight ) );
     264                 :          0 :   int nbBlocks = nbBlocksWidth * nbBlocksHeight;
     265                 :          0 :   mOutputRasterDataProvider->setEditable( true );
     266                 :          0 :   QgsRasterIterator outputIter( mOutputRasterDataProvider );
     267                 :          0 :   outputIter.startRasterRead( 1, mLayerWidth, mLayerHeight, mExtent );
     268                 :            : 
     269                 :          0 :   int iterLeft = 0;
     270                 :          0 :   int iterTop = 0;
     271                 :          0 :   int iterCols = 0;
     272                 :          0 :   int iterRows = 0;
     273                 :          0 :   QgsRectangle blockExtent;
     274                 :          0 :   std::unique_ptr< QgsRasterBlock > outputBlock;
     275                 :          0 :   while ( outputIter.readNextRasterPart( 1, iterCols, iterRows, outputBlock, iterLeft, iterTop, &blockExtent ) )
     276                 :            :   {
     277                 :          0 :     std::vector< std::unique_ptr< QgsRasterBlock > > inputBlocks;
     278                 :          0 :     for ( const QgsRasterAnalysisUtils::RasterLogicInput &i : std::as_const( mInputs ) )
     279                 :            :     {
     280                 :          0 :       if ( feedback->isCanceled() )
     281                 :          0 :         break; //in case some slow data sources are loaded
     282                 :          0 :       for ( int band : i.bands )
     283                 :            :       {
     284                 :          0 :         if ( feedback->isCanceled() )
     285                 :          0 :           break; //in case some slow data sources are loaded
     286                 :          0 :         std::unique_ptr< QgsRasterBlock > b( i.interface->block( band, blockExtent, iterCols, iterRows ) );
     287                 :          0 :         inputBlocks.emplace_back( std::move( b ) );
     288                 :          0 :       }
     289                 :            :     }
     290                 :            : 
     291                 :          0 :     feedback->setProgress( 100 * ( ( iterTop / maxHeight * nbBlocksWidth ) + iterLeft / maxWidth ) / nbBlocks );
     292                 :          0 :     for ( int row = 0; row < iterRows; row++ )
     293                 :            :     {
     294                 :          0 :       if ( feedback->isCanceled() )
     295                 :          0 :         break;
     296                 :            : 
     297                 :          0 :       for ( int col = 0; col < iterCols; col++ )
     298                 :            :       {
     299                 :          0 :         double result = 0;
     300                 :          0 :         bool noDataInStack = false;
     301                 :          0 :         std::vector<double> cellValues = QgsRasterAnalysisUtils::getCellValuesFromBlockStack( inputBlocks, row, col, noDataInStack );
     302                 :          0 :         int cellValueStackSize = cellValues.size();
     303                 :            : 
     304                 :          0 :         if ( noDataInStack && !mIgnoreNoData )
     305                 :            :         {
     306                 :            :           //output cell will always be NoData if NoData occurs in cellValueStack and NoData is not ignored
     307                 :            :           //this saves unnecessary iterations on the cellValueStack
     308                 :          0 :           if ( mMethod == QgsRasterAnalysisUtils::Count )
     309                 :          0 :             outputBlock->setValue( row, col,  cellValueStackSize );
     310                 :            :           else
     311                 :            :           {
     312                 :          0 :             outputBlock->setValue( row, col, mNoDataValue );
     313                 :            :           }
     314                 :          0 :         }
     315                 :          0 :         else if ( !noDataInStack || ( mIgnoreNoData && cellValueStackSize > 0 ) )
     316                 :            :         {
     317                 :          0 :           switch ( mMethod )
     318                 :            :           {
     319                 :            :             case QgsRasterAnalysisUtils::Sum:
     320                 :          0 :               result = std::accumulate( cellValues.begin(), cellValues.end(), 0.0 );
     321                 :          0 :               break;
     322                 :            :             case QgsRasterAnalysisUtils::Count:
     323                 :          0 :               result = cellValueStackSize;
     324                 :          0 :               break;
     325                 :            :             case QgsRasterAnalysisUtils::Mean:
     326                 :          0 :               result = QgsRasterAnalysisUtils::meanFromCellValues( cellValues, cellValueStackSize );
     327                 :          0 :               break;
     328                 :            :             case QgsRasterAnalysisUtils::Median:
     329                 :          0 :               result = QgsRasterAnalysisUtils::medianFromCellValues( cellValues, cellValueStackSize );
     330                 :          0 :               break;
     331                 :            :             case QgsRasterAnalysisUtils::StandardDeviation:
     332                 :          0 :               result = QgsRasterAnalysisUtils::stddevFromCellValues( cellValues, cellValueStackSize );
     333                 :          0 :               break;
     334                 :            :             case QgsRasterAnalysisUtils::Variance:
     335                 :          0 :               result = QgsRasterAnalysisUtils::varianceFromCellValues( cellValues, cellValueStackSize );
     336                 :          0 :               break;
     337                 :            :             case QgsRasterAnalysisUtils::Minimum:
     338                 :          0 :               result = QgsRasterAnalysisUtils::minimumFromCellValues( cellValues );
     339                 :          0 :               break;
     340                 :            :             case QgsRasterAnalysisUtils::Maximum:
     341                 :          0 :               result = QgsRasterAnalysisUtils::maximumFromCellValues( cellValues );
     342                 :          0 :               break;
     343                 :            :             case QgsRasterAnalysisUtils::Minority:
     344                 :          0 :               result = QgsRasterAnalysisUtils::minorityFromCellValues( cellValues, mNoDataValue, cellValueStackSize );
     345                 :          0 :               break;
     346                 :            :             case QgsRasterAnalysisUtils::Majority:
     347                 :          0 :               result = QgsRasterAnalysisUtils::majorityFromCellValues( cellValues, mNoDataValue, cellValueStackSize );
     348                 :          0 :               break;
     349                 :            :             case QgsRasterAnalysisUtils::Range:
     350                 :          0 :               result = QgsRasterAnalysisUtils::rangeFromCellValues( cellValues );
     351                 :          0 :               break;
     352                 :            :             case QgsRasterAnalysisUtils::Variety:
     353                 :          0 :               result = QgsRasterAnalysisUtils::varietyFromCellValues( cellValues );
     354                 :          0 :               break;
     355                 :            :           }
     356                 :          0 :           outputBlock->setValue( row, col, result );
     357                 :          0 :         }
     358                 :            :         else
     359                 :            :         {
     360                 :            :           //result is NoData if cellValueStack contains no valid values, eg. all cellValues are NoData
     361                 :          0 :           outputBlock->setValue( row, col, mNoDataValue );
     362                 :            :         }
     363                 :          0 :       }
     364                 :          0 :     }
     365                 :          0 :     mOutputRasterDataProvider->writeBlock( outputBlock.get(), 1, iterLeft, iterTop );
     366                 :          0 :   }
     367                 :          0 :   mOutputRasterDataProvider->setEditable( false );
     368                 :          0 : }
     369                 :            : 
     370                 :            : //
     371                 :            : //QgsCellStatisticsPercentileAlgorithm
     372                 :            : //
     373                 :          0 : QString QgsCellStatisticsPercentileAlgorithm::displayName() const
     374                 :            : {
     375                 :          0 :   return QObject::tr( "Cell stack percentile" );
     376                 :            : }
     377                 :            : 
     378                 :          0 : QString QgsCellStatisticsPercentileAlgorithm::name() const
     379                 :            : {
     380                 :          0 :   return QStringLiteral( "cellstackpercentile" );
     381                 :            : }
     382                 :            : 
     383                 :          0 : QStringList QgsCellStatisticsPercentileAlgorithm::tags() const
     384                 :            : {
     385                 :          0 :   return QObject::tr( "cell,pixel,statistic,percentile,quantile,quartile" ).split( ',' );
     386                 :          0 : }
     387                 :            : 
     388                 :          0 : QString QgsCellStatisticsPercentileAlgorithm::shortHelpString() const
     389                 :            : {
     390                 :          0 :   return QObject::tr( "The Cell stack percentile algorithm returns the cell-wise percentile value of a stack of rasters "
     391                 :            :                       "and writes the results to an output raster. The percentile to return is determined by the percentile input value (ranges between 0 and 1). "
     392                 :            :                       "At each cell location, the specified percentile is obtained using the respective value from "
     393                 :            :                       "the stack of all overlaid and sorted cell values of the input rasters.\n\n"
     394                 :            :                       "There are three methods for percentile calculation:"
     395                 :            :                       "<ul> "
     396                 :            :                       "   <li>Nearest rank</li>"
     397                 :            :                       "   <li>Inclusive linear interpolation (PERCENTILE.INC)</li>"
     398                 :            :                       "   <li>Exclusive linear interpolation (PERCENTILE.EXC)</li>"
     399                 :            :                       "</ul> "
     400                 :            :                       "While the output value can stay the same for the nearest rank method (obtains the value that is nearest to the "
     401                 :            :                       "specified percentile), the linear interpolation method return unique values for different percentiles. Both interpolation "
     402                 :            :                       "methods follow their counterpart methods implemented by LibreOffice or Microsoft Excel. \n\n"
     403                 :            :                       "The output raster's extent and resolution is defined by a reference "
     404                 :            :                       "raster. If the input raster layers that do not match the cell size of the reference raster layer will be "
     405                 :            :                       "resampled using nearest neighbor resampling. NoData values in any of the input layers will result in a NoData cell output if the Ignore NoData parameter is not set. "
     406                 :            :                       "The output raster data type will be set to the most complex data type present in the input datasets. " );
     407                 :            : }
     408                 :            : 
     409                 :          0 : QgsCellStatisticsPercentileAlgorithm *QgsCellStatisticsPercentileAlgorithm::createInstance() const
     410                 :            : {
     411                 :          0 :   return new QgsCellStatisticsPercentileAlgorithm();
     412                 :          0 : }
     413                 :            : 
     414                 :          0 : void QgsCellStatisticsPercentileAlgorithm::addSpecificAlgorithmParams()
     415                 :            : {
     416                 :          0 :   addParameter( new QgsProcessingParameterEnum( QStringLiteral( "METHOD" ), QObject::tr( "Method" ), QStringList() << QObject::tr( "Nearest rank" ) << QObject::tr( "Inclusive linear interpolation (PERCENTILE.INC)" ) << QObject::tr( "Exclusive linear interpolation (PERCENTILE.EXC)" ), false, 0, false ) );
     417                 :          0 :   addParameter( new QgsProcessingParameterNumber( QStringLiteral( "PERCENTILE" ), QObject::tr( "Percentile" ), QgsProcessingParameterNumber::Double, 0.25, false, 0.0, 1.0 ) );
     418                 :          0 : }
     419                 :            : 
     420                 :          0 : bool QgsCellStatisticsPercentileAlgorithm::prepareSpecificAlgorithmParameters( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
     421                 :            : {
     422                 :            :   Q_UNUSED( feedback )
     423                 :          0 :   mMethod = static_cast< QgsRasterAnalysisUtils::CellValuePercentileMethods >( parameterAsEnum( parameters, QStringLiteral( "METHOD" ), context ) );
     424                 :          0 :   mPercentile = parameterAsDouble( parameters, QStringLiteral( "PERCENTILE" ), context );
     425                 :            : 
     426                 :            :   //default percentile output data type to float32 raster if interpolation method is chosen
     427                 :            :   //otherwise use the most potent data type in the intput raster stack (see prepareAlgorithm() in base class)
     428                 :          0 :   if ( mMethod != QgsRasterAnalysisUtils::CellValuePercentileMethods::NearestRankPercentile && mDataType < 6 )
     429                 :          0 :     mDataType = Qgis::DataType::Float32;
     430                 :            : 
     431                 :          0 :   return true;
     432                 :          0 : }
     433                 :            : 
     434                 :          0 : void QgsCellStatisticsPercentileAlgorithm::processRasterStack( QgsProcessingFeedback *feedback )
     435                 :            : {
     436                 :            : 
     437                 :          0 :   int maxWidth = QgsRasterIterator::DEFAULT_MAXIMUM_TILE_WIDTH;
     438                 :          0 :   int maxHeight = QgsRasterIterator::DEFAULT_MAXIMUM_TILE_HEIGHT;
     439                 :          0 :   int nbBlocksWidth = static_cast< int>( std::ceil( 1.0 * mLayerWidth / maxWidth ) );
     440                 :          0 :   int nbBlocksHeight = static_cast< int >( std::ceil( 1.0 * mLayerHeight / maxHeight ) );
     441                 :          0 :   int nbBlocks = nbBlocksWidth * nbBlocksHeight;
     442                 :          0 :   mOutputRasterDataProvider->setEditable( true );
     443                 :          0 :   QgsRasterIterator outputIter( mOutputRasterDataProvider );
     444                 :          0 :   outputIter.startRasterRead( 1, mLayerWidth, mLayerHeight, mExtent );
     445                 :            : 
     446                 :          0 :   int iterLeft = 0;
     447                 :          0 :   int iterTop = 0;
     448                 :          0 :   int iterCols = 0;
     449                 :          0 :   int iterRows = 0;
     450                 :          0 :   QgsRectangle blockExtent;
     451                 :          0 :   std::unique_ptr< QgsRasterBlock > outputBlock;
     452                 :          0 :   while ( outputIter.readNextRasterPart( 1, iterCols, iterRows, outputBlock, iterLeft, iterTop, &blockExtent ) )
     453                 :            :   {
     454                 :          0 :     std::vector< std::unique_ptr< QgsRasterBlock > > inputBlocks;
     455                 :          0 :     for ( const QgsRasterAnalysisUtils::RasterLogicInput &i : std::as_const( mInputs ) )
     456                 :            :     {
     457                 :          0 :       if ( feedback->isCanceled() )
     458                 :          0 :         break; //in case some slow data sources are loaded
     459                 :          0 :       for ( int band : i.bands )
     460                 :            :       {
     461                 :          0 :         if ( feedback->isCanceled() )
     462                 :          0 :           break; //in case some slow data sources are loaded
     463                 :          0 :         std::unique_ptr< QgsRasterBlock > b( i.interface->block( band, blockExtent, iterCols, iterRows ) );
     464                 :          0 :         inputBlocks.emplace_back( std::move( b ) );
     465                 :          0 :       }
     466                 :            :     }
     467                 :            : 
     468                 :          0 :     feedback->setProgress( 100 * ( ( iterTop / maxHeight * nbBlocksWidth ) + iterLeft / maxWidth ) / nbBlocks );
     469                 :          0 :     for ( int row = 0; row < iterRows; row++ )
     470                 :            :     {
     471                 :          0 :       if ( feedback->isCanceled() )
     472                 :          0 :         break;
     473                 :            : 
     474                 :          0 :       for ( int col = 0; col < iterCols; col++ )
     475                 :            :       {
     476                 :          0 :         double result = 0;
     477                 :          0 :         bool noDataInStack = false;
     478                 :          0 :         std::vector<double> cellValues = QgsRasterAnalysisUtils::getCellValuesFromBlockStack( inputBlocks, row, col, noDataInStack );
     479                 :          0 :         int cellValueStackSize = cellValues.size();
     480                 :            : 
     481                 :          0 :         if ( noDataInStack && !mIgnoreNoData )
     482                 :            :         {
     483                 :          0 :           outputBlock->setValue( row, col, mNoDataValue );
     484                 :          0 :         }
     485                 :          0 :         else if ( !noDataInStack || ( mIgnoreNoData && cellValueStackSize > 0 ) )
     486                 :            :         {
     487                 :          0 :           switch ( mMethod )
     488                 :            :           {
     489                 :            :             case QgsRasterAnalysisUtils::NearestRankPercentile:
     490                 :          0 :               result = QgsRasterAnalysisUtils::nearestRankPercentile( cellValues, cellValueStackSize, mPercentile );
     491                 :          0 :               break;
     492                 :            :             case QgsRasterAnalysisUtils::InterpolatedPercentileInc:
     493                 :          0 :               result = QgsRasterAnalysisUtils::interpolatedPercentileInc( cellValues, cellValueStackSize, mPercentile );
     494                 :          0 :               break;
     495                 :            :             case QgsRasterAnalysisUtils::InterpolatedPercentileExc:
     496                 :          0 :               result = QgsRasterAnalysisUtils::interpolatedPercentileExc( cellValues, cellValueStackSize, mPercentile, mNoDataValue );
     497                 :          0 :               break;
     498                 :            :           }
     499                 :          0 :           outputBlock->setValue( row, col, result );
     500                 :          0 :         }
     501                 :            :         else
     502                 :            :         {
     503                 :            :           //result is NoData if cellValueStack contains no valid values, eg. all cellValues are NoData
     504                 :          0 :           outputBlock->setValue( row, col, mNoDataValue );
     505                 :            :         }
     506                 :          0 :       }
     507                 :          0 :     }
     508                 :          0 :     mOutputRasterDataProvider->writeBlock( outputBlock.get(), 1, iterLeft, iterTop );
     509                 :          0 :   }
     510                 :          0 :   mOutputRasterDataProvider->setEditable( false );
     511                 :          0 : }
     512                 :            : 
     513                 :            : //
     514                 :            : //QgsCellStatisticsPercentRankFromValueAlgorithm
     515                 :            : //
     516                 :          0 : QString QgsCellStatisticsPercentRankFromValueAlgorithm::displayName() const
     517                 :            : {
     518                 :          0 :   return QObject::tr( "Cell stack percent rank from value" );
     519                 :            : }
     520                 :            : 
     521                 :          0 : QString QgsCellStatisticsPercentRankFromValueAlgorithm::name() const
     522                 :            : {
     523                 :          0 :   return QStringLiteral( "cellstackpercentrankfromvalue" );
     524                 :            : }
     525                 :            : 
     526                 :          0 : QStringList QgsCellStatisticsPercentRankFromValueAlgorithm::tags() const
     527                 :            : {
     528                 :          0 :   return QObject::tr( "cell,pixel,statistic,percentrank,rank,percent,value" ).split( ',' );
     529                 :          0 : }
     530                 :            : 
     531                 :          0 : QString QgsCellStatisticsPercentRankFromValueAlgorithm::shortHelpString() const
     532                 :            : {
     533                 :          0 :   return QObject::tr( "The Cell stack percentrank from value algorithm calculates the cell-wise percentrank value of a stack of rasters based on a single input value "
     534                 :            :                       "and writes them to an output raster.\n\n"
     535                 :            :                       "At each cell location, the specified value is ranked among the respective values in the stack of all overlaid and sorted cell values from the input rasters. "
     536                 :            :                       "For values outside of the stack value distribution, the algorithm returns NoData because the value cannot be ranked among the cell values.\n\n"
     537                 :            :                       "There are two methods for percentile calculation:"
     538                 :            :                       "<ul> "
     539                 :            :                       "   <li>Inclusive linearly interpolated percent rank (PERCENTRANK.INC)</li>"
     540                 :            :                       "   <li>Exclusive linearly interpolated percent rank (PERCENTRANK.EXC)</li>"
     541                 :            :                       "</ul> "
     542                 :            :                       "The linear interpolation method return the unique percent rank for different values. Both interpolation "
     543                 :            :                       "methods follow their counterpart methods implemented by LibreOffice or Microsoft Excel. \n\n"
     544                 :            :                       "The output raster's extent and resolution is defined by a reference "
     545                 :            :                       "raster. If the input raster layers that do not match the cell size of the reference raster layer will be "
     546                 :            :                       "resampled using nearest neighbor resampling. NoData values in any of the input layers will result in a NoData cell output if the Ignore NoData parameter is not set. "
     547                 :            :                       "The output raster data type will always be Float32." );
     548                 :            : }
     549                 :            : 
     550                 :          0 : QgsCellStatisticsPercentRankFromValueAlgorithm *QgsCellStatisticsPercentRankFromValueAlgorithm::createInstance() const
     551                 :            : {
     552                 :          0 :   return new QgsCellStatisticsPercentRankFromValueAlgorithm();
     553                 :          0 : }
     554                 :            : 
     555                 :          0 : void QgsCellStatisticsPercentRankFromValueAlgorithm::addSpecificAlgorithmParams()
     556                 :            : {
     557                 :          0 :   addParameter( new QgsProcessingParameterEnum( QStringLiteral( "METHOD" ), QObject::tr( "Method" ), QStringList() << QObject::tr( "Inclusive linear interpolation (PERCENTRANK.INC)" ) << QObject::tr( "Exclusive linear interpolation (PERCENTRANK.EXC)" ), false, 0, false ) );
     558                 :          0 :   addParameter( new QgsProcessingParameterNumber( QStringLiteral( "VALUE" ), QObject::tr( "Value" ), QgsProcessingParameterNumber::Double, 10, false ) );
     559                 :          0 : }
     560                 :            : 
     561                 :          0 : bool QgsCellStatisticsPercentRankFromValueAlgorithm::prepareSpecificAlgorithmParameters( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
     562                 :            : {
     563                 :            :   Q_UNUSED( feedback )
     564                 :          0 :   mMethod = static_cast< QgsRasterAnalysisUtils::CellValuePercentRankMethods >( parameterAsEnum( parameters, QStringLiteral( "METHOD" ), context ) );
     565                 :          0 :   mValue = parameterAsDouble( parameters, QStringLiteral( "VALUE" ), context );
     566                 :            : 
     567                 :            :   //output data type always defaults to Float32 because result only ranges between 0 and 1
     568                 :          0 :   mDataType = Qgis::DataType::Float32;
     569                 :          0 :   return true;
     570                 :          0 : }
     571                 :            : 
     572                 :          0 : void QgsCellStatisticsPercentRankFromValueAlgorithm::processRasterStack( QgsProcessingFeedback *feedback )
     573                 :            : {
     574                 :            : 
     575                 :          0 :   int maxWidth = QgsRasterIterator::DEFAULT_MAXIMUM_TILE_WIDTH;
     576                 :          0 :   int maxHeight = QgsRasterIterator::DEFAULT_MAXIMUM_TILE_HEIGHT;
     577                 :          0 :   int nbBlocksWidth = static_cast< int>( std::ceil( 1.0 * mLayerWidth / maxWidth ) );
     578                 :          0 :   int nbBlocksHeight = static_cast< int >( std::ceil( 1.0 * mLayerHeight / maxHeight ) );
     579                 :          0 :   int nbBlocks = nbBlocksWidth * nbBlocksHeight;
     580                 :          0 :   mOutputRasterDataProvider->setEditable( true );
     581                 :          0 :   QgsRasterIterator outputIter( mOutputRasterDataProvider );
     582                 :          0 :   outputIter.startRasterRead( 1, mLayerWidth, mLayerHeight, mExtent );
     583                 :            : 
     584                 :          0 :   int iterLeft = 0;
     585                 :          0 :   int iterTop = 0;
     586                 :          0 :   int iterCols = 0;
     587                 :          0 :   int iterRows = 0;
     588                 :          0 :   QgsRectangle blockExtent;
     589                 :          0 :   std::unique_ptr< QgsRasterBlock > outputBlock;
     590                 :          0 :   while ( outputIter.readNextRasterPart( 1, iterCols, iterRows, outputBlock, iterLeft, iterTop, &blockExtent ) )
     591                 :            :   {
     592                 :          0 :     std::vector< std::unique_ptr< QgsRasterBlock > > inputBlocks;
     593                 :          0 :     for ( const QgsRasterAnalysisUtils::RasterLogicInput &i : std::as_const( mInputs ) )
     594                 :            :     {
     595                 :          0 :       if ( feedback->isCanceled() )
     596                 :          0 :         break; //in case some slow data sources are loaded
     597                 :          0 :       for ( int band : i.bands )
     598                 :            :       {
     599                 :          0 :         if ( feedback->isCanceled() )
     600                 :          0 :           break; //in case some slow data sources are loaded
     601                 :          0 :         std::unique_ptr< QgsRasterBlock > b( i.interface->block( band, blockExtent, iterCols, iterRows ) );
     602                 :          0 :         inputBlocks.emplace_back( std::move( b ) );
     603                 :          0 :       }
     604                 :            :     }
     605                 :            : 
     606                 :          0 :     feedback->setProgress( 100 * ( ( iterTop / maxHeight * nbBlocksWidth ) + iterLeft / maxWidth ) / nbBlocks );
     607                 :          0 :     for ( int row = 0; row < iterRows; row++ )
     608                 :            :     {
     609                 :          0 :       if ( feedback->isCanceled() )
     610                 :          0 :         break;
     611                 :            : 
     612                 :          0 :       for ( int col = 0; col < iterCols; col++ )
     613                 :            :       {
     614                 :          0 :         double result = 0;
     615                 :          0 :         bool noDataInStack = false;
     616                 :          0 :         std::vector<double> cellValues = QgsRasterAnalysisUtils::getCellValuesFromBlockStack( inputBlocks, row, col, noDataInStack );
     617                 :          0 :         int cellValueStackSize = cellValues.size();
     618                 :            : 
     619                 :          0 :         if ( noDataInStack && !mIgnoreNoData )
     620                 :            :         {
     621                 :          0 :           outputBlock->setValue( row, col, mNoDataValue );
     622                 :          0 :         }
     623                 :          0 :         else if ( !noDataInStack || ( mIgnoreNoData && cellValueStackSize > 0 ) )
     624                 :            :         {
     625                 :          0 :           switch ( mMethod )
     626                 :            :           {
     627                 :            :             case QgsRasterAnalysisUtils::InterpolatedPercentRankInc:
     628                 :          0 :               result = QgsRasterAnalysisUtils::interpolatedPercentRankInc( cellValues, cellValueStackSize, mValue, mNoDataValue );
     629                 :          0 :               break;
     630                 :            :             case QgsRasterAnalysisUtils::InterpolatedPercentRankExc:
     631                 :          0 :               result = QgsRasterAnalysisUtils::interpolatedPercentRankExc( cellValues, cellValueStackSize, mValue, mNoDataValue );
     632                 :          0 :               break;
     633                 :            :           }
     634                 :          0 :           outputBlock->setValue( row, col, result );
     635                 :          0 :         }
     636                 :            :         else
     637                 :            :         {
     638                 :            :           //result is NoData if cellValueStack contains no valid values, eg. all cellValues are NoData
     639                 :          0 :           outputBlock->setValue( row, col, mNoDataValue );
     640                 :            :         }
     641                 :          0 :       }
     642                 :          0 :     }
     643                 :          0 :     mOutputRasterDataProvider->writeBlock( outputBlock.get(), 1, iterLeft, iterTop );
     644                 :          0 :   }
     645                 :          0 :   mOutputRasterDataProvider->setEditable( false );
     646                 :          0 : }
     647                 :            : 
     648                 :            : 
     649                 :            : //
     650                 :            : //QgsCellStatisticsPercentRankFromRasterAlgorithm
     651                 :            : //
     652                 :          0 : QString QgsCellStatisticsPercentRankFromRasterAlgorithm::displayName() const
     653                 :            : {
     654                 :          0 :   return QObject::tr( "Cell stack percentrank from raster layer" );
     655                 :            : }
     656                 :            : 
     657                 :          0 : QString QgsCellStatisticsPercentRankFromRasterAlgorithm::name() const
     658                 :            : {
     659                 :          0 :   return QStringLiteral( "cellstackpercentrankfromrasterlayer" );
     660                 :            : }
     661                 :            : 
     662                 :          0 : QStringList QgsCellStatisticsPercentRankFromRasterAlgorithm::tags() const
     663                 :            : {
     664                 :          0 :   return QObject::tr( "cell,pixel,statistic,percentrank,rank,percent,value,raster" ).split( ',' );
     665                 :          0 : }
     666                 :            : 
     667                 :          0 : QString QgsCellStatisticsPercentRankFromRasterAlgorithm::shortHelpString() const
     668                 :            : {
     669                 :          0 :   return QObject::tr( "The Cell stack percentrank from raster layer algorithm calculates the cell-wise percentrank value of a stack of rasters based on an input value raster "
     670                 :            :                       "and writes them to an output raster.\n\n"
     671                 :            :                       "At each cell location, the current value of the value raster is used ranked among the respective values in the stack of all overlaid and sorted cell values of the input rasters. "
     672                 :            :                       "For values outside of the the stack value distribution, the algorithm returns NoData because the value cannot be ranked among the cell values.\n\n"
     673                 :            :                       "There are two methods for percentile calculation:"
     674                 :            :                       "<ul> "
     675                 :            :                       "   <li>Inclusive linearly interpolated percent rank (PERCENTRANK.INC)</li>"
     676                 :            :                       "   <li>Exclusive linearly interpolated percent rank (PERCENTRANK.EXC)</li>"
     677                 :            :                       "</ul> "
     678                 :            :                       "The linear interpolation method return the unique percent rank for different values. Both interpolation "
     679                 :            :                       "methods follow their counterpart methods implemented by LibreOffice or Microsoft Excel. \n\n"
     680                 :            :                       "The output raster's extent and resolution is defined by a reference "
     681                 :            :                       "raster. If the input raster layers that do not match the cell size of the reference raster layer will be "
     682                 :            :                       "resampled using nearest neighbor resampling.  NoData values in any of the input layers will result in a NoData cell output if the Ignore NoData parameter is not set. "
     683                 :            :                       "The output raster data type will always be Float32." );
     684                 :            : }
     685                 :            : 
     686                 :          0 : QgsCellStatisticsPercentRankFromRasterAlgorithm *QgsCellStatisticsPercentRankFromRasterAlgorithm::createInstance() const
     687                 :            : {
     688                 :          0 :   return new QgsCellStatisticsPercentRankFromRasterAlgorithm();
     689                 :          0 : }
     690                 :            : 
     691                 :          0 : void QgsCellStatisticsPercentRankFromRasterAlgorithm::addSpecificAlgorithmParams()
     692                 :            : {
     693                 :          0 :   addParameter( new QgsProcessingParameterRasterLayer( QStringLiteral( "INPUT_VALUE_RASTER" ), QObject::tr( "Value raster layer" ) ) );
     694                 :          0 :   addParameter( new QgsProcessingParameterBand( QStringLiteral( "VALUE_RASTER_BAND" ), QObject::tr( "Value raster band" ), 1, QStringLiteral( "VALUE_LAYER" ) ) );
     695                 :          0 :   addParameter( new QgsProcessingParameterEnum( QStringLiteral( "METHOD" ), QObject::tr( "Method" ), QStringList() << QObject::tr( "Inclusive linear interpolation (PERCENTRANK.INC)" ) << QObject::tr( "Exclusive linear interpolation (PERCENTRANK.EXC)" ), false, 0, false ) );
     696                 :          0 : }
     697                 :            : 
     698                 :          0 : bool QgsCellStatisticsPercentRankFromRasterAlgorithm::prepareSpecificAlgorithmParameters( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
     699                 :            : {
     700                 :            :   Q_UNUSED( feedback )
     701                 :          0 :   mMethod = static_cast< QgsRasterAnalysisUtils::CellValuePercentRankMethods >( parameterAsEnum( parameters, QStringLiteral( "METHOD" ), context ) );
     702                 :            : 
     703                 :          0 :   QgsRasterLayer *inputValueRaster = parameterAsRasterLayer( parameters, QStringLiteral( "INPUT_VALUE_RASTER" ), context );
     704                 :          0 :   if ( !inputValueRaster )
     705                 :          0 :     throw QgsProcessingException( invalidRasterError( parameters, QStringLiteral( "INPUT_VALUE_RASTER" ) ) );
     706                 :            : 
     707                 :          0 :   mValueRasterInterface.reset( inputValueRaster->dataProvider()->clone() );
     708                 :            : 
     709                 :          0 :   mValueRasterBand = parameterAsInt( parameters, QStringLiteral( "VALUE_RASTER_BAND" ), context );
     710                 :            : 
     711                 :            :   //output data type always defaults to Float32 because result only ranges between 0 and 1
     712                 :          0 :   mDataType = Qgis::DataType::Float32;
     713                 :          0 :   return true;
     714                 :          0 : }
     715                 :            : 
     716                 :          0 : void QgsCellStatisticsPercentRankFromRasterAlgorithm::processRasterStack( QgsProcessingFeedback *feedback )
     717                 :            : {
     718                 :          0 :   int maxWidth = QgsRasterIterator::DEFAULT_MAXIMUM_TILE_WIDTH;
     719                 :          0 :   int maxHeight = QgsRasterIterator::DEFAULT_MAXIMUM_TILE_HEIGHT;
     720                 :          0 :   int nbBlocksWidth = static_cast< int>( std::ceil( 1.0 * mLayerWidth / maxWidth ) );
     721                 :          0 :   int nbBlocksHeight = static_cast< int >( std::ceil( 1.0 * mLayerHeight / maxHeight ) );
     722                 :          0 :   int nbBlocks = nbBlocksWidth * nbBlocksHeight;
     723                 :          0 :   mOutputRasterDataProvider->setEditable( true );
     724                 :          0 :   QgsRasterIterator outputIter( mOutputRasterDataProvider );
     725                 :          0 :   outputIter.startRasterRead( 1, mLayerWidth, mLayerHeight, mExtent );
     726                 :            : 
     727                 :          0 :   int iterLeft = 0;
     728                 :          0 :   int iterTop = 0;
     729                 :          0 :   int iterCols = 0;
     730                 :          0 :   int iterRows = 0;
     731                 :          0 :   QgsRectangle blockExtent;
     732                 :          0 :   std::unique_ptr< QgsRasterBlock > outputBlock;
     733                 :          0 :   while ( outputIter.readNextRasterPart( 1, iterCols, iterRows, outputBlock, iterLeft, iterTop, &blockExtent ) )
     734                 :            :   {
     735                 :          0 :     std::unique_ptr< QgsRasterBlock > valueBlock( mValueRasterInterface->block( mValueRasterBand, blockExtent, iterCols, iterRows ) );
     736                 :            : 
     737                 :          0 :     std::vector< std::unique_ptr< QgsRasterBlock > > inputBlocks;
     738                 :          0 :     for ( const QgsRasterAnalysisUtils::RasterLogicInput &i : std::as_const( mInputs ) )
     739                 :            :     {
     740                 :          0 :       if ( feedback->isCanceled() )
     741                 :          0 :         break; //in case some slow data sources are loaded
     742                 :          0 :       for ( int band : i.bands )
     743                 :            :       {
     744                 :          0 :         if ( feedback->isCanceled() )
     745                 :          0 :           break; //in case some slow data sources are loaded
     746                 :          0 :         std::unique_ptr< QgsRasterBlock > b( i.interface->block( band, blockExtent, iterCols, iterRows ) );
     747                 :          0 :         inputBlocks.emplace_back( std::move( b ) );
     748                 :          0 :       }
     749                 :            :     }
     750                 :            : 
     751                 :          0 :     feedback->setProgress( 100 * ( ( iterTop / maxHeight * nbBlocksWidth ) + iterLeft / maxWidth ) / nbBlocks );
     752                 :          0 :     for ( int row = 0; row < iterRows; row++ )
     753                 :            :     {
     754                 :          0 :       if ( feedback->isCanceled() )
     755                 :          0 :         break;
     756                 :            : 
     757                 :          0 :       for ( int col = 0; col < iterCols; col++ )
     758                 :            :       {
     759                 :          0 :         bool percentRankValueIsNoData = false;
     760                 :          0 :         double percentRankValue = valueBlock->valueAndNoData( row, col, percentRankValueIsNoData );
     761                 :            : 
     762                 :          0 :         double result = 0;
     763                 :          0 :         bool noDataInStack = false;
     764                 :          0 :         std::vector<double> cellValues = QgsRasterAnalysisUtils::getCellValuesFromBlockStack( inputBlocks, row, col, noDataInStack );
     765                 :          0 :         int cellValueStackSize = cellValues.size();
     766                 :            : 
     767                 :          0 :         if ( noDataInStack && !mIgnoreNoData && !percentRankValueIsNoData )
     768                 :            :         {
     769                 :          0 :           outputBlock->setValue( row, col, mNoDataValue );
     770                 :          0 :         }
     771                 :          0 :         else if ( !noDataInStack || ( !percentRankValueIsNoData && mIgnoreNoData && cellValueStackSize > 0 ) )
     772                 :            :         {
     773                 :          0 :           switch ( mMethod )
     774                 :            :           {
     775                 :            :             case QgsRasterAnalysisUtils::InterpolatedPercentRankInc:
     776                 :          0 :               result = QgsRasterAnalysisUtils::interpolatedPercentRankInc( cellValues, cellValueStackSize, percentRankValue, mNoDataValue );
     777                 :          0 :               break;
     778                 :            :             case QgsRasterAnalysisUtils::InterpolatedPercentRankExc:
     779                 :          0 :               result = QgsRasterAnalysisUtils::interpolatedPercentRankExc( cellValues, cellValueStackSize, percentRankValue, mNoDataValue );
     780                 :          0 :               break;
     781                 :            :           }
     782                 :          0 :           outputBlock->setValue( row, col, result );
     783                 :          0 :         }
     784                 :            :         else
     785                 :            :         {
     786                 :            :           //result is NoData if cellValueStack contains no valid values, eg. all cellValues are NoData or percentRankValue is NoData
     787                 :          0 :           outputBlock->setValue( row, col, mNoDataValue );
     788                 :            :         }
     789                 :          0 :       }
     790                 :          0 :     }
     791                 :          0 :     mOutputRasterDataProvider->writeBlock( outputBlock.get(), 1, iterLeft, iterTop );
     792                 :          0 :   }
     793                 :          0 :   mOutputRasterDataProvider->setEditable( false );
     794                 :          0 : }
     795                 :            : 
     796                 :            : ///@endcond
     797                 :            : 
     798                 :            : 

Generated by: LCOV version 1.14