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

  • 编辑器: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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容