H3 and S2 share no index space, so there is no lookup between them — every cross-system conversion goes through geometry (a center point or a boundary polygon) and is therefore an approximation, not a translation. This page measures exactly how lossy one round trip is, using the live conversion tools, so the number is checkable rather than asserted. It is the evidence behind the §8 rule that a set operation spanning two cell systems MUST reject rather than silently convert.
The round trip
Start with a single H3 resolution-8 cell over midtown Manhattan,
882a100d67fffff (area 0.741 km²). Cover it with S2, then re-fill those
S2 cells back to H3:
- start
- 1 H3 cell, res 8 — 0.741 km²
- → S2 (level 13, cover)
- 4 S2 cells — union 4.35 km², coveredFraction 1.0, overreach 3.61 km²
- → H3 (res 8, cover, intersect)
- 12 H3 cells — summed underreach 0.06 m², summed overreach 9.73 km²
One cell became four, then twelve. The forward step alone adds 3.61 km² of S2 area beyond the original hexagon — the S2 cover contains the cell (coveredFraction 1.0) but spills ~5× its area, because a level-13 S2 quad does not align to an H3 hexagon's boundary. Re-filling those four S2 cells back to H3 adds a further 9.73 km² of overreach (summed across the four re-fills) while leaving a 0.06 m² underreach sliver — a gap so small it can only be a numerical artifact of the planar-corner S2 boundary, which is itself part of the approximation.
Within one grid, normalize-then-subtract is exact algebra: two H3 sets at the same resolution intersect and difference with no error. Across grids it is geometry — a boundary cover with quantified overreach. That is why a cross-system overlay belongs to a declared conversion, never inside a set operation, and why mixing systems in one overlay is a structural error, not a tolerance to be tuned.
Regenerate it
Every number above comes from two live MCP tools; nothing is hand-computed.
h3_to_s2— forward cover and its overreach:
- tool
- h3_to_s2
- arguments
- { cell: "882a100d67fffff", s2Level: 13, mode: "cover" }
- reads
- count = 4; metrics.coveredFraction = 1.0; metrics.overreachAreaM2 = 3,609,291.6
s2_to_h3— re-fill each of the four returned S2 tokens (89c258fc,89c25854,89c259ac,89c25904) back to H3 and union them:
- tool
- s2_to_h3 (once per S2 token)
- arguments
- { cell: <token>, res: 8, mode: "cover", rule: "intersect" }
- reads
- union = 12 H3 cells; Σ metrics.underreachAreaM2 = 0.0588 m²; Σ metrics.overreachAreaM2 = 9,733,826 m²
Both tools tag their output with the by_geo system discriminator
(system: "s2" on the forward step, system: "h3" on the return), so the two
halves of the round trip are never confused for the same grid. The exactness
is approximate by construction — the honest label for a conversion that
cannot be a lookup.
