Skip to main content

Overview

The routing engine provides multi-route comparison with traffic-adjusted durations, polyline decoding, and interactive route visualization using Leaflet and the Leaflet Routing Machine plugin.

Route Calculation

API Integration

Routes are fetched from the backend API with origin, destination, and travel mode:
navigation/findBestRoute/map.js
const apiUrl = `${pythonURI}/api/get_routes`;

const response = await fetch(apiUrl, {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ origin, destination, mode }),
});

const routes = await response.json();

Input Validation

The system validates user input before making API requests:
navigation/findBestRoute/map.js
if (!origin || !destination) {
  alert('Please enter both origin and destination.');
  return;
}

Polyline Decoding

Routes are encoded using Google’s polyline algorithm and decoded for rendering:
navigation/findBestRoute/map.js
import polyline from 'https://cdn.skypack.dev/@mapbox/polyline';

if (route.geometry) {
  const decoded = polyline.decode(route.geometry);
  const polylineLayer = L.polyline(decoded, {
    color: idx === 0 ? 'blue' : 'gray',
    weight: 4,
    opacity: 0.8,
  }).addTo(map);
  polylines.push(polylineLayer);
  if (idx === 0) map.fitBounds(polylineLayer.getBounds());
}
The first route (index 0) is highlighted in blue, while alternative routes appear in gray.

Multi-Route Comparison

The system displays multiple route options with traffic-adjusted durations:
navigation/findBestRoute/map.js
routes.forEach((route, idx) => {
  const header = document.createElement('h4');
  header.textContent = `Route ${idx + 1} - ${route.total_distance} - Est. Time: ${route.traffic_adjusted_duration || route.total_duration}`;
  resultDiv.appendChild(header);

  const ul = document.createElement('ul');
  route.details.forEach(step => {
    const li = document.createElement('li');
    li.innerHTML = `${step.instruction} - ${step.distance} (${step.duration})`;
    ul.appendChild(li);
  });
  resultDiv.appendChild(ul);
});

Route Response Structure

{
  "total_distance": "5.2 miles",
  "total_duration": "15 minutes",
  "traffic_adjusted_duration": "22 minutes",
  "geometry": "encoded_polyline_string",
  "details": [
    {
      "instruction": "Turn left onto Main St",
      "distance": "0.5 miles",
      "duration": "2 minutes"
    }
  ]
}

Polyline Layer Management

Layers are stored and cleared between route requests:
navigation/findBestRoute/map.js
let polylines = [];

// Clear previous routes
polylines.forEach(p => map.removeLayer(p));
polylines = [];

// Add new routes
const polylineLayer = L.polyline(decoded, {
  color: idx === 0 ? 'blue' : 'gray',
  weight: 4,
  opacity: 0.8,
}).addTo(map);
polylines.push(polylineLayer);

Leaflet Routing Machine

For the daily routine feature, Leaflet Routing Machine provides turn-by-turn directions:
navigation/dailyroutine.html
currentRouteControl = L.Routing.control({
  waypoints: [startLatLng, destLatLng],
  routeWhileDragging: false,
  addWaypoints: false,
  draggableWaypoints: false,
  show: false
}).addTo(map);

map.fitBounds(L.latLngBounds([startLatLng, destLatLng]), { padding: [50, 50] });

Geocoding for Routing

Location names are converted to coordinates using Nominatim:
navigation/dailyroutine.html
async function geocodePlace(place) {
  const response = await fetch(`https://nominatim.openstreetmap.org/search?format=json&q=${encodeURIComponent(place)}`);
  const data = await response.json();
  return data[0];
}

async function drawRoute(startName, destName) {
  try {
    const [start, dest] = await Promise.all([
      geocodePlace(startName),
      geocodePlace(destName)
    ]);

    if (!start || !dest) {
      alert("Could not find one or both locations.");
      return;
    }

    const startLatLng = L.latLng(parseFloat(start.lat), parseFloat(start.lon));
    const destLatLng = L.latLng(parseFloat(dest.lat), parseFloat(dest.lon));

    if (currentRouteControl) map.removeControl(currentRouteControl);

    currentRouteControl = L.Routing.control({
      waypoints: [startLatLng, destLatLng],
      routeWhileDragging: false,
      addWaypoints: false,
      draggableWaypoints: false,
      show: false
    }).addTo(map);

    map.fitBounds(L.latLngBounds([startLatLng, destLatLng]), { padding: [50, 50] });

  } catch (error) {
    console.error("Routing error:", error);
    alert("An error occurred while displaying the route.");
  }
}

Auto-Fit to Route Bounds

The map automatically adjusts to show the entire route:
navigation/findBestRoute/map.js
if (idx === 0) map.fitBounds(polylineLayer.getBounds());
With Leaflet Routing Machine:
navigation/dailyroutine.html
map.fitBounds(L.latLngBounds([startLatLng, destLatLng]), { padding: [50, 50] });

Error Handling

if (!Array.isArray(routes)) {
  resultDiv.innerHTML = `<p>Error: ${routes.error || 'No routes found'}</p>`;
  return;
}

Route Control Management

Previous route controls are removed before adding new ones:
navigation/dailyroutine.html
let currentRouteControl = null;

if (currentRouteControl) map.removeControl(currentRouteControl);

currentRouteControl = L.Routing.control({
  // ... configuration
}).addTo(map);

Best Practices

Always remove old polylines before adding new ones to prevent memory leaks:
polylines.forEach(p => map.removeLayer(p));
polylines = [];
Differentiate primary and alternative routes:
color: idx === 0 ? 'blue' : 'gray',
weight: 4,
opacity: 0.8,
Use Promise.all to geocode multiple locations simultaneously:
const [start, dest] = await Promise.all([
  geocodePlace(startName),
  geocodePlace(destName)
]);

Map Integration

Learn about Leaflet setup and map configuration

Location Manager

Manage location storage and geocoding

Build docs developers (and LLMs) love