// 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; } 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; // 新增信息字段 } export class DrawingTool { 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 = 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, extrudedHeight: 1000 }; 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; // 初始化点击事件监听 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 { 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; 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; } /** * 开始绘制圆形 */ 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 // } // }); // } /** * 打印空域基本信息 */ 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; } console.log(`=== 空域信息汇总 (共 ${this.drawingEntities.size} 个) ===`); this.drawingEntities.forEach((drawing, id) => { const info = this.generateDrawingInfo(drawing); this.printFormattedInfo(info); console.log('---'); // 分隔线 }); } /** * 格式化打印信息 */ private printFormattedInfo(info: DrawingInfo): void { const { id, type, area, properties } = info; 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`); } 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(`🎨 样式设置:`); console.log(` 填充颜色: ${properties.options.color}`); console.log(` 边框颜色: ${properties.options.outlineColor}`); console.log(` 边框宽度: ${properties.options.outlineWidth}px`); console.log(` 拉伸高度: ${properties.options.extrudedHeight}米`); } /** * 获取空域信息的JSON格式 */ getDrawingInfoJSON(drawingId: string): any { const drawing = this.drawingEntities.get(drawingId); if (!drawing) return null; const info = this.generateDrawingInfo(drawing); return this.formatInfoToJSON(info); } /** * 获取所有空域信息的JSON格式 */ getAllDrawingsInfoJSON(): any[] { const result = []; for (const [id, drawing] of this.drawingEntities) { const info = this.generateDrawingInfo(drawing); result.push(this.formatInfoToJSON(info)); } 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 }; } return jsonInfo; } /** * 导出空域信息为文本 */ exportDrawingInfoAsText(drawingId: string): string { const drawing = this.drawingEntities.get(drawingId); if (!drawing) return ''; const info = this.generateDrawingInfo(drawing); 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; } /** * 计算面积 */ 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; } } /** * 飞向图形 */ // 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) // }); // } // } /** * 飞向图形 */ flyToDrawing(id: string, duration: number = 2): void { console.log('=== flyToDrawing 开始执行 ==='); console.log('目标空域ID:', id); const drawing = this.drawingEntities.get(id); 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 }, duration: duration, complete: () => { console.log('✅ 飞向空域完成'); }, cancel: () => { console.log('❌ 飞向空域取消'); } }); } catch (error) { console.error('❌ 飞向空域时发生错误:', error); } } /** * 导出图形数据 */ 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(); } }