NavCore SDK Documentation
The navigation intelligence layer that plugs into your renderer and keeps Core and Pro behavior explicit.
Installation
Install the engine first. Add only the adapters, simulator, or provider package your app actually uses.
# Required engine
npm install @ingissa/navcore-core
# Optional packages
npm install @ingissa/navcore-mapbox # Mapbox Directions provider
npm install @ingissa/navcore-maplibre # MapLibre GL renderer adapter
npm install @ingissa/navcore-leaflet # Leaflet renderer adapter
npm install @ingissa/navcore-google-maps # Google Maps JS renderer adapter
npm install @ingissa/navcore-react-native # React Native useNavCore hook
npm install @ingissa/navcore-headless # Node.js / CI adapter
npm install @ingissa/navcore-simulator # GPS simulation and replay
npm install @ingissa/navcore-types # Shared ecosystem types@ingissa/navcore-core1.0.4The full NavCore orchestrator, Core subpath, Pro subpath, directions providers, and license manager.
@ingissa/navcore-mapbox1.0.4Mapbox Directions provider for token-based routing and waypoint chunking workflows.
@ingissa/navcore-react-native1.0.4React Native bindings with the useNavCore hook and NavCoreProvider lifecycle helpers.
@ingissa/navcore-simulator1.0.4GPS route simulator with replay, noise, scenarios, fleet simulation, and dev/CI entrypoints.
@ingissa/navcore-maplibre1.0.0Renderer adapter for MapLibre GL JS. Handles vehicle marker, route layer, and camera panning.
@ingissa/navcore-leaflet1.0.0Renderer adapter for Leaflet.js with route drawing, marker updates, and camera following.
@ingissa/navcore-google-maps1.0.0Renderer adapter for the Google Maps JavaScript API with marker and polyline support.
@ingissa/navcore-headless1.0.0Headless renderer for Node.js and CI assertions without a browser or visual map.
@ingissa/navcore-types1.0.1Shared TypeScript type definitions for SDK ecosystem integrations.
@ingissa/navcore-core. Mapbox Directions is provided by @ingissa/navcore-mapbox.Core vs Pro
| Tier | Import | Includes |
|---|---|---|
| Core | @ingissa/navcore-core/core | Geo utilities, KalmanFilter2D, RouteSnapper, core directions, CustomRouteBuilder, renderer types, LicenseManager |
| Pro | @ingissa/navcore-core/pro | DeadReckoningEngine, DeviationDetector, ParallelRoadResolver, ETAEngine, VoiceTriggerEngine, GeofencingEngine, InstructionEditor |
| Full engine | @ingissa/navcore-core | NavCore orchestrator. Core features work without a key; Pro features unlock from licenseKey at runtime. |
isDev constructor option in the current SDK. Use new NavCore() for Core behavior or new NavCore({ licenseKey }) for licensed Pro behavior (SDK v1.0.4).Quick Start
Get a route, create the engine, then feed GPS updates.
import { NavCore } from '@ingissa/navcore-core';
import { OSRMDirectionsProvider } from '@ingissa/navcore-core/core';
const provider = new OSRMDirectionsProvider({
baseUrl: 'http://router.project-osrm.org',
});
const route = await provider.getRoute([
[2.3522, 48.8566],
[2.3009, 48.8741],
]);
const engine = new NavCore({
licenseKey: process.env.NAVCORE_LICENSE_KEY, // optional for Core-only use
});
engine.setRoute(route.geometry);
engine.startNavigation();
engine.on('instruction', (instruction) => console.log(instruction.text));
engine.on('deviation', ({ anchorIndex }) => console.log('Off route at', anchorIndex));
engine.on('arrival', () => console.log('Arrived'));
navigator.geolocation.watchPosition(({ coords }) => {
const state = engine.update({
coord: [coords.longitude, coords.latitude],
accuracy: coords.accuracy,
bearing: coords.heading,
speed: coords.speed ?? 0,
timestamp: Date.now(),
});
console.log(state.snappedCoord, state.distanceToDestination, state.licenseStatus);
});Engine API
const engine = new NavCore(options?: NavCoreOptions);
engine.setRoute(route: Coordinate[], instructions?: NavInstruction[]): void
engine.startNavigation(): void
engine.update(gps: NavGpsUpdate): NavCoreState
engine.tick(nowMs: number): NavCoreState
engine.getState(): NavCoreState
engine.getRoute(): ReadonlyArray<Coordinate>
engine.getInstructions(): ReadonlyArray<NavInstruction>
engine.getLicenseManager(): LicenseManager
engine.getOffRouteFSM(): OffRouteFSM
engine.getLifecycleFSM(): NavigationFSM
engine.on(event, handler): void
engine.off(event, handler): void
engine.destroy(): voidState Snapshot
interface NavCoreState {
snappedCoord: Coordinate | null;
rawGpsCoord: Coordinate | null;
bearing: number;
rawGpsBearing: number | null;
routeIndex: number;
distanceToRoute: number;
corridor: number;
isOffRoute: boolean;
progressIndex: number;
hasArrived: boolean;
offRouteState: string;
lifecycleState: string;
distanceToDestination: number | null;
distanceToNextInstruction: number | null;
nextInstructionIndex: number | null;
currentSpeed: number;
isGpsStale: boolean;
gpsAgeMs: number;
licenseStatus: 'VALID' | 'INVALID' | 'EXPIRED' | 'MISSING';
}Renderer Adapters
Renderer adapters consume NavCoreState. The engine does not emit an update event; call adapter methods after engine.update() or from your app state loop.
import maplibregl from 'maplibre-gl';
import { NavCore } from '@ingissa/navcore-core';
import { MapLibreAdapter } from '@ingissa/navcore-maplibre';
const engine = new NavCore({ licenseKey: process.env.NAVCORE_LICENSE_KEY });
const adapter = new MapLibreAdapter(map);
engine.setRoute(route.geometry, instructions);
adapter.drawRoute(route.geometry, { color: '#7c3aed', width: 5 });
engine.startNavigation();
function onGps(coords: GeolocationCoordinates) {
const state = engine.update({
coord: [coords.longitude, coords.latitude],
accuracy: coords.accuracy,
bearing: coords.heading,
speed: coords.speed ?? 0,
timestamp: Date.now(),
});
if (!state.snappedCoord) return;
adapter.updateVehicle(state.snappedCoord, state.bearing, state);
adapter.panCamera(state.snappedCoord, state.bearing, { zoom: 15, pitch: 45 });
}Headless Testing
import { HeadlessAdapter } from '@ingissa/navcore-headless';
const adapter = new HeadlessAdapter();
adapter.drawRoute(route.geometry);
for (const gps of gpsTrace) {
const state = engine.update(gps);
if (state.snappedCoord) {
adapter.updateVehicle(state.snappedCoord, state.bearing, state);
}
}
adapter.assertArrived();
adapter.assertNeverOffRoute();
adapter.assertReached(route.geometry.at(-1)!, 30);Directions Providers
| Provider | Package | Typical use |
|---|---|---|
| OSRM | @ingissa/navcore-core/core | Self-hosted or public OSRM routing |
| Valhalla | @ingissa/navcore-core/core | Self-hosted Valhalla routing with costing models |
| OpenRouteService | @ingissa/navcore-core/core | Hosted ORS routing with API key |
| Mapbox Directions | @ingissa/navcore-mapbox | Mapbox token-based routing and waypoint chunking |
import {
OSRMDirectionsProvider,
ValhallaDirectionsProvider,
OpenRouteServiceProvider,
} from '@ingissa/navcore-core/core';
import { MapboxDirectionsProvider } from '@ingissa/navcore-mapbox';
const osrm = new OSRMDirectionsProvider({ baseUrl: 'http://router.project-osrm.org' });
const valhalla = new ValhallaDirectionsProvider({ baseUrl: 'https://valhalla1.openstreetmap.de' });
const ors = new OpenRouteServiceProvider({ apiKey: process.env.ORS_API_KEY });
const mapbox = new MapboxDirectionsProvider({ accessToken: process.env.MAPBOX_TOKEN });Pro Modules
ETA and Voice
import { ETAEngine, VoiceTriggerEngine } from '@ingissa/navcore-core/pro';
const eta = new ETAEngine({ speedWindowSize: 6 });
const voice = new VoiceTriggerEngine({ earlyTriggerMeters: 120, lateTriggerMeters: 10 });
voice.setInstructions(instructions);
const state = engine.update(gps);
const etaResult = eta.update(state);
const cue = voice.update(state);
if (etaResult.isReliable) {
console.log(Math.ceil(etaResult.etaSeconds / 60), 'min');
}
if (cue) {
speechSynthesis.speak(new SpeechSynthesisUtterance(cue.text));
}Geofencing
import { GeofencingEngine } from '@ingissa/navcore-core/pro';
const geo = new GeofencingEngine({ dwellThresholdMs: 10_000 });
geo.addCircle('school-zone', 'School Zone', [2.3522, 48.8566], 50);
geo.addPolygon('exam-area', 'Exam Area', [
[2.34, 48.85],
[2.36, 48.85],
[2.36, 48.87],
[2.34, 48.87],
]);
const state = engine.update(gps);
if (state.snappedCoord) {
const events = geo.update(state.snappedCoord);
for (const event of events) {
console.log(event.type, event.name, event.dwellMs);
}
}Custom Routes & Instructions
CustomRouteBuilder
import { CustomRouteBuilder } from '@ingissa/navcore-core/core';
const builder = new CustomRouteBuilder();
builder.addWaypoint([2.3522, 48.8566], { name: 'Start' });
builder.addWaypoint([2.3400, 48.8650], { name: 'Midpoint' });
builder.addWaypoint([2.3009, 48.8741], { name: 'End' });
const chunks = builder.chunk('OVERLAP_1', 25);
const results = await Promise.all(chunks.map((chunk) => provider.getRoute(chunk)));
const geometry = results.flatMap((result, index) =>
index === 0 ? result.geometry : result.geometry.slice(1)
);
engine.setRoute(geometry);InstructionEditor
import { InstructionEditor } from '@ingissa/navcore-core/pro';
const editor = new InstructionEditor();
editor
.addDepart(geometry[0], 'Start navigation')
.addTurn(geometry[50], 'Turn left onto Main St', 50)
.addExamPoint(geometry[80], 'Check mirrors', 80, { severity: 'warning' })
.addArrive(geometry[geometry.length - 1]);
editor.attachToCumulative(geometry);
editor.sort();
engine.setRoute(geometry, editor.toArray());React Native
import { useNavCore } from '@ingissa/navcore-react-native';
const nav = useNavCore({
gpsCoord,
gpsBearing,
gpsAccuracy,
gpsTimestamp,
speed,
routeGeometry,
instructions,
isNavigating,
options: {
licenseKey: process.env.EXPO_PUBLIC_NAVCORE_LICENSE_KEY,
isCircuit: false,
},
onDeviation: ({ anchorIndex }) => requestNewRoute(anchorIndex),
onInstruction: (instruction) => speak(instruction.text),
onArrival: () => showArrivalScreen(),
});
if (nav.snappedCoord) {
mapRef.current?.animateCamera({
center: {
latitude: nav.snappedCoord[1],
longitude: nav.snappedCoord[0],
},
heading: nav.bearing,
});
}Examples
The examples repository covers basic navigation, custom routes, instruction editing, renderer adapters, directions providers, voice, ETA, geofencing, parallel-road resolution, headless testing, and full Expo navigation screens.
Recommended first reads
- 01 Basic Navigation: Core engine plus OSRM route loading.
- 04 MapLibre Web: Browser rendering with MapLibre.
- 08 ETA Engine: Licensed ETA computation from NavCoreState.
- 11 Headless Testing: CI assertions without a map.
- 12 Expo Complete Navigation: Mobile screens for multiple renderer stacks.
Ready to integrate?
Start with Core, then move to Pro when your app needs licensed navigation intelligence.