QGIS API Documentation 4.3.0-Master (45633be667c)
Loading...
Searching...
No Matches
qgsalgorithmflowdirection.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsalgorithmflowdirection.cpp
3 ---------------------
4 begin : September 2026
5 copyright : (C) 2026 by Nyall Dawson
6 email : nyall dot dawson at gmail dot com
7 ***************************************************************************/
8
9/***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
17
19
22#include "qgsrasterfilewriter.h"
23
24#include <QString>
25
26using namespace Qt::StringLiterals;
27
29
30QString QgsFlowDirectionD8Algorithm::name() const
31{
32 return u"flowdirection"_s;
33}
34
35QString QgsFlowDirectionD8Algorithm::displayName() const
36{
37 return QObject::tr( "Flow direction" );
38}
39
40QStringList QgsFlowDirectionD8Algorithm::tags() const
41{
42 return QObject::tr( "dem,flow,direction,d8,steepest,hydrology,catchment,drainage" ).split( ',' );
43}
44
45QString QgsFlowDirectionD8Algorithm::group() const
46{
47 return QObject::tr( "Raster terrain analysis" );
48}
49
50QString QgsFlowDirectionD8Algorithm::groupId() const
51{
52 return u"rasterterrainanalysis"_s;
53}
54
55QString QgsFlowDirectionD8Algorithm::shortDescription() const
56{
57 return QObject::tr( "Calculates single-direction D8 flow directions from a digital elevation model." );
58}
59
60QString QgsFlowDirectionD8Algorithm::shortHelpString() const
61{
62 return QObject::tr(
63 "This algorithm calculates deterministic 8 (D8) flow directions for each cell in an input elevation raster (DEM).\n\n"
64 "Flow direction values are output as 8-neighbor directional indices numbered clockwise starting from North:\n"
65 "0 = North, 1 = North-East, 2 = East, 3 = South-East, 4 = South, 5 = South-West, 6 = West, 7 = North-West.\n\n"
66 "Sink/pit cells are assigned a value of -1 in the output, and flat areas are assigned -2.\n\n"
67 "Cells with no downslope neighbor or NoData elevation values are assigned nodata in the output.\n\n"
68 "This algorithm is a port of the flow direction calculation from SAGA 'Channel Network and Drainage Basins' tool."
69 );
70}
71
72QList<QgsAcademicReference> QgsFlowDirectionD8Algorithm::academicReferences() const
73{
74 const QgsAcademicReference ocallaghanReference = QgsAcademicReference::
75 createJournalArticle( { u"O'Callaghan, J. F."_s, u"Mark, D. M."_s }, 1984, u"The extraction of drainage networks from digital elevation data"_s, u"Computer Vision, Graphics and Image Processing"_s, u"28"_s, QString(), u"323-344"_s );
76 return { ocallaghanReference };
77}
78
79QList<QgsProcessingAlgorithm::ExternalLink> QgsFlowDirectionD8Algorithm::externalLinks() const
80{
81 return {
82 QgsProcessingAlgorithm::ExternalLink { QObject::tr( "SAGA tool source code" ), u"https://sourceforge.net/p/saga-gis/code/ci/33d1062b7120c696c9dd258378c48d86dc33560c/tree/saga-gis/src/tools/terrain_analysis/ta_channels/D8_Flow_Analysis.cpp"_s }
83 };
84}
85
86void QgsFlowDirectionD8Algorithm::initAlgorithm( const QVariantMap & )
87{
88 addParameter( new QgsProcessingParameterRasterLayer( u"INPUT"_s, QObject::tr( "Input layer" ) ) );
89
90 auto outputNodataParam = std::make_unique<QgsProcessingParameterNumber>( u"NODATA"_s, QObject::tr( "Output NoData value" ), Qgis::ProcessingNumberParameterType::Integer, -9999 );
91 outputNodataParam->setHelp( QObject::tr( "The NODATA value to use in the output raster." ) );
92 outputNodataParam->setFlags( outputNodataParam->flags() | Qgis::ProcessingParameterFlag::Advanced );
93 addParameter( outputNodataParam.release() );
94
95 auto creationOptsParam = std::make_unique<QgsProcessingParameterString>( u"CREATION_OPTIONS"_s, QObject::tr( "Creation options" ), QVariant(), false, true );
96 creationOptsParam->setHelp( QObject::tr( "The raster creation options for the output raster. These options control things like colorimetry, compression, etc." ) );
97 creationOptsParam->setMetadata( QVariantMap( { { u"widget_wrapper"_s, QVariantMap( { { u"widget_type"_s, u"rasteroptions"_s } } ) } } ) );
98 creationOptsParam->setFlags( creationOptsParam->flags() | Qgis::ProcessingParameterFlag::Advanced );
99 addParameter( creationOptsParam.release() );
100
101 auto outputParam = std::make_unique<QgsProcessingParameterRasterDestination>( u"OUTPUT"_s, QObject::tr( "Flow direction" ) );
102 addParameter( outputParam.release() );
103}
104
105QgsProcessingAlgorithm *QgsFlowDirectionD8Algorithm::createInstance() const
106{
107 return new QgsFlowDirectionD8Algorithm();
108}
109
110bool QgsFlowDirectionD8Algorithm::prepareAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback * )
111{
112 QgsRasterLayer *layer = parameterAsRasterLayer( parameters, u"INPUT"_s, context );
113 if ( !layer || !layer->dataProvider() )
114 throw QgsProcessingException( invalidRasterError( parameters, u"INPUT"_s ) );
115
116 mInterface.reset( layer->dataProvider()->clone() );
117 mLayerWidth = layer->width();
118 mLayerHeight = layer->height();
119 mExtent = layer->extent();
120 mCrs = layer->crs();
121 mCellSizeX = layer->rasterUnitsPerPixelX();
122 mCellSizeY = layer->rasterUnitsPerPixelY();
123
124 return true;
125}
126
127QVariantMap QgsFlowDirectionD8Algorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
128{
129 QGS_MARK_ALGORITHM_SOURCE
130
131 const QString creationOptions = parameterAsString( parameters, u"CREATION_OPTIONS"_s, context ).trimmed();
132 const QString outputPath = parameterAsOutputLayer( parameters, u"OUTPUT"_s, context );
133 const QString outputFormat = parameterAsOutputRasterFormat( parameters, u"OUTPUT"_s, context );
134 const int outputNoData = parameterAsInt( parameters, u"NODATA"_s, context );
135
136 std::unique_ptr<QgsRasterBlock> inputBlock( mInterface->block( 1, mExtent, mLayerWidth, mLayerHeight ) );
137 if ( !inputBlock )
138 throw QgsProcessingException( QObject::tr( "Could not read input raster block." ) );
139
140 auto outputBlock = std::make_unique<QgsRasterBlock>( Qgis::DataType::Int16, mLayerWidth, mLayerHeight );
141 outputBlock->setNoDataValue( outputNoData );
142
143 int16_t *outData = reinterpret_cast<int16_t *>( outputBlock->bits() );
144
145 // process D8 steepest gradient direction for every grid cell (matching SAGA CD8_Flow_Analysis::Get_Direction)
146 for ( int row = 0; row < mLayerHeight; ++row )
147 {
148 if ( feedback->isCanceled() )
149 return {};
150
151 feedback->setProgress( 100.0 * static_cast<double>( row ) / mLayerHeight );
152
153 const std::size_t rowOffset = static_cast<std::size_t>( row ) * mLayerWidth;
154 for ( int col = 0; col < mLayerWidth; ++col )
155 {
156 const int dir = QgsRasterAnalysisUtils::steepestGradientDirection( inputBlock.get(), row, col, mCellSizeX, mCellSizeY, true, true );
157 outData[rowOffset + col] = dir == -3 ? outputNoData : static_cast<int16_t>( dir );
158 }
159 }
160
161 auto outputWriter = std::make_unique<QgsRasterFileWriter>( outputPath );
162 outputWriter->setOutputProviderKey( u"gdal"_s );
163 if ( !creationOptions.isEmpty() )
164 {
165 outputWriter->setCreationOptions( creationOptions.split( '|' ) );
166 }
167 outputWriter->setOutputFormat( outputFormat );
168
169 std::unique_ptr<QgsRasterDataProvider> destProvider( outputWriter->createOneBandRaster( Qgis::DataType::Int16, mLayerWidth, mLayerHeight, mExtent, mCrs ) );
170 if ( !destProvider )
171 throw QgsProcessingException( QObject::tr( "Could not create raster output: %1" ).arg( outputPath ) );
172 if ( !destProvider->isValid() )
173 throw QgsProcessingException( QObject::tr( "Could not create raster output %1: %2" ).arg( outputPath, destProvider->error().message( QgsErrorMessage::Text ) ) );
174
175 destProvider->setNoDataValue( 1, outputNoData );
176 destProvider->setEditable( true );
177
178 if ( !destProvider->writeBlock( outputBlock.get(), 1 ) )
179 {
180 throw QgsProcessingException( QObject::tr( "Could not write raster block: %1" ).arg( destProvider->error().summary() ) );
181 }
182
183 destProvider->setEditable( false );
184
185 QVariantMap outputs;
186 outputs.insert( u"OUTPUT"_s, outputPath );
187 return outputs;
188}
189
@ Int16
Sixteen bit signed integer (qint16).
Definition qgis.h:398
@ Advanced
Parameter is an advanced parameter which should be hidden from users by default.
Definition qgis.h:4006
Encapsulates an academic reference and formats it according to style guidelines.
static QgsAcademicReference createJournalArticle(const QStringList &authors, int year, const QString &title, const QString &journal, const QString &volume=QString(), const QString &issue=QString(), const QString &pages=QString())
Creates a journal article reference.
@ Text
Plain text format.
Definition qgserror.h:40
bool isCanceled() const
Tells whether the operation has been canceled already.
Definition qgsfeedback.h:56
void setProgress(double progress)
Sets the current progress for the feedback object.
Definition qgsfeedback.h:65
virtual Q_INVOKABLE QgsRectangle extent() const
Returns the extent of the layer.
QgsCoordinateReferenceSystem crs
Definition qgsmaplayer.h:90
Abstract base class for processing algorithms.
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.
A raster layer parameter for processing algorithms.
QgsRasterDataProvider * clone() const override=0
Clone itself, create deep copy.
Represents a raster layer.
int height() const
Returns the height of the (unclipped) raster.
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.