Поиск и маршруты

Маршруты

Расстояние, время, варианты поездки и геометрия — через RoutingClient, без обязательной панели.

1. Рассчитайте маршрут

Карта для расчёта не нужна. JavaScript возвращает Promise<RoutingResponse>, Flutter — Future<RoutingResponse>. Создайте клиент один раз на время работы экрана.

Вкладка «Ответ» показывает сокращённый пример: первый вариант и начало геометрии первого участка. Полный ответ доступен ниже.

import { RoutingClient } from "@umap-llc/map-react/routing";
const routing = new RoutingClient({
  styleUrl: "YOUR_STYLE_URL",
  accessToken: "YOUR_ACCESS_TOKEN",
});
const result = await routing.calculate({
  from: { coordinates: [71.4106836, 51.1355788], name: "Астана Опера" },
  to: { coordinates: [71.4304544, 51.1282903], name: "Байтерек" },
  mode: "car",
  alternatives: 3,
});
for (const route of result.routes) {
  console.log(route.id, Math.ceil(route.durationS / 60), route.distanceM);
}
Полный JSON маршрутов ↗

2. Выберите транспорт и точки

name — подпись точки; клиент не ищет её координаты по названию. Сначала получите координаты через PlacesClient или свои данные.

ПолеТип / значение
from / toТочки отправления и назначения: coordinates и необязательное name.
modecar, walk, transit. Flutter: RoutingMode.car / walk / transit.
viaДо трёх промежуточных точек: всего до пяти вместе с from/to.
alternativesКоличество вариантов: 1–3.
walkingSpeedKmhСкорость ходьбы, км/ч.
maxTransfersМаксимум пересадок для transit.
maxWalkingDistanceMОграничение пешего участка, метры.
transitModesbus, lrt, metro; Flutter: список TransitMode.
preferencefastest, less_walking, fewer_transfers; Flutter: RoutePreference.

3. Прочитайте ответ

Время является оценкой сервиса. Для показа линии используйте геометрию legs, включая подходы и пересадки. Полный исходный ответ во Flutter доступен в result.json.

ПолеЗначение
routes[]Варианты поездки; проверяйте наличие вариантов перед выбором.
routes[].idИдентификатор варианта.
distanceM / durationSРасстояние в метрах и длительность в секундах.
bounds[west,south,east,north] для подбора камеры; Flutter: MapBounds.
legs[]Последовательность пеших, автомобильных и транспортных участков.
legs[].geometryGeoJSON LineString. Во Flutter leg.geometry — List<Coordinate>, leg.json содержит исходный GeoJSON.
legs[].line / stopsЛиния транспорта и остановки, если доступны для участка.
walkingDistanceM / walkingDurationS / waitingSПешеходная часть и ожидание.
transfers / fareПересадки и сведения о стоимости.
warnings / timing / networkОграничения результата, тип расчёта времени и сведения о сети.

4. Покажите результат на карте

JavaScript-пример добавляет GeoJSON линий. Во Flutter передайте result.routes в UMapView.routes и выбранный id в selectedRouteId. Переключение между уже полученными вариантами не требует повторного расчёта.

import * as sdk from "@umap-llc/map-react";

// HTML: <div id="map" style="height:480px;position:relative"></div>
const map = sdk.createMap({
  container: "map",
  style: "YOUR_STYLE_URL",
  accessToken: "YOUR_ACCESS_TOKEN",
  center: [71.423, 51.128],
  zoom: 14,
});

async function setupExample(map, sdk, report = console.log) {
  const result = await map.routing.calculate({
    from: { coordinates: [71.4106836, 51.1355788], name: "Астана Опера" },
    to: { coordinates: [71.4304544, 51.1282903], name: "Байтерек" },
    mode: "car",
    alternatives: 3,
  });
  const route = result.routes[0];
  if (!route) {
    report("Маршрут не найден");
    return;
  }
  await map.addSource("route", {
    type: "geojson",
    data: {
      type: "FeatureCollection",
      features: route.legs.map((leg) => ({
        type: "Feature",
        properties: {},
        geometry: leg.geometry,
      })),
    },
  });
  map.addLayer({
    id: "route-border",
    type: "line",
    source: "route",
    "rendering-order": "overlay",
    paint: { "line-color": "#fff", "line-width": 9 },
  });
  map.addLayer({
    id: "route-path",
    type: "line",
    source: "route",
    "rendering-order": "overlay",
    paint: { "line-color": "#1769e0", "line-width": 5 },
  });
  map.fitBounds(
    [
      [route.bounds[0], route.bounds[1]],
      [route.bounds[2], route.bounds[3]],
    ],
    { padding: 65, maxZoom: 15 },
  );
  report(
    `${Math.ceil(route.durationS / 60)} мин · ${(route.distanceM / 1000).toFixed(1)} км · ${result.routes.length} варианта`,
  );
  const options = document.createElement("div");
  options.style.cssText =
    "position:absolute;top:16px;left:16px;display:flex;gap:8px;flex-wrap:wrap;max-width:calc(100% - 120px);z-index:5";
  for (const candidate of result.routes) {
    const button = document.createElement("button");
    button.type = "button";
    button.textContent = `${Math.ceil(candidate.durationS / 60)} мин`;
    button.setAttribute("aria-pressed", String(candidate.id === route.id));
    const paint = (selected) => {
      button.style.cssText = `border:1px solid #dbe3ed;border-radius:12px;padding:10px 14px;cursor:pointer;font:600 14px system-ui;background:${selected ? "#1769e0" : "white"};color:${selected ? "white" : "#253348"}`;
    };
    paint(candidate.id === route.id);
    button.onclick = () => {
      map.setData("route", {
        type: "FeatureCollection",
        features: candidate.legs.map((leg) => ({
          type: "Feature",
          properties: {},
          geometry: leg.geometry,
        })),
      });
      for (const sibling of options.querySelectorAll("button")) {
        sibling.setAttribute("aria-pressed", "false");
        sibling.style.background = "white";
        sibling.style.color = "#253348";
      }
      button.setAttribute("aria-pressed", "true");
      paint(true);
      map.fitBounds(
        [
          [candidate.bounds[0], candidate.bounds[1]],
          [candidate.bounds[2], candidate.bounds[3]],
        ],
        { padding: 65, maxZoom: 15 },
      );
      report(
        `${Math.ceil(candidate.durationS / 60)} мин · ${(candidate.distanceM / 1000).toFixed(1)} км`,
      );
    };
    options.append(button);
  }
  map.getContainer().append(options);
  return () => options.remove();
}

map.once("load", async () => {
  const cleanup = await setupExample(map, sdk);
  if (cleanup) map.once("remove", cleanup);
});

5. Обработайте ошибку и отмену

const abort = new AbortController();
try {
  const result = await routing.calculate(request, { signal: abort.signal });
  console.log(result.routes);
} catch (error) {
  if (error.name !== "AbortError") console.error(error.status, error.message);
}
// При уходе со страницы: abort.abort();
Коды ошибок →