Skip to main content

Simulate Navigation

|

This example demonstrates how to simulate navigation along a calculated route. This is useful for testing navigation features without actually driving or moving.

How it works

The example app demonstrates the following features:

  • Calculate a route between two locations
  • Start navigation simulation along the route
  • Display turn-by-turn instructions
  • Show remaining distance and estimated time of arrival
  • Follow the simulated position on the map

Live Demo

Try the interactive demo below. Click "Build Route" first, then "Start Simulation" to see the navigation in action:

Code Implementation

Starting Navigation Simulation

This code demonstrates how to start navigation simulation after calculating a route:

index.ts
function startSimulation() {
if (!map || !routes || !routes[0]) return;
map.preferences.routes.clearAllButMainRoute?.();
const routesMap = map.preferences.routes;
if (!routesMap.mainRoute) {
showMessage('No main route available');
return;
}
navigationHandler = NavigationService.startSimulation(
routesMap.mainRoute,
undefined,
{
onNavigationInstruction: (instruction: NavigationInstruction) => {
isSimulationActive = true;
currentInstruction = instruction;
updateUI();
},
onError: (error: GemError) => {
isSimulationActive = false;
cancelRoute();
if (error !== GemError.cancel) {
stopSimulation();
}
}
}
);
map.startFollowingPosition?.();
isSimulationActive = true;
updateUI();
showMessage("Simulation started");
}

function stopSimulation() {
if (navigationHandler) {
NavigationService.cancelNavigation(navigationHandler);
navigationHandler = null;
}
cancelRoute();
isSimulationActive = false;
areRoutesBuilt = false;
updateUI();
showMessage("Simulation stopped");
}

Handling Navigation Updates

Process navigation instructions and update the UI:

index.ts
function onNavigationUpdate(instruction: NavigationInstruction) {
currentInstruction = instruction;

// Display the current instruction
displayInstruction(instruction);

// Update remaining distance and time
updateRemainingInfo(instruction);

// Update current location on map
if (instruction.currentPosition) {
updateMapPosition(instruction.currentPosition);
}
}

function displayInstruction(instruction: NavigationInstruction) {
const text = instruction.text || 'Continue on route';
const distance = getFormattedDistanceToNextTurn(instruction);

console.log(`Instruction: ${text} in ${distance}`);

// Update UI panel with instruction
updateInstructionPanel(text, distance);
}

function updateRemainingInfo(instruction: NavigationInstruction) {
const remainingDistance = getFormattedRemainingDistance(instruction);
const remainingDuration = getFormattedRemainingDuration(instruction);
const eta = getFormattedETA(instruction);

console.log(`Remaining: ${remainingDistance}, ${remainingDuration}, ETA: ${eta}`);

// Update UI with remaining info
updateBottomPanel(remainingDistance, remainingDuration, eta);
}

Formatting Navigation Data

Utility functions to format distance, duration, and time:

index.ts
function getFormattedDistanceToNextTurn(instr: NavigationInstruction): string {
const td = instr.timeDistanceToNextTurn;
const total = td.unrestrictedDistanceM + td.restrictedDistanceM;
return convertDistance(total);
}

function getFormattedRemainingDistance(instr: NavigationInstruction): string {
const td = instr.remainingTravelTimeDistance;
const total = td.unrestrictedDistanceM + td.restrictedDistanceM;
return convertDistance(total);
}

function getFormattedRemainingDuration(instr: NavigationInstruction): string {
const td = instr.remainingTravelTimeDistance;
const total = td.unrestrictedTimeS + td.restrictedTimeS;
return convertDuration(total);
}

function getFormattedETA(instr: NavigationInstruction): string {
const td = instr.remainingTravelTimeDistance;
const totalSeconds = td.unrestrictedTimeS + td.restrictedTimeS;
const now = new Date();
const eta = new Date(now.getTime() + totalSeconds * 1000);
return eta.toTimeString().slice(0, 5);
}

function convertDistance(meters: number): string {
if (meters >= 1000) {
return `${(meters / 1000).toFixed(1)} km`;
}
return `${meters} m`;
}

function convertDuration(seconds: number): string {
const hours = Math.floor(seconds / 3600);
const minutes = Math.floor((seconds % 3600) / 60);
return (hours > 0 ? `${hours} h ` : '') + `${minutes} min`;
}

UI Panels

Create instruction and info panels:

index.ts
function showInstructionPanel(instruction: NavigationInstruction) {
let panel = document.getElementById('instruction-panel');

if (!panel) {
panel = document.createElement('div');
panel.id = 'instruction-panel';
panel.style.cssText = `
position: fixed; top: 80px; left: 50%; transform: translateX(-50%);
background: white; padding: 20px; border-radius: 12px;
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
z-index: 2000; min-width: 300px; text-align: center;
`;
document.body.appendChild(panel);
}

const text = instruction.text || 'Continue on route';
const distance = getFormattedDistanceToNextTurn(instruction);

panel.innerHTML = `
<div style="font-size: 1.2em; font-weight: 600; margin-bottom: 8px;">
${text}
</div>
<div style="color: #666; font-size: 1em;">
in ${distance}
</div>
`;
}

function showBottomPanel(instruction: NavigationInstruction) {
let panel = document.getElementById('bottom-panel');

if (!panel) {
panel = document.createElement('div');
panel.id = 'bottom-panel';
panel.style.cssText = `
position: fixed; bottom: 20px; left: 50%; transform: translateX(-50%);
background: white; padding: 16px 24px; border-radius: 12px;
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
z-index: 2000; display: flex; gap: 24px;
`;
document.body.appendChild(panel);
}

const distance = getFormattedRemainingDistance(instruction);
const duration = getFormattedRemainingDuration(instruction);
const eta = getFormattedETA(instruction);

panel.innerHTML = `
<div>
<div style="color: #666; font-size: 0.85em;">Distance</div>
<div style="font-weight: 600; font-size: 1.1em;">${distance}</div>
</div>
<div>
<div style="color: #666; font-size: 0.85em;">Duration</div>
<div style="font-weight: 600; font-size: 1.1em;">${duration}</div>
</div>
<div>
<div style="color: #666; font-size: 0.85em;">ETA</div>
<div style="font-weight: 600; font-size: 1.1em;">${eta}</div>
</div>
`;
}

Key Features

  • Simulation Mode: Test navigation without real GPS movement
  • Turn-by-Turn Instructions: Clear visual and text-based navigation guidance
  • Real-Time Updates: Distance, duration, and ETA update as simulation progresses
  • Follow Mode: Map automatically follows the simulated position
  • Easy Controls: Start and stop simulation with simple button clicks

Next Steps