import * as Cesium from 'cesium' import { MapTooltip } from '@ztzf/utils' import { ToolBase } from '../ToolBase' import { buildEllipsePositions, formatBufferRadius, getBufferRadiusLabelStyle, getBufferRadiusPoint, } from '../shapeUtils' export class DrawBufferCircleTool extends ToolBase { constructor(viewer) { super(viewer) this.tooltip = new MapTooltip(viewer) this.dataSource = null this.centerCartesian = null this.radiusLevel1 = 0 this.radiusLevel2 = 0 this.radiusLevel3 = 0 this.circleLevel1Entity = null this.circleLevel2Entity = null this.circleLevel3Entity = null this.circleLevel1OutlineEntity = null this.circleLevel2OutlineEntity = null this.circleLevel3OutlineEntity = null this.radiusPoint1 = null this.radiusPoint2 = null this.radiusPoint3 = null this.radiusLevel1LabelEntity = null this.radiusLevel2LabelEntity = null this.radiusLevel3LabelEntity = null this.isDrawing = false this.drawStep = 0 this.isCompleted = false } start() { this.dataSource = new Cesium.CustomDataSource('draw-buffer-circle') 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.radiusLevel1 = 1 this.radiusLevel2 = 1 this.radiusLevel3 = 1 this.isDrawing = true this.drawStep = 1 this.createEntities() this.tooltip.show('单击设置一级缓冲圆', click.position) return } if (this.drawStep === 1) { const radius = Math.max(1, this.getSurfaceDistance(this.centerCartesian, position)) this.radiusLevel1 = radius this.radiusLevel2 = radius this.radiusLevel3 = radius this.radiusPoint1 = position this.drawStep = 2 this.tooltip.show('单击设置二级缓冲圆', click.position) return } if (this.drawStep === 2) { const radius = Math.max(1, this.getSurfaceDistance(this.centerCartesian, position)) this.radiusLevel2 = Math.max(radius, this.radiusLevel1) this.radiusLevel3 = this.radiusLevel2 this.radiusPoint2 = position this.drawStep = 3 this.tooltip.show('单击设置三级缓冲圆', click.position) return } if (this.drawStep === 3) { const radius = Math.max(1, this.getSurfaceDistance(this.centerCartesian, position)) this.radiusLevel3 = Math.max(radius, this.radiusLevel2) this.radiusPoint3 = position this.isDrawing = false this.drawStep = 0 this.isCompleted = true const positions = buildEllipsePositions(this.centerCartesian, this.radiusLevel3, this.radiusLevel3) this.notify('getPolygonPositions', { positions, meta: { center: this.centerCartesian, bufferRadii: [this.radiusLevel1, this.radiusLevel2, this.radiusLevel3], controlPoints: [this.centerCartesian, this.radiusPoint1, this.radiusPoint2, this.radiusPoint3].filter( Boolean ), }, }) this.tooltip.hide() this.clearPreviewEntities() } } handleMouseMove(movement) { if (this.isCompleted) return const tipText = this.drawStep === 0 ? '单击设置中心点' : this.drawStep === 1 ? '单击设置一级缓冲圆' : this.drawStep === 2 ? '单击设置二级缓冲圆' : '单击设置三级缓冲圆' 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.radiusLevel1 = radius this.radiusLevel2 = radius this.radiusLevel3 = radius return } if (this.drawStep === 2) { const radius = Math.max(1, this.getSurfaceDistance(this.centerCartesian, position)) this.radiusLevel2 = Math.max(radius, this.radiusLevel1) this.radiusLevel3 = this.radiusLevel2 return } if (this.drawStep === 3) { const radius = Math.max(1, this.getSurfaceDistance(this.centerCartesian, position)) this.radiusLevel3 = Math.max(radius, this.radiusLevel2) } } createEntities() { if (this.circleLevel1Entity || this.circleLevel2Entity || this.circleLevel3Entity) return this.circleLevel1Entity = this.dataSource.entities.add({ position: new Cesium.CallbackProperty(() => this.centerCartesian, false), ellipse: { semiMajorAxis: new Cesium.CallbackProperty(() => this.radiusLevel1, false), semiMinorAxis: new Cesium.CallbackProperty(() => this.radiusLevel1, false), material: Cesium.Color.fromBytes(247, 20, 20, 128), outline: false, heightReference: Cesium.HeightReference.CLAMP_TO_GROUND, }, }) this.circleLevel1OutlineEntity = this.dataSource.entities.add({ polyline: { positions: new Cesium.CallbackProperty(() => { if (!this.centerCartesian || this.radiusLevel1 <= 0) return [] const positions = buildEllipsePositions(this.centerCartesian, this.radiusLevel1, this.radiusLevel1) return positions.length ? [...positions, positions[0]] : positions }, false), clampToGround: true, width: 2, material: Cesium.Color.fromBytes(255, 0, 0, 255), }, }) this.circleLevel2Entity = this.dataSource.entities.add({ position: new Cesium.CallbackProperty(() => this.centerCartesian, false), ellipse: { semiMajorAxis: new Cesium.CallbackProperty(() => this.radiusLevel2, false), semiMinorAxis: new Cesium.CallbackProperty(() => this.radiusLevel2, false), material: Cesium.Color.fromBytes(255, 235, 59, 128), outline: false, heightReference: Cesium.HeightReference.CLAMP_TO_GROUND, }, }) this.circleLevel2OutlineEntity = this.dataSource.entities.add({ polyline: { positions: new Cesium.CallbackProperty(() => { if (!this.centerCartesian || this.radiusLevel2 <= 0) return [] const positions = buildEllipsePositions(this.centerCartesian, this.radiusLevel2, this.radiusLevel2) return positions.length ? [...positions, positions[0]] : positions }, false), clampToGround: true, width: 2, material: Cesium.Color.fromBytes(255, 235, 59, 255), }, }) this.circleLevel3Entity = this.dataSource.entities.add({ position: new Cesium.CallbackProperty(() => this.centerCartesian, false), ellipse: { semiMajorAxis: new Cesium.CallbackProperty(() => this.radiusLevel3, false), semiMinorAxis: new Cesium.CallbackProperty(() => this.radiusLevel3, false), material: Cesium.Color.fromBytes(25, 178, 230, 128), outline: false, heightReference: Cesium.HeightReference.CLAMP_TO_GROUND, }, }) this.circleLevel3OutlineEntity = this.dataSource.entities.add({ polyline: { positions: new Cesium.CallbackProperty(() => { if (!this.centerCartesian || this.radiusLevel3 <= 0) return [] const positions = buildEllipsePositions(this.centerCartesian, this.radiusLevel3, this.radiusLevel3) return positions.length ? [...positions, positions[0]] : positions }, false), clampToGround: true, width: 2, material: Cesium.Color.fromBytes(0, 251, 255, 255), }, }) this.radiusLevel1LabelEntity = this.createRadiusLabelEntity(() => this.radiusLevel1) this.radiusLevel2LabelEntity = this.createRadiusLabelEntity(() => this.radiusLevel2) this.radiusLevel3LabelEntity = this.createRadiusLabelEntity(() => this.radiusLevel3) } clearPreviewEntities() { if (!this.dataSource) return this.dataSource.entities.removeAll() this.circleLevel1Entity = null this.circleLevel2Entity = null this.circleLevel3Entity = null this.circleLevel1OutlineEntity = null this.circleLevel2OutlineEntity = null this.circleLevel3OutlineEntity = null this.radiusLevel1LabelEntity = null this.radiusLevel2LabelEntity = null this.radiusLevel3LabelEntity = null } 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 } getPositionFromScreen(screenPosition) { const scene = this.viewer.scene const ray = scene.camera.getPickRay(screenPosition) const cartesian = ray ? scene.globe.pick(ray, scene) : null if (cartesian) return cartesian return scene.camera.pickEllipsoid(screenPosition, scene.globe.ellipsoid) } getRadiusPoint(radius) { return getBufferRadiusPoint(this.centerCartesian, radius) } createRadiusLabelEntity(getRadius) { return this.dataSource.entities.add({ position: new Cesium.CallbackProperty( () => getBufferRadiusPoint(this.centerCartesian, getRadius()), false ), label: { ...getBufferRadiusLabelStyle(), text: new Cesium.CallbackProperty(() => formatBufferRadius(getRadius()), false), }, }) } 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.radiusLevel1 = 0 this.radiusLevel2 = 0 this.radiusLevel3 = 0 this.radiusPoint1 = null this.radiusPoint2 = null this.radiusPoint3 = null this.radiusLevel1LabelEntity = null this.radiusLevel2LabelEntity = null this.radiusLevel3LabelEntity = null this.isDrawing = false this.drawStep = 0 this.isCompleted = false } }