| 
									
										
										
										
											2025-10-11 16:10:41 +08:00
										 |  |  |  | // src/utils/cesium/DrawingTool.ts
 | 
					
						
							|  |  |  |  | import * as Cesium from 'cesium'; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | export interface DrawingOptions { | 
					
						
							|  |  |  |  |   color?: Cesium.Color; | 
					
						
							|  |  |  |  |   outlineColor?: Cesium.Color; | 
					
						
							|  |  |  |  |   outlineWidth?: number; | 
					
						
							|  |  |  |  |   fill?: boolean; | 
					
						
							|  |  |  |  |   classificationType?: Cesium.ClassificationType; | 
					
						
							|  |  |  |  |   height?: number; | 
					
						
							|  |  |  |  |   extrudedHeight?: number; | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | export interface CircleOptions extends DrawingOptions { | 
					
						
							|  |  |  |  |   radius?: number; | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | export interface PolygonOptions extends DrawingOptions { | 
					
						
							|  |  |  |  |   closePath?: boolean; | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-10-17 16:50:43 +08:00
										 |  |  |  | export interface ImportOptions { | 
					
						
							|  |  |  |  |   autoZoom?: boolean; | 
					
						
							|  |  |  |  |   mergeExisting?: boolean; | 
					
						
							|  |  |  |  |   coordinateSystem?: 'wgs84' | 'gcj02' | 'bd09'; | 
					
						
							|  |  |  |  | } | 
					
						
							| 
									
										
										
										
											2025-10-11 16:10:41 +08:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | export interface DrawingInfo { | 
					
						
							|  |  |  |  |   id: string; | 
					
						
							|  |  |  |  |   type: 'circle' | 'polygon'; | 
					
						
							|  |  |  |  |   positions: Cesium.Cartesian3[]; | 
					
						
							|  |  |  |  |   center?: Cesium.Cartesian3; // 圆心(仅圆形)
 | 
					
						
							|  |  |  |  |   radius?: number; // 半径(仅圆形)
 | 
					
						
							|  |  |  |  |   bounds?: { // 边界信息(仅多边形)
 | 
					
						
							|  |  |  |  |     north: number; | 
					
						
							|  |  |  |  |     south: number; | 
					
						
							|  |  |  |  |     east: number; | 
					
						
							|  |  |  |  |     west: number; | 
					
						
							|  |  |  |  |   }; | 
					
						
							|  |  |  |  |   area: number; | 
					
						
							|  |  |  |  |   properties: any; | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | export interface DrawingResult { | 
					
						
							|  |  |  |  |   id: string; | 
					
						
							|  |  |  |  |   type: 'circle' | 'polygon'; | 
					
						
							|  |  |  |  |   positions: Cesium.Cartesian3[]; | 
					
						
							|  |  |  |  |   entity: Cesium.Entity; | 
					
						
							|  |  |  |  |   properties: any; | 
					
						
							|  |  |  |  |   info: DrawingInfo; // 新增信息字段
 | 
					
						
							|  |  |  |  | } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-10-15 17:49:29 +08:00
										 |  |  |  | import { AirspaceEditor } from './AirspaceEditor'; | 
					
						
							| 
									
										
										
										
											2025-10-24 10:16:33 +08:00
										 |  |  |  | import { map } from 'lodash-es'; | 
					
						
							|  |  |  |  | import { Position } from '@element-plus/icons-vue/dist/types/index.js'; | 
					
						
							|  |  |  |  | import { API_GET_list, API_GET_byId, API_DELETE_delPlotting, API_POST_addPlotting, API_PUT_updatePlotting,} from "../../../api/uav/plotting"; | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-10-15 17:49:29 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-10-11 16:10:41 +08:00
										 |  |  |  | export class DrawingTool { | 
					
						
							| 
									
										
										
										
											2025-10-15 17:49:29 +08:00
										 |  |  |  |   private airspaceEditor: AirspaceEditor; | 
					
						
							| 
									
										
										
										
											2025-10-11 16:10:41 +08:00
										 |  |  |  |   private viewer: Cesium.Viewer; | 
					
						
							|  |  |  |  |   private handler: Cesium.ScreenSpaceEventHandler; | 
					
						
							|  |  |  |  |   private entities: Cesium.EntityCollection; | 
					
						
							|  |  |  |  |    | 
					
						
							|  |  |  |  |   private isDrawing: boolean = false; | 
					
						
							|  |  |  |  |   private currentType: 'circle' | 'polygon' | null = null; | 
					
						
							|  |  |  |  |   private currentPositions: Cesium.Cartesian3[] = []; | 
					
						
							|  |  |  |  |   private tempEntities: Cesium.Entity[] = []; | 
					
						
							|  |  |  |  |   private drawingEntities: Map<string, DrawingResult> = new Map(); | 
					
						
							|  |  |  |  |    | 
					
						
							|  |  |  |  |   private defaultOptions: DrawingOptions = { | 
					
						
							|  |  |  |  |     color: Cesium.Color.YELLOW.withAlpha(0.3), | 
					
						
							|  |  |  |  |     outlineColor: Cesium.Color.YELLOW, | 
					
						
							|  |  |  |  |     outlineWidth: 2, | 
					
						
							|  |  |  |  |     fill: true, | 
					
						
							|  |  |  |  |     classificationType: Cesium.ClassificationType.BOTH, | 
					
						
							|  |  |  |  |     height: 0, | 
					
						
							| 
									
										
										
										
											2025-10-17 16:50:43 +08:00
										 |  |  |  |     extrudedHeight: 0 | 
					
						
							| 
									
										
										
										
											2025-10-11 16:10:41 +08:00
										 |  |  |  |   }; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   private drawingCallbacks: { | 
					
						
							|  |  |  |  |     onStart?: () => void; | 
					
						
							|  |  |  |  |     onPointAdd?: (position: Cesium.Cartesian3) => void; | 
					
						
							|  |  |  |  |     onComplete?: (result: DrawingResult) => void; | 
					
						
							|  |  |  |  |     onCancel?: () => void; | 
					
						
							|  |  |  |  |     onClick?: (result: DrawingResult, info: DrawingInfo) => void; // 新增点击回调
 | 
					
						
							|  |  |  |  |   } = {}; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   constructor(viewer: Cesium.Viewer) { | 
					
						
							|  |  |  |  |     this.viewer = viewer; | 
					
						
							|  |  |  |  |     this.handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas); | 
					
						
							|  |  |  |  |     this.entities = viewer.entities; | 
					
						
							| 
									
										
										
										
											2025-10-15 17:49:29 +08:00
										 |  |  |  |     this.airspaceEditor = new AirspaceEditor(viewer); | 
					
						
							| 
									
										
										
										
											2025-10-11 16:10:41 +08:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |     // 初始化点击事件监听
 | 
					
						
							|  |  |  |  |     this.initClickHandler(); | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     /** | 
					
						
							|  |  |  |  |    * 初始化点击事件处理器 | 
					
						
							|  |  |  |  |    */ | 
					
						
							|  |  |  |  |   private initClickHandler(): void { | 
					
						
							|  |  |  |  |       this.handler.setInputAction((event: any) => { | 
					
						
							|  |  |  |  |         this.handleEntityClick(event.position); | 
					
						
							|  |  |  |  |       }, Cesium.ScreenSpaceEventType.LEFT_CLICK); | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     /** | 
					
						
							|  |  |  |  |    * 处理实体点击事件 | 
					
						
							|  |  |  |  |    */ | 
					
						
							|  |  |  |  |   private handleEntityClick(position: Cesium.Cartesian2): void { | 
					
						
							|  |  |  |  |       const pickedObject = this.viewer.scene.pick(position); | 
					
						
							|  |  |  |  |       if (!pickedObject || !pickedObject.id) return; | 
					
						
							|  |  |  |  |    | 
					
						
							|  |  |  |  |       const clickedEntity = pickedObject.id; | 
					
						
							|  |  |  |  |       const drawingResult = this.findDrawingByEntity(clickedEntity); | 
					
						
							|  |  |  |  |        | 
					
						
							|  |  |  |  |       if (drawingResult) { | 
					
						
							|  |  |  |  |         const drawingInfo = this.generateDrawingInfo(drawingResult); | 
					
						
							|  |  |  |  |         this.drawingCallbacks.onClick?.(drawingResult, drawingInfo); | 
					
						
							|  |  |  |  |       } | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     /** | 
					
						
							|  |  |  |  |    * 根据实体查找绘图结果 | 
					
						
							|  |  |  |  |    */ | 
					
						
							|  |  |  |  |   private findDrawingByEntity(entity: Cesium.Entity): DrawingResult | undefined { | 
					
						
							|  |  |  |  |       for (const drawing of this.drawingEntities.values()) { | 
					
						
							|  |  |  |  |         if (drawing.entity === entity) { | 
					
						
							|  |  |  |  |           return drawing; | 
					
						
							|  |  |  |  |         } | 
					
						
							|  |  |  |  |       } | 
					
						
							|  |  |  |  |       return undefined; | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     /** | 
					
						
							|  |  |  |  |    * 生成绘图信息 | 
					
						
							|  |  |  |  |    */ | 
					
						
							|  |  |  |  |   private generateDrawingInfo(drawing: DrawingResult): DrawingInfo { | 
					
						
							|  |  |  |  |       if (drawing.type === 'circle') { | 
					
						
							|  |  |  |  |         return this.generateCircleInfo(drawing); | 
					
						
							|  |  |  |  |       } else { | 
					
						
							|  |  |  |  |         return this.generatePolygonInfo(drawing); | 
					
						
							|  |  |  |  |       } | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   /** | 
					
						
							|  |  |  |  |      * 生成圆形信息 | 
					
						
							|  |  |  |  |      */ | 
					
						
							|  |  |  |  |   private generateCircleInfo(drawing: DrawingResult): DrawingInfo { | 
					
						
							| 
									
										
										
										
											2025-10-24 10:16:33 +08:00
										 |  |  |  |     console.log("drawing 圆形:",drawing) | 
					
						
							|  |  |  |  |     console.log("drawing 圆形:",drawing) | 
					
						
							| 
									
										
										
										
											2025-10-11 16:10:41 +08:00
										 |  |  |  |     const center = drawing.positions[0]; | 
					
						
							|  |  |  |  |     const radius = drawing.positions.length > 1 ?  | 
					
						
							|  |  |  |  |       Cesium.Cartesian3.distance(center, drawing.positions[1]) :  | 
					
						
							|  |  |  |  |       (drawing.properties.options.radius || 1000); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     const centerCartographic = Cesium.Cartographic.fromCartesian(center); | 
					
						
							|  |  |  |  |     const centerLongitude = Cesium.Math.toDegrees(centerCartographic.longitude); | 
					
						
							|  |  |  |  |     const centerLatitude = Cesium.Math.toDegrees(centerCartographic.latitude); | 
					
						
							|  |  |  |  |     const centerHeight = centerCartographic.height; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     return { | 
					
						
							|  |  |  |  |       id: drawing.id, | 
					
						
							|  |  |  |  |       type: 'circle', | 
					
						
							|  |  |  |  |       positions: drawing.positions, | 
					
						
							|  |  |  |  |       center: center, | 
					
						
							|  |  |  |  |       radius: radius, | 
					
						
							|  |  |  |  |       area: drawing.properties.area, | 
					
						
							|  |  |  |  |       properties: { | 
					
						
							|  |  |  |  |         ...drawing.properties, | 
					
						
							|  |  |  |  |         center: { | 
					
						
							|  |  |  |  |           longitude: centerLongitude, | 
					
						
							|  |  |  |  |           latitude: centerLatitude, | 
					
						
							|  |  |  |  |           height: centerHeight | 
					
						
							|  |  |  |  |         }, | 
					
						
							|  |  |  |  |         radius: radius, | 
					
						
							|  |  |  |  |         circumference: 2 * Math.PI * radius, | 
					
						
							|  |  |  |  |         diameter: 2 * radius | 
					
						
							|  |  |  |  |       } | 
					
						
							|  |  |  |  |     }; | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   /** | 
					
						
							|  |  |  |  |      * 生成多边形信息 | 
					
						
							|  |  |  |  |      */ | 
					
						
							|  |  |  |  |   private generatePolygonInfo(drawing: DrawingResult): DrawingInfo { | 
					
						
							|  |  |  |  |     // 计算边界
 | 
					
						
							|  |  |  |  |     let minLon = Infinity, maxLon = -Infinity; | 
					
						
							|  |  |  |  |     let minLat = Infinity, maxLat = -Infinity; | 
					
						
							|  |  |  |  |     let minHeight = Infinity, maxHeight = -Infinity; | 
					
						
							| 
									
										
										
										
											2025-10-24 10:16:33 +08:00
										 |  |  |  |     console.log("DrawingResult 多边形信息:",drawing) | 
					
						
							| 
									
										
										
										
											2025-10-11 16:10:41 +08:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |     const boundaryPoints = drawing.positions.map(position => { | 
					
						
							|  |  |  |  |       const cartographic = Cesium.Cartographic.fromCartesian(position); | 
					
						
							|  |  |  |  |       const lon = Cesium.Math.toDegrees(cartographic.longitude); | 
					
						
							|  |  |  |  |       const lat = Cesium.Math.toDegrees(cartographic.latitude); | 
					
						
							|  |  |  |  |       const height = cartographic.height; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       // 更新边界
 | 
					
						
							|  |  |  |  |       minLon = Math.min(minLon, lon); | 
					
						
							|  |  |  |  |       maxLon = Math.max(maxLon, lon); | 
					
						
							|  |  |  |  |       minLat = Math.min(minLat, lat); | 
					
						
							|  |  |  |  |       maxLat = Math.max(maxLat, lat); | 
					
						
							|  |  |  |  |       minHeight = Math.min(minHeight, height); | 
					
						
							|  |  |  |  |       maxHeight = Math.max(maxHeight, height); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       return { | 
					
						
							|  |  |  |  |         longitude: lon, | 
					
						
							|  |  |  |  |         latitude: lat, | 
					
						
							|  |  |  |  |         height: height | 
					
						
							|  |  |  |  |       }; | 
					
						
							|  |  |  |  |     }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     // 计算中心点
 | 
					
						
							|  |  |  |  |     const centerLon = (minLon + maxLon) / 2; | 
					
						
							|  |  |  |  |     const centerLat = (minLat + maxLat) / 2; | 
					
						
							|  |  |  |  |     const centerHeight = (minHeight + maxHeight) / 2; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     return { | 
					
						
							|  |  |  |  |       id: drawing.id, | 
					
						
							|  |  |  |  |       type: 'polygon', | 
					
						
							|  |  |  |  |       positions: drawing.positions, | 
					
						
							|  |  |  |  |       bounds: { | 
					
						
							|  |  |  |  |         north: maxLat, | 
					
						
							|  |  |  |  |         south: minLat, | 
					
						
							|  |  |  |  |         east: maxLon, | 
					
						
							|  |  |  |  |         west: minLon | 
					
						
							|  |  |  |  |       }, | 
					
						
							|  |  |  |  |       area: drawing.properties.area, | 
					
						
							|  |  |  |  |       properties: { | 
					
						
							|  |  |  |  |         ...drawing.properties, | 
					
						
							|  |  |  |  |         boundaryPoints: boundaryPoints, | 
					
						
							|  |  |  |  |         bounds: { | 
					
						
							|  |  |  |  |           north: maxLat, | 
					
						
							|  |  |  |  |           south: minLat, | 
					
						
							|  |  |  |  |           east: maxLon, | 
					
						
							|  |  |  |  |           west: minLon | 
					
						
							|  |  |  |  |         }, | 
					
						
							|  |  |  |  |         center: { | 
					
						
							|  |  |  |  |           longitude: centerLon, | 
					
						
							|  |  |  |  |           latitude: centerLat, | 
					
						
							|  |  |  |  |           height: centerHeight | 
					
						
							|  |  |  |  |         }, | 
					
						
							|  |  |  |  |         width: this.calculateDistance(minLon, minLat, maxLon, minLat), // 东西宽度
 | 
					
						
							|  |  |  |  |         height: this.calculateDistance(minLon, minLat, minLon, maxLat), // 南北高度
 | 
					
						
							|  |  |  |  |         perimeter: this.calculatePerimeter(drawing.positions) | 
					
						
							|  |  |  |  |       } | 
					
						
							|  |  |  |  |     }; | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   /** | 
					
						
							|  |  |  |  |      * 计算两点间距离 | 
					
						
							|  |  |  |  |      */ | 
					
						
							|  |  |  |  |   private calculateDistance(lon1: number, lat1: number, lon2: number, lat2: number): number { | 
					
						
							|  |  |  |  |     const cartesian1 = Cesium.Cartesian3.fromDegrees(lon1, lat1); | 
					
						
							|  |  |  |  |     const cartesian2 = Cesium.Cartesian3.fromDegrees(lon2, lat2); | 
					
						
							|  |  |  |  |     return Cesium.Cartesian3.distance(cartesian1, cartesian2); | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   /** | 
					
						
							|  |  |  |  |    * 计算多边形周长 | 
					
						
							|  |  |  |  |    */ | 
					
						
							|  |  |  |  |   private calculatePerimeter(positions: Cesium.Cartesian3[]): number { | 
					
						
							|  |  |  |  |     let perimeter = 0; | 
					
						
							|  |  |  |  |     const n = positions.length; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     for (let i = 0; i < n; i++) { | 
					
						
							|  |  |  |  |       const j = (i + 1) % n; | 
					
						
							|  |  |  |  |       perimeter += Cesium.Cartesian3.distance(positions[i], positions[j]); | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     return perimeter; | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-10-15 17:49:29 +08:00
										 |  |  |  |     /** | 
					
						
							|  |  |  |  |    * 开始编辑空域 | 
					
						
							|  |  |  |  |    */ | 
					
						
							|  |  |  |  |     startEditing(drawingId: string): void { | 
					
						
							|  |  |  |  |       console.log('开始编辑空域:', drawingId); | 
					
						
							|  |  |  |  |        | 
					
						
							|  |  |  |  |       const drawing = this.drawingEntities.get(drawingId); | 
					
						
							|  |  |  |  |       if (!drawing) { | 
					
						
							|  |  |  |  |         console.error('未找到空域:', drawingId); | 
					
						
							|  |  |  |  |         return; | 
					
						
							|  |  |  |  |       } | 
					
						
							|  |  |  |  |    | 
					
						
							|  |  |  |  |       this.airspaceEditor.startEditing(drawing); | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  |    | 
					
						
							|  |  |  |  |     /** | 
					
						
							|  |  |  |  |      * 完成编辑 | 
					
						
							|  |  |  |  |      */ | 
					
						
							|  |  |  |  |     finishEditing(): void { | 
					
						
							|  |  |  |  |       this.airspaceEditor.finishEditing(); | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  |    | 
					
						
							|  |  |  |  |     /** | 
					
						
							|  |  |  |  |      * 取消编辑 | 
					
						
							|  |  |  |  |      */ | 
					
						
							|  |  |  |  |     cancelEditing(): void { | 
					
						
							|  |  |  |  |       this.airspaceEditor.cancelEditing(); | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  |    | 
					
						
							|  |  |  |  |     /** | 
					
						
							|  |  |  |  |      * 获取编辑状态 | 
					
						
							|  |  |  |  |      */ | 
					
						
							|  |  |  |  |     getEditingState(): { isEditing: boolean; currentDrawing: any } { | 
					
						
							|  |  |  |  |       return this.airspaceEditor.getEditingState(); | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  |    | 
					
						
							|  |  |  |  |     /** | 
					
						
							|  |  |  |  |      * 是否正在编辑 | 
					
						
							|  |  |  |  |      */ | 
					
						
							|  |  |  |  |     isEditing(): boolean { | 
					
						
							|  |  |  |  |       return this.airspaceEditor.getEditingState().isEditing; | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  |    | 
					
						
							|  |  |  |  |     /** | 
					
						
							|  |  |  |  |      * 获取当前编辑的空域 | 
					
						
							|  |  |  |  |      */ | 
					
						
							|  |  |  |  |     getCurrentEditingDrawing(): any { | 
					
						
							|  |  |  |  |       return this.airspaceEditor.getEditingState().currentDrawing; | 
					
						
							|  |  |  |  |     } | 
					
						
							| 
									
										
										
										
											2025-10-11 16:10:41 +08:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   /** | 
					
						
							|  |  |  |  |    * 开始绘制圆形 | 
					
						
							|  |  |  |  |    */ | 
					
						
							|  |  |  |  |   startDrawingCircle(options: CircleOptions = {}): void { | 
					
						
							|  |  |  |  |     this.startDrawing('circle', options); | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   /** | 
					
						
							|  |  |  |  |    * 开始绘制多边形 | 
					
						
							|  |  |  |  |    */ | 
					
						
							|  |  |  |  |   startDrawingPolygon(options: PolygonOptions = {}): void { | 
					
						
							|  |  |  |  |     this.startDrawing('polygon', options); | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   /** | 
					
						
							|  |  |  |  |    * 开始绘制 | 
					
						
							|  |  |  |  |    */ | 
					
						
							|  |  |  |  |   private startDrawing(type: 'circle' | 'polygon', options: DrawingOptions = {}): void { | 
					
						
							|  |  |  |  |     if (this.isDrawing) { | 
					
						
							|  |  |  |  |       this.cancelDrawing(); | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     this.isDrawing = true; | 
					
						
							|  |  |  |  |     this.currentType = type; | 
					
						
							|  |  |  |  |     this.currentPositions = []; | 
					
						
							|  |  |  |  |     this.tempEntities = []; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     const mergedOptions = { ...this.defaultOptions, ...options }; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     // 设置鼠标样式
 | 
					
						
							|  |  |  |  |     this.viewer.scene.canvas.style.cursor = 'crosshair'; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     // 左键点击添加点
 | 
					
						
							|  |  |  |  |     this.handler.setInputAction((event: any) => { | 
					
						
							|  |  |  |  |       this.handleLeftClick(event.position, mergedOptions); | 
					
						
							|  |  |  |  |     }, Cesium.ScreenSpaceEventType.LEFT_CLICK); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     // 鼠标移动预览
 | 
					
						
							|  |  |  |  |     this.handler.setInputAction((event: any) => { | 
					
						
							|  |  |  |  |       this.handleMouseMove(event.endPosition, mergedOptions); | 
					
						
							|  |  |  |  |     }, Cesium.ScreenSpaceEventType.MOUSE_MOVE); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     // 右键完成绘制
 | 
					
						
							|  |  |  |  |     this.handler.setInputAction(() => { | 
					
						
							|  |  |  |  |       this.completeDrawing(mergedOptions); | 
					
						
							|  |  |  |  |     }, Cesium.ScreenSpaceEventType.RIGHT_CLICK); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     // 取消绘制
 | 
					
						
							|  |  |  |  |     const cancelHandler = (event: KeyboardEvent) => { | 
					
						
							|  |  |  |  |       if (event.key === 'Escape') { | 
					
						
							|  |  |  |  |         this.cancelDrawing(); | 
					
						
							|  |  |  |  |       } | 
					
						
							|  |  |  |  |     }; | 
					
						
							|  |  |  |  |     document.addEventListener('keydown', cancelHandler); | 
					
						
							|  |  |  |  |     (this as any).cancelHandler = cancelHandler; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     this.drawingCallbacks.onStart?.(); | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   /** | 
					
						
							|  |  |  |  |    * 处理左键点击 | 
					
						
							|  |  |  |  |    */ | 
					
						
							|  |  |  |  |   private handleLeftClick(position: Cesium.Cartesian2, options: DrawingOptions): void { | 
					
						
							|  |  |  |  |     const cartesian = this.pickCoordinate(position); | 
					
						
							|  |  |  |  |     if (!cartesian) return; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     this.currentPositions.push(cartesian); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     // 添加临时点标记
 | 
					
						
							|  |  |  |  |     const pointEntity = this.entities.add({ | 
					
						
							|  |  |  |  |       position: cartesian, | 
					
						
							|  |  |  |  |       point: { | 
					
						
							|  |  |  |  |         pixelSize: 8, | 
					
						
							|  |  |  |  |         color: Cesium.Color.RED, | 
					
						
							|  |  |  |  |         outlineColor: Cesium.Color.WHITE, | 
					
						
							|  |  |  |  |         outlineWidth: 2, | 
					
						
							|  |  |  |  |         heightReference: Cesium.HeightReference.CLAMP_TO_GROUND | 
					
						
							|  |  |  |  |       } | 
					
						
							|  |  |  |  |     }); | 
					
						
							|  |  |  |  |     this.tempEntities.push(pointEntity); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     this.drawingCallbacks.onPointAdd?.(cartesian); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     // 如果是圆形,第一个点确定圆心,第二个点确定半径
 | 
					
						
							|  |  |  |  |     if (this.currentType === 'circle' && this.currentPositions.length === 2) { | 
					
						
							|  |  |  |  |       this.completeDrawing(options); | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   /** | 
					
						
							|  |  |  |  |    * 处理鼠标移动 | 
					
						
							|  |  |  |  |    */ | 
					
						
							|  |  |  |  |   private handleMouseMove(position: Cesium.Cartesian2, options: DrawingOptions): void { | 
					
						
							|  |  |  |  |     if (this.currentPositions.length === 0) return; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     const cartesian = this.pickCoordinate(position); | 
					
						
							|  |  |  |  |     if (!cartesian) return; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     // 清除之前的临时图形
 | 
					
						
							|  |  |  |  |     this.clearTempShapes(); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     if (this.currentType === 'circle') { | 
					
						
							|  |  |  |  |       this.drawTempCircle(cartesian, options); | 
					
						
							|  |  |  |  |     } else if (this.currentType === 'polygon') { | 
					
						
							|  |  |  |  |       this.drawTempPolygon(cartesian, options); | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   /** | 
					
						
							|  |  |  |  |    * 绘制临时圆形 | 
					
						
							|  |  |  |  |    */ | 
					
						
							|  |  |  |  |   private drawTempCircle(mousePosition: Cesium.Cartesian3, options: CircleOptions): void { | 
					
						
							|  |  |  |  |     const center = this.currentPositions[0]; | 
					
						
							|  |  |  |  |     const radius = Cesium.Cartesian3.distance(center, mousePosition); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     const circleEntity = this.entities.add({ | 
					
						
							|  |  |  |  |       position: center, | 
					
						
							|  |  |  |  |       ellipse: { | 
					
						
							|  |  |  |  |         semiMinorAxis: radius, | 
					
						
							|  |  |  |  |         semiMajorAxis: radius, | 
					
						
							|  |  |  |  |         material: options.color || this.defaultOptions.color, | 
					
						
							|  |  |  |  |         outline: true, | 
					
						
							|  |  |  |  |         outlineColor: options.outlineColor || this.defaultOptions.outlineColor, | 
					
						
							|  |  |  |  |         outlineWidth: options.outlineWidth || this.defaultOptions.outlineWidth, | 
					
						
							|  |  |  |  |         height: options.height || this.defaultOptions.height, | 
					
						
							|  |  |  |  |         extrudedHeight: options.extrudedHeight || this.defaultOptions.extrudedHeight, | 
					
						
							|  |  |  |  |         classificationType: options.classificationType || this.defaultOptions.classificationType | 
					
						
							|  |  |  |  |       } | 
					
						
							|  |  |  |  |     }); | 
					
						
							|  |  |  |  |     this.tempEntities.push(circleEntity); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     // 绘制半径线
 | 
					
						
							|  |  |  |  |     if (this.currentPositions.length === 1) { | 
					
						
							|  |  |  |  |       const radiusLine = this.entities.add({ | 
					
						
							|  |  |  |  |         polyline: { | 
					
						
							|  |  |  |  |           positions: [center, mousePosition], | 
					
						
							|  |  |  |  |           width: 2, | 
					
						
							|  |  |  |  |           material: Cesium.Color.WHITE.withAlpha(0.7) | 
					
						
							|  |  |  |  |         } | 
					
						
							|  |  |  |  |       }); | 
					
						
							|  |  |  |  |       this.tempEntities.push(radiusLine); | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   /** | 
					
						
							|  |  |  |  |    * 绘制临时多边形 | 
					
						
							|  |  |  |  |    */ | 
					
						
							|  |  |  |  |   private drawTempPolygon(mousePosition: Cesium.Cartesian3, options: PolygonOptions): void { | 
					
						
							|  |  |  |  |     const positions = [...this.currentPositions, mousePosition]; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     // 绘制临时多边形
 | 
					
						
							|  |  |  |  |     const polygonEntity = this.entities.add({ | 
					
						
							|  |  |  |  |       polygon: { | 
					
						
							|  |  |  |  |         hierarchy: new Cesium.PolygonHierarchy(positions), | 
					
						
							|  |  |  |  |         material: options.color || this.defaultOptions.color, | 
					
						
							|  |  |  |  |         outline: true, | 
					
						
							|  |  |  |  |         outlineColor: options.outlineColor || this.defaultOptions.outlineColor, | 
					
						
							|  |  |  |  |         outlineWidth: options.outlineWidth || this.defaultOptions.outlineWidth, | 
					
						
							|  |  |  |  |         height: options.height || this.defaultOptions.height, | 
					
						
							|  |  |  |  |         extrudedHeight: options.extrudedHeight || this.defaultOptions.extrudedHeight, | 
					
						
							|  |  |  |  |         classificationType: options.classificationType || this.defaultOptions.classificationType | 
					
						
							|  |  |  |  |       } | 
					
						
							|  |  |  |  |     }); | 
					
						
							|  |  |  |  |     this.tempEntities.push(polygonEntity); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     // 绘制临时边线
 | 
					
						
							|  |  |  |  |     if (positions.length > 1) { | 
					
						
							|  |  |  |  |       const linePositions = [...positions]; | 
					
						
							|  |  |  |  |       if (positions.length > 2) { | 
					
						
							|  |  |  |  |         linePositions.push(positions[0]); // 闭合多边形
 | 
					
						
							|  |  |  |  |       } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       const lineEntity = this.entities.add({ | 
					
						
							|  |  |  |  |         polyline: { | 
					
						
							|  |  |  |  |           positions: linePositions, | 
					
						
							|  |  |  |  |           width: 3, | 
					
						
							|  |  |  |  |           material: options.outlineColor || this.defaultOptions.outlineColor | 
					
						
							|  |  |  |  |         } | 
					
						
							|  |  |  |  |       }); | 
					
						
							|  |  |  |  |       this.tempEntities.push(lineEntity); | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   /** | 
					
						
							|  |  |  |  |    * 完成绘制 | 
					
						
							|  |  |  |  |    */ | 
					
						
							|  |  |  |  |   // private completeDrawing(options: DrawingOptions): void {
 | 
					
						
							|  |  |  |  |   //   if (this.currentPositions.length < 2) {
 | 
					
						
							|  |  |  |  |   //     this.cancelDrawing();
 | 
					
						
							|  |  |  |  |   //     return;
 | 
					
						
							|  |  |  |  |   //   }
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   //   const id = `drawing_${Date.now()}`;
 | 
					
						
							|  |  |  |  |   //   let entity: Cesium.Entity;
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   //   if (this.currentType === 'circle') {
 | 
					
						
							|  |  |  |  |   //     entity = this.createCircleEntity(id, this.currentPositions, options as CircleOptions);
 | 
					
						
							|  |  |  |  |   //   } else {
 | 
					
						
							|  |  |  |  |   //     entity = this.createPolygonEntity(id, this.currentPositions, options as PolygonOptions);
 | 
					
						
							|  |  |  |  |   //   }
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   //   const result: DrawingResult = {
 | 
					
						
							|  |  |  |  |   //     id,
 | 
					
						
							|  |  |  |  |   //     type: this.currentType!,
 | 
					
						
							|  |  |  |  |   //     positions: this.currentPositions,
 | 
					
						
							|  |  |  |  |   //     entity,
 | 
					
						
							|  |  |  |  |   //     properties: {
 | 
					
						
							|  |  |  |  |   //       area: this.calculateArea(this.currentPositions, this.currentType!),
 | 
					
						
							|  |  |  |  |   //       options
 | 
					
						
							|  |  |  |  |   //     }
 | 
					
						
							|  |  |  |  |   //   };
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   //   this.drawingEntities.set(id, result);
 | 
					
						
							|  |  |  |  |   //   this.cleanupDrawing();
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   //   this.drawingCallbacks.onComplete?.(result);
 | 
					
						
							|  |  |  |  |   // }
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |    /** | 
					
						
							|  |  |  |  |    * 完成绘制 | 
					
						
							|  |  |  |  |    */ | 
					
						
							|  |  |  |  |   private completeDrawing(options: DrawingOptions): void { | 
					
						
							|  |  |  |  |     if (this.currentPositions.length < 2) { | 
					
						
							|  |  |  |  |       this.cancelDrawing(); | 
					
						
							|  |  |  |  |       return; | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     const id = `drawing_${Date.now()}`; | 
					
						
							|  |  |  |  |     let entity: Cesium.Entity; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     if (this.currentType === 'circle') { | 
					
						
							|  |  |  |  |       entity = this.createCircleEntity(id, this.currentPositions, options as CircleOptions); | 
					
						
							|  |  |  |  |     } else { | 
					
						
							|  |  |  |  |       entity = this.createPolygonEntity(id, this.currentPositions, options as PolygonOptions); | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     // 生成绘图信息
 | 
					
						
							|  |  |  |  |     const drawingInfo = this.currentType === 'circle' ?  | 
					
						
							|  |  |  |  |       this.generateCircleInfo({ id, type: 'circle', positions: this.currentPositions, entity, properties: { options, area: 0 } } as DrawingResult) : | 
					
						
							|  |  |  |  |       this.generatePolygonInfo({ id, type: 'polygon', positions: this.currentPositions, entity, properties: { options, area: 0 } } as DrawingResult); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     // 计算面积
 | 
					
						
							|  |  |  |  |     const area = this.calculateArea(this.currentPositions, this.currentType!); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     const result: DrawingResult = { | 
					
						
							|  |  |  |  |       id, | 
					
						
							|  |  |  |  |       type: this.currentType!, | 
					
						
							|  |  |  |  |       positions: this.currentPositions, | 
					
						
							|  |  |  |  |       entity, | 
					
						
							|  |  |  |  |       properties: { | 
					
						
							|  |  |  |  |         area: area, | 
					
						
							|  |  |  |  |         options | 
					
						
							|  |  |  |  |       }, | 
					
						
							|  |  |  |  |       info: { | 
					
						
							|  |  |  |  |         ...drawingInfo, | 
					
						
							|  |  |  |  |         area: area | 
					
						
							|  |  |  |  |       } | 
					
						
							|  |  |  |  |     }; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     this.drawingEntities.set(id, result); | 
					
						
							|  |  |  |  |     this.cleanupDrawing(); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     this.drawingCallbacks.onComplete?.(result); | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   /** | 
					
						
							|  |  |  |  |    * 创建圆形实体 | 
					
						
							|  |  |  |  |    */ | 
					
						
							|  |  |  |  |   // private createCircleEntity(id: string, positions: Cesium.Cartesian3[], options: CircleOptions): Cesium.Entity {
 | 
					
						
							|  |  |  |  |   //   const center = positions[0];
 | 
					
						
							|  |  |  |  |   //   const radius = positions.length > 1 ? Cesium.Cartesian3.distance(center, positions[1]) : (options.radius || 1000);
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   //   return this.entities.add({
 | 
					
						
							|  |  |  |  |   //     id,
 | 
					
						
							|  |  |  |  |   //     position: center,
 | 
					
						
							|  |  |  |  |   //     ellipse: {
 | 
					
						
							|  |  |  |  |   //       semiMinorAxis: radius,
 | 
					
						
							|  |  |  |  |   //       semiMajorAxis: radius,
 | 
					
						
							|  |  |  |  |   //       material: options.color || this.defaultOptions.color,
 | 
					
						
							|  |  |  |  |   //       outline: true,
 | 
					
						
							|  |  |  |  |   //       outlineColor: options.outlineColor || this.defaultOptions.outlineColor,
 | 
					
						
							|  |  |  |  |   //       outlineWidth: options.outlineWidth || this.defaultOptions.outlineWidth,
 | 
					
						
							|  |  |  |  |   //       height: options.height || this.defaultOptions.height,
 | 
					
						
							|  |  |  |  |   //       extrudedHeight: options.extrudedHeight || this.defaultOptions.extrudedHeight,
 | 
					
						
							|  |  |  |  |   //       classificationType: options.classificationType || this.defaultOptions.classificationType
 | 
					
						
							|  |  |  |  |   //     },
 | 
					
						
							|  |  |  |  |   //     label: {
 | 
					
						
							|  |  |  |  |   //       text: `圆形空域\n半径: ${(radius / 1000).toFixed(2)}km`,
 | 
					
						
							|  |  |  |  |   //       font: '14pt sans-serif',
 | 
					
						
							|  |  |  |  |   //       pixelOffset: new Cesium.Cartesian2(0, -50),
 | 
					
						
							|  |  |  |  |   //       fillColor: Cesium.Color.WHITE,
 | 
					
						
							|  |  |  |  |   //       outlineColor: Cesium.Color.BLACK,
 | 
					
						
							|  |  |  |  |   //       outlineWidth: 2,
 | 
					
						
							|  |  |  |  |   //       style: Cesium.LabelStyle.FILL_AND_OUTLINE,
 | 
					
						
							|  |  |  |  |   //       heightReference: Cesium.HeightReference.CLAMP_TO_GROUND
 | 
					
						
							|  |  |  |  |   //     }
 | 
					
						
							|  |  |  |  |   //   });
 | 
					
						
							|  |  |  |  |   // }
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   /** | 
					
						
							|  |  |  |  |    * 创建圆形实体 | 
					
						
							|  |  |  |  |    */ | 
					
						
							|  |  |  |  |   private createCircleEntity(id: string, positions: Cesium.Cartesian3[], options: CircleOptions): Cesium.Entity { | 
					
						
							|  |  |  |  |     const center = positions[0]; | 
					
						
							|  |  |  |  |     const radius = positions.length > 1 ? Cesium.Cartesian3.distance(center, positions[1]) : (options.radius || 1000); | 
					
						
							|  |  |  |  |     const centerCartographic = Cesium.Cartographic.fromCartesian(center); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     return this.entities.add({ | 
					
						
							|  |  |  |  |       id, | 
					
						
							|  |  |  |  |       position: center, | 
					
						
							|  |  |  |  |       ellipse: { | 
					
						
							|  |  |  |  |         semiMinorAxis: radius, | 
					
						
							|  |  |  |  |         semiMajorAxis: radius, | 
					
						
							|  |  |  |  |         material: options.color || this.defaultOptions.color, | 
					
						
							|  |  |  |  |         outline: true, | 
					
						
							|  |  |  |  |         outlineColor: options.outlineColor || this.defaultOptions.outlineColor, | 
					
						
							|  |  |  |  |         outlineWidth: options.outlineWidth || this.defaultOptions.outlineWidth, | 
					
						
							|  |  |  |  |         height: options.height || this.defaultOptions.height, | 
					
						
							|  |  |  |  |         extrudedHeight: options.extrudedHeight || this.defaultOptions.extrudedHeight, | 
					
						
							|  |  |  |  |         classificationType: options.classificationType || this.defaultOptions.classificationType | 
					
						
							|  |  |  |  |       }, | 
					
						
							|  |  |  |  |       label: { | 
					
						
							|  |  |  |  |         text: `圆形空域\n半径: ${(radius / 1000).toFixed(2)}km`, | 
					
						
							|  |  |  |  |         font: '14pt sans-serif', | 
					
						
							|  |  |  |  |         pixelOffset: new Cesium.Cartesian2(0, -50), | 
					
						
							|  |  |  |  |         fillColor: Cesium.Color.WHITE, | 
					
						
							|  |  |  |  |         outlineColor: Cesium.Color.BLACK, | 
					
						
							|  |  |  |  |         outlineWidth: 2, | 
					
						
							|  |  |  |  |         style: Cesium.LabelStyle.FILL_AND_OUTLINE, | 
					
						
							|  |  |  |  |         heightReference: Cesium.HeightReference.CLAMP_TO_GROUND | 
					
						
							|  |  |  |  |       }, | 
					
						
							|  |  |  |  |       // 添加自定义属性便于识别
 | 
					
						
							|  |  |  |  |       drawingType: 'circle', | 
					
						
							|  |  |  |  |       drawingId: id | 
					
						
							|  |  |  |  |     }); | 
					
						
							|  |  |  |  |     console.log("圆形空域:",) | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   /** | 
					
						
							|  |  |  |  |    * 创建多边形实体 | 
					
						
							|  |  |  |  |    */ | 
					
						
							|  |  |  |  |   private createPolygonEntity(id: string, positions: Cesium.Cartesian3[], options: PolygonOptions): Cesium.Entity { | 
					
						
							|  |  |  |  |     const hierarchy = new Cesium.PolygonHierarchy(positions); | 
					
						
							|  |  |  |  |     const area = this.calculateArea(positions, 'polygon'); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     return this.entities.add({ | 
					
						
							|  |  |  |  |       id, | 
					
						
							|  |  |  |  |       polygon: { | 
					
						
							|  |  |  |  |         hierarchy, | 
					
						
							|  |  |  |  |         material: options.color || this.defaultOptions.color, | 
					
						
							|  |  |  |  |         outline: true, | 
					
						
							|  |  |  |  |         outlineColor: options.outlineColor || this.defaultOptions.outlineColor, | 
					
						
							|  |  |  |  |         outlineWidth: options.outlineWidth || this.defaultOptions.outlineWidth, | 
					
						
							|  |  |  |  |         height: options.height || this.defaultOptions.height, | 
					
						
							|  |  |  |  |         extrudedHeight: options.extrudedHeight || this.defaultOptions.extrudedHeight, | 
					
						
							|  |  |  |  |         classificationType: options.classificationType || this.defaultOptions.classificationType | 
					
						
							|  |  |  |  |       }, | 
					
						
							|  |  |  |  |       label: { | 
					
						
							|  |  |  |  |         text: `多边形空域\n面积: ${(area / 1000000).toFixed(2)}km²`, | 
					
						
							|  |  |  |  |         font: '14pt sans-serif', | 
					
						
							|  |  |  |  |         pixelOffset: new Cesium.Cartesian2(0, -50), | 
					
						
							|  |  |  |  |         fillColor: Cesium.Color.WHITE, | 
					
						
							|  |  |  |  |         outlineColor: Cesium.Color.BLACK, | 
					
						
							|  |  |  |  |         outlineWidth: 2, | 
					
						
							|  |  |  |  |         style: Cesium.LabelStyle.FILL_AND_OUTLINE, | 
					
						
							|  |  |  |  |         heightReference: Cesium.HeightReference.CLAMP_TO_GROUND | 
					
						
							|  |  |  |  |       }, | 
					
						
							|  |  |  |  |       // 添加自定义属性便于识别
 | 
					
						
							|  |  |  |  |       drawingType: 'polygon', | 
					
						
							|  |  |  |  |       drawingId: id | 
					
						
							|  |  |  |  |     }); | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   /** | 
					
						
							|  |  |  |  |    * 创建多边形实体 | 
					
						
							|  |  |  |  |    */ | 
					
						
							|  |  |  |  |   // private createPolygonEntity(id: string, positions: Cesium.Cartesian3[], options: PolygonOptions): Cesium.Entity {
 | 
					
						
							|  |  |  |  |   //   const hierarchy = new Cesium.PolygonHierarchy(positions);
 | 
					
						
							|  |  |  |  |   //   const area = this.calculateArea(positions, 'polygon');
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   //   return this.entities.add({
 | 
					
						
							|  |  |  |  |   //     id,
 | 
					
						
							|  |  |  |  |   //     polygon: {
 | 
					
						
							|  |  |  |  |   //       hierarchy,
 | 
					
						
							|  |  |  |  |   //       material: options.color || this.defaultOptions.color,
 | 
					
						
							|  |  |  |  |   //       outline: true,
 | 
					
						
							|  |  |  |  |   //       outlineColor: options.outlineColor || this.defaultOptions.outlineColor,
 | 
					
						
							|  |  |  |  |   //       outlineWidth: options.outlineWidth || this.defaultOptions.outlineWidth,
 | 
					
						
							|  |  |  |  |   //       height: options.height || this.defaultOptions.height,
 | 
					
						
							|  |  |  |  |   //       extrudedHeight: options.extrudedHeight || this.defaultOptions.extrudedHeight,
 | 
					
						
							|  |  |  |  |   //       classificationType: options.classificationType || this.defaultOptions.classificationType
 | 
					
						
							|  |  |  |  |   //     },
 | 
					
						
							|  |  |  |  |   //     label: {
 | 
					
						
							|  |  |  |  |   //       text: `多边形空域\n面积: ${(area / 1000000).toFixed(2)}km²`,
 | 
					
						
							|  |  |  |  |   //       font: '14pt sans-serif',
 | 
					
						
							|  |  |  |  |   //       pixelOffset: new Cesium.Cartesian2(0, -50),
 | 
					
						
							|  |  |  |  |   //       fillColor: Cesium.Color.WHITE,
 | 
					
						
							|  |  |  |  |   //       outlineColor: Cesium.Color.BLACK,
 | 
					
						
							|  |  |  |  |   //       outlineWidth: 2,
 | 
					
						
							|  |  |  |  |   //       style: Cesium.LabelStyle.FILL_AND_OUTLINE,
 | 
					
						
							|  |  |  |  |   //       heightReference: Cesium.HeightReference.CLAMP_TO_GROUND
 | 
					
						
							|  |  |  |  |   //     }
 | 
					
						
							|  |  |  |  |   //   });
 | 
					
						
							|  |  |  |  |   // }
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-10-14 16:57:49 +08:00
										 |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-10-17 16:50:43 +08:00
										 |  |  |  |    /** | 
					
						
							|  |  |  |  |    * 导入空域数据 | 
					
						
							|  |  |  |  |    */ | 
					
						
							|  |  |  |  |    importAirspaceData(data: any[], options: ImportOptions = {}): string[] { | 
					
						
							|  |  |  |  |     console.log('=== 开始导入空域数据 ==='); | 
					
						
							|  |  |  |  |     console.log('导入数据:', data); | 
					
						
							|  |  |  |  |     console.log('导入选项:', options); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     const defaultOptions: ImportOptions = { | 
					
						
							|  |  |  |  |       autoZoom: true, | 
					
						
							|  |  |  |  |       mergeExisting: false, | 
					
						
							|  |  |  |  |       coordinateSystem: 'wgs84' | 
					
						
							|  |  |  |  |     }; | 
					
						
							|  |  |  |  |     const mergedOptions = { ...defaultOptions, ...options }; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     const importedIds: string[] = []; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     try { | 
					
						
							|  |  |  |  |       // 如果不合并现有数据,先清除所有
 | 
					
						
							|  |  |  |  |       if (!mergedOptions.mergeExisting) { | 
					
						
							|  |  |  |  |         this.clearAllDrawings(); | 
					
						
							|  |  |  |  |       } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       // 处理每个空域数据
 | 
					
						
							|  |  |  |  |       data.forEach((item, index) => { | 
					
						
							|  |  |  |  |         try { | 
					
						
							|  |  |  |  |           const entity = this.importSingleAirspace(item, mergedOptions); | 
					
						
							|  |  |  |  |           if (entity) { | 
					
						
							|  |  |  |  |             importedIds.push(item.id || `imported_${Date.now()}_${index}`); | 
					
						
							|  |  |  |  |           } | 
					
						
							|  |  |  |  |         } catch (error) { | 
					
						
							|  |  |  |  |           console.error(`导入第 ${index + 1} 个空域时发生错误:`, error); | 
					
						
							|  |  |  |  |         } | 
					
						
							|  |  |  |  |       }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       console.log(`✅ 成功导入 ${importedIds.length} 个空域`); | 
					
						
							|  |  |  |  |        | 
					
						
							|  |  |  |  |       // 自动缩放显示所有导入的空域
 | 
					
						
							|  |  |  |  |       if (mergedOptions.autoZoom && importedIds.length > 0) { | 
					
						
							|  |  |  |  |         setTimeout(() => { | 
					
						
							|  |  |  |  |           this.zoomToImportedAirspaces(importedIds); | 
					
						
							|  |  |  |  |         }, 500); | 
					
						
							|  |  |  |  |       } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       return importedIds; | 
					
						
							|  |  |  |  |     } catch (error) { | 
					
						
							|  |  |  |  |       console.error('❌ 导入空域数据时发生错误:', error); | 
					
						
							|  |  |  |  |       return []; | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   /** | 
					
						
							|  |  |  |  |    * 导入单个空域 | 
					
						
							|  |  |  |  |    */ | 
					
						
							|  |  |  |  |   private importSingleAirspace(data: any, options: ImportOptions): Cesium.Entity | null { | 
					
						
							|  |  |  |  |     try { | 
					
						
							|  |  |  |  |       // 验证数据
 | 
					
						
							|  |  |  |  |       if (!this.validateAirspaceData(data)) { | 
					
						
							|  |  |  |  |         console.error('空域数据验证失败:', data); | 
					
						
							|  |  |  |  |         return null; | 
					
						
							|  |  |  |  |       } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       const { type, coordinates, properties = {} } = data; | 
					
						
							|  |  |  |  |       const id = data.id || `imported_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       console.log(`导入空域 ${id}, 类型: ${type}`); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       let positions: Cesium.Cartesian3[]; | 
					
						
							|  |  |  |  |       let entity: Cesium.Entity; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       if (type === 'circle') { | 
					
						
							|  |  |  |  |         entity = this.importCircleAirspace(id, coordinates, properties, options); | 
					
						
							|  |  |  |  |       } else if (type === 'polygon') { | 
					
						
							|  |  |  |  |         entity = this.importPolygonAirspace(id, coordinates, properties, options); | 
					
						
							|  |  |  |  |       } else { | 
					
						
							|  |  |  |  |         console.error('不支持的空域类型:', type); | 
					
						
							|  |  |  |  |         return null; | 
					
						
							|  |  |  |  |       } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       if (entity) { | 
					
						
							|  |  |  |  |         // 生成绘图信息
 | 
					
						
							|  |  |  |  |         const drawingInfo = this.generateDrawingInfo({ | 
					
						
							|  |  |  |  |           id, | 
					
						
							|  |  |  |  |           type: type as 'circle' | 'polygon', | 
					
						
							| 
									
										
										
										
											2025-10-24 10:16:33 +08:00
										 |  |  |  |           positions: data.position, | 
					
						
							| 
									
										
										
										
											2025-10-17 16:50:43 +08:00
										 |  |  |  |           entity, | 
					
						
							|  |  |  |  |           properties: { | 
					
						
							|  |  |  |  |             ...properties, | 
					
						
							|  |  |  |  |             options: properties.options || this.defaultOptions, | 
					
						
							|  |  |  |  |             area: properties.area || 0 | 
					
						
							|  |  |  |  |           } | 
					
						
							|  |  |  |  |         } as DrawingResult); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |         // 保存到绘图实体集合
 | 
					
						
							|  |  |  |  |         const drawingResult: DrawingResult = { | 
					
						
							|  |  |  |  |           id, | 
					
						
							|  |  |  |  |           type: type as 'circle' | 'polygon', | 
					
						
							| 
									
										
										
										
											2025-10-24 10:16:33 +08:00
										 |  |  |  |           positions: data.position, | 
					
						
							| 
									
										
										
										
											2025-10-17 16:50:43 +08:00
										 |  |  |  |           entity, | 
					
						
							|  |  |  |  |           properties: { | 
					
						
							|  |  |  |  |             ...properties, | 
					
						
							|  |  |  |  |             imported: true, // 标记为导入的空域
 | 
					
						
							|  |  |  |  |             importTime: new Date().toISOString() | 
					
						
							|  |  |  |  |           }, | 
					
						
							|  |  |  |  |           info: { | 
					
						
							|  |  |  |  |             ...drawingInfo, | 
					
						
							| 
									
										
										
										
											2025-10-24 10:16:33 +08:00
										 |  |  |  |             area: properties.area || this.calculateArea(data.position, type as 'circle' | 'polygon') | 
					
						
							| 
									
										
										
										
											2025-10-17 16:50:43 +08:00
										 |  |  |  |           } | 
					
						
							|  |  |  |  |         }; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |         this.drawingEntities.set(id, drawingResult); | 
					
						
							|  |  |  |  |         console.log(`✅ 成功导入空域: ${id}`); | 
					
						
							|  |  |  |  |       } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       return entity; | 
					
						
							|  |  |  |  |     } catch (error) { | 
					
						
							|  |  |  |  |       console.error('导入单个空域时发生错误:', error); | 
					
						
							|  |  |  |  |       return null; | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   /** | 
					
						
							|  |  |  |  |    * 导入圆形空域 | 
					
						
							|  |  |  |  |    */ | 
					
						
							|  |  |  |  |   private importCircleAirspace( | 
					
						
							|  |  |  |  |     id: string,  | 
					
						
							|  |  |  |  |     coordinates: any,  | 
					
						
							|  |  |  |  |     properties: any,  | 
					
						
							|  |  |  |  |     options: ImportOptions | 
					
						
							|  |  |  |  |   ): Cesium.Entity { | 
					
						
							|  |  |  |  |     const { center, radius } = coordinates; | 
					
						
							|  |  |  |  |      | 
					
						
							|  |  |  |  |     // 坐标转换
 | 
					
						
							|  |  |  |  |     const convertedCenter = this.convertCoordinates( | 
					
						
							|  |  |  |  |       center.longitude,  | 
					
						
							|  |  |  |  |       center.latitude,  | 
					
						
							|  |  |  |  |       center.height || 0,  | 
					
						
							|  |  |  |  |       options.coordinateSystem | 
					
						
							|  |  |  |  |     ); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     const centerCartesian = Cesium.Cartesian3.fromDegrees( | 
					
						
							|  |  |  |  |       convertedCenter.longitude, | 
					
						
							|  |  |  |  |       convertedCenter.latitude, | 
					
						
							|  |  |  |  |       convertedCenter.height | 
					
						
							|  |  |  |  |     ); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     const positions = [centerCartesian]; | 
					
						
							|  |  |  |  |     if (radius > 0) { | 
					
						
							|  |  |  |  |       const edgePoint = this.calculateCircleEdgePoint(convertedCenter, radius); | 
					
						
							|  |  |  |  |       positions.push(edgePoint); | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     const entityOptions = { | 
					
						
							|  |  |  |  |       color: properties.color ? Cesium.Color.fromCssColorString(properties.color) : this.defaultOptions.color, | 
					
						
							|  |  |  |  |       outlineColor: properties.outlineColor ? Cesium.Color.fromCssColorString(properties.outlineColor) : this.defaultOptions.outlineColor, | 
					
						
							|  |  |  |  |       outlineWidth: properties.outlineWidth || this.defaultOptions.outlineWidth, | 
					
						
							|  |  |  |  |       height: properties.height || this.defaultOptions.height, | 
					
						
							|  |  |  |  |       extrudedHeight: properties.extrudedHeight || this.defaultOptions.extrudedHeight | 
					
						
							|  |  |  |  |     }; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     return this.createCircleEntity(id, positions, entityOptions); | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   /** | 
					
						
							|  |  |  |  |    * 导入多边形空域 | 
					
						
							|  |  |  |  |    */ | 
					
						
							|  |  |  |  |   private importPolygonAirspace( | 
					
						
							|  |  |  |  |     id: string,  | 
					
						
							|  |  |  |  |     coordinates: any,  | 
					
						
							|  |  |  |  |     properties: any,  | 
					
						
							|  |  |  |  |     options: ImportOptions | 
					
						
							|  |  |  |  |   ): Cesium.Entity { | 
					
						
							|  |  |  |  |     const positions = coordinates.map((coord: any) => { | 
					
						
							|  |  |  |  |       const convertedCoord = this.convertCoordinates( | 
					
						
							|  |  |  |  |         coord.longitude,  | 
					
						
							|  |  |  |  |         coord.latitude,  | 
					
						
							|  |  |  |  |         coord.height || 0,  | 
					
						
							|  |  |  |  |         options.coordinateSystem | 
					
						
							|  |  |  |  |       ); | 
					
						
							|  |  |  |  |        | 
					
						
							|  |  |  |  |       return Cesium.Cartesian3.fromDegrees( | 
					
						
							|  |  |  |  |         convertedCoord.longitude, | 
					
						
							|  |  |  |  |         convertedCoord.latitude, | 
					
						
							|  |  |  |  |         convertedCoord.height | 
					
						
							|  |  |  |  |       ); | 
					
						
							|  |  |  |  |     }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     const entityOptions = { | 
					
						
							|  |  |  |  |       color: properties.color ? Cesium.Color.fromCssColorString(properties.color) : this.defaultOptions.color, | 
					
						
							|  |  |  |  |       outlineColor: properties.outlineColor ? Cesium.Color.fromCssColorString(properties.outlineColor) : this.defaultOptions.outlineColor, | 
					
						
							|  |  |  |  |       outlineWidth: properties.outlineWidth || this.defaultOptions.outlineWidth, | 
					
						
							|  |  |  |  |       height: properties.height || this.defaultOptions.height, | 
					
						
							|  |  |  |  |       extrudedHeight: properties.extrudedHeight || this.defaultOptions.extrudedHeight | 
					
						
							|  |  |  |  |     }; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     return this.createPolygonEntity(id, positions, entityOptions); | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   /** | 
					
						
							|  |  |  |  |    * 验证空域数据 | 
					
						
							|  |  |  |  |    */ | 
					
						
							|  |  |  |  |   private validateAirspaceData(data: any): boolean { | 
					
						
							|  |  |  |  |     if (!data) { | 
					
						
							|  |  |  |  |       console.error('空域数据为空'); | 
					
						
							|  |  |  |  |       return false; | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     if (!data.type) { | 
					
						
							|  |  |  |  |       console.error('空域类型未定义'); | 
					
						
							|  |  |  |  |       return false; | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     if (!data.coordinates) { | 
					
						
							|  |  |  |  |       console.error('空域坐标未定义'); | 
					
						
							|  |  |  |  |       return false; | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     if (data.type === 'circle') { | 
					
						
							|  |  |  |  |       if (!data.coordinates.center || !data.coordinates.radius) { | 
					
						
							|  |  |  |  |         console.error('圆形空域缺少中心点或半径'); | 
					
						
							|  |  |  |  |         return false; | 
					
						
							|  |  |  |  |       } | 
					
						
							|  |  |  |  |     } else if (data.type === 'polygon') { | 
					
						
							|  |  |  |  |       if (!Array.isArray(data.coordinates) || data.coordinates.length < 3) { | 
					
						
							|  |  |  |  |         console.error('多边形空域坐标无效'); | 
					
						
							|  |  |  |  |         return false; | 
					
						
							|  |  |  |  |       } | 
					
						
							|  |  |  |  |     } else { | 
					
						
							|  |  |  |  |       console.error('不支持的空域类型:', data.type); | 
					
						
							|  |  |  |  |       return false; | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     return true; | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   /** | 
					
						
							|  |  |  |  |    * 坐标转换(支持不同坐标系) | 
					
						
							|  |  |  |  |    */ | 
					
						
							|  |  |  |  |   private convertCoordinates( | 
					
						
							|  |  |  |  |     longitude: number,  | 
					
						
							|  |  |  |  |     latitude: number,  | 
					
						
							|  |  |  |  |     height: number,  | 
					
						
							|  |  |  |  |     coordinateSystem: string = 'wgs84' | 
					
						
							|  |  |  |  |   ): { longitude: number; latitude: number; height: number } { | 
					
						
							|  |  |  |  |     // 这里可以添加坐标转换逻辑
 | 
					
						
							|  |  |  |  |     // 目前只支持WGS84,可以扩展支持GCJ02、BD09等
 | 
					
						
							|  |  |  |  |     switch (coordinateSystem) { | 
					
						
							|  |  |  |  |       case 'wgs84': | 
					
						
							|  |  |  |  |         return { longitude, latitude, height }; | 
					
						
							|  |  |  |  |       case 'gcj02': | 
					
						
							|  |  |  |  |         // 这里可以添加GCJ02到WGS84的转换
 | 
					
						
							|  |  |  |  |         console.warn('GCJ02坐标转换暂未实现,使用原始坐标'); | 
					
						
							|  |  |  |  |         return { longitude, latitude, height }; | 
					
						
							|  |  |  |  |       case 'bd09': | 
					
						
							|  |  |  |  |         // 这里可以添加BD09到WGS84的转换
 | 
					
						
							|  |  |  |  |         console.warn('BD09坐标转换暂未实现,使用原始坐标'); | 
					
						
							|  |  |  |  |         return { longitude, latitude, height }; | 
					
						
							|  |  |  |  |       default: | 
					
						
							|  |  |  |  |         console.warn(`不支持的坐标系: ${coordinateSystem},使用WGS84`); | 
					
						
							|  |  |  |  |         return { longitude, latitude, height }; | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   /** | 
					
						
							|  |  |  |  |    * 计算圆形边缘点(用于确定半径) | 
					
						
							|  |  |  |  |    */ | 
					
						
							|  |  |  |  |   private calculateCircleEdgePoint(center: any, radius: number): Cesium.Cartesian3 { | 
					
						
							|  |  |  |  |     const earthRadius = 6371000; // 地球半径(米)
 | 
					
						
							|  |  |  |  |     const angularDistance = radius / earthRadius; | 
					
						
							|  |  |  |  |      | 
					
						
							|  |  |  |  |     // 在正北方向计算边缘点
 | 
					
						
							|  |  |  |  |     const edgeLat = center.latitude + (angularDistance * 180 / Math.PI); | 
					
						
							|  |  |  |  |      | 
					
						
							|  |  |  |  |     return Cesium.Cartesian3.fromDegrees( | 
					
						
							|  |  |  |  |       center.longitude, | 
					
						
							|  |  |  |  |       edgeLat, | 
					
						
							|  |  |  |  |       center.height | 
					
						
							|  |  |  |  |     ); | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   /** | 
					
						
							|  |  |  |  |    * 缩放显示导入的空域 | 
					
						
							|  |  |  |  |    */ | 
					
						
							|  |  |  |  |   private zoomToImportedAirspaces(importedIds: string[]): void { | 
					
						
							|  |  |  |  |     try { | 
					
						
							|  |  |  |  |       const entities: Cesium.Entity[] = []; | 
					
						
							|  |  |  |  |        | 
					
						
							|  |  |  |  |       importedIds.forEach(id => { | 
					
						
							|  |  |  |  |         const drawing = this.drawingEntities.get(id); | 
					
						
							|  |  |  |  |         if (drawing) { | 
					
						
							|  |  |  |  |           entities.push(drawing.entity); | 
					
						
							|  |  |  |  |         } | 
					
						
							|  |  |  |  |       }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       if (entities.length > 0) { | 
					
						
							|  |  |  |  |         this.viewer.zoomTo(entities, new Cesium.HeadingPitchRange(0, -Math.PI/4, 0)); | 
					
						
							|  |  |  |  |         console.log('✅ 已缩放显示导入的空域'); | 
					
						
							|  |  |  |  |       } | 
					
						
							|  |  |  |  |     } catch (error) { | 
					
						
							|  |  |  |  |       console.error('缩放显示导入空域时发生错误:', error); | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   /** | 
					
						
							|  |  |  |  |    * 从GeoJSON导入空域 | 
					
						
							|  |  |  |  |    */ | 
					
						
							|  |  |  |  |   importFromGeoJSON(geoJSON: any, options: ImportOptions = {}): string[] { | 
					
						
							|  |  |  |  |     console.log('=== 从GeoJSON导入空域 ==='); | 
					
						
							|  |  |  |  |      | 
					
						
							|  |  |  |  |     try { | 
					
						
							|  |  |  |  |       if (!geoJSON || !geoJSON.features) { | 
					
						
							|  |  |  |  |         console.error('无效的GeoJSON数据'); | 
					
						
							|  |  |  |  |         return []; | 
					
						
							|  |  |  |  |       } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       const airspaceData: any[] = []; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       geoJSON.features.forEach((feature: any, index: number) => { | 
					
						
							|  |  |  |  |         try { | 
					
						
							|  |  |  |  |           const airspace = this.convertGeoJSONToAirspace(feature, index); | 
					
						
							|  |  |  |  |           if (airspace) { | 
					
						
							|  |  |  |  |             airspaceData.push(airspace); | 
					
						
							|  |  |  |  |           } | 
					
						
							|  |  |  |  |         } catch (error) { | 
					
						
							|  |  |  |  |           console.error(`转换GeoJSON要素 ${index} 时发生错误:`, error); | 
					
						
							|  |  |  |  |         } | 
					
						
							|  |  |  |  |       }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       return this.importAirspaceData(airspaceData, options); | 
					
						
							|  |  |  |  |     } catch (error) { | 
					
						
							|  |  |  |  |       console.error('导入GeoJSON时发生错误:', error); | 
					
						
							|  |  |  |  |       return []; | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   /** | 
					
						
							|  |  |  |  |    * 将GeoJSON要素转换为空域数据 | 
					
						
							|  |  |  |  |    */ | 
					
						
							|  |  |  |  |   private convertGeoJSONToAirspace(feature: any, index: number): any { | 
					
						
							|  |  |  |  |     const { geometry, properties } = feature; | 
					
						
							|  |  |  |  |      | 
					
						
							|  |  |  |  |     if (!geometry || !geometry.coordinates) { | 
					
						
							|  |  |  |  |       console.error('GeoJSON要素缺少几何数据'); | 
					
						
							|  |  |  |  |       return null; | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     const airspace: any = { | 
					
						
							|  |  |  |  |       id: feature.id || `geojson_${Date.now()}_${index}`, | 
					
						
							|  |  |  |  |       type: '', | 
					
						
							|  |  |  |  |       coordinates: null, | 
					
						
							|  |  |  |  |       properties: properties || {} | 
					
						
							|  |  |  |  |     }; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     switch (geometry.type) { | 
					
						
							|  |  |  |  |       case 'Polygon': | 
					
						
							|  |  |  |  |         airspace.type = 'polygon'; | 
					
						
							|  |  |  |  |         // GeoJSON多边形坐标是三维数组,取第一个环(外环)
 | 
					
						
							|  |  |  |  |         airspace.coordinates = geometry.coordinates[0].map((coord: number[]) => ({ | 
					
						
							|  |  |  |  |           longitude: coord[0], | 
					
						
							|  |  |  |  |           latitude: coord[1], | 
					
						
							|  |  |  |  |           height: coord[2] || 0 | 
					
						
							|  |  |  |  |         })); | 
					
						
							|  |  |  |  |         break; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       case 'Point': | 
					
						
							|  |  |  |  |         airspace.type = 'circle'; | 
					
						
							|  |  |  |  |         // 将点转换为圆形,默认半径1000米
 | 
					
						
							|  |  |  |  |         airspace.coordinates = { | 
					
						
							|  |  |  |  |           center: { | 
					
						
							|  |  |  |  |             longitude: geometry.coordinates[0], | 
					
						
							|  |  |  |  |             latitude: geometry.coordinates[1], | 
					
						
							|  |  |  |  |             height: geometry.coordinates[2] || 0 | 
					
						
							|  |  |  |  |           }, | 
					
						
							|  |  |  |  |           radius: properties?.radius || 1000 | 
					
						
							|  |  |  |  |         }; | 
					
						
							|  |  |  |  |         break; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       default: | 
					
						
							|  |  |  |  |         console.warn(`不支持的GeoJSON几何类型: ${geometry.type}`); | 
					
						
							|  |  |  |  |         return null; | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     return airspace; | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   /** | 
					
						
							|  |  |  |  |    * 从文件导入空域 | 
					
						
							|  |  |  |  |    */ | 
					
						
							|  |  |  |  |   async importFromFile(file: File, options: ImportOptions = {}): Promise<string[]> { | 
					
						
							|  |  |  |  |     return new Promise((resolve) => { | 
					
						
							|  |  |  |  |       console.log('=== 从文件导入空域 ==='); | 
					
						
							|  |  |  |  |       console.log('文件:', file); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       const reader = new FileReader(); | 
					
						
							|  |  |  |  |        | 
					
						
							|  |  |  |  |       reader.onload = (e) => { | 
					
						
							|  |  |  |  |         try { | 
					
						
							|  |  |  |  |           const content = e.target?.result as string; | 
					
						
							|  |  |  |  |           let data; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |           // 根据文件类型解析
 | 
					
						
							|  |  |  |  |           if (file.name.endsWith('.json')) { | 
					
						
							|  |  |  |  |             data = JSON.parse(content); | 
					
						
							|  |  |  |  |           } else if (file.name.endsWith('.geojson')) { | 
					
						
							|  |  |  |  |             data = JSON.parse(content); | 
					
						
							|  |  |  |  |             resolve(this.importFromGeoJSON(data, options)); | 
					
						
							|  |  |  |  |             return; | 
					
						
							|  |  |  |  |           } else { | 
					
						
							|  |  |  |  |             console.error('不支持的文件格式:', file.name); | 
					
						
							|  |  |  |  |             resolve([]); | 
					
						
							|  |  |  |  |             return; | 
					
						
							|  |  |  |  |           } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |           // 判断数据格式
 | 
					
						
							|  |  |  |  |           if (Array.isArray(data)) { | 
					
						
							|  |  |  |  |             resolve(this.importAirspaceData(data, options)); | 
					
						
							|  |  |  |  |           } else if (data.features) { | 
					
						
							|  |  |  |  |             resolve(this.importFromGeoJSON(data, options)); | 
					
						
							|  |  |  |  |           } else { | 
					
						
							|  |  |  |  |             console.error('无法识别的数据格式'); | 
					
						
							|  |  |  |  |             resolve([]); | 
					
						
							|  |  |  |  |           } | 
					
						
							|  |  |  |  |         } catch (error) { | 
					
						
							|  |  |  |  |           console.error('解析文件时发生错误:', error); | 
					
						
							|  |  |  |  |           resolve([]); | 
					
						
							|  |  |  |  |         } | 
					
						
							|  |  |  |  |       }; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       reader.onerror = () => { | 
					
						
							|  |  |  |  |         console.error('读取文件时发生错误'); | 
					
						
							|  |  |  |  |         resolve([]); | 
					
						
							|  |  |  |  |       }; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       reader.readAsText(file); | 
					
						
							|  |  |  |  |     }); | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   /** | 
					
						
							|  |  |  |  |    * 获取导入的空域统计信息 | 
					
						
							|  |  |  |  |    */ | 
					
						
							|  |  |  |  |   getImportStatistics(): { total: number; circles: number; polygons: number } { | 
					
						
							|  |  |  |  |     let circles = 0; | 
					
						
							|  |  |  |  |     let polygons = 0; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     this.drawingEntities.forEach((drawing) => { | 
					
						
							|  |  |  |  |       if (drawing.properties.imported) { | 
					
						
							|  |  |  |  |         if (drawing.type === 'circle') { | 
					
						
							|  |  |  |  |           circles++; | 
					
						
							|  |  |  |  |         } else { | 
					
						
							|  |  |  |  |           polygons++; | 
					
						
							|  |  |  |  |         } | 
					
						
							|  |  |  |  |       } | 
					
						
							|  |  |  |  |     }); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     return { | 
					
						
							|  |  |  |  |       total: circles + polygons, | 
					
						
							|  |  |  |  |       circles, | 
					
						
							|  |  |  |  |       polygons | 
					
						
							|  |  |  |  |     }; | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-10-14 16:57:49 +08:00
										 |  |  |  |    /** | 
					
						
							|  |  |  |  |    * 打印空域基本信息 | 
					
						
							|  |  |  |  |    */ | 
					
						
							|  |  |  |  |    printDrawingInfo(drawingId: string): void { | 
					
						
							|  |  |  |  |     const drawing = this.drawingEntities.get(drawingId); | 
					
						
							|  |  |  |  |     if (!drawing) { | 
					
						
							|  |  |  |  |       console.warn(`未找到ID为 ${drawingId} 的空域`); | 
					
						
							|  |  |  |  |       return; | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     const info = this.generateDrawingInfo(drawing); | 
					
						
							|  |  |  |  |     this.printFormattedInfo(info); | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   /** | 
					
						
							|  |  |  |  |    * 打印所有空域信息 | 
					
						
							|  |  |  |  |    */ | 
					
						
							|  |  |  |  |   printAllDrawingsInfo(): void { | 
					
						
							|  |  |  |  |     if (this.drawingEntities.size === 0) { | 
					
						
							|  |  |  |  |       console.log('当前没有绘制任何空域'); | 
					
						
							|  |  |  |  |       return; | 
					
						
							|  |  |  |  |     } | 
					
						
							| 
									
										
										
										
											2025-10-24 10:16:33 +08:00
										 |  |  |  |     this.getAllDrawingsInfoJSON(); | 
					
						
							| 
									
										
										
										
											2025-10-14 16:57:49 +08:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |     console.log(`=== 空域信息汇总 (共 ${this.drawingEntities.size} 个) ===`); | 
					
						
							| 
									
										
										
										
											2025-10-24 10:16:33 +08:00
										 |  |  |  |     console.log("=== 空域信息汇总", this.drawingEntities); | 
					
						
							| 
									
										
										
										
											2025-10-14 16:57:49 +08:00
										 |  |  |  |     this.drawingEntities.forEach((drawing, id) => { | 
					
						
							|  |  |  |  |       const info = this.generateDrawingInfo(drawing); | 
					
						
							|  |  |  |  |       this.printFormattedInfo(info); | 
					
						
							|  |  |  |  |       console.log('---'); // 分隔线
 | 
					
						
							|  |  |  |  |     }); | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-10-24 10:16:33 +08:00
										 |  |  |  |   // const geojson = new map
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-10-14 16:57:49 +08:00
										 |  |  |  |   /** | 
					
						
							|  |  |  |  |    * 格式化打印信息 | 
					
						
							|  |  |  |  |    */ | 
					
						
							|  |  |  |  |   private printFormattedInfo(info: DrawingInfo): void { | 
					
						
							|  |  |  |  |     const { id, type, area, properties } = info; | 
					
						
							| 
									
										
										
										
											2025-10-24 10:16:33 +08:00
										 |  |  |  |     const json = [];  | 
					
						
							| 
									
										
										
										
											2025-10-14 16:57:49 +08:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |     console.log(`🛡️ 空域ID: ${id}`); | 
					
						
							|  |  |  |  |     console.log(`📝 类型: ${type === 'circle' ? '圆形空域' : '多边形空域'}`); | 
					
						
							|  |  |  |  |     console.log(`📊 面积: ${(area / 1000000).toFixed(3)} km²`); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     if (type === 'circle') { | 
					
						
							|  |  |  |  |       const center = properties.center; | 
					
						
							|  |  |  |  |       console.log(`📍 圆心坐标:`); | 
					
						
							|  |  |  |  |       console.log(`   经度: ${center.longitude.toFixed(6)}°`); | 
					
						
							|  |  |  |  |       console.log(`   纬度: ${center.latitude.toFixed(6)}°`); | 
					
						
							|  |  |  |  |       console.log(`   高程: ${center.height.toFixed(2)} 米`); | 
					
						
							|  |  |  |  |       console.log(`📏 半径: ${(info.radius! / 1000).toFixed(3)} km`); | 
					
						
							|  |  |  |  |       console.log(`📐 直径: ${(info.radius! * 2 / 1000).toFixed(3)} km`); | 
					
						
							|  |  |  |  |       console.log(`🔄 周长: ${(properties.circumference / 1000).toFixed(3)} km`); | 
					
						
							| 
									
										
										
										
											2025-10-24 10:16:33 +08:00
										 |  |  |  |        | 
					
						
							| 
									
										
										
										
											2025-10-14 16:57:49 +08:00
										 |  |  |  |     } else { | 
					
						
							|  |  |  |  |       const center = properties.center; | 
					
						
							|  |  |  |  |       const bounds = properties.bounds; | 
					
						
							|  |  |  |  |       const boundaryPoints = properties.boundaryPoints; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       console.log(`📍 中心点坐标:`); | 
					
						
							|  |  |  |  |       console.log(`   经度: ${center.longitude.toFixed(6)}°`); | 
					
						
							|  |  |  |  |       console.log(`   纬度: ${center.latitude.toFixed(6)}°`); | 
					
						
							|  |  |  |  |       console.log(`   高程: ${center.height.toFixed(2)} 米`); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       console.log(`🗺️ 边界范围:`); | 
					
						
							|  |  |  |  |       console.log(`   北: ${bounds.north.toFixed(6)}°`); | 
					
						
							|  |  |  |  |       console.log(`   南: ${bounds.south.toFixed(6)}°`); | 
					
						
							|  |  |  |  |       console.log(`   东: ${bounds.east.toFixed(6)}°`); | 
					
						
							|  |  |  |  |       console.log(`   西: ${bounds.west.toFixed(6)}°`); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       console.log(`📏 尺寸信息:`); | 
					
						
							|  |  |  |  |       console.log(`   宽度: ${(properties.width / 1000).toFixed(3)} km`); | 
					
						
							|  |  |  |  |       console.log(`   高度: ${(properties.height / 1000).toFixed(3)} km`); | 
					
						
							|  |  |  |  |       console.log(`   周长: ${(properties.perimeter / 1000).toFixed(3)} km`); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       console.log(`📍 边界顶点 (${boundaryPoints.length}个):`); | 
					
						
							|  |  |  |  |       boundaryPoints.forEach((point, index) => { | 
					
						
							|  |  |  |  |         console.log(`   顶点${index + 1}: ${point.longitude.toFixed(6)}°, ${point.latitude.toFixed(6)}°, ${point.height.toFixed(2)}米`); | 
					
						
							|  |  |  |  |       }); | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     console.log(`🎨 样式设置:`); | 
					
						
							| 
									
										
										
										
											2025-10-24 10:16:33 +08:00
										 |  |  |  |     console.log(`   填充颜色: ${properties?.options?.color}`); | 
					
						
							|  |  |  |  |     console.log(`   边框颜色: ${properties?.options?.outlineColor}`); | 
					
						
							|  |  |  |  |     console.log(`   边框宽度: ${properties?.options?.outlineWidth}px`); | 
					
						
							|  |  |  |  |     console.log(`   拉伸高度: ${properties?.options?.extrudedHeight}米`); | 
					
						
							| 
									
										
										
										
											2025-10-14 16:57:49 +08:00
										 |  |  |  |   } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   /** | 
					
						
							|  |  |  |  |    * 获取空域信息的JSON格式 | 
					
						
							|  |  |  |  |    */ | 
					
						
							|  |  |  |  |   getDrawingInfoJSON(drawingId: string): any { | 
					
						
							|  |  |  |  |     const drawing = this.drawingEntities.get(drawingId); | 
					
						
							|  |  |  |  |     if (!drawing) return null; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     const info = this.generateDrawingInfo(drawing); | 
					
						
							| 
									
										
										
										
											2025-10-24 10:16:33 +08:00
										 |  |  |  |     return this.formatToCustomStructure(info); | 
					
						
							| 
									
										
										
										
											2025-10-14 16:57:49 +08:00
										 |  |  |  |   } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   /** | 
					
						
							|  |  |  |  |    * 获取所有空域信息的JSON格式 | 
					
						
							|  |  |  |  |    */ | 
					
						
							|  |  |  |  |   getAllDrawingsInfoJSON(): any[] { | 
					
						
							|  |  |  |  |     const result = []; | 
					
						
							|  |  |  |  |     for (const [id, drawing] of this.drawingEntities) { | 
					
						
							|  |  |  |  |       const info = this.generateDrawingInfo(drawing); | 
					
						
							| 
									
										
										
										
											2025-10-24 10:16:33 +08:00
										 |  |  |  |       result.push(this.formatToCustomStructure(info)); | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  |     console.log("result:",result); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       // 转换为格式化的 JSON 字符串
 | 
					
						
							|  |  |  |  |     const jsonString = JSON.stringify(result, null, 2); | 
					
						
							|  |  |  |  |     let fromData = { | 
					
						
							|  |  |  |  |       plottingJson: JSON.stringify(result) | 
					
						
							| 
									
										
										
										
											2025-10-14 16:57:49 +08:00
										 |  |  |  |     } | 
					
						
							| 
									
										
										
										
											2025-10-24 10:16:33 +08:00
										 |  |  |  |     console.log("result 字符串:", jsonString); | 
					
						
							|  |  |  |  |     API_POST_addPlotting(fromData).then((response) => { | 
					
						
							|  |  |  |  |       if (response.code === 200) { | 
					
						
							|  |  |  |  |           // proxy.success("保存文件成功");
 | 
					
						
							|  |  |  |  |        } else { | 
					
						
							|  |  |  |  |           // message.error(response.msg);
 | 
					
						
							|  |  |  |  |        } | 
					
						
							|  |  |  |  |        }, error => { | 
					
						
							|  |  |  |  |       //  message.success("保存文件失败");
 | 
					
						
							|  |  |  |  |      }); | 
					
						
							| 
									
										
										
										
											2025-10-14 16:57:49 +08:00
										 |  |  |  |     return result; | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   /** | 
					
						
							|  |  |  |  |    * 格式化信息为JSON | 
					
						
							|  |  |  |  |    */ | 
					
						
							|  |  |  |  |   private formatInfoToJSON(info: DrawingInfo): any { | 
					
						
							|  |  |  |  |     const jsonInfo: any = { | 
					
						
							|  |  |  |  |       id: info.id, | 
					
						
							|  |  |  |  |       type: info.type, | 
					
						
							|  |  |  |  |       area: { | 
					
						
							|  |  |  |  |         squareMeters: info.area, | 
					
						
							|  |  |  |  |         squareKilometers: info.area / 1000000 | 
					
						
							|  |  |  |  |       }, | 
					
						
							|  |  |  |  |       properties: { | 
					
						
							|  |  |  |  |         ...info.properties, | 
					
						
							|  |  |  |  |         options: { | 
					
						
							|  |  |  |  |           color: info.properties.options.color?.toCssColorString(), | 
					
						
							|  |  |  |  |           outlineColor: info.properties.options.outlineColor?.toCssColorString(), | 
					
						
							|  |  |  |  |           outlineWidth: info.properties.options.outlineWidth, | 
					
						
							|  |  |  |  |           extrudedHeight: info.properties.options.extrudedHeight | 
					
						
							|  |  |  |  |         } | 
					
						
							|  |  |  |  |       } | 
					
						
							|  |  |  |  |     }; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     // 移除循环引用
 | 
					
						
							|  |  |  |  |     delete jsonInfo.properties.boundaryPoints; | 
					
						
							|  |  |  |  |     delete jsonInfo.properties.center; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     if (info.type === 'circle') { | 
					
						
							|  |  |  |  |       jsonInfo.circle = { | 
					
						
							|  |  |  |  |         center: info.properties.center, | 
					
						
							|  |  |  |  |         radius: info.radius, | 
					
						
							|  |  |  |  |         diameter: info.radius! * 2, | 
					
						
							|  |  |  |  |         circumference: info.properties.circumference | 
					
						
							|  |  |  |  |       }; | 
					
						
							|  |  |  |  |     } else { | 
					
						
							|  |  |  |  |       jsonInfo.polygon = { | 
					
						
							|  |  |  |  |         center: info.properties.center, | 
					
						
							|  |  |  |  |         bounds: info.properties.bounds, | 
					
						
							|  |  |  |  |         dimensions: { | 
					
						
							|  |  |  |  |           width: info.properties.width, | 
					
						
							|  |  |  |  |           height: info.properties.height, | 
					
						
							|  |  |  |  |           perimeter: info.properties.perimeter | 
					
						
							|  |  |  |  |         }, | 
					
						
							|  |  |  |  |         vertexCount: info.properties.boundaryPoints.length, | 
					
						
							|  |  |  |  |         vertices: info.properties.boundaryPoints | 
					
						
							|  |  |  |  |       }; | 
					
						
							|  |  |  |  |     } | 
					
						
							| 
									
										
										
										
											2025-10-24 10:16:33 +08:00
										 |  |  |  |     console.log("jsonInfo:",jsonInfo) | 
					
						
							| 
									
										
										
										
											2025-10-14 16:57:49 +08:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |     return jsonInfo; | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-10-24 10:16:33 +08:00
										 |  |  |  |   /** | 
					
						
							|  |  |  |  |    * 格式化为空域自定义结构 | 
					
						
							|  |  |  |  |    */ | 
					
						
							|  |  |  |  |   private formatToCustomStructure(info: DrawingInfo): any { | 
					
						
							|  |  |  |  |     console.log("格式化为空域自定义结构:",info) | 
					
						
							|  |  |  |  |     const baseStructure: any = { | 
					
						
							|  |  |  |  |       id: info.id, | 
					
						
							|  |  |  |  |       type: info.type, | 
					
						
							|  |  |  |  |       position: info.positions, | 
					
						
							|  |  |  |  |       properties: { | 
					
						
							|  |  |  |  |         name: `${info.type === 'circle' ? '圆形' : '多边形'}空域_${info.id.slice(0, 8)}`, | 
					
						
							|  |  |  |  |         color: this.colorToHex(info.properties?.options?.color), | 
					
						
							|  |  |  |  |         outlineColor: this.colorToHex(info.properties?.options?.outlineColor), | 
					
						
							|  |  |  |  |         outlineWidth: info.properties?.options?.outlineWidth, | 
					
						
							|  |  |  |  |         extrudedHeight: info.properties?.options?.extrudedHeight, | 
					
						
							|  |  |  |  |         area: info.area, | 
					
						
							|  |  |  |  |         areaKm: info.area / 1000000 | 
					
						
							|  |  |  |  |       } | 
					
						
							|  |  |  |  |     }; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     if (info.type === 'circle') { | 
					
						
							|  |  |  |  |       baseStructure.coordinates = { | 
					
						
							|  |  |  |  |         center: { | 
					
						
							|  |  |  |  |           longitude: info.properties.center.longitude, | 
					
						
							|  |  |  |  |           latitude: info.properties.center.latitude, | 
					
						
							|  |  |  |  |           height: info.properties.center.height | 
					
						
							|  |  |  |  |         }, | 
					
						
							|  |  |  |  |         radius: info.radius | 
					
						
							|  |  |  |  |       }; | 
					
						
							|  |  |  |  |        | 
					
						
							|  |  |  |  |       // 为圆形添加额外属性
 | 
					
						
							|  |  |  |  |       baseStructure.properties.circumference = info.properties.circumference; | 
					
						
							|  |  |  |  |       baseStructure.properties.diameter = info.radius! * 2; | 
					
						
							|  |  |  |  |     } else { | 
					
						
							|  |  |  |  |       baseStructure.coordinates = info.properties.boundaryPoints.map((point: any) => ({ | 
					
						
							|  |  |  |  |         longitude: point.longitude, | 
					
						
							|  |  |  |  |         latitude: point.latitude, | 
					
						
							|  |  |  |  |         height: point.height | 
					
						
							|  |  |  |  |       })); | 
					
						
							|  |  |  |  |        | 
					
						
							|  |  |  |  |       // 为多边形添加额外属性
 | 
					
						
							|  |  |  |  |       baseStructure.properties.vertexCount = info.properties.boundaryPoints.length; | 
					
						
							|  |  |  |  |       baseStructure.properties.perimeter = info.properties.perimeter; | 
					
						
							|  |  |  |  |       baseStructure.properties.bounds = info.properties.bounds; | 
					
						
							|  |  |  |  |       baseStructure.properties.center = info.properties.center; | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  |     console.log("baseStructure:",baseStructure) | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     return baseStructure; | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   /** | 
					
						
							|  |  |  |  |    * 将颜色转换为十六进制格式 | 
					
						
							|  |  |  |  |    */ | 
					
						
							|  |  |  |  |   private colorToHex(color: any): string { | 
					
						
							|  |  |  |  |     if (!color) return '#FFFF004D'; | 
					
						
							|  |  |  |  |      | 
					
						
							|  |  |  |  |     try { | 
					
						
							|  |  |  |  |       if (typeof color.toCssColorString === 'function') { | 
					
						
							|  |  |  |  |         return color.toCssColorString(); | 
					
						
							|  |  |  |  |       } | 
					
						
							|  |  |  |  |        | 
					
						
							|  |  |  |  |       // 如果是 Cesium.Color 对象
 | 
					
						
							|  |  |  |  |       if (color.red !== undefined) { | 
					
						
							|  |  |  |  |         const r = Math.floor(color.red * 255); | 
					
						
							|  |  |  |  |         const g = Math.floor(color.green * 255); | 
					
						
							|  |  |  |  |         const b = Math.floor(color.blue * 255); | 
					
						
							|  |  |  |  |         return `#${r.toString(16).padStart(2, '0')}${g.toString(16).padStart(2, '0')}${b.toString(16).padStart(2, '0')}`; | 
					
						
							|  |  |  |  |       } | 
					
						
							|  |  |  |  |        | 
					
						
							|  |  |  |  |       return '#FFFF004D'; | 
					
						
							|  |  |  |  |     } catch (error) { | 
					
						
							|  |  |  |  |       console.warn('颜色转换失败:', error); | 
					
						
							|  |  |  |  |       return '#FFFF004D'; | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-10-14 16:57:49 +08:00
										 |  |  |  |   /** | 
					
						
							|  |  |  |  |    * 导出空域信息为文本 | 
					
						
							|  |  |  |  |    */ | 
					
						
							|  |  |  |  |   exportDrawingInfoAsText(drawingId: string): string { | 
					
						
							|  |  |  |  |     const drawing = this.drawingEntities.get(drawingId); | 
					
						
							|  |  |  |  |     if (!drawing) return ''; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     const info = this.generateDrawingInfo(drawing); | 
					
						
							| 
									
										
										
										
											2025-10-24 10:16:33 +08:00
										 |  |  |  |     console.log("导出的空域json形式是什么样的呢:",info) | 
					
						
							| 
									
										
										
										
											2025-10-14 16:57:49 +08:00
										 |  |  |  |     return this.formatInfoAsText(info); | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   /** | 
					
						
							|  |  |  |  |    * 格式化信息为文本 | 
					
						
							|  |  |  |  |    */ | 
					
						
							|  |  |  |  |   private formatInfoAsText(info: DrawingInfo): string { | 
					
						
							|  |  |  |  |     let text = ''; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     if (info.type === 'circle') { | 
					
						
							|  |  |  |  |       const center = info.properties.center; | 
					
						
							|  |  |  |  |       text = `圆形空域信息
 | 
					
						
							|  |  |  |  |     空域ID: ${info.id} | 
					
						
							|  |  |  |  |     圆心坐标: 经度${center.longitude.toFixed(6)}°, 纬度${center.latitude.toFixed(6)}°, 高程${center.height.toFixed(2)}米 | 
					
						
							|  |  |  |  |     半径: ${(info.radius! / 1000).toFixed(3)} km | 
					
						
							|  |  |  |  |     直径: ${(info.radius! * 2 / 1000).toFixed(3)} km | 
					
						
							|  |  |  |  |     周长: ${(info.properties.circumference / 1000).toFixed(3)} km | 
					
						
							|  |  |  |  |     面积: ${(info.area / 1000000).toFixed(3)} km²`;
 | 
					
						
							|  |  |  |  |         } else { | 
					
						
							|  |  |  |  |           const center = info.properties.center; | 
					
						
							|  |  |  |  |           const bounds = info.properties.bounds; | 
					
						
							|  |  |  |  |           text = `多边形空域信息
 | 
					
						
							|  |  |  |  |     空域ID: ${info.id} | 
					
						
							|  |  |  |  |     中心点: 经度${center.longitude.toFixed(6)}°, 纬度${center.latitude.toFixed(6)}°, 高程${center.height.toFixed(2)}米 | 
					
						
							|  |  |  |  |     边界范围: 北${bounds.north.toFixed(6)}° 南${bounds.south.toFixed(6)}° 东${bounds.east.toFixed(6)}° 西${bounds.west.toFixed(6)}° | 
					
						
							|  |  |  |  |     尺寸: 宽度${(info.properties.width / 1000).toFixed(3)}km 高度${(info.properties.height / 1000).toFixed(3)}km | 
					
						
							|  |  |  |  |     周长: ${(info.properties.perimeter / 1000).toFixed(3)} km | 
					
						
							|  |  |  |  |     面积: ${(info.area / 1000000).toFixed(3)} km² | 
					
						
							|  |  |  |  |     顶点数量: ${info.properties.boundaryPoints.length}`;
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |           // 添加顶点信息
 | 
					
						
							|  |  |  |  |           info.properties.boundaryPoints.forEach((point, index) => { | 
					
						
							|  |  |  |  |             text += `\n顶点${index + 1}: 经度${point.longitude.toFixed(6)}°, 纬度${point.latitude.toFixed(6)}°, 高程${point.height.toFixed(2)}米`; | 
					
						
							|  |  |  |  |           }); | 
					
						
							|  |  |  |  |         } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |         return text; | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-10-11 16:10:41 +08:00
										 |  |  |  |   /** | 
					
						
							|  |  |  |  |    * 计算面积 | 
					
						
							|  |  |  |  |    */ | 
					
						
							|  |  |  |  |   private calculateArea(positions: Cesium.Cartesian3[], type: 'circle' | 'polygon'): number { | 
					
						
							|  |  |  |  |     if (type === 'circle') { | 
					
						
							|  |  |  |  |       const center = positions[0]; | 
					
						
							|  |  |  |  |       console.log("圆形的圆心:",center) | 
					
						
							|  |  |  |  |       const radius = positions.length > 1 ? Cesium.Cartesian3.distance(center, positions[1]) : 0; | 
					
						
							|  |  |  |  |       console.log("圆形的半径:",radius) | 
					
						
							|  |  |  |  |       return Math.PI * radius * radius; | 
					
						
							|  |  |  |  |     } else { | 
					
						
							|  |  |  |  |       // 计算多边形面积
 | 
					
						
							|  |  |  |  |       // const cartographics = positions.map(pos => 
 | 
					
						
							|  |  |  |  |       //   Cesium.Cartographic.fromCartesian(pos)
 | 
					
						
							|  |  |  |  |       // );
 | 
					
						
							|  |  |  |  |       // return Cesium.PolygonGeometry.computeArea(cartographics);
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |        // 修正:使用正确的多边形面积计算方法
 | 
					
						
							|  |  |  |  |        const area = this.computePolygonAreaSimple(positions); | 
					
						
							|  |  |  |  |        console.log(`Polygon area: ${area} m², vertices: ${positions.length}`); | 
					
						
							|  |  |  |  |        return area; | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   /** | 
					
						
							|  |  |  |  |    * 简化但有效的多边形面积计算 | 
					
						
							|  |  |  |  |    */ | 
					
						
							|  |  |  |  |   private computePolygonAreaSimple(positions: Cesium.Cartesian3[]): number { | 
					
						
							|  |  |  |  |     if (positions.length < 3) return 0; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     // 将3D坐标转换为2D平面坐标(使用第一个点作为参考平面)
 | 
					
						
							|  |  |  |  |     const normal = new Cesium.Cartesian3(); | 
					
						
							|  |  |  |  |     Cesium.Ellipsoid.WGS84.geodeticSurfaceNormal(positions[0], normal); | 
					
						
							|  |  |  |  |      | 
					
						
							|  |  |  |  |     let area = 0; | 
					
						
							|  |  |  |  |     const n = positions.length; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     for (let i = 0; i < n; i++) { | 
					
						
							|  |  |  |  |       const j = (i + 1) % n; | 
					
						
							|  |  |  |  |        | 
					
						
							|  |  |  |  |       const p1 = positions[i]; | 
					
						
							|  |  |  |  |       const p2 = positions[j]; | 
					
						
							|  |  |  |  |        | 
					
						
							|  |  |  |  |       // 计算两个向量在切平面上的投影
 | 
					
						
							|  |  |  |  |       const v1 = Cesium.Cartesian3.subtract(p1, positions[0], new Cesium.Cartesian3()); | 
					
						
							|  |  |  |  |       const v2 = Cesium.Cartesian3.subtract(p2, positions[0], new Cesium.Cartesian3()); | 
					
						
							|  |  |  |  |        | 
					
						
							|  |  |  |  |       // 计算叉积的模长(平行四边形面积)
 | 
					
						
							|  |  |  |  |       const cross = Cesium.Cartesian3.cross(v1, v2, new Cesium.Cartesian3()); | 
					
						
							|  |  |  |  |       const parallelogramArea = Cesium.Cartesian3.magnitude(cross); | 
					
						
							|  |  |  |  |        | 
					
						
							|  |  |  |  |       area += parallelogramArea; | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     // 三角形面积是平行四边形面积的一半
 | 
					
						
							|  |  |  |  |     return Math.abs(area) / 2; | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   /** | 
					
						
							|  |  |  |  |    * 取消绘制 | 
					
						
							|  |  |  |  |    */ | 
					
						
							|  |  |  |  |   cancelDrawing(): void { | 
					
						
							|  |  |  |  |     this.cleanupDrawing(); | 
					
						
							|  |  |  |  |     this.drawingCallbacks.onCancel?.(); | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   /** | 
					
						
							|  |  |  |  |    * 清理绘制状态 | 
					
						
							|  |  |  |  |    */ | 
					
						
							|  |  |  |  |   private cleanupDrawing(): void { | 
					
						
							|  |  |  |  |     this.isDrawing = false; | 
					
						
							|  |  |  |  |     this.currentType = null; | 
					
						
							|  |  |  |  |     this.currentPositions = []; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     // 清除临时实体
 | 
					
						
							|  |  |  |  |     this.tempEntities.forEach(entity => this.entities.remove(entity)); | 
					
						
							|  |  |  |  |     this.tempEntities = []; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     // 移除事件监听
 | 
					
						
							|  |  |  |  |     this.handler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_CLICK); | 
					
						
							|  |  |  |  |     this.handler.removeInputAction(Cesium.ScreenSpaceEventType.MOUSE_MOVE); | 
					
						
							|  |  |  |  |     this.handler.removeInputAction(Cesium.ScreenSpaceEventType.RIGHT_CLICK); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     // 移除键盘事件
 | 
					
						
							|  |  |  |  |     if ((this as any).cancelHandler) { | 
					
						
							|  |  |  |  |       document.removeEventListener('keydown', (this as any).cancelHandler); | 
					
						
							|  |  |  |  |       (this as any).cancelHandler = null; | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     // 恢复鼠标样式
 | 
					
						
							|  |  |  |  |     this.viewer.scene.canvas.style.cursor = ''; | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   /** | 
					
						
							|  |  |  |  |    * 清除临时图形 | 
					
						
							|  |  |  |  |    */ | 
					
						
							|  |  |  |  |   private clearTempShapes(): void { | 
					
						
							|  |  |  |  |     this.tempEntities.forEach(entity => this.entities.remove(entity)); | 
					
						
							|  |  |  |  |     this.tempEntities = []; | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   /** | 
					
						
							|  |  |  |  |    * 拾取坐标 | 
					
						
							|  |  |  |  |    */ | 
					
						
							|  |  |  |  |   private pickCoordinate(position: Cesium.Cartesian2): Cesium.Cartesian3 | null { | 
					
						
							|  |  |  |  |     const ray = this.viewer.camera.getPickRay(position); | 
					
						
							|  |  |  |  |     if (!ray) return null; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     const cartesian = this.viewer.scene.globe.pick(ray, this.viewer.scene) || | 
					
						
							|  |  |  |  |                      this.viewer.scene.camera.pickEllipsoid(position, this.viewer.scene.globe.ellipsoid); | 
					
						
							|  |  |  |  |     return cartesian; | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   /** | 
					
						
							|  |  |  |  |    * 设置绘图回调 | 
					
						
							|  |  |  |  |    */ | 
					
						
							|  |  |  |  |   setCallbacks(callbacks: typeof this.drawingCallbacks): void { | 
					
						
							|  |  |  |  |     this.drawingCallbacks = { ...this.drawingCallbacks, ...callbacks }; | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   /** | 
					
						
							|  |  |  |  |    * 获取所有绘制的图形 | 
					
						
							|  |  |  |  |    */ | 
					
						
							|  |  |  |  |   getAllDrawings(): DrawingResult[] { | 
					
						
							|  |  |  |  |     return Array.from(this.drawingEntities.values()); | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   /** | 
					
						
							|  |  |  |  |    * 根据ID获取图形 | 
					
						
							|  |  |  |  |    */ | 
					
						
							|  |  |  |  |   getDrawing(id: string): DrawingResult | undefined { | 
					
						
							|  |  |  |  |     return this.drawingEntities.get(id); | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   /** | 
					
						
							|  |  |  |  |    * 移除图形 | 
					
						
							|  |  |  |  |    */ | 
					
						
							|  |  |  |  |   removeDrawing(id: string): boolean { | 
					
						
							|  |  |  |  |     const drawing = this.drawingEntities.get(id); | 
					
						
							|  |  |  |  |     if (drawing) { | 
					
						
							|  |  |  |  |       this.entities.remove(drawing.entity); | 
					
						
							|  |  |  |  |       this.drawingEntities.delete(id); | 
					
						
							|  |  |  |  |       return true; | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  |     return false; | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   /** | 
					
						
							|  |  |  |  |    * 清除所有图形 | 
					
						
							|  |  |  |  |    */ | 
					
						
							|  |  |  |  |   clearAllDrawings(): void { | 
					
						
							|  |  |  |  |     this.drawingEntities.forEach(drawing => { | 
					
						
							|  |  |  |  |       this.entities.remove(drawing.entity); | 
					
						
							|  |  |  |  |     }); | 
					
						
							|  |  |  |  |     this.drawingEntities.clear(); | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   /** | 
					
						
							|  |  |  |  |    * 高亮显示图形 | 
					
						
							|  |  |  |  |    */ | 
					
						
							|  |  |  |  |   highlightDrawing(id: string, highlight: boolean = true): void { | 
					
						
							|  |  |  |  |     const drawing = this.drawingEntities.get(id); | 
					
						
							|  |  |  |  |     if (!drawing) return; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     if (drawing.type === 'circle' && drawing.entity.ellipse) { | 
					
						
							|  |  |  |  |       drawing.entity.ellipse.outlineColor = highlight ?  | 
					
						
							|  |  |  |  |         Cesium.Color.RED :  | 
					
						
							|  |  |  |  |         (drawing.properties.options.outlineColor || this.defaultOptions.outlineColor); | 
					
						
							|  |  |  |  |       drawing.entity.ellipse.outlineWidth = highlight ? 4 : 2; | 
					
						
							|  |  |  |  |     } else if (drawing.entity.polygon) { | 
					
						
							|  |  |  |  |       drawing.entity.polygon.outlineColor = highlight ?  | 
					
						
							|  |  |  |  |         Cesium.Color.RED :  | 
					
						
							|  |  |  |  |         (drawing.properties.options.outlineColor || this.defaultOptions.outlineColor); | 
					
						
							|  |  |  |  |       drawing.entity.polygon.outlineWidth = highlight ? 4 : 2; | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-10-14 16:57:49 +08:00
										 |  |  |  |   /** | 
					
						
							|  |  |  |  |    * 飞向图形 | 
					
						
							|  |  |  |  |    */ | 
					
						
							|  |  |  |  |   // flyToDrawing(id: string, duration: number = 2): void {
 | 
					
						
							|  |  |  |  |   //   console.log("飞飞飞非法欸")
 | 
					
						
							|  |  |  |  |   //   const drawing = this.drawingEntities.get(id);
 | 
					
						
							|  |  |  |  |   //   console.log("飞飞飞非111")
 | 
					
						
							|  |  |  |  |   //   if (drawing) {
 | 
					
						
							|  |  |  |  |   //     console.log("飞飞飞222")
 | 
					
						
							|  |  |  |  |   //     this.viewer.flyTo(drawing.entity, {
 | 
					
						
							|  |  |  |  |   //       duration: duration,
 | 
					
						
							|  |  |  |  |   //       offset: new Cesium.HeadingPitchRange(0, -0.5, 0)
 | 
					
						
							|  |  |  |  |   //     });
 | 
					
						
							|  |  |  |  |   //   }
 | 
					
						
							|  |  |  |  |   // }
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-10-11 16:10:41 +08:00
										 |  |  |  |   /** | 
					
						
							|  |  |  |  |    * 飞向图形 | 
					
						
							|  |  |  |  |    */ | 
					
						
							|  |  |  |  |   flyToDrawing(id: string, duration: number = 2): void { | 
					
						
							| 
									
										
										
										
											2025-10-14 16:57:49 +08:00
										 |  |  |  |     console.log('=== flyToDrawing 开始执行 ==='); | 
					
						
							|  |  |  |  |     console.log('目标空域ID:', id); | 
					
						
							|  |  |  |  |      | 
					
						
							| 
									
										
										
										
											2025-10-11 16:10:41 +08:00
										 |  |  |  |     const drawing = this.drawingEntities.get(id); | 
					
						
							| 
									
										
										
										
											2025-10-14 16:57:49 +08:00
										 |  |  |  |     if (!drawing) { | 
					
						
							|  |  |  |  |       console.error('❌ 未找到空域:', id); | 
					
						
							|  |  |  |  |       return; | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     console.log('找到空域:', drawing); | 
					
						
							|  |  |  |  |      | 
					
						
							|  |  |  |  |     try { | 
					
						
							|  |  |  |  |       if (drawing.type === 'circle') { | 
					
						
							|  |  |  |  |         // 对于圆形,飞到圆心上方
 | 
					
						
							|  |  |  |  |         const center = drawing.positions[0]; | 
					
						
							|  |  |  |  |         const radius = drawing.positions.length > 1 ?  | 
					
						
							|  |  |  |  |           Cesium.Cartesian3.distance(center, drawing.positions[1]) :  | 
					
						
							|  |  |  |  |           (drawing.properties.options.radius || 1000); | 
					
						
							|  |  |  |  |          | 
					
						
							|  |  |  |  |         const height = radius * 2 + 500; // 在半径2倍高度加上500米
 | 
					
						
							|  |  |  |  |          | 
					
						
							|  |  |  |  |         console.log('圆形空域 - 圆心:', center); | 
					
						
							|  |  |  |  |         console.log('圆形空域 - 半径:', radius); | 
					
						
							|  |  |  |  |         console.log('圆形空域 - 飞行高度:', height); | 
					
						
							|  |  |  |  |          | 
					
						
							|  |  |  |  |         this.viewer.camera.flyTo({ | 
					
						
							|  |  |  |  |           destination: center, | 
					
						
							|  |  |  |  |           orientation: { | 
					
						
							|  |  |  |  |             heading: 0, | 
					
						
							|  |  |  |  |             pitch: -Math.PI/4, | 
					
						
							|  |  |  |  |             roll: 0 | 
					
						
							|  |  |  |  |           }, | 
					
						
							|  |  |  |  |           duration: duration, | 
					
						
							|  |  |  |  |           complete: () => { | 
					
						
							|  |  |  |  |             console.log('✅ 飞向圆形空域完成'); | 
					
						
							|  |  |  |  |           }, | 
					
						
							|  |  |  |  |           cancel: () => { | 
					
						
							|  |  |  |  |             console.log('❌ 飞向圆形空域取消'); | 
					
						
							|  |  |  |  |           } | 
					
						
							|  |  |  |  |         }); | 
					
						
							|  |  |  |  |          | 
					
						
							|  |  |  |  |       } else { | 
					
						
							|  |  |  |  |         // 对于多边形,飞到边界框
 | 
					
						
							|  |  |  |  |         console.log('多边形空域 - 实体:', drawing.entity); | 
					
						
							|  |  |  |  |          | 
					
						
							|  |  |  |  |         this.viewer.flyTo(drawing.entity, { | 
					
						
							|  |  |  |  |           duration: duration, | 
					
						
							|  |  |  |  |           offset: new Cesium.HeadingPitchRange(0, -Math.PI/4, 0), | 
					
						
							|  |  |  |  |           complete: () => { | 
					
						
							|  |  |  |  |             console.log('✅ 飞向多边形空域完成'); | 
					
						
							|  |  |  |  |           }, | 
					
						
							|  |  |  |  |           cancel: () => { | 
					
						
							|  |  |  |  |             console.log('❌ 飞向多边形空域取消'); | 
					
						
							|  |  |  |  |           } | 
					
						
							|  |  |  |  |         }); | 
					
						
							|  |  |  |  |       } | 
					
						
							|  |  |  |  |        | 
					
						
							|  |  |  |  |       console.log('✅ flyTo 命令已发送'); | 
					
						
							|  |  |  |  |        | 
					
						
							|  |  |  |  |     } catch (error) { | 
					
						
							|  |  |  |  |       console.error('❌ 飞向空域时发生错误:', error); | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   /** | 
					
						
							|  |  |  |  |    * 飞向图形(改进版本) | 
					
						
							|  |  |  |  |    */ | 
					
						
							|  |  |  |  |   flyToDrawingImproved(id: string, duration: number = 2): void { | 
					
						
							|  |  |  |  |     console.log('=== flyToDrawingImproved 开始执行 ==='); | 
					
						
							|  |  |  |  |      | 
					
						
							|  |  |  |  |     const drawing = this.drawingEntities.get(id); | 
					
						
							|  |  |  |  |     if (!drawing) { | 
					
						
							|  |  |  |  |       console.error('❌ 未找到空域:', id); | 
					
						
							|  |  |  |  |       return; | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     try { | 
					
						
							|  |  |  |  |       // 计算实体的边界球
 | 
					
						
							|  |  |  |  |       const boundingSphere = this.computeEntityBoundingSphere(drawing.entity); | 
					
						
							|  |  |  |  |       if (!boundingSphere) { | 
					
						
							|  |  |  |  |         console.error('❌ 无法计算边界球'); | 
					
						
							|  |  |  |  |         return; | 
					
						
							|  |  |  |  |       } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       console.log('边界球:', boundingSphere); | 
					
						
							|  |  |  |  |        | 
					
						
							|  |  |  |  |       // 飞到边界球
 | 
					
						
							|  |  |  |  |       this.viewer.camera.flyToBoundingSphere(boundingSphere, { | 
					
						
							|  |  |  |  |         duration: duration, | 
					
						
							|  |  |  |  |         offset: new Cesium.HeadingPitchRange(0, -Math.PI/4, boundingSphere.radius * 3), | 
					
						
							|  |  |  |  |         complete: () => { | 
					
						
							|  |  |  |  |           console.log('✅ 飞向空域完成'); | 
					
						
							|  |  |  |  |         }, | 
					
						
							|  |  |  |  |         cancel: () => { | 
					
						
							|  |  |  |  |           console.log('❌ 飞向空域取消'); | 
					
						
							|  |  |  |  |         } | 
					
						
							|  |  |  |  |       }); | 
					
						
							|  |  |  |  |        | 
					
						
							|  |  |  |  |     } catch (error) { | 
					
						
							|  |  |  |  |       console.error('❌ 飞向空域时发生错误:', error); | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   /** | 
					
						
							|  |  |  |  |    * 计算实体的边界球 | 
					
						
							|  |  |  |  |    */ | 
					
						
							|  |  |  |  |   private computeEntityBoundingSphere(entity: Cesium.Entity): Cesium.BoundingSphere | null { | 
					
						
							|  |  |  |  |     try { | 
					
						
							|  |  |  |  |       // 方法1: 使用 Cesium 的 computeBoundingSphere
 | 
					
						
							|  |  |  |  |       if (entity.polygon || entity.ellipse) { | 
					
						
							|  |  |  |  |         const boundingSphere = entity.computeBoundingSphere(); | 
					
						
							|  |  |  |  |         if (boundingSphere) { | 
					
						
							|  |  |  |  |           return boundingSphere; | 
					
						
							|  |  |  |  |         } | 
					
						
							|  |  |  |  |       } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       // 方法2: 手动计算边界球
 | 
					
						
							|  |  |  |  |       if (entity.polygon && entity.polygon.hierarchy) { | 
					
						
							|  |  |  |  |         const hierarchy = entity.polygon.hierarchy.getValue(Cesium.JulianDate.now()); | 
					
						
							|  |  |  |  |         if (hierarchy && hierarchy.positions) { | 
					
						
							|  |  |  |  |           return Cesium.BoundingSphere.fromPoints(hierarchy.positions); | 
					
						
							|  |  |  |  |         } | 
					
						
							|  |  |  |  |       } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       // 方法3: 对于椭圆,使用位置和半径
 | 
					
						
							|  |  |  |  |       if (entity.ellipse && entity.position) { | 
					
						
							|  |  |  |  |         const position = entity.position.getValue(Cesium.JulianDate.now()); | 
					
						
							|  |  |  |  |         const semiMajorAxis = entity.ellipse.semiMajorAxis?.getValue(Cesium.JulianDate.now()) || 1000; | 
					
						
							|  |  |  |  |         const semiMinorAxis = entity.ellipse.semiMinorAxis?.getValue(Cesium.JulianDate.now()) || 1000; | 
					
						
							|  |  |  |  |         const radius = Math.max(semiMajorAxis, semiMinorAxis); | 
					
						
							|  |  |  |  |          | 
					
						
							|  |  |  |  |         return new Cesium.BoundingSphere(position, radius); | 
					
						
							|  |  |  |  |       } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       return null; | 
					
						
							|  |  |  |  |     } catch (error) { | 
					
						
							|  |  |  |  |       console.error('计算边界球时发生错误:', error); | 
					
						
							|  |  |  |  |       return null; | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   /** | 
					
						
							|  |  |  |  |    * 飞向图形的简单方法 | 
					
						
							|  |  |  |  |    */ | 
					
						
							|  |  |  |  |   flyToDrawingSimple(id: string, duration: number = 2): void { | 
					
						
							|  |  |  |  |     console.log('=== flyToDrawingSimple 开始执行 ==='); | 
					
						
							|  |  |  |  |      | 
					
						
							|  |  |  |  |     const drawing = this.drawingEntities.get(id); | 
					
						
							|  |  |  |  |     if (!drawing) { | 
					
						
							|  |  |  |  |       console.error('❌ 未找到空域:', id); | 
					
						
							|  |  |  |  |       return; | 
					
						
							|  |  |  |  |     } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |     try { | 
					
						
							|  |  |  |  |       // 获取实体的位置
 | 
					
						
							|  |  |  |  |       const position = drawing.entity.position?.getValue(Cesium.JulianDate.now()); | 
					
						
							|  |  |  |  |       if (!position) { | 
					
						
							|  |  |  |  |         console.error('❌ 无法获取实体位置'); | 
					
						
							|  |  |  |  |         return; | 
					
						
							|  |  |  |  |       } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       console.log('实体位置:', position); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       // 转换到经纬度
 | 
					
						
							|  |  |  |  |       const cartographic = Cesium.Cartographic.fromCartesian(position); | 
					
						
							|  |  |  |  |       const longitude = Cesium.Math.toDegrees(cartographic.longitude); | 
					
						
							|  |  |  |  |       const latitude = Cesium.Math.toDegrees(cartographic.latitude); | 
					
						
							|  |  |  |  |       const height = cartographic.height + 2000; // 在实体高度上加2000米
 | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       console.log('目标位置 - 经度:', longitude, '纬度:', latitude, '高度:', height); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       // 飞到该位置
 | 
					
						
							|  |  |  |  |       this.viewer.camera.flyTo({ | 
					
						
							|  |  |  |  |         destination: Cesium.Cartesian3.fromDegrees(longitude, latitude, height), | 
					
						
							|  |  |  |  |         orientation: { | 
					
						
							|  |  |  |  |           heading: 0, | 
					
						
							|  |  |  |  |           pitch: -Math.PI/3, // -60度俯角
 | 
					
						
							|  |  |  |  |           roll: 0 | 
					
						
							|  |  |  |  |         }, | 
					
						
							| 
									
										
										
										
											2025-10-11 16:10:41 +08:00
										 |  |  |  |         duration: duration, | 
					
						
							| 
									
										
										
										
											2025-10-14 16:57:49 +08:00
										 |  |  |  |         complete: () => { | 
					
						
							|  |  |  |  |           console.log('✅ 飞向空域完成'); | 
					
						
							|  |  |  |  |         }, | 
					
						
							|  |  |  |  |         cancel: () => { | 
					
						
							|  |  |  |  |           console.log('❌ 飞向空域取消'); | 
					
						
							|  |  |  |  |         } | 
					
						
							| 
									
										
										
										
											2025-10-11 16:10:41 +08:00
										 |  |  |  |       }); | 
					
						
							| 
									
										
										
										
											2025-10-14 16:57:49 +08:00
										 |  |  |  | 
 | 
					
						
							|  |  |  |  |     } catch (error) { | 
					
						
							|  |  |  |  |       console.error('❌ 飞向空域时发生错误:', error); | 
					
						
							| 
									
										
										
										
											2025-10-11 16:10:41 +08:00
										 |  |  |  |     } | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   /** | 
					
						
							|  |  |  |  |    * 导出图形数据 | 
					
						
							|  |  |  |  |    */ | 
					
						
							|  |  |  |  |   exportDrawings(): any[] { | 
					
						
							|  |  |  |  |     return this.getAllDrawings().map(drawing => ({ | 
					
						
							|  |  |  |  |       id: drawing.id, | 
					
						
							|  |  |  |  |       type: drawing.type, | 
					
						
							|  |  |  |  |       positions: drawing.positions.map(pos => { | 
					
						
							|  |  |  |  |         const cartographic = Cesium.Cartographic.fromCartesian(pos); | 
					
						
							|  |  |  |  |         return { | 
					
						
							|  |  |  |  |           longitude: Cesium.Math.toDegrees(cartographic.longitude), | 
					
						
							|  |  |  |  |           latitude: Cesium.Math.toDegrees(cartographic.latitude), | 
					
						
							|  |  |  |  |           height: cartographic.height | 
					
						
							|  |  |  |  |         }; | 
					
						
							|  |  |  |  |       }), | 
					
						
							|  |  |  |  |       properties: drawing.properties | 
					
						
							|  |  |  |  |     })); | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   /** | 
					
						
							|  |  |  |  |    * 导入图形数据 | 
					
						
							|  |  |  |  |    */ | 
					
						
							|  |  |  |  |   importDrawings(data: any[]): void { | 
					
						
							|  |  |  |  |     data.forEach(item => { | 
					
						
							|  |  |  |  |       const positions = item.positions.map((pos: any) =>  | 
					
						
							|  |  |  |  |         Cesium.Cartesian3.fromDegrees(pos.longitude, pos.latitude, pos.height) | 
					
						
							|  |  |  |  |       ); | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       let entity: Cesium.Entity; | 
					
						
							|  |  |  |  |       if (item.type === 'circle') { | 
					
						
							|  |  |  |  |         entity = this.createCircleEntity(item.id, positions, item.properties.options); | 
					
						
							|  |  |  |  |       } else { | 
					
						
							|  |  |  |  |         entity = this.createPolygonEntity(item.id, positions, item.properties.options); | 
					
						
							|  |  |  |  |       } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       const drawing: DrawingResult = { | 
					
						
							|  |  |  |  |         id: item.id, | 
					
						
							|  |  |  |  |         type: item.type, | 
					
						
							|  |  |  |  |         positions, | 
					
						
							|  |  |  |  |         entity, | 
					
						
							|  |  |  |  |         properties: item.properties | 
					
						
							|  |  |  |  |       }; | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |       this.drawingEntities.set(item.id, drawing); | 
					
						
							|  |  |  |  |     }); | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | 
 | 
					
						
							|  |  |  |  |   /** | 
					
						
							|  |  |  |  |    * 销毁 | 
					
						
							|  |  |  |  |    */ | 
					
						
							|  |  |  |  |   destroy(): void { | 
					
						
							|  |  |  |  |     this.cancelDrawing(); | 
					
						
							|  |  |  |  |     this.clearAllDrawings(); | 
					
						
							|  |  |  |  |     this.handler.destroy(); | 
					
						
							|  |  |  |  |   } | 
					
						
							|  |  |  |  | } |