Манёвры и инструкции

При построении маршрута навигационная система должна не только вычислить путь между точками, но и сформировать набор инструкций для движения. В экосистеме Mapbox эта информация предоставляется сервисом маршрутизации через объект маршрута, содержащий последовательность шагов (steps) и манёвров (maneuvers).

Манёвр представляет собой конкретное действие, которое необходимо выполнить в определённой точке маршрута:

  • поворот налево;
  • поворот направо;
  • движение прямо;
  • разворот;
  • съезд с автомагистрали;
  • въезд на кольцевую развязку;
  • прибытие в пункт назначения.

Каждый шаг маршрута содержит объект maneuver, описывающий действие максимально подробно.

Пример структуры ответа:

{
  "maneuver": {
    "bearing_after": 180,
    "bearing_before": 90,
    "location": [37.6176, 55.7558],
    "modifier": "right",
    "type": "turn",
    "instruction": "Turn right onto Tverskaya Street"
  }
}

Основные свойства:

Свойство Описание
type Тип манёвра
modifier Направление действия
instruction Готовая текстовая инструкция
location Координаты точки манёвра
bearing_before Азимут до манёвра
bearing_after Азимут после манёвра

Получение инструкций маршрута

Для получения детализированных инструкций необходимо запросить маршрут через Directions API с параметром steps=true.

Пример запроса:

const url =
  'https://api.mapbox.com/directions/v5/mapbox/driving/' +
  '37.6176,55.7558;37.6200,55.7600' +
  '?steps=true' +
  '&geometries=geojson' +
  '&access_token=' + mapboxgl.accessToken;

fetch(url)
  .then(response => response.json())
  .then(data => {
    console.log(data);
  });

После получения данных инструкции находятся внутри структуры:

data.routes[0].legs[0].steps

Каждый элемент массива представляет отдельный этап маршрута.

Пример:

const steps = data.routes[0].legs[0].steps;

steps.forEach(step => {
  console.log(step.maneuver.instruction);
});

Результат:

Head north on Tverskaya Street
Turn right onto Mokhovaya Street
Continue straight
Arrive at destination

Структура шага маршрута

Типичный шаг содержит большое количество информации.

Пример:

{
  distance: 320.5,
  duration: 45.1,
  name: "Tverskaya Street",
  geometry: {...},
  maneuver: {...},
  mode: "driving",
  driving_side: "right"
}

Наиболее важные поля:

distance

Расстояние данного сегмента маршрута.

console.log(step.distance);

Результат:

320.5

Значение указывается в метрах.


duration

Время прохождения сегмента.

console.log(step.duration);

Результат:

45.1

Единица измерения — секунды.


name

Название дороги.

console.log(step.name);

Пример:

Leninsky Prospekt

maneuver

Описание действия навигации.

console.log(step.maneuver);

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


Типы манёвров

Поле type определяет категорию действия.

turn

Обычный поворот.

{
  type: "turn",
  modifier: "left"
}

Инструкция:

Turn left

depart

Начало маршрута.

{
  type: "depart"
}

Инструкция:

Head north

arrive

Завершение маршрута.

{
  type: "arrive"
}

Инструкция:

You have arrived

merge

Перестроение на другую дорогу.

{
  type: "merge"
}

Пример:

Merge onto highway

on ramp

Въезд на автомагистраль.

{
  type: "on ramp"
}

Пример:

Take the ramp

off ramp

Съезд с автомагистрали.

{
  type: "off ramp"
}

Пример:

Take exit 5

roundabout

Круговое движение.

{
  type: "roundabout"
}

Пример:

Enter the roundabout

roundabout turn

Поворот внутри кольцевой развязки.

{
  type: "roundabout turn"
}

Используется в некоторых странах и схемах дорожной сети.


continue

Продолжение движения без изменения направления.

{
  type: "continue"
}

Инструкция:

Continue straight

end of road

Достижение конца дороги.

{
  type: "end of road"
}

Пример:

At the end of the road turn right

fork

Развилка.

{
  type: "fork"
}

Пример:

Keep left at the fork

new name

Изменение названия дороги без изменения направления движения.

{
  type: "new name"
}

Пример:

Continue onto Garden Ring

notification

Информационное сообщение.

{
  type: "notification"
}

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


Направления манёвров

Поле modifier уточняет направление действия.

left

modifier: "left"

Поворот налево.


modifier: "right"

Поворот направо.


straight

modifier: "straight"

Движение прямо.


slight left

modifier: "slight left"

Небольшое отклонение влево.


slight right

modifier: "slight right"

Небольшое отклонение вправо.


sharp left

modifier: "sharp left"

Резкий поворот налево.


sharp right

modifier: "sharp right"

Резкий поворот направо.


uturn

modifier: "uturn"

Разворот.


Формирование собственного списка инструкций

Готовые инструкции можно вывести в пользовательском интерфейсе.

HTML:

<div id="instructions"></div>

Jav * aScript:

const instructions =
  document.getElementById('instructions');

steps.forEach(step => {
  const div = document.createElement('div');

  div.textContent =
    step.maneuver.instruction;

  instructions.appendChild(div);
});

Результат:

Head north
Turn right onto Main Street
Continue straight
Arrive at destination

Создание нумерованного списка

const list = document.createElement('ol');

steps.forEach(step => {
  const item = document.createElement('li');

  item.textContent =
    step.maneuver.instruction;

  list.appendChild(item);
});

document.body.appendChild(list);

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


Отображение расстояния до следующего действия

Информация о длине сегмента уже присутствует в объекте шага.

steps.forEach(step => {
  console.log(
    step.maneuver.instruction,
    step.distance
  );
});

Вывод:

Turn right 120
Continue straight 450
Turn left 80

Более удобный вариант:

steps.forEach(step => {
  console.log(
    `${step.maneuver.instruction} (${Math.round(step.distance)} м)`
  );
});

Конвертация расстояний в километры

function formatDistance(distance) {
  if (distance >= 1000) {
    return (distance / 1000).toFixed(1) + ' км';
  }

  return Math.round(distance) + ' м';
}

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

console.log(
  formatDistance(step.distance)
);

Пример:

2.3 км

Создание пиктограмм для манёвров

Для повышения удобства интерфейса тип манёвра можно преобразовывать в иконки.

function getIcon(type, modifier) {
  if (type === 'turn') {
    if (modifier === 'left') {
      return '⬅';
    }

    if (modifier === 'right') {
      return '➡';
    }
  }

  if (type === 'arrive') {
    return '?';
  }

  return '↑';
}

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

steps.forEach(step => {
  console.log(
    getIcon(
      step.maneuver.type,
      step.maneuver.modifier
    ),
    step.maneuver.instruction
  );
});

Отображение точек манёвров на карте

Каждый манёвр содержит координаты.

step.maneuver.location

Пример:

steps.forEach(step => {
  new mapboxgl.Marker()
    .setLngLat(step.maneuver.location)
    .addTo(map);
});

На карте появятся маркеры в местах выполнения действий.


Подписи манёвров

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

steps.forEach(step => {
  const popup = new mapboxgl.Popup({
    offset: 20
  }).setText(
    step.maneuver.instruction
  );

  new mapboxgl.Marker()
    .setLngLat(step.maneuver.location)
    .setPopup(popup)
    .addTo(map);
});

Подсветка текущего манёвра

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

Пример поиска следующего шага:

function getNextStep(index) {
  return steps[index + 1];
}

Вывод инструкции:

const nextStep = getNextStep(currentIndex);

if (nextStep) {
  console.log(
    nextStep.maneuver.instruction
  );
}

Голосовые инструкции

Текст манёвров совместим с Web Speech API.

function speak(text) {
  const utterance =
    new SpeechSynthesisUtterance(text);

  speechSynthesis.speak(utterance);
}

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

speak(
  step.maneuver.instruction
);

Пример команды:

Turn left onto Main Street

Локализация инструкций

Directions API позволяет получать инструкции на различных языках.

Пример:

const url =
  'https://api.mapbox.com/directions/v5/mapbox/driving/' +
  coordinates +
  '?steps=true' +
  '&language=ru' +
  '&access_token=' + mapboxgl.accessToken;

Результат:

Поверните направо
Через 300 метров держитесь левее
Вы прибыли в пункт назначения

Поддерживаются десятки языков, включая:

  • русский;
  • английский;
  • немецкий;
  • французский;
  • испанский;
  • итальянский;
  • китайский;
  • японский.

Получение информации о выездах и съездах

Для автомагистралей Directions API предоставляет дополнительные сведения.

Пример:

step.destinations

Содержимое:

Airport, City Center

Информация может использоваться для отображения дорожных указателей.


Работа с кольцевыми развязками

Для кругового движения присутствуют специальные свойства.

Пример:

{
  type: "roundabout",
  exit: 3
}

Поле exit показывает номер съезда.

console.log(
  `Съезд №${step.maneuver.exit}`
);

Результат:

Съезд №3

Построение панели навигации

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

function renderInstructions(steps) {
  const container =
    document.getElementById('instructions');

  container.innerHTML = '';

  steps.forEach(step => {
    const row =
      document.createElement('div');

    row.className = 'instruction';

    row.innerHTML = `
      <strong>
        ${step.maneuver.instruction}
      </strong>
      <br>
      ${Math.round(step.distance)} м
    `;

    container.appendChild(row);
  });
}

Подобный подход используется большинством веб-навигаторов и транспортных приложений. Благодаря объектам step и maneuver библиотека Mapbox GL JS в сочетании с Directions API позволяет создавать полноценные навигационные интерфейсы с текстовыми подсказками, визуализацией манёвров, голосовым сопровождением, локализацией и отображением ключевых точек маршрута на карте.