import { Cesium } from '../../namespace'
import { LatLon } from './ThirdPart'
import {
getCatesian3FromPX,
getPointByAngleDistance,
getPolygonCentroid,
getRegionExtreme,
interPoints,
radiansToDegrees,
subTriangle,
transformCartesianArrayToWGS84Array,
transformCartesianToWGS84,
transformWGS84ToCartographic,
} from './Coordinate'
import CreateRemindertip from './ReminderTip'
import { destroyHandler } from './Handler'
import CreatePolygon from '../core/creator/polygon/CreatePolygon'
/**
* 空间量测相关方法集
* @module Measurement
*/
/***
* 距离测量
* @param viewer
* @param type - line:空间,project:投影,onground:地表,elliptic:椭球
* @param parent
* @param handler
* @param callback
*/
export const distanceMeasure = (viewer, type, parent, handler, callback) => {
let positions = []
let processObj = []
let distance = 0
let sdkToolTip = '左键点击开始量测'
// 左键单击
handler.setInputAction((e) => {
sdkToolTip = '左键单击添加点,左键双击结束,右键单击回撤'
let cartesian = getCatesian3FromPX(viewer, e.position)
if (cartesian) {
if (positions.length == 0) {
positions.push(cartesian.clone())
viewer.entities.add({
parent: parent,
polyline: {
positions: new Cesium.CallbackProperty(function () {
return positions
}, false),
width: 5,
material: Cesium.Color.BLUE.withAlpha(0.8),
clampToGround: type != 'line',
},
})
}
positions.push(cartesian)
// 添加量测信息点
updateProcess(cartesian, positions[positions.length - 3])
}
}, Cesium.ScreenSpaceEventType.LEFT_CLICK)
// 鼠标移动
handler.setInputAction((e) => {
let endPos = e.endPosition
CreateRemindertip(sdkToolTip, endPos, true)
if (positions.length === 0) return
let cartesian = getCatesian3FromPX(viewer, endPos)
if (cartesian && positions.length >= 2) {
positions.pop()
positions.push(cartesian)
}
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE)
// 左键双击
handler.setInputAction(() => {
positions.pop()
if (positions.length < 2) return
removeProcess()
destroyHandler(handler)
CreateRemindertip(sdkToolTip, null, false)
callback && typeof callback === 'function' && callback(distance)
}, Cesium.ScreenSpaceEventType.LEFT_DOUBLE_CLICK)
// 右键点击
handler.setInputAction(() => {
positions.length > 2 && removeProcess()
}, Cesium.ScreenSpaceEventType.RIGHT_CLICK)
function removeProcess() {
positions.pop()
viewer.entities.remove(processObj[processObj.length - 1])
processObj.pop()
}
function calculateDis(PosArr) {
let points = []
switch (type) {
case 'line':
points = transformCartesianArrayToWGS84Array(PosArr)
break
case 'onground':
case 'project':
case 'elliptic':
points = interPoints(viewer, type, PosArr, [])
break
default:
break
}
return type === 'elliptic'
? getGeodesyDistance(points)
: getPositionDistance(points)
}
function updateProcess(position, prePos) {
let _distance = prePos ? calculateDis([prePos, position]) : 0
distance += _distance
const text =
distance > 1000
? `${(distance / 1000).toFixed(4)}公里`
: `${distance.toFixed(4)}米`
const entity = viewer.entities.add({
parent: parent,
position: position,
point: {
pixelSize: 10,
outlineColor: Cesium.Color.BLUE,
outlineWidth: 5,
},
label: {
text: text,
show: true,
showBackground: true,
backgroundColor: new Cesium.Color(0, 0, 0, 1.0),
font: '14px monospace',
horizontalOrigin: Cesium.HorizontalOrigin.CENTER,
verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
pixelOffset: new Cesium.Cartesian2(0, -25),
},
})
processObj.push(entity)
}
}
/***
* 面积测量
* @param viewer
* @param type - project:投影,onground:地表,elliptic:椭球
* @param parent
* @param handler
* @param callback
*/
export const areaMeasure = (viewer, type, parent, handler, callback) => {
let anchorpoints = []
let polygon = undefined
let sdkToolTip = '左键点击开始绘制'
// 左键点击事件
handler.setInputAction((event) => {
let pixPos = event.position
let cartesian = getCatesian3FromPX(viewer, pixPos)
if (!cartesian) return
if (anchorpoints.length == 0) {
sdkToolTip = '左键添加第二个顶点'
anchorpoints.push(cartesian)
let dynamicPositions = new Cesium.CallbackProperty(function () {
return new Cesium.PolygonHierarchy(anchorpoints)
}, false)
//设置颗粒度(来源于大神的思路)
let granularity = Math.PI / Math.pow(2, 11)
granularity = granularity / 64
polygon = viewer.entities.add({
name: 'Polygon',
parent: parent,
polygon: {
hierarchy: dynamicPositions,
material: Cesium.Color.BLUE.withAlpha(0.6),
heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
},
polyline: {
positions: new Cesium.CallbackProperty(function () {
const linePos = anchorpoints.concat(anchorpoints[0])
return linePos
}, false),
width: 5,
material: Cesium.Color.GREEN,
clampToGround: true,
},
})
} else {
sdkToolTip = '左键添加点,右键撤销,左键双击完成绘制'
}
anchorpoints.push(cartesian)
}, Cesium.ScreenSpaceEventType.LEFT_CLICK)
// 鼠标移动事件
handler.setInputAction((movement) => {
let endPos = movement.endPosition
CreateRemindertip(sdkToolTip, endPos, true)
if (Cesium.defined(polygon)) {
anchorpoints.pop()
let cartesian = getCatesian3FromPX(viewer, endPos)
if (!cartesian) {
return
}
anchorpoints.push(cartesian)
}
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE)
// 左键双击事件
handler.setInputAction((event) => {
anchorpoints.pop()
anchorpoints.pop() //因为是双击结束,所以要pop两次,一次是move的结果,一次是单击结果
destroyHandler(handler)
CreateRemindertip(sdkToolTip, event.position, false)
let areaNum =
type === 'project'
? getArea(anchorpoints)
: getSurfaceArea(anchorpoints, type)
const text =
areaNum > 1000000
? `${(areaNum / 1000000).toFixed(4)} 平方公里`
: `${areaNum.toFixed(4)}平方米`
const pos = getPolygonCentroid(anchorpoints)
polygon.position = Cesium.Cartesian3.fromDegrees(pos[0], pos[1])
polygon.label = {
text: text,
font: '24px Helvetica',
showBackground: true,
fillColor: Cesium.Color.WHITE,
outlineColor: Cesium.Color.BLACK,
outlineWidth: 2,
style: Cesium.LabelStyle.FILL_AND_OUTLINE,
horizontalOrigin: Cesium.HorizontalOrigin.CENTER,
verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
disableDepthTestDistance: 10000000,
scaleByDistance: new Cesium.NearFarScalar(1.0e2, 1.0, 2.0e5, 0.3),
}
if (typeof callback == 'function') callback(areaNum)
}, Cesium.ScreenSpaceEventType.LEFT_DOUBLE_CLICK)
// 右键摁下事件
handler.setInputAction(() => {
anchorpoints.pop()
}, Cesium.ScreenSpaceEventType.RIGHT_DOWN)
}
/***
* 高差量测
* @param viewer
* @param parent
* @param handler
* @param callback
*/
export const heightMeasure = (viewer, parent, handler, callback) => {
let positions = []
let sdkToolTip = '左键点击开始量测'
handler.setInputAction((e) => {
let cartesian = getCatesian3FromPX(viewer, e.position)
if (!cartesian) return
positions.push(cartesian)
if (positions.length === 1) {
sdkToolTip = '左键单击完成量测'
viewer.entities.add({
parent: parent,
position: cartesian,
point: {
pixelSize: 10,
outlineColor: Cesium.Color.BLUE,
outlineWidth: 5,
},
})
} else {
viewer.entities.add({
parent: parent,
position: cartesian,
point: {
pixelSize: 10,
outlineColor: Cesium.Color.BLUE,
outlineWidth: 5,
},
polyline: {
positions: positions,
width: 5,
material: Cesium.Color.BLUE.withAlpha(0.8),
clampToGround: false,
},
})
const pos0 = positions[0]
const pos1 = positions[1]
const midP = Cesium.Cartesian3.midpoint(
pos0,
pos1,
new Cesium.Cartesian3()
)
const graphic0 = Cesium.Cartographic.fromCartesian(pos0)
const graphic1 = Cesium.Cartographic.fromCartesian(pos1)
const height = Math.abs(graphic0.height - graphic1.height)
viewer.entities.add({
parent: parent,
position: midP,
label: {
text: '高差:' + height.toFixed(2) + 'm',
show: true,
showBackground: true,
backgroundColor: new Cesium.Color(0, 0, 0, 1.0),
font: '14px monospace',
horizontalOrigin: Cesium.HorizontalOrigin.CENTER,
verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
pixelOffset: new Cesium.Cartesian2(0, -25),
},
})
destroyHandler(handler)
CreateRemindertip(sdkToolTip, null, false)
if (typeof callback == 'function') callback(height)
}
}, Cesium.ScreenSpaceEventType.LEFT_CLICK)
handler.setInputAction((e) => {
let endPos = e.endPosition
CreateRemindertip(sdkToolTip, endPos, true)
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE)
}
/***
* 三角量测
* @param viewer
* @param parent
* @param handler
* @param callback
*/
export const triangleMeasure = (viewer, parent, handler, callback) => {
let positions = []
let sdkToolTip = '左键点击开始量测'
viewer.entities.add({
parent: parent,
polyline: {
positions: new Cesium.CallbackProperty(function () {
if (positions.length > 1) {
const points = updatePoints(positions)
points.push(points[0])
return points
} else {
return []
}
}, false),
width: 3,
material: Cesium.Color.BLUE.withAlpha(0.5),
clampToGround: false,
},
})
handler.setInputAction((e) => {
let cartesian = getCatesian3FromPX(viewer, e.position)
if (!cartesian) return
positions.push(cartesian)
if (positions.length === 1) {
positions.push(cartesian.clone())
viewer.entities.add({
parent: parent,
position: cartesian,
point: {
pixelSize: 10,
outlineColor: Cesium.Color.BLUE,
outlineWidth: 5,
},
})
sdkToolTip = '左键单击完成量测'
} else {
viewer.entities.add({
parent: parent,
position: cartesian,
point: {
pixelSize: 10,
outlineColor: Cesium.Color.BLUE,
outlineWidth: 5,
},
})
const points = updatePoints(positions)
drawLabel(points[0], points[1], '水平距离')
drawLabel(points[1], points[2], '高度差')
drawLabel(points[0], points[2], '空间距离')
destroyHandler(handler)
CreateRemindertip(sdkToolTip, null, false)
if (typeof callback == 'function') callback(points)
}
}, Cesium.ScreenSpaceEventType.LEFT_CLICK)
handler.setInputAction((e) => {
let endPos = e.endPosition
CreateRemindertip(sdkToolTip, endPos, true)
let cartesian = getCatesian3FromPX(viewer, endPos)
if (cartesian && positions.length >= 2) {
positions.pop()
positions.push(cartesian)
}
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE)
function updatePoints(points) {
const pos0 = points[0]
const pos1 = points[points.length - 1]
const graphic0 = Cesium.Cartographic.fromCartesian(pos0)
const graphic1 = Cesium.Cartographic.fromCartesian(pos1)
const h = graphic0.height - graphic1.height
const graphic =
h > 0
? new Cesium.Cartographic(
graphic1.longitude,
graphic1.latitude,
graphic0.height
)
: new Cesium.Cartographic(
graphic0.longitude,
graphic0.latitude,
graphic1.height
)
const car3 = Cesium.Cartographic.toCartesian(graphic)
return h > 0 ? [pos0, car3, pos1] : [pos1, car3, pos0]
}
function drawLabel(pos0, pos1, text) {
const pos = Cesium.Cartesian3.midpoint(pos0, pos1, new Cesium.Cartesian3())
const dis = Cesium.Cartesian3.distance(pos0, pos1)
viewer.entities.add({
parent: parent,
position: pos,
label: {
text: `${text}:${dis.toFixed(4)}m`,
show: true,
showBackground: true,
backgroundColor: new Cesium.Color(0, 0, 0, 1.0),
font: '14px monospace',
horizontalOrigin: Cesium.HorizontalOrigin.CENTER,
verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
pixelOffset: new Cesium.Cartesian2(0, 0),
},
})
}
}
/**
* 坡角量测
* @param {*} viewer
* @param {*} parent
* @param {*} handler
* @param {*} callback
*/
export const slopeAngleMeasure = (viewer, parent, handler, callback) => {
let positions = []
let sdkToolTip = '左键点击开始量测'
viewer.entities.add({
parent: parent,
polyline: {
positions: new Cesium.CallbackProperty(function () {
if (positions.length > 1) {
const points = updatePoints(positions)
points.push(points[0])
return points
} else {
return []
}
}, false),
width: 3,
material: Cesium.Color.BLUE.withAlpha(0.5),
clampToGround: false,
},
})
handler.setInputAction((e) => {
let cartesian = getCatesian3FromPX(viewer, e.position)
if (!cartesian) return
positions.push(cartesian)
if (positions.length === 1) {
positions.push(cartesian.clone())
viewer.entities.add({
parent: parent,
position: cartesian,
point: {
pixelSize: 10,
outlineColor: Cesium.Color.BLUE,
outlineWidth: 5,
},
})
sdkToolTip = '左键单击完成量测'
} else {
viewer.entities.add({
parent: parent,
position: cartesian,
point: {
pixelSize: 10,
outlineColor: Cesium.Color.BLUE,
outlineWidth: 5,
},
})
const points = updatePoints(positions)
const dis1 = Cesium.Cartesian3.distance(points[0], points[1])
const dis3 = Cesium.Cartesian3.distance(points[0], points[2])
const angle = calAngle(dis1, dis3, points[0])
destroyHandler(handler)
CreateRemindertip(sdkToolTip, null, false)
if (typeof callback == 'function') callback({ points, angle })
}
}, Cesium.ScreenSpaceEventType.LEFT_CLICK)
handler.setInputAction((e) => {
let endPos = e.endPosition
CreateRemindertip(sdkToolTip, endPos, true)
let cartesian = getCatesian3FromPX(viewer, endPos)
if (cartesian && positions.length >= 2) {
positions.pop()
positions.push(cartesian)
}
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE)
function updatePoints(points) {
const pos0 = points[0]
const pos1 = points[points.length - 1]
const graphic0 = Cesium.Cartographic.fromCartesian(pos0)
const graphic1 = Cesium.Cartographic.fromCartesian(pos1)
const h = graphic0.height - graphic1.height
const graphic =
h > 0
? new Cesium.Cartographic(
graphic0.longitude,
graphic0.latitude,
graphic1.height
)
: new Cesium.Cartographic(
graphic1.longitude,
graphic1.latitude,
graphic0.height
)
const car3 = Cesium.Cartographic.toCartesian(graphic)
return h > 0 ? [pos1, car3, pos0] : [pos0, car3, pos1]
}
function calAngle(dis1, dis2, point) {
const angle = radiansToDegrees(Math.acos(dis1 / dis2))
viewer.entities.add({
parent: parent,
position: point,
label: {
text: `${angle.toFixed(2)}°`,
show: true,
showBackground: true,
backgroundColor: new Cesium.Color(0, 0, 0, 1.0),
font: '14px monospace',
// heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
horizontalOrigin: Cesium.HorizontalOrigin.CENTER,
verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
pixelOffset: new Cesium.Cartesian2(0, -20),
},
})
return angle
}
}
/***
* 方位角量测
* @param viewer
* @param parent
* @param handler
* @param callback
*/
export const azimuthMeasure = (viewer, parent, handler, callback) => {
let positions = []
let sdkToolTip = '左键点击开始量测'
viewer.entities.add({
parent: parent,
polyline: {
positions: new Cesium.CallbackProperty(function () {
return positions
}, false),
width: 10,
material: new Cesium.PolylineArrowMaterialProperty(
Cesium.Color.BLUE.withAlpha(0.8)
),
clampToGround: true,
},
})
viewer.entities.add({
parent: parent,
position: new Cesium.CallbackProperty(function () {
const num = positions.length
return num > 1 ? positions[num - 1] : positions[0]
}, false),
label: {
text: new Cesium.CallbackProperty(function () {
if (positions.length > 1) {
const pos0 = transformCartesianToWGS84(positions[0])
const pos1 = transformCartesianToWGS84(
positions[positions.length - 1]
)
const angle = getAngle(pos0.x, pos0.y, pos1.x, pos1.y)
return `方位角:${angle.toFixed(2)}°`
} else {
return ''
}
}, false),
show: true,
showBackground: true,
backgroundColor: new Cesium.Color(0, 0, 0, 1.0),
font: '14px monospace',
horizontalOrigin: Cesium.HorizontalOrigin.CENTER,
verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
pixelOffset: new Cesium.Cartesian2(0, -25),
},
polyline: {
positions: new Cesium.CallbackProperty(function () {
if (positions.length > 1) {
const pos0 = positions[0]
const pos1 = positions[positions.length - 1]
const graphic0 = Cesium.Cartographic.fromCartesian(pos0)
const h = Cesium.Cartesian3.distance(pos0, pos1)
const graphic1 = new Cesium.Cartographic(
graphic0.longitude,
graphic0.latitude,
graphic0.height + h
)
const pos2 = Cesium.Cartographic.toCartesian(graphic1)
return [pos0, pos2]
} else {
return []
}
}, false),
width: 2,
material: new Cesium.PolylineDashMaterialProperty({
color: Cesium.Color.RED.withAlpha(0.5),
}),
clampToGround: false,
},
})
viewer.entities.add({
parent: parent,
polyline: {
positions: new Cesium.CallbackProperty(function () {
if (positions.length > 1) {
const pos0 = positions[0]
const pos1 = positions[positions.length - 1]
const dis = Cesium.Cartesian3.distance(pos0, pos1)
const sourceP = transformCartesianToWGS84(pos0)
const pos2 = getPointByAngleDistance(sourceP.x, sourceP.y, 0, dis)
const point = Cesium.Cartesian3.fromDegrees(pos2.x, pos2.y)
return [pos0, point]
} else {
return []
}
}, false),
width: 2,
material: Cesium.Color.YELLOW.withAlpha(0.5),
clampToGround: true,
},
})
handler.setInputAction((e) => {
let cartesian = getCatesian3FromPX(viewer, e.position)
if (!cartesian) return
positions.push(cartesian)
if (positions.length === 1) {
positions.push(cartesian.clone())
viewer.entities.add({
parent: parent,
position: cartesian,
point: {
pixelSize: 10,
outlineColor: Cesium.Color.BLUE,
outlineWidth: 5,
},
})
sdkToolTip = '左键单击完成量测'
} else {
viewer.entities.add({
parent: parent,
position: cartesian,
point: {
pixelSize: 8,
outlineColor: Cesium.Color.GREEN,
outlineWidth: 3,
},
})
destroyHandler(handler)
CreateRemindertip(sdkToolTip, null, false)
if (typeof callback == 'function') callback(positions)
}
}, Cesium.ScreenSpaceEventType.LEFT_CLICK)
handler.setInputAction((e) => {
let endPos = e.endPosition
CreateRemindertip(sdkToolTip, endPos, true)
let cartesian = getCatesian3FromPX(viewer, endPos)
if (cartesian && positions.length >= 2) {
positions.pop()
positions.push(cartesian)
}
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE)
}
/***
* 体积量测
* @param viewer
* @param parent
* @param handler
* @param callback
*/
export const volumeMeasure = (viewer, parent, handler, callback) => {
CreatePolygon(
viewer,
handler,
{ outline: true, outlineColor: '#ffff00' },
(polygon) => {
const points = polygon.PottingPoint
let minX = 10000
let minY = 10000
let maxX = -10000
let maxY = -10000
for (let i = 0; i < points.length; i++) {
const point = points[i]
const pos = transformCartesianToWGS84(point)
const { x, y } = pos
minX = Math.min(x, minX)
maxX = Math.max(x, maxX)
minY = Math.min(y, minY)
maxY = Math.max(y, maxY)
}
const d_x = Math.abs(maxX - minX)
const d_y = Math.abs(maxY - minY)
const s = d_x > d_y ? d_x : d_y
const triangles = subTriangle(points, 1.5 / s)
const extreme = getRegionExtreme(viewer, points)
const pos = getPolygonCentroid(points)
const positions = points.concat(points[0])
const num = positions.length
const { minH, maxH } = extreme
const wall = viewer.entities.add({
parent: parent,
position: Cesium.Cartesian3.fromDegrees(pos[0], pos[1], maxH),
polygon: {
hierarchy: positions,
material: Cesium.Color.BLUE.withAlpha(0.3),
},
wall: {
positions: positions,
maximumHeights: Array(num).fill(maxH),
minimumHeights: Array(num).fill(minH),
outline: true,
outlineColor: Cesium.Color.LIGHTGRAY.withAlpha(0.5),
outlineWidth: 4,
material: Cesium.Color.YELLOW.withAlpha(0.5),
},
})
viewer.entities.remove(polygon)
const volume = calculateVolume(viewer, triangles, minH)
wall.label = {
text: `区域高值:${maxH.toFixed(3)} m\n区域低值:${minH.toFixed(
3
)} m\n计算体积:${volume.toFixed(2)} m³`,
show: true,
showBackground: true,
backgroundColor: new Cesium.Color(0, 0, 0, 1.0),
font: '14px monospace',
horizontalOrigin: Cesium.HorizontalOrigin.LEFT,
verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
pixelOffset: new Cesium.Cartesian2(-25, -25),
}
if (typeof callback == 'function') callback({ minH, maxH, volume })
}
)
}
/**
* 计算体积(三角网算法)
* @method
* @param {Object} viewer 地图场景对象
* @param {Array<DegreePosZ[]>} triangles 三角网集合,WGS84坐标
* @param {Number} height 计算起始高度
* @returns {Number} 体积,单位:立方米(m³)
*/
export const calculateVolume = (viewer, triangles, height) => {
let curVolume = 0
for (let index = 0; index < triangles.length; index++) {
const triangle = triangles[index]
let heightSum = 0
const basicTriangle = []
for (let i = 0; i < triangle.length; i++) {
const cartesian = triangle[i]
const cartographic = Cesium.Cartographic.fromCartesian(cartesian)
let h = viewer.scene.sampleHeightSupported
? viewer.scene.sampleHeight(cartographic)
: viewer.scene.globe.getHeight(cartographic)
const graphic1 = new Cesium.Cartographic(
cartographic.longitude,
cartographic.latitude,
h
)
heightSum += h
const basicCartesian3 = Cesium.Cartographic.toCartesian(graphic1)
basicTriangle.push(basicCartesian3)
}
const area = getArea(basicTriangle)
const curHeight = heightSum / 3
const _h = curHeight - height
if (_h > 0) {
const volume = area * (curHeight - height)
curVolume += volume
}
}
return curVolume
}
/**
* 计算两点对于正北方向的朝向角度 [0,360]
* @method
* @param {Number} x0 起始点经度,WGS84坐标
* @param {Number} y0 起始点纬度,WGS84坐标
* @param {Number} x1 目标点经度,WGS84坐标
* @param {Number} y1 目标点纬度,WGS84坐标
* @returns {Number} 角度值,单位度(°)
*/
export const getAngle = (x0, y0, x1, y1) => {
let rad = Math.PI / 180,
lat1 = y0 * rad,
lat2 = y1 * rad,
lon1 = x0 * rad,
lon2 = x1 * rad
const a = Math.sin(lon2 - lon1) * Math.cos(lat2)
const b =
Math.cos(lat1) * Math.sin(lat2) -
Math.sin(lat1) * Math.cos(lat2) * Math.cos(lon2 - lon1)
return radiansToDegrees(Math.atan2(a, b))
}
/**
* 计算面积(表面or椭球)
* @method
* @param {Array<Cartesian3>} positions 计算区域边界点集合,笛卡尔坐标
* @param {String} type 计算模式:onground-地表面积,project-投影面积
* @returns {Number} 面积值,单位:平方米(㎡)
*/
export const getSurfaceArea = (positions, type) => {
let granularity = Math.PI / Math.pow(2, 11)
granularity = granularity / 64
let sum = 0
const polygonGeometry = Cesium.PolygonGeometry.fromPositions({
positions: positions,
vertexFormat: Cesium.PerInstanceColorAppearance.FLAT_VERTEX_FORMAT,
granularity: granularity,
})
const geom = Cesium.PolygonGeometry.createGeometry(polygonGeometry)
for (let i = 0; i < geom.indices.length; i += 3) {
const i0 = geom?.indices[i]
const i1 = geom?.indices[i + 1]
const i2 = geom?.indices[i + 2]
const stp = geom?.attributes.position.values
const pos0 = {
x: stp[i0 * 3],
y: stp[i0 * 3 + 1],
z: stp[i0 * 3 + 2],
}
const pos1 = {
x: stp[i1 * 3],
y: stp[i1 * 3 + 1],
z: stp[i1 * 3 + 2],
}
const pos2 = {
x: stp[i2 * 3],
y: stp[i2 * 3 + 1],
z: stp[i2 * 3 + 2],
}
const points = [pos0, pos1, pos2]
const _area = type === 'onground' ? getArea(points) : getGeodesyArea(points)
sum += _area
}
return sum
}
/***
* 计算面积(椭球)
* @param positions
* @returns
*/
export const getGeodesyArea = (positions) => {
const pos = positions.map((p) => {
const _pos = transformCartesianToWGS84(p)
return new LatLon(_pos.x, _pos.y)
})
return LatLon.areaOf(pos)
}
/***
* 计算面积
* @param positions
* @returns
*/
export const getArea = (positions) => {
const x = [0]
const y = [0]
const geodesic = new Cesium.EllipsoidGeodesic()
const radiansPerDegree = Math.PI / 180.0 //角度转化为弧度(rad)
//数组x,y分别按顺序存储各点的横、纵坐标值
for (let i = 0; i < positions.length - 1; i++) {
const p1 = positions[i]
const p2 = positions[i + 1]
const point1cartographic = Cesium.Cartographic.fromCartesian(p1)
const point2cartographic = Cesium.Cartographic.fromCartesian(p2)
geodesic.setEndPoints(point1cartographic, point2cartographic)
const s = Math.sqrt(
Math.pow(geodesic.surfaceDistance, 2) +
Math.pow(point2cartographic.height - point1cartographic.height, 2)
)
const lat1 = point2cartographic.latitude * radiansPerDegree
const lon1 = point2cartographic.longitude * radiansPerDegree
const lat2 = point1cartographic.latitude * radiansPerDegree
const lon2 = point1cartographic.longitude * radiansPerDegree
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
}
y.push(Math.sin(angle) * s + y[i])
x.push(Math.cos(angle) * s + x[i])
}
let sum = 0
for (let i = 0; i < x.length - 1; i++) {
sum += x[i] * y[i + 1] - x[i + 1] * y[i]
}
return Math.abs(sum + x[x.length - 1] * y[0] - x[0] * y[y.length - 1]) / 2
}
/**
* 获取多点总距离(椭球表面)
* @method
* @param {Array<DegreePos>} positions 待计算点集合,WGS84坐标
* @returns {Number} 距离,单位:米(m)
*/
export const getGeodesyDistance = (positions) => {
let distance = 0
for (let i = 0; i < positions.length - 1; i++) {
const p1 = new LatLon(positions[i].x, positions[i].y)
const p2 = new LatLon(positions[i + 1].x, positions[i + 1].y)
const s = p1.distanceTo(p2)
distance = distance + s
}
return distance
}
/**
* 获取多点总距离
* @method
* @param {Array<DegreePosZ>} positions 待计算点集合,WGS84坐标
* @returns {Number} 距离,单位:米(m)
*/
export const getPositionDistance = (positions) => {
let distance = 0
for (let i = 0; i < positions.length - 1; i++) {
const s = getDistanceTwo(positions[i], positions[i + 1])
distance = distance + s
}
return distance
}
/**
* 获取两点间距离
* @method
* @param {DegreePosZ} pos1 起始点坐标,WGS84坐标
* @param {DegreePosZ} pos2 目标点坐标,WGS84坐标
* @returns {Number} 距离,单位:米(m)
*/
export const getDistanceTwo = (pos1, pos2) => {
let cartographic1 = transformWGS84ToCartographic(pos1)
let cartographic2 = transformWGS84ToCartographic(pos2)
let geodesic = new Cesium.EllipsoidGeodesic()
geodesic.setEndPoints(cartographic1, cartographic2)
let s = geodesic.surfaceDistance
return Math.sqrt(
Math.pow(s, 2) + Math.pow(cartographic1.height - cartographic2.height, 2)
)
}