Refactor: 重构 HCesiumManager
This commit is contained in:
289
src/components/h-cesium-viewer/managers/HCesiumManager.卫星.ts
Normal file
289
src/components/h-cesium-viewer/managers/HCesiumManager.卫星.ts
Normal file
@ -0,0 +1,289 @@
|
||||
import * as Cesium from 'cesium';
|
||||
|
||||
import type { HCesiumManager } from './HCesiumManager';
|
||||
import type { I卫星 } from './HCesiumManager.types';
|
||||
|
||||
import { type OrbitCalculationResult, SatelliteCalculator } from '../calculators/SatelliteCalculator';
|
||||
|
||||
interface ManagedSatelliteEntities {
|
||||
coverageEntity?: Cesium.Entity;
|
||||
entity: Cesium.Entity;
|
||||
orbitEntity?: Cesium.Entity;
|
||||
}
|
||||
|
||||
export class SatelliteManager {
|
||||
private viewerManager: HCesiumManager;
|
||||
private calculator: SatelliteCalculator;
|
||||
// 用于存储当前由此管理器管理的卫星相关实体的 Map
|
||||
private currentSatelliteEntities: Map<string, ManagedSatelliteEntities> = new Map();
|
||||
|
||||
constructor(viewerManager: HCesiumManager, calculator: SatelliteCalculator) {
|
||||
this.viewerManager = viewerManager;
|
||||
this.calculator = calculator;
|
||||
}
|
||||
|
||||
/**
|
||||
* 向视图中添加或更新卫星实体及其相关元素(轨道、覆盖范围)。
|
||||
* 注意:当前实现仅添加,如果已存在则警告并返回现有实体。
|
||||
* @param options - 卫星的选项参数
|
||||
* @returns 添加的卫星主实体对象,如果已存在或添加失败则返回 null 或现有实体。
|
||||
*/
|
||||
addOrUpdateSatellite(options: I卫星): Cesium.Entity | null {
|
||||
const existingEntry = this.currentSatelliteEntities.get(options.id);
|
||||
if (existingEntry) {
|
||||
console.warn(`ID 为 "${options.id}" 的卫星实体已由管理器追踪,跳过添加。`);
|
||||
// 未来可以扩展为更新逻辑
|
||||
return existingEntry.entity;
|
||||
}
|
||||
|
||||
const { id, tle, showOrbit = true } = options;
|
||||
|
||||
// --- 解析 TLE 和计算轨道 ---
|
||||
const satrec = this.calculator.parseTle(tle, id);
|
||||
if (!satrec) {
|
||||
return null; // 解析失败,已打印错误
|
||||
}
|
||||
|
||||
const viewer = this.viewerManager.getViewer();
|
||||
if (!viewer) {
|
||||
console.error('Viewer 未初始化,无法计算轨道。');
|
||||
return null;
|
||||
}
|
||||
const startTime = viewer.clock.currentTime; // 使用当前 viewer 的时间作为起点
|
||||
|
||||
const orbitResult: null | OrbitCalculationResult = this.calculator.calculateOrbit(satrec, startTime, options, id);
|
||||
|
||||
if (!orbitResult) {
|
||||
console.error(`计算卫星 ${id} 的轨道失败。`);
|
||||
return null;
|
||||
}
|
||||
const { sampledPositionProperty, orbitPositions } = orbitResult;
|
||||
// --- 计算结束 ---
|
||||
|
||||
// --- 从 TLE 提取名称 ---
|
||||
const tleLines = tle.trim().split('\n');
|
||||
const name = tleLines.length > 0 ? tleLines[0].trim() : `Satellite ${id}`;
|
||||
// --- 提取结束 ---
|
||||
|
||||
// --- 创建实体 ---
|
||||
const randomBaseColor = Cesium.Color.fromRandom({ alpha: 1 }); // 确保 alpha 为 1
|
||||
|
||||
// 创建卫星实体
|
||||
const satelliteEntity = new Cesium.Entity({
|
||||
id,
|
||||
name,
|
||||
position: sampledPositionProperty, // 使用计算好的位置属性
|
||||
orientation: new Cesium.VelocityOrientationProperty(sampledPositionProperty),
|
||||
point: {
|
||||
pixelSize: 8,
|
||||
color: randomBaseColor,
|
||||
outlineColor: Cesium.Color.WHITE,
|
||||
outlineWidth: 1,
|
||||
},
|
||||
label: {
|
||||
text: name,
|
||||
font: '14pt sans-serif',
|
||||
style: Cesium.LabelStyle.FILL_AND_OUTLINE,
|
||||
outlineWidth: 2,
|
||||
verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
|
||||
pixelOffset: new Cesium.Cartesian2(0, -10),
|
||||
fillColor: Cesium.Color.WHITE,
|
||||
outlineColor: Cesium.Color.BLACK,
|
||||
},
|
||||
// 动态轨迹路径 (Path) - 注意:这与完整轨道线 (Polyline) 不同
|
||||
path: {
|
||||
resolution: 1,
|
||||
material: new Cesium.PolylineGlowMaterialProperty({
|
||||
glowPower: 0.15,
|
||||
color: randomBaseColor,
|
||||
}),
|
||||
width: 2,
|
||||
leadTime: (options.orbitDurationSeconds ?? 3600 * 2) / 2, // 默认1小时
|
||||
trailTime: (options.orbitDurationSeconds ?? 3600 * 2) / 2, // 默认1小时
|
||||
},
|
||||
});
|
||||
|
||||
// 添加卫星地面覆盖范围
|
||||
const coverageEntity = this.createCoverageEntity(id, name, sampledPositionProperty, randomBaseColor);
|
||||
|
||||
// 添加完整轨道线(如果需要)
|
||||
let orbitEntity: Cesium.Entity | undefined;
|
||||
if (showOrbit && orbitPositions.length > 1) {
|
||||
orbitEntity = new Cesium.Entity({
|
||||
id: `${id}-orbit`,
|
||||
name: `${name} 轨道`,
|
||||
polyline: {
|
||||
positions: orbitPositions,
|
||||
width: 1,
|
||||
material: new Cesium.PolylineDashMaterialProperty({
|
||||
color: randomBaseColor.withAlpha(0.5),
|
||||
dashLength: 16,
|
||||
}),
|
||||
clampToGround: false,
|
||||
},
|
||||
});
|
||||
}
|
||||
// --- 创建结束 ---
|
||||
|
||||
// --- 添加到 Viewer ---
|
||||
const addedMainEntity = this.viewerManager.addEntity(satelliteEntity);
|
||||
if (!addedMainEntity) {
|
||||
console.error(`通过 ViewerManager 添加卫星主体 ID 为 "${id}" 的实体失败。`);
|
||||
return null; // 主实体添加失败则中止
|
||||
}
|
||||
|
||||
let addedCoverageEntity: Cesium.Entity | null = null;
|
||||
if (coverageEntity) {
|
||||
addedCoverageEntity = this.viewerManager.addEntity(coverageEntity);
|
||||
if (!addedCoverageEntity) {
|
||||
console.warn(`通过 ViewerManager 添加卫星覆盖范围 ID 为 "${coverageEntity.id}" 的实体失败。`);
|
||||
}
|
||||
}
|
||||
|
||||
let addedOrbitEntity: Cesium.Entity | null = null;
|
||||
if (orbitEntity) {
|
||||
addedOrbitEntity = this.viewerManager.addEntity(orbitEntity);
|
||||
if (!addedOrbitEntity) {
|
||||
console.warn(`通过 ViewerManager 添加卫星轨道 ID 为 "${orbitEntity.id}" 的实体失败。`);
|
||||
}
|
||||
}
|
||||
// --- 添加结束 ---
|
||||
|
||||
// 存储实体引用
|
||||
this.currentSatelliteEntities.set(id, {
|
||||
entity: addedMainEntity, // 存储实际添加成功的实体
|
||||
orbitEntity: addedOrbitEntity ?? undefined,
|
||||
coverageEntity: addedCoverageEntity ?? undefined,
|
||||
});
|
||||
|
||||
return addedMainEntity;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建卫星覆盖范围实体。
|
||||
*/
|
||||
private createCoverageEntity(
|
||||
satelliteId: string,
|
||||
satelliteName: string,
|
||||
positionProperty: Cesium.SampledPositionProperty,
|
||||
baseColor: Cesium.Color,
|
||||
): Cesium.Entity | null {
|
||||
// 使用 CallbackProperty 动态计算星下点位置
|
||||
const subsatellitePosition = new Cesium.CallbackPositionProperty( // 使用 CallbackPositionProperty
|
||||
(time, result) => {
|
||||
const satelliteCartesian = positionProperty.getValue(time, result);
|
||||
if (!satelliteCartesian) return;
|
||||
const satelliteCartographic = Cesium.Cartographic.fromCartesian(satelliteCartesian);
|
||||
if (!satelliteCartographic) return;
|
||||
const subsatelliteCartographic = new Cesium.Cartographic(
|
||||
satelliteCartographic.longitude,
|
||||
satelliteCartographic.latitude,
|
||||
0,
|
||||
);
|
||||
return Cesium.Cartographic.toCartesian(subsatelliteCartographic, Cesium.Ellipsoid.WGS84, result);
|
||||
},
|
||||
false,
|
||||
Cesium.ReferenceFrame.FIXED,
|
||||
);
|
||||
|
||||
// 使用 CallbackProperty 动态计算覆盖半径 (基于高度的简单估算)
|
||||
const coverageRadius = new Cesium.CallbackProperty((time) => {
|
||||
const satelliteCartesian = positionProperty.getValue(time);
|
||||
if (!satelliteCartesian) return 100_000; // 默认半径
|
||||
const satelliteCartographic = Cesium.Cartographic.fromCartesian(satelliteCartesian);
|
||||
if (!satelliteCartographic) return 100_000;
|
||||
const altitude = satelliteCartographic.height;
|
||||
// 简化的估算
|
||||
const calculatedRadius = altitude * 0.8;
|
||||
return Math.max(calculatedRadius, 50_000); // 最小半径 50km
|
||||
}, false);
|
||||
|
||||
return new Cesium.Entity({
|
||||
id: `${satelliteId}-coverage`,
|
||||
name: `${satelliteName} 覆盖范围`,
|
||||
position: subsatellitePosition,
|
||||
ellipse: {
|
||||
semiMajorAxis: coverageRadius,
|
||||
semiMinorAxis: coverageRadius,
|
||||
material: baseColor.withAlpha(0.2), // 半透明填充
|
||||
heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
|
||||
outline: true,
|
||||
outlineColor: baseColor.withAlpha(0.5), // 轮廓
|
||||
outlineWidth: 1,
|
||||
granularity: Cesium.Math.toRadians(1),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 从视图中移除指定的卫星实体及其相关元素 (通过 ID)。
|
||||
* @param entityId - 要移除的卫星实体的 ID。
|
||||
* @returns 如果成功移除所有相关实体则返回 true,否则返回 false。
|
||||
*/
|
||||
removeSatellite(entityId: string): boolean {
|
||||
const satelliteData = this.currentSatelliteEntities.get(entityId);
|
||||
if (!satelliteData) {
|
||||
// console.warn(`未在 SatelliteManager 中找到 ID 为 "${entityId}" 的实体,无法移除。`);
|
||||
return false; // 不在管理范围内
|
||||
}
|
||||
|
||||
let allRemoved = true;
|
||||
|
||||
// 移除主体
|
||||
if (!this.viewerManager.removeEntity(satelliteData.entity)) {
|
||||
console.warn(`尝试通过 ViewerManager 移除卫星主体 ID 为 "${entityId}" 的实体失败。`);
|
||||
allRemoved = false;
|
||||
}
|
||||
|
||||
// 移除轨道
|
||||
if (satelliteData.orbitEntity && !this.viewerManager.removeEntity(satelliteData.orbitEntity)) {
|
||||
console.warn(`尝试通过 ViewerManager 移除卫星轨道 ID 为 "${satelliteData.orbitEntity.id}" 的实体失败。`);
|
||||
allRemoved = false;
|
||||
}
|
||||
|
||||
// 移除覆盖范围
|
||||
if (satelliteData.coverageEntity && !this.viewerManager.removeEntity(satelliteData.coverageEntity)) {
|
||||
console.warn(`尝试通过 ViewerManager 移除卫星覆盖范围 ID 为 "${satelliteData.coverageEntity.id}" 的实体失败。`);
|
||||
allRemoved = false;
|
||||
}
|
||||
|
||||
// 从 Map 中删除,无论移除是否完全成功,以避免状态不一致
|
||||
this.currentSatelliteEntities.delete(entityId);
|
||||
|
||||
return allRemoved;
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除所有由此管理器管理的卫星实体及其相关元素。
|
||||
*/
|
||||
clearAllSatellites(): void {
|
||||
const idsToRemove = [...this.currentSatelliteEntities.keys()];
|
||||
let removalFailed = false;
|
||||
for (const id of idsToRemove) {
|
||||
if (!this.removeSatellite(id)) {
|
||||
removalFailed = true;
|
||||
}
|
||||
}
|
||||
if (removalFailed) {
|
||||
console.warn('清除部分卫星实体时遇到问题。');
|
||||
}
|
||||
// 确保最终清空 Map
|
||||
this.currentSatelliteEntities.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前管理的卫星实体 Map。
|
||||
* @returns 返回包含当前卫星实体及其关联实体的 Map。
|
||||
*/
|
||||
getCurrentSatelliteEntities(): Map<string, ManagedSatelliteEntities> {
|
||||
return this.currentSatelliteEntities;
|
||||
}
|
||||
|
||||
/**
|
||||
* 销毁管理器,清理资源。
|
||||
*/
|
||||
destroy(): void {
|
||||
this.clearAllSatellites();
|
||||
// console.log('SatelliteManager 已销毁。');
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user