Карта отслеживания — это экран с наивысшей вовлечённостью в любом приложении доставки еды или аренды автомобилей. Это то, на что смотрят клиенты в ожидании. Правильная реализация, плавное движение, точные РПВ, чётко видимая линия маршрута — это разница между приложением, которое выглядит профессионально, и тем, которое кажется ненадёжным.
Это руководство строит карту отслеживания, видимую клиентом, с нуля: бэкенд, который транслирует позиции GPS через WebSocket, фронтенд, который их получает и перемещает маркер водителя без прыжков, линия маршрута из MapAtlas Routing API и живой дисплей РПВ. Полная реализация занимает менее 70 строк JavaScript на стороне клиента, предназначена для интеграции с любым бэкендом, способным отправлять сообщения WebSocket.
Архитектура работает для доставки еды, продуктов, аренды автомобилей, выездного обслуживания и любых других случаев, когда транспортное средство движется к фиксированному пункту назначения, а клиент наблюдает этот процесс в реальном времени.
Architecture Overview
Before writing code, it helps to understand the data flow:
- Driver app (mobile, GPS hardware) sends latitude/longitude to your backend every 3–5 seconds.
- Backend (Node.js, Python, Go, your choice) persists the last known position and broadcasts it via WebSocket to all connected order subscribers.
- Customer browser receives WebSocket messages and moves a marker on the map using interpolated animation.
- Routing API is called once when the order is created to fetch the planned route. The decoded polyline is displayed as a line layer.
- ETA is recalculated by comparing distance remaining to average speed, or by re-calling the Routing API from the driver's current position.
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.');
});
Окно интерполяции 400 мс хорошо соответствует типичному интервалу GPS пинга в 3–5 секунд, маркер всегда немного отстаёт от реальности, но никогда не прыгает заметно.
Step 4: Draw the Planned Route from the Routing API
Получите полный маршрут, когда заказ назначен. Сохраните координаты полилинии и нарисуйте их как слой линий GeoJSON. Руководство по API оптимизации маршрутов охватывает сценарии с несколькими остановками; для простой доставки от точки A к точке B запрос прост.
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
Рассчитайте РПВ, сравнивая текущую позицию водителя с пункт назначением. Для высокой точности повторно вызовите Routing API из текущей позиции водителя каждые 30 секунд, чтобы получить свежую оценку времени в пути.
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 по отслеживанию водителей
GPS-координаты водителей — это персональные данные в соответствии со статьёй 4(1) GDPR. Положения, регулирующие платформы доставки еды и аренды автомобилей в ЕС по этому вопросу, не двусмысленны:
Минимизация данных: отслеживайте только поля, необходимые для диспетчеризации, позицию, курс, скорость. Не логируйте историю необработанного GPS дальше того, что необходимо для работы.
Ограничения сохранения: детальные данные отслеживания поездки должны быть удалены или необратимо анонимизированы после завершения заказа. Агрегированные данные маршрута (без связи с отдельным водителем) могут сохраняться дольше для оптимизации сети.
Правовая основа: законный интерес в соответствии со статьей 6(1)(f) охватывает отслеживание диспетчеризации в реальном времени. Для любого вторичного использования данных отслеживания (аналитика, сравнительный анализ) необходимо документировать отдельное основание.
Прозрачность водителя: включите чёткое раскрытие отслеживания при подключении водителя. Водителям должно быть сообщено, что собирается, как долго это сохраняется и кто к этому может получить доступ.
Размещение данных: MapAtlas обрабатывает все запросы API в пределах ЕС. Это исключает проблему трансграничного переноса, которая возникает с поставщиками картографических услуг на основе США. Смотрите EU Developer's Guide to GDPR-Compliant Map APIs для полной картины соответствия.
Для варианта использования индустрия совместного проезда и мобильности в частности MapAtlas включает документацию DPA и гарантии серверов ЕС как стандартные. На странице индустрии логистики и доставки рассматриваются сценарии с несколькими водителями и парками.
Подготовка к производству
Перед отправкой функции отслеживания клиентам проверьте эти элементы:
- Переподключение WebSocket: добавьте
ws.addEventListener('close', reconnect)с экспоненциальной отсрочкой. Мобильные сети часто разрывают соединения. - Обработка устаревшей позиции: если обновление не приходит в течение 15 секунд, покажите состояние «поиск водителя» вместо того, чтобы оставлять последнюю позицию видимой.
- Обнаружение прибытия: когда
distKm < 0.1, запустите состояние «прибыл», закройте WebSocket и покажите экран подтверждения. - Камера следует за водителем: вызовите
map.panTo([lng, lat])при каждом обновлении позиции, чтобы держать водителя в центре. Дайте пользователям переключатель «блокировки» для отключения режима отслеживания, если они хотят исследовать карту.
Следующие шаги
- Зарегистрируйтесь для получения бесплатного API-ключа MapAtlas и начните работу
- Прочитайте руководство по API оптимизации маршрутов, чтобы добавить диспетчеризацию с несколькими остановками в приложение доставки
- Изучите руководство по карте недвижимости для другого примера динамических слоёв карты на основе данных
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.
