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>