Skip to content

Commit 1e75c30

Browse files
committed
Add _d8 suffix to D8 hydro modules and unified routing dispatch
Rename all 13 unsuffixed D8 source files and their test files to include a _d8 suffix (e.g. flow_direction.py -> flow_direction_d8.py), matching the existing _dinf/_mfd convention. Public functions inside each file are also renamed (flow_direction -> flow_direction_d8, etc.). stream_order_d8's "method" parameter (strahler/shreve) is renamed to "ordering" to avoid collision with the routing dispatch. hydro/__init__.py now provides unified wrappers via _RoutingDispatch, so callers can do flow_direction(dem, routing='dinf') instead of importing flow_direction_dinf directly. D8-only functions (basin, sink, fill, twi, snap_pour_point) accept routing= for forward compatibility. All cross-module imports, dinf/mfd helper imports, test imports, and example imports are updated accordingly.
1 parent bd49170 commit 1e75c30

39 files changed

+256
-146
lines changed

examples/flood_simulation.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,10 @@
4040
from matplotlib.animation import FuncAnimation
4141

4242
from xrspatial import generate_terrain, slope
43-
from xrspatial.hydro.flow_direction import flow_direction
44-
from xrspatial.hydro.flow_accumulation import flow_accumulation
45-
from xrspatial.hydro.flow_length import flow_length
46-
from xrspatial.hydro.hand import hand
43+
from xrspatial.hydro.flow_direction_d8 import flow_direction_d8 as flow_direction
44+
from xrspatial.hydro.flow_accumulation_d8 import flow_accumulation_d8 as flow_accumulation
45+
from xrspatial.hydro.flow_length_d8 import flow_length_d8 as flow_length
46+
from xrspatial.hydro.hand_d8 import hand_d8 as hand
4747
from xrspatial.flood import (
4848
curve_number_runoff,
4949
flood_depth,

examples/landslide_risk.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,9 @@
4343

4444
from xrspatial import generate_terrain, slope
4545
from xrspatial.curvature import curvature
46-
from xrspatial.hydro.flow_direction import flow_direction
47-
from xrspatial.hydro.flow_accumulation import flow_accumulation
48-
from xrspatial.hydro.twi import twi
46+
from xrspatial.hydro.flow_direction_d8 import flow_direction_d8 as flow_direction
47+
from xrspatial.hydro.flow_accumulation_d8 import flow_accumulation_d8 as flow_accumulation
48+
from xrspatial.hydro.twi_d8 import twi_d8 as twi
4949
from xrspatial.terrain_metrics import tpi
5050

5151
# -- Tunable parameters -----------------------------------------------------

examples/watershed_explorer.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,12 @@
3434
import matplotlib.colors as mcolors
3535

3636
from xrspatial import generate_terrain
37-
from xrspatial.hydro.flow_direction import flow_direction
38-
from xrspatial.hydro.flow_accumulation import flow_accumulation
39-
from xrspatial.hydro.stream_order import stream_order
40-
from xrspatial.hydro.snap_pour_point import snap_pour_point
41-
from xrspatial.hydro.watershed import watershed
42-
from xrspatial.hydro.basin import basin
37+
from xrspatial.hydro.flow_direction_d8 import flow_direction_d8 as flow_direction
38+
from xrspatial.hydro.flow_accumulation_d8 import flow_accumulation_d8 as flow_accumulation
39+
from xrspatial.hydro.stream_order_d8 import stream_order_d8 as stream_order
40+
from xrspatial.hydro.snap_pour_point_d8 import snap_pour_point_d8 as snap_pour_point
41+
from xrspatial.hydro.watershed_d8 import watershed_d8 as watershed
42+
from xrspatial.hydro.basin_d8 import basin_d8 as basin
4343

4444
# -- Tunable parameters -----------------------------------------------------
4545
CELL_SIZE = 30.0 # metres per pixel

xrspatial/__init__.py

Lines changed: 28 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@
2323
from xrspatial.curvature import curvature # noqa
2424
from xrspatial.emerging_hotspots import emerging_hotspots # noqa
2525
from xrspatial.erosion import erode # noqa
26-
from xrspatial.hydro.fill import fill # noqa
26+
from xrspatial.hydro import fill # noqa: unified wrapper
27+
from xrspatial.hydro import fill_d8 # noqa
2728
from xrspatial.interpolate import idw # noqa
2829
from xrspatial.interpolate import kriging # noqa
2930
from xrspatial.interpolate import spline # noqa
@@ -38,27 +39,22 @@
3839
from xrspatial.flood import flood_depth # noqa
3940
from xrspatial.flood import inundation # noqa
4041
from xrspatial.flood import travel_time # noqa
41-
from xrspatial.hydro.flow_accumulation import flow_accumulation # noqa
42-
from xrspatial.hydro.flow_accumulation_dinf import flow_accumulation_dinf # noqa
43-
from xrspatial.hydro.flow_accumulation_mfd import flow_accumulation_mfd # noqa
44-
from xrspatial.hydro.flow_direction import flow_direction # noqa
45-
from xrspatial.hydro.flow_direction_dinf import flow_direction_dinf # noqa
46-
from xrspatial.hydro.flow_direction_mfd import flow_direction_mfd # noqa
47-
from xrspatial.hydro.flow_length import flow_length # noqa
48-
from xrspatial.hydro.flow_length_dinf import flow_length_dinf # noqa
49-
from xrspatial.hydro.flow_length_mfd import flow_length_mfd # noqa
50-
from xrspatial.hydro.flow_path import flow_path # noqa
51-
from xrspatial.hydro.flow_path_dinf import flow_path_dinf # noqa
52-
from xrspatial.hydro.flow_path_mfd import flow_path_mfd # noqa
42+
from xrspatial.hydro import flow_accumulation # noqa: unified wrapper
43+
from xrspatial.hydro import flow_accumulation_d8, flow_accumulation_dinf, flow_accumulation_mfd # noqa
44+
from xrspatial.hydro import flow_direction # noqa: unified wrapper
45+
from xrspatial.hydro import flow_direction_d8, flow_direction_dinf, flow_direction_mfd # noqa
46+
from xrspatial.hydro import flow_length # noqa: unified wrapper
47+
from xrspatial.hydro import flow_length_d8, flow_length_dinf, flow_length_mfd # noqa
48+
from xrspatial.hydro import flow_path # noqa: unified wrapper
49+
from xrspatial.hydro import flow_path_d8, flow_path_dinf, flow_path_mfd # noqa
5350
from xrspatial.focal import mean # noqa
5451
from xrspatial.glcm import glcm_texture # noqa
5552
from xrspatial.morphology import morph_closing # noqa
5653
from xrspatial.morphology import morph_dilate # noqa
5754
from xrspatial.morphology import morph_erode # noqa
5855
from xrspatial.morphology import morph_opening # noqa
59-
from xrspatial.hydro.hand import hand # noqa
60-
from xrspatial.hydro.hand_dinf import hand_dinf # noqa
61-
from xrspatial.hydro.hand_mfd import hand_mfd # noqa
56+
from xrspatial.hydro import hand # noqa: unified wrapper
57+
from xrspatial.hydro import hand_d8, hand_dinf, hand_mfd # noqa
6258
from xrspatial.hillshade import hillshade # noqa
6359
from xrspatial.mahalanobis import mahalanobis # noqa
6460
from xrspatial.multispectral import arvi # noqa
@@ -80,14 +76,14 @@
8076
from xrspatial.proximity import great_circle_distance # noqa
8177
from xrspatial.proximity import manhattan_distance # noqa
8278
from xrspatial.proximity import proximity # noqa
83-
from xrspatial.hydro.sink import sink # noqa
84-
from xrspatial.hydro.snap_pour_point import snap_pour_point # noqa
85-
from xrspatial.hydro.stream_link import stream_link # noqa
86-
from xrspatial.hydro.stream_link_dinf import stream_link_dinf # noqa
87-
from xrspatial.hydro.stream_link_mfd import stream_link_mfd # noqa
88-
from xrspatial.hydro.stream_order import stream_order # noqa
89-
from xrspatial.hydro.stream_order_dinf import stream_order_dinf # noqa
90-
from xrspatial.hydro.stream_order_mfd import stream_order_mfd # noqa
79+
from xrspatial.hydro import sink # noqa: unified wrapper
80+
from xrspatial.hydro import sink_d8 # noqa
81+
from xrspatial.hydro import snap_pour_point # noqa: unified wrapper
82+
from xrspatial.hydro import snap_pour_point_d8 # noqa
83+
from xrspatial.hydro import stream_link # noqa: unified wrapper
84+
from xrspatial.hydro import stream_link_d8, stream_link_dinf, stream_link_mfd # noqa
85+
from xrspatial.hydro import stream_order # noqa: unified wrapper
86+
from xrspatial.hydro import stream_order_d8, stream_order_dinf, stream_order_mfd # noqa
9187
from xrspatial.sky_view_factor import sky_view_factor # noqa
9288
from xrspatial.slope import slope # noqa
9389
from xrspatial.surface_distance import surface_allocation # noqa
@@ -99,14 +95,16 @@
9995
from xrspatial.terrain_metrics import roughness # noqa
10096
from xrspatial.terrain_metrics import tpi # noqa
10197
from xrspatial.terrain_metrics import tri # noqa
102-
from xrspatial.hydro.twi import twi # noqa
98+
from xrspatial.hydro import twi # noqa: unified wrapper
99+
from xrspatial.hydro import twi_d8 # noqa
103100
from xrspatial.polygonize import polygonize # noqa
104101
from xrspatial.viewshed import viewshed # noqa
105-
from xrspatial.hydro.basin import basin # noqa
106-
from xrspatial.hydro.watershed import basins # noqa
107-
from xrspatial.hydro.watershed import watershed # noqa
108-
from xrspatial.hydro.watershed_dinf import watershed_dinf # noqa
109-
from xrspatial.hydro.watershed_mfd import watershed_mfd # noqa
102+
from xrspatial.hydro import basin # noqa: unified wrapper
103+
from xrspatial.hydro import basin_d8 # noqa
104+
from xrspatial.hydro import basins # noqa: backward-compat alias
105+
from xrspatial.hydro import basins_d8 # noqa
106+
from xrspatial.hydro import watershed # noqa: unified wrapper
107+
from xrspatial.hydro import watershed_d8, watershed_dinf, watershed_mfd # noqa
110108
from xrspatial.zonal import apply as zonal_apply # noqa
111109
from xrspatial.zonal import crop # noqa
112110
from xrspatial.zonal import trim # noqa

xrspatial/hydro/__init__.py

Lines changed: 133 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -3,35 +3,147 @@
33
Includes flow direction, flow accumulation, flow length, flow path,
44
watershed delineation, basin labeling, HAND, stream ordering, and
55
related utilities for D8, D-infinity, and MFD routing.
6+
7+
Each function family provides a unified wrapper that accepts a
8+
``routing`` parameter ('d8', 'dinf', or 'mfd') and dispatches to
9+
the corresponding implementation. The suffixed variants
10+
(e.g. ``flow_direction_d8``) are also importable directly.
611
"""
712

8-
from xrspatial.hydro.basin import basin # noqa
9-
from xrspatial.hydro.fill import fill # noqa
10-
from xrspatial.hydro.flow_accumulation import flow_accumulation # noqa
13+
# -- concrete D8 implementations ------------------------------------------
14+
from xrspatial.hydro.basin_d8 import basin_d8 # noqa
15+
from xrspatial.hydro.fill_d8 import fill_d8 # noqa
16+
from xrspatial.hydro.flow_accumulation_d8 import flow_accumulation_d8 # noqa
17+
from xrspatial.hydro.flow_direction_d8 import flow_direction_d8 # noqa
18+
from xrspatial.hydro.flow_length_d8 import flow_length_d8 # noqa
19+
from xrspatial.hydro.flow_path_d8 import flow_path_d8 # noqa
20+
from xrspatial.hydro.hand_d8 import hand_d8 # noqa
21+
from xrspatial.hydro.sink_d8 import sink_d8 # noqa
22+
from xrspatial.hydro.snap_pour_point_d8 import snap_pour_point_d8 # noqa
23+
from xrspatial.hydro.stream_link_d8 import stream_link_d8 # noqa
24+
from xrspatial.hydro.stream_order_d8 import stream_order_d8 # noqa
25+
from xrspatial.hydro.twi_d8 import twi_d8 # noqa
26+
from xrspatial.hydro.watershed_d8 import basins_d8 # noqa
27+
from xrspatial.hydro.watershed_d8 import watershed_d8 # noqa
28+
29+
# -- concrete D-infinity implementations -----------------------------------
1130
from xrspatial.hydro.flow_accumulation_dinf import flow_accumulation_dinf # noqa
12-
from xrspatial.hydro.flow_accumulation_mfd import flow_accumulation_mfd # noqa
13-
from xrspatial.hydro.flow_direction import flow_direction # noqa
1431
from xrspatial.hydro.flow_direction_dinf import flow_direction_dinf # noqa
15-
from xrspatial.hydro.flow_direction_mfd import flow_direction_mfd # noqa
16-
from xrspatial.hydro.flow_length import flow_length # noqa
1732
from xrspatial.hydro.flow_length_dinf import flow_length_dinf # noqa
18-
from xrspatial.hydro.flow_length_mfd import flow_length_mfd # noqa
19-
from xrspatial.hydro.flow_path import flow_path # noqa
2033
from xrspatial.hydro.flow_path_dinf import flow_path_dinf # noqa
21-
from xrspatial.hydro.flow_path_mfd import flow_path_mfd # noqa
22-
from xrspatial.hydro.hand import hand # noqa
2334
from xrspatial.hydro.hand_dinf import hand_dinf # noqa
24-
from xrspatial.hydro.hand_mfd import hand_mfd # noqa
25-
from xrspatial.hydro.sink import sink # noqa
26-
from xrspatial.hydro.snap_pour_point import snap_pour_point # noqa
27-
from xrspatial.hydro.stream_link import stream_link # noqa
2835
from xrspatial.hydro.stream_link_dinf import stream_link_dinf # noqa
29-
from xrspatial.hydro.stream_link_mfd import stream_link_mfd # noqa
30-
from xrspatial.hydro.stream_order import stream_order # noqa
3136
from xrspatial.hydro.stream_order_dinf import stream_order_dinf # noqa
32-
from xrspatial.hydro.stream_order_mfd import stream_order_mfd # noqa
33-
from xrspatial.hydro.twi import twi # noqa
34-
from xrspatial.hydro.watershed import basins # noqa
35-
from xrspatial.hydro.watershed import watershed # noqa
3637
from xrspatial.hydro.watershed_dinf import watershed_dinf # noqa
38+
39+
# -- concrete MFD implementations -----------------------------------------
40+
from xrspatial.hydro.flow_accumulation_mfd import flow_accumulation_mfd # noqa
41+
from xrspatial.hydro.flow_direction_mfd import flow_direction_mfd # noqa
42+
from xrspatial.hydro.flow_length_mfd import flow_length_mfd # noqa
43+
from xrspatial.hydro.flow_path_mfd import flow_path_mfd # noqa
44+
from xrspatial.hydro.hand_mfd import hand_mfd # noqa
45+
from xrspatial.hydro.stream_link_mfd import stream_link_mfd # noqa
46+
from xrspatial.hydro.stream_order_mfd import stream_order_mfd # noqa
3747
from xrspatial.hydro.watershed_mfd import watershed_mfd # noqa
48+
49+
50+
# =========================================================================
51+
# Routing dispatch
52+
# =========================================================================
53+
54+
class _RoutingDispatch:
55+
"""Map routing algorithm names to concrete implementations.
56+
57+
Inspired by ArrayTypeFunctionMapping but keyed on a string
58+
(``'d8'``, ``'dinf'``, ``'mfd'``) rather than array type.
59+
"""
60+
61+
__slots__ = ('_name', '_impls')
62+
63+
def __init__(self, name, **impls):
64+
self._name = name
65+
self._impls = impls
66+
67+
def __call__(self, *args, routing='d8', **kwargs):
68+
try:
69+
fn = self._impls[routing]
70+
except KeyError:
71+
opts = ', '.join(repr(k) for k in self._impls)
72+
raise ValueError(
73+
f"Unknown routing {routing!r} for {self._name}; "
74+
f"expected one of {opts}"
75+
) from None
76+
return fn(*args, **kwargs)
77+
78+
79+
# -- 8 families with d8 / dinf / mfd variants ----------------------------
80+
81+
flow_direction = _RoutingDispatch(
82+
'flow_direction',
83+
d8=flow_direction_d8, dinf=flow_direction_dinf, mfd=flow_direction_mfd,
84+
)
85+
86+
flow_accumulation = _RoutingDispatch(
87+
'flow_accumulation',
88+
d8=flow_accumulation_d8, dinf=flow_accumulation_dinf,
89+
mfd=flow_accumulation_mfd,
90+
)
91+
92+
flow_length = _RoutingDispatch(
93+
'flow_length',
94+
d8=flow_length_d8, dinf=flow_length_dinf, mfd=flow_length_mfd,
95+
)
96+
97+
flow_path = _RoutingDispatch(
98+
'flow_path',
99+
d8=flow_path_d8, dinf=flow_path_dinf, mfd=flow_path_mfd,
100+
)
101+
102+
watershed = _RoutingDispatch(
103+
'watershed',
104+
d8=watershed_d8, dinf=watershed_dinf, mfd=watershed_mfd,
105+
)
106+
107+
hand = _RoutingDispatch(
108+
'hand',
109+
d8=hand_d8, dinf=hand_dinf, mfd=hand_mfd,
110+
)
111+
112+
stream_link = _RoutingDispatch(
113+
'stream_link',
114+
d8=stream_link_d8, dinf=stream_link_dinf, mfd=stream_link_mfd,
115+
)
116+
117+
118+
# stream_order needs special handling: the ordering param (strahler/shreve)
119+
# is called `ordering` in d8 and `method` in dinf/mfd.
120+
121+
class _StreamOrderDispatch(_RoutingDispatch):
122+
def __call__(self, *args, routing='d8', ordering='strahler', **kwargs):
123+
try:
124+
fn = self._impls[routing]
125+
except KeyError:
126+
opts = ', '.join(repr(k) for k in self._impls)
127+
raise ValueError(
128+
f"Unknown routing {routing!r} for {self._name}; "
129+
f"expected one of {opts}"
130+
) from None
131+
if routing == 'd8':
132+
return fn(*args, ordering=ordering, **kwargs)
133+
return fn(*args, method=ordering, **kwargs)
134+
135+
136+
stream_order = _StreamOrderDispatch(
137+
'stream_order',
138+
d8=stream_order_d8, dinf=stream_order_dinf, mfd=stream_order_mfd,
139+
)
140+
141+
142+
# -- 5 D8-only functions (future-proofed with routing param) --------------
143+
144+
basin = _RoutingDispatch('basin', d8=basin_d8)
145+
basins = _RoutingDispatch('basins', d8=basins_d8)
146+
sink = _RoutingDispatch('sink', d8=sink_d8)
147+
snap_pour_point = _RoutingDispatch('snap_pour_point', d8=snap_pour_point_d8)
148+
fill = _RoutingDispatch('fill', d8=fill_d8)
149+
twi = _RoutingDispatch('twi', d8=twi_d8)
Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -272,7 +272,7 @@ def _basins_dask_iterative(flow_dir_da):
272272
Constructs basin pour_points lazily, then delegates to the
273273
watershed dask infrastructure.
274274
"""
275-
from xrspatial.hydro.watershed import _watershed_dask_iterative
275+
from xrspatial.hydro.watershed_d8 import _watershed_dask_iterative
276276

277277
chunks_y = flow_dir_da.chunks[0]
278278
chunks_x = flow_dir_da.chunks[1]
@@ -300,7 +300,7 @@ def _basins_make_pp_block(flow_dir_block, block_info=None):
300300
def _basins_dask_cupy(flow_dir_da):
301301
"""Dask+CuPy basins: native GPU via watershed infrastructure."""
302302
import cupy as cp
303-
from xrspatial.hydro.watershed import _watershed_dask_cupy
303+
from xrspatial.hydro.watershed_d8 import _watershed_dask_cupy
304304

305305
chunks_y = flow_dir_da.chunks[0]
306306
chunks_x = flow_dir_da.chunks[1]
@@ -332,7 +332,7 @@ def _basins_make_pp_block(flow_dir_block, block_info=None):
332332
# =====================================================================
333333

334334
@supports_dataset
335-
def basin(flow_dir: xr.DataArray,
335+
def basin_d8(flow_dir: xr.DataArray,
336336
name: str = 'basin') -> xr.DataArray:
337337
"""Delineate drainage basins: every cell labeled with its outlet ID.
338338
@@ -360,7 +360,7 @@ def basin(flow_dir: xr.DataArray,
360360
data = flow_dir.data
361361

362362
if isinstance(data, np.ndarray):
363-
from xrspatial.hydro.watershed import _watershed_cpu
363+
from xrspatial.hydro.watershed_d8 import _watershed_cpu
364364
fd = data.astype(np.float64)
365365
h, w = fd.shape
366366
labels = _basins_init_labels(fd, h, w, h, w, 0, 0)
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -449,7 +449,7 @@ def _fill_dask_cupy(dem_data):
449449
# =====================================================================
450450

451451
@supports_dataset
452-
def fill(dem: xr.DataArray,
452+
def fill_d8(dem: xr.DataArray,
453453
z_limit=None,
454454
name: str = 'fill') -> xr.DataArray:
455455
"""Fill depressions in a DEM using Planchon-Darboux iterative flooding.
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -881,7 +881,7 @@ def _flow_accum_dask_cupy(flow_dir_da):
881881
# =====================================================================
882882

883883
@supports_dataset
884-
def flow_accumulation(flow_dir: xr.DataArray,
884+
def flow_accumulation_d8(flow_dir: xr.DataArray,
885885
name: str = 'flow_accumulation') -> xr.DataArray:
886886
"""Compute flow accumulation from a D8 flow direction grid.
887887

xrspatial/hydro/flow_accumulation_dinf.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ class cupy: # type: ignore[no-redef]
3939
)
4040
from xrspatial.hydro._boundary_store import BoundaryStore
4141
from xrspatial.dataset_support import supports_dataset
42-
from xrspatial.hydro.flow_accumulation import (
42+
from xrspatial.hydro.flow_accumulation_d8 import (
4343
_find_ready_and_finalize,
4444
_preprocess_tiles,
4545
)
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -266,7 +266,7 @@ def _run_dask_cupy(data: da.Array,
266266
# =====================================================================
267267

268268
@supports_dataset
269-
def flow_direction(agg: xr.DataArray,
269+
def flow_direction_d8(agg: xr.DataArray,
270270
name: str = 'flow_direction',
271271
boundary: str = 'nan') -> xr.DataArray:
272272
"""Compute D8 flow direction for each cell.

0 commit comments

Comments
 (0)