rasterize: honor like.y orientation for ascending-y templates (#2170)#2181
Merged
Conversation
…plates (#2170) The rasterizer always burns row 0 = ymax (top-down image convention), but the output reused like.y verbatim. When like.y was ascending, result.sel(y=...) lined up against the wrong rows because the burned array still pointed the wrong way. Detect orientation once in _extract_grid_from_like, carry the flag through to the coord-assignment site, and flip the burned array on axis 0 right before assigning the template's coords. Works the same way for numpy, cupy, dask+numpy, and dask+cupy outputs. Closes #2170
brendancol
commented
May 20, 2026
Contributor
Author
brendancol
left a comment
There was a problem hiding this comment.
PR Review: rasterize honor like.y orientation for ascending-y templates (#2170)
Blockers
None.
Suggestions
xrspatial/rasterize.py:1968.y_ascending = height > 1 and float(y[-1]) > float(y[0])only looks at the first and last samples. Fine for a monotonic 1D coord, but a non-monotonic or duplicate-valuedlike.ywould be misclassified. The inline comment already defers the irregular-spacing case; a sentence noting monotonicity is assumed, or annp.all(np.diff(y) > 0)check, would make the contract explicit.xrspatial/rasterize.py:1978._extract_grid_from_likenow returns a 9-tuple and the call site unpacks positionally. ANamedTupleor small dataclass would scale better as future grid attributes get added. Out of scope for this fix.
Nits
xrspatial/rasterize.py:2298. Descending-x templates would hit the same bug class as descending-y did before this fix. A short comment near the y-flip noting "x is assumed ascending; descending-x templates are not supported" would document the asymmetry for the next reader.xrspatial/tests/test_rasterize.py:2244.test_numpy_explicit_bounds_skips_flipassertsresult.y.values[0] > result.y.values[-1]. Tightening to exact expected values (e.g.[3.5, 2.5, 1.5, 0.5]) would catch a regression where the bounds path produces an off-by-one coord centre.
What looks good
One detection site, one flip site, with comments at both explaining the image-vs-world convention. out[::-1, :] is backend-agnostic by construction. Tests hit all four backends and three geometry types, plus the xr.align round-trip and the explicit-bounds bypass. World-y selection asserts (result.sel(y=0.5, x=0.5)) target the bug directly rather than checking array indices. result.y is preserved byte-for-byte against like.y, which is what xr.align depends on. The height == 1 edge case falls out for free: the height > 1 guard makes y_ascending=False so the flip is a no-op.
- Convert _extract_grid_from_like to return a _LikeGrid NamedTuple instead of a 9-tuple. Positional unpacking was fragile. - Document the monotonicity assumption on the y_ascending detection and the same-class assumption for x being ascending. - Tighten test_numpy_explicit_bounds_skips_flip to lock the exact coord centres instead of only checking the order. Refs #2181.
brendancol
commented
May 20, 2026
Contributor
Author
brendancol
left a comment
There was a problem hiding this comment.
Follow-up review on 54fe792
All four findings from the first pass are addressed in the new commit.
Disposition
- Suggestion 1 (monotonicity contract). Fixed. The comment on
y_ascendingnow states that monotonicity is assumed and that the irregular-spacing case is out of scope. - Suggestion 2 (9-tuple unpacking). Fixed.
_extract_grid_from_likereturns a_LikeGridNamedTuple. The call site inrasterizeunpacks by attribute name. - Nit 1 (x-axis asymmetry). Fixed. The same comment block now flags the descending-x case as out of scope so the asymmetry is explicit.
- Nit 2 (test assertion tightness). Fixed.
test_numpy_explicit_bounds_skips_fliplocks the exact y/x coord centres.
Verification
pytest xrspatial/tests/test_rasterize.pypasses locally (199 passed, 2 skipped).
Remaining concerns
None.
# Conflicts: # xrspatial/rasterize.py # xrspatial/tests/test_rasterize.py
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.
Summary
like.yorientation once in_extract_grid_from_like().result.sel(y=...)lines up with the geometry whetherlike.yascends or descends.result.ystill equalslike.ybyte-for-byte, soxr.alignkeeps working.Closes #2170
Backend coverage
The flip is just
out[::-1, :], which behaves the same on numpy, cupy, dask+numpy, and dask+cupy arrays.Out of scope
There's a sibling issue for irregular
likegrids (non-uniform spacing). Not touched here.Test plan
TestLikeYOrientation2170inxrspatial/tests/test_rasterize.py, covering polygons, lines, and points across all four backends (GPU cases skip when CUDA isn't available).result.sel(y=0.5, x=0.5)returns the burned value for both ascending and descendinglike.y.result.y == like.yandxr.align(result, like)still works.test_rasterize.pypasses locally (199 passed, 2 skipped).