Files
vue-ts-example/src/components/h-cesium-viewer/managers/HCesiumManager.卫星.ts

290 lines
10 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

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.

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 HCesiumSatelliteManager {
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 } = 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 已销毁。');
}
}