Kayfa tanaa' kharitat tatbu' al-sa'iq al-hayya li-tatbiqat altaslim aw rakub al-sayarat
Tutorials

Kayfa tanaa' kharitat tatbu' al-sa'iq al-hayya li-tatbiqat altaslim aw rakub al-sayarat

Abni kharitat tatbu' sa'iq fi alwaqt alfei'li ma' tahdithat GPS 'abra WebSocket wa tahsin harakat al-'alamat wa 'ard al-tariq wa hisab waqt alwusul baistikhdam MapAtlas Maps wa Routing APIs.

MapAtlas Team9 min read
#live tracking map#driver tracking#real-time map#websocket map#delivery tracking#rideshare app

The tracking map is the highest-engagement screen in any food delivery or rideshare app. It is what customers stare at while they wait. Getting it right, smooth movement, accurate ETAs, a clear route line, is the difference between an app that feels professional and one that feels unreliable.

This tutorial builds the customer-facing tracking map from first principles: a backend that broadcasts GPS positions via WebSocket, a frontend that receives them and moves a driver marker without jumping, a route line from the MapAtlas Routing API, and a live ETA display. The complete implementation is under 70 lines of client-side JavaScript, designed to integrate with any backend that can push WebSocket messages.

The architecture works for food delivery, grocery, rideshare, field service, and any other use case where a vehicle moves toward a fixed destination and a customer watches it happen in real time.

Architecture Overview

Before writing code, it helps to understand the data flow:

  1. Driver app (mobile, GPS hardware) sends latitude/longitude to your backend every 3–5 seconds.
  2. Backend (Node.js, Python, Go, your choice) persists the last known position and broadcasts it via WebSocket to all connected order subscribers.
  3. Customer browser receives WebSocket messages and moves a marker on the map using interpolated animation.
  4. Routing API is called once when the order is created to fetch the planned route. The decoded polyline is displayed as a line layer.
  5. ETA is recalculated by comparing distance remaining to average speed, or by re-calling the Routing API from the driver's current position.

Architecture diagram showing driver app, backend WebSocket server, and customer browser map

[Image: A simple architecture diagram with three boxes, "Driver App" on the left sending GPS coordinates to a "Backend / WebSocket Server" in the centre, which pushes position updates to a "Customer Browser" on the right showing the map. Arrows show the data flow direction.]

The backend implementation is outside the scope of this tutorial, but any WebSocket server that sends messages in this format works with the frontend code below:

{
  "type": "position_update",
  "orderId": "order-8821",
  "lat": 52.3741,
  "lng": 4.8952,
  "heading": 92,
  "speed": 28,
  "timestamp": 1738234521000
}

Step 1: Map Initialisation

Set up the map centred on the delivery origin. The Routing API call happens in Step 4, so for now just initialise the 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);

Step 2: Driver Marker with Heading Rotation

Create the driver marker separately so you can update its position on each GPS ping. A custom HTML element lets you rotate the marker icon to reflect the driver's heading, a small detail that makes the tracking feel much more realistic.

// 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: WebSocket Connection and Smooth Interpolation

This is the core of the tracking map. Connecting to the WebSocket is one line; the interesting part is interpolating the marker position between GPS pings so it slides smoothly rather than teleporting.

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.');
});

The 400ms interpolation window matches a typical GPS ping interval of 3–5 seconds reasonably well, the marker is always slightly behind reality but never jumps noticeably.

Step 4: Draw the Planned Route from the Routing API

Fetch the full route when the order is assigned. Store the polyline coordinates and draw them as a GeoJSON line layer. The Route Optimization API tutorial covers multi-stop scenarios; for a simple A-to-B delivery the request is straightforward.

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: ETA Calculation and Display

Calculate ETA by comparing the driver's current position to the destination. For high accuracy, re-call the Routing API from the driver's current position every 30 seconds to get a fresh travel time estimate.

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`;
    }
  }
}

Live tracking map showing driver marker mid-route with ETA panel

[Image: A tracking map view showing a blue route line between a restaurant pin and a delivery address pin, with a car emoji marker positioned partway along the route. An ETA panel in the corner reads "ETA: 8 min (3.2 km)".]

GDPR Considerations for Driver Tracking

Driver GPS coordinates are personal data under GDPR Article 4(1). The regulations that govern EU food delivery and rideshare platforms on this point are not ambiguous:

Data minimisation: Track only the fields you need for dispatch, position, heading, speed. Do not log raw GPS history beyond what is operationally necessary.

Retention limits: Granular trip tracking data should be deleted or irreversibly anonymised once the order is complete. Aggregated route data (without linking back to an individual driver) may be retained longer for network optimisation.

Legal basis: Legitimate interest under Article 6(1)(f) covers real-time dispatch tracking. For any secondary use of tracking data (analytics, benchmarking), you need to document a separate basis.

Driver transparency: Include clear tracking disclosure in driver onboarding. Drivers must be told what is collected, how long it is retained, and who can access it.

Data residency: MapAtlas processes all API requests within the EU. This eliminates the third-country transfer concern that arises with US-based mapping providers. See the EU Developer's Guide to GDPR-Compliant Map APIs for the full compliance picture.

For the ridesharing and mobility industry use case specifically, MapAtlas includes DPA documentation and EU server guarantees as standard. The logistics and delivery industry page covers fleet and multi-driver scenarios.

Production Hardening

Before shipping a tracking feature to customers, check these items:

  • WebSocket reconnection: Add ws.addEventListener('close', reconnect) with exponential backoff. Mobile networks drop connections frequently.
  • Stale position handling: If no update arrives for 15 seconds, show a "locating driver" state rather than leaving the last position visible.
  • Arrival detection: When distKm < 0.1, trigger an "arrived" state, close the WebSocket, and show a confirmation screen.
  • Camera follows driver: Call map.panTo([lng, lat]) on each position update to keep the driver centred. Give users a "lock" toggle to disable follow mode if they want to explore the map.

Next Steps

Frequently Asked Questions

How do I show a driver's location moving smoothly on a map?

GPS pings arrive every few seconds, creating visible jumps if you update the marker position directly. Smooth interpolation animates the marker between the previous position and the new one over a short duration (300–500ms), using requestAnimationFrame to move the marker in small increments. This gives the appearance of continuous movement even with infrequent GPS updates.

Is driver location data subject to GDPR?

Yes. A driver's real-time GPS coordinates are personal data under GDPR Article 4. EU food delivery and rideshare platforms must minimise retention, tracking data should be deleted or anonymised once the trip is complete. Processing requires a legal basis and must be disclosed in the driver's privacy notice.

Can I use the MapAtlas Routing API to show the planned route on the tracking map?

Yes. Fetch the route from the Routing API when a trip is created, decode the polyline, and add it as a GeoJSON line layer on the map. As the driver moves, you can optionally re-fetch the route from the current position to recalculate the ETA dynamically.

وجدت هذا مفيداً؟ شاركه.

Back to Blog