How this skill is triggered — by the user, by Claude, or both
Slash command
/h3-js:h3-jsThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
H3 is Uber's hierarchical hexagonal geospatial indexing system. It tiles the sphere with hexagons at 16 resolutions (0-15), each ~7x finer than the last. The `h3-js` library provides JavaScript bindings for the H3 C library via emscripten.
H3 is Uber's hierarchical hexagonal geospatial indexing system. It tiles the sphere with hexagons at 16 resolutions (0-15), each ~7x finer than the last. The h3-js library provides JavaScript bindings for the H3 C library via emscripten.
npm install h3-js
import * as h3 from "h3-js"; // ESM
const h3 = require("h3-js"); // CJS
| System | Cell Shape | Hierarchy | Identifiers | Key Tradeoff |
|---|---|---|---|---|
| H3 | Hexagon | Aperture 7, approximate containment | 64-bit int | Equidistant neighbors; approximate subdivision |
| S2 | Square | Aperture 4, exact containment | 64-bit int | Exact subdivision; non-equidistant neighbors |
| Geohash | Rectangle | Exact containment | Variable-length string | Simple encoding; severe area distortion at poles |
| Hexbin | Hexagon | None | Projection-specific | Cheap to compute; no hierarchy, not portable |
| Admin boundaries | Irregular | None | Names/codes | Human-meaningful; incomparable sizes, change over time |
Understand these before using the API — they affect correctness.
"85283473fffffff").12 pentagons per resolution, centered on icosahedron vertices (all in ocean — land apps rarely hit them). Most functions handle pentagons transparently. Exceptions:
gridRing can throw near pentagons — use gridDisk as the safe alternative.gridDistance, gridPathCells, and cellToLocalIj may fail across pentagonal distortion.Child cell centers may fall outside their parent's boundary. Geographic containment is approximate; logical containment is exact. Use cellToParent/cellToChildren for logical hierarchy. For exact boundaries, combine H3 indexing with point-in-polygon checks.
compactCells / uncompactCells use logical containment and are exact.
Resolutions alternate orientation by ~19.1 degrees. Even (0, 2, 4, ...) = Class II; odd (1, 3, 5, ...) = Class III. Matters for algorithms depending on grid alignment.
true as the formatAsGeoJson / isGeoJson parameter for lng/lat (GeoJSON) order.cellToBoundary may return more than 6 vertices for cells that cross icosahedron face boundaries.Invalid inputs throw errors. Check .message for details.
try {
h3.cellToChildrenSize("invalid", 9);
} catch (e) {
console.error(e.message); // "Cell argument was not valid"
}
Common causes: invalid cell/edge/vertex, incompatible resolutions, cells too far apart for grid distance, pentagon distortion.
Each finer resolution has ~7x more cells. Total cells at resolution r: 2 + 120 * 7^r.
| Res | Avg Hex Area | Avg Edge Length | Total Cells | Typical Use Case |
|---|---|---|---|---|
| 0 | 4,357,449 km2 | 1,281 km | 122 | Continental |
| 3 | 12,393 km2 | 69 km | 41,162 | Country / state |
| 5 | 253 km2 | 9.9 km | 2,016,842 | Metro area |
| 7 | 5.16 km2 | 1.4 km | 98,825,162 | Neighborhood |
| 9 | 0.105 km2 | 201 m | 4.8 billion | City block |
| 11 | 0.00215 km2 | 28.7 m | 237 billion | Building |
| 13 | 43.9 m2 | 4.1 m | 11.6 trillion | Room |
| 15 | 0.9 m2 | 0.58 m | 569 trillion | Sub-meter |
Convert a radius (km) to a k-ring step count via average edge length.
const origin = h3.latLngToCell(lat, lng, 8);
const radiusKm = 4;
const edgeKm = h3.getHexagonEdgeLengthAvg(8, h3.UNITS.km);
const k = Math.floor(radiusKm / (edgeKm * 2));
const nearby = h3.gridDisk(origin, k);
Count points per cell for density visualization or analysis.
function aggregatePoints(points, res) {
const counts = {};
for (const { lat, lng } of points) {
const cell = h3.latLngToCell(lat, lng, res);
counts[cell] = (counts[cell] || 0) + 1;
}
return counts;
}
Spread each point's influence across surrounding cells with linear decay by grid distance.
function weightWithFalloff(points, res, kRadius) {
const weights = {};
const step = 1 / (kRadius + 1);
for (const { lat, lng } of points) {
const origin = h3.latLngToCell(lat, lng, res);
const rings = h3.gridDiskDistances(origin, kRadius);
rings.forEach((ring, dist) => {
for (const cell of ring) {
weights[cell] = (weights[cell] || 0) + 1 - dist * step;
}
});
}
return weights;
}
Reduce storage for large cell sets. All input cells must share the same resolution.
const cells = h3.polygonToCells(polygon, 9);
const compact = h3.compactCells(cells); // mixed-resolution, fewer cells
const restored = h3.uncompactCells(compact, 9); // back to uniform res 9
h3-js defaults to lat/lng ([37.77, -122.41]). GeoJSON requires lng/lat ([-122.41, 37.77]). Pass true for the isGeoJson/formatAsGeoJson parameter to switch. Most common integration bug: forgetting this.
cellToBoundary returns coordinate arrays, not GeoJSON objects. Wrap manually.
function cellToFeature(cell, properties = {}) {
const boundary = h3.cellToBoundary(cell, true); // true = GeoJSON [lng, lat] order
return {
type: "Feature",
properties: { h3index: cell, ...properties },
geometry: {
type: "Polygon",
coordinates: [[...boundary, boundary[0]]], // close the ring
},
};
}
One polygon feature per cell — useful for choropleth styling.
function cellsToFeatureCollection(cells, getProperties = () => ({})) {
return {
type: "FeatureCollection",
features: cells.map((cell) => cellToFeature(cell, getProperties(cell))),
};
}
cellsToMultiPolygon merges adjacent cells into polygon outlines. All cells must share the same resolution with no duplicates — undefined behavior otherwise.
function cellsToOutlineFeature(cells) {
const coordinates = h3.cellsToMultiPolygon(cells, true); // true = GeoJSON order
return {
type: "Feature",
properties: {},
geometry: { type: "MultiPolygon", coordinates },
};
}
Pass isGeoJson = true so h3-js reads coordinates in lng/lat order.
function featureToCells(feature, res) {
const coords = feature.geometry.coordinates;
// coords[0] = outer ring, coords[1..n] = holes
return h3.polygonToCells(coords, res, true);
}
polygonToCells uses center containment by default — includes a cell only if its center is inside the polygon. Use polygonToCellsExperimental for other modes.
// Include cells that overlap the polygon boundary at all (fuller coverage)
h3.polygonToCellsExperimental(
coords,
res,
h3.POLYGON_TO_CELLS_FLAGS.containment_overlapping,
true,
);
// Include only cells fully contained within the polygon (stricter)
h3.polygonToCellsExperimental(
coords,
res,
h3.POLYGON_TO_CELLS_FLAGS.containment_full,
true,
);
geojson2h3 provides higher-level conversion helpers:
npm install geojson2h3
import geojson2h3 from "geojson2h3";
// GeoJSON Feature → cell set
const cells = geojson2h3.featureToH3Set(geojsonFeature, res);
// Cell set → GeoJSON FeatureCollection (one Feature per cell)
const fc = geojson2h3.h3SetToFeatureCollection(cells, (cell) => ({
value: data[cell],
}));
// Cell set → single merged GeoJSON Feature (outline)
const outline = geojson2h3.h3SetToFeature(cells);
h3.latLngToCell(lat, lng, resolution) // → "85283473fffffff"
h3.cellToLatLng(cell) // → [lat, lng] (center point)
h3.cellToBoundary(cell, formatAsGeoJson?) // → [[lat, lng], ...] (vertices)
h3.getResolution(cell); // → number (0-15)
h3.isValidCell(cell); // → boolean
h3.isPentagon(cell); // → boolean
h3.isResClassIII(cell); // → boolean
h3.getBaseCellNumber(cell); // → number (0-121)
h3.getIcosahedronFaces(cell); // → [faceNum, ...]
h3.cellToParent(cell, parentRes); // → cell
h3.cellToChildren(cell, childRes); // → [cell, ...]
h3.cellToCenterChild(cell, childRes); // → cell
h3.compactCells(cells); // → mixed-resolution compact set
h3.uncompactCells(cells, res); // → uniform-resolution expanded set
h3.cellToChildPos(child, parentRes); // → position index
h3.childPosToCell(childPos, parent, childRes); // → cell
h3.gridDisk(origin, k); // filled disk within k steps (safe near pentagons)
h3.gridDiskDistances(origin, k); // disk grouped by distance → [[ring0], [ring1], ...]
h3.gridRing(origin, k); // hollow ring at exactly k steps (may throw near pentagons)
h3.gridDistance(a, b); // grid distance (same resolution; may fail across pentagons)
h3.gridPathCells(start, end); // path of cells (inclusive; may fail across pentagons)
h3.cellToLocalIj(origin, cell); // → {i, j} (experimental; local to origin)
h3.localIjToCell(origin, { i, j }); // → cell (experimental)
// polygon = array of coordinate rings; first is outer boundary, rest are holes
h3.polygonToCells(polygon, res, isGeoJson?)
h3.cellsToMultiPolygon(cells, formatAsGeoJson?) // all cells same res, no duplicates
h3.polygonToCellsExperimental(polygon, res, flags, isGeoJson?)
// flags: containment_center (default), containment_full, containment_overlapping, containment_overlapping_bbox
h3.areNeighborCells(a, b); // → boolean
h3.cellsToDirectedEdge(origin, dest); // → edge index
h3.getDirectedEdgeOrigin(edge); // → cell
h3.getDirectedEdgeDestination(edge); // → cell
h3.directedEdgeToCells(edge); // → [origin, dest]
h3.originToDirectedEdges(cell); // → [edge, ...] (all 6 or 5 edges)
h3.directedEdgeToBoundary(edge); // → [[lat, lng], ...]
h3.cellToVertex(cell, vertexNum); // → vertex index (0-5 hex, 0-4 pentagon)
h3.cellToVertexes(cell); // → [vertex, ...]
h3.vertexToLatLng(vertex); // → [lat, lng]
h3.isValidVertex(vertex); // → boolean
h3.getHexagonAreaAvg(res, h3.UNITS.km2); // average hex area at resolution
h3.getHexagonEdgeLengthAvg(res, h3.UNITS.km); // average edge length at resolution
h3.cellArea(cell, h3.UNITS.km2); // exact area of a specific cell
h3.edgeLength(edge, h3.UNITS.km); // exact length of a specific edge
h3.getNumCells(res); // total cell count at resolution
h3.greatCircleDistance(point1, point2, h3.UNITS.km); // haversine distance ([lat,lng] points)
h3.getRes0Cells(); // all 122 base cells
h3.getPentagons(res); // 12 pentagons at resolution
h3.degsToRads(degrees);
h3.radsToDegs(radians);
Units: h3.UNITS.km2, h3.UNITS.m2, h3.UNITS.rads2, h3.UNITS.km, h3.UNITS.m, h3.UNITS.rads
Function names changed in v4. The grid itself is unchanged — indexes from v3 are valid in v4.
| v3 | v4 |
|---|---|
geoToH3 | latLngToCell |
h3ToGeo | cellToLatLng |
h3ToGeoBoundary | cellToBoundary |
kRing | gridDisk |
hexRing | gridRing |
h3Distance | gridDistance |
h3ToParent | cellToParent |
h3ToChildren | cellToChildren |
polyfill | polygonToCells |
h3SetToMultiPolygon | cellsToMultiPolygon |
compact | compactCells |
uncompact | uncompactCells |
h3IndexesAreNeighbors | areNeighborCells |
See reference.md for the complete function name mapping.
Complete API reference, algorithm internals, community libraries, and 40+ Observable notebooks in reference.md.
npx claudepluginhub frontboat/h3-js --plugin h3-jsCreate, manipulate, and analyze ArcGIS geometries like points, polylines, polygons, extents using JS API classes and operators for buffering, intersections, unions, spatial calculations.
Encodes/decodes geohashes from lat/lon coordinates with GF(3) coloring for hierarchical spatial indexing, clustering, location schemes, and privacy-preserving representations.
Guides AI assistants on selecting Mapbox geospatial tools based on problem type: offline geometric (Turf.js) vs routing/navigation APIs for distance, travel time, containment, and route optimization.