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

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

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

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

Cesium предоставляет все необходимые инструменты для реализации подобных вычислений.


Координатные системы и их влияние на измерения

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

Наиболее часто используются:

  • Cartographic — географические координаты (широта, долгота, высота);
  • Cartesian3 — декартовы координаты в системе Earth-Centered Earth-Fixed (ECEF).

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

const cartesian = viewer.scene.pickPosition(click.position);

const cartographic = Cesium.Cartographic.fromCartesian(cartesian);

const longitude = Cesium.Math.toDegrees(cartographic.longitude);
const latitude = Cesium.Math.toDegrees(cartographic.latitude);
const height = cartographic.height;

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


Измерение прямого расстояния между двумя точками

Самый простой вариант — вычисление расстояния между двумя точками в трехмерном пространстве.

Для этого используется метод:

Cesium.Cartesian3.distance(pointA, pointB);

Пример:

const distance = Cesium.Cartesian3.distance(
    point1,
    point2
);

console.log(distance);

Результат возвращается в метрах.

Такой способ вычисляет длину пространственного вектора между точками и учитывает высоту объектов.

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


Пример интерактивного измерения

Создание инструмента измерения начинается с выбора двух точек мышью.

const positions = [];

const handler = new Cesium.ScreenSpaceEventHandler(
    viewer.canvas
);

handler.setInputAction((event) => {

    const position = viewer.scene.pickPosition(
        event.position
    );

    if (position) {
        positions.push(position);
    }

    if (positions.length === 2) {

        const distance =
            Cesium.Cartesian3.distance(
                positions[0],
                positions[1]
            );

        console.log(
            "Расстояние:",
            distance.toFixed(2),
            "м"
        );

        positions.length = 0;
    }

}, Cesium.ScreenSpaceEventType.LEFT_CLICK);

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


Визуализация измеряемого сегмента

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

viewer.entities.add({
    polyline: {
        positions: [
            point1,
            point2
        ],
        width: 3,
        material: Cesium.Color.RED
    }
});

На сцене появится отрезок между двумя точками измерения.


Отображение длины линии

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

Вычисление средней точки:

const midpoint = Cesium.Cartesian3.midpoint(
    point1,
    point2,
    new Cesium.Cartesian3()
);

Создание метки:

viewer.entities.add({
    position: midpoint,
    label: {
        text: `${distance.toFixed(2)} м`,
        font: "16px sans-serif",
        showBackground: true
    }
});

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


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

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

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

Cesium.EllipsoidGeodesic

Создание геодезической линии:

const geodesic =
    new Cesium.EllipsoidGeodesic(
        startCartographic,
        endCartographic
    );

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

const distance =
    geodesic.surfaceDistance;

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

const geodesic =
    new Cesium.EllipsoidGeodesic(
        start,
        end
    );

console.log(
    geodesic.surfaceDistance
);

Результат также возвращается в метрах.


Отличие прямого и геодезического расстояния

Рассмотрим две удаленные точки.

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

Cartesian3.distance(...)

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

EllipsoidGeodesic.surfaceDistance

Для больших дистанций результаты могут значительно различаться.

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

  • прямое расстояние проходит через пространство;
  • геодезическое следует кривизне поверхности эллипсоида.

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


Измерение длины маршрута

Маршрут может состоять из большого количества точек.

Например:

const positions = [
    p1,
    p2,
    p3,
    p4
];

Общая длина вычисляется суммированием сегментов.

let totalDistance = 0;

for (let i = 0; i < positions.length - 1; i++) {

    totalDistance +=
        Cesium.Cartesian3.distance(
            positions[i],
            positions[i + 1]
        );
}

Результат:

console.log(totalDistance);

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

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

let totalDistance = 0;

for (let i = 0; i < positions.length - 1; i++) {

    const start =
        Cesium.Cartographic.fromCartesian(
            positions[i]
        );

    const end =
        Cesium.Cartographic.fromCartesian(
            positions[i + 1]
        );

    const geodesic =
        new Cesium.EllipsoidGeodesic(
            start,
            end
        );

    totalDistance +=
        geodesic.surfaceDistance;
}

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


Измерение расстояния по рельефу

Стандартное геодезическое измерение учитывает форму эллипсоида, но не учитывает горы, овраги и другие особенности поверхности.

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

Подключение рельефа:

viewer.terrainProvider =
    await Cesium.createWorldTerrainAsync();

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


Получение высот точек

Cesium предоставляет функцию:

Cesium.sampleTerrainMostDetailed()

Пример:

const positions = [
    cartographic1,
    cartographic2,
    cartographic3
];

await Cesium.sampleTerrainMostDetailed(
    viewer.terrainProvider,
    positions
);

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


Построение профиля рельефа

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

const geodesic =
    new Cesium.EllipsoidGeodesic(
        start,
        end
    );

Создание выборки:

const samples = [];

for (let i = 0; i <= 100; i++) {

    samples.push(
        geodesic.interpolateUsingFraction(
            i / 100
        )
    );
}

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


Расчет расстояния по рельефу

После выборки высот:

let terrainDistance = 0;

for (let i = 0; i < samples.length - 1; i++) {

    const p1 =
        Cesium.Cartesian3.fromRadians(
            samples[i].longitude,
            samples[i].latitude,
            samples[i].height
        );

    const p2 =
        Cesium.Cartesian3.fromRadians(
            samples[i + 1].longitude,
            samples[i + 1].latitude,
            samples[i + 1].height
        );

    terrainDistance +=
        Cesium.Cartesian3.distance(
            p1,
            p2
        );
}

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


Динамическое измерение во время перемещения курсора

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

Для этого используется событие:

Cesium.ScreenSpaceEventType.MOUSE_MOVE

Пример:

handler.setInputAction((movement) => {

    const current =
        viewer.scene.pickPosition(
            movement.endPosition
        );

    if (!current) {
        return;
    }

    const distance =
        Cesium.Cartesian3.distance(
            startPoint,
            current
        );

    label.text =
        distance.toFixed(2) + " м";

}, Cesium.ScreenSpaceEventType.MOUSE_MOVE);

Значение обновляется при каждом перемещении мыши.


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

Для удобства отображения обычно используются разные единицы.

Пример:

function formatDistance(distance) {

    if (distance < 1000) {
        return distance.toFixed(2) + " м";
    }

    return (
        (distance / 1000).toFixed(2)
        + " км"
    );
}

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

label.text =
    formatDistance(distance);

Измерение расстояния между объектами

Иногда требуется определить расстояние между сущностями сцены.

Например:

const positionA =
    entityA.position.getValue(
        viewer.clock.currentTime
    );

const positionB =
    entityB.position.getValue(
        viewer.clock.currentTime
    );

Расчет:

const distance =
    Cesium.Cartesian3.distance(
        positionA,
        positionB
    );

Подобный подход часто применяется при отслеживании движущихся объектов.


Измерение расстояния до камеры

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

viewer.camera.position

Пример вычисления расстояния:

const distance =
    Cesium.Cartesian3.distance(
        viewer.camera.position,
        objectPosition
    );

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

  • управлении уровнем детализации;
  • активации эффектов видимости;
  • отображении дополнительной информации при приближении.

Оптимизация большого количества измерений

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

Неэффективно:

Cartesian3
→ Cartographic
→ Cartesian3

Эффективнее выполнять вычисления в одной системе координат.

Также рекомендуется:

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

Архитектура полноценного инструмента измерений

Типичный модуль измерений состоит из нескольких компонентов:

Менеджер взаимодействия

  • обработка кликов;
  • обработка движения мыши;
  • завершение измерения.

Модуль вычислений

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

Модуль визуализации

  • точки маршрута;
  • линии;
  • подписи;
  • подсветка сегментов.

Модуль управления данными

  • хранение маршрутов;
  • экспорт результатов;
  • очистка измерений.

Подобная архитектура позволяет создавать масштабируемые профессиональные инструменты измерений для геопорталов, цифровых двойников территорий, навигационных систем и полноценных ГИС-приложений на базе CesiumJS.