Серверная маршрутизация

Серверная маршрутизация — это процесс построения маршрутов на стороне сервера HERE с последующим получением готового результата через HTTP-запросы. В отличие от клиентской маршрутизации, где вычисления частично выполняются в браузере, серверный подход позволяет централизованно обрабатывать маршруты, скрывать API-ключи, объединять данные из нескольких источников и выполнять дополнительную бизнес-логику до передачи результатов клиентскому приложению.

Типичная схема взаимодействия выглядит следующим образом:

  1. Клиент отправляет запрос на собственный сервер.
  2. Сервер формирует запрос к HERE Routing API.
  3. HERE выполняет вычисление маршрута.
  4. Сервер получает результат.
  5. Сервер обрабатывает данные и возвращает клиенту только необходимые сведения.

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


Routing API как основа серверной маршрутизации

Основным сервисом построения маршрутов выступает Routing API.

Запросы отправляются через HTTPS к REST-интерфейсу.

Базовый адрес:

https://router.hereapi.com/v8/routes

Минимальный набор параметров:

  • origin — начальная точка;
  • destination — конечная точка;
  • transportMode — тип транспорта;
  • return — состав возвращаемых данных;
  • apiKey — ключ доступа.

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

GET https://router.hereapi.com/v8/routes
    ?transportMode=car
    &origin=52.53086,13.38469
    &destination=52.52644,13.36888
    &return=summary
    &apiKey=YOUR_API_KEY

Выполнение запросов через Node.js

Наиболее распространённый сценарий серверной маршрутизации реализуется на платформе Node.js.

Установка библиотеки для HTTP-запросов:

npm install axios

Базовый пример получения маршрута:

const axios = require("axios");

const API_KEY = process.env.HERE_API_KEY;

async function getRoute() {
    const response = await axios.get(
        "https://router.hereapi.com/v8/routes",
        {
            params: {
                transportMode: "car",
                origin: "52.53086,13.38469",
                destination: "52.52644,13.36888",
                return: "summary",
                apiKey: API_KEY
            }
        }
    );

    return response.data;
}

getRoute()
    .then(console.log)
    .catch(console.error);

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


Структура ответа Routing API

Ответ сервера обычно содержит массив маршрутов.

Пример упрощённой структуры:

{
  "routes": [
    {
      "sections": [
        {
          "summary": {
            "length": 1500,
            "duration": 320
          }
        }
      ]
    }
  ]
}

Ключевые элементы:

Поле Назначение
routes Список найденных маршрутов
sections Участки маршрута
summary Сводная информация
length Длина маршрута в метрах
duration Время движения в секундах
polyline Геометрия маршрута
actions Пошаговые инструкции

Получение расстояния:

const route =
    response.data.routes[0];

const section =
    route.sections[0];

console.log(section.summary.length);

Получение времени в пути:

console.log(section.summary.duration);

Выбор транспортного режима

Параметр transportMode определяет алгоритм расчёта.

Доступные варианты:

car
truck
pedestrian
bicycle
scooter
taxi
bus
privateBus

Пример:

params: {
    transportMode: "truck",
    origin,
    destination,
    return: "summary",
    apiKey
}

При смене режима сервер HERE автоматически использует различные правила маршрутизации.

Например:

  • для грузовиков учитываются ограничения дорог;
  • для пешеходов используются тротуары и проходы;
  • для велосипедов — велодорожки;
  • для автомобилей — дорожная сеть общего пользования.

Получение подробной геометрии маршрута

Для отображения маршрута на карте требуется его геометрия.

Запрос:

params: {
    transportMode: "car",
    origin,
    destination,
    return: "polyline",
    apiKey
}

Ответ содержит закодированную полилинию:

{
  "polyline": "BFoz5xJ67i1B1B7P..."
}

Извлечение значения:

const polyline =
    response.data.routes[0]
        .sections[0]
        .polyline;

Полученную полилинию можно передать клиентскому приложению для визуализации маршрута.


Пошаговые инструкции маршрута

Для навигационных интерфейсов используются маршрутные действия.

Запрос:

params: {
    transportMode: "car",
    origin,
    destination,
    return: "actions,instructions",
    apiKey
}

Фрагмент ответа:

{
  "actions": [
    {
      "action": "depart",
      "instruction": "Head north"
    },
    {
      "action": "turn",
      "instruction": "Turn right"
    }
  ]
}

Извлечение инструкций:

const actions =
    response.data.routes[0]
        .sections[0]
        .actions;

actions.forEach(action => {
    console.log(action.instruction);
});

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


Обработка промежуточных точек

Маршрут может содержать несколько остановок.

Пример:

params: {
    transportMode: "car",
    origin: "52.53086,13.38469",
    destination: "52.52000,13.40000",
    via: [
        "52.52500,13.39000",
        "52.52300,13.39500"
    ],
    return: "summary",
    apiKey
}

При использовании Axios массив параметров может формироваться вручную:

const query = new URLSearchParams();

query.append(
    "transportMode",
    "car"
);

query.append(
    "origin",
    origin
);

query.append(
    "destination",
    destination
);

query.append(
    "via",
    "52.52500,13.39000"
);

query.append(
    "via",
    "52.52300,13.39500"
);

В результате маршрут будет проходить через указанные точки.


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

HERE способен возвращать несколько вариантов движения.

Запрос:

params: {
    transportMode: "car",
    origin,
    destination,
    alternatives: 3,
    return: "summary",
    apiKey
}

Получение списка:

const routes =
    response.data.routes;

routes.forEach(route => {
    console.log(
        route.sections[0]
            .summary.length
    );
});

Такой механизм позволяет предлагать пользователю разные варианты:

  • самый быстрый;
  • самый короткий;
  • объездной;
  • маршрут с меньшим количеством пробок.

Учёт дорожной ситуации

Сервис автоматически использует данные о трафике.

Маршрут рассчитывается с учётом:

  • текущей загруженности дорог;
  • дорожных происшествий;
  • перекрытий;
  • средней скорости движения.

Пример:

params: {
    transportMode: "car",
    origin,
    destination,
    routingMode: "fast",
    return: "summary",
    apiKey
}

Основные режимы:

fast
short

Режим fast минимизирует время поездки.

Режим short минимизирует расстояние.


Ограничения для грузового транспорта

Routing API поддерживает специализированную маршрутизацию грузовиков.

Пример:

params: {
    transportMode: "truck",
    origin,
    destination,
    truck[height]: 4,
    truck[width]: 2.5,
    truck[length]: 12,
    truck[grossWeight]: 18000,
    return: "summary",
    apiKey
}

Учитываются:

  • высота мостов;
  • ограничения массы;
  • ограничения длины;
  • запреты для грузовиков;
  • опасные участки.

Это критически важно для логистических систем.


Избегание определённых участков

Маршрутизация поддерживает исключение некоторых типов дорог.

Пример:

params: {
    transportMode: "car",
    origin,
    destination,
    avoid: "tollRoad",
    return: "summary",
    apiKey
}

Возможные варианты:

tollRoad
ferry
tunnel
dirtRoad
controlledAccessHighway

Несколько ограничений:

avoid:
"tollRoad,ferry"

Серверный API-шлюз для маршрутизации

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

Пример на Express:

const express = require("express");
const axios = require("axios");

const app = express();

app.get("/route", async (req, res) => {

    try {

        const response =
            await axios.get(
                "https://router.hereapi.com/v8/routes",
                {
                    params: {
                        transportMode: "car",
                        origin: req.query.origin,
                        destination:
                            req.query.destination,
                        return:
                            "summary,polyline",
                        apiKey:
                            process.env.HERE_API_KEY
                    }
                }
            );

        res.json(response.data);

    } catch (error) {

        res.status(500).json({
            error: error.message
        });

    }

});

app.listen(3000);

Преимущества подобного подхода:

  • скрытие API-ключа;
  • централизованная бизнес-логика;
  • единый формат данных;
  • контроль доступа;
  • кэширование запросов.

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

При большом количестве запросов полезно хранить результаты в кэше.

Простейший пример:

const cache = new Map();

function buildKey(
    origin,
    destination
) {
    return `${origin}:${destination}`;
}

Проверка кэша:

const key =
    buildKey(
        origin,
        destination
    );

if (cache.has(key)) {
    return cache.get(key);
}

Сохранение:

cache.set(
    key,
    response.data
);

В производственных системах обычно применяются:

  • Redis;
  • Memcached;
  • KeyDB.

Обработка ошибок

Серверное приложение должно корректно реагировать на ошибки API.

Типичный шаблон:

try {

    const response =
        await axios.get(url);

} catch (error) {

    if (error.response) {

        console.error(
            error.response.status
        );

        console.error(
            error.response.data
        );

    } else {

        console.error(
            error.message
        );

    }

}

Наиболее распространённые причины ошибок:

Код Причина
400 Некорректные параметры
401 Неверный ключ API
403 Недостаточно прав
404 Ресурс не найден
429 Превышен лимит запросов
500 Ошибка сервера

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

Логистические платформы часто рассчитывают сотни или тысячи маршрутов.

Последовательный подход:

for (const item of points) {

    const route =
        await getRoute(
            item.origin,
            item.destination
        );

}

Параллельный подход:

const promises =
    points.map(point =>
        getRoute(
            point.origin,
            point.destination
        )
    );

const routes =
    await Promise.all(promises);

Для предотвращения превышения лимитов обычно применяется ограничение параллелизма.

Пример через пакет p-limit:

const limit = pLimit(5);

const routes =
    await Promise.all(
        points.map(point =>
            limit(() =>
                getRoute(
                    point.origin,
                    point.destination
                )
            )
        )
    );

Интеграция с клиентскими картами

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

{
  "distance": 15000,
  "duration": 1200,
  "polyline": "BFoz5xJ67i1B..."
}

На клиенте данные используются для:

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

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