Branch data Line data Source code
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 : : 21 : : ///@cond PRIVATE 22 : : 23 : 0 : QString QgsFillNoDataAlgorithm::name() const 24 : : { 25 : 0 : return QStringLiteral( "fillnodata" ); 26 : : } 27 : : 28 : 0 : QString QgsFillNoDataAlgorithm::displayName() const 29 : : { 30 : 0 : return QObject::tr( "Fill NoData cells" ); 31 : : } 32 : : 33 : 0 : QStringList QgsFillNoDataAlgorithm::tags() const 34 : : { 35 : 0 : return QObject::tr( "data,cells,fill,set" ).split( ',' ); 36 : 0 : } 37 : : 38 : 0 : QString QgsFillNoDataAlgorithm::group() const 39 : : { 40 : 0 : return QObject::tr( "Raster tools" ); 41 : : } 42 : : 43 : 0 : QString QgsFillNoDataAlgorithm::groupId() const 44 : : { 45 : 0 : return QStringLiteral( "rastertools" ); 46 : : } 47 : : 48 : 0 : void QgsFillNoDataAlgorithm::initAlgorithm( const QVariantMap & ) 49 : : { 50 : 0 : addParameter( new QgsProcessingParameterRasterLayer( QStringLiteral( "INPUT" ), QStringLiteral( "Raster input" ) ) ); 51 : 0 : addParameter( new QgsProcessingParameterBand( QStringLiteral( "BAND" ), QObject::tr( "Band Number" ), 1, QStringLiteral( "INPUT" ) ) ); 52 : 0 : addParameter( new QgsProcessingParameterNumber( QStringLiteral( "FILL_VALUE" ), QObject::tr( "Fill value" ), QgsProcessingParameterNumber::Double, 1, false ) ); 53 : 0 : addParameter( new QgsProcessingParameterRasterDestination( QStringLiteral( "OUTPUT" ), QObject::tr( "Output raster" ) ) ); 54 : 0 : } 55 : : 56 : 0 : QString QgsFillNoDataAlgorithm::shortHelpString() const 57 : : { 58 : 0 : 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 : 0 : QgsFillNoDataAlgorithm *QgsFillNoDataAlgorithm::createInstance() const 66 : : { 67 : 0 : return new QgsFillNoDataAlgorithm(); 68 : 0 : } 69 : : 70 : 0 : bool QgsFillNoDataAlgorithm::prepareAlgorithm( const QVariantMap ¶meters, QgsProcessingContext &context, QgsProcessingFeedback *feedback ) 71 : : { 72 : : Q_UNUSED( feedback ); 73 : 0 : mInputRaster = parameterAsRasterLayer( parameters, QStringLiteral( "INPUT" ), context ); 74 : 0 : mFillValue = parameterAsDouble( parameters, QStringLiteral( "FILL_VALUE" ), context ); 75 : : 76 : 0 : if ( !mInputRaster ) 77 : 0 : throw QgsProcessingException( invalidRasterError( parameters, QStringLiteral( "INPUT" ) ) ); 78 : : 79 : 0 : mBand = parameterAsInt( parameters, QStringLiteral( "BAND" ), context ); 80 : 0 : if ( mBand < 1 || mBand > mInputRaster->bandCount() ) 81 : 0 : 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 : 0 : mInterface.reset( mInputRaster->dataProvider()->clone() ); 84 : 0 : mInputNoDataValue = mInputRaster->dataProvider()->sourceNoDataValue( mBand ); 85 : 0 : mExtent = mInputRaster->extent(); 86 : 0 : mLayerWidth = mInputRaster->width(); 87 : 0 : mLayerHeight = mInputRaster->height(); 88 : 0 : mCrs = mInputRaster->crs(); 89 : 0 : mNbCellsXProvider = mInterface->xSize(); 90 : 0 : mNbCellsYProvider = mInterface->ySize(); 91 : 0 : return true; 92 : 0 : } 93 : : 94 : 0 : QVariantMap QgsFillNoDataAlgorithm::processAlgorithm( const QVariantMap ¶meters, QgsProcessingContext &context, QgsProcessingFeedback *feedback ) 95 : : { 96 : : //test if input dataset has NoData 97 : 0 : if ( !mInputRaster->dataProvider()->sourceHasNoDataValue( mBand ) ) 98 : 0 : feedback->reportError( QObject::tr( "Input raster has no NoData values. There exist no NoData cells to fill." ), false ); 99 : : 100 : : //prepare output dataset 101 : 0 : const QString outputFile = parameterAsOutputLayer( parameters, QStringLiteral( "OUTPUT" ), context ); 102 : 0 : QFileInfo fi( outputFile ); 103 : 0 : const QString outputFormat = QgsRasterFileWriter::driverForExtension( fi.suffix() ); 104 : 0 : std::unique_ptr< QgsRasterFileWriter > writer = std::make_unique< QgsRasterFileWriter >( outputFile ); 105 : 0 : writer->setOutputProviderKey( QStringLiteral( "gdal" ) ); 106 : 0 : writer->setOutputFormat( outputFormat ); 107 : 0 : std::unique_ptr<QgsRasterDataProvider > provider( writer->createOneBandRaster( mInterface->dataType( mBand ), mNbCellsXProvider, mNbCellsYProvider, mExtent, mCrs ) ); 108 : 0 : if ( !provider ) 109 : 0 : throw QgsProcessingException( QObject::tr( "Could not create raster output: %1" ).arg( outputFile ) ); 110 : 0 : if ( !provider->isValid() ) 111 : 0 : 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 : 0 : destinationRasterProvider = provider.get(); 116 : 0 : destinationRasterProvider->setEditable( true ); 117 : : 118 : 0 : int maxWidth = QgsRasterIterator::DEFAULT_MAXIMUM_TILE_WIDTH; 119 : 0 : int maxHeight = QgsRasterIterator::DEFAULT_MAXIMUM_TILE_HEIGHT; 120 : 0 : int nbBlocksWidth = static_cast< int >( std::ceil( 1.0 * mLayerWidth / maxWidth ) ); 121 : 0 : int nbBlocksHeight = static_cast< int >( std::ceil( 1.0 * mLayerHeight / maxHeight ) ); 122 : 0 : int nbBlocks = nbBlocksWidth * nbBlocksHeight; 123 : : 124 : 0 : QgsRasterIterator iter( mInterface.get() ); 125 : 0 : iter.startRasterRead( mBand, mLayerWidth, mLayerHeight, mExtent ); 126 : 0 : int iterLeft = 0; 127 : 0 : int iterTop = 0; 128 : 0 : int iterCols = 0; 129 : 0 : int iterRows = 0; 130 : 0 : std::unique_ptr< QgsRasterBlock > filledRasterBlock; 131 : 0 : while ( iter.readNextRasterPart( mBand, iterCols, iterRows, filledRasterBlock, iterLeft, iterTop ) ) 132 : : { 133 : 0 : if ( feedback ) 134 : 0 : feedback->setProgress( 100 * ( ( iterTop / maxHeight * nbBlocksWidth ) + iterLeft / maxWidth ) / nbBlocks ); 135 : : 136 : 0 : if ( feedback && feedback->isCanceled() ) 137 : 0 : break; 138 : : 139 : 0 : if ( !filledRasterBlock->hasNoDataValue() ) 140 : : { 141 : 0 : destinationRasterProvider->writeBlock( filledRasterBlock.get(), mBand, iterLeft, iterTop ); 142 : 0 : continue; 143 : : } 144 : : 145 : 0 : for ( int row = 0; row < iterRows; row++ ) 146 : : { 147 : 0 : if ( feedback && feedback->isCanceled() ) 148 : 0 : break; 149 : 0 : for ( int column = 0; column < iterCols; column++ ) 150 : : { 151 : 0 : if ( filledRasterBlock->isNoData( row, column ) ) 152 : 0 : filledRasterBlock->setValue( row, column, mFillValue ); 153 : 0 : } 154 : 0 : } 155 : 0 : destinationRasterProvider->writeBlock( filledRasterBlock.get(), mBand, iterLeft, iterTop ); 156 : : } 157 : 0 : destinationRasterProvider->setEditable( false ); 158 : : 159 : 0 : QVariantMap outputs; 160 : 0 : outputs.insert( QStringLiteral( "OUTPUT" ), outputFile ); 161 : 0 : return outputs; 162 : 0 : } 163 : : 164 : : 165 : : ///@endcond