Quick start

Pick the adapter for your map library, hand the tracker a stream of positions, and call start(). You will have smooth motion in three lines — the rest has production defaults.

Installation

Requires Node.js ≥ 20. Install the core plus the adapter for your map. Each package ships ESM + CJS and full type definitions.

You're usingInstallPeer
OpenLayersnpm i @kinesisjs/core @kinesisjs/openlayersol ≥ 8
Leafletnpm i @kinesisjs/core @kinesisjs/leafletleaflet ≥ 1.7
Angularnpm i @kinesisjs/angular @kinesisjs/core@angular/core ≥ 17
Core onlynpm i @kinesisjs/corenone

Vanilla TypeScript + OpenLayers

Build the map, construct an adapter, wrap it in a Tracker, then feed positions from your socket. The Position objects need only an id, lng and lat.

live-map.tsts
import Map from 'ol/Map';
import View from 'ol/View';
import { Tracker } from '@kinesisjs/core';
import { OpenLayersAdapter, createVehicleStyle } from '@kinesisjs/openlayers';

const map = new Map({ target: 'map', view: new View({ center: [29, 41], zoom: 11 }) });

const tracker = new Tracker({
adapter: new OpenLayersAdapter(map, {
  style: createVehicleStyle({ icon: '/car.png', iconScale: 0.7 }),
}),
interpolation: 'adaptive',
ingestThrottle: 100,
});

tracker.start();

const ws = new WebSocket('wss://your-backend/vehicles');
ws.onmessage = (e) => tracker.ingest(JSON.parse(e.data));

Leaflet

The Leaflet adapter is a drop-in swap. Two things to remember:

  • Import Leaflet's stylesheet: import 'leaflet/dist/leaflet.css'.
  • You still pass { lng, lat } in your positions — the adapter swaps to Leaflet's [lat, lng] order for you.
leaflet.tsts
import L from 'leaflet';
import 'leaflet/dist/leaflet.css';
import { Tracker } from '@kinesisjs/core';
import { LeafletAdapter } from '@kinesisjs/leaflet';

const map = L.map('map').setView([41, 29], 11); // Leaflet uses [lat, lng]
L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png').addTo(map);

const tracker = new Tracker({
adapter: new LeafletAdapter(map, { warningOpacity: 0.35 }),
interpolation: 'adaptive',
});
tracker.start();

Angular

The Angular wrapper hides the manual wiring behind a standalone kinesisMap directive. Bind a Signal or Observable of positions; teardown is handled by DestroyRef.

live-map.component.tsts
import { Component, inject } from '@angular/core';
import { KinesisMapDirective } from '@kinesisjs/angular';

@Component({
standalone: true,
imports: [KinesisMapDirective],
template: `<div kinesisMap [positions]="positions" [interpolation]="'adaptive'"
                [center]="[29, 41]" [zoom]="11" class="map"></div>`,
})
export class LiveMapComponent {
positions = inject(FeedService).positions; // Signal<Position[]>
}

Cleanup

In a plain setup, call tracker.destroy() when the view goes away — it clears slots, tells the adapter to tear down its features, and removes listeners.

cleanup.tsts
window.addEventListener('beforeunload', () => {
tracker.destroy();
map.dispose();
});
Angular

With the directive you don't call destroy() — it is wired to the component's DestroyRef automatically.