Skip to content

h3: keep cell areas exact across the antimeridian - #176

Merged
justinhwang merged 1 commit into
masterfrom
fix/area-antimeridian
Oct 21, 2026
Merged

justinhwang merged 1 commit into
masterfrom
fix/area-antimeridian

Conversation

@justinhwang

Copy link
Copy Markdown
Collaborator

Summary

CellAreaRads2, CellAreaKm2 and CellAreaM2 returned too small an area for cells that straddle the antimeridian. The per-edge term subtracts the endpoints' longitudes; for an edge crossing ±180° the raw difference is close to ±360°, and the true, tiny longitude step is lost to the rounding of a value that large. Measured by comparing a straddling cell's boundary with the same boundary rotated 180° about the polar axis:

res before after
7 1.0e-8 8.7e-13
13 1.1e-3 6.2e-10
15 8.8e-2 1.7e-8

The fix shifts both longitudes to the same side of the antimeridian before subtracting. sin and cos are 2π-periodic, so the result is mathematically unchanged, and edges that do not cross take the old path. This is upstream uber/h3#1274, still open, which the pinned v4.5.0 does not contain.

The conformance suite was blind to this: its seeded sample has straddling cells only at resolutions 0 and 1, where the loss is around 1e-13. So:

  • conformance/internal/gen/patches/0002-cellArea-antimeridian.patch carries the upstream area.c hunk, applied the same way as the atan2 patch and to be deleted once a pinned release contains it.
  • The generator writes a seventh curated file, floats/curated.jsonl: the three straddling and non-straddling pairs from the upstream tests at res 13 to 15, plus the cell containing (lat, 180°) at every resolution for latitudes 0°, 45° and −60°, 54 subjects. TestFloatCells runs it with the floats/cells checks.
  • Three tests port the upstream ones: pairs agree to 1e-5, a cell on the antimeridian keeps its area under the half-turn rotation at every resolution to 1e-6, and a small triangle across ±180° has the area of the same triangle across 0°. Each fails without the shift.

Test plan

  • The three new tests fail with the shift removed and pass with it
  • Full regeneration with both patches (go generate ./conformance, then digests.sh 7) adds floats/curated.jsonl, moves the area of the fifteen straddling res 0 and 1 cells already in floats/cells.jsonl by at most 4e-14 relative, and updates the manifest; every other file, the digests included, is byte for byte unchanged
  • go test -race ./... green, root and conformance at 100% coverage
  • build.sh with GEN_STRICT_CFLAGS='-Wall -Wextra -Wconversion -Wpedantic -Werror' succeeds
  • golangci-lint run ./... clean

@coveralls

coveralls commented Oct 21, 2026 •

Copy link
Copy Markdown

Coverage Report for CI Build 39681503510

Coverage remained the same at 100.0%

Details

  • Coverage remained the same as the base build.
  • Patch coverage: 12 of 12 lines across 2 files are fully covered (100%).
  • No coverage regressions found.

Uncovered Changes

No uncovered changes found.

Coverage Regressions

No coverage regressions found.


Coverage Stats

Coverage Status
Relevant Lines: 3669
Covered Lines: 3669
Line Coverage: 100.0%
Coverage Strength: 5169741.24 hits per line

💛 - Coveralls

cagnoli subtracted the raw longitudes of an edge's endpoints. For an edge
crossing +-180 degrees the difference is close to +-360 and the true, tiny
longitude step is lost to the rounding of a value that large, so cells
straddling the antimeridian came out too small: 1e-8 relative at res 7,
0.1% at res 13 and 9% at res 15. Shifting both longitudes to the same side
of the antimeridian before subtracting keeps the subtraction between two
nearby values; sin and cos are 2pi-periodic, so nothing else changes. This
is upstream uber/h3#1274, which the pinned release does not yet contain.

The conformance suite could not see the defect: its seeded sample has
straddling cells only at resolutions 0 and 1, where the loss is far below
tolerance. The generator now carries the upstream change as a second patch
and writes floats/curated.jsonl, the upstream test pairs at res 13 to 15
plus the cell containing (lat, 180) at every resolution for three
latitudes, which the floats/cells checks run. The fifteen straddling res 0
and 1 cells already in floats/cells.jsonl move by at most 4e-14 relative;
every other file, the digests included, is byte for byte unchanged.

Three tests port the upstream ones: straddling cells agree with their
non-straddling neighbours, a cell on the antimeridian keeps its area when
rotated onto the prime meridian, and a small triangle across +-180 has the
area of the same triangle across 0. All three fail without the shift.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@justinhwang
justinhwang force-pushed the fix/area-antimeridian branch from dfd2153 to 99510a8 Compare October 21, 2026 21:46
@justinhwang
justinhwang merged commit 67f9140 into master Oct 21, 2026
15 checks passed
@justinhwang
justinhwang deleted the fix/area-antimeridian branch October 21, 2026 22:17
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.

2 participants