Cesium空间距离量测

Cesium空间距离量测

最新在系统学习Cesium,之前学习了其绘制功能,那么在绘制的基础上如果把量测功能添加上是不是更完美?于是在学习他人代码的基础上,自己用Cesium + Vue3 实现一个距离量测功能。(看网上的这部分有些零散,就把自己md学习笔记汇总然后导进来。)

  1. 非贴地形距离测量

首先先获取两点的屏幕坐标,将其转为笛卡尔空间直角坐标。

  • 可以使用 Cesium.Cartesian3.distance() 计算出两点之间的距离。一条线上的所有点,每2个点依次计算距离,汇总之后得到总长度。(不考虑地球的曲率,也不考虑地表的曲面)

  • 还有一种方法是已知两个点的经纬度信息,创建了一个椭球体测地线对象(EllipsoidGeodesic),然后设置起始点和终点,利用其surfaceDistance属性获取这两个点之间的曲面距离。这个距离是沿着地球表面的最短路径距离,考虑了地球的曲率,通常用于测量地球上两个点之间的实际距离。其次,如果要更准确的计算,需要考虑这两个点的高度差,就需要用直角三角形求斜边方法来得到三维空间中两点的直线距离。

    计算的代码如下

  /* 空间两点距离计算函数 */
  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
  }

代码结果如图:
在这里插入图片描述

  1. 贴地线以及考虑地形要素后的贴地距离。

这部分算法主要参考了这位博主大大的贴地形距离算法

Cesium提供了一个叫 sampleTerrainMostDetailed 获取每个插值点在地形表面上的采样点的height

请添加图片描述

  • 由上,需要定义地形;开启地形深度检测,从而正确绘制;开启clampToGround属性绘制线,使其贴地;绘制点设置heightReference属性,使实体位置始终贴地。同时 使用 viewer.scene.pickPosition 取代 viewer.camera.pickEllipsoid 因此在地形上移动时可以得到准确的坐标。
    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.Cartesian3.distance()计算出两点间直线距离

    • 根据两点间直线距离来插,每1米插一下,插的越多越精准。对于插值,使用cesium 提供的·

      Cesium.Math.lerp(start, end, t) ,在这里不附带起点终点各自的height,并且分别对每个点的经度以及纬度插值,最后生成需要的数据类型

  • 根据接口异步返回的数据,就是每一个插值点所对应的经纬度高Cartographic对象组成的数组,调用距离计算函数,依次相加得到起点和终点对应的贴地距离。
    在这里插入图片描述
    两个函数的代码如下:

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)
    })
  }

结果如图:第一个是距离标签非动态变化。第二个则是可以随鼠标移动动态变化。

⚠️1. 异步问题:

在这里,距离的插值计算,以及最后插值点距离求和,都是一个异步函数。

使用语法:

  const getTerrainDistance = () => {
    return new Promise((resolve)=>{
     ...
      resolve(result) // 使用 resolve 返回计算结果
    })
// 如何拿到这个结果呢?
    getTerrainDistance().then((result)=>{})

而且上面的代码里面还是一个嵌套的异步,逻辑相同。

  1. 绘制线的问题

定义一个绘制线的点的数组。

逻辑:

对于鼠标左击,判断如果长度是0,就加入点击的点,在外部需要再次把这点加进去,那么第一次点击,这个数组的长度就是2。并且两个值相等。如果在此时就调用计算距离,用 length - 2 得到的就是0,所以需要在下一次鼠标左击时,此时数组的长度变成3,鼠标移动只会动态修改第三个值,那么我们用 length - 3 从而拿到第一次和第二次点击的坐标可以计算距离,后面的以此类推。

代码如下:这样实现的也是点击后计算距离

 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] // 终点
        })

鼠标移动,动态的修改数组中的最后一个元素,根据鼠标移动变化而变化,所以如果在这里计算距离,可以实现动态距离的显示,但要注意,点击后移动数组的长度就是2,我们只需要 将 length - 2 就可以实现

代码如下:

// 鼠标移动
  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)
  • 18
    点赞
  • 37
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值