Интеграция с routing сервисами

Интеграция маршрутизации в веб-карту строится вокруг разделения ответственности между картографическим движком и внешними сервисами вычисления маршрутов. OpenLayers обеспечивает отображение геометрии, управление слоями и взаимодействие с пользовательскими событиями, тогда как routing-сервисы отвечают за вычисление оптимального пути между точками с учётом дорожной сети, ограничений и транспортного профиля.

В типичной архитектуре присутствуют следующие компоненты:

  • клиентский слой карты (OpenLayers)
  • сервис маршрутизации (HTTP API)
  • слой трансформации данных (адаптер форматов)
  • слой визуализации маршрута (vector layer)
  • механизм обновления состояния (state management)

Основной поток данных выглядит следующим образом: координаты точек → HTTP запрос → получение геометрии маршрута → декодирование → построение feature → рендеринг на карте.


Основные классы routing-сервисов

Routing-сервисы можно разделить на несколько категорий по принципу предоставления данных и алгоритмов расчёта.

Open-source routing движки

OSRM (Open Source Routing Machine) — высокопроизводительный движок, основанный на данных OpenStreetMap. Используется для быстрых расчётов маршрутов по предобработанному графу дорог.

GraphHopper — гибкий routing-движок, поддерживающий различные профили транспорта (автомобиль, велосипед, пешком), а также кастомные ограничения.

Облачные API

Mapbox Directions API от Mapbox предоставляет маршрутизацию как сервис с поддержкой пробок, профилей движения и альтернативных маршрутов.

OpenRouteService — сервис на основе OpenStreetMap данных с расширенными функциями изохрон, матриц расстояний и многоцелевой оптимизации.


Взаимодействие OpenLayers с routing API

OpenLayers не содержит встроенного routing-движка, поэтому интеграция выполняется через HTTP запросы и последующую обработку результата.

Типовая схема включает:

  1. выбор двух и более точек на карте
  2. формирование запроса к routing API
  3. получение ответа (GeoJSON или encoded polyline)
  4. преобразование в ol.Feature
  5. отображение через VectorLayer

Формирование запроса к routing-сервису

Пример запроса к OSRM

const url = `https://router.project-osrm.org/route/v1/driving/
${start[0]},${start[1]};${end[0]},${end[1]}
?overview=full&geometries=geojson`;

Ответ содержит структуру:

  • routes[]
  • geometry
  • distance
  • duration

Пример запроса к Mapbox Directions API

const url = `https://api.mapbox.com/directions/v5/mapbox/driving/
${start[0]},${start[1]};${end[0]},${end[1]}
?geometries=geojson&access_token=TOKEN`;

Сервис от Mapbox возвращает несколько альтернативных маршрутов, включая метаданные о трафике и манёврах.


Пример запроса к OpenRouteService

const url = "https://api.openrouteservice.org/v2/directions/driving-car/geojson";

fetch(url, {
  method: "POST",
  headers: {
    "Authorization": "API_KEY",
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
    coordinates: [start, end]
  })
});

Отображение маршрута в OpenLayers

После получения GeoJSON необходимо преобразовать данные в объект слоя.

Создание слоя маршрута

import VectorLayer from 'ol/layer/Vector';
import VectorSource from 'ol/source/Vector';
import Feature from 'ol/Feature';
import LineString from 'ol/geom/LineString';

const routeSource = new VectorSource();

const routeLayer = new VectorLayer({
  source: routeSource,
  style: new Style({
    stroke: new Stroke({
      color: '#2a7fff',
      width: 4
    })
  })
});

Добавление маршрута на карту

function addRoute(geojson) {
  const coords = geojson.features[0].geometry.coordinates;

  const routeFeature = new Feature({
    geometry: new LineString(coords)
  });

  routeSource.clear();
  routeSource.addFeature(routeFeature);
}

Декодирование polyline формата

Некоторые сервисы возвращают маршрут в сжатом виде (encoded polyline). В этом случае требуется декодирование.

Пример декодирования

import {decode} from '@mapbox/polyline';

const coords = decode(encodedPolyline)
  .map(c => [c[1], c[0]]);

После декодирования данные приводятся к формату [lon, lat], используемому в OpenLayers.


Работа с несколькими точками маршрута

Routing-сервисы поддерживают multi-stop маршруты (waypoints). Формирование запроса усложняется:

const coordinates = [
  start,
  waypoint1,
  waypoint2,
  end
];

В зависимости от API:

  • OSRM использует ; разделитель
  • Mapbox поддерживает массив координат
  • GraphHopper требует JSON тело запроса

Обновление маршрута в реальном времени

Для динамических сценариев (перетаскивание точек, GPS трекинг) используется частое обновление маршрута.

Debounce запросов

function debounce(fn, delay) {
  let t;
  return (...args) => {
    clearTimeout(t);
    t = setTimeout(() => fn(...args), delay);
  };
}

Использование:

const updateRoute = debounce(fetchRoute, 300);

Интеграция с событиями карты

OpenLayers предоставляет событийную модель для взаимодействия с объектами карты.

Обработка клика по карте

map.on('click', function (event) {
  const coords = event.coordinate;

  addPoint(coords);
  updateRoute(currentPoints);
});

Стилизация маршрута

Визуальное представление маршрута зависит от слоя стилей.

Динамический стиль

const routeStyle = new Style({
  stroke: new Stroke({
    color: 'rgba(0, 120, 255, 0.8)',
    width: 5
  })
});

Дополнительно применяются:

  • пунктирные линии для альтернативных маршрутов
  • градиенты для отображения скорости
  • маркеры начала и конца маршрута

Работа с альтернативными маршрутами

Некоторые сервисы (например, Mapbox от Mapbox) возвращают несколько вариантов маршрута.

Структура обработки:

routes.forEach(route => {
  const feature = new Feature({
    geometry: new LineString(route.geometry.coordinates)
  });

  routeSource.addFeature(feature);
});

Оптимизация запросов к routing API

Кэширование маршрутов

Повторяющиеся запросы между одинаковыми точками можно кешировать:

const cache = new Map();

function getCacheKey(a, b) {
  return `${a.join(',')}-${b.join(',')}`;
}

Снижение частоты запросов

  • debounce при перемещении точек
  • throttling при GPS обновлениях
  • агрегация waypoint изменений

Работа с профилями маршрутизации

Routing-сервисы поддерживают различные профили:

  • автомобильный
  • пешеходный
  • велосипедный
  • грузовой транспорт

Пример для OSRM:

/route/v1/driving/
/route/v1/walking/
/route/v1/cycling/

Для GraphHopper профиль задаётся в параметрах запроса и влияет на граф дорог.


Геометрическая согласованность данных

Все координаты должны соответствовать системе EPSG:4326 до передачи в API, а затем трансформироваться в проекцию карты OpenLayers (обычно EPSG:3857).

import {fromLonLat} from 'ol/proj';

const point = fromLonLat([lon, lat]);

Интеграция манёвров и инструкций

Некоторые routing API возвращают пошаговые инструкции:

  • turn left
  • continue straight
  • exit roundabout

Эти данные могут быть связаны с координатами и отображаться как маркеры:

instructions.forEach(step => {
  const marker = new Feature({
    geometry: new Point(step.location)
  });

  routeSource.addFeature(marker);
});

Обработка ошибок routing сервисов

Типовые сценарии:

  • отсутствие маршрута
  • превышение лимита API
  • некорректные координаты
  • недоступность сервиса

Обработка:

try {
  const res = await fetch(url);
  if (!res.ok) throw new Error('Routing error');
} catch (e) {
  routeSource.clear();
}

Комбинирование нескольких источников маршрутизации

В сложных системах применяется fallback стратегия:

  1. основной сервис (Mapbox от Mapbox)
  2. резервный OSRM
  3. локальный GraphHopper

Это обеспечивает устойчивость системы при отказах API или ограничениях квоты.