美文网首页
个人笔记|vue+Cesium绘制点线面

个人笔记|vue+Cesium绘制点线面

作者: 图骨南 | 来源:发表于2022-03-10 16:30 被阅读0次
    • 编辑器:webstorm
    • 服务:node.js npm
    • CesiumJS: Cesium-v1.9.1
    • vue v2.9.6
    • 顺便丢一个感觉挺实用的cesium中文文档

    一、功能

    绘制点线面

    点击按钮切换模式,鼠标拖拽地图移动,在相应模式下单击右键结束绘制

    • 点模式

      点击“绘制点”按钮后在地图内单击鼠标左键,在地图上添加一个点,单击右键结束画点

    • 线模式

      点击“绘制线”按钮后单击鼠标左键在地图上添加一个点并开始画线,单击右键结束画线

    • 面模式

      点击“绘制面”按钮后单击鼠标左键在地图上添加一个点并开始画线,需要至少三个点以形成面,单击鼠标右键结束画线

    • 清空绘制

      点击按钮后删除所有绘制对象

    二、 实现

    环境搭建参照同文集下vue+cesium环境搭建

    1. 初始化场景

    参照同文集下vueCesium构建航班轨迹文件添加token,进行cesium初始化

    不过因为窗口显示的是野外,只想简单实现功能的话并不用加OSM Building(网速不行的话也同理(比如我

    此处借鉴官方示例,利用camera的lookAt方法令镜头初始化在火山口(?)上方而不是显示初始化地球

    在初始化场景的时候还可以通过赋值true/false来选择是否显示窗口小部件

    infoBox: 信息框

    selectionIndicator: 选择指示框

    navigation: 导航插件

    animation: 动画控制部件,左下角仪表盘

    shouldAnimate: 当动画控件出现,用来控制通过旋转控件调整动画速度

    timeline: 时间轴控件

    baseLayerPicker: 图层选择器

    geocoder: 查询定位按钮

    homeButton: 主页按钮

    sceneModePicker: 地图以3D/2D模式显示

    navigationHelpButton: 导航栏帮助按钮

    具体代码

    methods: {
        // 初始化
        Init () {
          // 引入个人token,这里填自己在Cesium Ion的token
          Cesium.Ion.defaultAccessToken = 'your_token'
          // 设置取景器
          this.viewer = new Cesium.Viewer('cesiumContainer', {
            terrainProvider: Cesium.createWorldTerrain(),
            selectionIndicator: false, // 不显示指示器小部件
            infoBox: false, //  不显示信息框
            sceneModePicker: false, // 不显示模式切换选项
            baseLayerPicker: false, // 不显示图层选择器
            navigationHelpButton: false // 不显示导航栏帮助按钮
          })
          // 若浏览器不支持pickPosition,显示报错信息
          if (!this.viewer.scene.pickPositionSupported) {
            window.alert('This browser does not support pickPosition.')
          }
          // 载入OSM建筑物,网速慢的话干脆注掉
          // const osmBuildings = this.viewer.scene.primitives.add(Cesium.createOsmBuildings()) // eslint-disable-line no-unused-vars
          // 初始化镜头
          this.viewer.camera.lookAt(
            Cesium.Cartesian3.fromDegrees(-122.2058, 46.1955, 1000.0),
            new Cesium.Cartesian3(5000.0, 5000.0, 5000.0)
          )      this.viewer.camera.lookAtTransform(Cesium.Matrix4.IDENTITY)
        },
    }
    

    2. 添加选择界面

    结构:一个内嵌页面组件,上部分是选择按钮,下部分是操作提示

    功能:点击按钮后相应事件有所相应,按钮响应鼠标点击事件

    具体HTML代码:

    <template>
      <div id="app">
        <div id="cesiumContainer"></div>
        <div class="btnContainer">
          <button @click="draw('Point')">绘制点</button>
          <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>
            <p>剩下的我慢慢写</p>
          </div>
        </div>
      </div>
    </template>
    

    具体CSS样式:

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

    3. 实现功能

    • 数据初始化
    <script>
        ...,
        export default {
            name: 'xxx',
            data () {
                return {
                    viewer: undefined,
                    tempEntities: []
                }
            }
        }
    </script>
    
    • 实现根据指定类型绘制对象
    /* 根据类型绘制对象
        * @param type point polyline polygon */
        draw (type) {
          let that = this
          let viewer = this.viewer
          let tempEntities = this.tempEntities
          let position = []
          let tempPoints = []
          // 开启深度检测
          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)
                tempPoints.push(position) // 记录点位
                let tempLength = tempPoints.length // 记录点数
                // 调用绘制点的接口
                let point = that.drawPoint(tempPoints[tempPoints.length - 1])
                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) {
                tempPoints = [] // 清空点位记录
                handler.destroy()
                handler = null
              }, Cesium.ScreenSpaceEventType.RIGHT_CLICK)
              break
            // 绘制面
            case 'Polygon':
              // 监听鼠标移动
              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)
                tempPoints.push(position) // 记录点位
                let tempLength = tempPoints.length // 记录点数
                // 调用绘制点的接口
                let point = that.drawPoint(tempPoints[tempPoints.length - 1])
                tempEntities.push(point)
                // 存在超过一个点时
                if (tempLength > 1) {
                  // 绘制线
                  let pointline = that.drawPolyline([tempPoints[tempPoints.length - 2], tempPoints[tempPoints.length - 1]])
                  tempEntities.push(pointline) // 保存记录
                  that.drawPolygon(tempPoints)
                }
              }, 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[tempPoints.length - 2], tempPoints[tempPoints.length - 1]])
                    tempEntities.push(pointline)
                    that.drawPolygon(tempPoints)
                    tempEntities.push(tempPoints)
                    handler.destroy()
                    handler = null
                  }
                }
              }, Cesium.ScreenSpaceEventType.RIGHT_CLICK)
              break
          }
        }
    
    • 具体的绘制方法(本质上就是添加实体
    // 绘制函数
        drawPoint (position) {
          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 // 规定贴地
            }
          })
        },
        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.GOLD
              }),
              depthFailmaterial: new Cesium.PolylineGlowMaterialProperty({
                // eslint-disable-next-line new-cap
                color: Cesium.Color.GOLD
              }),
              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.WHITE.withAlpha(0.4)
              )
            }
          })
        }
    
    • 清除绘制

    思路是把数据都还原成初始值,把实体都注销

    /* 清除实体 */
        clearAllDrawn () {
          let viewer = this.viewer
          this.tempEntities = []
          viewer.entities.removeAll()
        }
    

    4. 完整核心代码

    <script>
    import * as Cesium from 'cesium/Cesium'
    import * as widgets from 'cesium/Widgets/widgets.css'
    
    export default {
      name: 'App',
      data () {
        return {
          viewer: undefined,
          tempEntities: []
        }
      },
      mounted () {
        this.Init()
      },
      methods: {
        // 初始化
        Init () {
          // 引入个人token
          Cesium.Ion.defaultAccessToken = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiJkYmJkNWQ3Mi0zOGVkLTQ5N2YtYTBmMy0wMDAyODZiMDMyZWYiLCJpZCI6ODQ2NzQsImlhdCI6MTY0NjQ0NTYxNX0.XkHX3rdysM4uUe5VTKDVEV3W2An33zyh4qAkFUac2fk'
          // 设置取景器
          this.viewer = new Cesium.Viewer('cesiumContainer', {
            terrainProvider: Cesium.createWorldTerrain(),
            selectionIndicator: false, // 不显示指示器小部件
            infoBox: false, //  不显示信息框
            sceneModePicker: false, // 不显示模式切换选项
            baseLayerPicker: false,
            navigationHelpButton: false,
            animation: false,
            shouldAnimate: false,
            timeline: false,
            geocoder: false,
            homeButton: false
          })
          // 若浏览器不支持pickPosition,显示报错信息
          if (!this.viewer.scene.pickPositionSupported) {
            window.alert('This browser does not support pickPosition.')
          }
          // 载入OSM建筑物
          // const osmBuildings = this.viewer.scene.primitives.add(Cesium.createOsmBuildings()) // eslint-disable-line no-unused-vars
          // 初始化镜头
          this.viewer.camera.lookAt(
            Cesium.Cartesian3.fromDegrees(-122.2058, 46.1955, 1000.0),
            new Cesium.Cartesian3(5000.0, 5000.0, 5000.0)
          )
          this.viewer.camera.lookAtTransform(Cesium.Matrix4.IDENTITY)
        },
        // 绘制函数
        drawPoint (position) {
          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 // 规定贴地
            }
          })
        },
        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.GOLD
              }),
              depthFailmaterial: new Cesium.PolylineGlowMaterialProperty({
                // eslint-disable-next-line new-cap
                color: Cesium.Color.GOLD
              }),
              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.WHITE.withAlpha(0.4)
              )
            }
          })
        },
        /* 清除实体 */
        clearAllDrawn () {
          let viewer = this.viewer
          this.tempEntities = []
          viewer.entities.removeAll()
        },
        created () {
        },
        /* 根据类型绘制对象
        * @param type point polyline polygon */
        draw (type) {
          let that = this
          let viewer = this.viewer
          let tempEntities = this.tempEntities
          let position = []
          let tempPoints = []
          // 开启深度检测
          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)
                tempPoints.push(position) // 记录点位
                let tempLength = tempPoints.length // 记录点数
                // 调用绘制点的接口
                let point = that.drawPoint(tempPoints[tempPoints.length - 1])
                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) {
                tempPoints = [] // 清空点位记录
                handler.destroy()
                handler = null
              }, Cesium.ScreenSpaceEventType.RIGHT_CLICK)
              break
            // 绘制面
            case 'Polygon':
              // 监听鼠标移动
              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)
                tempPoints.push(position) // 记录点位
                let tempLength = tempPoints.length // 记录点数
                // 调用绘制点的接口
                let point = that.drawPoint(tempPoints[tempPoints.length - 1])
                tempEntities.push(point)
                // 存在超过一个点时
                if (tempLength > 1) {
                  // 绘制线
                  let pointline = that.drawPolyline([tempPoints[tempPoints.length - 2], tempPoints[tempPoints.length - 1]])
                  tempEntities.push(pointline) // 保存记录
                  that.drawPolygon(tempPoints)
                }
              }, 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[tempPoints.length - 2], tempPoints[tempPoints.length - 1]])
                    tempEntities.push(pointline)
                    that.drawPolygon(tempPoints)
                    tempEntities.push(tempPoints)
                    handler.destroy()
                    handler = null
                  }
                }
              }, Cesium.ScreenSpaceEventType.RIGHT_CLICK)
              break
          }
        }
      }
    }
    </script>
    

    三、 问题

    • 问题:提示Cannot read property 'draw' of undefined

      应对:这里是this指向出了问题,出这个问题的时候没有先在draw (type)方法里let that = this然后再在caselet point = that.drawPoint(position),而是直接this.drawPoint(position)了,所以要加上let that = this

    相关文章

      网友评论

          本文标题:个人笔记|vue+Cesium绘制点线面

          本文链接:https://www.haomeiwen.com/subject/hbafdrtx.html