cesium vue 天地图 WMTS图层数据 绘制点、线、面 测距、测面积

该文章展示了一个使用CesiumVue的示例,通过Vue组件实现3D地图上的点、线、面绘制,并提供了测距和测面积的功能。用户可以通过点击按钮来选择绘制类型,然后通过鼠标交互进行测量。代码中包括了初始化地图、添加图层、绘制和测量的方法,以及清除已绘制内容的逻辑。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

cesium vue 绘制点、线、面 测距、测面积
开箱即用
在这里插入图片描述

<template>
  <div class="map-container">
    <div class="btn-container">
      <el-button type="primary" @click="drawSelf('Point')">绘制点</el-button>
      <el-button type="primary"  @click="drawSelf('Polyline')">绘制线</el-button>
      <el-button type="primary"  @click="drawSelf('Polygon')">绘制面</el-button>
    </div>
    <div class="btn-container">
      <el-button type="primary" @click="draw('Polyline')">标点测距</el-button>
      <el-button type="primary" @click="draw('Polygon')">标点测面</el-button>
      <el-button type="primary" @click="clearAllDrawn()">清空数据</el-button>
    </div>
    <div class="btn-container">
    </div>
    <p>
      点击鼠标左键标点,右键结束
    </p>
    <div id="cesiumContainer"></div>
  </div>
</template>
 
<script>
import * as Cesium from "cesium";
import  {Cesium3DTileset, Viewer, WebMapTileServiceImageryProvider} from "cesium";
export default {
  name: 'App',
  data () {
    return {
      tempEntities: [],
      pointNum: 0,
      floatingPoint: undefined,
      activeShape: undefined,
      viewer: undefined,
      roadData:[],
      Cesium
    }
  },
  mounted () {
    this.Init()
    this.init3Dtiles()
  },
  methods: {
    /* 初始化 */
    Init () {
      const tianDiTuToken = '天地图token'
      const mapOption = {
          url: `http://t0.tianditu.com/img_w/wmts?service=wmts&request=GetTile&version=1.0.0&LAYER=img&tileMatrixSet=w&TileMatrix={TileMatrix}&TileRow={TileRow}&TileCol={TileCol}&style=default&format=tiles&tk=${tianDiTuToken}`,
          layer: "tdtBasicLayer",
          style: "default",
          format: "image/jpeg",
          tileMatrixSetID: "GoogleMapsCompatible",
          level:14,
          maximumLevel: 18
      }
      const imgProvider = new WebMapTileServiceImageryProvider(mapOption);
      const viewerOption = {
        animation: false,//是否创建动画小器件,左下角仪表
        baseLayerPicker: true,//是否显示图层选择器
        fullscreenButton: false,//是否显示全屏按钮
        geocoder: true,//是否显示geocoder小器件,右上角查询按钮
        homeButton: false,//是否显示Home按钮
        infoBox: false,//是否显示信息框
        sceneModePicker: false,//是否显示3D/2D选择器
        scene3DOnly: false,//如果设置为true,则所有几何图形以3D模式绘制以节约GPU资源
        selectionIndicator: false,//是否显示选取指示器组件
        timeline: false,//是否显示时间轴
        navigationHelpButton: false,//是否显示右上角的帮助按钮
        baselLayerPicker: false,// 将图层选择的控件关掉,才能添加其他影像数据
        shadows: false,//是否显示背影
        shouldAnimate: true,
        imageryProvider: imgProvider,
      }
      this.viewer = new Viewer("cesiumContainer", viewerOption);
      // 添加中文注记图层
      this.viewer.imageryLayers.addImageryProvider(
          new WebMapTileServiceImageryProvider({
            url: `http://t0.tianditu.com/cia_w/wmts?service=wmts&request=GetTile&version=1.0.0&LAYER=cia&tileMatrixSet=w&TileMatrix={TileMatrix}&TileRow={TileRow}&TileCol={TileCol}&style=default.jpg&tk=${tianDiTuToken}`,
            layer: "tdtAnnoLayer",
            style: "default",
            format: "image/jpeg",
            tileMatrixSetID: "GoogleMapsCompatible",
          //   show: false,
          })
      );
      //优化项--关闭相关特效
      this.viewer.scene.debugShowFramesPerSecond = true;//显示fps
      this.viewer.scene.moon.show = false;//月亮
      this.viewer.scene.fog.enabled = false;//雾
      this.viewer.scene.sun.show = false;//太阳
      this.viewer.scene.skyBox.show = false;//天空盒
      this.viewer.resolutionScale = 1.0;//画面细度,默认值为1.0
    },
    init3Dtiles(){
      let provider = new Cesium.WebMapTileServiceImageryProvider({
        url: 'http://pan.ifunpm.com/image/wmts/5EEBP6px/{z}/{x}/{y}', //服务地址WMTS
        layer: '', //图层名称
        style: '',
        format: 'image/png',
        tileMatrixSetID: "EPSG:900913",  // "EPSG:900913"
        // 地图飞入  定位的坐标  后端返回的数据
        rectangle: Cesium.Rectangle.fromDegrees(112.8308, 28.2280, 112.8458, 28.2412),
        //之所以要在这里提出这个tileMatrixLabels参数,是因为GeoServer在缓冲切分瓦片时对每一个缩放级别的存储目录没有使用相应的数字,而使用了网格+级别的方式来命名,如“EPSG:9100913:10”表示的是使用“EPSG:9100913”网格切分的第10级瓦片。
        tileMatrixLabels : [],
        tilingScheme: new Cesium.GeographicTilingScheme({
          numberOfLevelZeroTilesX: 2,
          numberOfLevelZeroTilesY: 1
        }),
        maximumLevel: 18 
      });
      let layer = this.viewer.imageryLayers.addImageryProvider(provider);
      //控制图层显示
      this.viewer.flyTo(layer, {
          duration: 2
      });
    },
    /* 空间两点距离计算函数 */
    getLength (start, end) {
      // 将起点与终点位置信息从笛卡尔坐标形式转换为Cartographic形式
      let startCartographic = Cesium.Cartographic.fromCartesian(start)
      let endCartographic = Cesium.Cartographic.fromCartesian(end)
      // 初始化测地线
      let geodesic = new Cesium.EllipsoidGeodesic()
      // 设置测地线起点和终点,EllipsoidGeodesic中setEndPoints常与surfaceDistance搭配使用
      geodesic.setEndPoints(startCartographic, endCartographic)
      // 获取起点和终点之间的表面距离,单位为km,规定四舍五入保留两位小数
      // surfaceDistance返回number 单位为m,带小数
      return (geodesic.surfaceDistance / 1000).toFixed(2)
    },
    /* 空间两点计算中点函数 */
    getMidpoint (start, end) {
      let startPoint = Cesium.Cartographic.fromCartesian(start)
      let endPoint = Cesium.Cartographic.fromCartesian(end)
      let geodesic = new Cesium.EllipsoidGeodesic()
      geodesic.setEndPoints(startPoint, endPoint)
      let geoPoint = geodesic.interpolateUsingFraction(0.5)
      return Cesium.Ellipsoid.WGS84.cartographicToCartesian(geoPoint)
    },
    addLabel (midPoint, labelLength) {
      let viewer = this.viewer
      return viewer.entities.add({
        name: '中点',
        position: midPoint,
        label: {
          text: labelLength + 'km',
          font: '20px sans-serif',
          fillColor: Cesium.Color.WHITE,
          outlineWidth: 2,
          backgroundColor: Cesium.Color.BLACK,
          showBackground: true,
          style: Cesium.LabelStyle.FILL,
          verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
          horizontalOrigin: Cesium.HorizontalOrigin.CENTER,
          heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
          disableDepthTestDistance: Number.POSITIVE_INFINITY // 不被建筑物遮挡
        }
      })
    },
    /* 测量空间面积 */
    // 方向
    Bearing (from, to) {
      let fromCartographic = Cesium.Cartographic.fromCartesian(from)
      let toCartographic = Cesium.Cartographic.fromCartesian(to)
      let lat1 = fromCartographic.latitude
      let lon1 = fromCartographic.longitude
      let lat2 = toCartographic.latitude
      let lon2 = toCartographic.longitude
      let angle = -Math.atan2(Math.sin(lon1 - lon2) * Math.cos(lat2), Math.cos(lat1) * Math.sin(lat2) - Math.sin(lat1) * Math.cos(lat2) * Math.cos(lon1 - lon2))
      if (angle < 0) {
        angle += Math.PI * 2.0
      }
      return angle
    },
    // 角度
    pointAngle (point1, point2, point3) {
      let bearing21 = this.Bearing(point2, point1)
      let bearing23 = this.Bearing(point2, point3)
      let angle = bearing21 - bearing23
      if (angle < 0) {
        angle += Math.PI * 2.0
      }
      return angle
    },
    getArea (positions) {
      let res = 0
      for (let i = 0; i < positions.length - 2; i++) {
        let j = (i + 1) % positions.length
        let k = (i + 2) % positions.length
        let totalAngle = this.pointAngle(positions[i], positions[j], positions[k])
        let tempLength1 = this.getLength(positions[j], positions[0])
        let tempLength2 = this.getLength(positions[k], positions[0])
        res += tempLength1 * tempLength2 * Math.sin(totalAngle) / 2
      }
      res = res.toFixed(2)
      res = parseFloat(res)
      return Math.abs(res)
    },
    addArea (area, positions) {
      let viewer = this.viewer
      return viewer.entities.add({
        name: '多边形面积',
        position: positions[positions.length - 1],
        label: {
          text: area + '平方公里',
          font: '20px sans-serif',
          fillColor: Cesium.Color.WHITE,
          outlineWidth: 2,
          backgroundColor: Cesium.Color.BLACK,
          showBackground: true,
          style: Cesium.LabelStyle.FILL,
          pixelOffset: new Cesium.Cartesian2(60, -60),
          verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
          horizontalOrigin: Cesium.HorizontalOrigin.CENTER,
          heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
          disableDepthTestDistance: Number.POSITIVE_INFINITY
        }
      })
    },
    /* 绘制函数 */
    drawPointLabel (position, pointNum) {
      let viewer = this.viewer
      // 本质上就是添加一个点的实体
      return viewer.entities.add({
        name: '点几何对象',
        position: position,
        point: {
          color: Cesium.Color.WHEAT,
          pixelSize: 5,
          outlineWidth: 3,
          disableDepthTestDistance: Number.POSITIVE_INFINITY, //
          heightReference: Cesium.HeightReference.CLAMP_TO_GROUND // 规定贴地
        },
        label: {
          text: pointNum,
          font: '30px sans-serif',
          fillColor: Cesium.Color.WHITE,
          outlineWidth: 2,
          backgroundColor: Cesium.Color.BLACK,
          showBackground: true,
          style: Cesium.LabelStyle.FILL,
          disableDepthTestDistance: Number.POSITIVE_INFINITY,
          verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
          horizontalOrigin: Cesium.HorizontalOrigin.CENTER
        }
      })
    },
    drawPoint (position) {
      let viewer = this.viewer
      // 本质上就是添加一个点的实体
      return viewer.entities.add({
        position: position,
        point: {
          color: Cesium.Color.WHEAT,
          pixelSize: 5,
          outlineWidth: 3,
          disableDepthTestDistance: Number.POSITIVE_INFINITY,
          heightReference: Cesium.HeightReference.CLAMP_TO_GROUND // 规定贴地
        }
      })
    },
    drawPolyline (positions) {
      let viewer = this.viewer
      if (positions.length < 1) return
      return viewer.entities.add({
        name: '线几何对象',
        polyline: {
          positions: positions,
          width: 5.0,
          material: new Cesium.PolylineGlowMaterialProperty({
            // eslint-disable-next-line new-cap
            color: Cesium.Color.WHEAT
          }),
          depthFailMaterial: new Cesium.PolylineGlowMaterialProperty({
            // eslint-disable-next-line new-cap
            color: Cesium.Color.WHEAT
          }),
          clampToGround: true
        }
      })
    },
    drawPolygon (positions) {
      let viewer = this.viewer
      if (positions.length < 2) return
      return viewer.entities.add({
        name: '面几何对象',
        polygon: {
          hierarchy: positions,
          // eslint-disable-next-line new-cap
          material: new Cesium.ColorMaterialProperty(
            Cesium.Color.WHEAT.withAlpha(0.4)
          )
        }
      })
    },
    /* 清除实体 */
    clearAllDrawn () {
      let viewer = this.viewer
      this.tempEntities = []
      this.pointNum = 0
      viewer.entities.removeAll()
    },
    drawSelf(type){
      let that = this
      let viewer = this.viewer
      let position= []
      let tempPointsSelf = []
      let tempEntitiesSelf = []
      // 开启深度检测
      viewer.scene.globe.depthTestAgainstTerrain = true
      // 创建场景的HTML canvas元素
      let handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas)
      switch (type) {
        case 'Point':
          // 监听鼠标左键 左键单击开始绘制
          handler.setInputAction(function (movement) {
            // 从相机位置创建一条射线,这条射线通过世界中movement.position像素所在的坐标
            let ray = viewer.camera.getPickRay(movement.position)
            // 找到射线与渲染的地球表面之间的交点。射线必须以世界坐标给出。
            position = viewer.scene.globe.pick(ray, viewer.scene)
            // 画出交点
            let point = that.drawPoint(position)
            // // 将其添加到tempEntities数组的末尾
            // tempEntities.push(point)
          }, Cesium.ScreenSpaceEventType.LEFT_CLICK)
          // 左键双击或右键单击时停止绘制
          handler.setInputAction(function () {
            // 停止监听 关闭事件句柄
            handler.destroy()
            handler = null
          }, Cesium.ScreenSpaceEventType.LEFT_DOUBLE_CLICK)
          handler.setInputAction(function () {
            handler.destroy()
            handler = null
          }, Cesium.ScreenSpaceEventType.RIGHT_CLICK)
        break
        case 'Polyline':
          // 监听鼠标移动
          handler.setInputAction(function (movement) {
          }, Cesium.ScreenSpaceEventType.MOUSE_MOVE)
          // 左键单击开始画线
          handler.setInputAction(function (click) {
              // 获取位置信息
              let ray = viewer.camera.getPickRay(click.position)
              position = viewer.scene.globe.pick(ray, viewer.scene)
              tempPointsSelf.push(position) // 记录点位
            let tempLength = tempPointsSelf.length // 记录点数
            // 调用绘制点的接口
            let point = that.drawPoint(tempPointsSelf[tempPointsSelf.length - 1])
            tempEntitiesSelf.push(point)
            // 存在超过一个点时
            if (tempLength > 1) {
              // 绘制线
              let pointline = that.drawPolyline([tempPointsSelf[tempPointsSelf.length - 2], tempPointsSelf[tempPointsSelf.length - 1]])
              tempEntitiesSelf.push(pointline) // 保存记录
            }
          }, Cesium.ScreenSpaceEventType.LEFT_CLICK)
          // 右键单击结束画线
          handler.setInputAction(function (click) {
            tempPointsSelf = [] // 清空点位记录
            handler.destroy()
            handler = null
          }, Cesium.ScreenSpaceEventType.RIGHT_CLICK)
        break  
        case 'Polygon':
          // 取消鼠标双击事件
          viewer.cesiumWidget.screenSpaceEventHandler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_DOUBLE_CLICK)
          // 监听鼠标移动
          handler.setInputAction(function (movement) {
          }, Cesium.ScreenSpaceEventType.MOUSE_MOVE)
          // 左键单击开始画线
          handler.setInputAction(function (click) {
          // 获取位置信息
            let ray = viewer.camera.getPickRay(click.position)
            position = viewer.scene.globe.pick(ray, viewer.scene)
            tempPointsSelf.push(position) // 记录点位
            let tempLength = tempPointsSelf.length // 记录点数
            // 调用绘制点的接口
            let point = that.drawPoint(tempPointsSelf[tempPointsSelf.length - 1])
            tempEntitiesSelf.push(point)
            // 存在超过一个点时
            if (tempLength > 1) {
              // 绘制线
              let pointline = that.drawPolyline([tempPointsSelf[tempPointsSelf.length - 2], tempPointsSelf[tempPointsSelf.length - 1]])
              tempEntitiesSelf.push(pointline) // 保存记录
            }
          }, Cesium.ScreenSpaceEventType.LEFT_CLICK)
          // 右键单击结束画面
          handler.setInputAction(function (click) {
            // 选择一个椭球或地图
            let cartesian = viewer.camera.pickEllipsoid(click.position, viewer.scene.globe.ellipsoid)
            if (cartesian) {
              let tempLength = tempPointsSelf.length
              if (tempLength < 3) {
                alert('闭合操作需要至少3个点!')
              } else {
                // 闭合最后一条线
                let pointline = that.drawPolyline([tempPointsSelf[tempPointsSelf.length - 1], tempPointsSelf[0]])
                tempEntitiesSelf.push(pointline)
                that.drawPolygon(tempPointsSelf)
                tempEntitiesSelf.push(tempPointsSelf)
                handler.destroy()
                handler = null
              }
            }
          }, Cesium.ScreenSpaceEventType.RIGHT_CLICK)
        break  
      }
    },
    /* 根据类型绘制对象
    * @param type point polyline polygon */
    draw (type) {
      let that = this
      let viewer = this.viewer
      let tempEntities = this.tempEntities
      let floatingPoint = this.floatingPoint
      let activeShape = this.activeShape
      let position = []
      let tempPoints = []
      let activeShapePoints = []
      // 开启深度检测
      viewer.scene.globe.depthTestAgainstTerrain = true
      // 创建场景的HTML canvas元素
      let handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas)
      switch (type) {
        // 绘制线
        case 'Polyline':
          // 取消鼠标双击事件
          viewer.cesiumWidget.screenSpaceEventHandler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_DOUBLE_CLICK)
          // 监听鼠标移动
          handler.setInputAction(function (movement) {
            if (Cesium.defined(floatingPoint)) {
              let newPosition = viewer.scene.pickPosition(movement.endPosition)
              if (Cesium.defined(newPosition)) {
                floatingPoint.position.setValue(newPosition)
                activeShapePoints.pop()
                activeShapePoints.push(newPosition)
              }
            }
          }, Cesium.ScreenSpaceEventType.MOUSE_MOVE)
          // 左键单击开始画线
          handler.setInputAction(function (click) {
            let earthPosition = viewer.scene.pickPosition(click.position)
            if (Cesium.defined(earthPosition)) {
              floatingPoint = that.drawPoint(earthPosition)
            }
            // 获取位置信息
            // 从相机位置创建一条射线,这条射线通过世界中movement.position像素所在的坐标,返回Cartesian3坐标
            let ray = viewer.camera.getPickRay(click.position)
            // 找到射线与渲染的地球表面之间的交点。射线必须以世界坐标给出。返回Cartesian3坐标
            position = viewer.scene.globe.pick(ray, viewer.scene)
            tempPoints.push(position) // 记录点位
            that.pointNum += 1
            let tempLength = tempPoints.length // 记录点数
            // 调用绘制点的接口
            let point = that.drawPointLabel(tempPoints[tempPoints.length - 1], JSON.stringify(that.pointNum))
            tempEntities.push(point)
            // 存在超过一个点时
            if (tempLength > 1) {
              // 绘制线
              let pointLength = that.getLength(tempPoints[tempPoints.length - 2], tempPoints[tempPoints.length - 1])
              let midPosition = that.getMidpoint(tempPoints[tempPoints.length - 2], tempPoints[tempPoints.length - 1])
              let pointline = that.drawPolyline([tempPoints[tempPoints.length - 2], tempPoints[tempPoints.length - 1]])
              let pointLabel = that.addLabel(midPosition, pointLength)
              tempEntities.push(pointline) // 保存记录
              tempEntities.push(pointLabel)
            }
          }, Cesium.ScreenSpaceEventType.LEFT_CLICK)
          // 右键单击结束画线
          handler.setInputAction(function (click) {
            activeShapePoints.pop()
            viewer.entities.remove(activeShapePoints)
            viewer.entities.remove(floatingPoint)
            tempPoints = [] // 清空点位记录
            handler.destroy()
            handler = null
            floatingPoint = undefined
            activeShape = undefined
            activeShapePoints = []
          }, Cesium.ScreenSpaceEventType.RIGHT_CLICK)
          break
        // 绘制面
        case 'Polygon':
          // 取消鼠标双击事件
          viewer.cesiumWidget.screenSpaceEventHandler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_DOUBLE_CLICK)
          // 监听鼠标移动
          handler.setInputAction(function (movement) {
            if (Cesium.defined(floatingPoint)) {
              let newPosition = viewer.scene.pickPosition(movement.endPosition)
              if (Cesium.defined(newPosition)) {
                floatingPoint.position.setValue(newPosition)
                activeShapePoints.pop()
                activeShapePoints.push(newPosition)
              }
            }
          }, Cesium.ScreenSpaceEventType.MOUSE_MOVE)
          // 左键单击开始画线
          handler.setInputAction(function (click) {
            let earthPosition = viewer.scene.pickPosition(click.position)
            if (Cesium.defined(earthPosition)) {
              if (activeShapePoints.length === 0) {
                floatingPoint = that.drawPoint(earthPosition)
                activeShapePoints.push(earthPosition)
                const dynamicPositions = new Cesium.CallbackProperty(function () {
                  return new Cesium.PolygonHierarchy(activeShapePoints)
                }, false)
                activeShape = that.drawPolygon(dynamicPositions)
              }
              activeShapePoints.push(earthPosition)
            }
            // 获取位置信息
            let ray = viewer.camera.getPickRay(click.position)
            position = viewer.scene.globe.pick(ray, viewer.scene)
            tempPoints.push(position) // 记录点位
            let tempLength = tempPoints.length // 记录点数
            that.pointNum += 1
            // 调用绘制点的接口
            let point = that.drawPointLabel(tempPoints[tempPoints.length - 1], JSON.stringify(that.pointNum))
            tempEntities.push(point)
            // 存在超过一个点时
            if (tempLength > 1) {
              // 绘制线
              let pointline = that.drawPolyline([tempPoints[tempPoints.length - 2], tempPoints[tempPoints.length - 1]])
              tempEntities.push(pointline) // 保存记录
            }
          }, Cesium.ScreenSpaceEventType.LEFT_CLICK)
          // 右键单击结束画面
          handler.setInputAction(function (click) {
            // 选择一个椭球或地图
            let cartesian = viewer.camera.pickEllipsoid(click.position, viewer.scene.globe.ellipsoid)
            if (cartesian) {
              let tempLength = tempPoints.length
              if (tempLength < 3) {
                alert('闭合操作需要至少3个点!')
              } else {
                // 闭合最后一条线
                let pointline = that.drawPolyline([tempPoints[0], tempPoints[tempPoints.length - 1]])
                tempEntities.push(pointline)
                that.drawPolygon(tempPoints)
                let pointArea = that.getArea(tempPoints)
                that.addArea(JSON.stringify(pointArea), tempPoints)
                tempEntities.push(tempPoints)
                handler.destroy()
                handler = null
              }
            }
            activeShapePoints.pop()
            viewer.entities.remove(activeShapePoints)
            viewer.entities.remove(floatingPoint)
            floatingPoint = undefined
            activeShapePoints = []
          }, Cesium.ScreenSpaceEventType.RIGHT_CLICK)
          break
      }
    }
  }
}
</script>
 
<style>
canvas{
width:1200px;
height:600px;
margin: 20px;
}
.cesium-performanceDisplay,.cesium-viewer-bottom{
  display: none;
}
</style>
<style lang="scss" scoped>
.map-container{
  width:100%;
  height:100%;
  padding:20px;
}
.btn-container{
  margin-bottom:20px;
}
</style>
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值