Cesium空间距离量测:从基础到进阶的完整指南
创作时间:
作者:
@小白创作中心
Cesium空间距离量测:从基础到进阶的完整指南
引用
CSDN
1.
https://blog.csdn.net/m0_67946993/article/details/136839992
Cesium是一个强大的开源WebGIS框架,广泛应用于三维地理信息系统开发。本文将详细介绍如何在Cesium中实现空间距离量测功能,包括非贴地形距离测量和贴地线距离测量两种方法。
非贴地形距离测量
在Cesium中进行非贴地形距离测量,首先需要获取两点的屏幕坐标,并将其转换为笛卡尔空间直角坐标。可以使用Cesium.Cartesian3.distance()
函数计算两点之间的距离。这种方法不考虑地球的曲率和地表的曲面,适用于简单的距离测量场景。
如果需要更精确的距离测量,可以使用椭球体测地线对象(EllipsoidGeodesic
)来计算两点之间的曲面距离。这种方法考虑了地球的曲率,通常用于测量地球上两个点之间的实际距离。为了进一步提高精度,还需要考虑两点之间的高度差,可以使用直角三角形求斜边的方法来计算三维空间中两点的直线距离。
下面是具体的代码实现:
/* 空间两点距离计算函数 */
const getSpaceDistance = (positions: any) => {
let lengthAll = 0;
for (let i = 0; i < positions.length - 1; i++) {
// 1.将起点与终点位置信息从笛卡尔坐标形式转换为Cartographic形式
const point1cartographic = Cesium.Cartographic.fromCartesian(positions[i]);
const point2cartographic = Cesium.Cartographic.fromCartesian(positions[i + 1]);
// 2.设置测地线起点和终点,EllipsoidGeodesic中setEndPoints常与surfaceDistance搭配使用
const geodesic = new Cesium.EllipsoidGeodesic();
geodesic.setEndPoints(point1cartographic, point2cartographic);
// 3. 得到空间中点投影到地球表面的曲面距离
let s = geodesic.surfaceDistance; // surfaceDistance返回number 单位为m,带小数
// 4. 考虑两点的高度 利用直角三角形求斜边来求实际距离
s = Math.sqrt(Math.pow(s, 2) + Math.pow(point2cartographic.height - point1cartographic.height, 2));
// 5.每段距离求和
lengthAll += s
}
return lengthAll
}
贴地线距离测量
贴地线距离测量需要考虑地形要素,使用Cesium提供的sampleTerrainMostDetailed
函数获取每个插值点在地形表面上的采样点高度。为了实现贴地效果,需要进行以下设置:
- 定义地形提供者
- 开启地形深度检测
- 设置
clampToGround
属性为true
- 设置实体的高度参考为
Cesium.HeightReference.CLAMP_TO_GROUND
下面是具体的代码实现:
const viewer = new Cesium.Viewer('cesiumContainer', {
terrainProvider: await Cesium.createWorldTerrainAsync() // createWorldTerrainAsync是个异步函数
})
viewer.scene.globe.depthTestAgainstTerrain = true; //开启地形深度检测
clampToGround: true,
heightReference: Cesium.HeightReference.CLAMP_TO_GROUND
贴地线距离测量的具体步骤如下:
- 根据两个点的坐标计算直线距离
- 使用
Cesium.Math.lerp
函数进行插值,每1米插一个点 - 使用
Cesium.sampleTerrainMostDetailed
函数获取每个插值点的地形高度 - 计算所有相邻插值点之间的距离并求和
下面是具体的代码实现:
const getTerrainDistance = (position: Cartesian3[]) => {
return new Promise((resolve)=>{
let i = position.length - 2
let startCart3 = position[i] // 起点
let endCart3 = position[i + 1] // 终点
let linearDistance = Cesium.Cartesian3.distance(startCart3, endCart3)
// 插值数量,我这里根据两点间直线距离来插,每1米插一下,插的越多越精准
let splitNum = Math.floor(linearDistance)
// 拿到这个直线间每个点的经度纬度然后转为Cartographic的数组包括起点终点
const positions = []
let startCartographic = Cesium.Cartographic.fromCartesian(startCart3)
let endCartographic = Cesium.Cartographic.fromCartesian(endCart3)
// 不附带起点终点的地形而插值
let startDegrees = [startCartographic.longitude, startCartographic.latitude] // 经度纬度弧度制
let endDegrees = [endCartographic.longitude, endCartographic.latitude] // 经度纬度弧度制
positions.push(new Cesium.Cartographic(startDegrees[0], startDegrees[1]))
for (let i = 0; i < splitNum; i++) {
// 分别在经度纬度方向插值
let x = Cesium.Math.lerp(startDegrees[0], endDegrees[0], i / splitNum)
let y = Cesium.Math.lerp(startDegrees[1], endDegrees[1], i / splitNum)
positions.push(new Cesium.Cartographic(x, y))
}
// 地形细节采样:传入 目标地形 和 制图坐标插值组(不贴附地形意思就是height = 0) 获取 贴地形的制图坐标插值组,经度纬度高,不过经纬度是弧度制 再计算距离
Cesium.sampleTerrainMostDetailed(viewer.terrainProvider, positions).then(cartographicArr => {
// console.log(cartographicArr)
addDistance(cartographicArr).then(distance=>{
// console.log(result)
resolve(distance)
})
})
})
}
/**
* @description 根据制图坐标计算距离
* @param {Array.<Cesium.Cartographic>} cartographicArr 制图坐标数组
* @returns {number} 距离值
*/
const addDistance = (cartographicArr:Cartographic[]) =>{
return new Promise((resolve)=>{
let terrainDistance = 0;
cartographicArr.map((item,index)=>{
if (index === cartographicArr.length - 1) return
let nextItem = cartographicArr[index+1]
let currentPosition = Cesium.Cartesian3.fromRadians(item.longitude, item.latitude, item.height)
let nextPosition = Cesium.Cartesian3.fromRadians(nextItem.longitude, nextItem.latitude, nextItem.height)
terrainDistance += Cesium.Cartesian3.distance(currentPosition,nextPosition)
})
// 异步完成后返回
resolve(terrainDistance)
})
}
绘制线和动态显示距离
在实现距离量测功能时,还需要处理绘制线和动态显示距离的问题。具体实现如下:
- 定义一个绘制线的点的数组
- 对于鼠标左击事件,判断数组长度,如果长度大于等于3,则调用
getTerrainDistance
函数计算距离 - 对于鼠标移动事件,动态修改数组中的最后一个元素,并实时计算和显示距离
下面是具体的代码实现:
if (polylinePoints.length >=3){
getTerrainDistance(polylinePoints).then((result:any) => {
console.log(result)
})
const getTerrainDistance = (position: Cartesian3[]) => {
return new Promise((resolve)=>{
let i = position.length - 3
let startCart3 = position[i] // 起点
let endCart3 = position[i + 1] // 终点
})
}
// 鼠标移动
handler.setInputAction((movement: any) => {
const worldPoint = viewer.scene.pickPosition(movement.endPosition);
if (entity) {
polylinePoints.pop()
polylinePoints.push(worldPoint)
// 动态计算距离
getTerrainDistance(polylinePoints).then((result:any) => {
// console.log(result)
distance = result
viewer.entities.remove(labelEntity); // 标签动态变化
labelEntity = drawLabel(polylinePoints[polylinePoints.length - 1], result.toFixed(2) + "米");
})
}
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE)
异步问题处理
在实现过程中,需要注意处理异步问题。距离的插值计算和最后插值点距离求和都是异步函数,需要使用Promise和then语法来处理。如果嵌套多个异步函数,逻辑处理方式相同。
结果展示
下面是两种距离测量方法的结果展示:
- 非动态变化的距离标签
- 可以随鼠标移动动态变化的距离标签
热门推荐
深度探索,邵阳——湖南的一颗璀璨明珠,旅游景点大全及旅行指南
困境与突围:网络货运行业税务合规之发展现状
AI算力股“疯涨”简直跟不上节奏!最牛暴涨超8倍,光模块龙头业绩炸裂
公司名称变更后土地证是否有效?详解变更流程与所需材料
桂附地黄丸服用不能超过多久
平遥长山药:被誉为“中国小人参”的地理标志农产品
什么是尿结石?医生提醒:出现5个症状,及时就医
红军标语下的紫云锅厂换新颜
儿童患流感为何恢复有快有慢?专家:吃得科学很重要
12道美味凉拌菜制作方法,让你轻松掌握夏日餐桌必备佳肴
电鳗:水中的“高压线”
隆科多:雍正皇帝的舅舅,为何被囚禁至死?
李云雷:告别乡土文学传统之后,如何为当代中国都市的诗意赋形?丨新批评
藿香正气水、蛋黄派、腐乳、榴莲、醉虾……到底谁会致酒驾?
阿莫西林过敏应急处理指南:如何应对过敏反应
阿莫西林过敏应急处理指南:如何应对过敏反应
全合成机油多久换一次?这一篇全部给你说清楚!
小心!骨质疏松悄悄盯上你,这些习惯让骨骼健康“逆袭”
如何保持软件和硬件的兼容性
付幸洲:北纬30°的“世界名湖” 是大自然留给武汉的“活文化”
网络买虚拟物品被骗怎么办
独龙玉是什么玉?产地,价格,寓意,保养,鉴别,禁忌
Excel中提取省市区信息的四种实用方法
邵阳周边值得一游的山脉景点
隆科多:雍正皇帝的舅舅,为何被囚禁至死?
IC卡水表使用指南:如何查看每月用水吨数?
头部按摩养生法:5个步骤轻松缓解疲劳
安宫牛黄丸和牛黄清心丸区别有哪些 牛黄清心丸和安宫牛黄丸能一起服用吗
肺气肿吃什么好?这三类食物要多吃
如何正确使用本田econ模式?