Loading...
Loading...
Index points into a hexagonal grid
npx skill4agent add frontboat/h3-js h3-jsh3-jsnpm install h3-jsimport * 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 |
"85283473fffffff"gridRinggridDiskgridDistancegridPathCellscellToLocalIjcellToParentcellToChildrencompactCellsuncompactCellstrueformatAsGeoJsonisGeoJsoncellToBoundary.messagetry {
h3.cellToChildrenSize("invalid", 9);
} catch (e) {
console.error(e.message); // "Cell argument was not valid"
}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 |
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);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;
}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;
}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[37.77, -122.41][-122.41, 37.77]trueisGeoJsonformatAsGeoJsoncellToBoundaryfunction 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
},
};
}function cellsToFeatureCollection(cells, getProperties = () => ({})) {
return {
type: "FeatureCollection",
features: cells.map((cell) => cellToFeature(cell, getProperties(cell))),
};
}cellsToMultiPolygonfunction cellsToOutlineFeature(cells) {
const coordinates = h3.cellsToMultiPolygon(cells, true); // true = GeoJSON order
return {
type: "Feature",
properties: {},
geometry: { type: "MultiPolygon", coordinates },
};
}isGeoJson = truefunction featureToCells(feature, res) {
const coords = feature.geometry.coordinates;
// coords[0] = outer ring, coords[1..n] = holes
return h3.polygonToCells(coords, res, true);
}polygonToCellspolygonToCellsExperimental// 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,
);geojson2h3npm install geojson2h3import 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); // → cellh3.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_bboxh3.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); // → booleanh3.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);h3.UNITS.km2h3.UNITS.m2h3.UNITS.rads2h3.UNITS.kmh3.UNITS.mh3.UNITS.rads| v3 | v4 |
|---|---|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |