2025-10-11 16:10:41 +08:00
|
|
|
|
// src/utils/cesium/DrawingTool.ts
|
|
|
|
|
|
import * as Cesium from 'cesium';
|
|
|
|
|
|
|
|
|
|
|
|
export interface DrawingOptions {
|
|
|
|
|
|
color?: Cesium.Color;
|
|
|
|
|
|
outlineColor?: Cesium.Color;
|
|
|
|
|
|
outlineWidth?: number;
|
|
|
|
|
|
fill?: boolean;
|
|
|
|
|
|
classificationType?: Cesium.ClassificationType;
|
|
|
|
|
|
height?: number;
|
|
|
|
|
|
extrudedHeight?: number;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export interface CircleOptions extends DrawingOptions {
|
|
|
|
|
|
radius?: number;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
export interface PolygonOptions extends DrawingOptions {
|
|
|
|
|
|
closePath?: boolean;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
// }
|
|
|
|
|
|
// });
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
2025-10-14 16:57:49 +08:00
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 打印空域基本信息
|
|
|
|
|
|
*/
|
|
|
|
|
|
printDrawingInfo(drawingId: string): void {
|
|
|
|
|
|
const drawing = this.drawingEntities.get(drawingId);
|
|
|
|
|
|
if (!drawing) {
|
|
|
|
|
|
console.warn(`未找到ID为 ${drawingId} 的空域`);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const info = this.generateDrawingInfo(drawing);
|
|
|
|
|
|
this.printFormattedInfo(info);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 打印所有空域信息
|
|
|
|
|
|
*/
|
|
|
|
|
|
printAllDrawingsInfo(): void {
|
|
|
|
|
|
if (this.drawingEntities.size === 0) {
|
|
|
|
|
|
console.log('当前没有绘制任何空域');
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2025-10-11 16:10:41 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 计算面积
|
|
|
|
|
|
*/
|
|
|
|
|
|
private calculateArea(positions: Cesium.Cartesian3[], type: 'circle' | 'polygon'): number {
|
|
|
|
|
|
if (type === 'circle') {
|
|
|
|
|
|
const center = positions[0];
|
|
|
|
|
|
console.log("圆形的圆心:",center)
|
|
|
|
|
|
const radius = positions.length > 1 ? Cesium.Cartesian3.distance(center, positions[1]) : 0;
|
|
|
|
|
|
console.log("圆形的半径:",radius)
|
|
|
|
|
|
return Math.PI * radius * radius;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
// 计算多边形面积
|
|
|
|
|
|
// const cartographics = positions.map(pos =>
|
|
|
|
|
|
// Cesium.Cartographic.fromCartesian(pos)
|
|
|
|
|
|
// );
|
|
|
|
|
|
// return Cesium.PolygonGeometry.computeArea(cartographics);
|
|
|
|
|
|
|
|
|
|
|
|
// 修正:使用正确的多边形面积计算方法
|
|
|
|
|
|
const area = this.computePolygonAreaSimple(positions);
|
|
|
|
|
|
console.log(`Polygon area: ${area} m², vertices: ${positions.length}`);
|
|
|
|
|
|
return area;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 简化但有效的多边形面积计算
|
|
|
|
|
|
*/
|
|
|
|
|
|
private computePolygonAreaSimple(positions: Cesium.Cartesian3[]): number {
|
|
|
|
|
|
if (positions.length < 3) return 0;
|
|
|
|
|
|
|
|
|
|
|
|
// 将3D坐标转换为2D平面坐标(使用第一个点作为参考平面)
|
|
|
|
|
|
const normal = new Cesium.Cartesian3();
|
|
|
|
|
|
Cesium.Ellipsoid.WGS84.geodeticSurfaceNormal(positions[0], normal);
|
|
|
|
|
|
|
|
|
|
|
|
let area = 0;
|
|
|
|
|
|
const n = positions.length;
|
|
|
|
|
|
|
|
|
|
|
|
for (let i = 0; i < n; i++) {
|
|
|
|
|
|
const j = (i + 1) % n;
|
|
|
|
|
|
|
|
|
|
|
|
const p1 = positions[i];
|
|
|
|
|
|
const p2 = positions[j];
|
|
|
|
|
|
|
|
|
|
|
|
// 计算两个向量在切平面上的投影
|
|
|
|
|
|
const v1 = Cesium.Cartesian3.subtract(p1, positions[0], new Cesium.Cartesian3());
|
|
|
|
|
|
const v2 = Cesium.Cartesian3.subtract(p2, positions[0], new Cesium.Cartesian3());
|
|
|
|
|
|
|
|
|
|
|
|
// 计算叉积的模长(平行四边形面积)
|
|
|
|
|
|
const cross = Cesium.Cartesian3.cross(v1, v2, new Cesium.Cartesian3());
|
|
|
|
|
|
const parallelogramArea = Cesium.Cartesian3.magnitude(cross);
|
|
|
|
|
|
|
|
|
|
|
|
area += parallelogramArea;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 三角形面积是平行四边形面积的一半
|
|
|
|
|
|
return Math.abs(area) / 2;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 取消绘制
|
|
|
|
|
|
*/
|
|
|
|
|
|
cancelDrawing(): void {
|
|
|
|
|
|
this.cleanupDrawing();
|
|
|
|
|
|
this.drawingCallbacks.onCancel?.();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 清理绘制状态
|
|
|
|
|
|
*/
|
|
|
|
|
|
private cleanupDrawing(): void {
|
|
|
|
|
|
this.isDrawing = false;
|
|
|
|
|
|
this.currentType = null;
|
|
|
|
|
|
this.currentPositions = [];
|
|
|
|
|
|
|
|
|
|
|
|
// 清除临时实体
|
|
|
|
|
|
this.tempEntities.forEach(entity => this.entities.remove(entity));
|
|
|
|
|
|
this.tempEntities = [];
|
|
|
|
|
|
|
|
|
|
|
|
// 移除事件监听
|
|
|
|
|
|
this.handler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_CLICK);
|
|
|
|
|
|
this.handler.removeInputAction(Cesium.ScreenSpaceEventType.MOUSE_MOVE);
|
|
|
|
|
|
this.handler.removeInputAction(Cesium.ScreenSpaceEventType.RIGHT_CLICK);
|
|
|
|
|
|
|
|
|
|
|
|
// 移除键盘事件
|
|
|
|
|
|
if ((this as any).cancelHandler) {
|
|
|
|
|
|
document.removeEventListener('keydown', (this as any).cancelHandler);
|
|
|
|
|
|
(this as any).cancelHandler = null;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 恢复鼠标样式
|
|
|
|
|
|
this.viewer.scene.canvas.style.cursor = '';
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 清除临时图形
|
|
|
|
|
|
*/
|
|
|
|
|
|
private clearTempShapes(): void {
|
|
|
|
|
|
this.tempEntities.forEach(entity => this.entities.remove(entity));
|
|
|
|
|
|
this.tempEntities = [];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 拾取坐标
|
|
|
|
|
|
*/
|
|
|
|
|
|
private pickCoordinate(position: Cesium.Cartesian2): Cesium.Cartesian3 | null {
|
|
|
|
|
|
const ray = this.viewer.camera.getPickRay(position);
|
|
|
|
|
|
if (!ray) return null;
|
|
|
|
|
|
|
|
|
|
|
|
const cartesian = this.viewer.scene.globe.pick(ray, this.viewer.scene) ||
|
|
|
|
|
|
this.viewer.scene.camera.pickEllipsoid(position, this.viewer.scene.globe.ellipsoid);
|
|
|
|
|
|
return cartesian;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 设置绘图回调
|
|
|
|
|
|
*/
|
|
|
|
|
|
setCallbacks(callbacks: typeof this.drawingCallbacks): void {
|
|
|
|
|
|
this.drawingCallbacks = { ...this.drawingCallbacks, ...callbacks };
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 获取所有绘制的图形
|
|
|
|
|
|
*/
|
|
|
|
|
|
getAllDrawings(): DrawingResult[] {
|
|
|
|
|
|
return Array.from(this.drawingEntities.values());
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 根据ID获取图形
|
|
|
|
|
|
*/
|
|
|
|
|
|
getDrawing(id: string): DrawingResult | undefined {
|
|
|
|
|
|
return this.drawingEntities.get(id);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 移除图形
|
|
|
|
|
|
*/
|
|
|
|
|
|
removeDrawing(id: string): boolean {
|
|
|
|
|
|
const drawing = this.drawingEntities.get(id);
|
|
|
|
|
|
if (drawing) {
|
|
|
|
|
|
this.entities.remove(drawing.entity);
|
|
|
|
|
|
this.drawingEntities.delete(id);
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 清除所有图形
|
|
|
|
|
|
*/
|
|
|
|
|
|
clearAllDrawings(): void {
|
|
|
|
|
|
this.drawingEntities.forEach(drawing => {
|
|
|
|
|
|
this.entities.remove(drawing.entity);
|
|
|
|
|
|
});
|
|
|
|
|
|
this.drawingEntities.clear();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 高亮显示图形
|
|
|
|
|
|
*/
|
|
|
|
|
|
highlightDrawing(id: string, highlight: boolean = true): void {
|
|
|
|
|
|
const drawing = this.drawingEntities.get(id);
|
|
|
|
|
|
if (!drawing) return;
|
|
|
|
|
|
|
|
|
|
|
|
if (drawing.type === 'circle' && drawing.entity.ellipse) {
|
|
|
|
|
|
drawing.entity.ellipse.outlineColor = highlight ?
|
|
|
|
|
|
Cesium.Color.RED :
|
|
|
|
|
|
(drawing.properties.options.outlineColor || this.defaultOptions.outlineColor);
|
|
|
|
|
|
drawing.entity.ellipse.outlineWidth = highlight ? 4 : 2;
|
|
|
|
|
|
} else if (drawing.entity.polygon) {
|
|
|
|
|
|
drawing.entity.polygon.outlineColor = highlight ?
|
|
|
|
|
|
Cesium.Color.RED :
|
|
|
|
|
|
(drawing.properties.options.outlineColor || this.defaultOptions.outlineColor);
|
|
|
|
|
|
drawing.entity.polygon.outlineWidth = highlight ? 4 : 2;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-10-14 16:57:49 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 飞向图形
|
|
|
|
|
|
*/
|
|
|
|
|
|
// flyToDrawing(id: string, duration: number = 2): void {
|
|
|
|
|
|
// console.log("飞飞飞非法欸")
|
|
|
|
|
|
// const drawing = this.drawingEntities.get(id);
|
|
|
|
|
|
// console.log("飞飞飞非111")
|
|
|
|
|
|
// if (drawing) {
|
|
|
|
|
|
// console.log("飞飞飞222")
|
|
|
|
|
|
// this.viewer.flyTo(drawing.entity, {
|
|
|
|
|
|
// duration: duration,
|
|
|
|
|
|
// offset: new Cesium.HeadingPitchRange(0, -0.5, 0)
|
|
|
|
|
|
// });
|
|
|
|
|
|
// }
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
|
|
|
2025-10-11 16:10:41 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 飞向图形
|
|
|
|
|
|
*/
|
|
|
|
|
|
flyToDrawing(id: string, duration: number = 2): void {
|
2025-10-14 16:57:49 +08:00
|
|
|
|
console.log('=== flyToDrawing 开始执行 ===');
|
|
|
|
|
|
console.log('目标空域ID:', id);
|
|
|
|
|
|
|
2025-10-11 16:10:41 +08:00
|
|
|
|
const drawing = this.drawingEntities.get(id);
|
2025-10-14 16:57:49 +08:00
|
|
|
|
if (!drawing) {
|
|
|
|
|
|
console.error('❌ 未找到空域:', id);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
console.log('找到空域:', drawing);
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
if (drawing.type === 'circle') {
|
|
|
|
|
|
// 对于圆形,飞到圆心上方
|
|
|
|
|
|
const center = drawing.positions[0];
|
|
|
|
|
|
const radius = drawing.positions.length > 1 ?
|
|
|
|
|
|
Cesium.Cartesian3.distance(center, drawing.positions[1]) :
|
|
|
|
|
|
(drawing.properties.options.radius || 1000);
|
|
|
|
|
|
|
|
|
|
|
|
const height = radius * 2 + 500; // 在半径2倍高度加上500米
|
|
|
|
|
|
|
|
|
|
|
|
console.log('圆形空域 - 圆心:', center);
|
|
|
|
|
|
console.log('圆形空域 - 半径:', radius);
|
|
|
|
|
|
console.log('圆形空域 - 飞行高度:', height);
|
|
|
|
|
|
|
|
|
|
|
|
this.viewer.camera.flyTo({
|
|
|
|
|
|
destination: center,
|
|
|
|
|
|
orientation: {
|
|
|
|
|
|
heading: 0,
|
|
|
|
|
|
pitch: -Math.PI/4,
|
|
|
|
|
|
roll: 0
|
|
|
|
|
|
},
|
|
|
|
|
|
duration: duration,
|
|
|
|
|
|
complete: () => {
|
|
|
|
|
|
console.log('✅ 飞向圆形空域完成');
|
|
|
|
|
|
},
|
|
|
|
|
|
cancel: () => {
|
|
|
|
|
|
console.log('❌ 飞向圆形空域取消');
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
// 对于多边形,飞到边界框
|
|
|
|
|
|
console.log('多边形空域 - 实体:', drawing.entity);
|
|
|
|
|
|
|
|
|
|
|
|
this.viewer.flyTo(drawing.entity, {
|
|
|
|
|
|
duration: duration,
|
|
|
|
|
|
offset: new Cesium.HeadingPitchRange(0, -Math.PI/4, 0),
|
|
|
|
|
|
complete: () => {
|
|
|
|
|
|
console.log('✅ 飞向多边形空域完成');
|
|
|
|
|
|
},
|
|
|
|
|
|
cancel: () => {
|
|
|
|
|
|
console.log('❌ 飞向多边形空域取消');
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
console.log('✅ flyTo 命令已发送');
|
|
|
|
|
|
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('❌ 飞向空域时发生错误:', error);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 飞向图形(改进版本)
|
|
|
|
|
|
*/
|
|
|
|
|
|
flyToDrawingImproved(id: string, duration: number = 2): void {
|
|
|
|
|
|
console.log('=== flyToDrawingImproved 开始执行 ===');
|
|
|
|
|
|
|
|
|
|
|
|
const drawing = this.drawingEntities.get(id);
|
|
|
|
|
|
if (!drawing) {
|
|
|
|
|
|
console.error('❌ 未找到空域:', id);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
// 计算实体的边界球
|
|
|
|
|
|
const boundingSphere = this.computeEntityBoundingSphere(drawing.entity);
|
|
|
|
|
|
if (!boundingSphere) {
|
|
|
|
|
|
console.error('❌ 无法计算边界球');
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
console.log('边界球:', boundingSphere);
|
|
|
|
|
|
|
|
|
|
|
|
// 飞到边界球
|
|
|
|
|
|
this.viewer.camera.flyToBoundingSphere(boundingSphere, {
|
|
|
|
|
|
duration: duration,
|
|
|
|
|
|
offset: new Cesium.HeadingPitchRange(0, -Math.PI/4, boundingSphere.radius * 3),
|
|
|
|
|
|
complete: () => {
|
|
|
|
|
|
console.log('✅ 飞向空域完成');
|
|
|
|
|
|
},
|
|
|
|
|
|
cancel: () => {
|
|
|
|
|
|
console.log('❌ 飞向空域取消');
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('❌ 飞向空域时发生错误:', error);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 计算实体的边界球
|
|
|
|
|
|
*/
|
|
|
|
|
|
private computeEntityBoundingSphere(entity: Cesium.Entity): Cesium.BoundingSphere | null {
|
|
|
|
|
|
try {
|
|
|
|
|
|
// 方法1: 使用 Cesium 的 computeBoundingSphere
|
|
|
|
|
|
if (entity.polygon || entity.ellipse) {
|
|
|
|
|
|
const boundingSphere = entity.computeBoundingSphere();
|
|
|
|
|
|
if (boundingSphere) {
|
|
|
|
|
|
return boundingSphere;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 方法2: 手动计算边界球
|
|
|
|
|
|
if (entity.polygon && entity.polygon.hierarchy) {
|
|
|
|
|
|
const hierarchy = entity.polygon.hierarchy.getValue(Cesium.JulianDate.now());
|
|
|
|
|
|
if (hierarchy && hierarchy.positions) {
|
|
|
|
|
|
return Cesium.BoundingSphere.fromPoints(hierarchy.positions);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 方法3: 对于椭圆,使用位置和半径
|
|
|
|
|
|
if (entity.ellipse && entity.position) {
|
|
|
|
|
|
const position = entity.position.getValue(Cesium.JulianDate.now());
|
|
|
|
|
|
const semiMajorAxis = entity.ellipse.semiMajorAxis?.getValue(Cesium.JulianDate.now()) || 1000;
|
|
|
|
|
|
const semiMinorAxis = entity.ellipse.semiMinorAxis?.getValue(Cesium.JulianDate.now()) || 1000;
|
|
|
|
|
|
const radius = Math.max(semiMajorAxis, semiMinorAxis);
|
|
|
|
|
|
|
|
|
|
|
|
return new Cesium.BoundingSphere(position, radius);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return null;
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('计算边界球时发生错误:', error);
|
|
|
|
|
|
return null;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 飞向图形的简单方法
|
|
|
|
|
|
*/
|
|
|
|
|
|
flyToDrawingSimple(id: string, duration: number = 2): void {
|
|
|
|
|
|
console.log('=== flyToDrawingSimple 开始执行 ===');
|
|
|
|
|
|
|
|
|
|
|
|
const drawing = this.drawingEntities.get(id);
|
|
|
|
|
|
if (!drawing) {
|
|
|
|
|
|
console.error('❌ 未找到空域:', id);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
// 获取实体的位置
|
|
|
|
|
|
const position = drawing.entity.position?.getValue(Cesium.JulianDate.now());
|
|
|
|
|
|
if (!position) {
|
|
|
|
|
|
console.error('❌ 无法获取实体位置');
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
console.log('实体位置:', position);
|
|
|
|
|
|
|
|
|
|
|
|
// 转换到经纬度
|
|
|
|
|
|
const cartographic = Cesium.Cartographic.fromCartesian(position);
|
|
|
|
|
|
const longitude = Cesium.Math.toDegrees(cartographic.longitude);
|
|
|
|
|
|
const latitude = Cesium.Math.toDegrees(cartographic.latitude);
|
|
|
|
|
|
const height = cartographic.height + 2000; // 在实体高度上加2000米
|
|
|
|
|
|
|
|
|
|
|
|
console.log('目标位置 - 经度:', longitude, '纬度:', latitude, '高度:', height);
|
|
|
|
|
|
|
|
|
|
|
|
// 飞到该位置
|
|
|
|
|
|
this.viewer.camera.flyTo({
|
|
|
|
|
|
destination: Cesium.Cartesian3.fromDegrees(longitude, latitude, height),
|
|
|
|
|
|
orientation: {
|
|
|
|
|
|
heading: 0,
|
|
|
|
|
|
pitch: -Math.PI/3, // -60度俯角
|
|
|
|
|
|
roll: 0
|
|
|
|
|
|
},
|
2025-10-11 16:10:41 +08:00
|
|
|
|
duration: duration,
|
2025-10-14 16:57:49 +08:00
|
|
|
|
complete: () => {
|
|
|
|
|
|
console.log('✅ 飞向空域完成');
|
|
|
|
|
|
},
|
|
|
|
|
|
cancel: () => {
|
|
|
|
|
|
console.log('❌ 飞向空域取消');
|
|
|
|
|
|
}
|
2025-10-11 16:10:41 +08:00
|
|
|
|
});
|
2025-10-14 16:57:49 +08:00
|
|
|
|
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('❌ 飞向空域时发生错误:', error);
|
2025-10-11 16:10:41 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 导出图形数据
|
|
|
|
|
|
*/
|
|
|
|
|
|
exportDrawings(): any[] {
|
|
|
|
|
|
return this.getAllDrawings().map(drawing => ({
|
|
|
|
|
|
id: drawing.id,
|
|
|
|
|
|
type: drawing.type,
|
|
|
|
|
|
positions: drawing.positions.map(pos => {
|
|
|
|
|
|
const cartographic = Cesium.Cartographic.fromCartesian(pos);
|
|
|
|
|
|
return {
|
|
|
|
|
|
longitude: Cesium.Math.toDegrees(cartographic.longitude),
|
|
|
|
|
|
latitude: Cesium.Math.toDegrees(cartographic.latitude),
|
|
|
|
|
|
height: cartographic.height
|
|
|
|
|
|
};
|
|
|
|
|
|
}),
|
|
|
|
|
|
properties: drawing.properties
|
|
|
|
|
|
}));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 导入图形数据
|
|
|
|
|
|
*/
|
|
|
|
|
|
importDrawings(data: any[]): void {
|
|
|
|
|
|
data.forEach(item => {
|
|
|
|
|
|
const positions = item.positions.map((pos: any) =>
|
|
|
|
|
|
Cesium.Cartesian3.fromDegrees(pos.longitude, pos.latitude, pos.height)
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
let entity: Cesium.Entity;
|
|
|
|
|
|
if (item.type === 'circle') {
|
|
|
|
|
|
entity = this.createCircleEntity(item.id, positions, item.properties.options);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
entity = this.createPolygonEntity(item.id, positions, item.properties.options);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const drawing: DrawingResult = {
|
|
|
|
|
|
id: item.id,
|
|
|
|
|
|
type: item.type,
|
|
|
|
|
|
positions,
|
|
|
|
|
|
entity,
|
|
|
|
|
|
properties: item.properties
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
this.drawingEntities.set(item.id, drawing);
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 销毁
|
|
|
|
|
|
*/
|
|
|
|
|
|
destroy(): void {
|
|
|
|
|
|
this.cancelDrawing();
|
|
|
|
|
|
this.clearAllDrawings();
|
|
|
|
|
|
this.handler.destroy();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|