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 } }