rasterize: honour input order across geometry types (#2064)#2077
Merged
Conversation
merge='first'/'last' decides a pixel's value by the global input position of the geometry, not by the polygon -> line -> point burn phase. Before: [(Point, 9), (Polygon, 1)] with merge='last' returned 9 at the shared pixel because points always burned after polygons. Now it returns 1 because the polygon is the last input. The fix: - _classify_geometries returns a per-type int64 global-index array alongside the existing props arrays. GeometryCollection unpacking inherits the parent input position. - A new int64 ``order`` array tracks which input index currently owns each pixel. Per-type burn kernels (scanline, lines, points) consult ``should_write(is_first, new_idx, cur_idx)`` before any write, so ordered merges gate by global index and commutative merges keep their old behaviour. - Built-in merges dispatch to (merge_fn, should_write) pairs. User callables keep the public (pixel, props, is_first) signature paired with the always-write predicate. - All four backends (numpy, cupy, dask+numpy, dask+cupy) take the new plumbing. Dask tiles slice the per-type global-idx arrays with the same boolean masks used for props. Closes #2064
Address review nits with two regression tests: - GeometryCollection sub-geoms share the parent's input index. When two sub-geoms (polygon + point) of the same GC compete for a pixel, the gate sees new_idx == cur_idx and skips, so the first-burned (polygon) wins. Documents the policy. - User callables for merge keep the pre-2064 "last-burned-wins" semantics because they pair with the always-write predicate. Built-in 'last' returns the polygon (last input); the callable returns the point (last burned).
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
merge='first'/merge='last'now decide a pixel's value by the global input position of the geometry, not by the internal polygon -> line -> point burn phase.Before:
[(Point(...), 9), (Polygon(...), 1)]withmerge='last'returned9at the shared pixel because points were always burned after polygons. The user supplied the polygon as the last input, so they expected1.Now: input order wins, on all four backends (numpy, cupy, dask+numpy, dask+cupy).
Mechanics
_classify_geometriesreturns a per-type int64 global-index array alongside the existing props arrays. GeometryCollection unpacking inherits the parent's input position.orderarray (one per output raster / per tile) tracks which input index currently owns each pixel. Burn kernels consultshould_write(is_first, new_idx, cur_idx)before any write.(merge_fn, should_write_fn)pairs.'first'usesshould_write_first(smallest index wins),'last'usesshould_write_last(largest index wins), commutative merges (max,min,sum,count) useshould_write_anyand keep their old behaviour.(pixel, props, is_first)signature, paired with the always-write predicate (current behaviour for callables).Closes #2064
Test plan
TestMergeOrderAcrossTypesclass: point-before-polygon, three-types-reverse-order, both'first'and'last', commutative-merge regression, dask+numpy path.