26using namespace Qt::StringLiterals;
30QString QgsFlowConnectivityD8Algorithm::name()
const
32 return u
"flowconnectivity"_s;
35QString QgsFlowConnectivityD8Algorithm::displayName()
const
37 return QObject::tr(
"Flow connectivity" );
40QStringList QgsFlowConnectivityD8Algorithm::tags()
const
42 return QObject::tr(
"dem,flow,connectivity,d8,confluence,topology,hydrology,drainage" ).split(
',' );
45QString QgsFlowConnectivityD8Algorithm::group()
const
47 return QObject::tr(
"Raster terrain analysis" );
50QString QgsFlowConnectivityD8Algorithm::groupId()
const
52 return u
"rasterterrainanalysis"_s;
55QString QgsFlowConnectivityD8Algorithm::shortDescription()
const
57 return QObject::tr(
"Calculates the number of adjacent cells flowing directly into each grid cell using D8 flow routing." );
60QString QgsFlowConnectivityD8Algorithm::shortHelpString()
const
63 "This algorithm calculates deterministic 8 (D8) flow connectivity for each cell in an input elevation raster (DEM).\n\n"
64 "Output cell values represent the number of immediate 8-neighbor adjacent cells (0 to 8) whose D8 steepest downslope flow direction points directly into the cell:\n"
65 "• 0 = Ridge, crest, or spring cell receiving no incoming surface flow.\n"
66 "• 1 = Channel segment cell receiving flow from a single upstream neighbor.\n"
67 "• 2+ = Stream junction or confluence cell receiving flow from multiple converging upstream paths.\n\n"
68 "This algorithm is a port of the flow connectivity calculation from SAGA 'Channel Network and Drainage Basins' tool."
72QList<QgsAcademicReference> QgsFlowConnectivityD8Algorithm::academicReferences()
const
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 };
79QList<QgsProcessingAlgorithm::ExternalLink> QgsFlowConnectivityD8Algorithm::externalLinks()
const
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 }
86void QgsFlowConnectivityD8Algorithm::initAlgorithm(
const QVariantMap & )
91 outputNodataParam->setHelp( QObject::tr(
"The NODATA value to use in the output raster." ) );
93 addParameter( outputNodataParam.release() );
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 } } ) } } ) );
99 addParameter( creationOptsParam.release() );
101 auto outputParam = std::make_unique<QgsProcessingParameterRasterDestination>( u
"OUTPUT"_s, QObject::tr(
"Flow direction" ) );
102 addParameter( outputParam.release() );
107 return new QgsFlowConnectivityD8Algorithm();
112 QgsRasterLayer *layer = parameterAsRasterLayer( parameters, u
"INPUT"_s, context );
117 mLayerWidth = layer->
width();
118 mLayerHeight = layer->
height();
119 mExtent = layer->
extent();
129 QGS_MARK_ALGORITHM_SOURCE
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 );
136 std::unique_ptr<QgsRasterBlock> inputBlock( mInterface->block( 1, mExtent, mLayerWidth, mLayerHeight ) );
140 const qgssize totalCells =
static_cast<qgssize>( mLayerWidth ) * mLayerHeight;
143 multiStepFeedback.setCurrentStep( 0 );
145 std::vector<int8_t> d8Directions( totalCells, -1 );
148 for (
int row = 0; row < mLayerHeight; ++row )
153 multiStepFeedback.setProgress( 100.0 *
static_cast<double>( row ) / mLayerHeight );
155 const qgssize rowOffset =
static_cast<qgssize>( row ) * mLayerWidth;
156 for (
int col = 0; col < mLayerWidth; ++col )
158 const int dir = QgsRasterAnalysisUtils::steepestGradientDirection( inputBlock.get(), row, col, mCellSizeX, mCellSizeY,
true,
true );
159 d8Directions[rowOffset + col] =
static_cast<int8_t
>( dir );
163 auto outputBlock = std::make_unique<QgsRasterBlock>(
Qgis::DataType::Int16, mLayerWidth, mLayerHeight );
164 outputBlock->setNoDataValue( outputNoData );
167 multiStepFeedback.setCurrentStep( 1 );
168 int16_t *outData =
reinterpret_cast<int16_t *
>( outputBlock->bits() );
170 for (
int row = 0; row < mLayerHeight; ++row )
175 multiStepFeedback.setProgress(
static_cast<double>( row ) / mLayerHeight );
177 const qgssize rowOffset =
static_cast<qgssize>( row ) * mLayerWidth;
178 for (
int col = 0; col < mLayerWidth; ++col )
180 if ( inputBlock->isNoData( row, col ) )
182 outData[rowOffset + col] = outputNoData;
186 int incomingCount = 0;
187 for (
int dir = 0; dir < 8; ++dir )
189 const int oppositeDir = ( dir + 4 ) % 8;
192 if ( QgsRasterAnalysisUtils::neighborCellCoordinates( oppositeDir, row, col, neighborRow, neighborCol, mLayerHeight, mLayerWidth ) )
194 const qgssize neighborIdx =
static_cast<qgssize>( neighborRow ) * mLayerWidth + neighborCol;
195 if ( d8Directions[neighborIdx] == dir )
202 outData[rowOffset + col] =
static_cast<int16_t
>( incomingCount );
206 auto outputWriter = std::make_unique<QgsRasterFileWriter>( outputPath );
207 outputWriter->setOutputProviderKey( u
"gdal"_s );
208 if ( !creationOptions.isEmpty() )
210 outputWriter->setCreationOptions( creationOptions.split(
'|' ) );
212 outputWriter->setOutputFormat( outputFormat );
214 std::unique_ptr<QgsRasterDataProvider> destProvider( outputWriter->createOneBandRaster(
Qgis::DataType::Int16, mLayerWidth, mLayerHeight, mExtent, mCrs ) );
217 if ( !destProvider->isValid() )
220 destProvider->setNoDataValue( 1, outputNoData );
221 destProvider->setEditable(
true );
223 if ( !destProvider->writeBlock( outputBlock.get(), 1 ) )
225 throw QgsProcessingException( QObject::tr(
"Could not write raster block: %1" ).arg( destProvider->error().summary() ) );
228 destProvider->setEditable(
false );
231 outputs.insert( u
"OUTPUT"_s, outputPath );
@ Int16
Sixteen bit signed integer (qint16).
@ Advanced
Parameter is an advanced parameter which should be hidden from users by default.
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.
bool isCanceled() const
Tells whether the operation has been canceled already.
virtual Q_INVOKABLE QgsRectangle extent() const
Returns the extent of the layer.
QgsCoordinateReferenceSystem crs
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.
Processing feedback object for multi-step operations.
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.
unsigned long long qgssize
Qgssize is used instead of size_t, because size_t is stdlib type, unknown by SIP, and it would be har...
Encapsulates details of an external link describing an algorithm's behavior or source.