1.修改平面距离、面积测量功能的实现逻辑和显示效果;2.优化相关代码,修复bug;
This commit is contained in:
parent
eef0209e58
commit
0f43d4d08a
|
@ -54,8 +54,13 @@ export const useDrawTool = (viewer) => {
|
|||
)
|
||||
|
||||
if (!assistLayer) {
|
||||
assistLayer = new CustomDataSource('__assist_edit')
|
||||
viewer.dataSources.add(assistLayer)
|
||||
const layers = viewer.dataSources.getByName('__assist_edit')
|
||||
if(layers.length && layers.length > 0) {
|
||||
assistLayer = layers[0]
|
||||
} else {
|
||||
assistLayer = new CustomDataSource('__assist_edit')
|
||||
viewer.dataSources.add(assistLayer)
|
||||
}
|
||||
}
|
||||
|
||||
// 辅助点,用于显示获取的实际位置
|
||||
|
|
|
@ -1,223 +0,0 @@
|
|||
import * as Cesium from 'cesium'
|
||||
import * as turf from '@turf/turf'
|
||||
import { useDrawTool } from './useDrawTool'
|
||||
|
||||
// 存储测量内容的辅助图层
|
||||
let measureLayer = null
|
||||
|
||||
/**
|
||||
* 计算折线长度
|
||||
* @param {Cesium.Cartesian3[]} positions 位置列表
|
||||
* @returns 折线长度,单位米
|
||||
*/
|
||||
const getDistance = (positions) => {
|
||||
const line = turf.lineString(positions.map((pos) => {
|
||||
const cartographic = Cesium.Cartographic.fromCartesian(pos)
|
||||
return [Cesium.Math.toDegrees(cartographic.longitude), Cesium.Math.toDegrees(cartographic.latitude)]
|
||||
}))
|
||||
return turf.length(line, { units: 'kilometers' }) * 1000 // 返回米
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算多边形面积
|
||||
* @param {Cesium.Cartesian3[]} positions 位置列表
|
||||
* @returns 多边形面积,单位平方米
|
||||
*/
|
||||
const getArea = (positions) => {
|
||||
const pts = [...positions, positions[0]] // 闭合多边形
|
||||
const polygon = turf.polygon([pts.map((pos) => {
|
||||
const cartographic = Cesium.Cartographic.fromCartesian(pos)
|
||||
return [Cesium.Math.toDegrees(cartographic.longitude), Cesium.Math.toDegrees(cartographic.latitude)]
|
||||
})])
|
||||
return turf.area(polygon) // 返回平方米
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取多个点的中心位置,根据边界范围确定
|
||||
* @param {Cesium.Cartesian3[]} positions 位置列表
|
||||
* @param {number} height 返回位置的高度,默认为0
|
||||
* @returns 根据边界范围确定的中心点位置
|
||||
*/
|
||||
const getBoundingCenter = (positions, height = 0) => {
|
||||
const coord = getBoundingCenterCoordinate(positions)
|
||||
return Cesium.Cartesian3.fromDegrees(coord[0], coord[1], height)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取多个点的中心位置坐标,根据边界范围确定
|
||||
* @param {Cesium.Cartesian3[]} positions 位置列表
|
||||
* @returns 根据边界范围确定的中心点位置坐标,格式为 [经度, 纬度]
|
||||
*/
|
||||
const getBoundingCenterCoordinate = (positions) => {
|
||||
const polygon = turf.lineString(positions.map((pos) => {
|
||||
const cartographic = Cesium.Cartographic.fromCartesian(pos)
|
||||
return [Cesium.Math.toDegrees(cartographic.longitude), Cesium.Math.toDegrees(cartographic.latitude)]
|
||||
}))
|
||||
const centerFeature = turf.center(polygon)
|
||||
return centerFeature.geometry.coordinates
|
||||
}
|
||||
|
||||
export const useMeasureTool = (viewer) => {
|
||||
if (!viewer) {
|
||||
throw new Error('获取 Viewer 实例失败,请确保在 Viewer 初始化后调用 useMeasureTool 方法!')
|
||||
}
|
||||
|
||||
const editTool = useDrawTool(viewer)
|
||||
if (!measureLayer) {
|
||||
measureLayer = new Cesium.CustomDataSource('__assist_measure')
|
||||
viewer.dataSources.add(measureLayer)
|
||||
}
|
||||
|
||||
// 距离测量
|
||||
const measureDistance = () => {
|
||||
return editTool.drawPolyline().then((positions) => {
|
||||
const style = {
|
||||
width: 5,
|
||||
material: Cesium.Color.fromCssColorString('#57e0e0'),
|
||||
}
|
||||
let arr = []
|
||||
for (let i = 0; i < positions.length; i++) {
|
||||
if (i + 1 < positions.length) {
|
||||
if (!positions[i] || !positions[i + 1]) continue
|
||||
arr.push([positions[i], positions[i + 1]])
|
||||
}
|
||||
}
|
||||
console.log(arr)
|
||||
let result = 0
|
||||
arr.forEach((item) => {
|
||||
let start = Cesium.Cartographic.fromCartesian(item[0])
|
||||
let end = Cesium.Cartographic.fromCartesian(item[1])
|
||||
let startObj = { longitude: '', latitude: '' }
|
||||
let endObj = { longitude: '', latitude: '' }
|
||||
startObj.longitude = Cesium.Math.toDegrees(start.longitude)
|
||||
startObj.latitude = Cesium.Math.toDegrees(start.latitude)
|
||||
endObj.longitude = Cesium.Math.toDegrees(end.longitude)
|
||||
endObj.latitude = Cesium.Math.toDegrees(end.latitude)
|
||||
|
||||
console.log(startObj)
|
||||
console.log(endObj)
|
||||
|
||||
let from = turf.point([startObj.longitude, startObj.latitude])
|
||||
let to = turf.point([endObj.longitude, endObj.latitude])
|
||||
|
||||
let distance = turf.distance(from, to, { units: 'kilometers' }) * 1000
|
||||
|
||||
result += distance
|
||||
|
||||
const tempPolyline = new Cesium.Entity({
|
||||
name: '__measure_distance',
|
||||
// position: [ {x: (item[0].x + item[1].x) / 2, y: (item[0].y + item[1].y) / 2, z: (item[0].z + item[1].z) / 2} ],
|
||||
position: new Cesium.Cartesian3(
|
||||
(item[0].x + item[1].x) / 2,
|
||||
(item[0].y + item[1].y) / 2,
|
||||
(item[0].z + item[1].z) / 2,
|
||||
),
|
||||
polyline: {
|
||||
positions: [...item],
|
||||
width: style.width,
|
||||
material: style.material,
|
||||
clampToGround: true,
|
||||
},
|
||||
label: new Cesium.LabelGraphics({
|
||||
text: distance.toFixed(2) + '米',
|
||||
heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
|
||||
pixelOffset: new Cesium.Cartesian2(10, 10),
|
||||
disableDepthTestDistance: Number.POSITIVE_INFINITY,
|
||||
}),
|
||||
})
|
||||
|
||||
measureLayer.entities.add(tempPolyline)
|
||||
return positions
|
||||
})
|
||||
|
||||
let len = positions.length - 1
|
||||
if (len > 1) {
|
||||
let resultLabel = new Cesium.Entity({
|
||||
name: 'measure_distance_length',
|
||||
position: new Cesium.Cartesian3(
|
||||
(positions[0].x + positions[len].x) / 2,
|
||||
(positions[0].y + positions[len].y) / 2,
|
||||
(positions[0].z + positions[len].z) / 2,
|
||||
),
|
||||
label: new Cesium.LabelGraphics({
|
||||
text: '总距离:' + result.toFixed(2) + '米',
|
||||
heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
|
||||
disableDepthTestDistance: Number.POSITIVE_INFINITY,
|
||||
fillColor: Cesium.Color.AQUAMARINE,
|
||||
}),
|
||||
})
|
||||
measureLayer.entities.add(resultLabel)
|
||||
}
|
||||
return positions
|
||||
})
|
||||
}
|
||||
|
||||
// 面积测量
|
||||
const measureArea = () => {
|
||||
return editTool.drawPolygon().then((positions) => {
|
||||
let polygon = []
|
||||
positions.forEach((item) => {
|
||||
let obj = Cesium.Cartographic.fromCartesian(item)
|
||||
polygon.push([Cesium.Math.toDegrees(obj.longitude), Cesium.Math.toDegrees(obj.latitude)])
|
||||
})
|
||||
|
||||
let features = turf.points(polygon)
|
||||
let center = turf.center(features)
|
||||
|
||||
console.log(center)
|
||||
|
||||
let points0 = Cesium.Cartographic.fromCartesian(positions[0])
|
||||
polygon.push([
|
||||
Cesium.Math.toDegrees(points0.longitude),
|
||||
Cesium.Math.toDegrees(points0.latitude),
|
||||
])
|
||||
let areaPolygon = turf.polygon([polygon])
|
||||
let area = turf.area(areaPolygon)
|
||||
|
||||
const tempPolygon = new Cesium.Entity({
|
||||
name: '__measure_area',
|
||||
position: Cesium.Cartesian3.fromDegrees(
|
||||
center.geometry.coordinates[0],
|
||||
center.geometry.coordinates[1],
|
||||
),
|
||||
polygon: {
|
||||
hierarchy: new Cesium.PolygonHierarchy(positions),
|
||||
heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
|
||||
material: Cesium.Color.fromCssColorString('#ffff00').withAlpha(0.3),
|
||||
},
|
||||
polyline: {
|
||||
positions: [...positions, positions[0]],
|
||||
clampToGround: true,
|
||||
width: 2,
|
||||
material: Cesium.Color.fromCssColorString('#ffff00'),
|
||||
},
|
||||
label: new Cesium.LabelGraphics({
|
||||
text: area.toFixed(2) + '平方米',
|
||||
heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
|
||||
disableDepthTestDistance: Number.POSITIVE_INFINITY,
|
||||
}),
|
||||
})
|
||||
measureLayer.entities.add(tempPolygon)
|
||||
return positions
|
||||
})
|
||||
}
|
||||
|
||||
// 取消当前测量
|
||||
const abort = () => {
|
||||
editTool.abort() // 调用编辑工具的取消方法
|
||||
}
|
||||
|
||||
// 清除所有测量结果
|
||||
const clear = () => {
|
||||
measureLayer.entities.removeAll()
|
||||
}
|
||||
|
||||
return {
|
||||
distance: measureDistance,
|
||||
area: measureArea,
|
||||
abort,
|
||||
clear,
|
||||
}
|
||||
}
|
||||
|
||||
export { getDistance, getArea, getBoundingCenter, getBoundingCenterCoordinate }
|
|
@ -0,0 +1,124 @@
|
|||
import * as Cesium from 'cesium'
|
||||
|
||||
import { useEventBus, ScreenMode } from '../useEventBus'
|
||||
import { useHoverPosition } from '../useHoverPosition'
|
||||
import { getDistance, getArea, getBoundingCenter, getBoundingCenterCoordinate } from './utils'
|
||||
import { measureDistance as _measureDistance } from './measureDistance'
|
||||
import { measureArea as _measureArea } from './measureArea'
|
||||
|
||||
// 存储测量内容的辅助图层
|
||||
let measureLayer = null
|
||||
// 存储编辑内容的辅助图层
|
||||
// let assistLayer = null
|
||||
|
||||
// 所有useMeasureTool实例共用一个绘制状态和取消回调
|
||||
let abortCallback = null
|
||||
|
||||
export const useMeasureTool = (viewer) => {
|
||||
if (!viewer) {
|
||||
throw new Error('获取 Viewer 实例失败,请确保在 Viewer 初始化后调用 useMeasureTool 方法!')
|
||||
}
|
||||
|
||||
// 获取实时位置
|
||||
const { isHover, position } = useHoverPosition(viewer)
|
||||
// 获取事件总线
|
||||
const bus = useEventBus(viewer)
|
||||
|
||||
// 判断辅助点是否显示
|
||||
watch(
|
||||
() => [bus.mode.value, isHover.value],
|
||||
([nowStatus, hover]) => {
|
||||
if (hover && nowStatus === ScreenMode.DRAW) {
|
||||
assistPoint.show = true
|
||||
} else {
|
||||
assistPoint.show = false
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
if (!measureLayer) {
|
||||
const layers = viewer.dataSources.getByName('__assist_measure')
|
||||
if(layers.length && layers.length > 0) {
|
||||
measureLayer = layers[0]
|
||||
} else {
|
||||
measureLayer = new Cesium.CustomDataSource('__assist_measure')
|
||||
viewer.dataSources.add(measureLayer)
|
||||
}
|
||||
}
|
||||
|
||||
// if (!assistLayer) {
|
||||
// const layers = viewer.dataSources.getByName('__assist_edit')
|
||||
// if(layers.length && layers.length > 0) {
|
||||
// assistLayer = layers[0]
|
||||
// } else {
|
||||
// assistLayer = new Cesium.CustomDataSource('__assist_edit')
|
||||
// viewer.dataSources.add(assistLayer)
|
||||
// }
|
||||
// }
|
||||
|
||||
// 辅助点,用于显示获取的实际位置
|
||||
const assistPoint = new Cesium.Entity({
|
||||
name: '__draw_icon',
|
||||
show: false,
|
||||
position: new Cesium.CallbackPositionProperty(() => {
|
||||
return position.value || undefined
|
||||
}, false),
|
||||
point: {
|
||||
pixelSize: 7,
|
||||
color: Cesium.Color.BLUE,
|
||||
outlineColor: Cesium.Color.WHITE,
|
||||
outlineWidth: 1,
|
||||
heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
|
||||
},
|
||||
})
|
||||
viewer.entities.add(assistPoint)
|
||||
|
||||
// 全局取消当前绘制操作的函数
|
||||
const abort = () => {
|
||||
if (abortCallback) {
|
||||
abortCallback() // 调用当前注册的取消回调
|
||||
abortCallback = null // 清空回调
|
||||
}
|
||||
}
|
||||
|
||||
// 注册当前绘制操作的取消回调函数
|
||||
const registerAbort = (cb) => {
|
||||
abortCallback = cb
|
||||
}
|
||||
|
||||
// 准备传递给绘制函数的依赖对象
|
||||
const drawDependencies = {
|
||||
viewer,
|
||||
bus,
|
||||
ScreenMode,
|
||||
position,
|
||||
measureLayer,
|
||||
registerAbort,
|
||||
globalAbort: abort, // 将全局 abort 函数也传递进去,方便内部调用
|
||||
}
|
||||
|
||||
// 调用拆分后的测量函数,并传递依赖
|
||||
const distance = () => _measureDistance(drawDependencies)
|
||||
|
||||
const area = () => _measureArea(drawDependencies)
|
||||
|
||||
|
||||
// 取消当前测量
|
||||
// const abort = () => {
|
||||
// editTool.abort() // 调用编辑工具的取消方法
|
||||
// }
|
||||
|
||||
// 清除所有测量结果
|
||||
const clear = () => {
|
||||
measureLayer.entities.removeAll()
|
||||
}
|
||||
|
||||
return {
|
||||
distance,
|
||||
area,
|
||||
abort,
|
||||
clear,
|
||||
}
|
||||
}
|
||||
|
||||
export { getDistance, getArea, getBoundingCenter, getBoundingCenterCoordinate }
|
|
@ -0,0 +1,166 @@
|
|||
import * as Cesium from 'cesium'
|
||||
import { getBoundingCenter, getArea } from './utils'
|
||||
|
||||
// 面积测量
|
||||
export const measureArea = (deps) => {
|
||||
const { viewer, bus, ScreenMode, position, measureLayer, registerAbort, globalAbort } = deps
|
||||
|
||||
// 如果在执行其它操作,则取消
|
||||
globalAbort()
|
||||
|
||||
bus.mode.value = ScreenMode.DRAW
|
||||
const featurePositions = []
|
||||
|
||||
// 临时多边形实体 (包含填充和边界线)
|
||||
const tempPolygon = new Cesium.Entity({
|
||||
name: '__draw_temp_polygon',
|
||||
position: new Cesium.CallbackProperty(() => {
|
||||
if (featurePositions.length < 2) return null
|
||||
|
||||
return getBoundingCenter([...featurePositions, position.value])
|
||||
}, false),
|
||||
label: {
|
||||
text: new Cesium.CallbackProperty(() => {
|
||||
if (featurePositions.length < 2) return null
|
||||
|
||||
return `${getArea([...featurePositions, position.value]).toFixed(2)}平方米`
|
||||
}, false),
|
||||
font: '14px sans-serif',
|
||||
pixelOffset: new Cesium.Cartesian2(0, -6),
|
||||
verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
|
||||
showBackground: true,
|
||||
backgroundPadding: new Cesium.Cartesian2(8, 4),
|
||||
heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
|
||||
disableDepthTestDistance: Number.POSITIVE_INFINITY,
|
||||
},
|
||||
polygon: {
|
||||
hierarchy: new Cesium.CallbackProperty(() => {
|
||||
const positions = [...featurePositions]
|
||||
// 添加当前鼠标位置作为最后一个点,形成动态效果
|
||||
if (
|
||||
position.value &&
|
||||
!position.value.equals(featurePositions[featurePositions.length - 1]) // 避免重复添加同一个点
|
||||
) {
|
||||
positions.push(position.value)
|
||||
}
|
||||
// 多边形需要至少3个点才能形成
|
||||
if (positions.length < 3) {
|
||||
return null
|
||||
}
|
||||
return new Cesium.PolygonHierarchy(positions)
|
||||
}, false),
|
||||
heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
|
||||
material: Cesium.Color.fromCssColorString('#ff0000').withAlpha(0.3),
|
||||
},
|
||||
// 绘制边界线,连接所有点和第一个点,形成闭合效果
|
||||
polyline: {
|
||||
positions: new Cesium.CallbackProperty(() => {
|
||||
const positions = [...featurePositions]
|
||||
// 添加当前鼠标位置作为最后一个点
|
||||
if (
|
||||
position.value &&
|
||||
!position.value.equals(featurePositions[featurePositions.length - 1])
|
||||
) {
|
||||
positions.push(position.value)
|
||||
}
|
||||
// 如果有至少一个点,则连接到第一个点形成闭合线
|
||||
if (featurePositions.length > 0 && featurePositions[0]) {
|
||||
positions.push(featurePositions[0])
|
||||
}
|
||||
return positions
|
||||
}, false),
|
||||
// clampToGround: true, // 多边形边界线通常不需要 clampToGround,因为多边形本身已经处理了高度
|
||||
width: 2,
|
||||
material: Cesium.Color.fromCssColorString('#ff0000'),
|
||||
clampToGround: true,
|
||||
},
|
||||
})
|
||||
|
||||
viewer.entities.add(tempPolygon)
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
// 清理资源
|
||||
const dispose = () => {
|
||||
registerAbort(null) // 取消注册当前的 abort 回调
|
||||
viewer.entities.remove(tempPolygon)
|
||||
bus.offScreen(Cesium.ScreenSpaceEventType.LEFT_CLICK, handleAddPoint)
|
||||
bus.offScreen(Cesium.ScreenSpaceEventType.RIGHT_CLICK, handleCancelDraw)
|
||||
bus.offScreen(Cesium.ScreenSpaceEventType.LEFT_DOUBLE_CLICK, handleEndDraw)
|
||||
bus.mode.value = ScreenMode.VIEW
|
||||
}
|
||||
|
||||
// 取消绘制
|
||||
const abort = (info) => {
|
||||
dispose()
|
||||
reject(info || '取消多边形绘制!')
|
||||
}
|
||||
|
||||
// 左键加点
|
||||
const handleAddPoint = () => {
|
||||
if (position.value && !position.value.equals(featurePositions[featurePositions.length - 1])) {
|
||||
featurePositions.push(position.value)
|
||||
}
|
||||
}
|
||||
|
||||
// 左键双击完成绘制
|
||||
const handleEndDraw = () => {
|
||||
// 双击时,如果鼠标位置与最后一个点不同,先添加当前点
|
||||
handleAddPoint()
|
||||
if (featurePositions.length < 3) {
|
||||
// TODO: 替换为实际的错误提示方式
|
||||
console.error('请保多边形至少有三个点!')
|
||||
return
|
||||
}
|
||||
// 绘制完成的面积测量实体
|
||||
measureLayer.entities.add({
|
||||
position: getBoundingCenter(featurePositions),
|
||||
label: {
|
||||
text: `${getArea(featurePositions).toFixed(2)}平方米`,
|
||||
font: '14px sans-serif',
|
||||
pixelOffset: new Cesium.Cartesian2(0, -6),
|
||||
verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
|
||||
showBackground: true,
|
||||
backgroundPadding: new Cesium.Cartesian2(8, 4),
|
||||
heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
|
||||
disableDepthTestDistance: Number.POSITIVE_INFINITY,
|
||||
},
|
||||
polygon: {
|
||||
hierarchy: [...featurePositions],
|
||||
heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
|
||||
material: Cesium.Color.fromCssColorString('#ff0000').withAlpha(0.3),
|
||||
},
|
||||
// 绘制边界线,连接所有点和第一个点,形成闭合效果
|
||||
polyline: {
|
||||
positions: [...featurePositions, featurePositions[0]],
|
||||
width: 2,
|
||||
material: Cesium.Color.fromCssColorString('#ff0000'),
|
||||
clampToGround: true,
|
||||
},
|
||||
})
|
||||
dispose()
|
||||
// 使用 setTimeout 避免立即 resolve 可能导致的事件冲突或其他问题
|
||||
setTimeout(() => {
|
||||
resolve(featurePositions)
|
||||
}, 0)
|
||||
}
|
||||
|
||||
// 右键取消绘制或删除最后一个点
|
||||
const handleCancelDraw = () => {
|
||||
if (featurePositions.length > 0) {
|
||||
// 取消最后绘制的点
|
||||
featurePositions.pop()
|
||||
} else {
|
||||
// 如果没有点,则取消整个绘制
|
||||
abort()
|
||||
}
|
||||
}
|
||||
|
||||
// 注册当前的取消回调
|
||||
registerAbort(abort)
|
||||
|
||||
// 绑定绘制相关事件
|
||||
bus.onScreen(Cesium.ScreenSpaceEventType.LEFT_CLICK, handleAddPoint)
|
||||
bus.onScreen(Cesium.ScreenSpaceEventType.RIGHT_CLICK, handleCancelDraw)
|
||||
bus.onScreen(Cesium.ScreenSpaceEventType.LEFT_DOUBLE_CLICK, handleEndDraw)
|
||||
})
|
||||
}
|
|
@ -0,0 +1,183 @@
|
|||
import * as Cesium from 'cesium'
|
||||
import { getBoundingCenter, getDistance } from './utils'
|
||||
|
||||
// 距离测量
|
||||
export const measureDistance = (deps) => {
|
||||
const { viewer, bus, ScreenMode, position, measureLayer, registerAbort, globalAbort } = deps
|
||||
|
||||
// 如果在执行其它操作,则取消
|
||||
globalAbort();
|
||||
|
||||
bus.mode.value = ScreenMode.DRAW;
|
||||
const featurePositions = [];
|
||||
/**
|
||||
* 测距分段折线存储结构,按绘制顺序排序
|
||||
* {
|
||||
* entity: 绘制完成对应折线部分的实体,
|
||||
* distance: 绘制距离,用于计算总距离
|
||||
* }
|
||||
*/
|
||||
const subDistance = []
|
||||
|
||||
// 临时折线实体
|
||||
const tempPolyline = new Cesium.Entity({
|
||||
name: "__draw_temp_polyline",
|
||||
position: new Cesium.CallbackProperty(() => {
|
||||
if (featurePositions.length === 0) return null
|
||||
// 获取最后一个位置和当前鼠标位置的中点,作为标签显示位置
|
||||
// return getBoundingCenter([featurePositions[featurePositions.length - 1], position.value])
|
||||
return position.value
|
||||
}, false),
|
||||
label: {
|
||||
text: new Cesium.CallbackProperty(() => {
|
||||
if (featurePositions.length === 0) return null
|
||||
// 获取两个点之间的距离
|
||||
const distance = getDistance([featurePositions[featurePositions.length - 1], position.value])
|
||||
return `${distance.toFixed(2)}米\n总距离:${(subDistance.reduce((total, d) => total + d.distance, 0) + distance).toFixed(2)}米`;
|
||||
}, false),
|
||||
font: '14px sans-serif',
|
||||
pixelOffset: new Cesium.Cartesian2(0, -6),
|
||||
verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
|
||||
showBackground: true,
|
||||
backgroundPadding: new Cesium.Cartesian2(8, 4),
|
||||
heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
|
||||
disableDepthTestDistance: Number.POSITIVE_INFINITY,
|
||||
},
|
||||
polyline: {
|
||||
positions: new Cesium.CallbackProperty(() => {
|
||||
if(featurePositions.length === 0) return null
|
||||
|
||||
const positions = [featurePositions[featurePositions.length - 1]]
|
||||
// 添加当前鼠标位置作为最后一个点,形成动态效果
|
||||
if (
|
||||
position.value &&
|
||||
!position.value.equals(
|
||||
featurePositions[featurePositions.length - 1]
|
||||
) // 避免重复添加同一个点
|
||||
) {
|
||||
positions.push(position.value);
|
||||
}
|
||||
if (positions.length < 2) return null
|
||||
|
||||
return positions;
|
||||
}, false),
|
||||
width: 2,
|
||||
material: Cesium.Color.fromCssColorString("#FF0000"),
|
||||
clampToGround: true,
|
||||
},
|
||||
});
|
||||
|
||||
viewer.entities.add(tempPolyline);
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
// 清理资源
|
||||
const dispose = () => {
|
||||
subDistance.length = 0
|
||||
registerAbort(null); // 取消注册当前的 abort 回调
|
||||
viewer.entities.remove(tempPolyline);
|
||||
bus.offScreen(Cesium.ScreenSpaceEventType.LEFT_CLICK, handleAddPoint);
|
||||
bus.offScreen(Cesium.ScreenSpaceEventType.RIGHT_CLICK, handleCancelDraw);
|
||||
bus.offScreen(Cesium.ScreenSpaceEventType.LEFT_DOUBLE_CLICK, handleEndDraw);
|
||||
bus.mode.value = ScreenMode.VIEW;
|
||||
};
|
||||
|
||||
// 取消绘制
|
||||
const abort = (info) => {
|
||||
dispose();
|
||||
reject(info || "取消折线绘制!");
|
||||
};
|
||||
|
||||
// 左键加点
|
||||
const handleAddPoint = () => {
|
||||
if (
|
||||
position.value &&
|
||||
!position.value.equals(featurePositions[featurePositions.length - 1])
|
||||
) {
|
||||
featurePositions.push(position.value);
|
||||
if(featurePositions.length > 1) {
|
||||
const positions = [
|
||||
featurePositions[featurePositions.length - 2],
|
||||
featurePositions[featurePositions.length - 1],
|
||||
];
|
||||
// 当至少有2个点时,添加最后2个点的折线
|
||||
subDistance.push({
|
||||
entity: measureLayer.entities.add(new Cesium.Entity({
|
||||
position: getBoundingCenter(positions),
|
||||
label: {
|
||||
text: `${getDistance(positions).toFixed(2)}米`,
|
||||
font: '14px sans-serif',
|
||||
pixelOffset: new Cesium.Cartesian2(0, -6),
|
||||
verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
|
||||
showBackground: true,
|
||||
backgroundPadding: new Cesium.Cartesian2(8, 4),
|
||||
heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
|
||||
disableDepthTestDistance: Number.POSITIVE_INFINITY,
|
||||
},
|
||||
polyline: {
|
||||
positions,
|
||||
width: 2,
|
||||
material: Cesium.Color.fromCssColorString('#ff0000'),
|
||||
clampToGround: true,
|
||||
}
|
||||
})),
|
||||
distance: getDistance(positions),
|
||||
})
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 左键双击完成绘制
|
||||
const handleEndDraw = () => {
|
||||
// 双击时,如果鼠标位置与最后一个点不同,先添加当前点
|
||||
handleAddPoint();
|
||||
if (featurePositions.length < 2) {
|
||||
// TODO: 替换为实际的错误提示方式
|
||||
console.error("请保证折线至少有两个点!");
|
||||
return;
|
||||
}
|
||||
|
||||
// 绘制总长标签
|
||||
measureLayer.entities.add(new Cesium.Entity({
|
||||
position: featurePositions[featurePositions.length - 1],
|
||||
label: {
|
||||
text: `总距离:${subDistance.reduce((total, d) => total + d.distance, 0).toFixed(2)}米`,
|
||||
font: '14px sans-serif',
|
||||
pixelOffset: new Cesium.Cartesian2(0, -6),
|
||||
verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
|
||||
showBackground: true,
|
||||
backgroundPadding: new Cesium.Cartesian2(8, 4),
|
||||
heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
|
||||
disableDepthTestDistance: Number.POSITIVE_INFINITY,
|
||||
}
|
||||
}))
|
||||
dispose();
|
||||
// 使用 setTimeout 避免立即 resolve 可能导致的事件冲突或其他问题
|
||||
setTimeout(() => {
|
||||
resolve(featurePositions);
|
||||
}, 0);
|
||||
};
|
||||
|
||||
// 右键取消绘制或删除最后一个点
|
||||
const handleCancelDraw = () => {
|
||||
if (featurePositions.length > 0) {
|
||||
// 取消最后绘制的点
|
||||
featurePositions.pop();
|
||||
// 如果有已经绘制的测量线段,删除最后绘制的
|
||||
if (subDistance.length > 0) {
|
||||
measureLayer.entities.remove(subDistance.pop().entity);
|
||||
}
|
||||
} else {
|
||||
// 如果没有点,则取消整个绘制
|
||||
abort();
|
||||
}
|
||||
};
|
||||
|
||||
// 注册当前的取消回调
|
||||
registerAbort(abort);
|
||||
|
||||
// 绑定绘制相关事件
|
||||
bus.onScreen(Cesium.ScreenSpaceEventType.LEFT_CLICK, handleAddPoint);
|
||||
bus.onScreen(Cesium.ScreenSpaceEventType.RIGHT_CLICK, handleCancelDraw);
|
||||
bus.onScreen(Cesium.ScreenSpaceEventType.LEFT_DOUBLE_CLICK, handleEndDraw);
|
||||
});
|
||||
};
|
|
@ -0,0 +1,54 @@
|
|||
import * as Cesium from 'cesium'
|
||||
import * as turf from '@turf/turf'
|
||||
|
||||
/**
|
||||
* 计算折线长度
|
||||
* @param {Cesium.Cartesian3[]} positions 位置列表
|
||||
* @returns 折线长度,单位米
|
||||
*/
|
||||
export const getDistance = (positions) => {
|
||||
const line = turf.lineString(positions.map((pos) => {
|
||||
const cartographic = Cesium.Cartographic.fromCartesian(pos)
|
||||
return [Cesium.Math.toDegrees(cartographic.longitude), Cesium.Math.toDegrees(cartographic.latitude)]
|
||||
}))
|
||||
return turf.length(line, { units: 'kilometers' }) * 1000 // 返回米
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算多边形面积
|
||||
* @param {Cesium.Cartesian3[]} positions 位置列表
|
||||
* @returns 多边形面积,单位平方米
|
||||
*/
|
||||
export const getArea = (positions) => {
|
||||
const pts = [...positions, positions[0]] // 闭合多边形
|
||||
const polygon = turf.polygon([pts.map((pos) => {
|
||||
const cartographic = Cesium.Cartographic.fromCartesian(pos)
|
||||
return [Cesium.Math.toDegrees(cartographic.longitude), Cesium.Math.toDegrees(cartographic.latitude)]
|
||||
})])
|
||||
return turf.area(polygon) // 返回平方米
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取多个点的中心位置,根据边界范围确定
|
||||
* @param {Cesium.Cartesian3[]} positions 位置列表
|
||||
* @param {number} height 返回位置的高度,默认为0
|
||||
* @returns 根据边界范围确定的中心点位置
|
||||
*/
|
||||
export const getBoundingCenter = (positions, height = 0) => {
|
||||
const coord = getBoundingCenterCoordinate(positions)
|
||||
return Cesium.Cartesian3.fromDegrees(coord[0], coord[1], height)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取多个点的中心位置坐标,根据边界范围确定
|
||||
* @param {Cesium.Cartesian3[]} positions 位置列表
|
||||
* @returns 根据边界范围确定的中心点位置坐标,格式为 [经度, 纬度]
|
||||
*/
|
||||
export const getBoundingCenterCoordinate = (positions) => {
|
||||
const polygon = turf.lineString(positions.map((pos) => {
|
||||
const cartographic = Cesium.Cartographic.fromCartesian(pos)
|
||||
return [Cesium.Math.toDegrees(cartographic.longitude), Cesium.Math.toDegrees(cartographic.latitude)]
|
||||
}))
|
||||
const centerFeature = turf.center(polygon)
|
||||
return centerFeature.geometry.coordinates
|
||||
}
|
Loading…
Reference in New Issue