Refactor: 重构 HCesiumManager

This commit is contained in:
严浩
2025-04-03 12:35:18 +08:00
parent b1c8597936
commit 8198fefe91
21 changed files with 905 additions and 683 deletions

12
.vscode/settings.json vendored
View File

@ -3,9 +3,7 @@
"source.fixAll.eslint": "explicit",
"source.fixAll.stylelint": "explicit",
"source.fixAll.oxc": "explicit",
// "source.fixAll.eslint": "never",
// "source.fixAll.stylelint": "never",
// "source.fixAll.oxc": "never",
"source.organizeImports": "never"
},
"editor.formatOnSave": true,
@ -27,5 +25,11 @@
"typescript.preferences.autoImportFileExcludePatterns": ["vue-router/auto$"],
"i18n-ally.localesPaths": ["src/locales"],
"i18n-ally.keystyle": "nested",
"oxc.enable": true
"oxc.enable": true,
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "never",
"source.fixAll.stylelint": "never",
"source.fixAll.oxc": "never"
},
"editor.formatOnSave": false
}

View File

@ -85,6 +85,7 @@ export default defineConfigWithVueTs(
'perfectionist/sort-classes': 'off',
'perfectionist/sort-objects': 'off',
'perfectionist/sort-imports': ['error'],
'perfectionist/sort-modules':'off'
},
},
// endregion <<< eslint-plugin-perfectionist <<<

View File

@ -9,6 +9,7 @@
"all": "run-p build-only format type-check lint",
"build": "run-p type-check \"build-only {@}\" --",
"build-only": "vite build",
"lint-format": "run-p lint:oxlint lint:eslint format",
"format": "prettier --write src/",
"type-check": "vue-tsc --build",
"lint": "run-s lint:*",

View File

@ -0,0 +1,101 @@
import * as Cesium from 'cesium';
import { eciToEcf, gstime, propagate, type SatRec, twoline2satrec } from 'satellite.js';
import type { I卫星 } from '../managers/HCesiumManager.types'; // 保持类型定义位置
const = 2 * 60 * 60; // 2小时
const = 30;
export interface OrbitCalculationResult {
orbitPositions: Cesium.Cartesian3[]; // 用于绘制完整轨道线
sampledPositionProperty: Cesium.SampledPositionProperty;
}
export interface OrbitSample {
position: Cesium.Cartesian3;
time: Cesium.JulianDate;
}
export class SatelliteCalculator {
/**
* 解析 TLE 字符串获取卫星记录对象。
* @param tle - 包含名称和两行数据的 TLE 字符串。
* @param satelliteId - 用于错误日志的卫星 ID。
* @returns 解析成功返回 SatRec 对象,否则返回 null。
*/
parseTle(tle: string, satelliteId: string): null | SatRec {
const tleLines = tle.trim().split('\n');
if (tleLines.length < 3) {
console.error(`无效的 TLE 格式 (ID: ${satelliteId}): TLE 字符串至少需要三行`);
return null;
}
const tle1 = tleLines[1].trim();
const tle2 = tleLines[2].trim();
try {
return twoline2satrec(tle1, tle2);
} catch (error) {
console.error(`解析 TLE 失败 (ID: ${satelliteId}):`, error);
return null;
}
}
/**
* 计算卫星在给定时间段内的轨道位置。
* @param satrec - 卫星记录对象。
* @param startTime - 计算轨道的开始时间。
* @param options - 包含轨道时长、步长等选项。
* @param satelliteId - 用于日志记录的卫星 ID。
* @returns 包含 SampledPositionProperty 和轨道点数组的对象,如果计算失败则返回 null。
*/
calculateOrbit(
satrec: SatRec,
startTime: Cesium.JulianDate,
options: Pick<I卫星, 'orbitDurationSeconds' | 'showOrbit' | 'timeStepSeconds'>,
satelliteId: string,
): null | OrbitCalculationResult {
const { orbitDurationSeconds = , timeStepSeconds = , showOrbit = true } = options;
const sampledPositionProperty = new Cesium.SampledPositionProperty();
const orbitPositions: Cesium.Cartesian3[] = [];
let hasSamples = false; // 标记是否成功添加了至少一个样本点
for (let i = 0; i <= orbitDurationSeconds; 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') {
console.warn(`卫星 ${satelliteId} 在时间 ${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);
// 添加位置样本
sampledPositionProperty.addSample(time, cesiumPosition);
hasSamples = true; // 标记已添加样本
if (showOrbit) {
orbitPositions.push(cesiumPosition);
}
} catch (error) {
console.error(`计算卫星 ${satelliteId} 在时间 ${jsDate} 的位置时出错:`, error);
// 可以在这里决定是跳过还是中断循环
continue;
}
}
// 确保至少有一个样本点,否则 SampledPositionProperty 会有问题
if (!hasSamples) {
console.warn(`卫星 ${satelliteId} 未能计算出任何有效轨道点。`);
return null;
}
return { sampledPositionProperty, orbitPositions };
}
}

View File

@ -1,472 +0,0 @@
import * as Cesium from 'cesium';
import {
eciToEcf,
gstime,
propagate,
type SatRec,
twoline2satrec,
} from 'satellite.js';
import type {
GroundStationOptions,
SatelliteOptions,
} from './h-cesium-viewer-class.types';
import { VIEWER_OPTIONS_FN } from './helper/_VIEWER_OPTIONS';
import { configureCesium } from './helper/configureCesium';
import { configureTimeLine } from './helper/configureTimeLine';
const = 2 * 60 * 60; // 2小时
export {
type GroundStationOptions,
type SatelliteOptions,
} from './h-cesium-viewer-class.types';
Cesium.Ion.defaultAccessToken = import.meta.env.VITE_CESIUM_ION_TOKEN; // 用了离线地图的情况是不需要的。
Object.assign(globalThis, { Cesium });
configureCesium();
export class HCesiumViewerCls {
viewer: Cesium.Viewer | null = null;
// 用于存储当前地面站实体的 Map
currentStationEntities: Map<string, Cesium.Entity> = new Map();
// 用于存储当前卫星实体的 Map (包括轨道实体)
currentSatelliteEntities: Map<
string,
{
coverageEntity?: Cesium.Entity;
entity: Cesium.Entity;
orbitEntity?: Cesium.Entity;
}
> = new Map();
/**
* 初始化 Cesium Viewer
* @param container - 用于承载 Cesium Viewer 的 DOM 元素或其 ID
*/
initCesiumViewer(container: ConstructorParameters<typeof Cesium.Viewer>[0]) {
this.viewer = new Cesium.Viewer(container, VIEWER_OPTIONS_FN());
configureTimeLine(this.viewer);
this.viewer.scene.debugShowFramesPerSecond = true;
// 初始化时清空可能存在的旧实体引用
this.currentStationEntities.clear();
this.currentSatelliteEntities.clear();
}
// region 地面站点相关方法
/**
* 向视图中添加地面站实体
* @param options - 地面站的选项参数
* @returns 添加的实体对象,如果添加失败则返回 null。
*/
addGroundStation(options: GroundStationOptions): Cesium.Entity | null {
if (!this.viewer) {
console.error('视图未初始化。无法添加地面站。');
return null;
}
// 检查是否已存在相同 ID 的实体
if (this.currentStationEntities.has(options.id)) {
console.warn(`ID 为 "${options.id}" 的地面站实体已存在,跳过添加。`);
return this.currentStationEntities.get(options.id) || null;
}
// 解构赋值获取站点信息
const {
height = 0,
id,
latitude,
longitude,
name,
pixelSize = 10,
} = options;
const position = Cesium.Cartesian3.fromDegrees(longitude, latitude, height);
const groundStationEntity = new Cesium.Entity({
// 使用传入的 id 作为实体的唯一标识符
id,
label: {
font: '14pt sans-serif',
outlineWidth: 2,
pixelOffset: new Cesium.Cartesian2(0, -12), // 标签略微偏移到点的上方
style: Cesium.LabelStyle.FILL_AND_OUTLINE,
text: name,
verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
},
name,
point: {
color: Cesium.Color.fromRandom(), // 随机颜色
outlineColor: Cesium.Color.WHITE,
outlineWidth: 2,
pixelSize,
},
position,
});
const addedEntity = this.viewer.entities.add(groundStationEntity);
// 添加成功后,将其存入 Map
this.currentStationEntities.set(id, addedEntity);
return addedEntity;
}
/**
* 从视图中移除指定的地面站实体 (通过 ID)
* @param entityId - 要移除的地面站实体的 ID
* @returns 如果成功移除则返回 true否则返回 false
*/
removeGroundStationById(entityId: string): boolean {
if (!this.viewer) {
console.error('视图未初始化。无法移除地面站。');
return false;
}
const entityToRemove = this.currentStationEntities.get(entityId);
if (entityToRemove) {
const removed = this.viewer.entities.remove(entityToRemove);
if (removed) {
// 移除成功后,从 Map 中删除
this.currentStationEntities.delete(entityId);
return true;
} else {
console.warn(`尝试从 Cesium 移除 ID 为 "${entityId}" 的实体失败。`);
return false; // Cesium 移除失败
}
} else {
// console.warn(`未在 Map 中找到 ID 为 "${entityId}" 的地面站实体,无法移除。`); // 可能在 clearAll 时触发,不一定是警告
return false; // Map 中未找到
}
}
clearAllGroundStations() {
if (!this.viewer) return;
for (const entity of this.currentStationEntities.values()) {
this.viewer?.entities.remove(entity);
}
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,
orbitDurationSeconds = ,
timeStepSeconds = 30, // 默认步长 30 秒
showOrbit = true, // 默认显示轨道
} = options;
// --- 从 tle 字符串解析 name, tle1, tle2 ---
const tleLines = tle.trim().split('\n') as [string, string, string];
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 randomBaseColor = Cesium.Color.fromRandom();
// 创建卫星实体
const satelliteEntity = this.viewer.entities.add({
id,
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: randomBaseColor, // 使用随机基色
outlineColor: Cesium.Color.WHITE,
outlineWidth: 1,
},
// 动态轨迹路径
path: {
resolution: 1,
material: new Cesium.PolylineGlowMaterialProperty({
glowPower: 0.15,
color: randomBaseColor, // 使用随机基色
}),
width: 2,
leadTime: orbitDurationSeconds / 2, // 显示未来一半时间的轨迹
trailTime: orbitDurationSeconds / 2, // 显示过去一半时间的轨迹
},
});
// --- 计算轨道 ---
const startTime = this.viewer.clock.currentTime; // 使用当前 viewer 的时间作为起点
const positionProperty = new Cesium.SampledPositionProperty();
const orbitPositions: Cesium.Cartesian3[] = []; // 用于存储完整轨道点
for (let i = 0; i <= orbitDurationSeconds; 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,
);
// --- 添加卫星地面覆盖范围 ---
// 使用 CallbackProperty 动态计算星下点位置
const subsatellitePosition = new Cesium.CallbackPositionProperty(
(time, result) => {
// 从 satelliteEntity 获取当前时间的精确位置
const satelliteCartesian = positionProperty.getValue(time, result);
if (!satelliteCartesian) {
return; // 如果位置无效,则不返回任何内容
}
// 转换为地理坐标(包含高度)
const satelliteCartographic =
Cesium.Cartographic.fromCartesian(satelliteCartesian);
if (!satelliteCartographic) {
return; // 如果转换失败,则不返回任何内容
}
// 创建星下点地理坐标高度设为0
const subsatelliteCartographic = new Cesium.Cartographic(
satelliteCartographic.longitude,
satelliteCartographic.latitude,
0,
);
// 转换回笛卡尔坐标
return Cesium.Cartographic.toCartesian(
subsatelliteCartographic,
Cesium.Ellipsoid.WGS84,
result,
);
},
false,
Cesium.ReferenceFrame.FIXED,
); // isConstant: false, referenceFrame: FIXED
// 使用 CallbackProperty 动态计算覆盖半径 (基于高度的简单估算)
const coverageRadius = new Cesium.CallbackProperty((time) => {
const satelliteCartesian = positionProperty.getValue(time);
if (!satelliteCartesian) {
return 100_000; // 默认半径 100km
}
const satelliteCartographic =
Cesium.Cartographic.fromCartesian(satelliteCartesian);
if (!satelliteCartographic) {
return 100_000;
}
const altitude = satelliteCartographic.height;
// 简化的估算:半径约为高度的 0.8 倍,最小 50km
// 实际应用中应基于卫星的视场角 (FOV) 或波束宽度计算
const calculatedRadius = altitude * 0.8;
return Math.max(calculatedRadius, 50_000); // 最小半径 50km
}, false); // isConstant 设置为 false
const coverageEntity = this.viewer.entities.add({
id: `${id}-coverage`,
name: `${name} 覆盖范围`,
position: subsatellitePosition,
ellipse: {
semiMajorAxis: coverageRadius,
semiMinorAxis: coverageRadius, // 假设圆形覆盖
material: randomBaseColor.withAlpha(0.2 + 0.3), // 基于随机基色的半透明填充
heightReference: Cesium.HeightReference.CLAMP_TO_GROUND, // 贴合地形
outline: true,
outlineColor: randomBaseColor.withAlpha(0.8), // 基于随机基色的较深半透明轮廓
outlineWidth: 2, //
granularity: Cesium.Math.toRadians(1), // 控制椭圆边缘平滑度
},
});
// --- 覆盖范围结束 ---
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: randomBaseColor.withAlpha(0.5), // 基于随机基色的半透明虚线
dashLength: 16,
}),
clampToGround: false, // 轨道不贴地
},
});
}
// 存储实体引用
this.currentSatelliteEntities.set(id, {
entity: satelliteEntity,
orbitEntity,
coverageEntity,
});
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;
let removedCoverage = 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}" 的实体失败。`,
);
}
}
// 移除覆盖范围
if (satelliteData.coverageEntity) {
removedCoverage = this.viewer.entities.remove(
satelliteData.coverageEntity,
);
if (!removedCoverage) {
console.warn(
`尝试从 Cesium 移除卫星覆盖范围 ID 为 "${satelliteData.coverageEntity.id}" 的实体失败。`,
);
}
}
// 如果主体和轨道(如果存在)都移除成功或不存在,则从 Map 中删除
// 如果主体、轨道(如果存在)和覆盖范围(如果存在)都移除成功或不存在,则从 Map 中删除
if (removedMain && removedOrbit && removedCoverage) {
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(); // 确保清空
}
}

View File

@ -1,9 +1,9 @@
<script setup lang="ts">
import type { GroundStationState, SatelliteState } from './types';
import type { GroundStationState, SatelliteState } from './useHCesiumViewerCls.types';
import { useHCesiumViewerCls } from './useHCesiumViewerCls';
import { useHCesiumViewerClsGroundStation } from './useHCesiumViewerClsGroundStation';
import { useHCesiumViewerClsSatellite } from './useHCesiumViewerClsSatellite';
import { useHCesiumViewerClsSatellite } from './useHCesiumViewerCls.卫星';
import { useHCesiumViewerClsGroundStation } from './useHCesiumViewerCls.站点';
import 'cesium/Build/Cesium/Widgets/widgets.css';
@ -13,19 +13,19 @@ const props = defineProps<{
}>();
// 1. 管理 Cesium Viewer 实例生命周期
const { hCesiumViewerInst } = useHCesiumViewerCls('cesium-container');
const { hCesiumViewerManager } = useHCesiumViewerCls('cesium-container'); // 获取新的 Manager 实例
// 2. 同步地面站实体
// 将实例的 getter 和 props 的 getter 传递给组合函数
useHCesiumViewerClsGroundStation(
() => hCesiumViewerInst, // 传递 getter 以处理实例可能为 null 的情况
() => hCesiumViewerManager, // 传递 Manager 实例的 getter
() => props.groundStationState?.groundStations, // 从新的 prop 中获取列表
() => props.groundStationState?.selectedIds, // 从新的 prop 中获取选中 ID
);
// 3. 同步卫星实体
useHCesiumViewerClsSatellite(
() => hCesiumViewerInst, // 传递 viewer 实例 getter
() => hCesiumViewerManager, // 传递 Manager 实例 getter
() => props.satelliteState?.satellites, // 传递卫星列表 getter
() => props.satelliteState?.selectedIds, // 传递选中卫星 ID getter
);

View File

@ -6,9 +6,7 @@ export const VIEWER_OPTIONS_FN = (): Viewer.ConstructorOptions => {
return {
animation: true, // .cesium-viewer-animationContainer https://cesium.com/learn/ion-sdk/ref-doc/Animation.html
baseLayer: Cesium.ImageryLayer.fromProviderAsync(
Cesium.TileMapServiceImageryProvider.fromUrl(
Cesium.buildModuleUrl('Assets/Textures/NaturalEarthII'),
),
Cesium.TileMapServiceImageryProvider.fromUrl(Cesium.buildModuleUrl('Assets/Textures/NaturalEarthII')),
),
baseLayerPicker: false,
fullscreenButton: !true, // 全屏按钮

View File

@ -20,30 +20,18 @@ export function configureCesium() {
// Animation 的时间日期格式化
Cesium.AnimationViewModel.defaultDateFormatter = function (date) {
const dataZone8 = Cesium.JulianDate.addMinutes(
date,
minutes,
new Cesium.JulianDate(),
);
const dataZone8 = Cesium.JulianDate.addMinutes(date, minutes, new Cesium.JulianDate());
return Cesium.JulianDate.toIso8601(dataZone8).slice(0, 10);
};
Cesium.AnimationViewModel.defaultTimeFormatter = function (time) {
const dataZone8 = Cesium.JulianDate.addMinutes(
time,
minutes,
new Cesium.JulianDate(),
);
const dataZone8 = Cesium.JulianDate.addMinutes(time, minutes, new Cesium.JulianDate());
return Cesium.JulianDate.toIso8601(dataZone8).slice(11, 19);
};
// Timeline 的时间日期格式化
// @ts-expect-error node_modules/@cesium/widgets/Source/Timeline/Timeline.js
Cesium.Timeline.prototype.makeLabel = function (time) {
const dataZone8 = Cesium.JulianDate.addMinutes(
time,
minutes,
new Cesium.JulianDate(),
);
const dataZone8 = Cesium.JulianDate.addMinutes(time, minutes, new Cesium.JulianDate());
return Cesium.JulianDate.toIso8601(dataZone8).slice(0, 19);
};
}

View File

@ -19,11 +19,8 @@ export function configureMapTile(viewer: Cesium.Viewer) {
tooltip: '高德地图',
});
// 设置高德地图为默认图层
viewer.baseLayerPicker.viewModel.imageryProviderViewModels.unshift(
customLayerViewModel,
);
const selectedViewModel =
viewer.baseLayerPicker.viewModel.imageryProviderViewModels[0];
viewer.baseLayerPicker.viewModel.imageryProviderViewModels.unshift(customLayerViewModel);
const selectedViewModel = viewer.baseLayerPicker.viewModel.imageryProviderViewModels[0];
if (!selectedViewModel) {
console.error('未找到默认底图');
return;

View File

@ -2,16 +2,9 @@ import * as Cesium from 'cesium';
const = 2 * 60 * 60; // 2小时
export function configureTimeLine(
viewer: Cesium.Viewer,
totalSeconds = ,
) {
export function configureTimeLine(viewer: Cesium.Viewer, totalSeconds = ) {
const start = Cesium.JulianDate.fromIso8601(new Date().toISOString());
const stop = Cesium.JulianDate.addSeconds(
start,
totalSeconds,
new Cesium.JulianDate(),
);
const stop = Cesium.JulianDate.addSeconds(start, totalSeconds, new Cesium.JulianDate());
// 设置时钟范围
viewer.clock.startTime = start.clone();

View File

@ -0,0 +1,150 @@
import * as Cesium from 'cesium';
import { VIEWER_OPTIONS_FN } from '../helper/_VIEWER_OPTIONS';
import { configureCesium } from '../helper/configureCesium';
import { configureTimeLine } from '../helper/configureTimeLine';
// Cesium Ion Token 和全局配置,暂时保留在此处,也可考虑移至更全局的初始化位置
Cesium.Ion.defaultAccessToken = import.meta.env.VITE_CESIUM_ION_TOKEN;
Object.assign(globalThis, { Cesium });
configureCesium();
export class HCesiumManager {
viewer: Cesium.Viewer | null = null;
private containerId: Element | null | string = null;
/**
* 初始化 Cesium Viewer。
* @param container - 用于承载 Cesium Viewer 的 DOM 元素或其 ID。
* @returns 返回创建的 Viewer 实例,如果失败则返回 null。
*/
init(container: ConstructorParameters<typeof Cesium.Viewer>[0]): Cesium.Viewer | null {
if (this.viewer) {
console.warn('Cesium Viewer 已初始化,请先销毁再重新初始化。');
return this.viewer;
}
try {
this.containerId = container; // 保存 container 引用或 ID
this.viewer = new Cesium.Viewer(container, VIEWER_OPTIONS_FN());
configureTimeLine(this.viewer);
// 可以在这里添加其他 Viewer 级别的配置
this.viewer.scene.debugShowFramesPerSecond = true; // 例如 FPS 显示
console.log('Cesium Viewer 初始化成功。');
return this.viewer;
} catch (error) {
console.error('Cesium Viewer 初始化失败:', error);
this.viewer = null;
return null;
}
}
/**
* 销毁 Cesium Viewer 实例。
*/
destroy(): void {
if (this.viewer && !this.viewer.isDestroyed()) {
try {
// 在销毁 Viewer 前,确保所有通过此 Manager 添加的实体或数据源已被移除
// (或者依赖各个子 Manager 在销毁前自行清理)
// this.viewer.entities.removeAll(); // 谨慎使用,可能会移除不应移除的实体
// this.viewer.dataSources.removeAll(true); // 谨慎使用
this.viewer.destroy();
console.log('Cesium Viewer 已销毁。');
} catch (error) {
console.error('销毁 Cesium Viewer 时出错:', error);
} finally {
this.viewer = null;
this.containerId = null;
}
} else {
// console.log('Cesium Viewer 未初始化或已被销毁,无需再次销毁。');
this.viewer = null; // 确保 viewer 为 null
this.containerId = null;
}
}
/**
* 向 Viewer 添加一个实体。
* @param entity - 要添加的 Cesium 实体。
* @returns 返回添加的实体,如果 Viewer 未初始化则返回 null。
*/
addEntity(entity: Cesium.Entity): Cesium.Entity | null {
if (!this.viewer) {
console.error('Viewer 未初始化,无法添加实体。');
return null;
}
try {
return this.viewer.entities.add(entity);
} catch (error) {
console.error('添加实体时出错:', error, entity);
return null;
}
}
/**
* 根据 ID 从 Viewer 移除一个实体。
* @param entityId - 要移除的实体的 ID。
* @returns 如果成功移除则返回 true否则返回 false。
*/
removeEntityById(entityId: string): boolean {
if (!this.viewer) {
console.error('Viewer 未初始化,无法移除实体。');
return false;
}
try {
return this.viewer.entities.removeById(entityId);
} catch (error) {
console.error(`移除 ID 为 "${entityId}" 的实体时出错:`, error);
return false;
}
}
/**
* 根据实体对象从 Viewer 移除一个实体。
* @param entity - 要移除的实体对象。
* @returns 如果成功移除则返回 true否则返回 false。
*/
removeEntity(entity: Cesium.Entity): boolean {
if (!this.viewer) {
console.error('Viewer 未初始化,无法移除实体。');
return false;
}
try {
return this.viewer.entities.remove(entity);
} catch (error) {
console.error('移除实体时出错:', error, entity);
return false;
}
}
/**
* 根据 ID 获取 Viewer 中的实体。
* @param entityId - 要获取的实体的 ID。
* @returns 返回找到的实体,如果未找到或 Viewer 未初始化则返回 undefined。
*/
getEntityById(entityId: string): Cesium.Entity | undefined {
if (!this.viewer) {
console.error('Viewer 未初始化,无法获取实体。');
return undefined;
}
try {
return this.viewer.entities.getById(entityId);
} catch (error) {
console.error(`获取 ID 为 "${entityId}" 的实体时出错:`, error);
return undefined;
}
}
/**
* 获取当前的 Cesium Viewer 实例。
* @returns 返回 Viewer 实例,如果未初始化则返回 null。
*/
getViewer(): Cesium.Viewer | null {
return this.viewer;
}
}

View File

@ -1,4 +1,4 @@
export interface GroundStationOptions {
export interface I站点 {
height?: number; // 可选高度默认为0
id: string; // 站点的唯一标识符
latitude: number;
@ -8,7 +8,7 @@ export interface GroundStationOptions {
}
// 卫星选项接口
export interface SatelliteOptions {
export interface I卫星 {
id: string; // 卫星的唯一标识符
orbitDurationSeconds?: number; // 轨道显示时长
showOrbit?: boolean; // 是否显示完整轨道线,默认为 true

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

View File

@ -0,0 +1,124 @@
import * as Cesium from 'cesium';
import type { HCesiumManager } from './HCesiumManager';
import type { I站点 } from './HCesiumManager.types';
export class GroundStationManager {
private viewerManager: HCesiumManager;
// 用于存储当前由此管理器管理的地面站实体的 Map
private currentStationEntities: Map<string, Cesium.Entity> = new Map();
constructor(viewerManager: HCesiumManager) {
this.viewerManager = viewerManager;
}
/**
* 向视图中添加或更新地面站实体。
* 注意:当前实现仅添加,如果已存在则警告并返回现有实体。
* @param options - 地面站的选项参数
* @returns 添加的实体对象,如果已存在或添加失败则返回 null 或现有实体。
*/
addOrUpdateStation(options: I站点): Cesium.Entity | null {
const existingEntity = this.currentStationEntities.get(options.id);
if (existingEntity) {
console.warn(`ID 为 "${options.id}" 的地面站实体已由管理器追踪,跳过添加。`);
// 未来可以扩展为更新逻辑
return existingEntity;
}
// 解构赋值获取站点信息
const { height = 0, id, latitude, longitude, name, pixelSize = 10 } = options;
const position = Cesium.Cartesian3.fromDegrees(longitude, latitude, height);
const groundStationEntity = new Cesium.Entity({
id, // 使用传入的 id 作为实体的唯一标识符
name,
position,
point: {
pixelSize,
color: Cesium.Color.fromRandom({ alpha: 1 }), // 确保 alpha 为 1
outlineColor: Cesium.Color.WHITE,
outlineWidth: 2,
},
label: {
text: name,
font: '14pt sans-serif',
style: Cesium.LabelStyle.FILL_AND_OUTLINE,
outlineWidth: 2,
verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
pixelOffset: new Cesium.Cartesian2(0, -12), // 标签略微偏移到点的上方
},
});
const addedEntity = this.viewerManager.addEntity(groundStationEntity);
if (addedEntity) {
// 添加成功后,将其存入 Map
this.currentStationEntities.set(id, addedEntity);
return addedEntity;
} else {
console.error(`通过 ViewerManager 添加 ID 为 "${id}" 的地面站实体失败。`);
return null;
}
}
/**
* 从视图中移除指定的地面站实体 (通过 ID)。
* @param entityId - 要移除的地面站实体的 ID。
* @returns 如果成功移除则返回 true否则返回 false。
*/
removeStation(entityId: string): boolean {
const entityToRemove = this.currentStationEntities.get(entityId);
if (!entityToRemove) {
// console.warn(`未在 GroundStationManager 中找到 ID 为 "${entityId}" 的实体,无法移除。`);
return false; // 不在管理范围内
}
const removed = this.viewerManager.removeEntity(entityToRemove); // 优先使用实体对象移除
if (removed) {
// 移除成功后,从 Map 中删除
this.currentStationEntities.delete(entityId);
return true;
} else {
console.warn(`尝试通过 ViewerManager 移除 ID 为 "${entityId}" 的地面站实体失败。`);
// 即使 ViewerManager 移除失败,也尝试从内部 Map 中移除,以保持一致性?
// 或者保留在 Map 中以供调试?暂时选择移除。
this.currentStationEntities.delete(entityId);
return false;
}
}
/**
* 清除所有由此管理器管理的地面站实体。
*/
clearAllStations(): void {
// 迭代 Map 的 ID 进行移除,避免在迭代 Map 值时修改 Map
const idsToRemove = [...this.currentStationEntities.keys()];
let removalFailed = false;
for (const id of idsToRemove) {
if (!this.removeStation(id)) {
removalFailed = true; // 记录是否有移除失败的情况
}
}
if (removalFailed) {
console.warn('清除部分地面站实体时遇到问题。');
}
// 确保最终清空 Map
this.currentStationEntities.clear();
}
/**
* 获取当前管理的地面站实体 Map。
* @returns 返回包含当前地面站实体的 Map。
*/
getCurrentStationEntities(): Map<string, Cesium.Entity> {
return this.currentStationEntities;
}
/**
* 销毁管理器,清理资源。
*/
destroy(): void {
this.clearAllStations();
// console.log('GroundStationManager 已销毁。');
}
}

View File

@ -1,25 +1,27 @@
// src/utils/useHCesiumViewerCls.ts
import { HCesiumViewerCls } from './h-cesium-viewer-class';
import { HCesiumManager } from './managers/HCesiumManager'; // 导入新的 Manager
/**
* 管理 HCesiumViewerCls 实例的生命周期。
* 管理 HCesiumViewerManager 实例的生命周期。
* @param containerId - Cesium Viewer 容器的 DOM ID。
* @returns 返回 HCesiumViewerCls 实例
* @returns 返回包含 HCesiumViewerManager 实例的对象
*/
export function useHCesiumViewerCls(containerId: string) {
const hCesiumViewerInst = new HCesiumViewerCls();
if ($__DEV__) Object.assign(globalThis, { hCesiumViewerInst }); // 在开发模式下暴露到全局
const hCesiumViewerManager = new HCesiumManager();
// 可以在开发模式下暴露 manager 实例,方便调试
if ($__DEV__) Object.assign(globalThis, { hCesiumViewerManager });
onMounted(() => {
hCesiumViewerInst.initCesiumViewer(containerId);
hCesiumViewerManager.init(containerId);
});
onBeforeUnmount(() => {
hCesiumViewerInst.destroy();
hCesiumViewerManager.destroy();
});
// 返回 Manager 实例,供其他 Composable 或组件使用
return {
hCesiumViewerInst,
hCesiumViewerManager,
};
}

View File

@ -1,7 +1,4 @@
import type {
GroundStationOptions,
SatelliteOptions,
} from './h-cesium-viewer-class';
import type { I卫星, I站点 } from './managers/HCesiumManager.types'; // 从 .types 文件导入
/**
*
@ -9,7 +6,7 @@ import type {
*/
export interface GroundStationState {
/** 地面站配置数组 */
groundStations: GroundStationOptions[];
groundStations: I站点[];
/** 选中的地面站 ID 集合 */
selectedIds: Set<string>;
}
@ -20,7 +17,7 @@ export interface GroundStationState {
*/
export interface SatelliteState {
/** 卫星配置数组 */
satellites: SatelliteOptions[];
satellites: I卫星[];
/** 选中的卫星 ID 集合 */
selectedIds: Set<string>;
}

View File

@ -0,0 +1,102 @@
// src/components/h-cesium-viewer/useHCesiumViewerClsSatellite.ts
import type { MaybeRefOrGetter } from 'vue';
import type { HCesiumManager } from './managers/HCesiumManager'; // 导入新的 Viewer Manager
import type { I卫星 } from './managers/HCesiumManager.types'; // 类型定义保持不变
// ESLint: Add blank line between type and value imports
import { SatelliteCalculator } from './calculators/SatelliteCalculator'; // 导入计算器
import { SatelliteManager } from './managers/HCesiumManager.卫星'; // 导入 Satellite Manager
/**
* 管理 Cesium Viewer 中的卫星实体,根据选中的 ID 列表进行同步。
* @param hCesiumViewerManager - HCesiumViewerManager 实例或其 getter。
* @param satelliteList - 包含所有可用卫星选项的数组或 getter。
* @param selectedSatelliteIds - 包含当前选中卫星 ID 的 Set 或 getter。
*/
export function useHCesiumViewerClsSatellite(
hCesiumViewerManager: MaybeRefOrGetter<HCesiumManager | null>, // 更新参数类型和名称
satelliteList: MaybeRefOrGetter<Array<I卫星> | undefined>,
selectedSatelliteIds: MaybeRefOrGetter<Set<string> | undefined>,
) {
// SatelliteManager 和 Calculator 实例引用
const satelliteManager = ref<null | SatelliteManager>(null); // 正确的类型顺序
// Calculator 通常是无状态的,可以在 setup 作用域创建一次
const satelliteCalculator = new SatelliteCalculator();
// 创建一个从 ID 到卫星选项的映射,方便查找
const satelliteMap = computed(() => {
const map = new Map<string, I卫星>();
const list = toValue(satelliteList) ?? [];
for (const satellite of list) {
map.set(satellite.id, satellite);
}
return map;
});
// 使用 watch 显式监听依赖项
watch(
[hCesiumViewerManager, satelliteList, selectedSatelliteIds], // 监听这些源的变化
() => {
// 回调函数
const viewerManagerInstance = toValue(hCesiumViewerManager);
// 检查 Viewer Manager 和内部 Viewer 实例是否存在
if (!viewerManagerInstance || !viewerManagerInstance.getViewer()) {
// 如果 viewer manager 或 viewer 实例尚未初始化或已销毁,则清理旧 manager 并返回
satelliteManager.value?.destroy();
satelliteManager.value = null;
return;
}
// 确保 SatelliteManager 实例存在且与当前的 ViewerManager 关联
if (!satelliteManager.value) {
// 创建 SatelliteManager 时传入 ViewerManager 和 Calculator
satelliteManager.value = new SatelliteManager(viewerManagerInstance, satelliteCalculator);
}
const manager = satelliteManager.value; // 使用 manager 实例
const selectedIdsSet = toValue(selectedSatelliteIds) ?? new Set<string>(); // 直接获取 Set如果为 undefined 则创建空 Set
const currentEntityIds = new Set(manager.getCurrentSatelliteEntities().keys()); // 从 manager 获取当前实体 ID
// 1. 移除不再选中的卫星
for (const entityId of currentEntityIds) {
if (!selectedIdsSet.has(entityId)) {
manager.removeSatellite(entityId); // 使用 manager 的移除方法
}
}
// 2. 添加新增的选中卫星
for (const selectedId of selectedIdsSet) {
if (!currentEntityIds.has(selectedId)) {
const satelliteToAdd = satelliteMap.value.get(selectedId);
if (satelliteToAdd) {
// console.debug(
// `尝试添加卫星 "${satelliteToAdd.id}" 到地图`, // 简化日志
// );
manager.addOrUpdateSatellite(satelliteToAdd); // 使用 manager 的添加/更新方法
} else {
// 如果在 satelliteList 中找不到对应的卫星信息,发出警告
console.warn(`无法找到 ID 为 "${selectedId}" 的卫星信息,无法添加到地图。`);
}
}
} // <--- 移除这个多余的括号
// watch 的回调函数不需要 onCleanup
},
{ immediate: true, deep: false }, // 立即执行一次,非深度监听
); // watch 结束
// 组件卸载时确保最终清理
onBeforeUnmount(() => {
// console.log('Unmounting component, ensuring SatelliteManager cleanup');
satelliteManager.value?.destroy();
satelliteManager.value = null; // 明确置空
});
// 返回 satelliteMap 可能在某些场景下有用
return {
satelliteMap,
};
}

View File

@ -0,0 +1,92 @@
// src/utils/useHCesiumViewerClsGroundStation.ts
import type { MaybeRefOrGetter } from 'vue';
import type { HCesiumManager } from './managers/HCesiumManager'; // 导入新的 Viewer Manager
import type { I站点 } from './managers/HCesiumManager.types'; // 类型定义保持不变
import { GroundStationManager } from './managers/HCesiumManager.站点'; // 导入 GroundStation Manager
/**
* 管理 Cesium Viewer 中的地面站实体,根据选中的 ID 列表进行同步。
* @param hCesiumViewerManager - HCesiumViewerManager 实例或其 getter。
* @param groundStationList - 包含所有可用地面站选项的数组或 getter。
* @param selectedStationIds - 包含当前选中地面站 ID 的 Set 或 getter。
*/
export function useHCesiumViewerClsGroundStation(
hCesiumViewerManager: MaybeRefOrGetter<HCesiumManager | null>, // 更新参数类型和名称
groundStationList: MaybeRefOrGetter<Array<I站点> | undefined>,
selectedStationIds: MaybeRefOrGetter<Set<string> | undefined>,
) {
// GroundStationManager 实例引用
const groundStationManager = ref<GroundStationManager | null>(null);
// 创建一个从 ID 到站点选项的映射,方便查找
const stationMap = computed(() => {
const map = new Map<string, I站点>();
const list = toValue(groundStationList) ?? [];
for (const station of list) {
map.set(station.id, station);
}
return map;
});
// 使用 watch 显式监听依赖项
watch(
[hCesiumViewerManager, groundStationList, selectedStationIds], // 监听这些源的变化
() => {
// 回调函数
const viewerManagerInstance = toValue(hCesiumViewerManager);
// 检查 Viewer Manager 和内部 Viewer 实例是否存在
if (!viewerManagerInstance || !viewerManagerInstance.getViewer()) {
// 如果 viewer manager 或 viewer 实例尚未初始化或已销毁,则清理旧 manager 并返回
groundStationManager.value?.destroy();
groundStationManager.value = null;
return;
}
// 确保 GroundStationManager 实例存在且与当前的 ViewerManager 关联
if (!groundStationManager.value) {
groundStationManager.value = new GroundStationManager(viewerManagerInstance);
}
const manager = groundStationManager.value; // 使用 manager 实例
const selectedIdsSet = toValue(selectedStationIds) ?? new Set<string>(); // 直接获取 Set如果为 undefined 则创建空 Set
const currentEntityIds = new Set(manager.getCurrentStationEntities().keys()); // 从 manager 获取当前实体 ID
// 1. 移除不再选中的站点
for (const entityId of currentEntityIds) {
if (!selectedIdsSet.has(entityId)) {
manager.removeStation(entityId); // 使用 manager 的移除方法
}
}
// 2. 添加新增的选中站点
for (const selectedId of selectedIdsSet) {
if (!currentEntityIds.has(selectedId)) {
const stationToAdd = stationMap.value.get(selectedId);
if (stationToAdd) {
manager.addOrUpdateStation(stationToAdd); // 使用 manager 的添加/更新方法
} else {
// 如果在 groundStationList 中找不到对应的站点信息,发出警告
console.warn(`无法找到 ID 为 "${selectedId}" 的站点信息,无法添加到地图。`);
}
}
}
},
{ immediate: true, deep: false }, // 立即执行一次,非深度监听(通常足够)
); // watch 结束
// 组件卸载时确保最终清理
onBeforeUnmount(() => {
// console.log('Unmounting component, ensuring GroundStationManager cleanup');
groundStationManager.value?.destroy();
groundStationManager.value = null; // 明确置空
});
// 返回 stationMap 可能在某些场景下有用,例如调试或扩展
return {
stationMap, // 可以考虑是否真的需要返回这个
};
}

View File

@ -1,73 +0,0 @@
// src/utils/useHCesiumViewerClsGroundStation.ts
import type { MaybeRefOrGetter } from 'vue';
import type {
GroundStationOptions,
HCesiumViewerCls,
} from './h-cesium-viewer-class';
/**
* 管理 Cesium Viewer 中的地面站实体,根据选中的 ID 列表进行同步。
* @param hCesiumViewerInst - HCesiumViewerCls 实例或其 getter。
* @param groundStationList - 包含所有可用地面站选项的数组或 getter。
* @param selectedStationIds - 包含当前选中地面站 ID 的 Set 或 getter。
*/
export function useHCesiumViewerClsGroundStation(
hCesiumViewerInst: MaybeRefOrGetter<HCesiumViewerCls | null>,
groundStationList: MaybeRefOrGetter<Array<GroundStationOptions> | undefined>,
selectedStationIds: MaybeRefOrGetter<Set<string> | undefined>,
) {
// 创建一个从 ID 到站点选项的映射,方便查找
const stationMap = computed(() => {
const map = new Map<string, GroundStationOptions>();
const list = toValue(groundStationList) ?? [];
for (const station of list) {
map.set(station.id, station);
}
return map;
});
// watchEffect: 同步 selectedStationIds 到 Cesium 实体
watchEffect(() => {
const viewerInstance = toValue(hCesiumViewerInst);
if (!viewerInstance) {
// 如果 viewer 实例尚未初始化或已销毁,则不执行任何操作
return;
}
const selectedIdsSet = toValue(selectedStationIds) ?? new Set<string>(); // 直接获取 Set如果为 undefined 则创建空 Set
const currentEntityIds = new Set(
viewerInstance.currentStationEntities.keys(),
); // 当前已显示的站点 ID
// 1. 移除不再选中的站点
for (const entityId of currentEntityIds) {
if (!selectedIdsSet.has(entityId)) {
// 如果当前显示的实体不在新的选中列表里,则移除
viewerInstance.removeGroundStationById(entityId);
}
}
// 2. 添加新增的选中站点
for (const selectedId of selectedIdsSet) {
if (!currentEntityIds.has(selectedId)) {
// 如果选中的 ID 对应的实体当前未显示,则添加
const stationToAdd = stationMap.value.get(selectedId); // 从映射中查找站点信息
if (stationToAdd) {
viewerInstance.addGroundStation(stationToAdd);
} else {
// 如果在 groundStationList 中找不到对应的站点信息,发出警告
console.warn(
`无法找到 ID 为 "${selectedId}" 的站点信息,无法添加到地图。`,
);
}
}
}
});
// 返回 stationMap 可能在某些场景下有用,例如调试或扩展
return {
stationMap, // 可以考虑是否真的需要返回这个
};
}

View File

@ -1,76 +0,0 @@
// src/components/h-cesium-viewer/useHCesiumViewerClsSatellite.ts
import type { MaybeRefOrGetter } from 'vue';
import type {
HCesiumViewerCls,
SatelliteOptions,
} from './h-cesium-viewer-class';
/**
* 管理 Cesium Viewer 中的卫星实体,根据选中的 ID 列表进行同步。
* @param hCesiumViewerInst - HCesiumViewerCls 实例或其 getter。
* @param satelliteList - 包含所有可用卫星选项的数组或 getter。
* @param selectedSatelliteIds - 包含当前选中卫星 ID 的 Set 或 getter。
*/
export function useHCesiumViewerClsSatellite(
hCesiumViewerInst: MaybeRefOrGetter<HCesiumViewerCls | null>,
satelliteList: MaybeRefOrGetter<Array<SatelliteOptions> | undefined>,
selectedSatelliteIds: MaybeRefOrGetter<Set<string> | undefined>,
) {
// 创建一个从 ID 到卫星选项的映射,方便查找
const satelliteMap = computed(() => {
const map = new Map<string, SatelliteOptions>();
const list = toValue(satelliteList) ?? [];
for (const satellite of list) {
map.set(satellite.id, satellite);
}
return map;
});
// watchEffect: 同步 selectedSatelliteIds 到 Cesium 实体
watchEffect(() => {
const viewerInstance = toValue(hCesiumViewerInst);
if (!viewerInstance) {
// 如果 viewer 实例尚未初始化或已销毁,则不执行任何操作
return;
}
const selectedIdsSet = toValue(selectedSatelliteIds) ?? new Set<string>(); // 直接获取 Set如果为 undefined 则创建空 Set
const currentEntityIds = new Set(
viewerInstance.currentSatelliteEntities.keys(),
); // 当前已显示的卫星 ID
// 1. 移除不再选中的卫星
for (const entityId of currentEntityIds) {
if (!selectedIdsSet.has(entityId)) {
// 如果当前显示的实体不在新的选中列表里,则移除
viewerInstance.removeSatelliteById(entityId);
}
}
// 2. 添加新增的选中卫星
for (const selectedId of selectedIdsSet) {
if (!currentEntityIds.has(selectedId)) {
// 如果选中的 ID 对应的实体当前未显示,则添加
const satelliteToAdd = satelliteMap.value.get(selectedId); // 从映射中查找卫星信息
if (satelliteToAdd) {
console.debug(
`添加卫星 "${satelliteToAdd.id}" 到地图TLE: ${satelliteToAdd.tle}`,
);
viewerInstance.addSatellite(satelliteToAdd);
} else {
// 如果在 satelliteList 中找不到对应的卫星信息,发出警告
console.warn(
`无法找到 ID 为 "${selectedId}" 的卫星信息,无法添加到地图。`,
);
}
}
}
});
// 返回 satelliteMap 可能在某些场景下有用
return {
satelliteMap,
};
}

View File

@ -4,7 +4,7 @@ meta:
</route>
<script setup lang="ts">
import type { GroundStationState, SatelliteState } from '@/components/h-cesium-viewer/types';
import type { GroundStationState, SatelliteState } from '@/components/h-cesium-viewer/useHCesiumViewerCls.types';
// 地面站和选中状态
const groundStationState = reactive<GroundStationState>({
@ -79,7 +79,8 @@ const addRandomStation = () => {
longitude: randomLon,
name: `随机站 ${randomId.slice(-4)}`,
});
groundStationState.selectedIds.add(randomId); // 同时将新站点 ID 添加到选中项
// 创建新的 Set 以触发响应式更新
groundStationState.selectedIds = new Set([randomId, ...groundStationState.selectedIds]); // ESLint: 调整顺序
consola.info('添加随机站点:', groundStationState.groundStations.at(-1)); // 使用 .at() 访问最后一个元素
};
@ -90,7 +91,10 @@ const removeLastStation = () => {
if (removedStation) {
consola.info('移除站点:', removedStation);
// 同时从选中项中移除
groundStationState.selectedIds.delete(removedStation.id);
// 创建新的 Set 以触发响应式更新
const newSelectedIds = new Set(groundStationState.selectedIds);
newSelectedIds.delete(removedStation.id);
groundStationState.selectedIds = newSelectedIds;
}
}
};
@ -98,7 +102,7 @@ const removeLastStation = () => {
// 清空所有选中项
const clearAllStations = () => {
groundStationState.groundStations = [];
groundStationState.selectedIds.clear(); // 清空选中项
groundStationState.selectedIds = new Set(); // 创建新的空 Set 以触发响应式更新
consola.info('清空所有站点');
};