import * as Cesium from 'cesium'
|
import { MapTooltip } from '@ztzf/utils'
|
import { ToolBase } from '../ToolBase'
|
|
const POINT_ENTITY_NAME = 'draw-polygon-point'
|
const DEFAULT_STYLE = {
|
fill: Cesium.Color.fromBytes(45, 140, 240, 99),
|
outline: Cesium.Color.fromBytes(45, 140, 240, 255),
|
}
|
const resolveStyle = style => ({
|
fill: style?.fill || DEFAULT_STYLE.fill,
|
outline: style?.outline || DEFAULT_STYLE.outline,
|
})
|
|
export class DrawPolygonTool extends ToolBase {
|
constructor(viewer, options = {}) {
|
super(viewer)
|
this.tooltip = new MapTooltip(viewer)
|
this.dataSource = null
|
this.polygonEntity = null
|
this.polylineEntity = null
|
this.positions = []
|
this.floatPosition = null
|
this.isDrawing = false
|
this.lastMousePosition = null
|
this.style = resolveStyle(options?.style)
|
}
|
|
start() {
|
this.dataSource = new Cesium.CustomDataSource('draw-polygon')
|
this.viewer.dataSources.add(this.dataSource)
|
this.handler = new Cesium.ScreenSpaceEventHandler(this.viewer.scene.canvas)
|
this.handler.setInputAction(click => this.handleLeftClick(click), Cesium.ScreenSpaceEventType.LEFT_CLICK)
|
this.handler.setInputAction(click => this.handleRightClick(click), Cesium.ScreenSpaceEventType.RIGHT_CLICK)
|
this.handler.setInputAction(movement => this.handleMouseMove(movement), Cesium.ScreenSpaceEventType.MOUSE_MOVE)
|
this.isDrawing = true
|
this.tooltip.hide()
|
}
|
|
handleLeftClick(click) {
|
if (!this.isDrawing) return
|
|
const pickedEntity = this.viewer.scene.pick(click.position)?.id
|
if (this.isFinishTrigger(pickedEntity)) {
|
this.finishDrawing()
|
return
|
}
|
if (pickedEntity?.name === POINT_ENTITY_NAME) return
|
|
const position = this.getPositionFromScreen(click.position)
|
if (!position) return
|
|
this.addPoint(position)
|
this.tooltip.show(this.getTipText(), click.position)
|
}
|
|
handleRightClick(click) {
|
if (!this.isDrawing) return
|
const pickedEntity = this.viewer.scene.pick(click.position)?.id
|
if (!pickedEntity || pickedEntity.name !== POINT_ENTITY_NAME) return
|
const index = pickedEntity.customData?.index
|
if (typeof index !== 'number') return
|
this.removePoint(index)
|
this.tooltip.show(this.getTipText(), click.position)
|
}
|
|
handleMouseMove(movement) {
|
if (!this.isDrawing) return
|
this.tooltip.show(this.getTipText(), movement.endPosition)
|
if (this.positions.length === 0) return
|
const position = this.getPositionFromScreen(movement.endPosition)
|
if (!position) return
|
this.lastMousePosition = position
|
this.floatPosition = position
|
}
|
|
getTipText() {
|
const count = this.positions.length
|
if (count === 0) {
|
return '单击增加点'
|
}
|
if (count < 3) {
|
return '单击增加点,右击删除点'
|
}
|
return '单击增加点,右击删除点,双击结束绘制'
|
}
|
|
isFinishTrigger(pickedEntity) {
|
if (!pickedEntity || pickedEntity.name !== POINT_ENTITY_NAME) return false
|
if (this.positions.length < 3) return false
|
const index = pickedEntity.customData?.index
|
return index === this.positions.length - 1
|
}
|
|
addPoint(position) {
|
this.positions.push(position)
|
if (!this.polygonEntity) {
|
this.createEntities()
|
}
|
this.rebuildPointEntities()
|
}
|
|
removePoint(index) {
|
if (index < 0 || index >= this.positions.length) return
|
this.positions.splice(index, 1)
|
this.rebuildPointEntities()
|
if (this.positions.length === 0) {
|
this.floatPosition = null
|
this.lastMousePosition = null
|
return
|
}
|
if (this.lastMousePosition) {
|
this.floatPosition = this.lastMousePosition
|
} else {
|
this.floatPosition = this.positions[this.positions.length - 1]
|
}
|
}
|
|
createEntities() {
|
const getPreviewPositions = () => {
|
if (this.positions.length === 0) return []
|
if (!this.isDrawing || !this.floatPosition) return this.positions
|
return [...this.positions, this.floatPosition]
|
}
|
|
this.polygonEntity = this.dataSource.entities.add({
|
polygon: {
|
hierarchy: new Cesium.CallbackProperty(() => new Cesium.PolygonHierarchy(getPreviewPositions()), false),
|
material: this.style.fill,
|
outline: false,
|
heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
|
show: new Cesium.CallbackProperty(() => getPreviewPositions().length >= 3, false),
|
},
|
})
|
|
this.polylineEntity = this.dataSource.entities.add({
|
polyline: {
|
positions: new Cesium.CallbackProperty(() => {
|
const positions = getPreviewPositions()
|
if (positions.length < 2) return positions
|
if (positions.length >= 3) {
|
return [...positions, positions[0]]
|
}
|
return positions
|
}, false),
|
clampToGround: true,
|
width: 2,
|
material: this.style.outline,
|
show: new Cesium.CallbackProperty(() => getPreviewPositions().length >= 2, false),
|
},
|
})
|
}
|
|
rebuildPointEntities() {
|
this.dataSource.entities.values
|
.slice()
|
.filter(entity => entity?.name === POINT_ENTITY_NAME)
|
.forEach(entity => this.dataSource.entities.remove(entity))
|
|
this.positions.forEach((position, index) => {
|
this.dataSource.entities.add({
|
name: POINT_ENTITY_NAME,
|
position: position.clone ? position.clone() : position,
|
point: {
|
pixelSize: 12,
|
color: Cesium.Color.WHITE,
|
outlineColor: this.style.outline,
|
outlineWidth: 2,
|
heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
|
disableDepthTestDistance: Number.POSITIVE_INFINITY,
|
},
|
customData: {
|
index,
|
},
|
})
|
})
|
}
|
|
finishDrawing() {
|
if (this.positions.length < 3) return
|
this.isDrawing = false
|
this.floatPosition = null
|
this.notify('getPolygonPositions', this.positions)
|
this.tooltip.hide()
|
this.clearPreviewEntities()
|
}
|
|
setStyle(style) {
|
this.style = resolveStyle(style)
|
if (this.polygonEntity?.polygon) {
|
this.polygonEntity.polygon.material = this.style.fill
|
}
|
if (this.polylineEntity?.polyline) {
|
this.polylineEntity.polyline.material = this.style.outline
|
}
|
if (this.dataSource) {
|
this.dataSource.entities.values
|
.filter(entity => entity?.name === POINT_ENTITY_NAME)
|
.forEach(entity => {
|
if (entity?.point) {
|
entity.point.outlineColor = this.style.outline
|
}
|
})
|
}
|
}
|
|
clearPreviewEntities() {
|
if (!this.dataSource) return
|
this.dataSource.entities.removeAll()
|
this.polygonEntity = null
|
this.polylineEntity = null
|
}
|
|
getPositionFromScreen(screenPosition) {
|
const scene = this.viewer.scene
|
const cartesian = scene.pickPosition(screenPosition)
|
if (cartesian) return cartesian
|
return scene.camera.pickEllipsoid(screenPosition, scene.globe.ellipsoid)
|
}
|
|
destroy() {
|
if (this.dataSource) {
|
this.dataSource.entities.removeAll()
|
this.viewer.dataSources.remove(this.dataSource)
|
this.dataSource = null
|
}
|
if (this.handler) {
|
this.handler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_CLICK)
|
this.handler.removeInputAction(Cesium.ScreenSpaceEventType.RIGHT_CLICK)
|
this.handler.removeInputAction(Cesium.ScreenSpaceEventType.MOUSE_MOVE)
|
this.handler.destroy()
|
this.handler = null
|
}
|
this.tooltip?.destroy()
|
this.tooltip = null
|
this.positions = []
|
this.floatPosition = null
|
this.isDrawing = false
|
this.lastMousePosition = null
|
}
|
}
|