rasterize: support descending-x in like= templates (#2568)#2595
Merged
Conversation
The rasterizer always burns with column 0 = xmin (image convention). When the template's x axis is descending (high-to-low), the burned array now flips along axis 1 before assigning like.x, so result.sel(x=...) lines up with the geometry in world coordinates. Mirrors the existing ascending-y flip on axis 0. Previously descending-x silently mislabelled coords: a polygon at world x=0.5 ended up under coord x=3.5. No exception was raised. Tests cover numpy, dask+numpy, cupy, and dask+cupy.
brendancol
commented
May 28, 2026
Contributor
Author
brendancol
left a comment
There was a problem hiding this comment.
PR Review: rasterize: support descending-x in like= templates (#2568)
Blockers (must fix before merge)
None.
Suggestions (should fix, not blocking)
None.
Nits (optional improvements)
xrspatial/tests/test_rasterize_descending_x_2568.py:42:_like_2568has ay_descending=Trueparameter that only gets flipped in one test (test_numpy_descending_x_and_ascending_y). The asymmetry is harmless. If you want fewer one-off helpers later, this could fold into a parameterized test pair.xrspatial/rasterize.py:2940:x_descending = width > 1 and float(x[-1]) < float(x[0])mirrors the y case. Both correctly treat width/height == 1 as "no flip needed," but a comment noting that single-column templates short-circuit would help the next reader.
What looks good
- The fix is symmetric with the existing y-axis treatment: same comment shape, same
_LikeGridfield pattern, same plumbing throughrasterize, same slicing flip. - The
reuse_like_coordsgate is unchanged, so the flip only runs in the path where it matters (template grid reused bit-identically). When the caller passes explicit bounds/width/height/resolution, the linspace-rebuild path produces ascending coords as before.test_numpy_explicit_bounds_skips_flipcovers that. - Slicing (
out[:, ::-1]) works the same way for numpy, dask, cupy, and dask+cupy, so no per-backend code is needed. - Test matrix hits all four backends, plus polygon/point/line geometry types, plus xr.align round-trip, plus the combined ascending-y + descending-x case.
Checklist
- Algorithm matches reference/paper (n/a, bug fix to existing internal logic)
- All implemented backends produce consistent results (covered by tests)
- NaN handling is correct (unchanged path; fill semantics preserved)
- Edge cases are covered by tests
- Dask chunk boundaries handled correctly (flip is a view, no chunk impact)
- No premature materialization or unnecessary copies (slice view, lazy under dask)
- Benchmark exists or is not needed (n/a for a coord-orientation fix)
- README feature matrix updated (n/a, no new function)
- Docstrings present and accurate (no new public API; existing docstrings unaffected)
brendancol
commented
May 28, 2026
Contributor
Author
brendancol
left a comment
There was a problem hiding this comment.
Follow-up review (after 2f5c8ce)
Disposition of the previous nits
- Nit 1 (parameterize
_like_2568y_descending): dismissed. The current helper lets one test cover both-axes-flipped without bloating every other test signature. Folding it into a fixture would be more code for the same coverage. - Nit 2 (single-column short-circuit comment): fixed in 2f5c8ce. The detection comment now spells out that
width/height > 1is the short-circuit guard.
New findings
None. Follow-up is comment-only.
# Conflicts: # CHANGELOG.md
# Conflicts: # CHANGELOG.md
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 #2568.
Summary
rasterize(like=...)silently mislabelled output coords when the template's x axis was descending. The burner writes column 0 = xmin (image convention), butreuse_like_coordsassigned the descending x-coord unchanged, so a polygon at world x=0.5 ended up under coord x=3.5._extract_grid_from_like, carry it on_LikeGrid, and flip the burned array along axis 1 inrasterizewhen reusing coords. Same shape as the existing ascending-y axis-0 flip.Why "support" rather than "reject"
The y-axis already supports both ascending and descending transparently via the axis-0 flip (added for #2170). Treating x asymmetrically would be surprising for users who get a descending-x
likefrom a pipeline that flipped a raster (e.g. negative-stride affine transforms). The fix is small and local, and it matches the existing pattern. Rejecting withValueErrorwas the other reasonable option; this PR picks the symmetric one.Descending y was investigated while in the area and is already correctly handled (it is the default image convention -- the burner emits row 0 = ymax natively).
Test plan
xrspatial/tests/test_rasterize_descending_x_2568.pycover polygons, points, lines, world-coord round-trip withxr.align, and both axes flipped together.xrspatial/tests/test_rasterize.py(215 tests) still passes.test_rasterize_accuracy.py,test_rasterize_coverage_*.py) still pass.