Description
The viewshed CPU, windowed, and dask paths all derive a single constant cell resolution from the coordinate endpoints:
ew_res = (x_range[1] - x_range[0]) / (width - 1) if width > 1 else 1.0
ns_res = (y_range[1] - y_range[0]) / (height - 1) if height > 1 else 1.0
This appears at xrspatial/viewshed.py:1568 (CPU), :2090 (windowed), and :2196 (dask). It assumes the x and y coordinates are uniformly spaced. If the coordinates are irregular (non-uniform spacing), the endpoint-derived resolution does not match the actual cell sizes, so distances and vertical angles come out physically wrong with no warning.
Proposed fix
The algorithm already requires a regular grid. Make that requirement explicit and enforced:
- Validate that the x and y coordinate arrays are monotonic and uniformly spaced (within a small tolerance) before the sweep runs.
- Raise a clear
ValueError that names which axis is irregular and the expected uniform spacing.
- Run the validation on all three dispatch paths via a shared helper.
- Document the regular-grid requirement in the
viewshed() docstring.
A full coordinate-aware rewrite of the sweep algorithm is out of scope here; if that is warranted it should be a separate design effort.
Description
The viewshed CPU, windowed, and dask paths all derive a single constant cell resolution from the coordinate endpoints:
This appears at
xrspatial/viewshed.py:1568(CPU),:2090(windowed), and:2196(dask). It assumes the x and y coordinates are uniformly spaced. If the coordinates are irregular (non-uniform spacing), the endpoint-derived resolution does not match the actual cell sizes, so distances and vertical angles come out physically wrong with no warning.Proposed fix
The algorithm already requires a regular grid. Make that requirement explicit and enforced:
ValueErrorthat names which axis is irregular and the expected uniform spacing.viewshed()docstring.A full coordinate-aware rewrite of the sweep algorithm is out of scope here; if that is warranted it should be a separate design effort.