2025-10-14 16:58:17 +08:00

1326 lines
40 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 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<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: 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();
}
}