Skip to main content

Getting started with Navigation

Last updated: April 7, 2026 | 4 minutes read

The Navigation module provides comprehensive turn-by-turn navigation capabilities, enabling real-time guidance along calculated routes. It includes features such as voice instructions, automatic route recalculation, lane guidance, and speed limit warnings.

Starting navigation

To start navigation, you first need a calculated route:

import {
NavigationService,
NavigationInstruction,
NavigationInstructionUpdateEvents,
GemError,
Landmark,
Route,
TaskHandler,
} from '@magiclane/maps-sdk';

let navigationHandler: TaskHandler | null = null;

function startNavigation() {
const routesMap = map.preferences.routes;
if (!routesMap.mainRoute) {
console.log('No main route available');
return;
}

navigationHandler = NavigationService.startNavigation(routesMap.mainRoute, undefined, {
onNavigationInstruction: (
instruction: NavigationInstruction,
events: Set<NavigationInstructionUpdateEvents>
) => {
console.log('Next turn:', instruction.nextTurnInstruction);
console.log('Distance to next turn:', instruction.timeDistanceToNextTurn.totalDistanceM, 'm');
updateNavigationUI(instruction);
},
onTextToSpeechInstruction: (textInstruction: string) => {
// Use browser TTS or custom audio to speak the instruction
const utterance = new SpeechSynthesisUtterance(textInstruction);
speechSynthesis.speak(utterance);
},
onWaypointReached: (landmark: Landmark) => {
console.log('Reached waypoint:', landmark.name);
},
onDestinationReached: (landmark: Landmark) => {
console.log('Arrived at destination:', landmark.name);
stopNavigation();
},
onRouteUpdated: (route: Route) => {
console.log('Route was recalculated');
},
onError: (error: GemError) => {
console.error('Navigation error:', error);
stopNavigation();
},
});

map.startFollowingPosition();
}

The startNavigation and startSimulation methods accept an options object with multiple callback functions:

CallbackDescription
onNavigationInstructionPeriodic updates (typically every second) with the current NavigationInstruction and a set of NavigationInstructionUpdateEvents
onNavigationStartedCalled when navigation has started
onTextToSpeechInstructionProvides text strings meant for voice guidance (TTS)
onWaypointReachedCalled when an intermediate waypoint is reached
onDestinationReachedCalled when the final destination is reached
onRouteUpdatedCalled when the route is recalculated (e.g., after a deviation)
onBetterRouteDetectedCalled when a faster alternative route is detected
onBetterRouteRejectedCalled when a better route is rejected
onErrorCalled when navigation fails with a GemError
onNotifyStatusChangeCalled when navigation status changes
onRouteCalculationStartedCalled when route recalculation begins
onRouteCalculationCompletedCalled when route recalculation completes

Simulation mode

For testing navigation without actual GPS movement, use startSimulation:

import { NavigationService, Route, TaskHandler } from '@magiclane/maps-sdk';

let simulationHandler: TaskHandler | null = null;

function startSimulation(route: Route) {
simulationHandler = NavigationService.startSimulation(route, undefined, {
onNavigationInstruction: (instruction, events) => {
console.log('Simulated instruction:', instruction.nextTurnInstruction);
updateNavigationUI(instruction);
},
onDestinationReached: (landmark) => {
console.log('Simulation complete');
stopNavigation();
},
onError: (error) => {
console.error('Simulation error:', error);
},
});

map.startFollowingPosition();
}
Note

The startSimulation method accepts the same callbacks as startNavigation. The second parameter is a deprecated callback — pass undefined for it.

Stopping navigation

When navigation is complete or cancelled:

function stopNavigation() {
if (navigationHandler) {
NavigationService.cancelNavigation(navigationHandler);
navigationHandler = null;
}

map.stopFollowingPosition();
console.log('Navigation stopped');
}

Example of integrating navigation with a map view:

import {
GemMap,
NavigationService,
NavigationInstruction,
NavigationInstructionUpdateEvents,
GemAnimation,
AnimationType,
GemError,
Route,
TaskHandler,
} from '@magiclane/maps-sdk';

let navigationHandler: TaskHandler | null = null;

function setupNavigationUI(gemMap: GemMap, route: Route) {
navigationHandler = NavigationService.startNavigation(route, undefined, {
onNavigationInstruction: (
instruction: NavigationInstruction,
events: Set<NavigationInstructionUpdateEvents>
) => {
// Update instruction panel
const nextTurn = instruction.nextTurnInstruction;
const distanceToTurn = instruction.timeDistanceToNextTurn.totalDistanceM;
const remainingDistance = instruction.remainingTravelTimeDistance.totalDistanceM;
const remainingTime = instruction.remainingTravelTimeDistance.totalTimeS;

document.getElementById('instruction-text')!.textContent = nextTurn;
document.getElementById('instruction-distance')!.textContent = `${Math.round(distanceToTurn)}m`;
document.getElementById('distance-remaining')!.textContent =
`${(remainingDistance / 1000).toFixed(1)} km`;
document.getElementById('time-remaining')!.textContent =
`${Math.round(remainingTime / 60)} min`;
},
onTextToSpeechInstruction: (textInstruction: string) => {
const utterance = new SpeechSynthesisUtterance(textInstruction);
speechSynthesis.speak(utterance);
},
onDestinationReached: () => {
console.log('Arrived at destination!');
stopNavigation();
},
onError: (error: GemError) => {
console.error('Navigation error:', error);
stopNavigation();
},
});

// Follow user position
gemMap.startFollowingPosition({
animation: new GemAnimation({ type: AnimationType.linear, duration: 1000 }),
});
}

function stopNavigation() {
if (navigationHandler) {
NavigationService.cancelNavigation(navigationHandler);
navigationHandler = null;
}
}

Next Steps

Now that you understand navigation basics, you can explore: