Cesium 测距、测面功能实现

时间:2025-03-05 06:56:14

官方示例:

  • Callback Property - 计算线段长度 文字居中
  • Billboards + Points - 往地图上加点
  • Labels + Map Pins + Picking - 往地球上加标签
  • Polyline - 连线
  • Polyline Dash + Polygon - 画面

功能描述:

  • 选择测距,点击地图,出现点并显示标号,两点间以线连接,线端中间显示距离,点击右键结束测量
  • 选择测量面积,点击地图,出现点并显示标号,两点间以线连接,三点及以上成面,围成的面积的具体数据显示在中心,点击右键结束测量
  • 选择清空绘制,地图上的标记全部消失

功能代码:

1、初始化

Init () {
      // 引入个人token
       = 'xxxx'
      // 设置取景器
       = new ('cesiumContainer', {
        terrainProvider: (),
        selectionIndicator: false, // 不显示指示器小部件
        infoBox: false, //  不显示信息框
        sceneModePicker: false, // 不显示模式切换选项
        baseLayerPicker: false,
        navigationHelpButton: false,
        animation: false,
        shouldAnimate: false,
        timeline: false,
        geocoder: false,
        homeButton: false,
        // 添加ArcGIS在线影像底图
        imageryProvider: new ({
          url: '/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}',
          subdomains: ['0', '1', '2', '3'],
          tilingScheme: new ()
        })
      })
      // 若浏览器不支持pickPosition,显示报错信息
      if (!) {
        ('This browser does not support pickPosition.')
      }
      // 载入OSM建筑物
      // const osmBuildings = (()) // eslint-disable-line no-unused-vars
      // 初始化镜头
      (
        Cesium.(-122.2058, 46.1955, 1000.0),
        new Cesium.Cartesian3(5000.0, 5000.0, 5000.0)
      )
      (Cesium.)
    }

2、测距

/* 空间两点距离计算函数 */
    getLength (start, end) {
      // 将起点与终点位置信息从笛卡尔坐标形式转换为Cartographic形式
      let startCartographic = (start)
      let endCartographic = (end)
      // 初始化测地线
      let geodesic = new ()
      // 设置测地线起点和终点,EllipsoidGeodesic中setEndPoints常与surfaceDistance搭配使用
      (startCartographic, endCartographic)
      // 获取起点和终点之间的表面距离,单位为km,规定四舍五入保留两位小数
      // surfaceDistance返回number 单位为m,带小数
      // (( / 1000).toFixed(2))
      return ( / 1000).toFixed(2)
    },
    /* 空间两点计算中点函数 */
    getMidpoint (start, end) {
      let startPoint = (start)
      let endPoint = (end)
      let geodesic = new ()
      (startPoint, endPoint)
      let geoPoint = (0.5)
      (.(geoPoint))
      return .(geoPoint)
    },
    /* 在线段中点处添加标签,显示长度 */
    addLabel (midPoint, labelLength) {
      let viewer = 
      return ({
        name: '中点',
        position: midPoint,
        label: {
          text: labelLength + 'km',
          font: '20px sans-serif',
          fillColor: ,
          outlineWidth: 2,
          backgroundColor: ,
          showBackground: true,
          style: ,
          verticalOrigin: ,
          horizontalOrigin: ,
          heightReference: .CLAMP_TO_GROUND,
          disableDepthTestDistance: Number.POSITIVE_INFINITY
        }
      })
    },

3、测面

/* 测量空间面积 */
    // 方向
    Bearing (from, to) {
      let fromCartographic = (from)
      let toCartographic = (to)
      let lat1 = 
      let lon1 = 
      let lat2 = 
      let lon2 = 
      let angle = -Math.atan2((lon1 - lon2) * (lat2), (lat1) * (lat2) - (lat1) * (lat2) * (lon1 - lon2))
      if (angle < 0) {
        angle +=  * 2.0
      }
      return angle
    },
    // 角度
    pointAngle (point1, point2, point3) {
      let bearing21 = (point2, point1)
      let bearing23 = (point2, point3)
      let angle = bearing21 - bearing23
      if (angle < 0) {
        angle +=  * 2.0
      }
      return angle
    },
    /* 计算空间面积 */
    getArea (positions) {
      let res = 0
      for (let i = 0; i <  - 2; i++) {
        let j = (i + 1) % 
        let k = (i + 2) % 
        let totalAngle = (positions[i], positions[j], positions[k])
        let tempLength1 = (positions[j], positions[0])
        let tempLength2 = (positions[k], positions[0])
        res += tempLength1 * tempLength2 * (totalAngle) / 2
      }
      res = (2)
      // (res)
      res = parseFloat(res)
      // ((res))
      return (res)
    },
    /* 在最后一个点处添加标签,显示面积 */
    addArea (area, positions) {
      let viewer = 
      return ({
        name: '多边形面积',
        position: positions[ - 1],
        label: {
          text: area + '平方公里',
          font: '20px sans-serif',
          fillColor: ,
          outlineWidth: 2,
          backgroundColor: ,
          showBackground: true,
          style: ,
          pixelOffset: new Cesium.Cartesian2(60, -60),
          verticalOrigin: ,
          horizontalOrigin: ,
          heightReference: .CLAMP_TO_GROUND,
          disableDepthTestDistance: Number.POSITIVE_INFINITY
        }
      })
    }

4、地图生成线和面部分

/* 绘制函数 */
    drawPointLabel (position, pointNum) {
      let viewer = 
      // 本质上就是添加一个点的实体
      return ({
        name: '点几何对象',
        position: position,
        point: {
          color: ,
          pixelSize: 5,
          outlineWidth: 3,
          disableDepthTestDistance: Number.POSITIVE_INFINITY, //
          heightReference: .CLAMP_TO_GROUND // 规定贴地
        },
        label: {
          text: pointNum,
          font: '30px sans-serif',
          fillColor: ,
          outlineWidth: 2,
          backgroundColor: ,
          showBackground: true,
          style: ,
          verticalOrigin: ,
          horizontalOrigin: 
        }
      })
    },
    drawPoint (position) {
      let viewer = 
      // 本质上就是添加一个点的实体
      return ({
        position: position,
        point: {
          color: ,
          pixelSize: 5,
          outlineWidth: 3,
          disableDepthTestDistance: Number.POSITIVE_INFINITY,
          heightReference: .CLAMP_TO_GROUND // 规定贴地
        }
      })
    },
    drawPolyline (positions) {
      let viewer = 
      if ( < 1) return
      return ({
        name: '线几何对象',
        polyline: {
          positions: positions,
          width: 5.0,
          material: new ({
            // eslint-disable-next-line new-cap
            color: 
          }),
          depthFailMaterial: new ({
            // eslint-disable-next-line new-cap
            color: 
          }),
          clampToGround: true
        }
      })
    },
    drawPolygon (positions) {
      let viewer = 
      if ( < 2) return
      return ({
        name: '面几何对象',
        polygon: {
          hierarchy: positions,
          // eslint-disable-next-line new-cap
          material: new (
            (0.4)
          )
        }
      })
    },

5、清除绘制

/* 清除实体 */
    clearAllDrawn () {
      let viewer = 
       = []
       = 0
      ()
    },
    created () {
    },

6、按钮绑定

/* 根据类型绘制对象
    * @param type point polyline polygon */
    draw (type) {
      let that = this
      let viewer = 
      // let pointNum = 
      // (pointNum)
      let tempEntities = 
      let floatingPoint = 
      let activeShape = 
      let position = []
      let tempPoints = []
      let activeShapePoints = []
      // 开启深度检测
       = true
      // 创建场景的HTML canvas元素
      let handler = new ()
      switch (type) {
        // 绘制线
        case 'Polyline':
          // 取消鼠标双击事件
          (.LEFT_DOUBLE_CLICK)
          // 监听鼠标移动
          (function (movement) {
            if ((floatingPoint)) {
              let newPosition = ()
              if ((newPosition)) {
                (newPosition)
                ()
                (newPosition)
              }
            }
          }, .MOUSE_MOVE)
          // 左键单击开始画线
          (function (click) {
            let earthPosition = ()
            if ((earthPosition)) {
              floatingPoint = (earthPosition)
            }
            // 获取位置信息
            // 从相机位置创建一条射线,这条射线通过世界中像素所在的坐标,返回Cartesian3坐标
            let ray = ()
            // 找到射线与渲染的地球表面之间的交点。射线必须以世界坐标给出。返回Cartesian3坐标
            position = (ray, )
            (position) // 记录点位
             += 1
            let tempLength =  // 记录点数
            // 调用绘制点的接口
            let point = (tempPoints[ - 1], ())
            (point)
            // 存在超过一个点时
            if (tempLength > 1) {
              // 绘制线
              let pointLength = (tempPoints[ - 2], tempPoints[ - 1])
              let midPosition = (tempPoints[ - 2], tempPoints[ - 1])
              let pointline = ([tempPoints[ - 2], tempPoints[ - 1]])
              let pointLabel = (midPosition, pointLength)
              (pointline) // 保存记录
              (pointLabel)
            }
          }, .LEFT_CLICK)
          // 右键单击结束画线
          (function (click) {
            ()
            ()
            (activeShapePoints)
            (floatingPoint)
            tempPoints = [] // 清空点位记录
            ()
            handler = null
            floatingPoint = undefined
            activeShape = undefined
            activeShapePoints = []
            ()
          }, .RIGHT_CLICK)
          break
        // 绘制面
        case 'Polygon':
          // 取消鼠标双击事件
          (.LEFT_DOUBLE_CLICK)
          // 监听鼠标移动
          (function (movement) {
            if ((floatingPoint)) {
              let newPosition = ()
              if ((newPosition)) {
                (newPosition)
                ()
                (newPosition)
              }
            }
          }, .MOUSE_MOVE)
          // 左键单击开始画线
          (function (click) {
            let earthPosition = ()
            if ((earthPosition)) {
              if ( === 0) {
                floatingPoint = (earthPosition)
                (earthPosition)
                const dynamicPositions = new (function () {
                  return new (activeShapePoints)
                }, false)
                activeShape = (dynamicPositions)
              }
              (earthPosition)
            }
            // 获取位置信息
            let ray = ()
            position = (ray, )
            (position) // 记录点位
            let tempLength =  // 记录点数
             += 1
            // 调用绘制点的接口
            let point = (tempPoints[ - 1], ())
            (point)
            // 存在超过一个点时
            if (tempLength > 1) {
              // 绘制线
              let pointline = ([tempPoints[ - 2], tempPoints[ - 1]])
              (pointline) // 保存记录
            }
          }, .LEFT_CLICK)
          // 右键单击结束画面
          (function (click) {
            // 选择一个椭球或地图
            let cartesian = (, )
            if (cartesian) {
              let tempLength = 
              if (tempLength < 3) {
                alert('闭合操作需要至少3个点嗷')
              } else {
                // 闭合最后一条线
                let pointline = ([tempPoints[0], tempPoints[ - 1]])
                (pointline)
                (tempPoints)
                let pointArea = (tempPoints)
                ((pointArea), tempPoints)
                (tempPoints)
                ()
                handler = null
              }
            }
            ()
            (activeShapePoints)
            (floatingPoint)
            floatingPoint = undefined
            activeShapePoints = []
          }, .RIGHT_CLICK)
          break
      }
    }

7、完整代码(可直接引用查看Demo效果)

<template>
  <div >
    <div ></div>
    <div class="btnContainer">
      <button @click="draw('Polyline')">标点测距</button>
      <button @click="draw('Polygon')">标点测面</button>
      <button @click="clearAllDrawn()">清空数据</button>
      <div class="tip">
        <p>点击按钮后,在场景内单击左键标点,单击右键结束测量。</p>
        <p>点击“标点测距”按钮后在场景内单击鼠标左键加点,在两点间显示距离,单击右键结束测量。</p>
        <p>点击“标点测面”按钮后在场景内单击鼠标左键加点,单击右键在勾出的范围中显示面积。</p>
        <p>点击“清空数据”按钮删除所有数据。</p>
      </div>
    </div>
  </div>
</template>

<script>
import * as Cesium from 'cesium/Cesium'
import * as widgets from 'cesium/Widgets/'

export default {
  name: 'App',
  data () {
    return {
      viewer: undefined,
      tempEntities: [],
      pointNum: 0,
      floatingPoint: undefined,
      activeShape: undefined
    }
  },
  mounted () {
    ()
  },
  methods: {
    /* 初始化 */
    Init () {
      // 引入个人token
       = 'xxxx'
      // 设置取景器
       = new ('cesiumContainer', {
        terrainProvider: (),
        selectionIndicator: false, // 不显示指示器小部件
        infoBox: false, //  不显示信息框
        sceneModePicker: false, // 不显示模式切换选项
        baseLayerPicker: false,
        navigationHelpButton: false,
        animation: false,
        shouldAnimate: false,
        timeline: false,
        geocoder: false,
        homeButton: false,
        // 添加ArcGIS在线影像底图
        imageryProvider: new ({
          url: '/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}',
          subdomains: ['0', '1', '2', '3'],
          tilingScheme: new ()
        })
      })
      // 若浏览器不支持pickPosition,显示报错信息
      if (!) {
        ('This browser does not support pickPosition.')
      }
      // 载入OSM建筑物
      // const osmBuildings = (()) // eslint-disable-line no-unused-vars
      // 初始化镜头
      (
        Cesium.(-122.2058, 46.1955, 1000.0),
        new Cesium.Cartesian3(5000.0, 5000.0, 5000.0)
      )
      (Cesium.)
    },
    /* 空间两点距离计算函数 */
    getLength (start, end) {
      // 将起点与终点位置信息从笛卡尔坐标形式转换为Cartographic形式
      let startCartographic = (start)
      let endCartographic = (end)
      // 初始化测地线
      let geodesic = new ()
      // 设置测地线起点和终点,EllipsoidGeodesic中setEndPoints常与surfaceDistance搭配使用
      (startCartographic, endCartographic)
      // 获取起点和终点之间的表面距离,单位为km,规定四舍五入保留两位小数
      // surfaceDistance返回number 单位为m,带小数
      // (( / 1000).toFixed(2))
      return ( / 1000).toFixed(2)
    },
    /* 空间两点计算中点函数 */
    getMidpoint (start, end) {
      let startPoint = (start)
      let endPoint = (end)
      let geodesic = new ()
      (startPoint, endPoint)
      let geoPoint = (0.5)
      (.(geoPoint))
      return .(geoPoint)
    },
    addLabel (midPoint, labelLength) {
      let viewer = 
      return ({
        name: '中点',
        position: midPoint,
        label: {
          text: labelLength + 'km',
          font: '20px sans-serif',
          fillColor: ,
          outlineWidth: 2,
          backgroundColor: ,
          showBackground: true,
          style: ,
          verticalOrigin: ,
          horizontalOrigin: ,
          heightReference: .CLAMP_TO_GROUND,
          disableDepthTestDistance: Number.POSITIVE_INFINITY
        }
      })
    },
    /* 测量空间面积 */
    // 方向
    Bearing (from, to) {
      let fromCartographic = (from)
      let toCartographic = (to)
      let lat1 = 
      let lon1 = 
      let lat2 = 
      let lon2 = 
      let angle = -Math.atan2((lon1 - lon2) * (lat2), (lat1) * (lat2) - (lat1) * (lat2) * (lon1 - lon2))
      if (angle < 0) {
        angle +=  * 2.0
      }
      return angle
    },
    // 角度
    pointAngle (point1, point2, point3) {
      let bearing21 = (point2, point1)
      let bearing23 = (point2, point3)
      let angle = bearing21 - bearing23
      if (angle < 0) {
        angle +=  * 2.0
      }
      return angle
    },
    getArea (positions) {
      let res = 0
      for (let i = 0; i <  - 2; i++) {
        let j = (i + 1) % 
        let k = (i + 2) % 
        let totalAngle = (positions[i], positions[j], positions[k])
        let tempLength1 = (positions[j], positions[0])
        let tempLength2 = (positions[k], positions[0])
        res += tempLength1 * tempLength2 * (totalAngle) / 2
      }
      res = (2)
      // (res)
      res = parseFloat(res)
      // ((res))
      return (res)
    },
    addArea (area, positions) {
      let viewer = 
      return ({
        name: '多边形面积',
        position: positions[ - 1],
        label: {
          text: area + '平方公里',
          font: '20px sans-serif',
          fillColor: ,
          outlineWidth: 2,
          backgroundColor: ,
          showBackground: true,
          style: ,
          pixelOffset: new Cesium.Cartesian2(60, -60),
          verticalOrigin: ,
          horizontalOrigin: ,
          heightReference: .CLAMP_TO_GROUND,
          disableDepthTestDistance: Number.POSITIVE_INFINITY
        }
      })
    },
    /* 绘制函数 */
    drawPointLabel (position, pointNum) {
      let viewer = 
      // 本质上就是添加一个点的实体
      return ({
        name: '点几何对象',
        position: position,
        point: {
          color: ,
          pixelSize: 5,
          outlineWidth: 3,
          disableDepthTestDistance: Number.POSITIVE_INFINITY, //
          heightReference: .CLAMP_TO_GROUND // 规定贴地
        },
        label: {
          text: pointNum,
          font: '30px sans-serif',
          fillColor: ,
          outlineWidth: 2,
          backgroundColor: ,
          showBackground: true,
          style: ,
          verticalOrigin: ,
          horizontalOrigin: 
        }
      })
    },
    drawPoint (position) {
      let viewer = 
      // 本质上就是添加一个点的实体
      return ({
        position: position,
        point: {
          color: ,
          pixelSize: 5,
          outlineWidth: 3,
          disableDepthTestDistance: Number.POSITIVE_INFINITY,
          heightReference: .CLAMP_TO_GROUND // 规定贴地
        }
      })
    },
    drawPolyline (positions) {
      let viewer = 
      if ( < 1) return
      return ({
        name: '线几何对象',
        polyline: {
          positions: positions,
          width: 5.0,
          material: new ({
            // eslint-disable-next-line new-cap
            color: 
          }),
          depthFailMaterial: new ({
            // eslint-disable-next-line new-cap
            color: 
          }),
          clampToGround: true
        }
      })
    },
    drawPolygon (positions) {
      let viewer = 
      if ( < 2) return
      return ({
        name: '面几何对象',
        polygon: {
          hierarchy: positions,
          // eslint-disable-next-line new-cap
          material: new (
            (0.4)
          )
        }
      })
    },
    /* 清除实体 */
    clearAllDrawn () {
      let viewer = 
       = []
       = 0
      ()
    },
    created () {
    },
    /* 根据类型绘制对象
    * @param type point polyline polygon */
    draw (type) {
      let that = this
      let viewer = 
      // let pointNum = 
      // (pointNum)
      let tempEntities = 
      let floatingPoint = 
      let activeShape = 
      let position = []
      let tempPoints = []
      let activeShapePoints = []
      // 开启深度检测
       = true
      // 创建场景的HTML canvas元素
      let handler = new ()
      switch (type) {
        // 绘制线
        case 'Polyline':
          // 取消鼠标双击事件
          (.LEFT_DOUBLE_CLICK)
          // 监听鼠标移动
          (function (movement) {
            if ((floatingPoint)) {
              let newPosition = ()
              if ((newPosition)) {
                (newPosition)
                ()
                (newPosition)
              }
            }
          }, .MOUSE_MOVE)
          // 左键单击开始画线
          (function (click) {
            let earthPosition = ()
            if ((earthPosition)) {
              floatingPoint = (earthPosition)
            }
            // 获取位置信息
            // 从相机位置创建一条射线,这条射线通过世界中像素所在的坐标,返回Cartesian3坐标
            let ray = ()
            // 找到射线与渲染的地球表面之间的交点。射线必须以世界坐标给出。返回Cartesian3坐标
            position = (ray, )
            (position) // 记录点位
             += 1
            let tempLength =  // 记录点数
            // 调用绘制点的接口
            let point = (tempPoints[ - 1], ())
            (point)
            // 存在超过一个点时
            if (tempLength > 1) {
              // 绘制线
              let pointLength = (tempPoints[ - 2], tempPoints[ - 1])
              let midPosition = (tempPoints[ - 2], tempPoints[ - 1])
              let pointline = ([tempPoints[ - 2], tempPoints[ - 1]])
              let pointLabel = (midPosition, pointLength)
              (pointline) // 保存记录
              (pointLabel)
            }
          }, .LEFT_CLICK)
          // 右键单击结束画线
          (function (click) {
            ()
            ()
            (activeShapePoints)
            (floatingPoint)
            tempPoints = [] // 清空点位记录
            ()
            handler = null
            floatingPoint = undefined
            activeShape = undefined
            activeShapePoints = []
            ()
          }, .RIGHT_CLICK)
          break
        // 绘制面
        case 'Polygon':
          // 取消鼠标双击事件
          (.LEFT_DOUBLE_CLICK)
          // 监听鼠标移动
          (function (movement) {
            if ((floatingPoint)) {
              let newPosition = ()
              if ((newPosition)) {
                (newPosition)
                ()
                (newPosition)
              }
            }
          }, .MOUSE_MOVE)
          // 左键单击开始画线
          (function (click) {
            let earthPosition = ()
            if ((earthPosition)) {
              if ( === 0) {
                floatingPoint = (earthPosition)
                (earthPosition)
                const dynamicPositions = new (function () {
                  return new (activeShapePoints)
                }, false)
                activeShape = (dynamicPositions)
              }
              (earthPosition)
            }
            // 获取位置信息
            let ray = ()
            position = (ray, )
            (position) // 记录点位
            let tempLength =  // 记录点数
             += 1
            // 调用绘制点的接口
            let point = (tempPoints[ - 1], ())
            (point)
            // 存在超过一个点时
            if (tempLength > 1) {
              // 绘制线
              let pointline = ([tempPoints[ - 2], tempPoints[ - 1]])
              (pointline) // 保存记录
            }
          }, .LEFT_CLICK)
          // 右键单击结束画面
          (function (click) {
            // 选择一个椭球或地图
            let cartesian = (, )
            if (cartesian) {
              let tempLength = 
              if (tempLength < 3) {
                alert('闭合操作需要至少3个点嗷')
              } else {
                // 闭合最后一条线
                let pointline = ([tempPoints[0], tempPoints[ - 1]])
                (pointline)
                (tempPoints)
                let pointArea = (tempPoints)
                ((pointArea), tempPoints)
                (tempPoints)
                ()
                handler = null
              }
            }
            ()
            (activeShapePoints)
            (floatingPoint)
            floatingPoint = undefined
            activeShapePoints = []
          }, .RIGHT_CLICK)
          break
      }
    }
  }
}
</script>

<style>
html,
body {
  width: 100%;
  height: 100%;
  padding: 0;
  margin: 0;
}
#app,#cesiumContainer {
  font-family: "Avenir", Helvetica, Arial, sans-serif;
  width: 100%;
  height: 100%;
  overflow: hidden;
}
.btnContainer {
  position: absolute;
  left: 15px;
  top: 80px;
  padding: 10px 15px;
  /*添加圆角边框*/
  border-radius: 5px;
  border: 1px solid rgba(128,128,128, 0.5);
  color: #ffffff;
  background: rgba(0, 0, 0,0.4);
  box-shadow: 0 4px 8px rgb(128 128 128 / 50%);
  max-width: 300px;
}
button {
  background: transparent;
  border: 1px solid #00d0ffb8;
  color: white;
  padding: 7px 9px;
  border-radius: 3px;
  cursor: pointer;
}
.tip p{
  margin: 2px 0px;
  padding: 5px 1px;
}
</style>