cesium-spatial
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.
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
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,
});
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
Camera to visible extent to cells, with the resolution derived from ground meters-per-pixel rather than a made-up zoom number.
Counts are estimated from area before any cells are generated, so a zoomed-out view coarsens instead of trying to build millions of hexagons.
Thousands of cells become one primitive. Recoloring writes straight into per-instance attributes, so styling never rebuilds geometry.
Batched primitives report geometry instances, not objects. CellPicker
turns a click or hover back into a cell index.
Wrapping extents are split before H3 sees them, so a wide view covers the longitudes you asked for rather than their complement.
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.
H3EntityLayer gives each cell a real Cesium entity, with an options bag
or a full factory hook for construction.
Drape cells over terrain, or extrude them into prisms with a per-cell height for the usual data-on-a-globe treatment.
Every exported function, class and option across the three packages, generated from the source with TypeDoc and cross-linked to Cesium's own reference.
Hexagon cells: traversal, cover, layers and resolution mapping.
S2 cells: tokens, four-neighbor traversal, uniform and adaptive covers.
Shared rendering, picking, camera measurement and level-of-detail.
The H3 hexagonal grid. Available now.
The S2 quadrilateral grid. Four neighbors everywhere, tokens instead of indices, and S2's native mixed-level covering.
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.