Skip to content

MobilityDB/MEOS.js

Repository files navigation

MEOS.js

npm version docs WebAssembly Memory64

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.js

Documentation · Examples · MobilityDB

Requires a runtime with WebAssembly MEMORY64 (Node.js 22+, or a recent browser).

Table of contents

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.wasm is 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.json

The two-layer architecture consists of:

  • codegen/: reads codegen/res/meos-idl.json and generates core/c-src/bindings.c and core/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.js

The 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 install

3. Run the tests

npm test

Using 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 generate

This reads codegen/res/meos-idl.json, applies the templates in codegen/res/, and overwrites both generated files.

Do not edit bindings.c or functions.generated.ts manually: 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 generate

Bump 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 test

Run a specific test file

node --import tsx/esm --test test/types/boxes/test_TBox.ts

Run a specific test by name

node --import tsx/esm --test --test-name-pattern="fromString" test/types/boxes/test_TBox.ts

Doc

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:api

This 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:dev

Build the static docs site

npm run docs:build

The output is placed under docs/.vitepress/dist/, which is what the GitHub Pages workflow deploys.

Preview the built site

npm run docs:preview

Memory 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

using requires TypeScript 5.2+ with "lib": ["ES2022"] or "ESNext" in tsconfig.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.

About

JavaScript binding for the MEOS spatiotemporal library, targeting browsers via WebAssembly and Node.js

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors