Skip to content

rasterize: drop stale transform/res from like.attrs when grid is overridden#2253

Merged
brendancol merged 2 commits into
mainfrom
deep-sweep-metadata-rasterize-2026-05-21
May 21, 2026
Merged

rasterize: drop stale transform/res from like.attrs when grid is overridden#2253
brendancol merged 2 commits into
mainfrom
deep-sweep-metadata-rasterize-2026-05-21

Conversation

@brendancol
Copy link
Copy Markdown
Contributor

Closes #2251

Summary

  • When rasterize(geoms, like=template, ...) is called with an explicit
    bounds, width/height, or resolution that reshapes the output
    relative to the template, the inherited attrs['res'] and
    attrs['transform'] describe the template's grid, not the actual
    output. get_dataarray_resolution prefers attrs['res'] over
    computing from coords, so downstream slope/aspect/proximity callers
    silently used the wrong cellsize.
  • Fix strips res / transform from the propagated like.attrs when
    the output grid no longer matches the template (the existing
    reuse_like_coords flag already captures exactly this condition).
    crs, spatial_ref, and the nodata triplet still propagate.
  • 9 new tests in TestLikeStaleGridAttrs2251 cover bounds /
    width-height / resolution overrides, matching-size keeps the attrs,
    get_dataarray_resolution consistency, and parity across numpy /
    cupy / dask+numpy / dask+cupy backends.

Test plan

  • New tests in TestLikeStaleGridAttrs2251 pass (9/9, all 4 backends)
  • Existing TestMetadataPropagation tests still pass (13/13)
  • Full test_rasterize.py suite passes (224 passed, 2 skipped)
  • Other rasterize tests pass (test_rasterize_accuracy,
    test_rasterize_all_touched_supercover_2169,
    test_rasterize_coverage_2026_05_17,
    test_rasterize_gpu_race_2167,
    test_rasterize_tile_props_slice_2020: 153/153)
  • Callers pass (test_zonal, test_polygon_clip, test_accessor:
    173/173)

Found by /deep-sweep metadata propagation audit (Cat 1, HIGH).

…ridden

Closes #2251

When rasterize(geoms, like=template, ...) is called with an explicit
bounds/width/height/resolution that reshapes the output relative to
the template, the inherited attrs['res'] and attrs['transform'] from
``like`` describe the template's grid rather than the actual output.
``get_dataarray_resolution`` prefers attrs['res'] over computing from
coords, so a stale res silently poisoned downstream slope/aspect/
proximity callers with the template's cellsize. Same class as the
sky_view_factor cellsize bug.

The fix strips res/transform from the propagated like.attrs when the
output grid no longer matches the template (the existing
reuse_like_coords flag already captures exactly this condition).
Crs, spatial_ref, and other non-grid-shape attrs still propagate.
When the output grid matches the template (no override), res and
transform stay so chained pipelines see the unchanged spatial
metadata.

9 new tests in TestLikeStaleGridAttrs2251 cover bounds / width-height /
resolution overrides, matching-size keeps the attrs, get_dataarray_
resolution consistency end-to-end, and parity across numpy / cupy /
dask+numpy / dask+cupy backends.

State CSV updated to record the re-audit on 2026-05-21.
@github-actions github-actions Bot added the performance PR touches performance-sensitive code label May 21, 2026
Copy link
Copy Markdown
Contributor Author

@brendancol brendancol left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR Review: rasterize -- drop stale transform/res from like.attrs when grid is overridden

Summary

I read through the fix in xrspatial/rasterize.py lines 3216-3227. The guard if like_attrs is not None and not reuse_like_coords does the right thing: it drops res and transform exactly when the output grid was reshaped relative to like. Reusing the existing reuse_like_coords flag is the right call -- there's no need to invent a second predicate for the same condition.

I also checked one thing that worried me: out_attrs = like_attrs followed by pop could mutate the caller's template attrs if like_attrs were a live reference. It isn't. _extract_grid_from_like returns attrs=dict(like.attrs) at line 2846, so it's already a copy.

get_dataarray_resolution in xrspatial/utils.py:483 does prefer attrs.get('res') over calc_res, so the symptom the PR describes is real and not theoretical.

Blockers

None.

Suggestions

None.

Nits

  • xrspatial/rasterize.py:3225: one slightly over-eager edge case. If a caller passes bounds= that exactly matches the template's bounds (with matching width/height), bounds_explicit=True so reuse_like_coords is False, and res/transform get stripped even though the grid is effectively identical. That said, the rebuilt linspace coords may drift by a ulp or two, so the strip is still arguably correct. Not worth changing; maybe worth a sentence in the comment if someone touches this block again.

What looks good

  • 9 tests, and they hit the three override paths plus the matching-size case plus the get_dataarray_resolution end-to-end symptom plus dask/cupy/dask+cupy parity.
  • The strip-case tests assert crs still propagates, which keeps anyone from later "fixing" this by stripping everything.
  • All four backends funnel through the same return statement, so one 2-line patch covers all of them.
  • The inline comment at lines 3216-3224 explains the why, not the what. Future-me will appreciate that.

@brendancol brendancol merged commit 1bbb95f into main May 21, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

performance PR touches performance-sensitive code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

rasterize: stale transform/res attrs propagated from like when grid is overridden

1 participant