Leaflet.routing для маршрутизации

Установка и подключение плагина

Маршрутизация в экосистеме Leaflet реализуется через плагин Leaflet Routing Machine, который добавляет слой построения маршрутов, управление точками и интеграцию с внешними routing-сервисами.

Подключение через CDN:

<link rel="stylesheet" href="https://unpkg.com/leaflet-routing-machine/dist/leaflet-routing-machine.css" />
<script src="https://unpkg.com/leaflet-routing-machine/dist/leaflet-routing-machine.js"></script>

Также требуется базовый Leaflet:

<link rel="stylesheet" href="https://unpkg.com/leaflet/dist/leaflet.css" />
<script src="https://unpkg.com/leaflet/dist/leaflet.js"></script>

При использовании npm:

npm install leaflet leaflet-routing-machine
import L from "leaflet";
import "leaflet-routing-machine";

Базовая маршрутизация

Основная сущность плагина — L.Routing.control, которая создаёт маршрут между точками.

const map = L.map("map").setView([51.505, -0.09], 13);

L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", {
  attribution: "© OpenStreetMap contributors"
}).addTo(map);

L.Routing.control({
  waypoints: [
    L.latLng(51.505, -0.09),
    L.latLng(51.515, -0.1)
  ]
}).addTo(map);

После выполнения создаётся маршрут с интерактивной линией и маркерами точек.


Waypoints и управление точками

Маршрут строится на основе массива waypoints.

waypoints: [
  L.latLng(lat1, lng1),
  L.latLng(lat2, lng2)
]

Особенности:

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

Программное обновление:

routingControl.setWaypoints([
  L.latLng(51.5, -0.1),
  L.latLng(51.52, -0.12),
  L.latLng(51.53, -0.13)
]);

Очистка маршрута:

routingControl.setWaypoints([]);

Подключение routing-сервера OSRM

По умолчанию используется сервис OSRM (Open Source Routing Machine).

Явная настройка:

L.Routing.control({
  router: L.Routing.osrmv1({
    serviceUrl: "https://router.project-osrm.org/route/v1"
  }),
  waypoints: [
    L.latLng(51.505, -0.09),
    L.latLng(51.515, -0.1)
  ]
}).addTo(map);

OSRM обеспечивает:

  • автомобильные маршруты
  • пешие маршруты (в зависимости от профиля сервера)
  • расчёт расстояния и времени

Использование альтернативных провайдеров

Mapbox Routing

L.Routing.control({
  router: L.Routing.mapbox("TOKEN_API"),
  waypoints: [
    L.latLng(51.505, -0.09),
    L.latLng(51.515, -0.1)
  ]
}).addTo(map);

Поддерживает:

  • дорожные сети высокой точности
  • traffic-aware маршруты (в платных планах)

GraphHopper

L.Routing.control({
  router: L.Routing.graphHopper("API_KEY"),
  waypoints: [
    L.latLng(51.505, -0.09),
    L.latLng(51.515, -0.1)
  ]
}).addTo(map);

Особенности:

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

Настройка внешнего вида маршрута

Стиль линии

lineOptions: {
  styles: [
    { color: "blue", opacity: 0.7, weight: 5 }
  ]
}

Альтернативные маршруты

showAlternatives: true

Система возвращает несколько вариантов пути, которые можно переключать.


Пользовательский интерфейс контроллера

По умолчанию добавляется панель управления. Её можно скрыть:

L.Routing.control({
  show: false
})

Или полностью отключить создание UI:

createMarker: function() { return null; }

Кастомизация маркеров

createMarker: function(i, waypoint, n) {
  return L.marker(waypoint.latLng, {
    draggable: true
  }).bindPopup(`Точка ${i + 1}`);
}

Расширенные сценарии:

  • кастомные иконки
  • нумерация точек
  • разные стили для start/end

Обработка событий маршрутизации

Построение маршрута

routingControl.on("routesfound", function(e) {
  const routes = e.routes;
  console.log(routes[0].summary);
});

Ошибки

routingControl.on("routingerror", function(e) {
  console.error(e.error);
});

Изменение маршрута

routingControl.on("waypointschanged", function(e) {
  console.log("Waypoints updated");
});

Структура ответа маршрута

Объект маршрута содержит:

  • distance — длина маршрута
  • time — время в секундах
  • coordinates — массив точек линии
  • instructions — пошаговые инструкции

Пример обработки:

routingControl.on("routesfound", function(e) {
  const route = e.routes[0];

  console.log("Distance:", route.summary.totalDistance);
  console.log("Time:", route.summary.totalTime);

  route.instructions.forEach(step => {
    console.log(step.text);
  });
});

Геокодирование и интеграция поиска адресов

Leaflet Routing Machine не включает встроенный геокодер, но поддерживает интеграцию:

geocoder: L.Control.Geocoder.nominatim()

Пример подключения:

L.Routing.control({
  geocoder: L.Control.Geocoder.nominatim()
}).addTo(map);

Это позволяет:

  • искать адреса
  • автоматически задавать waypoint
  • строить маршрут по строковым запросам

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

Частый сценарий — обновление маршрута по клику:

map.on("click", function(e) {
  const waypoints = routingControl.getWaypoints();
  waypoints.push(e.latlng);

  routingControl.setWaypoints(waypoints);
});

Оптимизация производительности

При большом количестве запросов маршрутизации учитываются ограничения:

  • кэширование маршрутов
  • debounce при изменении waypoint
  • ограничение частоты запросов к OSRM

Пример debounce:

let timeout;

map.on("click", function(e) {
  clearTimeout(timeout);

  timeout = setTimeout(() => {
    routingControl.spliceWaypoints(
      routingControl.getWaypoints().length,
      0,
      e.latlng
    );
  }, 300);
});

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

altLineOptions: {
  styles: [
    { color: "gray", opacity: 0.5, weight: 4 }
  ]
}

Переключение маршрутов выполняется автоматически через UI или программно:

routingControl.setAlternatives([
  route1,
  route2
]);

Интеграция с пользовательскими данными

Маршруты часто строятся на основе внешних данных:

fetch("/api/points")
  .then(res => res.json())
  .then(points => {
    routingControl.setWaypoints(
      points.map(p => L.latLng(p.lat, p.lng))
    );
  });

Ограничения и особенности работы OSRM

При использовании публичного сервера:

  • ограничение количества запросов
  • отсутствие гарантированной SLA
  • зависимость от сети
  • ограничения на сложные маршруты

В production-средах обычно используется собственный OSRM-инстанс.


Использование собственного routing-сервера

Настройка кастомного backend:

L.Routing.control({
  router: L.Routing.osrmv1({
    serviceUrl: "https://my-server.com/route/v1"
  })
}).addTo(map);

Преимущества:

  • контроль нагрузки
  • кастомные профили маршрутов
  • независимость от публичных API

Стилизация интерфейса маршрутизатора

CSS переопределение:

.leaflet-routing-container {
  background: white;
  font-size: 14px;
}

.leaflet-routing-alt {
  max-height: 200px;
  overflow-y: auto;
}

Программное управление маршрутом

Удаление последней точки:

routingControl.spliceWaypoints(
  routingControl.getWaypoints().length - 1,
  1
);

Замена стартовой точки:

routingControl.spliceWaypoints(0, 1, L.latLng(51.51, -0.11));

Комбинирование с другими слоями Leaflet

Маршрут можно комбинировать с:

  • маркерами POI
  • heatmap слоями
  • geojson-объектами
  • пользовательскими overlay
L.geoJSON(data).addTo(map);
routingControl.addTo(map);

Расширение функциональности плагина

Leaflet Routing Machine допускает расширение через:

  • кастомные router-адаптеры
  • переопределение plan
  • кастомные instruction formatter’ы

Пример кастомного форматтера:

formatter: new L.Routing.Formatter({
  language: "ru"
})

Использование в сложных интерфейсах

В SPA-приложениях маршрутизация часто управляется состоянием:

  • React/Vue интеграция через refs
  • хранение waypoints в store
  • синхронизация URL с координатами
history.pushState({}, "", `?from=${a.lat},${a.lng}&to=${b.lat},${b.lng}`);

Обработка перегрузки маршрутов

При массовых изменениях:

  • группировка обновлений
  • отключение автопересчёта
  • ручной trigger пересчёта
routingControl.route();