La mappa di tracciamento è la schermata con il maggiore engagement in qualsiasi app di food delivery o ride sharing. È quella che i clienti fissano mentre aspettano. Farla bene, con un movimento fluido, ETA accurati e una linea del percorso chiara, fa la differenza tra un'app che sembra professionale e una che sembra inaffidabile.
Questo tutorial costruisce da zero la mappa di tracciamento lato cliente: un backend che trasmette le posizioni GPS via WebSocket, un frontend che le riceve e sposta il marker del conducente senza scatti, una linea del percorso ottenuta dall'API di routing di MapAtlas e un ETA aggiornato in tempo reale. L'implementazione completa richiede meno di 70 righe di JavaScript lato client ed è pensata per integrarsi con qualsiasi backend in grado di inviare messaggi WebSocket.
L'architettura funziona per food delivery, spesa a domicilio, ride sharing, assistenza sul campo e qualsiasi altro caso in cui un veicolo si muove verso una destinazione fissa mentre un cliente lo segue in tempo reale.
Panoramica dell'architettura
Prima di scrivere codice conviene capire il flusso dei dati:
- L'app del conducente (smartphone o dispositivo GPS) invia latitudine e longitudine al tuo backend ogni 3-5 secondi.
- Il backend (Node.js, Python, Go, a tua scelta) salva l'ultima posizione nota e la trasmette via WebSocket a tutti i client connessi che seguono l'ordine.
- Il browser del cliente riceve i messaggi WebSocket e sposta un marker sulla mappa con un'animazione interpolata.
- La Routing API viene chiamata una sola volta, alla creazione dell'ordine, per ottenere il percorso pianificato. La polilinea decodificata viene mostrata come layer di linea.
- L'ETA viene ricalcolato confrontando la distanza residua con la velocità media, oppure chiamando di nuovo la Routing API dalla posizione attuale del conducente.
L'implementazione del backend esula da questo tutorial, ma qualsiasi server WebSocket che invia messaggi in questo formato funziona con il codice frontend che segue:
{
"type": "position_update",
"orderId": "order-8821",
"lat": 52.3741,
"lng": 4.8952,
"heading": 92,
"speed": 28,
"timestamp": 1738234521000
}
Passo 1: inizializza la mappa
Configura la mappa centrata sull'origine della consegna. La chiamata alla Routing API arriva nel passo 4, quindi per ora inizializza solo il canvas.
import mapmetricsgl from '@mapmetrics/mapmetrics-gl';
import '@mapmetrics/mapmetrics-gl/dist/mapmetrics-gl.css';
const map = new mapmetricsgl.Map({
container: 'tracking-map',
style: 'https://gateway.mapmetrics-atlas.net/styles/?fileName=YOUR_STYLE_ID/style.json&token=YOUR_API_KEY',
center: [4.9041, 52.3676],
zoom: 14
});
// Destination marker (restaurant or pickup point)
const destination = [4.9001, 52.3791];
new mapmetricsgl.Marker({ color: '#EF4444' })
.setLngLat(destination)
.setPopup(new mapmetricsgl.Popup().setHTML('<strong>Pickup point</strong>'))
.addTo(map);
Passo 2: crea il marker del conducente con rotazione in base alla direzione
Crea il marker del conducente separatamente, così puoi aggiornarne la posizione a ogni ping GPS. Un elemento HTML personalizzato ti permette di ruotare l'icona del marker in base alla direzione di marcia: un piccolo dettaglio che rende il tracciamento molto più realistico.
// Custom element so we can rotate it
const driverEl = document.createElement('div');
driverEl.innerHTML = '🚗';
driverEl.style.cssText = 'font-size:28px;transform-origin:center;transition:transform 0.3s';
const driverMarker = new mapmetricsgl.Marker({ element: driverEl, anchor: 'center' })
.setLngLat([4.9041, 52.3676])
.addTo(map);
function setDriverHeading(heading) {
driverEl.style.transform = `rotate(${heading}deg)`;
}
Passo 3: collega il WebSocket e interpola il movimento
Questo è il cuore della mappa di tracciamento. La connessione al WebSocket richiede una sola riga; la parte interessante è interpolare la posizione del marker tra un ping GPS e l'altro, così che scivoli in modo fluido invece di teletrasportarsi.
let prevPos = null; // { lat, lng }
let animFrame = null;
function interpolateMarker(fromLat, fromLng, toLat, toLng, durationMs) {
const startTime = performance.now();
function step(now) {
const elapsed = now - startTime;
const t = Math.min(elapsed / durationMs, 1); // 0 → 1
const lat = fromLat + (toLat - fromLat) * t;
const lng = fromLng + (toLng - fromLng) * t;
driverMarker.setLngLat([lng, lat]);
if (t < 1) {
animFrame = requestAnimationFrame(step);
}
}
if (animFrame) cancelAnimationFrame(animFrame);
animFrame = requestAnimationFrame(step);
}
const ws = new WebSocket('wss://your-backend.example.com/track/order-8821');
ws.addEventListener('message', (event) => {
const msg = JSON.parse(event.data);
if (msg.type !== 'position_update') return;
const { lat, lng, heading } = msg;
if (prevPos) {
// Smooth animation from previous to new position
interpolateMarker(prevPos.lat, prevPos.lng, lat, lng, 400);
} else {
// First ping, place marker immediately
driverMarker.setLngLat([lng, lat]);
map.flyTo({ center: [lng, lat], zoom: 15 });
}
setDriverHeading(heading);
updateETA(lat, lng);
prevPos = { lat, lng };
});
ws.addEventListener('close', () => {
console.log('Driver has arrived or connection closed.');
});
La finestra di interpolazione di 400 ms si adatta abbastanza bene a un intervallo tipico di 3-5 secondi tra un ping GPS e l'altro: il marker resta sempre leggermente indietro rispetto alla realtà, ma non fa mai salti evidenti.
Passo 4: disegna il percorso pianificato con la Routing API
Recupera il percorso completo quando l'ordine viene assegnato. Salva le coordinate della polilinea e disegnale come layer di linea GeoJSON. Il tutorial sull'API di ottimizzazione dei percorsi tratta gli scenari con più fermate; per una semplice consegna da A a B la richiesta è immediata.
async function fetchAndDrawRoute(originLat, originLng, destLat, destLng) {
const url = new URL('https://gateway.mapmetrics-atlas.net/directions/');
url.searchParams.set('token', 'YOUR_API_KEY');
const res = await fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
locations: [
{ lat: originLat, lon: originLng },
{ lat: destLat, lon: destLng },
],
costing: 'auto',
format: 'osrm',
shape_format: 'geojson',
}),
});
const data = await res.json();
if (!data.routes?.length) return;
const route = data.routes[0];
map.on('load', () => {
map.addSource('route', {
type: 'geojson',
data: {
type: 'Feature',
geometry: route.geometry // GeoJSON LineString
}
});
map.addLayer({
id: 'route-line',
type: 'line',
source: 'route',
layout: { 'line-join': 'round', 'line-cap': 'round' },
paint: {
'line-color': '#3B82F6',
'line-width': 4,
'line-opacity': 0.8
}
});
});
return route.duration; // seconds
}
Passo 5: calcola e mostra l'ETA
Calcola l'ETA confrontando la posizione attuale del conducente con la destinazione. Per una precisione maggiore, chiama di nuovo la Routing API dalla posizione attuale del conducente ogni 30 secondi e ottieni una stima aggiornata del tempo di viaggio.
function haversineKm(lat1, lon1, lat2, lon2) {
const R = 6371;
const dLat = ((lat2 - lat1) * Math.PI) / 180;
const dLon = ((lon2 - lon1) * Math.PI) / 180;
const a = Math.sin(dLat / 2) ** 2 +
Math.cos((lat1 * Math.PI) / 180) *
Math.cos((lat2 * Math.PI) / 180) *
Math.sin(dLon / 2) ** 2;
return R * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
}
let lastRouteFetch = 0;
async function updateETA(driverLat, driverLng) {
const [destLng, destLat] = destination;
const distKm = haversineKm(driverLat, driverLng, destLat, destLng);
// Fast client-side estimate (assume 30 km/h urban average)
const minutesEstimate = Math.ceil((distKm / 30) * 60);
document.getElementById('eta').textContent =
distKm < 0.1
? 'Arriving now'
: `ETA: ${minutesEstimate} min (${distKm.toFixed(1)} km)`;
// Re-fetch from Routing API every 30 seconds for accuracy
const now = Date.now();
if (now - lastRouteFetch > 30_000) {
lastRouteFetch = now;
const url = new URL('https://gateway.mapmetrics-atlas.net/directions/');
url.searchParams.set('token', 'YOUR_API_KEY');
const res = await fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
locations: [
{ lat: driverLat, lon: driverLng },
{ lat: destLat, lon: destLng },
],
costing: 'auto',
format: 'osrm',
shape_format: 'geojson',
}),
});
const data = await res.json();
if (data.routes?.length) {
const mins = Math.ceil(data.routes[0].duration / 60);
document.getElementById('eta').textContent = `ETA: ${mins} min`;
}
}
}
Aspetti GDPR del tracciamento dei conducenti
Le coordinate GPS del conducente sono dati personali ai sensi dell'articolo 4, paragrafo 1, del GDPR. I principi del GDPR che si applicano qui sono la minimizzazione dei dati e la limitazione della conservazione previste dall'articolo 5:
Minimizzazione dei dati: traccia solo i campi che ti servono per il dispatch, cioè posizione, direzione e velocità. Non registrare lo storico GPS grezzo oltre quanto è necessario per l'operatività.
Limiti di conservazione: i dati di tracciamento dettagliati di ogni viaggio vanno eliminati o anonimizzati in modo irreversibile una volta completato l'ordine. I dati aggregati sui percorsi (senza collegamento a un singolo conducente) possono essere conservati più a lungo per ottimizzare la rete.
Base giuridica: il legittimo interesse ai sensi dell'articolo 6, paragrafo 1, lettera f, supportato da un bilanciamento degli interessi documentato, oppure l'esecuzione del contratto ai sensi della lettera b dello stesso paragrafo copre di solito il tracciamento in tempo reale per il dispatch. Per qualsiasi uso secondario dei dati di tracciamento (analisi, benchmarking) devi documentare una base giuridica separata.
Trasparenza verso i conducenti: includi un'informativa chiara sul tracciamento nella fase di onboarding. I conducenti devono sapere quali dati vengono raccolti, per quanto tempo vengono conservati e chi può accedervi.
Residenza dei dati: MapAtlas elabora tutte le richieste API all'interno dell'UE. In questo modo non si pone il problema del trasferimento verso paesi terzi che nasce con i fornitori di mappe con sede negli Stati Uniti. Consulta la guida per sviluppatori UE alle API di mappe conformi al GDPR per il quadro completo sulla conformità.
Per il caso d'uso del settore ride sharing e mobilità in particolare, MapAtlas include di serie la documentazione DPA e la garanzia di server nell'UE. La pagina dedicata al settore logistica e consegne tratta gli scenari con flotte e più conducenti.
Prepara il tracciamento per la produzione
Prima di rilasciare ai clienti una funzione di tracciamento, controlla questi punti:
- Riconnessione WebSocket: aggiungi
ws.addEventListener('close', reconnect)con backoff esponenziale. Le reti mobili perdono spesso la connessione. - Gestione delle posizioni non aggiornate: se per 15 secondi non arriva nessun aggiornamento, mostra uno stato «ricerca del conducente in corso» invece di lasciare visibile l'ultima posizione.
- Rilevamento dell'arrivo: quando
distKm < 0.1, attiva uno stato «arrivato», chiudi il WebSocket e mostra una schermata di conferma. - Visuale che segue il conducente: chiama
map.panTo([lng, lat])a ogni aggiornamento di posizione per mantenere il conducente al centro. Offri agli utenti un interruttore di «blocco» per disattivare l'inseguimento se vogliono esplorare la mappa.
Prossimi passi
- Registrati per ottenere la tua chiave API MapAtlas gratuita e inizia a sviluppare
- Leggi il tutorial sull'API di ottimizzazione dei percorsi per aggiungere il dispatch con più fermate alla tua app di consegne
- Esplora il tutorial sulla mappa immobiliare per un altro esempio di layer di mappa dinamici basati sui dati
Domande frequenti
Come posso mostrare la posizione di un conducente che si muove in modo fluido su una mappa?
I ping GPS arrivano ogni pochi secondi e creano salti visibili se aggiorni direttamente la posizione del marker. L'interpolazione fluida anima il marker tra la posizione precedente e quella nuova in un breve intervallo (300-500 ms), usando requestAnimationFrame per spostarlo a piccoli incrementi. Così il movimento appare continuo anche con aggiornamenti GPS poco frequenti.
I dati sulla posizione del conducente sono soggetti al GDPR?
Sì. Le coordinate GPS in tempo reale di un conducente sono dati personali ai sensi dell'articolo 4 del GDPR. Le piattaforme dovrebbero conservare i dati di tracciamento solo per il tempo necessario, ed eliminarli o anonimizzarli una volta concluso il viaggio è la scelta predefinita più sicura. Il trattamento richiede una base giuridica e va indicato nell'informativa privacy del conducente.
Posso usare l'API di routing di MapAtlas per mostrare il percorso pianificato sulla mappa di tracciamento?
Sì. Recupera il percorso dalla Routing API quando viene creato un viaggio, decodifica la polilinea e aggiungila alla mappa come layer di linea GeoJSON. Mentre il conducente si muove, puoi anche richiedere di nuovo il percorso dalla posizione attuale per ricalcolare l'ETA in modo dinamico.
Domande frequenti
Come posso mostrare la posizione di un conducente che si muove in modo fluido su una mappa?
I ping GPS arrivano ogni pochi secondi e creano salti visibili se aggiorni direttamente la posizione del marker. L'interpolazione fluida anima il marker tra la posizione precedente e quella nuova in un breve intervallo (300-500 ms), usando requestAnimationFrame per spostarlo a piccoli incrementi. Così il movimento appare continuo anche con aggiornamenti GPS poco frequenti.
I dati sulla posizione del conducente sono soggetti al GDPR?
Sì. Le coordinate GPS in tempo reale di un conducente sono dati personali ai sensi dell'articolo 4 del GDPR. Le piattaforme dovrebbero conservare i dati di tracciamento solo per il tempo necessario, ed eliminarli o anonimizzarli una volta concluso il viaggio è la scelta predefinita più sicura. Il trattamento richiede una base giuridica (di solito il legittimo interesse per le operazioni di dispatch o l'esecuzione del contratto) e va indicato nell'informativa privacy del conducente.
Posso usare l'API di routing di MapAtlas per mostrare il percorso pianificato sulla mappa di tracciamento?
Sì. Recupera il percorso dalla Routing API quando viene creato un viaggio, decodifica la polilinea e aggiungila alla mappa come layer di linea GeoJSON. Mentre il conducente si muove, puoi anche richiedere di nuovo il percorso dalla posizione attuale per ricalcolare l'ETA in modo dinamico.

