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语法来处理。如果嵌套多个异步函数,逻辑处理方式相同。
结果展示
下面是两种距离测量方法的结果展示:
- 非动态变化的距离标签
- 可以随鼠标移动动态变化的距离标签
热门推荐
双椒鱿鱼卷:科学烹饪的健康美味
爆汁炒鱿鱼和孜然鱿鱼,哪款是你的心头好?
鱿鱼料理大赛:谁是鱿鱼王?
情绪波动引发右胸不适,小心心脏问题
饮食调理,告别右胸不适
肺炎来袭,小心右胸疼痛预警
血脂到底怎么降?日常注意这8点!守护心血管健康
《蛋仔派对》客服热线大揭秘!
法甜技巧:关于巧克力调温你想知道的都在这里
牙疼怎么止疼最快?牙疼药最好使的三种药(特效药)
牙疼不用忍!银翘散、黄连上清丸等10种中成药可有效缓解牙痛!
周红波调研亚龙湾度假区:高标准提升改造,打造世界级旅游目的地
虎奶菌抗癌效果如何?科学解读来了
香港理工发现虎奶菇提取物可制成抗癌纳米硒粒子
虎奶菇:一种兼具营养与药用价值的珍贵真菌
黄家兴博士揭秘虎奶菌抗癌奥秘:从传统应用到现代研究
金融科技:高质量发展的新引擎
IMF点赞,中国经济成全球增长引擎
小米食谱,面神经炎患者的福音
冬季面神经炎高发,这些治疗方法你知道吗?
解放军陆军第74集团军医院推荐:面神经炎患者的心理调适方法
面神经炎护理全攻略:从生活细节做起
一些国家规范管理农村自建房的经验做法
智能化个性化家居产品成消费新趋势
安宫牛黄丸安全性揭秘:汞含量大起底
同仁堂 vs 广誉远:谁家的安宫牛黄丸更胜一筹?
抗战大汉奸汪精卫罕见留影,风度翩翩容貌过人,长相酷似张国荣
如何优化房屋的内部结构布局?这种布局怎样符合居住习惯?
中卫沙漠旅游何以热辣滚烫
守护“绵阳蓝” 绵阳市生态环境质量持续改善