坐标测量
import * as Cesium from 'cesium'
import { useCesiumStore } from '@/stores/useCesiumStore.js'
import { toRaw } from 'vue'
export const MeasurePoint = () =>{
const viewer = toRaw(useCesiumStore().viewer) //获取viewer对象
const handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas) //创建屏幕空间事件处理器
const annotations = viewer.scene.primitives.add(new Cesium.LabelCollection()) //创建标注集合
viewer.scene.canvas.style.cursor = 'crosshair'
//添加点
const createPoint = (worldPosition) => {
const point = viewer.entities.add({
position: worldPosition,
name: 'measure_point',
point: {
pixelSize: 10,
color:Cesium.Color.CRIMSON,
outlineColor: Cesium.Color.ALICEBLUE,
outlineWidth: 2,
disableDepthTestDistance: 4000
}
})
return point
}
//添加点和标注框
const annotate = (cartesian,lng,lat,height) => {
//创建点
createPoint(cartesian)
//创建标注框
annotations.add({
position: cartesian,
text:
'Lon:' + lng.toFixed(5) + '\u00B0' +
'\nLat:' + lat.toFixed(5) + '\u00B0' +
'\nHeight:' + height.toFixed(2) +'m',
showBackground: true,
font: '22px monospace',
horizontalOrigin: Cesium.HorizontalOrigin.LEFT,
verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
disableDepthTestDistance:Number.POSITIVE_INFINITY
})
}
//鼠标左键单击事件
handler.setInputAction((event) => {
const pickedObject = viewer.scene.pick(event.position) //判断是否拾取到模型
//如果拾取到模型
if (viewer.scene.pickPositionSupported && Cesium.defined(pickedObject)){
const cartesian = viewer.scene.pickPosition(event.position) //获取点击位置的笛卡尔坐标
if(Cesium.defined(cartesian)){
const cartographic = Cesium.Cartographic.fromCartesian(cartesian) //将笛卡尔坐标转为经纬度高度
const lng = Cesium.Math.toDegrees(cartographic.longitude) //获取经度
const lat = Cesium.Math.toDegrees(cartographic.latitude) //获取纬度
const height = cartographic.height //获取高度
annotate(cartesian,lng,lat,height) //添加点和标注框
}
}
//如果未拾取到模型而拾取到地形
else{
const ray = viewer.camera.getPickRay(event.position) //获取射线
const cartesian = viewer.scene.globe.pick(ray, viewer.scene) //获取地形的笛卡尔坐标
if(Cesium.defined(cartesian)){
const cartographic = Cesium.Cartographic.fromCartesian(cartesian) //将笛卡尔坐标转为经纬度高度
const lng = Cesium.Math.toDegrees(cartographic.longitude) //获取经度
const lat = Cesium.Math.toDegrees(cartographic.latitude) //获取纬度
const height = cartographic.height //获取高度
annotate(cartesian,lng,lat,height) //添加点和标注框
}
}
}, Cesium.ScreenSpaceEventType.LEFT_CLICK)
//使用鼠标右键单击,删除点和标注框
handler.setInputAction(() => {
// viewer.entities.removeAll()
for (let i = 0; i < viewer.entities.values.length; i++) {
if (viewer.entities.values[i].name === 'measure_point' ) {
viewer.entities.remove(viewer.entities.values[i])
i-- //删除后索引减一,数组塌陷
}
}
annotations.removeAll()
viewer.scene.canvas.style.cursor = 'default'
handler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_CLICK)
}, Cesium.ScreenSpaceEventType.RIGHT_CLICK)
}
空间距离测量,不是贴地距离。
import * as Cesium from 'cesium'
import { useCesiumStore } from '@/stores/useCesiumStore.js'
import { toRaw } from 'vue'
export const MeasureDistance = () => {
const viewer = toRaw(useCesiumStore().viewer) //获取viewer对象
const handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas) //创建屏幕空间事件处理器
viewer.scene.canvas.style.cursor = 'crosshair'
//计算空间距离函数
const getSpaceDistance = (position) => {
let distance = 0
for (let i = 0; i < position.length - 1; i++){
distance += Cesium.Cartesian3.distance(position[i], position[i+1]) //计算两点之间的距离
}
return distance.toFixed(2) //保留两位小数
}
let positions = [] //记录鼠标点击的位置
let activeShapePoints = [] //记录鼠标点击的位置
let activeShape = null //动态图形
let floatingPoint = null //第一个点,判断是否开始获取鼠标移动位置并添加至activeShapePoints
//绘制点与标签
const drawPoint = (position,textDitance) => {
const pointGeometry = viewer.entities.add({
name:'distancePoint',
position: position,
point: {
color:Cesium.Color.SKYBLUE,
pixelSize:6,
outlineColor:Cesium.Color.RED,
outlineWidth:2,
disableDepthTestDistance:4000
},
label: {
text: textDitance + '米',
font:'18px sans-serif',
fillColor:Cesium.Color.GOLD,
style : Cesium.LabelStyle.FILL_AND_OUTLINE,
outlineWidth:2, //外边框宽度
verticalOrigin: Cesium.VerticalOrigin.BOTTOM, //垂直方向居下
pixelOffset: new Cesium.Cartesian2(20, -20), //偏移量
heightReference: Cesium.HeightReference.NONE, //高度参考
disableDepthTestDistance: Number.POSITIVE_INFINITY
}
})
return pointGeometry
}
//绘制图形
const drawShape = (positionData) => {
const shape = viewer.entities.add({
name:'distanceShape',
polyline:{
positions:positionData,
width:5,
material:new Cesium.PolylineOutlineMaterialProperty({
color:Cesium.Color.RED,
}),
//贴地
// clampToGround: true,
// disableDepthTestDistance: Number.POSITIVE_INFINITY,
}
})
return shape
}
//鼠标左键单击事件
handler.setInputAction((event) => {
const earthPosition = viewer.scene.pickPosition(event.position) //获取点击位置的经纬度坐标
//如果鼠标指针不在地球上,则earthPosition为undefined
if(Cesium.defined(earthPosition)){
//计算距离
positions.push(earthPosition) //记录点击位置
let disance = getSpaceDistance(positions) //计算距离
if (activeShapePoints.length === 0) {
activeShapePoints.push(earthPosition) //记录第一个点
const dynamicPosition = new Cesium.CallbackProperty(() => {
return activeShapePoints
}, false) //动态位置
activeShape = drawShape(dynamicPosition) //绘制动态图形
}
//添加当前点到activeShapePoints,实时渲染动态图
activeShapePoints.push(earthPosition)
floatingPoint = drawPoint(earthPosition,disance) //绘制点与标签
}else{
window.alert('请点击地球上的位置')
return
}
},Cesium.ScreenSpaceEventType.LEFT_CLICK)
//鼠标移动事件
handler.setInputAction((event) => {
if(Cesium.defined(floatingPoint)){
let newPosition = viewer.scene.pickPosition(event.endPosition) //获取鼠标移动位置的经纬度坐标
if(Cesium.defined(newPosition)){
activeShapePoints.pop() //删除最后一个点
activeShapePoints.push(newPosition) //添加新的点
}
}
},Cesium.ScreenSpaceEventType.MOUSE_MOVE)
//鼠标右键单击事件
handler.setInputAction(() => {
activeShapePoints.pop() //删除最后一个点
if(activeShapePoints.length) {
drawShape(activeShapePoints) //绘制最终图形
}
viewer.entities.remove(activeShape) //去除动态图形
floatingPoint = undefined
activeShape = undefined
activeShapePoints = []
positions = []
viewer.scene.canvas.style.cursor = 'default'
handler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_CLICK)
},Cesium.ScreenSpaceEventType.RIGHT_CLICK)
}
面积测量
import * as Cesium from 'cesium'
import { useCesiumStore } from '@/stores/useCesiumStore.js'
import { toRaw } from 'vue'
export const MeasureArea = () => {
const viewer = toRaw(useCesiumStore().viewer) //获取viewer对象
const handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas) //创建屏幕空间事件处理器
viewer.scene.canvas.style.cursor = 'crosshair'
let p = [] //存储要计算面积的点
let pCartographic = [] //存储计算距离与添加label的点
let activeShapePoints = [] //存储当前正在绘制的多边形顶点坐标
let activeShape = null //当前正在绘制的多边形
let floatingPoint = null //作为第一个点,用于判断是否开始获取鼠标移动结束位置
//定义变量,进行角度和弧度之间的转换;弧度=pi/180*度数,角度=弧度*180/pi
const radiansPerDegree = Math.PI / 180.0 //角度转弧度
const degreesPerRadian = 180.0 / Math.PI //弧度转角度
//计算两点朝向,从A点到B点相对于平面X轴的平面角度的函数getBearing
//from:起点
//to:终点
const getBearing = (from, to) => {
from = Cesium.Cartographic.fromCartesian(from) //将Cartesian3坐标转换为Cartographic坐标
to = Cesium.Cartographic.fromCartesian(to)
const lat1 = from.latitude * radiansPerDegree //起点纬度弧度
const lon1 = from.longitude * radiansPerDegree //起点经度弧度
const lat2 = to.latitude * radiansPerDegree //终点纬度弧度
const lon2 = to.longitude * radiansPerDegree //终点经度弧度
//返回从原点(0,0)到(x,y)点的线段与x轴正方向夹角的弧度值,转为角度
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 += 2 * Math.PI
}
return angle * degreesPerRadian //返回角度值
}
//计算3个点直接连线的角度的函数getAngle
/* p1,p2,p3:三点坐标 ,分别计算相邻两点的朝向,并根据两个线段朝向计算,得到3个点连城的夹角*/
const getAngle = (p1, p2, p3) => {
const bearing1 = getBearing(p2, p1) //相邻两点的朝向
const bearing2 = getBearing(p2, p3)
let angle = bearing2 - bearing1 //两个线段的夹角
if (angle < 0) {
angle += 360
}
return angle
}
//封装计算相邻两点距离函数getDistance,根据相邻两点之间经纬度、高度计算两点之间的空间距离
const getDistance = (p1, p2) => {
const geodesic = new Cesium.EllipsoidGeodesic() //创建椭球体几何对象
geodesic.setEndPoints(p1, p2) //设置起点和终点
//获取起点和终点之间的表面距离
let s = geodesic.surfaceDistance
//获取起点和终点之间的表面距离的平方
s = Math.sqrt(Math.pow(s, 2) + Math.pow(p1.height - p2.height, 2))
return s //返回距离值
}
//计算多边形面积的函数getArea
/* points:多边形顶点坐标数组
将多边形拆分成三角曲面,通过计算每3个点组成的线段的夹角及两条边的长度分别计算三角形的面积,然后将所有三角形面积相加*/
const getArea = (points) => {
let res = 0
for (let i = 0; i < points.length - 2; i++) {
const j = (i+1) % points.length //相邻点的第二个点
const k = (i+2) % points.length //相邻点的第三个点
const totalAngle = getAngle(points[i], points[j], points[k]).toFixed() //计算相邻两点的夹角
const distance1 = getDistance(pCartographic[i], pCartographic[j]) //计算两点之间的距离
const distance2 = getDistance(pCartographic[j], pCartographic[k])
// console.log(j,k,distance1,distance2,totalAngle);
//计算三角形面积
res += distance1 * distance2 * Math.abs(Math.round(Math.sin(totalAngle * radiansPerDegree ) * 1000000) / 1000000)
// console.log(res)
}
return res.toFixed(2) //返回面积值,单位是平方米
}
//封装addLabel,用于完成面积计算后显示结果,绘制到最后一个点上
const addLabel = (pCartographic, text) => {
const position = Cesium.Cartesian3.fromRadians(
pCartographic[pCartographic.length - 1].longitude,
pCartographic[pCartographic.length - 1].latitude,
pCartographic[pCartographic.length - 1].height
)
const label = viewer.entities.add({
name: 'areaLabel',
position: position,
label: {
text: text + '平方米',
font: '18px sans-serif',
fillColor: Cesium.Color.GOLD,
style:Cesium.LabelStyle.FILL_AND_OUTLINE,
outlineWidth: 2,
verticalOrigin: Cesium.VerticalOrigin.BOTTOM, //垂直方向上对齐
pixelOffset: new Cesium.Cartesian2(20, -20), //偏移量
heightReference: Cesium.HeightReference.NONE, //高度参考
disableDepthTestDistance: Number.POSITIVE_INFINITY
}
})
return label
}
//绘制点
const drawPoint = (position) => {
const pointGeometry = viewer.entities.add({
name: 'areaPoint',
position: position,
point: {
pixelSize: 6,
Color: Cesium.Color.SKYBLUE,
outlineColor: Cesium.Color.YELLOW,
outlineWidth: 2,
disableDepthTestDistance: 4000 //距离4000以下不被遮挡
}
})
return pointGeometry
}
//绘制多边形
const drawShape = (positionData) => {
const shape = viewer.entities.add({
name: 'areaShape',
polygon: {
hierarchy: positionData,
material: new Cesium.ColorMaterialProperty(Cesium.Color.BLUE.withAlpha(0.5))
}
})
return shape
}
//鼠标左键单击事件
handler.setInputAction((event) => {
const earthPosition = viewer.scene.pickPosition(event.position) //获取点击位置的地理坐标
if (Cesium.defined(earthPosition)) {
pCartographic.push(Cesium.Cartographic.fromCartesian(earthPosition)) //将点击位置的地理坐标添加到pCartographic数组
p.push(earthPosition) //将点击位置的地理坐标添加到p数组
//第一次单击时
if (activeShapePoints.length === 0) {
floatingPoint = drawPoint(earthPosition) //绘制第一个点
activeShapePoints.push(earthPosition) //将第一个点添加到activeShapePoints数组
const dynamicPositions = new Cesium.CallbackProperty(() => {
return new Cesium.PolygonHierarchy(activeShapePoints)
}, false)
activeShape = drawShape(dynamicPositions) //绘制第一个多边形
}
//添加当前点到activeShapePoints数组,实时渲染动态图
activeShapePoints.push(earthPosition)
drawPoint(earthPosition)
}
else {
alert('请点击地球上的位置')
return
}
}, Cesium.ScreenSpaceEventType.LEFT_CLICK)
//鼠标移动事件
handler.setInputAction((event) => {
if(Cesium.defined(floatingPoint)) {
const newPosition = viewer.scene.pickPosition(event.endPosition) //获取鼠标移动结束位置的地理坐标
if(Cesium.defined(newPosition)){
activeShapePoints.pop() //删除最后一个点
activeShapePoints.push(newPosition) //添加新的点
}
}
},Cesium.ScreenSpaceEventType.MOUSE_MOVE)
//鼠标右键单击事件
handler.setInputAction(() => {
activeShapePoints.pop() //删除最后一个点
if (activeShapePoints.length ) {
drawShape(activeShapePoints)
}
const text = getArea(p)
addLabel(pCartographic, text) //计算面积并显示结果
viewer.entities.remove(activeShape) //删除多边形
viewer.entities.remove(floatingPoint) //删除第一个点
floatingPoint = undefined //清空变量
activeShape = undefined //清空变量
activeShapePoints = [] //清空数组
p = [] //清空数组
pCartographic = [] //清空数组
viewer.scene.canvas.style.cursor = 'default'
// handler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_CLICK)
handler.destroy() //销毁事件处理器
},Cesium.ScreenSpaceEventType.RIGHT_CLICK)
}