JavaScript / WASM#
M3S ships a JavaScript build that wraps the same Rust core as the Python package, so a cell encoded in Python decodes identically in JavaScript and vice versa. The JS API mirrors the Python API (Quickstart) in camelCase.
Note
The JS build is a thin wrapper over the core’s encode/decode primitives. Polygon fill, precision strategies, GeoPandas and cross-grid conversion are Python-only — see JS vs Python: what’s not here below.
Install#
npm install @nkarasiak/m3s
The package bundles both a Node (CommonJS WASM) and a browser (ESM WASM) build;
the right one is selected automatically through the exports map.
Build from source#
To build from a checkout instead, compile the two WASM targets with wasm-pack, then import the wrapper directly:
git clone https://github.com/nkarasiak/m3s.git
cd m3s/bindings/js
wasm-pack build --target nodejs --out-dir pkg # Node build -> index.node.js
wasm-pack build --target web --out-dir pkg-web # browser build -> index.web.js
Initialize, then use#
The browser build must initialize the WASM module before any grid call;
await m3s.ready() does that. On Node it resolves immediately, so the same
line works everywhere.
import * as m3s from "@nkarasiak/m3s"; // resolves index.web.js
await m3s.ready(); // loads + inits the WASM
const cell = m3s.H3.fromPoint(2.35, 48.86, 9); // (lon, lat, precision)
console.log(cell.id, cell.areaKm2);
import * as m3s from "@nkarasiak/m3s";
await m3s.ready(); // no-op on Node
const cell = m3s.H3.fromPoint(2.35, 48.86, 9);
console.log(cell.id, cell.areaKm2);
Coordinate order#
The wrapper boundary is GIS-native ``(lon, lat)``, matching the Python API,
Shapely and GeoJSON. Cell ring, centroid and bounds are [lon,
lat] too. (The underlying core takes (lat, lon); the wrapper does that
swap for you.)
Grids#
All 13 grids are exposed as singletons with the same names as Python:
m3s.A5 m3s.Geohash m3s.H3 m3s.MGRS m3s.S2 m3s.Quadkey
m3s.Slippy m3s.CSquares m3s.GARS m3s.Maidenhead m3s.PlusCode m3s.EAQuad
m3s.RHEALPix
Each grid carries its precision metadata from the core:
m3s.H3.name; // "h3"
m3s.H3.precisionRange; // [0, 15]
m3s.H3.defaultPrecision; // 7
m3s.H3.hierarchical; // true (false for GARS, Maidenhead, MGRS)
Grid methods#
const grid = m3s.H3;
grid.fromPoint(lon, lat, precision?); // -> Cell
grid.fromBbox([minLon, minLat, maxLon, maxLat], precision?); // -> CellCollection
grid.fromId(id); // -> Cell
grid.fromIds([id, ...]); // -> CellCollection
grid.fromGeometry(geom, precision?); // [lon,lat] | bbox | ring | GeoJSON
grid.neighbors(cellOrId, depth = 1, includeSelf = true); // -> CellCollection
grid.children(cellOrId); // -> CellCollection (hierarchical only)
grid.parent(cellOrId); // -> Cell (hierarchical only)
grid.withPrecision(p); // -> Grid clone
Calling children()/parent() on a non-hierarchical grid (GARS,
Maidenhead, MGRS) throws, mirroring Python.
Cell#
cell.id; // string identifier
cell.precision; // number
cell.ring; // closed [[lon,lat], ...]
cell.ringOpen; // closing vertex dropped (deck.gl-friendly)
cell.areaKm2; // geodesic area, delegated to the core (matches Python)
cell.centroid; // [lon, lat]
cell.bounds; // [minLon, minLat, maxLon, maxLat]
cell.toGeoJSON(); // GeoJSON Polygon Feature
CellCollection#
Returned by fromBbox, fromIds, neighbors, children. Iterable.
coll.length; coll.at(i); coll.slice(a, b);
coll.toIds(); coll.ids; coll.toPolygons(); coll.toGeoJSON();
coll.filter(fn); coll.map(fn); coll.unique();
coll.neighbors(depth); coll.refine(p); coll.coarsen(p);
coll.totalAreaKm2; coll.bounds;
Parity with Python#
Both bindings consume one m3s-core crate, and CI runs golden-vector parity
gates (tests/js/parity.cjs for the core, tests/js/wrapper_parity.mjs for
this wrapper) so JS results cannot drift from Python.
JS vs Python: what’s not here#
The core supports point→cell and bbox→cells. These features stay Python-only and are deliberately absent from the JS build:
Python |
JavaScript |
|---|---|
|
|
|
pass an explicit precision; read |
|
use |
|
render |
|
not available (no Shapely) |
|
query each grid directly for the same area |
|
not available |