Vue+Cesium从入门到放弃-:动态绘图(点、线、多边形),可编辑

前言

博主又开新系列啦!!!毕业前学了3个月的postgresql+geoserver+leaflet准备工作后大展拳脚,事实证明我还是太年轻啊,因为我学的一个也没用到,因为公司项目以三维为主,于是我又开始了Vue+Cesium从入门到放弃之路,相比二维,三维平台还不是很成熟,很多在二维下很容易实现的功能在三维下可以需要从头写起,话不多说,先上图。
PS:以下功能都是Cesium结合Vue实现的,如果不懂Vue语法可以在理解起来有点困难。
在这里插入图片描述

在这里插入图片描述

添加标记(marker)

功能:添加一个注记,允许用户输入注记信息。
添加标记应该是GIS常用的一个功能,但是Cesium并没有很好的实现这个功能,Cesium有一个Entry类,可以用来在地图上创建一个实体,包括后面的点线面都要用这个类实现(当然也有其它办法),这个的参数有billboard,point、polyline、polygon、label,分别用来创建广告牌(Cesium中的叫法,就是用图片在图上添加一个标记)、点、线、面、文字。
思路:Cesium有添加一个图片(注记)的功能(billboard),也有添加文字的功能(label),但并没有给注记添加文字信息的功能,所以要实现这个功能就要把billboard和labe合成一个组件。具体:

  • 监测鼠标单击事件,捕获坐坐标
  • 添加注记,并弹出信息输入框
  • 确定输入,显示注记信息

实现过程:marker.vue
1.设计模板,需要一个文本框和一个按钮

<template>
  <div id="markerContainer">
    <!--<input id="msg" v-show="isShow" type="text" :style="{left:scenePos.x+'px',top:scenePos.y+'px'}"-->
    <!--placeholder="请输入内容"-->
    <!--v-model="msg"/>-->
    <!--<input type="button" id='submit' v-show="isShow" @click="add"  :style="{left:scenePos.x+150+'px',top:scenePos.y+2+'px'}" value="ok"/>-->


    <div id="msg1" :style="{left:scenePos.x+30+'px',top:scenePos.y+'px'}" v-show="isShow">
      <input id="msg" type="text"  placeholder="标记名称" @keyup.enter="update" v-model="msg" ref="msg_input" v-focus class="form-control"/>
    </div>
    <div id='submit1' :style="{left:scenePos.x+180+'px',top:scenePos.y+'px'}" v-show="isShow">
      <input type="button"  name="nameSave" class="btn btn-primary" id='submit' @click="update"  value="ok"  />
    </div>
    <div id="menu" v-show="menuShow" oncontextmenu="return false">
      <div><a href="#" @click="edit">编 辑</a></div>
      <div><a href="#" @click="drop">删 除</a></div>
    </div>
    <div id="tooltip" :style="{left:tipPos.x-100+'px',top:tipPos.y-90+'px'}" v-show="tipShow">
      <span>名称:{{curText}}</span><br/>
      <span>纬度:{{curLat}}</span><br/>
      <span>经度:{{curLon}}</span><br/>
      <div class="arrow"></div>
    </div>

  </div>
</template>
    <!--scenePos是捕获的屏幕坐标,:style="{left:scenePos.x+30+'px',top:scenePos.y+'px'}"是Vue语法,设置文本框在鼠标点击的位置-->
    <!--@keyup.enter="add" Vue语法,绑定click事件,添加文字到地图-->
    <!--其它语法我就不解释了,都是Vue基本语法-->

2.功能实现

  import Cesium from 'cesium/Cesium'
  import Bus from '@/js/Bus'
  export default {
    data() {
      return {
        msg:'',
        tipPos:{x:0,y:0},
        viewer:'',
        scenePos:'',
        isShow:false,
        billboards:'',
        labels:'',
        selectedObj:'',
        menuShow:false,
        tipShow:true,
        curIndex:null,
        isDrawing:true,
        curLat:0,
        curLon:0,
        curText:''
      }
    },
    computed:{
      handler(){
        return new Cesium.ScreenSpaceEventHandler(this.viewer.canvas)
      }
    },
    directives:{
      focus(el){

        el.focus()
      }

    },
    mounted(){
      const _this=this
      _this.viewer=new Cesium.Viewer('cesiumContainer')
      Bus.$on('drawingStop',_this.stopDrawing)
      Bus.$on('markerViewerInit',_this.init)
      Bus.$on('markerViewerDestroy',_this.destroy)
    },
    methods: {
      init(){
        const _this=this
        // _this.isShow=true
        _this.handler.setInputAction(evt=>{
          // _this.menuShow=false
          const e = window.event,//event|| window.event,
            target = e.target || e.srcElement;
          if(!_this.isDrawing){
            return
          }
          //如果点击的对象是地球,添加一个marker
          if(target&&target.tagName=='CANVAS'&&target.parentElement.className=='cesium-widget'){
            _this.scenePos=evt.position
            _this.add()
            _this.msg=''
            // _this.$refs.msg_input.focus()
          }else {

          }

        },Cesium.ScreenSpaceEventType.LEFT_CLICK)
        //将billboard和label分别存储在数组中
        _this.billboards=_this.viewer.scene.primitives.add(new Cesium.BillboardCollection())
        _this.labels=_this.viewer.scene.primitives.add(new Cesium.LabelCollection())
      },
      add() {
        const _this=this
        _this.isShow=true
        _this.tipShow=false
        _this.billboards.add({
          id:'marker%d' % _this.billboards.length,
          position: _this.viewer.scene.globe.pick(_this.viewer.camera.getPickRay(_this.scenePos),_this.viewer.scene),
          image:'static/images/pin.png'//marker的图片,可以任意替换
        })
        _this.labels.add({
          position:_this.viewer.scene.globe.pick(_this.viewer.camera.getPickRay(_this.scenePos),_this.viewer.scene),
          id:'marker_label%d' % _this.billboards.length,
          text:'',
          font: '24px sans-serif',
          fillColor: Cesium.Color.BLACK,
          outlineWidth: 5,
          outlineColor: Cesium.Color.WHITE,
          pixelOffset: new Cesium.Cartesian2(20, 20),
          style: Cesium.LabelStyle.FILL_AND_OUTLINE
        })

      },
      update:function(){
        if(this.curIndex==undefined){
          this.curIndex=this.labels.length-1
        }
        this.labels.get(this.curIndex).text=this.msg
        this.isShow=false
        this.msg=''
        this.curIndex=undefined
      },
      destroy(){
        this.handler.destroy()
        this.labels.destroy()
        this.billboards.destroy()

      }
    }
  }
</script>

3.样式

<style>
<!--设计你自己想要的样式-->
</style>

4.调用
在你的主页面引入刚才写的marker组件

<template>
<markerViewer></markerViewer>
</template>
<script>
import markerViewer from 'marker'
import Bus from '@/js/Bus'
export default {
    data() {
      return {
        
      }
    },
    components:{markerViewer},//引用自己写的组件
    methods: {},
    mounted() {
      Bus.$emit('markerViewerInit')//初始化
    }
</script>

5.Bus.js

import Vue from 'vue'
export default new Vue()

6.总结
到这里添加一个标记的功能就基本完成了,图片中的单击显示标记信息和右键菜单的代码我并没有贴出来,因为代码量太大影响阅读,对此功能有需求的可以联系博主。
时间有限,今天就写到这里吧,下次再写绘点线面。。。。。

=================== 4.16更新分割线=================

动态画线、面

今天我打算写简单一点,时间有限,直接上代码

  const viewer = new Cesium.Viewer('cesiumContainer');
  const handler=new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas)
  let drawMode='line'
  let activePoints=[]
  let floatPoint,dynamicShape
  handler.setInputAction(function (e) {
    const wp=viewer.scene.globe.pick(viewer.camera.getPickRay(e.position),viewer.scene)
    if(Cesium.defined(wp)){
      activePoints.push(wp)
      //下面这个if中的代码用于创建一个临时多边形(线)多边形的开关跟随鼠标的位置变化,
      if(!Cesium.defined(floatPoint)){
        floatPoint=new Cesium.CallbackProperty(function () {
          return activePoints
        })
        activePoints.push(wp)
        dynamicShape=drawShape(floatPoint)
      }
    }

  },Cesium.ScreenSpaceEventType.LEFT_CLICK)
  handler.setInputAction(function (e) {
    const wp=viewer.scene.globe.pick(viewer.camera.getPickRay(e.endPosition),viewer.scene)
    if(Cesium.defined(wp)){
      if(activePoints.length>1){
        activePoints.pop()
        activePoints.push(wp)
      }
    }
  },Cesium.ScreenSpaceEventType.MOUSE_MOVE)
  handler.setInputAction(function (e) {
    drawShape(activePoints)
    activePoints=[]
    floatPoint=null
  },Cesium.ScreenSpaceEventType.RIGHT_CLICK)
  function drawShape(position) {
    console.log(position)
    const entity=drawMode=='line'?
      viewer.entities.add({
        polyline:{
          positions:position,
          width:3,
          material: new Cesium.ColorMaterialProperty(Cesium.Color.RED.withAlpha(0.8)),
          clampToGround: true

        }
      }):
      viewer.entities.add({
        polygon:{
          hierarchy:position,
          material: new Cesium.ColorMaterialProperty(Cesium.Color.RED.withAlpha(0.3)),
          //material: new Cesium.ColorMaterialProperty(new Cesium.Color(205, 139, 14, 1)),
          outline: true,
          outlineColor: Cesium.Color.BLACK,
          outlineWidth:3
        }
      })
    return entity
    //viewer.entities.add(entity)
  }

以上。有问题请留言。
-------------------------------我又来更新啦------------------------------
最近把这个功能完善了一下,增加了编辑和删除功能,下面是效果图
在这里插入图片描述
在这里插入图片描述
修改后的代码我上传到了github,地址https://github.com/xtfge/vue-cesium-draw,有兴趣的请自己下载。

2020.3.3更新
Github被墙了,更新码云地址

Logo

前往低代码交流专区

更多推荐