Skip to content

[GH-3415] Fix allTouched rasterization dropping the row or column across a grid line - #3417

Draft
jiayuasu wants to merge 3 commits into
apache:masterfrom
jiayuasu:fix-alltouched-grid-line-tolerance
Draft

jiayuasu wants to merge 3 commits into
apache:masterfrom
jiayuasu:fix-alltouched-grid-line-tolerance

Conversation

@jiayuasu

Copy link
Copy Markdown
Member

Did you read the Contributor Guide?

Is this PR related to a ticket?

What changes were proposed in this PR?

Stacked on #3416. Until that merges, this PR also shows its commit; the changes for this issue are the last two commits. I'll rebase once #3416 lands.

  • Tolerant grid-line check. In rasterizeGeomExtent, the four allTouched expansion checks compared the geometry envelope with the snapped grid line using ==. The snap goes through a BigDecimal round trip and can land a few ULPs off the grid line computed in double arithmetic (59.99916666666667 vs 59.99916666666666), so the extent was not widened and the touched row or column across the line was never visited. The checks now use isOnGridLine, which allows 8 ULPs of the largest operand (raster origin included, so a grid line near zero is judged against the origin's magnitude). The widened extent is only the search window; each cell is still tested against the geometry, so a point strictly inside one row does not burn its neighbour.
  • Regression test for line rasterization at the far raster edges. Lines that leave the raster through its right or bottom edge, or end exactly on it, keep the edge pixels on a 1/3600 degree tile. This already passes on master and guards the edge handling in burnCell.

How was this patch tested?

Did this PR include necessary documentation updates?

  • No, this PR does not affect any public API so no need to change the documentation.

…e raster grid

rasterizePoint walked the geometry extent by repeatedly adding scaleX /
scaleY to a world coordinate while counting the pixel index separately.
When the pixel size has no exact double representation (for example
1/3600 degree, the Copernicus GLO-30 grid) the running coordinate can
fall a fraction of an ULP short of the extent's far edge and take one
more step. The index then lands outside the grid, and because the JAI
banded raster's setSample does no bounds check the write goes past the
data buffer: a point on a 256x256 tile's bottom-right corner fails with
"Index 65536 out of bounds for length 65536", and a point on a row
boundary with "Index -246". The same drift moves the tested cell edges a
few ULPs off the grid lines, so the cell tested and the cell written can
disagree.

Iterate over integer column and row ranges instead, clamped to the grid,
and build each cell envelope from its index on the reference raster's
grid. The envelope and the written index can no longer drift apart, and
a cropped output resolves boundary contacts the same way as a
full-extent one. The geometry is prepared once for the per-cell tests.

A polygon that only grazes the raster clips to a point, a line or a
GeometryCollection, which rasterizePolygon cast to Polygon
(ClassCastException). Rasterize such clips through rasterizeGeometry,
keeping only their polygonal parts unless allTouched is set, since a
point or line left behind by the clip covers no pixel centre.

In fillPolygon, clamp fill ranges to the grid and discard an unpaired
scanline intercept that lands past it instead of writing out of bounds.
… raster edges

On a 1/3600 degree grid the raster's right edge converts to a pixel
coordinate a rounding error past the grid, so a segment that ends on or
crosses it makes the cell traversal visit one column outside the raster,
which burnCell skips. The test checks that the edge cells are still
burned, comparing each geometry (lines in both directions and an
allTouched polygon) against the same geometry rasterized on a larger
raster with the same origin and cropped back.
…sits on a grid line

rasterizeGeomExtent snaps the geometry envelope to pixel edges through a
BigDecimal round trip, and the allTouched expansion then compared the
envelope against the snapped value with ==. The round trip can land a
few ULPs away from the grid line as computed in double arithmetic, so a
geometry lying exactly on a row or column boundary was not recognized as
touching the neighbouring row or column, and that row or column was
never rasterized. Compare within a few ULPs instead, scaled by the
largest operand involved so that a grid line near zero is judged against
the raster origin.

The widened extent is only the search window: each cell is still tested
against the geometry, so a point just inside one row does not burn the
row next to it.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

allTouched rasterization drops the row or column across a grid line when a geometry lies exactly on it

1 participant