Files
vue-ts-example/src/pages/Satellite/Cesium/cesium-helper/demo_02_Track.ts
严浩 a2f07c7d76
All checks were successful
/ depcheck (push) Successful in 2m5s
/ lint-build-and-check (push) Successful in 2m43s
/ build-and-deploy-to-vercel (push) Successful in 3m1s
/ surge (push) Successful in 3m3s
/ playwright (push) Successful in 5m52s
feat: 添加卫星实体类及相关功能,优化 Cesium 初始化和时间线设置
2025-03-11 16:01:55 +08:00

216 lines
7.9 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import * as Cesium from 'cesium';
import { type Viewer } from 'cesium';
import { eciToEcf, gstime, propagate, twoline2satrec } from 'satellite.js';
/**
* 未来2小时内每30秒的位置
*/
export async function demo_02_Track(viewer: Viewer) {
const tle = `STARLINK-11371 [DTC]
1 62879U 25024A 25062.93300820 .00003305 00000+0 21841-4 0 9995
2 62879 42.9977 257.3937 0001725 269.2925 90.7748 15.77864921 5143`;
// 解析TLE数据
const lines = tle.split('\n') as [string, string, string];
const satelliteName = lines[0]?.trim();
const satrec = twoline2satrec(lines[1], lines[2]);
// 创建卫星实体
const satelliteEntity = viewer.entities.add({
id: 'STARLINK-11371',
label: {
fillColor: Cesium.Color.WHITE,
font: '14pt sans-serif',
outlineColor: Cesium.Color.BLACK,
outlineWidth: 2,
pixelOffset: new Cesium.Cartesian2(0, -10),
style: Cesium.LabelStyle.FILL_AND_OUTLINE,
text: satelliteName,
verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
},
name: 'STARLINK-11371',
// 卫星轨迹
path: {
material: new Cesium.PolylineGlowMaterialProperty({
color: Cesium.Color.BLUE,
glowPower: 0.2,
}),
resolution: 1,
width: 2,
},
// 使用简单点图形表示卫星
point: {
color: Cesium.Color.YELLOW,
outlineColor: Cesium.Color.WHITE,
outlineWidth: 2,
pixelSize: 10,
},
});
// 计算并绘制卫星轨迹
const totalSeconds = 60 * 60 * 2; // 2小时的轨迹
const timeStepInSeconds = 30; // 每30秒一个点
const startTime = Cesium.JulianDate.fromDate(new Date());
const endTime = Cesium.JulianDate.addSeconds(startTime, totalSeconds, new Cesium.JulianDate());
// 设置时钟范围
viewer.clock.startTime = startTime.clone();
viewer.clock.stopTime = endTime.clone();
viewer.clock.currentTime = startTime.clone();
viewer.clock.clockRange = Cesium.ClockRange.LOOP_STOP;
viewer.clock.multiplier = 30; // 30倍速播放
// 设置时间轴范围
viewer.timeline.zoomTo(startTime, endTime);
// 用于存储卫星位置的样本集合
const positionProperty = new Cesium.SampledPositionProperty();
// 用于存储轨道点的数组(用于显示完整轨道)
const orbitPositions: Cesium.Cartesian3[] = [];
// 保存卫星高度数据,用于覆盖范围计算
// const satelliteAltitude = 550_000; // 默认高度为550公里
// 计算轨道上的点
for (let i = 0; i <= totalSeconds; i += timeStepInSeconds) {
const time = Cesium.JulianDate.addSeconds(startTime, i, new Cesium.JulianDate());
const jsDate = Cesium.JulianDate.toDate(time);
// 计算卫星位置
const positionAndVelocity = propagate(satrec, jsDate);
if (typeof positionAndVelocity.position === 'boolean') {
console.error('Error calculating satellite position');
} else {
const gmst = gstime(jsDate);
const position = eciToEcf(positionAndVelocity.position, gmst);
// 转换为Cesium坐标单位
const cesiumPosition = new Cesium.Cartesian3(position.x * 1000, position.y * 1000, position.z * 1000);
// 计算卫星高度
// const cartographic = Cesium.Cartographic.fromCartesian(cesiumPosition);
// satelliteAltitude = cartographic.height;
// 保存位置用于绘制完整轨道
orbitPositions.push(cesiumPosition);
// 添加位置样本
positionProperty.addSample(time, cesiumPosition);
}
}
// 添加完整轨道线
viewer.entities.add({
id: 'STARLINK-11371-orbit',
name: 'STARLINK-11371 Full Orbit',
polyline: {
clampToGround: false,
material: new Cesium.PolylineDashMaterialProperty({
color: Cesium.Color.CYAN,
dashLength: 8,
}),
positions: orbitPositions,
width: 1,
},
});
// 设置卫星的位置
satelliteEntity.position = positionProperty;
satelliteEntity.orientation = new Cesium.VelocityOrientationProperty(positionProperty);
// 添加卫星覆盖范围
// Starlink 卫星通常覆盖范围以锥形方式向地面投射
// 创建一个椭球体来表示卫星的覆盖范围
const coverageAngle = 45; // 覆盖角度(度)
// satelliteAltitude * Math.tan(Cesium.Math.toRadians(coverageAngle));
// 创建覆盖范围实体
viewer.entities.add({
ellipsoid: {
material: Cesium.Color.BLUE.withAlpha(0.2),
outline: true,
outlineColor: Cesium.Color.BLUE.withAlpha(0.8),
radii: new Cesium.CallbackProperty(() => {
const position = satelliteEntity.position?.getValue(viewer.clock.currentTime);
if (!position) return new Cesium.Cartesian3(10_000, 10_000, 10_000);
// 获取当前卫星高度
const cartographic = Cesium.Cartographic.fromCartesian(position);
const height = cartographic.height;
// 计算覆盖范围半径 (基于锥角)
const coverageRadius = height * Math.tan(Cesium.Math.toRadians(coverageAngle));
// 椭球体形状: xy平面为圆形基底z轴方向为高度
return new Cesium.Cartesian3(coverageRadius, coverageRadius, height / 2);
}, false),
slicePartitions: 24,
stackPartitions: 16,
},
id: 'STARLINK-11371-coverage',
name: 'STARLINK-11371 Coverage',
orientation: new Cesium.CallbackProperty(() => {
// 确保椭球体始终指向地球中心
const position = satelliteEntity.position?.getValue(viewer.clock.currentTime);
if (!position) return Cesium.Transforms.eastNorthUpToFixedFrame(new Cesium.Cartesian3());
// 计算从卫星到地球中心的方向
Cesium.Cartesian3.normalize(Cesium.Cartesian3.negate(position, new Cesium.Cartesian3()), new Cesium.Cartesian3());
return Cesium.Transforms.headingPitchRollQuaternion(position, new Cesium.HeadingPitchRoll(0, Math.PI, 0));
}, false),
position: positionProperty,
});
// 添加地面覆盖范围投影
viewer.entities.add({
ellipse: {
granularity: Cesium.Math.toRadians(1),
height: 0,
material: Cesium.Color.BLUE.withAlpha(0.2),
outline: true,
outlineColor: Cesium.Color.BLUE.withAlpha(0.8),
outlineWidth: 2,
semiMajorAxis: new Cesium.CallbackProperty(() => {
const satPosition = satelliteEntity.position?.getValue(viewer.clock.currentTime);
if (!satPosition) return 10_000;
// 计算卫星高度
const cartographic = Cesium.Cartographic.fromCartesian(satPosition);
// 根据高度和覆盖角度计算地面覆盖半径
return cartographic.height * Math.tan(Cesium.Math.toRadians(coverageAngle));
}, false),
semiMinorAxis: new Cesium.CallbackProperty(() => {
const satPosition = satelliteEntity.position?.getValue(viewer.clock.currentTime);
if (!satPosition) return 10_000;
// 计算卫星高度
const cartographic = Cesium.Cartographic.fromCartesian(satPosition);
// 根据高度和覆盖角度计算地面覆盖半径
return cartographic.height * Math.tan(Cesium.Math.toRadians(coverageAngle));
}, false),
},
id: 'STARLINK-11371-ground-coverage',
name: 'STARLINK-11371 Ground Coverage',
position: new Cesium.CallbackPositionProperty(() => {
const satPosition = satelliteEntity.position?.getValue(viewer.clock.currentTime);
if (!satPosition) return new Cesium.Cartesian3();
// 计算卫星在地表的投影点
const cartographic = Cesium.Cartographic.fromCartesian(satPosition);
return Cesium.Cartesian3.fromRadians(cartographic.longitude, cartographic.latitude, 0);
}, false),
});
// - https://community.cesium.com/t/entity-viewfrom-property-example/2299/3
// - https://github.com/CesiumGS/cesium/issues/8900#issuecomment-638114149
// - which is normal from a bird's eye view. But after setting it as the trackedEntity, it behaves abnormally.
// 设置相机自动跟踪卫星
viewer.trackedEntity = satelliteEntity;
// 开始动画
viewer.clock.shouldAnimate = true;
}