import * as Cesium from 'cesium' import { MapTooltip } from '@ztzf/utils' import { ToolBase } from '../ToolBase' import { buildEllipsePositions } from '../shapeUtils' 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 DrawEllipseTool extends ToolBase { constructor(viewer, options = {}) { super(viewer) this.tooltip = new MapTooltip(viewer) this.dataSource = null this.ellipseEntity = null this.centerCartesian = null this.majorPoint = null this.minorPoint = null this.semiMajor = 0 this.semiMinor = 0 this.isDrawing = false this.drawStep = 0 this.isCompleted = false this.style = resolveStyle(options?.style) } start() { this.dataSource = new Cesium.CustomDataSource('draw-ellipse') 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(movement => this.handleMouseMove(movement), Cesium.ScreenSpaceEventType.MOUSE_MOVE) this.drawStep = 0 this.isCompleted = false this.tooltip.hide() } handleLeftClick(click) { if (this.isCompleted) return const position = this.getPositionFromScreen(click.position) if (!position) return if (this.drawStep === 0) { this.centerCartesian = position this.semiMajor = 1 this.semiMinor = 1 this.isDrawing = true this.drawStep = 1 this.createEntities() this.tooltip.show('单击设置长轴', click.position) return } if (this.drawStep === 1) { this.majorPoint = position this.semiMajor = Math.max(1, this.getSurfaceDistance(this.centerCartesian, this.majorPoint)) this.semiMinor = this.semiMajor this.drawStep = 2 this.tooltip.show('单击设置短轴', click.position) return } if (this.drawStep === 2) { this.minorPoint = position const minorRadius = Math.max(1, this.getSurfaceDistance(this.centerCartesian, this.minorPoint)) this.semiMinor = Math.min(minorRadius, this.semiMajor) this.isDrawing = false this.drawStep = 0 this.isCompleted = true const positions = buildEllipsePositions(this.centerCartesian, this.semiMajor, this.semiMinor) this.notify('getPolygonPositions', { positions, meta: { center: this.centerCartesian, majorPoint: this.majorPoint, minorPoint: this.minorPoint, semiMajor: this.semiMajor, semiMinor: this.semiMinor, }, }) this.tooltip.hide() this.clearPreviewEntities() } } handleMouseMove(movement) { if (this.isCompleted) return const tipText = this.drawStep === 0 ? '单击设置中心点' : this.drawStep === 1 ? '单击设置长轴' : '单击设置短轴' if (!this.tooltip?.isVisible) { this.tooltip.show(tipText, movement.endPosition) } else { this.tooltip.show(tipText) this.tooltip.move(movement.endPosition) } if (!this.isDrawing) return const position = this.getPositionFromScreen(movement.endPosition) if (!position) return if (this.drawStep === 1) { const radius = Math.max(1, this.getSurfaceDistance(this.centerCartesian, position)) this.semiMajor = radius this.semiMinor = radius return } if (this.drawStep === 2) { const minorRadius = Math.max(1, this.getSurfaceDistance(this.centerCartesian, position)) this.semiMinor = Math.min(minorRadius, this.semiMajor) } } createEntities() { if (this.ellipseEntity) return this.ellipseEntity = this.dataSource.entities.add({ position: new Cesium.CallbackProperty(() => this.centerCartesian, false), ellipse: { semiMajorAxis: new Cesium.CallbackProperty(() => this.semiMajor, false), semiMinorAxis: new Cesium.CallbackProperty(() => this.semiMinor, false), material: this.style.fill, outline: true, outlineColor: this.style.outline, outlineWidth: 2, heightReference: Cesium.HeightReference.CLAMP_TO_GROUND, }, }) } getSurfaceDistance(startCartesian, endCartesian) { const start = Cesium.Cartographic.fromCartesian(startCartesian) const end = Cesium.Cartographic.fromCartesian(endCartesian) const geodesic = new Cesium.EllipsoidGeodesic(start, end) return geodesic.surfaceDistance } clearPreviewEntities() { if (!this.dataSource) return this.dataSource.entities.removeAll() this.ellipseEntity = 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) } setStyle(style) { this.style = resolveStyle(style) if (this.ellipseEntity?.ellipse) { this.ellipseEntity.ellipse.material = this.style.fill this.ellipseEntity.ellipse.outlineColor = this.style.outline } } 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.MOUSE_MOVE) this.handler.destroy() this.handler = null } this.tooltip?.destroy() this.tooltip = null this.centerCartesian = null this.majorPoint = null this.minorPoint = null this.isDrawing = false this.drawStep = 0 this.isCompleted = false } }