QGIS API Documentation  3.20.0-Odense (decaadbb31)
qgsalgorithmfillnodata.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsalgorithmfillnodata.cpp
3  ---------------------
4  begin : January 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 "qgsalgorithmfillnodata.h"
19 #include "qgsrasterfilewriter.h"
20 
22 
23 QString QgsFillNoDataAlgorithm::name() const
24 {
25  return QStringLiteral( "fillnodata" );
26 }
27 
28 QString QgsFillNoDataAlgorithm::displayName() const
29 {
30  return QObject::tr( "Fill NoData cells" );
31 }
32 
33 QStringList QgsFillNoDataAlgorithm::tags() const
34 {
35  return QObject::tr( "data,cells,fill,set" ).split( ',' );
36 }
37 
38 QString QgsFillNoDataAlgorithm::group() const
39 {
40  return QObject::tr( "Raster tools" );
41 }
42 
43 QString QgsFillNoDataAlgorithm::groupId() const
44 {
45  return QStringLiteral( "rastertools" );
46 }
47 
48 void QgsFillNoDataAlgorithm::initAlgorithm( const QVariantMap & )
49 {
50  addParameter( new QgsProcessingParameterRasterLayer( QStringLiteral( "INPUT" ), QStringLiteral( "Raster input" ) ) );
51  addParameter( new QgsProcessingParameterBand( QStringLiteral( "BAND" ), QObject::tr( "Band Number" ), 1, QStringLiteral( "INPUT" ) ) );
52  addParameter( new QgsProcessingParameterNumber( QStringLiteral( "FILL_VALUE" ), QObject::tr( "Fill value" ), QgsProcessingParameterNumber::Double, 1, false ) );
53  addParameter( new QgsProcessingParameterRasterDestination( QStringLiteral( "OUTPUT" ), QObject::tr( "Output raster" ) ) );
54 }
55 
56 QString QgsFillNoDataAlgorithm::shortHelpString() const
57 {
58  return QObject::tr( "This algorithm resets the NoData values in the input raster "
59  "to a chosen value, resulting in a raster dataset with no NoData pixels. "
60  "This value can be set by the user using the Fill value parameter. "
61  "The algorithm respects the input raster data type (eg. a floating point fill value will be truncated "
62  "when applied to an integer raster)." );
63 }
64 
65 QgsFillNoDataAlgorithm *QgsFillNoDataAlgorithm::createInstance() const
66 {
67  return new QgsFillNoDataAlgorithm();
68 }
69 
70 bool QgsFillNoDataAlgorithm::prepareAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
71 {
72  Q_UNUSED( feedback );
73  mInputRaster = parameterAsRasterLayer( parameters, QStringLiteral( "INPUT" ), context );
74  mFillValue = parameterAsDouble( parameters, QStringLiteral( "FILL_VALUE" ), context );
75 
76  if ( !mInputRaster )
77  throw QgsProcessingException( invalidRasterError( parameters, QStringLiteral( "INPUT" ) ) );
78 
79  mBand = parameterAsInt( parameters, QStringLiteral( "BAND" ), context );
80  if ( mBand < 1 || mBand > mInputRaster->bandCount() )
81  throw QgsProcessingException( QObject::tr( "Invalid band number for BAND (%1): Valid values for input raster are 1 to %2" ).arg( mBand ).arg( mInputRaster->bandCount() ) );
82 
83  mInterface.reset( mInputRaster->dataProvider()->clone() );
84  mInputNoDataValue = mInputRaster->dataProvider()->sourceNoDataValue( mBand );
85  mExtent = mInputRaster->extent();
86  mLayerWidth = mInputRaster->width();
87  mLayerHeight = mInputRaster->height();
88  mCrs = mInputRaster->crs();
89  mNbCellsXProvider = mInterface->xSize();
90  mNbCellsYProvider = mInterface->ySize();
91  return true;
92 }
93 
94 QVariantMap QgsFillNoDataAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
95 {
96  //test if input dataset has NoData
97  if ( !mInputRaster->dataProvider()->sourceHasNoDataValue( mBand ) )
98  feedback->reportError( QObject::tr( "Input raster has no NoData values. There exist no NoData cells to fill." ), false );
99 
100  //prepare output dataset
101  const QString outputFile = parameterAsOutputLayer( parameters, QStringLiteral( "OUTPUT" ), context );
102  QFileInfo fi( outputFile );
103  const QString outputFormat = QgsRasterFileWriter::driverForExtension( fi.suffix() );
104  std::unique_ptr< QgsRasterFileWriter > writer = std::make_unique< QgsRasterFileWriter >( outputFile );
105  writer->setOutputProviderKey( QStringLiteral( "gdal" ) );
106  writer->setOutputFormat( outputFormat );
107  std::unique_ptr<QgsRasterDataProvider > provider( writer->createOneBandRaster( mInterface->dataType( mBand ), mNbCellsXProvider, mNbCellsYProvider, mExtent, mCrs ) );
108  if ( !provider )
109  throw QgsProcessingException( QObject::tr( "Could not create raster output: %1" ).arg( outputFile ) );
110  if ( !provider->isValid() )
111  throw QgsProcessingException( QObject::tr( "Could not create raster output %1: %2" ).arg( outputFile, provider->error().message( QgsErrorMessage::Text ) ) );
112 
113  //prepare output provider
114  QgsRasterDataProvider *destinationRasterProvider;
115  destinationRasterProvider = provider.get();
116  destinationRasterProvider->setEditable( true );
117 
120  int nbBlocksWidth = static_cast< int >( std::ceil( 1.0 * mLayerWidth / maxWidth ) );
121  int nbBlocksHeight = static_cast< int >( std::ceil( 1.0 * mLayerHeight / maxHeight ) );
122  int nbBlocks = nbBlocksWidth * nbBlocksHeight;
123 
124  QgsRasterIterator iter( mInterface.get() );
125  iter.startRasterRead( mBand, mLayerWidth, mLayerHeight, mExtent );
126  int iterLeft = 0;
127  int iterTop = 0;
128  int iterCols = 0;
129  int iterRows = 0;
130  std::unique_ptr< QgsRasterBlock > filledRasterBlock;
131  while ( iter.readNextRasterPart( mBand, iterCols, iterRows, filledRasterBlock, iterLeft, iterTop ) )
132  {
133  if ( feedback )
134  feedback->setProgress( 100 * ( ( iterTop / maxHeight * nbBlocksWidth ) + iterLeft / maxWidth ) / nbBlocks );
135 
136  if ( feedback && feedback->isCanceled() )
137  break;
138 
139  if ( !filledRasterBlock->hasNoDataValue() )
140  {
141  destinationRasterProvider->writeBlock( filledRasterBlock.get(), mBand, iterLeft, iterTop );
142  continue;
143  }
144 
145  for ( int row = 0; row < iterRows; row++ )
146  {
147  if ( feedback && feedback->isCanceled() )
148  break;
149  for ( int column = 0; column < iterCols; column++ )
150  {
151  if ( filledRasterBlock->isNoData( row, column ) )
152  filledRasterBlock->setValue( row, column, mFillValue );
153  }
154  }
155  destinationRasterProvider->writeBlock( filledRasterBlock.get(), mBand, iterLeft, iterTop );
156  }
157  destinationRasterProvider->setEditable( false );
158 
159  QVariantMap outputs;
160  outputs.insert( QStringLiteral( "OUTPUT" ), outputFile );
161  return outputs;
162 }
163 
164 
bool isCanceled() const SIP_HOLDGIL
Tells whether the operation has been canceled already.
Definition: qgsfeedback.h:54
void setProgress(double progress)
Sets the current progress for the feedback object.
Definition: qgsfeedback.h:63
Contains information about the context in which a processing algorithm is executed.
Custom exception class for processing related exceptions.
Definition: qgsexception.h:83
Base class for providing feedback from a processing algorithm.
virtual void reportError(const QString &error, bool fatalError=false)
Reports that the algorithm encountered an error while executing.
A raster band parameter for Processing algorithms.
A numeric parameter for processing algorithms.
A raster layer destination parameter, for specifying the destination path for a raster layer created ...
A raster layer parameter for processing algorithms.
Base class for raster data providers.
bool writeBlock(QgsRasterBlock *block, int band, int xOffset=0, int yOffset=0)
Writes pixel data from a raster block into the provider data source.
virtual bool setEditable(bool enabled)
Turns on/off editing mode of the provider.
static QString driverForExtension(const QString &extension)
Returns the GDAL driver name for a specified file extension.
Iterator for sequentially processing raster cells.
static const int DEFAULT_MAXIMUM_TILE_WIDTH
Default maximum tile width.
static const int DEFAULT_MAXIMUM_TILE_HEIGHT
Default maximum tile height.