Delivery Tracking
Intermediate
SmartMaps GL
Marker
Popup
flyTo Animation
This use-case demo showcases a real-time delivery tracking interface, similar to those found in modern e-commerce and logistics apps. It displays the current package status with a visual progress bar, an estimated delivery window, and a live vehicle position that moves along a route on the map toward the delivery destination.
Features & APIs
- SmartMaps GL Map -- Renders an interactive map using
smartmapsgl.Mapwith theessentialstyle - Custom Markers -- Uses
smartmapsgl.Markerwith custom HTML elements for both the delivery vehicle and the destination (home) icon - Animated Vehicle Movement -- Simulates real-time delivery progress by updating the vehicle marker position every 5 seconds across a series of waypoints
- Fly-To Animation -- Smoothly pans and zooms the map to follow the vehicle using
map.flyTo() - Popup on Arrival -- Displays a
smartmapsgl.Popupmessage when the vehicle reaches the destination - Delivery Progress Bar -- A visual progress indicator that advances in sync with the vehicle movement, transitioning through order stages (Ordered, Shipped, In Delivery, Delivered)
- Maki Icon Integration -- Loads SVG icons from the SmartMaps CDN (
cdn.smartmaps.cloud/packages/maki/icons/) for marker styling
How it works
The demo simulates delivery progress by cycling through a predefined list of GPS waypoints. Every 5 seconds, the vehicle marker jumps to the next waypoint, the camera smoothly pans to follow via flyTo, and the progress bar advances. When the final waypoint is reached, the UI switches to a "delivered" state with a popup confirmation.
Code
// =============================================================
// DELIVERY TRACKING DEMO
// Simulates a real-time delivery experience with a vehicle marker
// that moves through a series of waypoints toward a destination.
// The progress bar and status text update in sync.
//
// APIs used:
// - SmartMaps GL JS (Map, Marker, Popup, flyTo)
// =============================================================
// --- CONFIGURATION & CONSTANTS ---
const TRACKING_ZOOM = 15; // Zoom level when following the vehicle
const FLY_TO_SPEED = 0.8; // flyTo animation speed
const UPDATE_INTERVAL_MS = 5000; // Milliseconds between location updates
const PROGRESS_INCREMENT = 5; // Percentage points per update step
const INITIAL_PROGRESS = 75; // Starting progress (3 of 4 stages complete)
// --- MAP INITIALIZATION ---
const map = new smartmapsgl.Map({
apiKey:
"[INSERT API-KEY]",
container: "map",
style: "essential",
center: [8.438, 49.018], // Karlsruhe city center
zoom: 13,
});
// --- ROUTE DATA ---
// Simulated waypoints the delivery vehicle will pass through
const locations = [
[8.42982, 49.01394],
[8.4354, 49.01523],
[8.44042, 49.02009],
[8.43828, 49.02101],
[8.43933, 49.02166], // Final destination
];
// --- HELPER FUNCTIONS ---
/** Update all UI elements to show the delivery-complete state. */
function showDeliveryComplete(progressBar) {
document.getElementById("update-banner").innerText =
"Zustellung erfolgt!";
document.getElementById("delivery-status-heading").innerText =
"Erfolgreich zugestellt";
progressBar.style.width = "100%";
// Activate the final progress dot
const deliveredDot = document.getElementById("status-dot-delivered");
deliveredDot.classList.remove("bg-gray-300");
deliveredDot.classList.add("bg-blue-600");
// Show arrival popup at the destination
new smartmapsgl.Popup({ closeButton: false, offset: 35 })
.setText("Sie sind da!")
.setLngLat(locations[locations.length - 1])
.addTo(map);
}
/** Move the vehicle marker and camera to the given location. */
function moveVehicleTo(vehicleMarker, location) {
vehicleMarker.setLngLat(location);
map.flyTo({
center: location,
zoom: TRACKING_ZOOM,
speed: FLY_TO_SPEED,
});
}
// --- MAP SETUP ---
let vehicleMarker;
map.on("load", () => {
// Create destination marker (red border, home icon)
const destinationEl = document.createElement("div");
destinationEl.className = "destination-marker";
const destinationIcon = document.createElement("img");
destinationIcon.src =
"https://cdn.smartmaps.cloud/packages/maki/icons/home.svg";
destinationEl.appendChild(destinationIcon);
new smartmapsgl.Marker({ element: destinationEl })
.setLngLat(locations[locations.length - 1])
.addTo(map);
// Create vehicle marker (green border, bus icon)
const vehicleEl = document.createElement("div");
vehicleEl.className = "vehicle-marker";
const vehicleIcon = document.createElement("img");
vehicleIcon.src =
"https://cdn.smartmaps.cloud/packages/maki/icons/bus.svg";
vehicleEl.appendChild(vehicleIcon);
vehicleMarker = new smartmapsgl.Marker({ element: vehicleEl })
.setLngLat(locations[0])
.addTo(map);
// --- LOCATION UPDATE LOOP ---
const progressBar = document.getElementById("progress-bar-fill");
let locationIndex = 0;
let progressPercent = INITIAL_PROGRESS;
progressBar.style.width = `${progressPercent}%`;
const intervalId = setInterval(() => {
locationIndex++;
// Check if the vehicle has reached the destination
if (locationIndex >= locations.length) {
clearInterval(intervalId);
showDeliveryComplete(progressBar);
return;
}
// Move vehicle and update progress bar
moveVehicleTo(vehicleMarker, locations[locationIndex]);
progressPercent += PROGRESS_INCREMENT;
progressBar.style.width = `${progressPercent}%`;
}, UPDATE_INTERVAL_MS);
});
<body class="bg-gray-100 h-screen flex justify-center">
<div class="w-full max-w-md h-full bg-white flex flex-col shadow-lg">
<header class="p-4 border-b border-gray-200">
<div class="flex items-center justify-between text-gray-700">
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7" />
</svg>
<div class="flex-grow mx-4 relative">
<input type="text" placeholder="Suchen oder eine Frage stellen"
class="w-full bg-gray-100 border-none rounded-lg py-2 px-4 text-sm focus:ring-2 focus:ring-blue-500" />
</div>
<div class="flex items-center space-x-4">
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M3 9a2 2 0 012-2h.93a2 2 0 001.664-.89l.812-1.22A2 2 0 0110.07 4h3.86a2 2 0 011.664.89l.812 1.22A2 2 0 0018.07 7H19a2 2 0 012 2v9a2 2 0 01-2 2H5a2 2 0 01-2-2V9z" />
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M15 13a3 3 0 11-6 0 3 3 0 016 0z" />
</svg>
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M19 11a7 7 0 01-7 7m0 0a7 7 0 01-7-7m7 7v4m0 0H8m4 0h4m-4-8a3 3 0 01-3-3V5a3 3 0 116 0v6a3 3 0 01-3 3z" />
</svg>
</div>
</div>
<div class="mt-4 flex justify-between items-center">
<div>
<h1 class="text-xl font-bold text-gray-900">Zustellung heute</h1>
<p class="text-gray-600 font-medium">
16h - 19h
<span
class="inline-block bg-gray-200 text-gray-600 text-xs font-bold rounded-full h-4 w-4 text-center leading-4 ml-1">i</span>
</p>
</div>
<a href="#" class="text-sm font-medium text-blue-600">Alle Bestellungen anzeigen</a>
</div>
</header>
<section class="p-4 border-b border-gray-200">
<div class="flex items-center space-x-4">
<img src="/assets/examples/img/ph-shoe.svg" alt="Product Image 1"
class="w-16 h-16 rounded-lg object-cover bg-gray-200" />
<img src="/assets/examples/img/ph-pants.svg" alt="Product Image 2"
class="w-16 h-16 rounded-lg object-cover bg-gray-200" />
<img src="/assets/examples/img/ph-pants.svg" alt="Product Image 3"
class="w-16 h-16 rounded-lg object-cover bg-gray-200" />
</div>
</section>
<main class="flex-grow relative">
<div id="map"></div>
<!-- Update banner with solid background for visibility -->
<div id="update-banner"
class="absolute top-4 left-1/2 -translate-x-1/2 text-sm text-gray-700 bg-white px-3 py-1 rounded-full shadow">
Wird alle 5s aktualisiert
</div>
</main>
<section class="p-4 bg-white border-t border-gray-200">
<h2 id="delivery-status-heading" class="text-lg font-bold text-center mb-4">
In Zustellung
</h2>
<div class="relative w-full">
<div class="h-2 bg-gray-200 rounded-full"></div>
<div id="progress-bar-fill"
class="absolute top-0 left-0 h-2 bg-blue-600 rounded-full transition-all duration-500 ease-linear"
style="width: 75%"></div>
<div class="absolute flex justify-between w-full -top-1">
<span class="h-4 w-4 bg-blue-600 border-2 border-white rounded-full"></span>
<span class="h-4 w-4 bg-blue-600 border-2 border-white rounded-full"></span>
<span class="h-4 w-4 bg-blue-600 border-2 border-white rounded-full"></span>
<span id="status-dot-delivered" class="h-4 w-4 bg-gray-300 border-2 border-white rounded-full"></span>
</div>
</div>
<div class="flex justify-between text-xs mt-2 text-gray-500">
<span>Bestellt</span>
<span>Versendet</span>
<span>In Zustellung</span>
<span>Zugestellt</span>
</div>
</section>
<nav class="flex justify-around items-center p-2 border-t border-gray-200 text-gray-600">
<a href="#" class="text-center hover:text-blue-600">
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 mx-auto" fill="none" viewBox="0 0 24 24"
stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6" />
</svg>
<span class="text-xs">Home</span>
</a>
<a href="#" class="text-center hover:text-blue-600">
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 mx-auto" fill="none" viewBox="0 0 24 24"
stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z" />
</svg>
<span class="text-xs">Profile</span>
</a>
<a href="#" class="text-center hover:text-blue-600">
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 mx-auto" fill="none" viewBox="0 0 24 24"
stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M3 3h2l.4 2M7 13h10l4-8H5.4M7 13L5.4 5M7 13l-2.293 2.293c-.63.63-.184 1.707.707 1.707H17m0 0a2 2 0 100 4 2 2 0 000-4z" />
</svg>
<span class="text-xs">Cart</span>
</a>
<a href="#" class="text-center hover:text-blue-600">
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 mx-auto" fill="none" viewBox="0 0 24 24"
stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16" />
</svg>
<span class="text-xs">Menu</span>
</a>
</nav>
</div>
</body>
@font-face {
font-family: 'Inter';
src: url('../fonts/Inter-Regular.woff2') format('woff2');
font-weight: 400;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: 'Inter';
src: url('../fonts/Inter-Medium.woff2') format('woff2');
font-weight: 500;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: 'Inter';
src: url('../fonts/Inter-Bold.woff2') format('woff2');
font-weight: 700;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: 'Material Icons';
font-style: normal;
font-weight: 400;
src: url(../fonts/MaterialIcons-Regular.woff2) format('woff2');
}
body {
font-family: "Inter", sans-serif;
overscroll-behavior: none;
}
/* Ensure map fills its container */
#map {
width: 100%;
height: 100%;
}
/* Base style for the circular markers */
.destination-marker,
.vehicle-marker {
width: 40px;
height: 40px;
background-color: white;
border-radius: 50%;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
}
/* Specific border colors to distinguish markers */
.destination-marker {
border: 3px solid #ef4444;
/* red-500 */
}
.vehicle-marker {
border: 3px solid #10b981;
/* emerald-500 */
}
/* Styling for the SVG icons within the markers */
.destination-marker img,
.vehicle-marker img {
width: 22px;
height: 22px;
}
/* Custom popup styling */
.maplibregl-popup-content {
background-color: white;
border-radius: 9999px;
/* rounded-full */
padding: 8px 16px;
font-weight: 500;
color: #1f2937;
/* gray-800 */
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}
.maplibregl-popup-tip {
display: none;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Delivery Tracking</title>
<script src="https://cdn.smartmaps.cloud/packages/smartmaps/smartmaps-gl/v2/umd/smartmaps-gl.min.js"></script>
<link rel="stylesheet" href="css/tailwind.min.css" />
<link rel="stylesheet" href="css/smartmaps-demo-styles.css" />
<style>
@font-face {
font-family: 'Inter';
src: url('../fonts/Inter-Regular.woff2') format('woff2');
font-weight: 400;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: 'Inter';
src: url('../fonts/Inter-Medium.woff2') format('woff2');
font-weight: 500;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: 'Inter';
src: url('../fonts/Inter-Bold.woff2') format('woff2');
font-weight: 700;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: 'Material Icons';
font-style: normal;
font-weight: 400;
src: url(../fonts/MaterialIcons-Regular.woff2) format('woff2');
}
body {
font-family: "Inter", sans-serif;
overscroll-behavior: none;
}
/* Ensure map fills its container */
#map {
width: 100%;
height: 100%;
}
/* Base style for the circular markers */
.destination-marker,
.vehicle-marker {
width: 40px;
height: 40px;
background-color: white;
border-radius: 50%;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
}
/* Specific border colors to distinguish markers */
.destination-marker {
border: 3px solid #ef4444;
/* red-500 */
}
.vehicle-marker {
border: 3px solid #10b981;
/* emerald-500 */
}
/* Styling for the SVG icons within the markers */
.destination-marker img,
.vehicle-marker img {
width: 22px;
height: 22px;
}
/* Custom popup styling */
.maplibregl-popup-content {
background-color: white;
border-radius: 9999px;
/* rounded-full */
padding: 8px 16px;
font-weight: 500;
color: #1f2937;
/* gray-800 */
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}
.maplibregl-popup-tip {
display: none;
}
</style>
</head>
<body class="bg-gray-100 h-screen flex justify-center">
<div class="w-full max-w-md h-full bg-white flex flex-col shadow-lg">
<header class="p-4 border-b border-gray-200">
<div class="flex items-center justify-between text-gray-700">
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7" />
</svg>
<div class="flex-grow mx-4 relative">
<input type="text" placeholder="Suchen oder eine Frage stellen"
class="w-full bg-gray-100 border-none rounded-lg py-2 px-4 text-sm focus:ring-2 focus:ring-blue-500" />
</div>
<div class="flex items-center space-x-4">
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M3 9a2 2 0 012-2h.93a2 2 0 001.664-.89l.812-1.22A2 2 0 0110.07 4h3.86a2 2 0 011.664.89l.812 1.22A2 2 0 0018.07 7H19a2 2 0 012 2v9a2 2 0 01-2 2H5a2 2 0 01-2-2V9z" />
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M15 13a3 3 0 11-6 0 3 3 0 016 0z" />
</svg>
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M19 11a7 7 0 01-7 7m0 0a7 7 0 01-7-7m7 7v4m0 0H8m4 0h4m-4-8a3 3 0 01-3-3V5a3 3 0 116 0v6a3 3 0 01-3 3z" />
</svg>
</div>
</div>
<div class="mt-4 flex justify-between items-center">
<div>
<h1 class="text-xl font-bold text-gray-900">Zustellung heute</h1>
<p class="text-gray-600 font-medium">
16h - 19h
<span
class="inline-block bg-gray-200 text-gray-600 text-xs font-bold rounded-full h-4 w-4 text-center leading-4 ml-1">i</span>
</p>
</div>
<a href="#" class="text-sm font-medium text-blue-600">Alle Bestellungen anzeigen</a>
</div>
</header>
<section class="p-4 border-b border-gray-200">
<div class="flex items-center space-x-4">
<img src="/assets/examples/img/ph-shoe.svg" alt="Product Image 1"
class="w-16 h-16 rounded-lg object-cover bg-gray-200" />
<img src="/assets/examples/img/ph-pants.svg" alt="Product Image 2"
class="w-16 h-16 rounded-lg object-cover bg-gray-200" />
<img src="/assets/examples/img/ph-pants.svg" alt="Product Image 3"
class="w-16 h-16 rounded-lg object-cover bg-gray-200" />
</div>
</section>
<main class="flex-grow relative">
<div id="map"></div>
<!-- Update banner with solid background for visibility -->
<div id="update-banner"
class="absolute top-4 left-1/2 -translate-x-1/2 text-sm text-gray-700 bg-white px-3 py-1 rounded-full shadow">
Wird alle 5s aktualisiert
</div>
</main>
<section class="p-4 bg-white border-t border-gray-200">
<h2 id="delivery-status-heading" class="text-lg font-bold text-center mb-4">
In Zustellung
</h2>
<div class="relative w-full">
<div class="h-2 bg-gray-200 rounded-full"></div>
<div id="progress-bar-fill"
class="absolute top-0 left-0 h-2 bg-blue-600 rounded-full transition-all duration-500 ease-linear"
style="width: 75%"></div>
<div class="absolute flex justify-between w-full -top-1">
<span class="h-4 w-4 bg-blue-600 border-2 border-white rounded-full"></span>
<span class="h-4 w-4 bg-blue-600 border-2 border-white rounded-full"></span>
<span class="h-4 w-4 bg-blue-600 border-2 border-white rounded-full"></span>
<span id="status-dot-delivered" class="h-4 w-4 bg-gray-300 border-2 border-white rounded-full"></span>
</div>
</div>
<div class="flex justify-between text-xs mt-2 text-gray-500">
<span>Bestellt</span>
<span>Versendet</span>
<span>In Zustellung</span>
<span>Zugestellt</span>
</div>
</section>
<nav class="flex justify-around items-center p-2 border-t border-gray-200 text-gray-600">
<a href="#" class="text-center hover:text-blue-600">
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 mx-auto" fill="none" viewBox="0 0 24 24"
stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6" />
</svg>
<span class="text-xs">Home</span>
</a>
<a href="#" class="text-center hover:text-blue-600">
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 mx-auto" fill="none" viewBox="0 0 24 24"
stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z" />
</svg>
<span class="text-xs">Profile</span>
</a>
<a href="#" class="text-center hover:text-blue-600">
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 mx-auto" fill="none" viewBox="0 0 24 24"
stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M3 3h2l.4 2M7 13h10l4-8H5.4M7 13L5.4 5M7 13l-2.293 2.293c-.63.63-.184 1.707.707 1.707H17m0 0a2 2 0 100 4 2 2 0 000-4z" />
</svg>
<span class="text-xs">Cart</span>
</a>
<a href="#" class="text-center hover:text-blue-600">
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 mx-auto" fill="none" viewBox="0 0 24 24"
stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16" />
</svg>
<span class="text-xs">Menu</span>
</a>
</nav>
</div>
<script>
// =============================================================
// DELIVERY TRACKING DEMO
// Simulates a real-time delivery experience with a vehicle marker
// that moves through a series of waypoints toward a destination.
// The progress bar and status text update in sync.
//
// APIs used:
// - SmartMaps GL JS (Map, Marker, Popup, flyTo)
// =============================================================
// --- CONFIGURATION & CONSTANTS ---
const TRACKING_ZOOM = 15; // Zoom level when following the vehicle
const FLY_TO_SPEED = 0.8; // flyTo animation speed
const UPDATE_INTERVAL_MS = 5000; // Milliseconds between location updates
const PROGRESS_INCREMENT = 5; // Percentage points per update step
const INITIAL_PROGRESS = 75; // Starting progress (3 of 4 stages complete)
// --- MAP INITIALIZATION ---
const map = new smartmapsgl.Map({
apiKey:
"[INSERT API-KEY]",
container: "map",
style: "essential",
center: [8.438, 49.018], // Karlsruhe city center
zoom: 13,
});
// --- ROUTE DATA ---
// Simulated waypoints the delivery vehicle will pass through
const locations = [
[8.42982, 49.01394],
[8.4354, 49.01523],
[8.44042, 49.02009],
[8.43828, 49.02101],
[8.43933, 49.02166], // Final destination
];
// --- HELPER FUNCTIONS ---
/** Update all UI elements to show the delivery-complete state. */
function showDeliveryComplete(progressBar) {
document.getElementById("update-banner").innerText =
"Zustellung erfolgt!";
document.getElementById("delivery-status-heading").innerText =
"Erfolgreich zugestellt";
progressBar.style.width = "100%";
// Activate the final progress dot
const deliveredDot = document.getElementById("status-dot-delivered");
deliveredDot.classList.remove("bg-gray-300");
deliveredDot.classList.add("bg-blue-600");
// Show arrival popup at the destination
new smartmapsgl.Popup({ closeButton: false, offset: 35 })
.setText("Sie sind da!")
.setLngLat(locations[locations.length - 1])
.addTo(map);
}
/** Move the vehicle marker and camera to the given location. */
function moveVehicleTo(vehicleMarker, location) {
vehicleMarker.setLngLat(location);
map.flyTo({
center: location,
zoom: TRACKING_ZOOM,
speed: FLY_TO_SPEED,
});
}
// --- MAP SETUP ---
let vehicleMarker;
map.on("load", () => {
// Create destination marker (red border, home icon)
const destinationEl = document.createElement("div");
destinationEl.className = "destination-marker";
const destinationIcon = document.createElement("img");
destinationIcon.src =
"https://cdn.smartmaps.cloud/packages/maki/icons/home.svg";
destinationEl.appendChild(destinationIcon);
new smartmapsgl.Marker({ element: destinationEl })
.setLngLat(locations[locations.length - 1])
.addTo(map);
// Create vehicle marker (green border, bus icon)
const vehicleEl = document.createElement("div");
vehicleEl.className = "vehicle-marker";
const vehicleIcon = document.createElement("img");
vehicleIcon.src =
"https://cdn.smartmaps.cloud/packages/maki/icons/bus.svg";
vehicleEl.appendChild(vehicleIcon);
vehicleMarker = new smartmapsgl.Marker({ element: vehicleEl })
.setLngLat(locations[0])
.addTo(map);
// --- LOCATION UPDATE LOOP ---
const progressBar = document.getElementById("progress-bar-fill");
let locationIndex = 0;
let progressPercent = INITIAL_PROGRESS;
progressBar.style.width = `${progressPercent}%`;
const intervalId = setInterval(() => {
locationIndex++;
// Check if the vehicle has reached the destination
if (locationIndex >= locations.length) {
clearInterval(intervalId);
showDeliveryComplete(progressBar);
return;
}
// Move vehicle and update progress bar
moveVehicleTo(vehicleMarker, locations[locationIndex]);
progressPercent += PROGRESS_INCREMENT;
progressBar.style.width = `${progressPercent}%`;
}, UPDATE_INTERVAL_MS);
});
</script>
</body>
</html>