meos.js
v1.0.2
Published
JavaScript/TypeScript bindings for MobilityDB MEOS via WebAssembly
Downloads
200
Readme
MEOS.js
TypeScript/JavaScript bindings for MEOS, the C library that powers MobilityDB spatiotemporal types.
MEOS is compiled to WebAssembly (wasm64/MEMORY64) via Emscripten. MEOS.js wraps the resulting .wasm module in a typed TypeScript API so you can work with temporal values, spans, sets, and bounding boxes in Node.js or the browser.
npm install meos.jsDocumentation · Examples · MobilityDB
Requires a runtime with WebAssembly MEMORY64 (Node.js 22+, or a recent browser).
Table of contents
- Requirements
- Project Structure
- Installation
- Using from JavaScript
- Code Generation
- Tests
- Doc
- Memory management
- DeckGL integration
- Use Case Examples
Requirements
Docker: only needed to build the WASM module from source. Not needed if you use the prebuilt files.
A JS engine with WebAssembly MEMORY64 support: needed to run MEOS.js, because
meos.wasmis compiled with-sMEMORY64=1. In practice:- server-side: Node.js 22+
- browser-side: recent Chromium-based browsers or Firefox with the MEMORY64 proposal enabled
initMeos()probes for MEMORY64 at startup and throws a clear error if the engine doesn't support it.
Node.js 22+ is additionally required to run the tests, the code generator, the TypeScript build and the docs. Not needed for the WASM build itself.
Project Structure
MEOS.js/
├── codegen/ ← Code generator
│ ├── res/
│ │ ├── meos-idl.json ← MEOS API description
│ │ ├── meos.h, meos_geo.h ← Cached upstream headers
│ │ ├── bindings_c_header.c.template
│ │ └── functions_ts_header.ts.template
│ └── FunctionsGenerator.ts ← Eemits the C glue + TS bindings
├── core/
│ ├── c-src/
│ │ └── bindings.c ← Generated C glue
│ ├── functions/
│ │ ├── functions.generated.ts ← Generated TS bindings
│ │ ├── errors.ts ← MEOS error code handling
│ │ └── ptr_array.ts ← Pointer-array marshalling helpers
│ ├── runtime/
│ │ └── meos.ts ← WASM module loader
│ ├── types/ ← High-level typed wrappers
│ │ ├── basic/ ← TBool, TInt, TFloat, TText...
│ │ ├── boxes/ ← TBox, STBox
│ │ ├── collections/ ← Span, SpanSet, MeoSet...
│ │ └── temporal/ ← Temporal base class + factory
│ └── index.ts ← Public exports
├── wasm/ ← Build output (meos.js, meos.wasm)
├── test/ ← Unit tests (node:test + tsx)
├── docs/ ← TypeDoc + VitePress sources & HTML
├── Dockerfile ← Multi-stage build: MEOS → WASM
└── package.jsonThe two-layer architecture consists of:
codegen/: readscodegen/res/meos-idl.jsonand generatescore/c-src/bindings.candcore/functions/functions.generated.ts.core/: implements the high-level typed wrappers on top of the generated bindings, plus the runtime that loads the WASM module.
Installation
Use meos.js in your project
npm install meos.jsThe published package bundles the compiled WASM module, so you need neither Docker nor a source checkout. The optional deck.gl integration is available under meos.js/deckgl (see DeckGL integration).
Requires a runtime with WebAssembly MEMORY64 (Node.js 22+, or a recent browser). See Requirements.
Develop MEOS.js from source
Only needed to work on MEOS.js itself.
1. Get the WASM module
docker build --output type=local,dest=./wasm --target wasm .This produces wasm/meos.js and wasm/meos.wasm. The first build may take a while as it compiles GEOS, PROJ, SQLite, GSL, JSON-C, and MobilityDB from source.
2. Install dependencies
npm install3. Run the tests
npm testUsing from JavaScript
MEOS.js is written in TypeScript for maintainability but ships as plain JavaScript (ES2022 / ESM) with bundled type declarations. You can use it from any JavaScript project without TypeScript in your toolchain.
npm run build:ts emits dist/core/*.js (the runtime) plus dist/core/*.d.ts (the types). From a plain JS file:
import { initMeos, TsTzSpan } from 'meos.js';
await initMeos();
const span = TsTzSpan.fromString('[2020-01-01, 2021-01-01)');
console.log(span.toString());
span.free();Everything works identically: every class (TBool, TInt, TFloat, TGeomPoint, ...), the factory functions, the using / [Symbol.dispose] lifecycle (ES2023, not TS-specific). The bundled .d.ts files also give you IDE autocompletion and hover-docs in .js files.
The only thing TypeScript users get extra is compile-time type checking at write-time; the runtime surface is the same.
Code Generation
The codegen/ directory contains the generator that produces core/c-src/bindings.c and core/functions/functions.generated.ts from the MEOS API description file (codegen/res/meos-idl.json).
When to regenerate: whenever meos-idl.json is updated (e.g. after a MEOS version upgrade) or whenever FunctionsGenerator.ts / the templates change.
Running the generator
npm run generateThis reads codegen/res/meos-idl.json, applies the templates in codegen/res/, and overwrites both generated files.
Do not edit
bindings.corfunctions.generated.tsmanually: any change will be lost the next time the generator runs. Manual overrides live in the templates (codegen/res/*_header.*.template).
Updating the input file
The canonical meos-idl.json is produced by MEOS-API. To refresh against a newer MEOS surface:
# in a MEOS-API checkout
python setup.py
python run.py
cp output/meos-idl.json /path/to/MEOS.js/codegen/res/meos-idl.json
# back in MEOS.js
npm run generateBump the MOBILITYDB_COMMIT pin in the Dockerfile together with the IDL refresh so the WASM build stays in sync with the bindings.
Tests
Unit tests live in test/ and use Node's built-in test runner with tsx for on-the-fly TypeScript transpilation.
Run all tests
npm testRun a specific test file
node --import tsx/esm --test test/types/boxes/test_TBox.tsRun a specific test by name
node --import tsx/esm --test --test-name-pattern="fromString" test/types/boxes/test_TBox.tsDoc
The API reference is generated by TypeDoc and served by VitePress. The published site lives at https://mobilitydb.github.io/MEOS.js/ and is rebuilt by .github/workflows/docs.yml on every push to main.
Build the API reference only
npm run docs:apiThis invokes TypeDoc with the config in typedoc.json and writes Markdown pages to docs/api/.
Run the docs site locally (with hot reload)
npm run docs:devBuild the static docs site
npm run docs:buildThe output is placed under docs/.vitepress/dist/, which is what the GitHub Pages workflow deploys.
Preview the built site
npm run docs:previewMemory management
Every MEOS.js object wraps a raw pointer allocated in WASM memory. This memory is not managed by the JavaScript garbage collector and must be freed explicitly.
Option 1: manual free()
const span = TsTzSpan.fromString('[2020-01-01, 2021-01-01)');
// ... use span ...
span.free();Option 2: using
All types implement [Symbol.dispose](), so you can use the Explicit Resource Management syntax. The object is freed automatically when the block exits, even if an exception is thrown.
{
using span = TsTzSpan.fromString('[2020-01-01, 2021-01-01)');
console.log(span.toString());
} // span.free() called automatically here
usingrequires TypeScript 5.2+ with"lib": ["ES2022"]or"ESNext"intsconfig.json.
DeckGL integration
MEOS.js ships an optional, framework-free adapter for rendering temporal points with deck.gl's TripsLayer. It lives in two sub-exports so the core library never depends on deck.gl or React:
| Import | Depends on | Contents |
|---|---|---|
| meos.js/deckgl | nothing (only MEOS.js) | adapter + browser-side temporal helpers |
| meos.js/deckgl/layer | @deck.gl/* (peer) | ready-to-use TripsLayer factory |
@deck.gl/core and @deck.gl/geo-layers are declared as optional peer dependencies: installing meos.js does not pull them in. Install them yourself when you use meos.js/deckgl/layer.
Adapter
tgeompointsToTrips converts temporal points into the { path, timestamps } shape TripsLayer expects, placing every trip on a single shared animation clock:
import { initMeos, TGeomPoint } from 'meos.js';
import { tgeompointsToTrips } from 'meos.js/deckgl';
await initMeos();
const trajectories = [/* TGeomPoint, … */];
const { trips, timeOrigin, timeRange } = tgeompointsToTrips(trajectories);
// trips: { path: [lng, lat][]; timestamps: number[] }[]A trajectory with temporal gaps (a sequence set) maps to several paths.
Browser-side temporal logic
The same sub-export wraps common MobilityDB operations so the temporal work runs in the browser instead of on a server:
import { atGeometry, atTime, tripsWithSpeed } from 'meos.js/deckgl';
atGeometry(t, 'POLYGON((…))'); // clip to a zone (tpoint_at_geom)
atTime(t, '[2024-01-15 09:00+00, 2024-01-15 09:10+00]'); // clip to a period (temporal_at_tstzspan)
tripsWithSpeed(t); // trips + per-vertex speed (tpoint_speed)Ready-to-use layer
import { tripsLayerFromTGeompoints } from 'meos.js/deckgl/layer';
const layer = tripsLayerFromTGeompoints(trajectories, {
currentTime,
trailLength: 180,
widthMinPixels: 4,
});The layer renders standalone or interleaved over a MapLibre basemap, so it can sit on top of an existing map.
See the DeckGL guide for the full API.
Use Case Examples
A complete, animated end-to-end demo (MEOS.js → adapter → TripsLayer interleaved over MapLibre, with in-browser zone/time/speed controls) lives in the MEOS.js-examples repository.
