Skip to content

fix(h3t): render H3 hexagons crossing the antimeridian#211

Open
bbest wants to merge 1 commit into
walkerke:mainfrom
bbest:fix/h3t-antimeridian
Open

fix(h3t): render H3 hexagons crossing the antimeridian#211
bbest wants to merge 1 commit into
walkerke:mainfrom
bbest:fix/h3t-antimeridian

Conversation

@bbest

@bbest bbest commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

Problem

H3 hexagons that cross the antimeridian (±180°) render as a jagged tear/gap in
add_h3t_source() (and add_h3j_source() / setH3JData()). h3-js
h3ToGeoBoundary() returns such a cell's vertices split between +179° and
−179°, so the resulting polygon ring spans ~358° of longitude. geojson-vt then
mis-tiles that "wraps-the-whole-globe" polygon, leaving a gap of missing/
degenerate cells along 180°.

Fix

The cell→polygon builder (generate() for the Polygon path) now detects
boundaries whose longitude span exceeds 180° and unwraps them — shifting the
negative-longitude vertices by +360° — so the ring is continuous near +180°
before geojson-vt tiles it. geojson-vt's own antimeridian wrap then emits the
cell on both sides of the seam. Non-crossing cells are untouched.

o.generate = h3id => {
  if (o.geometry_type !== 'Polygon') return utils.h3.h3ToGeo(h3id).reverse();
  const ring = utils.h3.h3ToGeoBoundary(h3id, true);
  let min = 180, max = -180;
  for (const p of ring) { if (p[0] < min) min = p[0]; if (p[0] > max) max = p[0]; }
  return (max - min > 180)
    ? [ring.map(p => [p[0] < 0 ? p[0] + 360 : p[0], p[1]])]   // unwrap across 180°
    : [ring];
};

Applied to the three generate() definitions in the bundled
inst/htmlwidgets/lib/h3j-h3t/h3j_h3t.js (addH3TSource / addH3JSource /
setH3JData). The same one-liner applies upstream to
INSPIDE/h3j-h3t src/index.js
— happy to file that PR too so a future h3j-h3t bump keeps the fix.

Verification

Tested against a live global OBIS h3t endpoint
(h3t.marinesensitivity.org, ES50 by H3 cell). Before: a wide jagged tear down
180°. After: hexagons render continuously across the antimeridian on both the
globe and flat (mercator) projections. (Verified with a headless capture; a
faint hairline can remain exactly at the tile boundary under software
rendering — a geojson-vt seam characteristic, not the original gap.)

This is a follow-up to #209 / #199.

bbest added a commit to MarineSensitivity/apps that referenced this pull request Jun 24, 2026
H3 hexagons crossing ±180° rendered as a jagged tear; pin the antimeridian-fixed
fork branch (bbest/mapgl@fix/h3t-antimeridian) until the upstream PR merges.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@bbest bbest force-pushed the fix/h3t-antimeridian branch 3 times, most recently from bd622e6 to e4c4717 Compare June 24, 2026 11:03
H3 cells straddling ±180° rendered as a jagged tear/gap: h3-js
`h3ToGeoBoundary()` returns their vertices split between +179 and -179, so the
polygon spans ~358° of longitude and geojson-vt mis-tiles it.

Two parts, in the bundled `h3j-h3t` cell builders (addH3TSource / addH3JSource /
setH3JData):

1. fixTransmeridian (Nick Rabinowitz,
   https://observablehq.com/@nrabinowitz/mapbox-utils): a boundary ring with any
   arc > 180° lon has its negative-lon vertices shifted +360°, so it stays
   continuous near +180° instead of wrapping the globe. Complete for the
   non-tiled add_h3j_source / setH3JData.

2. For the tiled add_h3t_source, a crossing cell is fetched into more than one
   {z}/{x}/{y} tile; each cell is normalized by ±360° onto the side of the
   antimeridian the rendered tile sits on, so its halves draw in the correct
   tiles and meet seamlessly. Pairs with an antimeridian-aware tile filter on
   the h3t server that returns a crossing cell to both edge tiles.

No extra libraries; non-crossing cells are untouched.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@bbest bbest force-pushed the fix/h3t-antimeridian branch from e4c4717 to 1e52f60 Compare June 24, 2026 12:20
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.

1 participant