Измерение расстояний является одной из наиболее востребованных функций в геоинформационных системах. В трехмерной среде CesiumJS задача измерения значительно сложнее по сравнению с двумерными картами, поскольку необходимо учитывать форму Земли, рельеф местности, высоты объектов и особенности пространственных координат.
В зависимости от поставленной задачи могут использоваться различные методы измерений:
Cesium предоставляет все необходимые инструменты для реализации подобных вычислений.
Перед выполнением измерений важно понимать, что Cesium работает сразу с несколькими системами координат.
Наиболее часто используются:
Получение координат точки на карте обычно выглядит следующим образом:
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.