Расчет расстояний между точками

Расчёт расстояний между точками в Google Maps JavaScript API опирается на несколько уровней абстракции: геодезические вычисления, сферическая геометрия, сервисы маршрутизации и вспомогательные библиотеки. В зависимости от задачи используется либо математическое расстояние по поверхности Земли, либо дорожная дистанция, учитывающая реальную дорожную сеть.

Земля в большинстве расчётов в картографических API рассматривается как сфера или эллипсоид. В Google Maps JavaScript API чаще применяется сферическая модель с радиусом Земли:

R ≈ 6 371 000 м

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

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

Классическая формула расчёта расстояния между двумя точками:

d = 2R ()

где:

  • φ — широта
  • λ — долгота
  • Δφ = φ₂ − φ₁
  • Δλ = λ₂ − λ₁
  • R — радиус Земли

Эта формула устойчива к численным ошибкам при малых расстояниях и широко применяется в геоинформационных системах.

Реализация без API

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

function haversineDistance(lat1, lng1, lat2, lng2) {
  const R = 6371000;

  const dLat = toRad(lat2 - lat1);
  const dLng = toRad(lng2 - lng1);

  const a =
    Math.sin(dLat / 2) * Math.sin(dLat / 2) +
    Math.cos(toRad(lat1)) * Math.cos(toRad(lat2)) *
    Math.sin(dLng / 2) * Math.sin(dLng / 2);

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

  return R * c;
}

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

Геометрическая библиотека Google Maps

В Google Maps JavaScript API встроен модуль geometry, предоставляющий оптимизированные функции для работы с расстояниями и геометрией.

Подключение:

<script src="https://maps.googleapis.com/maps/api/js?key=API_KEY&libraries=geometry"></script>

computeDistanceBetween

Основная функция:

const pointA = new google.maps.LatLng(55.751244, 37.618423);
const pointB = new google.maps.LatLng(59.934280, 30.335099);

const distance = google.maps.geometry.spherical.computeDistanceBetween(
  pointA,
  pointB
);

console.log(distance);

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

  • возвращает расстояние в метрах
  • использует сферическую модель Земли
  • оптимизирована внутри API
  • точнее ручной реализации за счёт встроенных поправок

computeLength для линий

Для ломаных линий используется расчёт длины полилинии:

const path = [
  new google.maps.LatLng(55.751244, 37.618423),
  new google.maps.LatLng(56.326797, 44.006516),
  new google.maps.LatLng(59.934280, 30.335099)
];

const length = google.maps.geometry.spherical.computeLength(path);

Метод суммирует расстояния между последовательными точками.

Расстояние по маршруту (дорожная сеть)

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

DirectionsService

const directionsService = new google.maps.DirectionsService();

directionsService.route(
  {
    origin: { lat: 55.751244, lng: 37.618423 },
    destination: { lat: 59.934280, lng: 30.335099 },
    travelMode: google.maps.TravelMode.DRIVING
  },
  (result, status) => {
    if (status === "OK") {
      console.log(result.routes[0].legs[0].distance.value);
    }
  }
);

Структура результата

  • routes[]

    • legs[]

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

Особенности расчёта маршрута

  • учитывает дорожную сеть

  • может изменяться во времени (пробки, перекрытия)

  • зависит от travelMode:

    • DRIVING
    • WALKING
    • BICYCLING
    • TRANSIT

Сравнение геодезического и маршрутного расстояния

Геодезическое расстояние:

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

Маршрутное расстояние:

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

Расстояния для множества точек

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

Матрица расстояний вручную

function buildDistanceMatrix(points) {
  const matrix = [];

  for (let i = 0; i < points.length; i++) {
    matrix[i] = [];
    for (let j = 0; j < points.length; j++) {
      matrix[i][j] = google.maps.geometry.spherical.computeDistanceBetween(
        points[i],
        points[j]
      );
    }
  }

  return matrix;
}

Сложность O(n²), что важно учитывать при больших наборах данных.

Использование Distance Matrix Service

Встроенный сервис:

const service = new google.maps.DistanceMatrixService();

service.getDistanceMatrix(
  {
    origins: [
      { lat: 55.751244, lng: 37.618423 }
    ],
    destinations: [
      { lat: 59.934280, lng: 30.335099 }
    ],
    travelMode: google.maps.TravelMode.DRIVING
  },
  (response, status) => {
    console.log(response.rows[0].elements[0].distance.value);
  }
);

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

  • оптимизация запросов
  • поддержка маршрутов
  • кэширование на стороне сервера Google

Учёт кривизны Земли и проекции

При визуализации расстояний на карте возникает дополнительная проблема искажений проекций. В Google Maps JavaScript API используется проекция Mercator.

Искажения:

  • увеличиваются к полюсам
  • не влияют на расчёт LatLng, но влияют на пиксельное отображение

Поэтому:

  • расстояния всегда считаются в географических координатах
  • экранные координаты используются только для UI

Работа с полилиниями и сложными маршрутами

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

const polyline = new google.maps.Polyline({
  path: [
    { lat: 55.75, lng: 37.61 },
    { lat: 56.32, lng: 44.00 },
    { lat: 59.93, lng: 30.33 }
  ]
});

const distance = google.maps.geometry.spherical.computeLength(polyline.getPath());

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

  • измерять длину маршрутов пользователя
  • анализировать треки GPS
  • вычислять отклонения маршрута

Оптимизация вычислений расстояний

При интенсивной работе с координатами применяются стратегии оптимизации:

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

Пример bounding box фильтра:

function inBounds(point, bounds) {
  return (
    point.lat >= bounds.minLat &&
    point.lat <= bounds.maxLat &&
    point.lng >= bounds.minLng &&
    point.lng <= bounds.maxLng
  );
}

Ошибки и особенности вычислений

Типичные источники погрешностей:

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

Поведение API:

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

Комбинированные сценарии

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

  • геодезическое расстояние для предварительного отбора
  • Distance Matrix для массовых расчётов
  • DirectionsService для финального маршрута
  • geometry library для локальных вычислений

Такая многоуровневая архитектура снижает нагрузку и повышает точность итоговых данных