Skip to content

viewshed() returns wrong results for single-row or single-column rasters (divide-by-zero in resolution) #2744

@brendancol

Description

@brendancol

Describe the bug

viewshed() on a single-row or single-column raster silently returns wrong results. The CPU sweep helper _viewshed_cpu computes the grid resolution as:

ew_res = (x_range[1] - x_range[0]) / (width - 1)
ns_res = (y_range[1] - y_range[0]) / (height - 1)

When width == 1 or height == 1, the denominator is zero. With float coordinates this gives NaN (a divide-by-zero RuntimeWarning, not an exception). That NaN resolution then flows through every distance and gradient calculation, so every non-observer cell comes back INVISIBLE, even on flat terrain where they should all be visible. Nothing raises, so the caller gets a plausible-looking but wrong array.

The sibling helpers _viewshed_windowed and _viewshed_dask already guard this with if width > 1 else 1.0 / if height > 1 else 1.0. _viewshed_cpu is missing the same guard. And since _viewshed_windowed routes a degenerate window back through _viewshed_cpu, the max_distance path is affected too.

Reproduce

import numpy as np, xarray as xr
from xrspatial import viewshed
arr = np.zeros((1, 5))
r = xr.DataArray(arr, coords=dict(x=np.arange(5.0), y=np.arange(1.0)), dims=["y", "x"])
print(viewshed(r, x=2.0, y=0.0, observer_elev=5).values)
# [[ -1.  -1. 180.  -1.  -1.]]  -- every cell but the observer is INVISIBLE

Expected behavior

On flat terrain with an elevated observer, all cells should be visible. The fix is to fall back to unit resolution when an axis has length 1, matching what the windowed and dask helpers already do.

Additional context

Found during a robustness sweep of the viewshed module. Silent wrong output in an analysis function, so worth treating as high priority.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions