cesium-spatial

H3 and S2 grids,
without the grid library.

Cover what the camera is looking at with H3 hexagons or S2 cells, draw thousands of them as a single batched primitive, and walk neighbors, parents and children — all in Cesium's own vocabulary. No grid-library internals, no hand-rolled level-of-detail, no antimeridian surprises.

Install

Cesium stays a peer dependency, so the library never pulls in a second copy of the engine. Anything from CesiumJS 1.95 onward works, and @cesium/engine is supported too.

npm install @stevenpg/cesium-h3 cesium   # hexagons
npm install @stevenpg/cesium-s2 cesium   # or S2 cells

A layer that follows the camera

H3ViewLayer is the whole feature in one object: it watches the camera, picks a resolution from the view's ground resolution, covers the visible extent, and rebuilds only when the set of cells actually changes.

import { Viewer } from 'cesium';
import { H3ViewLayer } from '@stevenpg/cesium-h3';

const viewer = new Viewer('cesiumContainer');

const layer = new H3ViewLayer(viewer.scene, {
  maxCells: 8000,        // budget; coarsens rather than overrunning
  targetEdgePixels: 72,  // how big a cell should look on screen
  outlines: true,
});

Or drive it yourself

Every piece underneath is a plain function. Cover an extent, traverse the grid, and hand the result to a batched layer whenever you like.

import { cellAt, disk, parent, H3CellLayer } from '@stevenpg/cesium-h3';
import { Color } from 'cesium';

const origin = cellAt(-122.4194, 37.7749, 9);   // longitude first
const patch  = disk(origin, 12);                // 469 cells

const layer = new H3CellLayer(viewer.scene, {
  style: (cell) => ({
    color: cell === origin ? Color.ORANGE : Color.CYAN.withAlpha(0.3),
  }),
});

layer.setCells(patch);
layer.setCellStyle(parent(origin), { color: Color.MAGENTA });  // no rebuild

What it handles for you

View-driven cover

Camera to visible extent to cells, with the resolution derived from ground meters-per-pixel rather than a made-up zoom number.

A real cell budget

Counts are estimated from area before any cells are generated, so a zoomed-out view coarsens instead of trying to build millions of hexagons.

Batched rendering

Thousands of cells become one primitive. Recoloring writes straight into per-instance attributes, so styling never rebuilds geometry.

Picking that works

Batched primitives report geometry instances, not objects. CellPicker turns a click or hover back into a cell index.

Antimeridian and poles

Wrapping extents are split before H3 sees them, so a wide view covers the longitudes you asked for rather than their complement.

Pentagon-safe traversal

Neighbors and rings use the safe H3 paths, so the twelve pentagons behave like every other cell instead of returning nothing. S2 has no exceptions to begin with.

Entities when you need them

H3EntityLayer gives each cell a real Cesium entity, with an options bag or a full factory hook for construction.

Terrain and extrusion

Drape cells over terrain, or extrude them into prisms with a per-cell height for the usual data-on-a-globe treatment.

API reference

Every exported function, class and option across the three packages, generated from the source with TypeDoc and cross-linked to Cesium's own reference.

cesium-h3

Hexagon cells: traversal, cover, layers and resolution mapping.

cesium-s2

S2 cells: tokens, four-neighbor traversal, uniform and adaptive covers.

spatial-core

Shared rendering, picking, camera measurement and level-of-detail.

Packages

@stevenpg/cesium-h3

The H3 hexagonal grid. Available now.

@stevenpg/cesium-s2

The S2 quadrilateral grid. Four neighbors everywhere, tokens instead of indices, and S2's native mixed-level covering.

@stevenpg/cesium-spatial-core

Shared rendering, camera measurement and level-of-detail. Installed for you as a dependency; use it directly only if you are building your own index.

Both grids run on the same core, so S2 arrived as an adapter rather than a second implementation. Each package keeps its own idiom, though — H3 speaks of resolutions, disks and rings, S2 of levels, tokens and cube faces — so they read naturally to anyone who already knows that index.