Расчет расстояния и времени

Работа с картами в MapLibre GL JS MapLibre GL JS почти всегда выходит за рамки простого отображения тайлов и слоёв. Практические задачи требуют вычисления расстояний между точками, оценки времени в пути, построения маршрутов и анализа геометрии объектов прямо в браузере или в связке с серверными API.

Географические координаты и их особенности

Координаты в веб-картах задаются в системе WGS84:

  • долгота (longitude)
  • широта (latitude)

Важно учитывать, что это сферическая модель Земли, поэтому прямое применение евклидовой геометрии приводит к ошибкам.

Типичная ошибка — вычисление расстояния как:

const dx = lon2 - lon1;
const dy = lat2 - lat1;
const distance = Math.sqrt(dx * dx + dy * dy);

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

Формула гаверсинусов

Для корректного расчёта расстояния по поверхности Земли используется формула гаверсинусов:

d = 2R ()

Где:

  • ( R ) — радиус Земли (≈ 6371 км)
  • ( ) — широта
  • ( ) — долгота

Реализация на Jav * aScript:

function toRad(value) {
  return (value * Math.PI) / 180;
}

function getDistance(lat1, lon1, lat2, lon2) {
  const R = 6371e3; // метры
  const φ1 = toRad(lat1);
  const φ2 = toRad(lat2);
  const Δφ = toRad(lat2 - lat1);
  const Δλ = toRad(lon2 - lon1);

  const a =
    Math.sin(Δφ / 2) * Math.sin(Δφ / 2) +
    Math.cos(φ1) * Math.cos(φ2) *
    Math.sin(Δλ / 2) * Math.sin(Δλ / 2);

  const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));

  return R * c;
}

Этот метод обеспечивает достаточную точность для большинства задач интерфейсов карт.

Использование Turf.js для геометрических вычислений

В экосистеме веб-карт часто применяется Turf.js, предоставляющий готовые функции для геоаналитики.

Пример расчёта расстояния:

import { distance } from "@turf/turf";

const from = [37.6173, 55.7558]; // Москва
const to = [30.3141, 59.9386];   // Санкт-Петербург

const result = distance(from, to, { units: "kilometers" });

Turf.js автоматически учитывает кривизну Земли и поддерживает разные единицы измерения.

Привязка расчётов к MapLibre GL JS

В MapLibre GL JS вычисления расстояний часто интегрируются с событиями карты.

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

let points = [];

map.on("click", (e) => {
  const { lng, lat } = e.lngLat;

  points.push([lng, lat]);

  if (points.length === 2) {
    const from = points[0];
    const to = points[1];

    const dist = turf.distance(from, to, { units: "meters" });

    console.log(`Расстояние: ${dist.toFixed(2)} м`);

    points = [];
  }
});

Типичный сценарий — интерактивный инструмент измерения длины маршрута.

Расчёт длины маршрута

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

const route = [
  [30.31, 59.93],
  [30.32, 59.94],
  [30.34, 59.95]
];

Длина вычисляется суммированием сегментов:

import { lineString, length } from "@turf/turf";

const line = lineString(route);

const totalLength = length(line, { units: "kilometers" });

Этот подход используется для:

  • анализа треков GPS
  • построения маршрутов
  • оценки логистики

Оценка времени в пути

Время движения напрямую зависит от:

  • расстояния
  • средней скорости
  • типа транспорта
  • дорожных условий

Базовая модель:

t =

Где:

  • ( t ) — время
  • ( d ) — расстояние
  • ( v ) — средняя скорость

Пример расчёта:

function estimateTime(distanceMeters, speedKmh) {
  const speedMs = speedKmh * (1000 / 3600);
  return distanceMeters / speedMs; // секунды
}

Более реалистичный вариант учитывает сегментацию маршрута:

function estimateRouteTime(route, speedKmh) {
  let total = 0;

  for (let i = 0; i < route.length - 1; i++) {
    const segment = turf.distance(route[i], route[i + 1], {
      units: "kilometers"
    });

    total += segment;
  }

  return (total / speedKmh) * 3600;
}

Интеграция с маршрутными API

MapLibre GL JS не предоставляет встроенного маршрутизатора. Поэтому расчёт времени часто делегируется внешним сервисам:

  • OSRM (Open Source Routing Machine)
  • GraphHopper
  • Valhalla

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

fetch(
  "https://router.project-osrm.org/route/v1/driving/30.31,59.93;30.32,59.94?overview=false"
)
  .then(res => res.json())
  .then(data => {
    const route = data.routes[0];

    console.log("Distance:", route.distance);
    console.log("Duration:", route.duration);
  });

Результат включает:

  • distance (в метрах)
  • duration (в секундах)

Эти данные затем визуализируются в MapLibre:

map.addSource("route", {
  type: "geojson",
  data: geojsonRoute
});

map.addLayer({
  id: "route-line",
  type: "line",
  source: "route",
  paint: {
    "line-width": 4
  }
});

Интерполяция и плотность точек маршрута

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

Пример с Turf.js:

import { lineString, along, length } from "@turf/turf";

const line = lineString(route);

const lineLength = length(line);

const points = [];

for (let i = 0; i < lineLength; i += 1) {
  const point = along(line, i);
  points.push(point.geometry.coordinates);
}

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

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

Анимация движения по маршруту

Расчёт времени используется для синхронизации анимации:

let progress = 0;

function animate() {
  const point = turf.along(line, progress, { units: "kilometers" });

  map.getSource("moving-point").setData(point);

  progress += 0.01;

  requestAnimationFrame(animate);
}

Чем выше точность расчёта расстояния, тем стабильнее анимация.

Ошибки и ограничения вычислений

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

  • игнорирование кривизны Земли
  • смешение градусов и радиан
  • неверные единицы измерения
  • отсутствие нормализации координат

Особенно критично:

  • использование меток MapLibre в пикселях вместо геокоординат
  • попытки линейной интерполяции в экранных координатах

Корректный подход всегда опирается на географические координаты, а не на пиксели карты.

Связь времени и динамических факторов

В реальных системах время движения корректируется:

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

Модель усложняется до:

t =

где каждый сегмент маршрута имеет собственную скорость.

Такая модель используется в навигационных системах и логистике, интегрируемых с MapLibre через внешние API и собственные вычислительные модули.