24QString QgsFillSinksWangLiuAlgorithm::name()
 const 
   26  return QStringLiteral( 
"fillsinkswangliu" );
 
   29QString QgsFillSinksWangLiuAlgorithm::displayName()
 const 
   31  return QObject::tr( 
"Fill sinks (Wang & Liu)" );
 
   34QStringList QgsFillSinksWangLiuAlgorithm::tags()
 const 
   36  return QObject::tr( 
"fill,filter,slope,dsm,dtm,terrain,water,shed,basin,direction,flow" ).split( 
',' );
 
   39QString QgsFillSinksWangLiuAlgorithm::group()
 const 
   41  return QObject::tr( 
"Raster terrain analysis" );
 
   44QString QgsFillSinksWangLiuAlgorithm::groupId()
 const 
   46  return QStringLiteral( 
"rasterterrainanalysis" );
 
   49QString QgsFillSinksWangLiuAlgorithm::shortHelpString()
 const 
   51  return QObject::tr( 
"This algorithm uses a method proposed by Wang & Liu to identify and fill surface depressions in digital elevation models.\n\n" 
   53                      "The method was enhanced to allow the creation of hydrologically sound elevation models, i.e. not only to fill the depression(s) " 
   54                      "but also to preserve a downward slope along the flow path. If desired, this is accomplished by preserving a minimum slope " 
   55                      "gradient (and thus elevation difference) between cells.\n\n" 
   57                      "References: Wang, L. & H. Liu (2006): An efficient method for identifying and filling surface depressions in digital elevation models for hydrologic analysis and modelling. International Journal of Geographical Information Science, Vol. 20, No. 2: 193-213.\n\n" 
   59                      "This algorithm is a port of the SAGA 'Fill Sinks (Wang & Liu)' tool." );
 
   62QString QgsFillSinksWangLiuAlgorithm::shortDescription()
 const 
   64  return QObject::tr( 
"Identifies and fills surface depressions in digital elevation models using a method proposed by Wang & Liu." );
 
   67void QgsFillSinksWangLiuAlgorithm::initAlgorithm( 
const QVariantMap & )
 
   71  addParameter( 
new QgsProcessingParameterBand( QStringLiteral( 
"BAND" ), QObject::tr( 
"Band number" ), 1, QStringLiteral( 
"INPUT" ) ) );
 
   74  minSlopeParam->setHelp( QObject::tr( 
"Minimum slope gradient to preserve from cell to cell. With a value of zero sinks are filled up to the spill elevation (which results in flat areas). Units are degrees." ) );
 
   75  addParameter( minSlopeParam.release() );
 
   77  auto createOptsParam = std::make_unique<QgsProcessingParameterString>( QStringLiteral( 
"CREATION_OPTIONS" ), QObject::tr( 
"Creation options" ), QVariant(), 
false, 
true );
 
   78  createOptsParam->setMetadata( QVariantMap( { { QStringLiteral( 
"widget_wrapper" ), QVariantMap( { { QStringLiteral( 
"widget_type" ), QStringLiteral( 
"rasteroptions" ) } } ) } } ) );
 
   80  addParameter( createOptsParam.release() );
 
   82  auto outputFilledDem = std::make_unique<QgsProcessingParameterRasterDestination>( QStringLiteral( 
"OUTPUT_FILLED_DEM" ), QObject::tr( 
"Output layer (filled DEM)" ), QVariant(), 
true, 
true );
 
   83  outputFilledDem->setHelp( QObject::tr( 
"Depression-free digital elevation model." ) );
 
   84  addParameter( outputFilledDem.release() );
 
   86  auto outputFlowDirections = std::make_unique<QgsProcessingParameterRasterDestination>( QStringLiteral( 
"OUTPUT_FLOW_DIRECTIONS" ), QObject::tr( 
"Output layer (flow directions)" ), QVariant(), 
true, 
false );
 
   87  outputFlowDirections->setHelp( QObject::tr( 
"Computed flow directions, 0=N, 1=NE, 2=E, ... 7=NW." ) );
 
   88  addParameter( outputFlowDirections.release() );
 
   90  auto outputWatershedBasins = std::make_unique<QgsProcessingParameterRasterDestination>( QStringLiteral( 
"OUTPUT_WATERSHED_BASINS" ), QObject::tr( 
"Output layer (watershed basins)" ), QVariant(), 
true, 
false );
 
   91  outputWatershedBasins->setHelp( QObject::tr( 
"Delineated watershed basin." ) );
 
   92  addParameter( outputWatershedBasins.release() );
 
   95QgsFillSinksWangLiuAlgorithm *QgsFillSinksWangLiuAlgorithm::createInstance()
 const 
   97  return new QgsFillSinksWangLiuAlgorithm();
 
  102  QgsRasterLayer *layer = parameterAsRasterLayer( parameters, QStringLiteral( 
"INPUT" ), context );
 
  106  const int band = parameterAsInt( parameters, QStringLiteral( 
"BAND" ), context );
 
  108  mBand = parameterAsInt( parameters, QStringLiteral( 
"BAND" ), context );
 
  109  if ( mBand < 1 || mBand > layer->
bandCount() )
 
  110    throw QgsProcessingException( QObject::tr( 
"Invalid band number for BAND (%1): Valid values for input raster are 1 to %2" ).arg( mBand ).arg( layer->
bandCount() ) );
 
  114  mLayerWidth = layer->
width();
 
  115  mLayerHeight = layer->
height();
 
  116  mExtent = layer->
extent();
 
  120  mRasterDiagonal = std::sqrt( mRasterUnitsPerPixelX * mRasterUnitsPerPixelX + mRasterUnitsPerPixelY * mRasterUnitsPerPixelY );
 
  123  mDirectionalLengths = { mRasterUnitsPerPixelY, mRasterDiagonal, mRasterUnitsPerPixelX, mRasterDiagonal, mRasterUnitsPerPixelY, mRasterDiagonal, mRasterUnitsPerPixelX, mRasterDiagonal };
 
  127static constexpr std::array< int, 8 > COL_DIRECTION_OFFSETS { 0, 1, 1, 1, 0, -1, -1, -1 };
 
  128static constexpr std::array< int, 8 > ROW_DIRECTION_OFFSETS { -1, -1, 0, 1, 1, 1, 0, -1 };
 
  130bool QgsFillSinksWangLiuAlgorithm::isInGrid( 
int row, 
int col )
 const 
  132  return col >= 0 && col < mLayerWidth && row >= 0 && row < mLayerHeight;
 
  135QgsFillSinksWangLiuAlgorithm::Direction QgsFillSinksWangLiuAlgorithm::getDir( 
int row, 
int col, 
double z, 
const QgsRasterBlock *filled )
 const 
  138  double maxGradient = 0;
 
  139  bool isNoData = 
false;
 
  141  for ( 
Direction direction : { North, NorthEast, East, SouthEast, South, SouthWest, West, NorthWest } )
 
  143    const int neighborCol = col + COL_DIRECTION_OFFSETS[direction];
 
  144    const int neighborRow = row + ROW_DIRECTION_OFFSETS[direction];
 
  146    if ( isInGrid( neighborRow, neighborCol ) )
 
  148      const double neighborZ = filled->
valueAndNoData( neighborRow, neighborCol, isNoData );
 
  149      if ( !isNoData && neighborZ < z )
 
  151        const double gradient = ( z - neighborZ ) / mDirectionalLengths[direction];
 
  152        if ( gradient >= maxGradient )
 
  154          maxGradient = gradient;
 
  155          steepestDirection = direction;
 
  161  return steepestDirection;
 
  164struct CFillSinks_WL_Node
 
  174    bool operator()( CFillSinks_WL_Node n1, CFillSinks_WL_Node n2 )
 const 
  176      return n1.spill > n2.spill;
 
  180typedef std::vector< CFillSinks_WL_Node > nodeVector;
 
  181typedef std::priority_queue< CFillSinks_WL_Node, nodeVector, CompareGreater > PriorityQ;
 
  185  const QString createOptions = parameterAsString( parameters, QStringLiteral( 
"CREATION_OPTIONS" ), context ).trimmed();
 
  187  const QString filledDemOutputFile = parameterAsOutputLayer( parameters, QStringLiteral( 
"OUTPUT_FILLED_DEM" ), context );
 
  188  const QString flowDirectionsOutputFile = parameterAsOutputLayer( parameters, QStringLiteral( 
"OUTPUT_FLOW_DIRECTIONS" ), context );
 
  189  const QString watershedBasinsOutputFile = parameterAsOutputLayer( parameters, QStringLiteral( 
"OUTPUT_WATERSHED_BASINS" ), context );
 
  191  std::unique_ptr<QgsRasterFileWriter> filledDemWriter;
 
  192  std::unique_ptr<QgsRasterDataProvider> filledDemDestProvider;
 
  194  if ( !filledDemOutputFile.isEmpty() )
 
  196    const QFileInfo fi( filledDemOutputFile );
 
  199    filledDemWriter = std::make_unique<QgsRasterFileWriter>( filledDemOutputFile );
 
  200    filledDemWriter->setOutputProviderKey( QStringLiteral( 
"gdal" ) );
 
  201    if ( !createOptions.isEmpty() )
 
  203      filledDemWriter->setCreationOptions( createOptions.split( 
'|' ) );
 
  205    filledDemWriter->setOutputFormat( outputFormat );
 
  207    filledDemDestProvider.reset( filledDemWriter->createOneBandRaster( mDataType, mLayerWidth, mLayerHeight, mExtent, mCrs ) );
 
  209    if ( !filledDemDestProvider )
 
  210      throw QgsProcessingException( QObject::tr( 
"Could not create raster output: %1" ).arg( filledDemOutputFile ) );
 
  211    if ( !filledDemDestProvider->isValid() )
 
  214    filledDemDestProvider->setNoDataValue( 1, mNoData );
 
  215    filledDemDestProvider->setEditable( 
true );
 
  218  std::unique_ptr<QgsRasterFileWriter> flowDirectionsWriter;
 
  219  std::unique_ptr<QgsRasterDataProvider> flowDirectionsDestProvider;
 
  221  if ( !flowDirectionsOutputFile.isEmpty() )
 
  223    const QFileInfo fi( flowDirectionsOutputFile );
 
  226    flowDirectionsWriter = std::make_unique<QgsRasterFileWriter>( flowDirectionsOutputFile );
 
  227    flowDirectionsWriter->setOutputProviderKey( QStringLiteral( 
"gdal" ) );
 
  228    flowDirectionsWriter->setOutputFormat( outputFormat );
 
  230    flowDirectionsDestProvider.reset( flowDirectionsWriter->createOneBandRaster( 
Qgis::DataType::Byte, mLayerWidth, mLayerHeight, mExtent, mCrs ) );
 
  232    if ( !flowDirectionsDestProvider )
 
  233      throw QgsProcessingException( QObject::tr( 
"Could not create raster output: %1" ).arg( flowDirectionsOutputFile ) );
 
  234    if ( !flowDirectionsDestProvider->isValid() )
 
  237    flowDirectionsDestProvider->setNoDataValue( 1, 255 );
 
  238    flowDirectionsDestProvider->setEditable( 
true );
 
  241  std::unique_ptr<QgsRasterFileWriter> watershedBasinsWriter;
 
  242  std::unique_ptr<QgsRasterDataProvider> watershedBasinsDestProvider;
 
  244  if ( !watershedBasinsOutputFile.isEmpty() )
 
  246    const QFileInfo fi( watershedBasinsOutputFile );
 
  249    watershedBasinsWriter = std::make_unique<QgsRasterFileWriter>( watershedBasinsOutputFile );
 
  250    watershedBasinsWriter->setOutputProviderKey( QStringLiteral( 
"gdal" ) );
 
  251    watershedBasinsWriter->setOutputFormat( outputFormat );
 
  253    watershedBasinsDestProvider.reset( watershedBasinsWriter->createOneBandRaster( 
Qgis::DataType::Int32, mLayerWidth, mLayerHeight, mExtent, mCrs ) );
 
  255    if ( !watershedBasinsDestProvider )
 
  256      throw QgsProcessingException( QObject::tr( 
"Could not create raster output: %1" ).arg( watershedBasinsOutputFile ) );
 
  257    if ( !watershedBasinsDestProvider->isValid() )
 
  260    watershedBasinsDestProvider->setNoDataValue( 1, -1 );
 
  261    watershedBasinsDestProvider->setEditable( 
true );
 
  264  std::unique_ptr< QgsRasterBlock > sourceDemData( mInterface->block( mBand, mExtent, mLayerWidth, mLayerHeight ) );
 
  265  if ( !sourceDemData )
 
  270  auto filledDemData = std::make_unique<QgsRasterBlock>( mDataType, mLayerWidth, mLayerHeight );
 
  271  filledDemData->setNoDataValue( mNoData );
 
  272  filledDemData->setIsNoData();
 
  274  auto watershedData = std::make_unique<QgsRasterBlock>( 
Qgis::DataType::Int32, mLayerWidth, mLayerHeight );
 
  275  watershedData->setNoDataValue( -1 );
 
  276  watershedData->setIsNoData();
 
  278  auto outputFlowData = std::make_unique<QgsRasterBlock>( 
Qgis::DataType::Byte, mLayerWidth, mLayerHeight );
 
  279  outputFlowData->setNoDataValue( 255 );
 
  280  outputFlowData->setIsNoData();
 
  282  auto seedData = std::make_unique<QgsRasterBlock>( 
Qgis::DataType::Byte, mLayerWidth, mLayerHeight );
 
  285  double minSlope = parameterAsDouble( parameters, QStringLiteral( 
"MIN_SLOPE" ), context );
 
  287  bool preserve = 
false;
 
  288  if ( minSlope > 0.0 )
 
  290    minSlope = tan( minSlope * M_PI / 180.0 );
 
  291    for ( 
int i = 0; i < 8; i++ )
 
  292      mindiff[i] = minSlope * mDirectionalLengths[i];
 
  297  CFillSinks_WL_Node tempNode;
 
  302  bool isNoData = 
false;
 
  305  std::size_t processed = 0;
 
  306  const std::size_t totalCells = 
static_cast< std::size_t 
>( mLayerWidth ) * mLayerHeight;
 
  308  for ( 
int row = 0; row < mLayerHeight; row++ )
 
  313    for ( 
int col = 0; col < mLayerWidth; col++ )
 
  315      value = sourceDemData->valueAndNoData( row, col, isNoData );
 
  318        for ( 
Direction direction : { North, NorthEast, East, SouthEast, South, SouthWest, West, NorthWest } )
 
  320          int iCol = col + COL_DIRECTION_OFFSETS[direction];
 
  321          int iRow = row + ROW_DIRECTION_OFFSETS[direction];
 
  323          if ( !isInGrid( iRow, iCol ) || sourceDemData->isNoData( iRow, iCol ) )
 
  325            const double z = value;
 
  326            filledDemData->setValue( row, col, z );
 
  327            seedData->setValue( row, col, 1.0 );
 
  328            watershedData->setValue( row, col, 
static_cast< double >( 
id ) );
 
  334            theQueue.push( tempNode );
 
  340      feedback->
setProgress( 
static_cast< double >( processed ) / 
static_cast< double >( totalCells ) * 100 );
 
  348  feedback->
setProgressText( QObject::tr( 
"Filling using least cost paths" ) );
 
  350  while ( !theQueue.empty() )
 
  352    PriorityQ::value_type tempNode = theQueue.top();
 
  354    const int row = tempNode.row;
 
  355    const int col = tempNode.col;
 
  356    const double z = tempNode.spill;
 
  359    const long long id = 
static_cast< long long >( watershedData->value( row, col ) );
 
  361    for ( 
Direction direction : { North, NorthEast, East, SouthEast, South, SouthWest, West, NorthWest } )
 
  363      const int iCol = col + COL_DIRECTION_OFFSETS[direction];
 
  364      const int iRow = row + ROW_DIRECTION_OFFSETS[direction];
 
  366      const bool iInGrid = isInGrid( iRow, iCol );
 
  367      double iz = iInGrid ? sourceDemData->valueAndNoData( iRow, iCol, isNoData ) : 0;
 
  368      if ( iInGrid && !isNoData )
 
  370        if ( filledDemData->isNoData( iRow, iCol ) )
 
  374            iz = std::max( iz, z + mindiff[
static_cast< int >( direction )] );
 
  379            outputFlowData->setValue( iRow, iCol, INVERSE_DIRECTION[
static_cast< int >( direction )] );
 
  385          theQueue.push( tempNode );
 
  387          filledDemData->setValue( iRow, iCol, iz );
 
  388          watershedData->setValue( iRow, iCol, 
id );
 
  391        else if ( seedData->value( iRow, iCol ) == 1 )
 
  393          watershedData->setValue( iRow, iCol, 
id );
 
  398    if ( outputFlowData->isNoData( row, col ) )
 
  399      outputFlowData->setValue( row, col, getDir( row, col, z, filledDemData.get() ) );
 
  401    feedback->
setProgress( 
static_cast< double >( processed ) / 
static_cast< double >( totalCells ) * 100 );
 
  411  if ( filledDemDestProvider )
 
  413    if ( !filledDemDestProvider->writeBlock( filledDemData.get(), 1, 0, 0 ) )
 
  415      throw QgsProcessingException( QObject::tr( 
"Could not write raster block: %1" ).arg( filledDemDestProvider->error().summary() ) );
 
  417    filledDemDestProvider->setEditable( 
false );
 
  418    outputs.insert( QStringLiteral( 
"OUTPUT_FILLED_DEM" ), filledDemOutputFile );
 
  420  if ( flowDirectionsDestProvider )
 
  422    if ( !flowDirectionsDestProvider->writeBlock( outputFlowData.get(), 1, 0, 0 ) )
 
  424      throw QgsProcessingException( QObject::tr( 
"Could not write raster block: %1" ).arg( flowDirectionsDestProvider->error().summary() ) );
 
  426    flowDirectionsDestProvider->setEditable( 
false );
 
  427    outputs.insert( QStringLiteral( 
"OUTPUT_FLOW_DIRECTIONS" ), flowDirectionsOutputFile );
 
  429  if ( watershedBasinsDestProvider )
 
  431    if ( !watershedBasinsDestProvider->writeBlock( watershedData.get(), 1, 0, 0 ) )
 
  433      throw QgsProcessingException( QObject::tr( 
"Could not write raster block: %1" ).arg( watershedBasinsDestProvider->error().summary() ) );
 
  435    watershedBasinsDestProvider->setEditable( 
false );
 
  436    outputs.insert( QStringLiteral( 
"OUTPUT_WATERSHED_BASINS" ), watershedBasinsOutputFile );
 
@ Byte
Eight bit unsigned integer (quint8)
 
@ Int32
Thirty two bit signed integer (qint32)
 
@ Advanced
Parameter is an advanced parameter which should be hidden from users by default.
 
@ Double
Double/float values.
 
bool isCanceled() const
Tells whether the operation has been canceled already.
 
void setProgress(double progress)
Sets the current progress for the feedback object.
 
virtual QgsRectangle extent() const
Returns the extent of the layer.
 
QgsCoordinateReferenceSystem crs
 
Contains information about the context in which a processing algorithm is executed.
 
Custom exception class for processing related exceptions.
 
Base class for providing feedback from a processing algorithm.
 
virtual void setProgressText(const QString &text)
Sets a progress report text string.
 
A raster band parameter for Processing algorithms.
 
A raster layer parameter for processing algorithms.
 
double valueAndNoData(int row, int column, bool &isNoData) const
Reads a single value from the pixel at row and column, if type of block is numeric.
 
QgsRasterDataProvider * clone() const override=0
Clone itself, create deep copy.
 
virtual bool sourceHasNoDataValue(int bandNo) const
Returns true if source band has no data value.
 
virtual double sourceNoDataValue(int bandNo) const
Value representing no data value.
 
Qgis::DataType dataType(int bandNo) const override=0
Returns data type for the band specified by number.
 
static QString driverForExtension(const QString &extension)
Returns the GDAL driver name for a specified file extension.
 
Represents a raster layer.
 
int height() const
Returns the height of the (unclipped) raster.
 
int bandCount() const
Returns the number of bands in this layer.
 
double rasterUnitsPerPixelX() const
Returns the number of raster units per each raster pixel in X axis.
 
QgsRasterDataProvider * dataProvider() override
Returns the source data provider.
 
double rasterUnitsPerPixelY() const
Returns the number of raster units per each raster pixel in Y axis.
 
int width() const
Returns the width of the (unclipped) raster.