Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion docs/source/reference/hydrology.rst
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,6 @@ Routing variants:
xrspatial.hydro.watershed_d8.watershed_d8
xrspatial.hydro.watershed_dinf.watershed_dinf
xrspatial.hydro.watershed_mfd.watershed_mfd
xrspatial.hydro.watershed_d8.basins_d8

Stream Link
===========
Expand Down
2 changes: 1 addition & 1 deletion examples/user_guide/3_Zonal.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -427,7 +427,7 @@
},
"outputs": [],
"source": [
"cropped = terrain.xrs.crop(zones_agg, zones_ids=[13, 14])\n",
"cropped = terrain.xrs.crop(zones_agg, zone_ids=[13, 14])\n",
"\n",
"# Show full terrain with the crop boundary, then the cropped result\n",
"crop_zones = zones_agg.where(zones_agg.isin([13, 14]))\n",
Expand Down
1 change: 0 additions & 1 deletion xrspatial/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,6 @@
from xrspatial.visibility import line_of_sight # noqa
from xrspatial.visibility import visibility_frequency # noqa
from xrspatial.hydro import basin # noqa: unified wrapper
from xrspatial.hydro import basins # noqa: backward-compat alias
from xrspatial.hydro import watershed # noqa: unified wrapper
from xrspatial.zonal import apply as zonal_apply # noqa
from xrspatial.zonal import crop # noqa
Expand Down
13 changes: 2 additions & 11 deletions xrspatial/accessor.py
Original file line number Diff line number Diff line change
Expand Up @@ -1063,10 +1063,6 @@ def basin(self, **kwargs):
from .hydro import basin
return basin(self._obj, **kwargs)

def basins(self, **kwargs):
from .hydro import basins
return basins(self._obj, **kwargs)

def sink(self, **kwargs):
from .hydro import sink
return sink(self._obj, **kwargs)
Expand Down Expand Up @@ -1305,9 +1301,9 @@ def zonal_hypsometric_integral(self, zones, **kwargs):
from .zonal import hypsometric_integral
return hypsometric_integral(zones, self._obj, **kwargs)

def crop(self, zones, zones_ids, **kwargs):
def crop(self, zones, zone_ids, **kwargs):
from .zonal import crop
return crop(zones, self._obj, zones_ids, **kwargs)
return crop(zones, self._obj, zone_ids, **kwargs)

def clip_polygon(self, geometry, **kwargs):
from .polygon_clip import clip_polygon
Expand Down Expand Up @@ -1863,10 +1859,6 @@ def basin(self, **kwargs):
from .hydro import basin
return basin(self._obj, **kwargs)

def basins(self, **kwargs):
from .hydro import basins
return basins(self._obj, **kwargs)

def sink(self, **kwargs):
from .hydro import sink
return sink(self._obj, **kwargs)
Expand Down Expand Up @@ -2362,7 +2354,6 @@ def validate(self, *, raise_on_error=False):
'flow_direction': 'flow_direction_d8',
'flow_accumulation': 'flow_accumulation_d8',
'basin': 'basin_d8',
'basins': 'basins_d8',
'watershed': 'watershed_d8',
'snap_pour_point': 'snap_pour_point_d8',
'flow_path': 'flow_path_d8',
Expand Down
29 changes: 0 additions & 29 deletions xrspatial/classify.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
from __future__ import annotations

import cmath
import functools
import warnings
from functools import partial
from typing import List, Optional
Expand Down Expand Up @@ -803,35 +802,7 @@ def _run_dask_cupy_natural_break(agg, num_sample, k):
return out


def _natural_breaks_legacy_order(func):
"""Bridge the pre-1.0 ``natural_breaks`` parameter order.

The old signature was ``(agg, num_sample, name, k)``. The new order is
``(agg, k, num_sample, name)`` to match ``quantile`` and
``maximum_breaks``. Legacy callers always passed ``k`` as a keyword
(it was the last parameter), so a call that supplies ``k=`` together
with a second positional argument is using the old order: that
positional is the old ``num_sample``. Remap it and warn.
"""
@functools.wraps(func)
def wrapper(agg, *args, **kwargs):
if 'k' in kwargs and len(args) >= 1 and 'num_sample' not in kwargs:
warnings.warn(
"natural_breaks parameter order changed to "
"(agg, k, num_sample, name) to match the other "
"classifiers. Passing num_sample positionally is "
"deprecated; pass num_sample=... instead.",
DeprecationWarning,
stacklevel=2,
)
kwargs['num_sample'] = args[0]
args = args[1:]
return func(agg, *args, **kwargs)
return wrapper


@supports_dataset
@_natural_breaks_legacy_order
def natural_breaks(agg: xr.DataArray,
k: int = 5,
num_sample: Optional[int] = 20000,
Expand Down
48 changes: 3 additions & 45 deletions xrspatial/focal.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import copy
import math
import warnings
from functools import partial
from math import isnan

Expand Down Expand Up @@ -152,36 +151,6 @@ def _apply_per_band(band_func, agg, *args, **kwargs):
return xr.concat(slices, dim=band_dim)


def _resolve_raster_alias(agg, raster, func_name):
"""Support the deprecated ``raster=`` keyword for the first argument.

The focal public functions standardised their first parameter on
``agg`` to match the rest of the library. ``apply`` and ``hotspots``
historically named it ``raster``; this shim keeps that keyword working
while emitting a ``DeprecationWarning``.
"""
if raster is not None:
if agg is not None:
raise TypeError(
f"{func_name}() got both 'agg' and the deprecated 'raster' "
f"argument; pass the input as 'agg' only."
)
warnings.warn(
f"{func_name}(): the 'raster' argument is deprecated and will be "
f"removed in a future release; use 'agg' instead.",
DeprecationWarning,
# stacklevel=3 points the warning at the caller:
# caller -> public func (apply/hotspots) -> this helper.
stacklevel=3,
)
return raster
if agg is None:
raise TypeError(
f"{func_name}() missing required argument: 'agg'"
)
return agg


# TODO: Make convolution more generic with numba first-class functions.


Expand Down Expand Up @@ -626,8 +595,8 @@ def _apply_dask_cupy(data, kernel, func, boundary='nan'):
return out


def apply(agg=None, kernel=None, func=None, name='focal_apply',
boundary='nan', *, raster=None):
def apply(agg, kernel=None, func=None, name='focal_apply',
boundary='nan'):
"""
Returns custom function applied array using a user-created window.

Expand Down Expand Up @@ -666,9 +635,6 @@ def apply(agg=None, kernel=None, func=None, name='focal_apply',
agg : xarray.DataArray of same type as `agg`
2D aggregate array of filtered values.

.. deprecated::
The ``raster`` keyword argument is deprecated; use ``agg``.

Examples
--------
Focal apply works with NumPy backed xarray DataArray
Expand Down Expand Up @@ -753,8 +719,6 @@ def apply(agg=None, kernel=None, func=None, name='focal_apply',
[2. , 2. , 2. , 1.5]])
Dimensions without coordinates: y, x
"""
agg = _resolve_raster_alias(agg, raster, 'apply')

_validate_raster(agg, func_name='apply', name='agg', ndim=(2, 3))

if func is None:
Expand Down Expand Up @@ -1735,8 +1699,7 @@ def _hotspots_cupy(raster, kernel, boundary='nan'):
return out


def hotspots(agg=None, kernel=None, name='hotspots', boundary='nan', *,
raster=None):
def hotspots(agg, kernel=None, name='hotspots', boundary='nan'):
"""
Identify statistically significant hot spots and cold spots in an
input raster using the Getis-Ord Gi* statistic. To be a
Expand Down Expand Up @@ -1791,9 +1754,6 @@ def hotspots(agg=None, kernel=None, name='hotspots', boundary='nan', *,
hotspots_agg : xarray.DataArray of same type as `agg`
2D array of hotspots with values indicating confidence level.

.. deprecated::
The ``raster`` keyword argument is deprecated; use ``agg``.

Examples
--------
.. sourcecode:: python
Expand All @@ -1816,8 +1776,6 @@ def hotspots(agg=None, kernel=None, name='hotspots', boundary='nan', *,
Dimensions without coordinates: dim_0, dim_1
"""

agg = _resolve_raster_alias(agg, raster, 'hotspots')

_validate_raster(agg, func_name='hotspots', name='agg', ndim=(2, 3))

if agg.ndim == 3:
Expand Down
6 changes: 0 additions & 6 deletions xrspatial/hydro/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
from xrspatial.hydro.stream_link_d8 import stream_link_d8 # noqa
from xrspatial.hydro.stream_order_d8 import stream_order_d8 # noqa
from xrspatial.hydro.twi_d8 import twi_d8 # noqa
from xrspatial.hydro.watershed_d8 import basins_d8 # noqa
from xrspatial.hydro.watershed_d8 import watershed_d8 # noqa

# -- concrete D-infinity implementations -----------------------------------
Expand Down Expand Up @@ -165,11 +164,6 @@ def __call__(self, *args, routing='d8', ordering='strahler', **kwargs):
summary="Label each cell with the outlet of the basin it drains to.",
d8=basin_d8,
)
basins = _RoutingDispatch(
'basins',
summary="Deprecated alias for :func:`basin`.",
d8=basins_d8,
)
sink = _RoutingDispatch(
'sink',
summary="Label depression cells that have no downstream neighbor.",
Expand Down
4 changes: 2 additions & 2 deletions xrspatial/hydro/tests/test_routing_public_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
WRAPPERS = [
'flow_direction', 'flow_accumulation', 'flow_length', 'flow_path',
'watershed', 'hand', 'stream_link', 'stream_order',
'basin', 'basins', 'sink', 'snap_pour_point', 'fill', 'twi',
'basin', 'sink', 'snap_pour_point', 'fill', 'twi',
]

# Representative suffixed implementations across every family.
Expand All @@ -26,7 +26,7 @@
'flow_accumulation_d8', 'flow_accumulation_dinf', 'flow_accumulation_mfd',
'flow_length_d8', 'flow_path_mfd', 'watershed_dinf', 'hand_mfd',
'stream_link_d8', 'stream_order_dinf',
'basin_d8', 'basins_d8', 'sink_d8', 'snap_pour_point_d8', 'fill_d8',
'basin_d8', 'sink_d8', 'snap_pour_point_d8', 'fill_d8',
'twi_d8',
]

Expand Down
40 changes: 8 additions & 32 deletions xrspatial/hydro/tests/test_watershed_d8.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import pytest
import xarray as xr

from xrspatial.hydro import watershed, basins
from xrspatial.hydro import watershed
from xrspatial.tests.general_checks import (
create_test_raster,
cuda_and_cupy_available,
Expand Down Expand Up @@ -165,39 +165,15 @@ def test_multiple_paths_to_same_pour_point():


# ====================================================================
# Backward-compat: basins() still works via wrapper
# The ``basins`` / ``basins_d8`` aliases were removed in 1.0.0
# ====================================================================

def test_basins_backward_compat():
"""basins() wrapper delegates to basin() correctly."""
from xrspatial.hydro import basin

flow_dir = np.array([
[2.0, 4.0, 8.0],
[1.0, 0.0, 16.0],
[128.0, 64.0, 32.0],
], dtype=np.float64)
fd_da = create_test_raster(flow_dir)
old_result = basins(fd_da)
new_result = basin(fd_da)
np.testing.assert_allclose(old_result.data, new_result.data, equal_nan=True)


def test_basins_d8_emits_deprecation_warning():
"""basins_d8 wrapper warns and still returns the basin result."""
from xrspatial.hydro.basin_d8 import basin_d8
from xrspatial.hydro.watershed_d8 import basins_d8

flow_dir = np.array([
[2.0, 4.0, 8.0],
[1.0, 0.0, 16.0],
[128.0, 64.0, 32.0],
], dtype=np.float64)
fd_da = create_test_raster(flow_dir)
with pytest.warns(DeprecationWarning, match="basins_d8 is deprecated"):
old_result = basins_d8(fd_da)
new_result = basin_d8(fd_da)
np.testing.assert_allclose(old_result.data, new_result.data, equal_nan=True)
def test_basins_alias_removed():
import xrspatial
from xrspatial.hydro import watershed_d8
assert not hasattr(xrspatial, 'basins')
assert not hasattr(xrspatial.hydro, 'basins')
assert not hasattr(watershed_d8, 'basins_d8')


# ====================================================================
Expand Down
14 changes: 1 addition & 13 deletions xrspatial/hydro/watershed_d8.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
Two complementary functions:
- ``watershed(flow_dir, pour_points)`` — labels each cell with the
pour point it drains to; cells not reaching any pour point → NaN.
- ``basins(flow_dir)`` — automatically identifies all outlets (pits +
- ``basin(flow_dir)`` — automatically identifies all outlets (pits +
edge-exit cells) and labels every valid cell; no pour points needed.

Both use **downstream tracing with path compression** on CPU — follow
Expand Down Expand Up @@ -1070,15 +1070,3 @@ def watershed_d8(flow_dir: xr.DataArray,
coords=flow_dir.coords,
dims=flow_dir.dims,
attrs=flow_dir.attrs)


def basins_d8(flow_dir, name='basins'):
"""Backward-compatible wrapper; use :func:`basin` instead."""
import warnings
warnings.warn(
"basins_d8 is deprecated; use basin (basin_d8) instead.",
DeprecationWarning,
stacklevel=2,
)
from .basin_d8 import basin_d8
return basin_d8(flow_dir, name=name)
19 changes: 18 additions & 1 deletion xrspatial/tests/test_accessor.py
Original file line number Diff line number Diff line change
Expand Up @@ -579,7 +579,7 @@ def test_hydro_accessor_matches_direct(method_name, elevation):

_ALL_HYDRO_METHODS = [
'flow_direction', 'flow_direction_dinf', 'flow_direction_mfd',
'flow_accumulation', 'flow_accumulation_mfd', 'watershed', 'basin', 'basins',
'flow_accumulation', 'flow_accumulation_mfd', 'watershed', 'basin',
'sink', 'fill', 'stream_order', 'stream_link', 'snap_pour_point',
'flow_path', 'flow_length', 'twi', 'hand',
]
Expand Down Expand Up @@ -865,3 +865,20 @@ def test_coregister_raster_preserves_dask_backend():
out = grid.xrs.coregister(src)
assert isinstance(out.data, da.Array)
assert out.shape == grid.shape


def test_accessor_crop_uses_zone_ids_keyword():
"""The accessor's crop() takes zone_ids like the function; zones_ids is gone (#3762)."""
import pytest
from xrspatial.zonal import crop

arr = np.array([[0, 4, 0, 3],
[0, 4, 4, 3],
[0, 1, 1, 3],
[0, 0, 0, 0]], dtype=np.int64)
raster = xr.DataArray(arr, dims=('y', 'x'))
via_accessor = raster.xrs.crop(raster, zone_ids=(1, 3))
direct = crop(raster, raster, zone_ids=(1, 3))
xr.testing.assert_identical(via_accessor, direct)
with pytest.raises(TypeError, match="zone_ids"):
raster.xrs.crop(raster, zones_ids=(1, 3))
10 changes: 4 additions & 6 deletions xrspatial/tests/test_classify.py
Original file line number Diff line number Diff line change
Expand Up @@ -1109,13 +1109,11 @@ def test_natural_breaks_positional_k_matches_siblings():
np.testing.assert_array_equal(positional.data, keyword.data)


def test_natural_breaks_legacy_positional_num_sample_warns():
"""Legacy (agg, num_sample, k=...) order warns and still maps correctly."""
def test_natural_breaks_legacy_positional_num_sample_removed():
"""The pre-1.0 (agg, num_sample, k=...) remap is gone (#3762)."""
agg = input_data()
with pytest.warns(DeprecationWarning):
legacy = natural_breaks(agg, 20000, k=3)
new = natural_breaks(agg, k=3, num_sample=20000)
np.testing.assert_array_equal(legacy.data, new.data)
with pytest.raises(TypeError, match="k"):
natural_breaks(agg, 20000, k=3)


# ===================================================================
Expand Down
Loading
Loading