Длина polyline

В Leaflet полилиния (L.Polyline) представляет собой упорядоченный набор координатных точек LatLng, соединённых прямыми отрезками на карте. Каждая точка хранится в географических координатах:

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

Структура данных полилинии может быть как простой:

L.polyline([
  [51.505, -0.09],
  [51.51, -0.1],
  [51.52, -0.12]
]);

так и вложенной (multi-polyline):

L.polyline([
  [
    [51.505, -0.09],
    [51.51, -0.1]
  ],
  [
    [51.52, -0.12],
    [51.53, -0.13]
  ]
]);

Геометрический смысл длины полилинии

Длина полилинии — сумма длин всех сегментов между последовательными точками:

[ L = {i=1}^{n-1} d(P_i, P{i+1})]

где (d(P_i, P_{i+1})) — расстояние между двумя географическими точками.

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

Плоское (евклидово) расстояние

  • подходит только для локальных карт
  • искажает результат на больших расстояниях

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

  • учитывает кривизну Земли
  • основано на формуле гаверсинусов или сферической модели

Извлечение координат из Polyline

Leaflet предоставляет метод получения всех точек:

const latlngs = polyline.getLatLngs();

Результат может быть:

  • массив LatLng
  • массив массивов LatLng (для мультилиний)

Поэтому перед расчётом длины требуется нормализация структуры.


Рекурсивная обработка вложенных линий

function flattenLatLngs(latlngs) {
  const result = [];

  latlngs.forEach(item => {
    if (Array.isArray(item)) {
      result.push(...flattenLatLngs(item));
    } else {
      result.push(item);
    }
  });

  return result;
}

Расчёт расстояния по формуле гаверсинусов

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

d = 2R ()

где:

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

Реализация вычисления расстояния между точками

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

function distance(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;
}

Вычисление длины полилинии вручную

function polylineLength(polyline) {
  const latlngs = flattenLatLngs(polyline.getLatLngs());

  let total = 0;

  for (let i = 1; i < latlngs.length; i++) {
    total += distance(
      latlngs[i - 1].lat,
      latlngs[i - 1].lng,
      latlngs[i].lat,
      latlngs[i].lng
    );
  }

  return total;
}

Использование встроенных возможностей Leaflet-плагинов

Leaflet core не содержит встроенного метода длины полилинии, однако часто применяется расширение leaflet-geometryutil.

После подключения плагина появляется возможность вычисления длины:

const length = L.GeometryUtil.length(polyline);

или:

const latlngs = polyline.getLatLngs();
const length = L.GeometryUtil.length(latlngs);

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

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

Альтернативный подход через Turf.js

Библиотека Turf.js предоставляет геоаналитические функции, включая измерение длины линии.

import length from '@turf/length';

const geojson = polyline.toGeoJSON();

const result = length(geojson, { units: 'meters' });

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

  • строгая работа с GeoJSON
  • поддержка единиц измерения (км, м, мили)
  • высокая точность для GIS-задач

Обновление длины при изменении полилинии

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

Для Leaflet.Draw:

map.on('draw:edited', function (e) {
  e.layers.eachLayer(function (layer) {
    const len = polylineLength(layer);
    console.log(len);
  });
});

Для событий изменения точек:

polyline.on('edit', function () {
  const len = polylineLength(polyline);
});

Особенности производительности

При большом количестве точек:

  • сложность алгоритма линейная O(n)
  • основная нагрузка — тригонометрические функции
  • оптимизация возможна через кэширование сегментов

Подходы оптимизации:

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

Точность вычислений и погрешности

На практике точность зависит от:

  • модели Земли (сфера vs эллипсоид)
  • плотности точек
  • используемой библиотеки

Для инженерных задач допустимо отклонение в пределах нескольких метров при использовании сферической модели, тогда как геодезические системы могут использовать эллипсоид WGS84 для повышения точности.


Влияние проекции карты

Leaflet работает в Web Mercator (EPSG:3857), где визуальные расстояния и географические расстояния различаются.

Поэтому:

  • визуальная длина на экране ≠ реальная длина
  • вычисления всегда должны выполняться в географических координатах (LatLng), а не пикселях карты
map.project(latlng) // только для экранных координат

Итоговые подходы вычисления длины

  • прямой расчёт через формулу гаверсинусов
  • использование L.GeometryUtil
  • применение Turf.js для GIS-уровня
  • ручная агрегация сегментов из getLatLngs()

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