个人笔记|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
然后再在case
里let point = that.drawPoint(position)
,而是直接this.drawPoint(position)
了,所以要加上let that = this