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语法来处理。如果嵌套多个异步函数,逻辑处理方式相同。
结果展示
下面是两种距离测量方法的结果展示:
- 非动态变化的距离标签
- 可以随鼠标移动动态变化的距离标签
热门推荐
蒸鱼多长时间最佳?15分钟是普通鱼的黄金时间
资源管理的创新策略:助力可持续发展为目标
幼儿园海姆立克法、防溺水等急救常识集锦,关键时刻可以救命!(家长老师必看)
如何应对团队缺人
司乘险:人身保险的贴心保障
传统杀软与云查杀大比拼
黄花菜怎么做好吃
新鲜黄花菜怎么处理
蓝牙耳机连接手机常见问题及解决方案
探讨浪费信托:如何有效地管理资源并减少浪费
瓷碗可以微波炉 选购与使用的安全指南
律师事务所的用法:了解其目的和功能的必要指南
何以中国·弦歌不辍丨当一天孔子的学生,能学到什么?
4岁的孩子喜欢啃指甲,这正常吗?
未来材料领航者:全氟醚FFKM橡胶的科技革命之路
食用油怎么选?菜籽油选购攻略
经济责任绩效考核:激励企业提高经济效益的关键手段
MATLAB矩阵转置指南:从基础到应用,全面解析矩阵转置的奥秘
三甲医院如何选择?
崩坏:星穹铁道2.4版本卡池深度解析:新角色与复刻角色全攻略
非典疫情什么时候开始的
冬季必备的八道滋补炖汤,暖身又营养
支气管炎如何护理
如何让孩子一键切换“开学模式”?专家支招
在中超称王称霸!却在亚洲赛场无所作为!武磊反差太大什么原因?
最新SA8000认证指南:如何适应全球行业合规要求
对芥子纳须弥的理解
品读八字箴言:身如芥子,心藏须弥
ETO交易平台:智能合约如何影响传统金融交易模式
难启动,行驶无力,油耗高,都是碳罐惹的祸?搞清原理少花钱!