La mappa di tracciamento e lo schermo con il piu alto engagement in qualsiasi app di consegna di cibo o rideshare. E quello su cui i clienti fissano lo sguardo mentre aspettano. Farlo bene, movimento fluido, ETA accurati, una linea di percorso chiara, e la differenza tra un'app che sembra professionale e una che sembra inaffidabile.
Questo tutorial costruisce la mappa di tracciamento rivolta al cliente da principi primi: un backend che trasmette posizioni GPS tramite WebSocket, un frontend che le riceve e sposta un marker del conducente senza saltare, una linea di percorso dall'API di routing di MapAtlas e un display ETA in diretta. L'implementazione completa e inferiore a 70 righe di JavaScript lato client, progettata per integrarsi con qualsiasi backend in grado di spingere messaggi WebSocket.
L'architettura funziona per la consegna di cibo, alimentari, rideshare, servizio sul campo e qualsiasi altro caso d'uso in cui un veicolo si muove verso una destinazione fissa e un cliente lo osserva accadere in tempo reale.
Panoramica dell'architettura
Prima di scrivere il codice, e utile comprendere il flusso di dati:
- App del conducente (hardware mobile, GPS) invia latitudine/longitudine al tuo backend ogni 3-5 secondi.
- Backend (Node.js, Python, Go, la tua scelta) persiste l'ultima posizione nota e la trasmette via WebSocket a tutti gli abbonati agli ordini connessi.
- Browser del cliente riceve messaggi WebSocket e sposta un marker sulla mappa utilizzando l'animazione interpolata.
- L'API di routing viene chiamata una volta al momento della creazione dell'ordine per recuperare il percorso pianificato. La polilinea decodificata viene visualizzata come un livello di linea.
- L'ETA viene ricalcolato confrontando la distanza rimanente con la velocita media, o ricalciando l'API di routing dalla posizione corrente del conducente.
L'implementazione del backend esce dall'ambito di questo tutorial, ma qualsiasi server WebSocket che invia messaggi in questo formato funziona con il codice frontend di seguito:
{
"type": "position_update",
"orderId": "order-8821",
"lat": 52.3741,
"lng": 4.8952,
"heading": 92,
"speed": 28,
"timestamp": 1738234521000
}
Step 1: Inizializzazione della mappa
Configura la mappa centrata sull'origine della consegna. La chiamata dell'API di routing avviene nel passaggio 4, quindi per ora inizializza solo la tela.
import mapmetricsgl from '@mapmetrics/mapmetrics-gl';
import '@mapmetrics/mapmetrics-gl/dist/mapmetrics-gl.css';
const map = new mapmetricsgl.Map({
container: 'tracking-map',
style: 'https://tiles.mapatlas.eu/styles/basic/style.json?key=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);
Step 2: Marker del conducente con rotazione della direzione
Crea il marker del conducente separatamente in modo da poter aggiornare la sua posizione ad ogni ping GPS. Un elemento HTML personalizzato ti permette di ruotare l'icona del marker per riflettere la direzione del conducente, un piccolo dettaglio che rende il tracciamento molto piu 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)`;
}
Step 3: Connessione WebSocket e interpolazione fluida
Questo e il nucleo della mappa di tracciamento. La connessione al WebSocket e una riga; la parte interessante e l'interpolazione della posizione del marker tra i ping GPS in modo che scivoli uniformemente piuttosto che 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 corrisponde ragionevolmente bene a un tipico intervallo di ping GPS di 3-5 secondi, il marker e sempre leggermente dietro la realta ma non salta mai notevolmente.
Step 4: Disegna il percorso pianificato dall'API di routing
Recupera il percorso completo quando l'ordine viene assegnato. Archivia le coordinate della polilinea e disegnale come un livello di linea GeoJSON. Il tutorial dell'API di ottimizzazione dei percorsi copre scenari multi-stop; per una semplice consegna A-a-B la richiesta e semplice.
async function fetchAndDrawRoute(originLat, originLng, destLat, destLng) {
const url = new URL('https://api.mapatlas.eu/v1/routing/route');
url.searchParams.set('origin', `${originLat},${originLng}`);
url.searchParams.set('destination', `${destLat},${destLng}`);
url.searchParams.set('profile', 'driving');
url.searchParams.set('key', 'YOUR_API_KEY');
const res = await fetch(url);
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
}
Step 5: Calcolo e visualizzazione dell'ETA
Calcola l'ETA confrontando la posizione attuale del conducente con la destinazione. Per un'elevata accuratezza, richiama l'API di routing dalla posizione attuale del conducente ogni 30 secondi per ottenere una stima del tempo di viaggio fresca.
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://api.mapatlas.eu/v1/routing/route');
url.searchParams.set('origin', `${driverLat},${driverLng}`);
url.searchParams.set('destination', `${destLat},${destLng}`);
url.searchParams.set('profile', 'driving');
url.searchParams.set('key', 'YOUR_API_KEY');
const res = await fetch(url);
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`;
}
}
}
Considerazioni GDPR per il tracciamento dei conducenti
Le coordinate GPS del conducente sono dati personali secondo l'articolo 4(1) del GDPR. I regolamenti che governano le piattaforme di consegna di cibo e rideshare dell'UE su questo punto non sono ambigui:
Minimizzazione dei dati: Traccia solo i campi di cui hai bisogno per il dispatch, posizione, direzione, velocita. Non registrare la cronologia GPS grezza oltre cio che e operazionalmente necessario.
Limiti di conservazione: I dati granulari di tracciamento dei viaggi dovrebbero essere eliminati o resi irrevocabilmente anonimi una volta completato l'ordine. I dati del percorso aggregati (senza collegamento a un singolo conducente) possono essere conservati piu a lungo per l'ottimizzazione della rete.
Base legale: L'interesse legittimo secondo l'articolo 6(1)(f) copre il tracciamento del dispatch in tempo reale. Per qualsiasi uso secondario dei dati di tracciamento (analitiche, benchmarking), devi documentare una base separata.
Trasparenza del conducente: Includi la divulgazione del tracciamento chiara nell'onboarding del conducente. I conducenti devono essere informati su cio che viene raccolto, per quanto tempo viene conservato e chi puo accedervi.
Residenza dei dati: MapAtlas elabora tutte le richieste API all'interno dell'UE. Questo elimina il problema del trasferimento verso paesi terzi che sorge con i provider di mappatura basati negli USA. Vedi la Guida dello sviluppatore dell'UE per le API di mapping conformi al GDPR per il quadro di conformita completo.
Per il caso d'uso dell'industria del ridesharing e della mobilita in particolare, MapAtlas include documentazione DPA e garanzie di server dell'UE come standard. La pagina dell'industria logistica e consegna copre scenari multi-conducente e di flotta.
Indurimento della produzione
Prima di implementare una funzione di tracciamento ai clienti, controlla questi elementi:
- Riconnessione WebSocket: Aggiungi
ws.addEventListener('close', reconnect)con backoff esponenziale. Le reti mobili perdono frequentemente le connessioni. - Gestione della posizione non aggiornata: Se nessun aggiornamento arriva entro 15 secondi, mostra uno stato 'localizzazione del conducente' piuttosto che 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. - La fotocamera segue il conducente: Chiama
map.panTo([lng, lat])ad ogni aggiornamento di posizione per mantenere il conducente centrato. Dai agli utenti un interruttore 'blocco' per disabilitare la modalita di follow se vogliono esplorare la mappa.
Passaggi successivi
- Iscriviti per la tua chiave API gratuita di MapAtlas e inizia a costruire
- Leggi il tutorial dell'API di ottimizzazione dei percorsi per aggiungere il dispatch multi-stop alla tua app di consegna
- Esplora il tutorial della mappa immobiliare per un altro esempio di livelli di mappa dinamici e guidati dai 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, creando saltelli visibili se aggiorni direttamente la posizione del marker. L'interpolazione fluida anima il marker tra la posizione precedente e quella nuova su una breve durata (300-500 ms), utilizzando requestAnimationFrame per spostare il marker in piccoli incrementi. Cio da l'apparenza di movimento continuo anche con aggiornamenti GPS non frequenti.
I dati sulla posizione del conducente sono soggetti al GDPR?
Si. Le coordinate GPS in tempo reale di un conducente sono dati personali secondo l'articolo 4 del GDPR. Le piattaforme di consegna di cibo e rideshare dell'UE devono minimizzare la conservazione, i dati di tracciamento devono essere eliminati o resi anonimi una volta completato il viaggio. L'elaborazione richiede una base legale e deve essere divulgata nell'avviso sulla privacy del conducente.
Posso usare l'API di routing di MapAtlas per mostrare il percorso pianificato sulla mappa di tracciamento?
Si. Recupera il percorso dall'API di routing quando viene creato un viaggio, decodifica la polilinea e aggiungila come un livello di linea GeoJSON sulla mappa. Mentre il conducente si muove, puoi facoltativamente ricalciare il percorso dalla posizione attuale per ricalcolare dinamicamente l'ETA.
