Die Verfolgungskarte ist der bildschirm mit der hoechsten benutzerengagement in jeder lebensmittellieferungs- oder mitfahrgelegenheits-app. Es ist das, worauf kunden staerken, waehrend sie warten. Es richtig zu machen, mit sanfter bewegung, genauen ankunftszeiten und einer klaren routenlinie, ist der unterschied zwischen einer app, die sich professionell anfuehlt, und einer, die sich unzuverlaessig anfuehlt.
Dieses tutorial erstellt die kundenseitige verfolgungskarte von grund auf: ein backend, das gps-positionen ueber websocket uebermittelt, ein frontend, das sie empfaengt und einen fahrermarker ohne spruenge bewegt, eine routenlinie von der MapAtlas Routing API, und eine live-ankunftszeit-anzeige. Die komplette implementierung umfasst weniger als 70 zeilen client-seitiges javascript, entworfen fuer die integration mit jedem backend, das websocket-nachrichten versenden kann.
Die architektur funktioniert fuer lebensmittellieferung, lebensmittelversorung, mitfahrgelegenheiten, feldservice und jeden anderen anwendungsfall, bei dem ein fahrzeug zu einem festen ziel faehrt und ein kunde es in echtzeit beobachtet.
Architektur-ueberblick
Bevor code geschrieben wird, hilft es, den datenfuss zu verstehen:
- Fahreranwendung (mobil, gps-hardware) sendet breitengrad/laengengrad alle 3-5 sekunden an ihr backend.
- Backend (node.js, python, go, ihrer wahl) speichert die letzte bekannte position und sendet sie ueber websocket an alle verbundenen bestellungsabonnenten.
- Kundenwebbrowser empfaengt websocket-nachrichten und bewegt einen marker auf der karte mit interpolierter animation.
- Routing API wird einmal aufgerufen, wenn die bestellung erstellt wird, um die geplante route abzurufen. Das dekodierte polyline wird als linienschicht angezeigt.
- Ankunftszeit wird neu berechnet, indem die verbleibende entfernung mit der durchschnittsgeschwindigkeit verglichen wird, oder indem die routing-api von der aktuellen position des fahrers erneut aufgerufen wird.
Die backend-implementierung liegt ausserhalb des umfangs dieses tutorials, aber jeder websocket-server, der nachrichten in diesem format sendet, funktioniert mit dem unten stehenden frontend-code:
{
"type": "position_update",
"orderId": "order-8821",
"lat": 52.3741,
"lng": 4.8952,
"heading": 92,
"speed": 28,
"timestamp": 1738234521000
}
Schritt 1: Karten-Initialisierung
Richten sie die karte auf das lieferursprungszentrum ein. Der routing-api-aufruf erfolgt in schritt 4, also initialisieren sie voerst einfach die 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://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);
Schritt 2: Fahrermarker mit kopfrichtungsrotation
Erstellen sie den fahrermarker separat, damit sie seine position bei jedem gps-ping aktualisieren koennen. Ein benutzerdefiniertes html-element laesst sie das marker-symbol drehen, um die kopfrichtung des fahrers widerzuspiegeln, ein kleines detail, das das verfolgungsverfahren viel realitaetsnaher macht.
// 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)`;
}
Schritt 3: WebSocket-Verbindung und glatte Interpolation
Dies ist das kernstuck der verfolgungskarte. Die verbindung mit dem websocket ist eine zeile, der interessante teil ist die interpolation der markerposition zwischen gps-pings, damit sie sanft gleitet statt zu teleportieren.
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.');
});
Das 400ms-interpolationsfenster entspricht ziemlich gut einem typischen gps-ping-intervall von 3-5 sekunden, der marker ist immer leicht hinter der realitaet, aber springt niemals merklich.
Schritt 4: Zeichnen sie die geplante route von der routing-api
Rufen sie die vollstaendige route ab, wenn die bestellung zugewiesen wird. Speichern sie die polyline-koordinaten und zeichnen sie sie als geojson-linienschicht. Das Route Optimization API Tutorial behandelt mehrschichtszenarien, fuer eine einfache a-zu-b-lieferung ist die anfrage unkompliziert.
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
}
Schritt 5: Ankunftszeit-Berechnung und -Anzeige
Berechnen sie die ankunftszeit durch vergleich der aktuellen position des fahrers mit dem ziel. Fuer hoechste genauigkeit rufen sie die routing-api alle 30 sekunden von der aktuellen position des fahrers auf, um eine aktuelle reisezeitschaetzung zu erhalten.
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`;
}
}
}
GDPR-Erwägungen zur Fahrerverfolgungu
Fahrer-gps-koordinaten sind persoenliche daten gemäss GDPR-artikel 4(1). Die vorschriften, die eu-lebensmittellieferungs- und mitfahrgelegenheitsplattformen auf diesen punkt regeln, sind nicht mehrdeutig:
Datenvermeidung: Verfolgen sie nur die felder, die sie fuer dispatch benoetigen, position, kopfrichtung, geschwindigkeit. Protokollieren sie keine rohen gps-historien ueber das betrieblich notwendige hinaus.
Aufbewahrungsrichtlinien: Granulare fahrtenverfolgungsdaten sollten geloescht oder unumkehrbar anonymisiert werden, sobald die bestellung abgeschlossen ist. Aggregierte routendaten (ohne rueckverfolgung zu einem einzelnen fahrer) koennen laenger fuer netzwerkoptimierung aufbewahrt werden.
Rechtsgrund: Berechtigtes interesse gemäss artikel 6(1)(f) deckt die echtzeit-dispatchverfolgung ab. Fuer jede sekundaere nutzung von verfolgungsdaten (analytik, benchmarking) muessen sie eine separate grundlage dokumentieren.
Fahrerlichkeit: Beziehen sie eine klare verfolgungsoffenlegung beim fahrereinstellungsprozess ein. Fahrer muessen mitgeteilt werden, was erfasst wird, wie lange es aufbewahrt wird und wer darauf zugreifen kann.
Datenspeicherort: MapAtlas verarbeitet alle api-anfragen innerhalb der eu. Dies beseitigt das bedenken bezueglich der drittlanduebertraegung, die sich bei us-basierten mapping-anbietern ergibt. Siehe den EU Developer's Guide to GDPR-Compliant Map APIs fuer das komplette compliance-bild.
Fuer den ridesharing- und mobilitaetsbranche anwendungsfall bietet MapAtlas standardmaessig dpa-dokumentation und eu-server-garantien. Die seite Logistik und Lieferung behandelt flotten- und mehrfahrerszenarios.
Produktionshärtung
Vor dem versand eines verfolgungsmerkmales an kunden ueberpruefen sie diese punkte:
- WebSocket-Wiederverbindung: Fuegen sie
ws.addEventListener('close', reconnect)mit exponentiellem rueckgang hinzu. Mobilitaetsnetze trennen verbindungen haeufig ab. - Abgelaufene positionsbehandlung: Wenn keine aktualisierung innerhalb von 15 sekunden ankommt, zeigen sie den status 'fahrer lokalisieren' statt die letzte position sichtbar zu lassen.
- Ankunftserkennung: Wenn
distKm < 0.1, loesen sie den status 'angekommen' aus, schliessen sie den websocket und zeigen sie einen bestaetigung-bildschirm. - Kamera folgt dem fahrer: Rufen sie
map.panTo([lng, lat])bei jedem positionsupdate auf, um den fahrer zentriert zu halten. Geben sie benutzern einen 'lock'-toggle, um den verfolgungsmodus zu deaktivieren, wenn sie die karte erkunden moechten.
Nächste Schritte
- Melden Sie sich fuer Ihren kostenlosen MapAtlas API-Schluessel an und beginnen Sie mit der erstellung
- Lesen Sie das Route Optimization API Tutorial, um mehrschichtiges dispatch zu Ihrer lieferapp hinzuzufuegen
- Erkunden Sie das Real Estate Property Map Tutorial fuer ein weiteres beispiel dynamischer, datengesteuerter kartenschichten
Häufig gestellte Fragen
Wie zeige ich die position eines fahrers sanft bewegend auf einer karte an?
GPS-pings treffen alle paar sekunden ein und erzeugen sichtbare spruenge, wenn Sie die markerposition direkt aktualisieren. Sanfte interpolation animiert den marker zwischen seiner vorherigen und neuen position ueber eine kurze dauer (300-500ms) unter verwendung von requestAnimationFrame, um den marker in kleinen inkrementen zu bewegen. Dies gibt den anschein kontinuierlicher bewegung, selbst bei seltenen gps-updates.
Unterliegen fahrerstandortdaten GDPR?
Ja. Die echtzeit-gps-koordinaten eines fahrers sind persoenliche daten gemaess GDPR-artikel 4. EU-lebensmittellieferungs- und mitfahrgelegenheitsplattformen muessen die aufbewahrung minimieren, verfolgungsdaten sollten geloescht oder anonymisiert werden, sobald die fahrt abgeschlossen ist. Die verarbeitung erfordert eine rechtsgrundlage und muss in der datenschutzerklaerung des fahrers offengelegt werden.
Kann ich die MapAtlas Routing API verwenden, um die geplante route auf der verfolgungskarte anzuzeigen?
Ja. Rufen Sie die route von der Routing API ab, wenn eine fahrt erstellt wird, dekodieren Sie die polyline und fuegen Sie sie als geojson-linienschicht auf der karte hinzu. Waehrend der fahrt koennen sie optional die route von der aktuellen position des fahrers erneut abrufen, um die ankunftszeit dynamisch neu zu berechnen.
