Skip to content

Commit

Permalink
Hydrography doc and tests
Browse files Browse the repository at this point in the history
  • Loading branch information
sdunesme committed Jan 10, 2025
1 parent cbf7d1a commit e07dfb2
Show file tree
Hide file tree
Showing 18 changed files with 307 additions and 18 deletions.
Binary file added docs/img/hack_order.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/img/strahler_order.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
30 changes: 26 additions & 4 deletions fct/algorithms/hydrography/HackOrder.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,15 @@
QgsProcessingParameterFeatureSink,
QgsProcessingParameterFeatureSource,
QgsProcessingParameterField,
QgsProcessingException
QgsProcessingException,
QgsProcessingUtils
)

from ..metadata import AlgorithmMetadata
from ..util import asQgsFields
from ...utils.assertions import assertLayersCompatibility

import processing

def index_by(i, d, x):
d[x[i]].append(x)
Expand Down Expand Up @@ -89,21 +93,24 @@ def initAlgorithm(self, configuration):
self.tr('From Node Field'),
parentLayerParameterName=self.INPUT,
type=QgsProcessingParameterField.Numeric,
defaultValue='NODEA'))
defaultValue='NODEA',
optional=True))

self.addParameter(QgsProcessingParameterField(
self.TO_NODE_FIELD,
self.tr('To Node Field'),
parentLayerParameterName=self.INPUT,
type=QgsProcessingParameterField.Numeric,
defaultValue='NODEB'))
defaultValue='NODEB',
optional=True))

self.addParameter(QgsProcessingParameterField(
self.MEASURE_FIELD,
self.tr('Measure Field'),
parentLayerParameterName=self.INPUT,
type=QgsProcessingParameterField.Numeric,
defaultValue='MEASURE'))
defaultValue='MEASURE',
optional=True))

self.addParameter(QgsProcessingParameterBoolean(
self.IS_DOWNSTREAM_MEAS,
Expand All @@ -123,6 +130,21 @@ def processAlgorithm(self, parameters, context, feedback):
distance_field = self.parameterAsString(parameters, self.MEASURE_FIELD, context)
is_downstream = self.parameterAsBool(parameters, self.IS_DOWNSTREAM_MEAS, context)

assertLayersCompatibility([self.parameterAsVectorLayer(parameters, self.INPUT, context)], feedback=feedback)

if not from_node_field or not to_node_field or not distance_field:
measurenetwork = processing.run('fct:measurenetworkfromoutlet', {
'INPUT': self.parameterAsVectorLayer(parameters, self.INPUT, context),
'FROM_NODE_FIELD':'',
'TO_NODE_FIELD':'',
'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT
}, context=context, feedback=feedback, is_child_algorithm=True)

layer = QgsProcessingUtils.variantToSource(measurenetwork['OUTPUT'], context)
from_node_field = 'NODEA'
to_node_field = 'NODEB'
distance_field = 'MEASURE'

# Step 1 - Find sources and build djacency index

feedback.setProgressText(self.tr("Build adjacency index ..."))
Expand Down
2 changes: 2 additions & 0 deletions fct/algorithms/hydrography/HackOrder.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
displayName: Hack (Length-wise) Stream Order
group: hydrography
description: |
![Hack Order](/fct-qgis/img/hack_order.png)
shortHelpString: NULL
helpString: NULL
helpUrl: NULL
Expand Down
25 changes: 22 additions & 3 deletions fct/algorithms/hydrography/NetworkNodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,15 @@
QgsProcessingParameterFeatureSource,
QgsProcessingParameterField,
QgsWkbTypes,
QgsProcessingException
QgsProcessingException,
QgsProcessingUtils
)

from ..metadata import AlgorithmMetadata
from ..util import asQgsFields
from ...utils.assertions import assertLayersCompatibility

import processing

def asPolyline(geometry):

Expand Down Expand Up @@ -94,14 +98,16 @@ def initAlgorithm(self, configuration):
self.tr('From Node Field'),
parentLayerParameterName=self.INPUT,
type=QgsProcessingParameterField.Numeric,
defaultValue='NODEA'))
defaultValue='NODEA',
optional=True))

self.addParameter(QgsProcessingParameterField(
self.TO_NODE_FIELD,
self.tr('To Node Field'),
parentLayerParameterName=self.INPUT,
type=QgsProcessingParameterField.Numeric,
defaultValue='NODEB'))
defaultValue='NODEB',
optional=True))

self.addParameter(QgsProcessingParameterField(
self.MEAS_FIELD,
Expand Down Expand Up @@ -137,6 +143,19 @@ def processAlgorithm(self, parameters, context, feedback):
measure_field = self.parameterAsString(parameters, self.MEAS_FIELD, context)
subset = self.parameterAsInt(parameters, self.SUBSET, context)

assertLayersCompatibility([self.parameterAsVectorLayer(parameters, self.INPUT, context)], feedback=feedback)

if not from_node_field or not to_node_field:
identifynodes = processing.run('fct:identifynetworknodes', {
'INPUT': self.parameterAsVectorLayer(parameters, self.INPUT, context),
'NODES': QgsProcessing.TEMPORARY_OUTPUT,
'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT
}, context=context, feedback=feedback, is_child_algorithm=True)

layer = QgsProcessingUtils.variantToSource(identifynodes['OUTPUT'], context)
from_node_field = 'NODEA'
to_node_field = 'NODEB'

subset_map = [
('All', {}),
('Source', {'Source', 'Divergence'}),
Expand Down
19 changes: 16 additions & 3 deletions fct/algorithms/hydrography/SelectConnectedComponents.yml
Original file line number Diff line number Diff line change
@@ -1,9 +1,22 @@
displayName: Select Connected Reaches
group: hydrography
shortHelpString: NULL
helpString: NULL
helpUrl: NULL
summary: Select reaches connected to the selected one in the input layer.
description: |
The input hydrographic network needs to be well oriented. You can use the [Fix Link Orientation](/fct-qgis/algorithms/hydrography/FixLinkOrientation/) to check that.
This tool is useful to extract a subset of a hydrographic network.
Examples:
- Select a downstream reach in the network, then execute *Select Connected Reaches* with upstream `DIRECTION` parameter. All the upstream reaches will be selected.
- Select an upstream reach in the network, then execute *Select Connected Reaches* with downstream `DIRECTION` parameter. All the downstream axis will be selected.
tags:
- hydrography
- graph
- selection
- connectivity

seealso:
- "[IdentifyNetworkNodes](/fct-qgis/algorithms/hydrography/IdentifyNetworkNodes/)"
- "[FixLinkOrientation](/fct-qgis/algorithms/hydrography/FixLinkOrientation/)"
28 changes: 25 additions & 3 deletions fct/algorithms/hydrography/StrahlerOrder.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,14 @@
QgsProcessingParameterFeatureSource,
QgsProcessingParameterField,
QgsProcessingException,
QgsProcessingUtils
)

from ..metadata import AlgorithmMetadata
from ..util import appendUniqueField
from ...utils.assertions import assertLayersCompatibility

import processing

class StrahlerOrder(AlgorithmMetadata, QgsProcessingAlgorithm):
""" Horton-Strahler stream order of each link in a stream network
Expand All @@ -59,22 +63,24 @@ def initAlgorithm(self, configuration):
self.tr('From Node Field'),
parentLayerParameterName=self.INPUT,
type=QgsProcessingParameterField.Numeric,
defaultValue='NODEA'))
defaultValue='NODEA',
optional=True))

self.addParameter(QgsProcessingParameterField(
self.TO_NODE_FIELD,
self.tr('To Node Field'),
parentLayerParameterName=self.INPUT,
type=QgsProcessingParameterField.Numeric,
defaultValue='NODEB'))
defaultValue='NODEB',
optional=True))

self.addParameter(QgsProcessingParameterField(
self.AXIS_FIELD,
self.tr('Hack Order Field'),
parentLayerParameterName=self.INPUT,
type=QgsProcessingParameterField.Numeric,
defaultValue='HACK',
optional=False))
optional=True))

self.addParameter(QgsProcessingParameterFeatureSink(
self.OUTPUT,
Expand All @@ -90,6 +96,22 @@ def processAlgorithm(self, parameters, context, fb):
to_node_field = self.parameterAsString(parameters, self.TO_NODE_FIELD, context)
axis_field = self.parameterAsString(parameters, self.AXIS_FIELD, context)

assertLayersCompatibility([self.parameterAsVectorLayer(parameters, self.INPUT, context)], feedback=feedback)

if not from_node_field or not to_node_field or not axis_field:
hackorder = processing.run('fct:hackorder', {
'INPUT': self.parameterAsVectorLayer(parameters, self.INPUT, context),
'FROM_NODE_FIELD': '',
'TO_NODE_FIELD': '',
'MEASURE_FIELD': '',
'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT
}, context=context, feedback=feedback, is_child_algorithm=True)

layer = QgsProcessingUtils.variantToSource(hackorder['OUTPUT'], context)
from_node_field = 'NODEA'
to_node_field = 'NODEB'
axis_field = 'HACK'

# Step 1 - Build adjacency index

feedback.setCurrentStep(0)
Expand Down
2 changes: 2 additions & 0 deletions fct/algorithms/hydrography/StrahlerOrder.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
displayName: Strahler Stream Order
group: hydrography
description: |
![Strahler Order](/fct-qgis/img/strahler_order.png)
shortHelpString: NULL
helpString: NULL
helpUrl: NULL
Expand Down
23 changes: 21 additions & 2 deletions fct/algorithms/referencing/MeasureNetworkFromOutlet.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
QgsProcessing,
QgsProcessingAlgorithm,
QgsProcessingException,
QgsProcessingUtils,
QgsProcessingParameterFeatureSink,
QgsProcessingParameterFeatureSource,
QgsProcessingParameterField,
Expand All @@ -36,6 +37,9 @@

from ..metadata import AlgorithmMetadata
from ..util import asQgsFields
from ...utils.assertions import assertLayersCompatibility

import processing

Link = namedtuple('Link', ('a', 'b', 'edge_id', 'length'))

Expand Down Expand Up @@ -83,14 +87,16 @@ def initAlgorithm(self, configuration):
self.tr('From Node Field'),
parentLayerParameterName=self.INPUT,
type=QgsProcessingParameterField.Numeric,
defaultValue='NODEA'))
defaultValue='NODEA',
optional=True))

self.addParameter(QgsProcessingParameterField(
self.TO_NODE_FIELD,
self.tr('To Node Field'),
parentLayerParameterName=self.INPUT,
type=QgsProcessingParameterField.Numeric,
defaultValue='NODEB'))
defaultValue='NODEB',
optional=True))

self.addParameter(QgsProcessingParameterFeatureSink(
self.OUTPUT,
Expand All @@ -103,6 +109,19 @@ def processAlgorithm(self, parameters, context, feedback):
from_node_field = self.parameterAsString(parameters, self.FROM_NODE_FIELD, context)
to_node_field = self.parameterAsString(parameters, self.TO_NODE_FIELD, context)

assertLayersCompatibility([self.parameterAsVectorLayer(parameters, self.INPUT, context)], feedback=feedback)

if not from_node_field or not to_node_field:
identifynodes = processing.run('fct:identifynetworknodes', {
'INPUT': self.parameterAsVectorLayer(parameters, self.INPUT, context),
'NODES': QgsProcessing.TEMPORARY_OUTPUT,
'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT
}, context=context, feedback=feedback, is_child_algorithm=True)

layer = QgsProcessingUtils.variantToSource(identifynodes['OUTPUT'], context)
from_node_field = 'NODEA'
to_node_field = 'NODEB'

fields = layer.fields().toList() + [
QgsField('MEASURE', QVariant.Double, len=10, prec=2),
QgsField('LENGTH', QVariant.Double, len=6, prec=2)
Expand Down
2 changes: 1 addition & 1 deletion templates/algorithm.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ title: {{ displayName }}
[comment]: # (Autogenerated - Do Not Edit This File)

# {{ displayName }}
![Coverage Status](https://img.shields.io/badge/tested-{{ coverage }}%25-{{ covcolor }})
![Coverage Status](https://img.shields.io/badge/tested-{{ coverage }}%25-{{ covcolor }}) [![Report an issue](https://img.shields.io/badge/Report_an_issue-{{ displayName }}-blue?style=social&logo=github)](https://github.com/EVS-GIS/fct-qgis/issues/new?title={{ displayName }})

## Summary

Expand Down
2 changes: 1 addition & 1 deletion test/reports/coverage.json

Large diffs are not rendered by default.

Loading

0 comments on commit e07dfb2

Please sign in to comment.