2025-10-24 10:16:33 +08:00

1954 lines
59 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 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 ImportOptions {
autoZoom?: boolean;
mergeExisting?: boolean;
coordinateSystem?: 'wgs84' | 'gcj02' | 'bd09';
}
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; // 新增信息字段
}
import { AirspaceEditor } from './AirspaceEditor';
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";
export class DrawingTool {
private airspaceEditor: AirspaceEditor;
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,
extrudedHeight: 0
};
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.airspaceEditor = new AirspaceEditor(viewer);
// 初始化点击事件监听
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 {
console.log("drawing 圆形:",drawing)
console.log("drawing 圆形:",drawing)
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;
console.log("DrawingResult 多边形信息:",drawing)
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;
}
/**
* 开始编辑空域
*/
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;
}
/**
* 开始绘制圆形
*/
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
// }
// });
// }
/**
* 导入空域数据
*/
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',
positions: data.position,
entity,
properties: {
...properties,
options: properties.options || this.defaultOptions,
area: properties.area || 0
}
} as DrawingResult);
// 保存到绘图实体集合
const drawingResult: DrawingResult = {
id,
type: type as 'circle' | 'polygon',
positions: data.position,
entity,
properties: {
...properties,
imported: true, // 标记为导入的空域
importTime: new Date().toISOString()
},
info: {
...drawingInfo,
area: properties.area || this.calculateArea(data.position, type as 'circle' | 'polygon')
}
};
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
};
}
/**
* 打印空域基本信息
*/
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;
}
this.getAllDrawingsInfoJSON();
console.log(`=== 空域信息汇总 (共 ${this.drawingEntities.size} 个) ===`);
console.log("=== 空域信息汇总", this.drawingEntities);
this.drawingEntities.forEach((drawing, id) => {
const info = this.generateDrawingInfo(drawing);
this.printFormattedInfo(info);
console.log('---'); // 分隔线
});
}
// const geojson = new map
/**
* 格式化打印信息
*/
private printFormattedInfo(info: DrawingInfo): void {
const { id, type, area, properties } = info;
const json = [];
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.formatToCustomStructure(info);
}
/**
* 获取所有空域信息的JSON格式
*/
getAllDrawingsInfoJSON(): any[] {
const result = [];
for (const [id, drawing] of this.drawingEntities) {
const info = this.generateDrawingInfo(drawing);
result.push(this.formatToCustomStructure(info));
}
console.log("result:",result);
// 转换为格式化的 JSON 字符串
const jsonString = JSON.stringify(result, null, 2);
let fromData = {
plottingJson: JSON.stringify(result)
}
console.log("result 字符串:", jsonString);
API_POST_addPlotting(fromData).then((response) => {
if (response.code === 200) {
// proxy.success("保存文件成功");
} else {
// message.error(response.msg);
}
}, error => {
// message.success("保存文件失败");
});
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
};
}
console.log("jsonInfo:",jsonInfo)
return jsonInfo;
}
/**
* 格式化为空域自定义结构
*/
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';
}
}
/**
* 导出空域信息为文本
*/
exportDrawingInfoAsText(drawingId: string): string {
const drawing = this.drawingEntities.get(drawingId);
if (!drawing) return '';
const info = this.generateDrawingInfo(drawing);
console.log("导出的空域json形式是什么样的呢:",info)
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();
}
}