feat: 添加卫星管理功能,新增 SatelliteOptions 接口,更新相关组件以支持卫星实体的添加和移除
Some checks failed
/ build-and-deploy-to-vercel (push) Successful in 2m45s
/ lint-build-and-check (push) Successful in 4m28s
/ surge (push) Successful in 2m46s
/ playwright (push) Failing after 12m11s

This commit is contained in:
严浩
2025-04-01 18:00:25 +08:00
parent 13681003fb
commit da515f4dfc
5 changed files with 386 additions and 22 deletions

View File

@ -1,4 +1,5 @@
import * as Cesium from 'cesium';
import { eciToEcf, gstime, propagate, type SatRec, twoline2satrec } from 'satellite.js';
import { VIEWER_OPTIONS_FN } from './VIEWER_OPTIONS';
@ -11,10 +12,24 @@ export interface GroundStationOptions {
pixelSize?: number; // 点的可选像素大小
}
// 卫星选项接口
export interface SatelliteOptions {
id: string; // 卫星的唯一标识符
orbitDurationHours?: number; // 轨道显示时长(小时),默认为 2
showOrbit?: boolean; // 是否显示完整轨道线,默认为 true
timeStepSeconds?: number; // 轨道计算步长(秒),默认为 30
tle: string; // 包含卫星名称和两行 TLE 数据的字符串,格式如下:
// NAME
// TLE1
// TLE2
}
export class HCesiumViewerCls {
private viewer: Cesium.Viewer | null = null;
// 用于存储当前地面站实体的 Map
currentStationEntities: Map<string, Cesium.Entity> = new Map();
// 用于存储当前卫星实体的 Map (包括轨道实体)
currentSatelliteEntities: Map<string, { entity: Cesium.Entity; orbitEntity?: Cesium.Entity }> = new Map();
/**
* 初始化 Cesium Viewer
@ -22,10 +37,14 @@ export class HCesiumViewerCls {
*/
initCesiumViewer(container: ConstructorParameters<typeof Cesium.Viewer>[0]) {
this.viewer = new Cesium.Viewer(container, VIEWER_OPTIONS_FN());
// 初始化时清空可能存在的旧实体引用
this.currentStationEntities.clear();
this.currentSatelliteEntities.clear();
}
// region 地面站点相关方法
/**
* 向视图中添加地面站实体
* @param options - 地面站的选项参数
@ -108,13 +127,220 @@ export class HCesiumViewerCls {
}
this.currentStationEntities.clear();
}
// endregion 地面站点相关方法
// region 卫星相关方法
/**
* 向视图中添加卫星实体及其轨道
* @param options - 卫星的选项参数
* @returns 添加的卫星实体对象,如果添加失败则返回 null。
*/
addSatellite(options: SatelliteOptions): Cesium.Entity | null {
if (!this.viewer) {
console.error('视图未初始化。无法添加卫星。');
return null;
}
// 检查是否已存在相同 ID 的实体
if (this.currentSatelliteEntities.has(options.id)) {
console.warn(`ID 为 "${options.id}" 的卫星实体已存在,跳过添加。`);
return this.currentSatelliteEntities.get(options.id)?.entity || null;
}
const {
id,
tle,
orbitDurationHours = 2, // 默认轨道时长 2 小时
timeStepSeconds = 30, // 默认步长 30 秒
showOrbit = true, // 默认显示轨道
} = options;
// --- 从 tle 字符串解析 name, tle1, tle2 ---
const tleLines = tle.trim().split('\n');
if (tleLines.length < 3) {
console.error(`无效的 TLE 格式 (ID: ${id}): TLE 字符串 "${tle}" 至少需要三行`);
return null;
}
const name = tleLines[0].trim();
const tle1 = tleLines[1].trim();
const tle2 = tleLines[2].trim();
// --- 解析结束 ---
let satrec: SatRec;
try {
// 解析 TLE 数据
satrec = twoline2satrec(tle1, tle2);
} catch (error) {
console.error(`解析 TLE 失败 (ID: ${id}):`, error);
return null;
}
// 创建卫星实体
const satelliteEntity = this.viewer.entities.add({
id: id,
name: name,
label: {
text: name,
font: '14pt sans-serif',
style: Cesium.LabelStyle.FILL_AND_OUTLINE,
outlineWidth: 2,
pixelOffset: new Cesium.Cartesian2(0, -10), // 标签偏移
verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
fillColor: Cesium.Color.WHITE,
outlineColor: Cesium.Color.BLACK,
},
// 使用点表示卫星
point: {
pixelSize: 8,
color: Cesium.Color.YELLOW,
outlineColor: Cesium.Color.WHITE,
outlineWidth: 1,
},
// 动态轨迹路径
path: {
resolution: 1,
material: new Cesium.PolylineGlowMaterialProperty({
glowPower: 0.15,
color: Cesium.Color.BLUE,
}),
width: 2,
leadTime: (orbitDurationHours * 3600) / 2, // 显示未来一半时间的轨迹
trailTime: (orbitDurationHours * 3600) / 2, // 显示过去一半时间的轨迹
},
});
// --- 计算轨道 ---
const totalSeconds = orbitDurationHours * 60 * 60;
const startTime = this.viewer.clock.currentTime; // 使用当前 viewer 的时间作为起点
const positionProperty = new Cesium.SampledPositionProperty();
const orbitPositions: Cesium.Cartesian3[] = []; // 用于存储完整轨道点
for (let i = 0; i <= totalSeconds; i += timeStepSeconds) {
const time = Cesium.JulianDate.addSeconds(startTime, i, new Cesium.JulianDate());
const jsDate = Cesium.JulianDate.toDate(time);
try {
const positionAndVelocity = propagate(satrec, jsDate);
if (typeof positionAndVelocity.position === 'boolean') {
// 如果 propagate 返回布尔值,说明计算出错或卫星已衰减
console.warn(`卫星 ${id} 在时间 ${jsDate} 位置计算失败或已衰减。`);
continue; // 跳过这个时间点
}
const gmst = gstime(jsDate);
const positionEcf = eciToEcf(positionAndVelocity.position, gmst);
// 转换为 Cesium 坐标(单位:米)
const cesiumPosition = new Cesium.Cartesian3(positionEcf.x * 1000, positionEcf.y * 1000, positionEcf.z * 1000);
// 添加位置样本
positionProperty.addSample(time, cesiumPosition);
if (showOrbit) {
orbitPositions.push(cesiumPosition);
}
} catch (error) {
console.error(`计算卫星 ${id} 在时间 ${jsDate} 的位置时出错:`, error);
// 可以在这里决定是跳过还是中断循环
continue;
}
}
// 设置卫星的位置和方向
satelliteEntity.position = positionProperty;
satelliteEntity.orientation = new Cesium.VelocityOrientationProperty(positionProperty);
let orbitEntity: Cesium.Entity | undefined;
// 添加完整轨道线(如果需要)
if (showOrbit && orbitPositions.length > 1) {
orbitEntity = this.viewer.entities.add({
id: `${id}-orbit`, // 轨道实体 ID
name: `${name} 轨道`, // 使用解析出的 name
polyline: {
positions: orbitPositions,
width: 1,
material: new Cesium.PolylineDashMaterialProperty({
color: Cesium.Color.CYAN.withAlpha(0.5), // 半透明青色虚线
dashLength: 16,
}),
clampToGround: false, // 轨道不贴地
},
});
}
// 存储实体引用
this.currentSatelliteEntities.set(id, { entity: satelliteEntity, orbitEntity });
return satelliteEntity;
}
/**
* 从视图中移除指定的卫星实体及其轨道 (通过 ID)
* @param entityId - 要移除的卫星实体的 ID
* @returns 如果成功移除则返回 true否则返回 false
*/
removeSatelliteById(entityId: string): boolean {
if (!this.viewer) {
console.error('视图未初始化。无法移除卫星。');
return false;
}
const satelliteData = this.currentSatelliteEntities.get(entityId);
if (satelliteData) {
let removedMain = true;
let removedOrbit = true;
// 移除卫星主体
if (satelliteData.entity) {
removedMain = this.viewer.entities.remove(satelliteData.entity);
if (!removedMain) {
console.warn(`尝试从 Cesium 移除卫星主体 ID 为 "${entityId}" 的实体失败。`);
}
}
// 移除轨道线
if (satelliteData.orbitEntity) {
removedOrbit = this.viewer.entities.remove(satelliteData.orbitEntity);
if (!removedOrbit) {
console.warn(`尝试从 Cesium 移除卫星轨道 ID 为 "${satelliteData.orbitEntity.id}" 的实体失败。`);
}
}
// 如果主体和轨道(如果存在)都移除成功或不存在,则从 Map 中删除
if (removedMain && removedOrbit) {
this.currentSatelliteEntities.delete(entityId);
return true;
} else {
// 如果有任何一个移除失败,保留在 Map 中可能有助于调试,但返回 false
return false;
}
} else {
// console.warn(`未在 Map 中找到 ID 为 "${entityId}" 的卫星实体,无法移除。`); // 可能在 clearAll 时触发
return false; // Map 中未找到
}
}
/** 清除所有卫星实体和轨道 */
clearAllSatellites() {
if (!this.viewer) return;
// 迭代 Map 的 ID 进行移除,避免在迭代 Map 值时修改 Map
const idsToRemove = [...this.currentSatelliteEntities.keys()];
for (const id of idsToRemove) {
this.removeSatelliteById(id); // 使用封装好的移除方法
}
// 确保清空 Map即使移除过程中有失败
this.currentSatelliteEntities.clear();
}
// endregion 卫星相关方法
destroy() {
if (this.viewer) {
this.clearAllGroundStations();
this.clearAllSatellites();
this.viewer.destroy();
this.viewer = null;
}
this.currentStationEntities.clear(); // 确保清空
this.currentSatelliteEntities.clear(); // 确保清空
}
}