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

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

В OpenLayers измерение расстояний обычно реализуется с помощью:

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

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


Подготовка карты

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

import Map from 'ol/Map.js';
import View from 'ol/View.js';
import TileLayer from 'ol/layer/Tile.js';
import VectorLayer from 'ol/layer/Vector.js';
import VectorSource from 'ol/source/Vector.js';
import OSM from 'ol/source/OSM.js';

const vectorSource = new VectorSource();

const vectorLayer = new VectorLayer({
    source: vectorSource
});

const map = new Map({
    target: 'map',
    layers: [
        new TileLayer({
            source: new OSM()
        }),
        vectorLayer
    ],
    view: new View({
        center: [0, 0],
        zoom: 2
    })
});

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


Добавление инструмента Draw

Взаимодействие Draw позволяет пользователю создавать новые геометрические объекты непосредственно на карте.

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

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

map.addInteraction(draw);

Параметр:

Параметр Описание
source Источник, куда сохраняются объекты
type Тип создаваемой геометрии

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


Получение длины линии

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

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

    const feature = event.feature;

    const geometry = feature.getGeometry();

    const length = geometry.getLength();

    console.log(length);

});

Метод:

geometry.getLength()

возвращает длину линии в единицах используемой проекции.


Особенности метода getLength()

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

Если используется проекция Web Mercator (EPSG:3857), результат представляет собой приблизительное значение в метрах и может содержать погрешности, особенно на больших расстояниях и в высоких широтах.

Пример:

const length = geometry.getLength();

console.log(`${length} м`);

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


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

OpenLayers содержит модуль сферических вычислений.

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

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

const length = getLength(geometry);

console.log(length);

Теперь расчёт выполняется по поверхности Земли с учётом её кривизны.

Это существенно повышает точность измерений.


Сравнение методов измерения

Геометрический расчёт

geometry.getLength();

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

  • высокая скорость;
  • зависит от проекции;
  • подходит для локальных задач.

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

getLength(geometry);

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

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

На практике большинство приложений используют именно сферический расчёт.


Форматирование результатов

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

Пример функции форматирования:

function formatLength(length) {

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

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

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

const length = getLength(geometry);

console.log(formatLength(length));

Примеры результатов:

Значение Вывод
120 120.00 м
850 850.00 м
1450 1.45 км
23500 23.50 км

Измерение в реальном времени

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

Для этого отслеживается изменение геометрии.

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

    const feature = event.feature;

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

        const geometry = evt.target;

        const length = getLength(geometry);

        console.log(formatLength(length));

    });

});

Каждый раз при добавлении новой вершины будет вычисляться новая длина.


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

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

Создание HTML-элемента:

<div id="measureTooltip"></div>

Создание Overlay:

import Overlay from 'ol/Overlay.js';

const tooltipElement =
    document.getElementById('measureTooltip');

const tooltip = new Overlay({
    element: tooltipElement,
    offset: [0, -15],
    positioning: 'bottom-center'
});

map.addOverlay(tooltip);

Обновление положения подсказки

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

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

    const geometry = evt.target;

    const coordinates =
        geometry.getLastCoordinate();

    const length = getLength(geometry);

    tooltipElement.innerHTML =
        formatLength(length);

    tooltip.setPosition(coordinates);

});

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


Стилизация подсказки

Пример CSS-оформления:

#measureTooltip {
    background: rgba(0,0,0,0.7);
    color: white;
    padding: 4px 8px;
    border-radius: 4px;
    white-space: nowrap;
    font-size: 12px;
}

Такой стиль напоминает измерительные инструменты профессиональных ГИС-систем.


Настройка внешнего вида измеряемой линии

Для повышения читаемости можно использовать собственный стиль.

import Style from 'ol/style/Style.js';
import Stroke from 'ol/style/Stroke.js';

vectorLayer.setStyle(
    new Style({
        stroke: new Stroke({
            color: '#ff0000',
            width: 3
        })
    })
);

Результат:

  • яркая линия;
  • хорошая видимость на карте;
  • удобство измерений.

Отображение вершин линии

Часто требуется показывать точки маршрута.

import CircleStyle from 'ol/style/Circle.js';
import Fill from 'ol/style/Fill.js';

const style = new Style({
    stroke: new Stroke({
        color: '#ff0000',
        width: 3
    }),
    image: new CircleStyle({
        radius: 5,
        fill: new Fill({
            color: '#ffffff'
        }),
        stroke: new Stroke({
            color: '#ff0000',
            width: 2
        })
    })
});

Вершины позволяют лучше контролировать процесс измерения.


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

Иногда требуется хранить только одно измерение.

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

draw.on('drawstart', function() {
    vectorSource.clear();
});

На карте всегда будет отображаться только последняя линия.


Удаление результатов измерения

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

vectorSource.clear();

Либо через кнопку интерфейса:

document
    .getElementById('clear')
    .addEventListener('click', () => {

        vectorSource.clear();

    });

Измерение сложных маршрутов

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

A ---- B
         \
          C
           \
            D

Общая длина определяется как сумма длин всех сегментов:

AB + BC + CD

Пользователь может строить маршруты произвольной формы, а OpenLayers автоматически вычисляет их суммарную длину.


Получение длины отдельных сегментов

Координаты линии доступны через метод:

const coordinates =
    geometry.getCoordinates();

Пример:

[
    [x1, y1],
    [x2, y2],
    [x3, y3]
]

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

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


Отображение длины каждого сегмента

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

Алгоритм:

  1. Получить массив координат.
  2. Построить сегменты.
  3. Вычислить длину каждого сегмента.
  4. Разместить подпись в середине сегмента.

Подобный подход используется в кадастровых и инженерных ГИС.


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

Полный пример расчёта после завершения рисования:

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

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

    const geometry =
        event.feature.getGeometry();

    const length =
        getLength(geometry);

    alert(
        'Расстояние: ' +
        formatLength(length)
    );

});

После завершения построения маршрута пользователь получает итоговую длину.


Типичная архитектура инструмента измерения

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

  1. Векторный источник.
  2. Векторный слой.
  3. Draw-взаимодействие.
  4. Геодезический расчёт через ol/sphere.
  5. Overlay с подсказкой.
  6. Форматирование единиц измерения.
  7. Кнопка очистки результатов.

Такая структура применяется в большинстве веб-ГИС, построенных на OpenLayers.


Практические рекомендации

Для максимальной точности

Использовать:

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

вместо:

geometry.getLength();

Для удобства пользователей

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

Для крупных проектов

Отдельно реализовывать:

  • очистку измерений;
  • отображение сегментов;
  • историю измерений;
  • экспорт результатов.

Для профессиональных ГИС

Комбинировать:

  • измерение расстояний;
  • измерение площадей;
  • привязку к объектам;
  • отображение координат вершин;
  • сохранение результатов в формате GeoJSON.

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