Refactor: 重构 HCesiumManager
This commit is contained in:
12
.vscode/settings.json
vendored
12
.vscode/settings.json
vendored
@ -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
|
||||
}
|
||||
|
@ -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 <<<
|
||||
|
@ -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:*",
|
||||
|
@ -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 };
|
||||
}
|
||||
}
|
@ -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(); // 确保清空
|
||||
}
|
||||
}
|
@ -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
|
||||
);
|
||||
|
@ -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, // 全屏按钮
|
||||
|
@ -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);
|
||||
};
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
|
150
src/components/h-cesium-viewer/managers/HCesiumManager.ts
Normal file
150
src/components/h-cesium-viewer/managers/HCesiumManager.ts
Normal 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;
|
||||
}
|
||||
}
|
@ -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
|
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 已销毁。');
|
||||
}
|
||||
}
|
124
src/components/h-cesium-viewer/managers/HCesiumManager.站点.ts
Normal file
124
src/components/h-cesium-viewer/managers/HCesiumManager.站点.ts
Normal 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 已销毁。');
|
||||
}
|
||||
}
|
@ -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,
|
||||
};
|
||||
}
|
||||
|
@ -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>;
|
||||
}
|
102
src/components/h-cesium-viewer/useHCesiumViewerCls.卫星.ts
Normal file
102
src/components/h-cesium-viewer/useHCesiumViewerCls.卫星.ts
Normal 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,
|
||||
};
|
||||
}
|
92
src/components/h-cesium-viewer/useHCesiumViewerCls.站点.ts
Normal file
92
src/components/h-cesium-viewer/useHCesiumViewerCls.站点.ts
Normal 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, // 可以考虑是否真的需要返回这个
|
||||
};
|
||||
}
|
@ -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, // 可以考虑是否真的需要返回这个
|
||||
};
|
||||
}
|
@ -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,
|
||||
};
|
||||
}
|
@ -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('清空所有站点');
|
||||
};
|
||||
|
||||
|
Reference in New Issue
Block a user