Skip to content

Comments

Fixes #733: add cost_distance() for weighted proximity#859

Open
brendancol wants to merge 3 commits intomasterfrom
fixes-733-weighted-proximity-distance
Open

Fixes #733: add cost_distance() for weighted proximity#859
brendancol wants to merge 3 commits intomasterfrom
fixes-733-weighted-proximity-distance

Conversation

@brendancol
Copy link
Contributor

Summary

  • Adds new cost_distance() function that computes minimum accumulated traversal cost through a friction surface to reach the nearest target pixel (raster equivalent of GRASS r.cost / ArcGIS Cost Distance)
  • Uses multi-source Dijkstra with a numba-JIT binary min-heap for performance
  • Supports numpy and dask+numpy backends, 4/8-connectivity, max_cost truncation for dask scalability, target_values filtering, and xr.Dataset input via @supports_dataset

Algorithm

  1. Seed all source pixels at cost 0 in a priority queue
  2. Pop minimum-cost pixel, relax neighbours: edge_cost = geo_distance * avg_friction
  3. Repeat until queue empty or max_cost exceeded

Dask strategy

For finite max_cost, computes max_radius = max_cost / (f_min * cellsize) and uses this as depth for da.map_overlap — exact within the cost budget. Falls back to single-chunk for unbounded or very large radii.

Files changed

  • New: xrspatial/cost_distance.py — module with heap, Dijkstra kernel, numpy/dask wrappers, public API
  • New: xrspatial/tests/test_cost_distance.py — 19 tests (uniform friction, analytic case, barriers, multiple sources, max_cost truncation, dask/numpy equivalence, connectivity, validation errors)
  • New: examples/user_guide/9_Cost_Distance.ipynb — example notebook comparing cost_distance with proximity across 7 scenarios
  • Modified: xrspatial/__init__.py — export cost_distance

Test plan

  • pytest xrspatial/tests/test_cost_distance.py -v — 19/19 pass
  • pytest xrspatial/tests/test_proximity.py -v — 24/24 pass (no regressions)
  • Example notebook executes end-to-end without errors
  • Dask output matches numpy output exactly
  • max_cost truncation produces NaN beyond budget
  • Review memory profile on large raster with dask distributed dashboard

…ce Dijkstra

New cost_distance() function computes minimum accumulated traversal cost
through a friction surface to reach the nearest target pixel, the raster
equivalent of GRASS r.cost / ArcGIS Cost Distance. Supports numpy and
dask+numpy backends, 4/8-connectivity, max_cost truncation for dask
scalability, and xr.Dataset input via @supports_dataset. Includes 19
tests and an example notebook comparing cost_distance with proximity.
…rechunk

The existing dask proximity path rechunked the entire raster into one chunk
when max_distance was unbounded, defeating dask's out-of-core purpose. This
adds a two-phase scipy.spatial.cKDTree approach: Phase 1 streams chunks to
collect target coordinates, Phase 2 queries the tree per-chunk via map_blocks.
Exact results, memory proportional to targets not raster size. Supports
EUCLIDEAN and MANHATTAN metrics; GREAT_CIRCLE/ALLOCATION/DIRECTION fall back
to the existing single-chunk path. Gracefully degrades when scipy is absent.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant