Skip to main content
Карта отслеживания водителя в реальном времени для доставки и такси
Tutorials

Карта отслеживания водителя в реальном времени для доставки и такси

Создайте карту отслеживания водителя в реальном времени: GPS-обновления через WebSocket, плавная интерполяция маркера, маршрут и ETA на MapAtlas Maps и Routing API.

Brent van der Heiden8 min read
#live tracking map#driver tracking#real-time map#websocket map#delivery tracking#rideshare app

Карта отслеживания становится самым просматриваемым экраном в любом приложении доставки еды или такси. Именно на неё клиенты смотрят, пока ждут. Плавное движение, точное время прибытия и понятная линия маршрута отличают приложение, которое выглядит профессионально, от приложения, которое кажется ненадёжным.

В этом туториале клиентская карта отслеживания строится с нуля: бэкенд транслирует GPS-позиции через WebSocket, фронтенд получает их и перемещает маркер водителя без скачков, линия маршрута строится через MapAtlas Routing API, а время прибытия обновляется в реальном времени. Полная реализация занимает меньше 70 строк клиентского JavaScript и подключается к любому бэкенду, который умеет отправлять сообщения через WebSocket.

Такая архитектура подходит для доставки еды и продуктов, такси, выездного обслуживания и любых других сценариев, где транспорт движется к фиксированной точке, а клиент наблюдает за этим в реальном времени.

Обзор архитектуры

Перед тем как писать код, полезно разобраться в потоке данных:

  1. Приложение водителя (смартфон, GPS-трекер) отправляет широту и долготу на ваш бэкенд каждые 3-5 секунд.
  2. Бэкенд (Node.js, Python, Go, на ваш выбор) сохраняет последнюю известную позицию и рассылает её через WebSocket всем подписчикам заказа.
  3. Браузер клиента получает сообщения WebSocket и перемещает маркер на карте с интерполированной анимацией.
  4. Routing API вызывается один раз при создании заказа, чтобы получить запланированный маршрут. Декодированная полилиния отображается линейным слоем.
  5. Время прибытия (ETA) пересчитывается по оставшемуся расстоянию и средней скорости или повторным вызовом Routing API от текущей позиции водителя.

Реализация бэкенда выходит за рамки этого туториала, но с фронтенд-кодом ниже работает любой WebSocket-сервер, который отправляет сообщения в таком формате:

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

Шаг 1: инициализация карты

Настройте карту с центром в точке отправления. Вызов Routing API будет на шаге 4, а пока просто инициализируйте холст.

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

// Маркер пункта назначения (ресторан или точка выдачи)
const destination = [4.9001, 52.3791];

new mapmetricsgl.Marker({ color: '#EF4444' })
  .setLngLat(destination)
  .setPopup(new mapmetricsgl.Popup().setHTML('<strong>Точка выдачи</strong>'))
  .addTo(map);

Шаг 2: маркер водителя с поворотом по направлению движения

Создайте маркер водителя отдельно, чтобы обновлять его позицию при каждом GPS-сигнале. Собственный HTML-элемент позволяет поворачивать значок маркера по направлению движения водителя. Это небольшая деталь, но с ней отслеживание выглядит гораздо реалистичнее.

// Собственный элемент, чтобы его можно было поворачивать
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)`;
}

Шаг 3: подключение WebSocket и плавная интерполяция

Это ядро карты отслеживания. Подключение к WebSocket занимает одну строку, а самое интересное здесь в интерполяции позиции маркера между GPS-сигналами, чтобы он плавно скользил, а не перескакивал.

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) {
    // Плавная анимация от предыдущей позиции к новой
    interpolateMarker(prevPos.lat, prevPos.lng, lat, lng, 400);
  } else {
    // Первый сигнал: сразу ставим маркер
    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 секунд: маркер всегда немного отстаёт от реального положения, но никогда заметно не перескакивает.

Шаг 4: отрисовка запланированного маршрута через Routing API

Получите полный маршрут в момент назначения заказа. Сохраните координаты полилинии и нарисуйте их линейным слоем GeoJSON. Сценарии с несколькими остановками разобраны в туториале по Route Optimization API, а для простой доставки из точки A в точку B запрос выглядит просто.

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; // секунды
}

Шаг 5: расчёт и отображение времени прибытия

Рассчитывайте время прибытия, сравнивая текущую позицию водителя с пунктом назначения. Для высокой точности каждые 30 секунд повторно вызывайте Routing API от текущей позиции водителя, чтобы получать свежую оценку времени в пути.

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);

  // Быстрая оценка на клиенте (средняя скорость в городе 30 км/ч)
  const minutesEstimate = Math.ceil((distKm / 30) * 60);
  document.getElementById('eta').textContent =
    distKm < 0.1
      ? 'Прибывает'
      : `Прибытие через ${minutesEstimate} мин (${distKm.toFixed(1)} км)`;

  // Для точности каждые 30 секунд запрашиваем Routing API заново
  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 = `Прибытие через ${mins} мин`;
    }
  }
}

GDPR и отслеживание водителей

GPS-координаты водителей являются персональными данными по статье 4(1) GDPR. Здесь применяются принципы GDPR о минимизации данных и ограничении хранения из статьи 5:

Минимизация данных: отслеживайте только поля, нужные для диспетчеризации: позицию, направление движения, скорость. Не храните сырую историю GPS сверх того, что необходимо для работы.

Ограничение сроков хранения: детальные данные отслеживания поездки нужно удалять или необратимо анонимизировать после завершения заказа. Агрегированные данные о маршрутах (без привязки к конкретному водителю) можно хранить дольше для оптимизации сети.

Правовое основание: законный интерес по статье 6(1)(f), подкреплённый задокументированной оценкой баланса интересов, или исполнение договора по статье 6(1)(b) обычно покрывает отслеживание в реальном времени для диспетчеризации. Для любого вторичного использования данных отслеживания (аналитика, бенчмаркинг) нужно задокументировать отдельное основание.

Прозрачность для водителей: включите понятное уведомление об отслеживании в процесс подключения водителей. Водителям нужно сообщить, какие данные собираются, как долго хранятся и у кого есть к ним доступ.

Место обработки данных: MapAtlas обрабатывает все запросы к API в пределах ЕС. Это снимает вопрос о передаче данных в третьи страны, который возникает с американскими картографическими провайдерами. Полная картина соответствия описана в статье Руководство для EU-разработчиков по API карт, соответствующим GDPR.

Для сценариев из отрасли такси и мобильности MapAtlas по умолчанию предоставляет документацию DPA и гарантии размещения серверов в ЕС. На странице отрасли логистики и доставки разобраны сценарии с автопарками и множеством водителей.

Подготовка к продакшену

Перед запуском функции отслеживания для клиентов проверьте следующее:

  • Переподключение WebSocket: добавьте ws.addEventListener('close', reconnect) с экспоненциальной задержкой. Мобильные сети часто обрывают соединение.
  • Устаревшая позиция: если обновление не пришло в течение 15 секунд, показывайте состояние «определяем местоположение водителя», а не оставляйте на карте последнюю позицию.
  • Определение прибытия: когда distKm < 0.1, переключайтесь в состояние «прибыл», закрывайте WebSocket и показывайте экран подтверждения.
  • Камера следует за водителем: вызывайте map.panTo([lng, lat]) при каждом обновлении позиции, чтобы водитель оставался в центре. Добавьте переключатель фиксации, чтобы пользователь мог отключить слежение и свободно изучать карту.

Что дальше

Часто задаваемые вопросы

Как показать плавное движение водителя на карте?

GPS-сигналы приходят раз в несколько секунд, и если обновлять позицию маркера напрямую, возникают заметные скачки. Плавная интерполяция анимирует маркер от предыдущей позиции к новой за короткое время (300-500 мс) и с помощью requestAnimationFrame сдвигает его небольшими шагами. Так движение выглядит непрерывным даже при редких обновлениях GPS.

Подпадают ли данные о местоположении водителя под GDPR?

Да. GPS-координаты водителя в реальном времени являются персональными данными по статье 4 GDPR. Платформам следует хранить данные отслеживания не дольше необходимого, а самый безопасный подход по умолчанию состоит в том, чтобы удалять или анонимизировать их после завершения поездки. Для обработки требуется правовое основание, и её необходимо указать в уведомлении о конфиденциальности для водителей.

Можно ли показать запланированный маршрут на карте отслеживания через MapAtlas Routing API?

Да. Получите маршрут из Routing API при создании поездки, декодируйте полилинию и добавьте её на карту как линейный слой GeoJSON. По мере движения водителя можно при необходимости запрашивать маршрут заново от текущей позиции, чтобы динамически пересчитывать время прибытия.

Часто задаваемые вопросы

Как показать плавное движение водителя на карте?

GPS-сигналы приходят раз в несколько секунд, и если обновлять позицию маркера напрямую, возникают заметные скачки. Плавная интерполяция анимирует маркер от предыдущей позиции к новой за короткое время (300-500 мс) и с помощью `requestAnimationFrame` сдвигает его небольшими шагами. Так движение выглядит непрерывным даже при редких обновлениях GPS.

Подпадают ли данные о местоположении водителя под GDPR?

Да. GPS-координаты водителя в реальном времени являются персональными данными по статье 4 GDPR. Платформам следует хранить данные отслеживания не дольше необходимого, а самый безопасный подход по умолчанию состоит в том, чтобы удалять или анонимизировать их после завершения поездки. Для обработки требуется правовое основание (обычно законный интерес для диспетчеризации или исполнение договора), и её необходимо указать в уведомлении о конфиденциальности для водителей.

Можно ли показать запланированный маршрут на карте отслеживания через MapAtlas Routing API?

Да. Получите маршрут из Routing API при создании поездки, декодируйте полилинию и добавьте её на карту как линейный слой GeoJSON. По мере движения водителя можно при необходимости запрашивать маршрут заново от текущей позиции, чтобы динамически пересчитывать время прибытия.

Оказалось полезным? Поделитесь.

Об авторе

Brent van der Heiden

Автор

Brent van der Heiden

Co-Founder & CEO at MapAtlas

Brent built MapAtlas out of a conviction that developers deserve location APIs with fair pricing and genuine end-user privacy. Before that he co-founded MapMetrics, a community mapping and navigation app. He writes about geospatial infrastructure, map data, AI search visibility, and how location data powers the products people rely on every day.

Все статьи →
Вернуться в блог