Расчет длин линий

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

Расчёт длины линии позволяет решать широкий спектр задач:

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

В OpenLayers вычисление длины может выполняться как для уже существующих объектов, так и для геометрий, создаваемых пользователем в режиме рисования.


Геометрия LineString

Для представления линии используется класс LineString.

Пример создания линии:

import LineString from 'ol/geom/LineString.js';

const line = new LineString([
    [0, 0],
    [100, 100],
    [200, 50]
]);

Каждая вершина задаётся парой координат.

Структура объекта:

LineString
 ├─ Point 1
 ├─ Point 2
 └─ Point 3

Линия может содержать любое количество сегментов.


Метод getLength()

Базовый способ вычисления длины линии — использование метода getLength().

const length = line.getLength();

console.log(length);

Метод возвращает сумму длин всех сегментов линии.

Для координат:

const line = new LineString([
    [0, 0],
    [3, 4]
]);

console.log(line.getLength());

результат будет:

5

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

genui{“math_block_widget_always_prefetch_v2”:{“content”:“d=”}}


Расчёт длины многосегментной линии

Для ломаной линии длина определяется как сумма длин всех сегментов.

Пример:

const line = new LineString([
    [0, 0],
    [3, 4],
    [6, 8]
]);

console.log(line.getLength());

Расчёт:

Сегмент 1 = 5
Сегмент 2 = 5

Общая длина = 10

OpenLayers выполняет эти вычисления автоматически.


Единицы измерения результата

Важно понимать, что getLength() возвращает длину в единицах используемой проекции.

Например:

EPSG:3857

map.getView().getProjection().getCode();

Результат:

EPSG:3857

В большинстве случаев длина будет возвращаться в метрах.

const length = line.getLength();

console.log(length);
1250.8

означает примерно:

1250.8 метров

Географические координаты EPSG:4326

Если координаты представлены в градусах:

const line = new LineString([
    [37.61, 55.75],
    [38.00, 55.90]
]);

то результат getLength() будет выражен в градусах, а не в метрах.

console.log(line.getLength());
0.417...

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

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


Геодезический расчёт длины

Для получения реального расстояния по поверхности Земли применяется модуль ol/sphere.

Импорт:

import { getLength } from 'ol/sphere.js';

Пример:

const length = getLength(line);

console.log(length);

Полученный результат будет возвращён в метрах.


Пример расчёта между городами

Пусть имеется линия между двумя точками:

const line = new LineString([
    [37.6176, 55.7558],
    [30.3351, 59.9343]
]);

Перед вычислением необходимо указать проекцию данных:

const distance = getLength(line, {
    projection: 'EPSG:4326'
});

console.log(distance);

Результат:

около 634000 метров

или:

634 км

Вычисление выполняется по эллипсоиду Земли и значительно точнее простого расчёта в плоской системе координат.


Форматирование длины

Обычно пользователю неудобно работать с большими числами метров.

Например:

12543.458732

Гораздо удобнее выводить расстояния в километрах.

Пример:

function formatLength(length) {
    if (length > 1000) {
        return (length / 1000).toFixed(2) + ' км';
    }

    return length.toFixed(2) + ' м';
}

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

const length = getLength(line);

console.log(formatLength(length));

Результат:

12.54 км

Получение длины объекта из слоя

Часто линия хранится внутри векторного слоя.

Получение объекта:

const feature = vectorSource.getFeatures()[0];

Извлечение геометрии:

const geometry = feature.getGeometry();

Вычисление длины:

const length = geometry.getLength();

console.log(length);

Полный пример:

const feature = vectorSource.getFeatures()[0];

const line = feature.getGeometry();

const length = line.getLength();

console.log(length);

Работа с MultiLineString

Некоторые объекты состоят из нескольких линий.

Для этого используется класс MultiLineString.

import MultiLineString from 'ol/geom/MultiLineString.js';

Создание:

const geometry = new MultiLineString([
    [
        [0, 0],
        [100, 0]
    ],
    [
        [0, 0],
        [0, 50]
    ]
]);

Для расчёта общей длины:

import { getLength } from 'ol/sphere.js';

const total = getLength(geometry);

console.log(total);

OpenLayers суммирует длины всех линий.


Расчёт длины при рисовании

Очень распространённый сценарий — отображение длины линии во время её создания пользователем.

Подключение инструмента рисования:

import Draw from 'ol/interaction/Draw.js';

Создание взаимодействия:

const draw = new Draw({
    source: vectorSource,
    type: 'LineString'
});

map.addInteraction(draw);

Отслеживание изменений геометрии

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

draw.on('drawstart', function(event) {

    const feature = event.feature;

    feature.getGeometry().on('change', function(e) {

        const geometry = e.target;

        const length = getLength(geometry);

        console.log(length);
    });

});

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


Вывод длины в интерфейс

Создадим элемент страницы:

<div id="length"></div>

Обновление значения:

const output = document.getElementById('length');

feature.getGeometry().on('change', function(e) {

    const length = getLength(e.target);

    output.innerHTML = formatLength(length);

});

Теперь пользователь видит актуальную длину строящейся линии.


Измерение расстояний инструментом Measure

На практике часто создаётся отдельный инструмент измерений.

Основная последовательность:

  1. Пользователь активирует режим измерения.
  2. Начинает рисовать линию.
  3. OpenLayers вычисляет длину.
  4. Значение выводится рядом с линией.
  5. После завершения измерение сохраняется либо удаляется.

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


Использование Overlay для отображения длины

Для вывода результата непосредственно на карте применяется класс Overlay.

Создание контейнера:

<div id="tooltip"></div>

Создание оверлея:

import Overlay from 'ol/Overlay.js';

const overlay = new Overlay({
    element: document.getElementById('tooltip')
});

map.addOverlay(overlay);

Обновление позиции:

overlay.setPosition(
    geometry.getLastCoordinate()
);

Вывод значения:

tooltip.innerHTML = formatLength(length);

В результате длина отображается рядом с последней точкой линии.


Сравнение getLength() и sphere.getLength()

Метод Результат
geometry.getLength() Плоское расстояние
sphere.getLength() Геодезическое расстояние
Быстродействие Выше у getLength()
Точность на больших расстояниях Выше у sphere.getLength()
Работа с EPSG:4326 Предпочтительно использовать sphere.getLength()

Для большинства карт на основе географических координат рекомендуется применять именно функции из модуля ol/sphere.


Производительность при большом количестве объектов

Если необходимо вычислить длину тысяч линий:

const features = vectorSource.getFeatures();

следует избегать постоянного пересчёта.

Неэффективный вариант:

features.forEach(feature => {
    console.log(
        getLength(feature.getGeometry())
    );
});

при каждом обновлении карты.

Лучше вычислить значения один раз:

features.forEach(feature => {

    const length = getLength(
        feature.getGeometry()
    );

    feature.set('length', length);

});

После этого использовать сохранённое свойство:

console.log(
    feature.get('length')
);

Такой подход существенно снижает нагрузку на браузер.


Проверка типа геометрии перед вычислением

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

const geometry = feature.getGeometry();

Проверка:

if (
    geometry.getType() === 'LineString'
) {
    console.log(
        geometry.getLength()
    );
}

Для нескольких типов:

const type = geometry.getType();

if (
    type === 'LineString' ||
    type === 'MultiLineString'
) {
    const length = getLength(geometry);

    console.log(length);
}

Это предотвращает ошибки при работе с точками и полигонами.


Автоматический пересчёт после редактирования

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

feature.getGeometry().on(
    'change',
    function(event) {

        const length = getLength(
            event.target
        );

        console.log(length);

    }
);

После перемещения любой вершины значение будет пересчитано.


Практические сценарии применения

Анализ дорожной сети

const roadLength =
    getLength(roadGeometry);

Используется для подсчёта общей протяжённости дорог.

Учёт трубопроводов

const pipeLength =
    getLength(pipeGeometry);

Позволяет оценивать объёмы строительства и обслуживания.

Туристические маршруты

const routeLength =
    getLength(routeGeometry);

Применяется для отображения длины треков.

Землеустройство

const borderLength =
    getLength(borderGeometry);

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

Инструменты измерения

const measureLength =
    getLength(measureGeometry);

Основа интерактивных измерительных модулей веб-карт.


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

Использовать geometry.getLength():

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

Использовать sphere.getLength():

  • при работе с EPSG:4326;
  • для измерения больших расстояний;
  • для маршрутов между городами и странами;
  • для пользовательских инструментов измерения на веб-картах;
  • когда приоритетом является геодезическая точность.

Грамотное сочетание обоих подходов позволяет создавать быстрые и одновременно точные инструменты измерения расстояний в приложениях на базе OpenLayers.