Guard degenerate-axis resolution in viewshed CPU sweep#2745
Open
brendancol wants to merge 1 commit into
Open
Conversation
_viewshed_cpu computed ew_res/ns_res as range/(dim-1), which is 0/0 = NaN for a single-row or single-column raster. The NaN propagated through all distance/gradient math, silently marking every non-observer cell INVISIBLE on flat terrain. Fall back to unit resolution for a degenerate axis, matching _viewshed_windowed and _viewshed_dask. The max_distance window path routes a degenerate window back through _viewshed_cpu, so it is fixed too. Adds regression tests for 1xN and Nx1 rasters on both the full and max_distance paths.
brendancol
commented
May 30, 2026
Contributor
Author
brendancol
left a comment
There was a problem hiding this comment.
PR Review: Guard degenerate-axis resolution in viewshed CPU sweep
Blockers
None.
Suggestions
None.
Nits
None.
What looks good
- The fix matches the guard already used in
_viewshed_windowed(viewshed.py:2078-2079) and_viewshed_dask(viewshed.py:2180-2181), so the three CPU-resolution computations now agree. - Falling back to
1.0for a length-1 axis is the right call: a single row has no defined y-resolution, and unit resolution keeps the distance/gradient geometry well-defined. - Tests cover both orientations (1xN and Nx1) and both code paths (full sweep and
max_distancewindowing, which delegates back to_viewshed_cpu). The assertions check the real failure mode: observer cell at 180, all other cells visible, and no NaN in the output. - I checked the 1x1 case by hand (both axes degenerate): it returns
[180.]without error, so the two guards compose correctly.
Checklist
- Algorithm matches reference/convention (mirrors sibling helpers)
- NaN handling is correct (the fix removes the NaN source)
- Edge cases covered by tests (1xN, Nx1; 1x1 verified manually)
- No premature materialization or unnecessary copies
- Benchmark not needed (bug fix, no perf change)
- README feature matrix not applicable (no API change)
- Docstrings unchanged and still accurate
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #2744
_viewshed_cpucomputed grid resolution asrange / (dim - 1). For a single-row or single-column raster that is0 / 0 = NaN, which then flowed through every distance and gradient calculation and silently marked every non-observer cell INVISIBLE on flat terrain (no error raised)._viewshed_windowedand_viewshed_dask.max_distancepath routes a degenerate window back through_viewshed_cpu, so it is covered by the same fix.Backend coverage: numpy (direct fix). The dask and cupy/RTX entry points already guarded this, so their behavior is unchanged; the windowed path that delegates to the CPU helper is now correct on degenerate inputs.
Test plan:
test_viewshed_single_row_or_column(1x5 and 5x1) — observer cell is 180, all cells visible, no NaNtest_viewshed_single_row_or_column_max_distance(1x7 and 7x1) — windowed path correcttest_viewshed.pysuite passes (29 passed)