h-cesium-viewer
This commit is contained in:
188
src/pages/_cesium-demos/_TLE_DATA.ts
Normal file
188
src/pages/_cesium-demos/_TLE_DATA.ts
Normal file
@ -0,0 +1,188 @@
|
||||
// eslint-disable no-useless-spread
|
||||
|
||||
/* eslint-disable unicorn/numeric-separators-style */
|
||||
// eslint-disable no-unused-vars
|
||||
|
||||
// https://en.wikipedia.org/wiki/Two-line_element_set#Format
|
||||
|
||||
export const DEMO_DATA = {
|
||||
// #############################
|
||||
tleJson: {
|
||||
ARG_OF_PERICENTER: 181.9338,
|
||||
BSTAR: 0.00039937,
|
||||
CLASSIFICATION_TYPE: 'U',
|
||||
ECCENTRICITY: 0.7375486,
|
||||
ELEMENT_SET_NO: 999,
|
||||
EPHEMERIS_TYPE: 0,
|
||||
EPOCH: '2025-03-12T15:49:04.420416',
|
||||
INCLINATION: 19.0363,
|
||||
MEAN_ANOMALY: 171.615,
|
||||
MEAN_MOTION: 2.21786616,
|
||||
MEAN_MOTION_DDOT: -5.0502e-7,
|
||||
MEAN_MOTION_DOT: 0.00072212,
|
||||
NORAD_CAT_ID: 63158,
|
||||
OBJECT_ID: '2025-045B',
|
||||
OBJECT_NAME: '2025-045B',
|
||||
RA_OF_ASC_NODE: 63.0294,
|
||||
REV_AT_EPOCH: 13,
|
||||
},
|
||||
get tleLine1() {
|
||||
return this.tleString.split('\n')[1];
|
||||
},
|
||||
get tleLine2() {
|
||||
return this.tleString.split('\n')[2];
|
||||
},
|
||||
get tleName() {
|
||||
return this.tleString.split('\n')[0];
|
||||
},
|
||||
tleString: `2025-045B
|
||||
1 63158U 25045B 25071.65907894 .00072212 -50502-6 39937-3 0 9999
|
||||
2 63158 19.0363 63.0294 7375486 181.9338 171.6150 2.21786616 127`,
|
||||
};
|
||||
|
||||
export const TLE_LIST = [
|
||||
DEMO_DATA.tleString,
|
||||
...[
|
||||
`STARLINK-32915
|
||||
1 63152U 25043W 25070.91668981 .00451203 87802-4 12020-2 0 9994
|
||||
2 63152 43.0011 279.4620 0003944 269.6759 119.1758 15.93738994 2341`,
|
||||
`STARLINK-32915 [测修改1]
|
||||
1 00000U 00000W 25070.91668981 .00000000 00000-0 00000-0 0 0000
|
||||
2 00000 43.0011 279.4620 0003944 269.6759 119.1758 15.93738994 0000`,
|
||||
],
|
||||
];
|
||||
|
||||
// 为了twoline2satrec函数需要哪些参数
|
||||
// https://github.com/shashwatak/satellite-js/blob/0e921026d20b75f42553366994af70eed63a304f/src/io.js#L62
|
||||
function twoline2satrecFake(longstr1: string, longstr2: string) {
|
||||
console.group('twoline2satrecFake');
|
||||
const satrec = {} as Record<string, any>;
|
||||
satrec.error = 0;
|
||||
|
||||
satrec.satnum = longstr1.slice(2, 7);
|
||||
console.debug(`longstr1.substring(2, 7) :>>`, `"${longstr1.slice(2, 7)}"`);
|
||||
|
||||
satrec.epochyr = Number.parseInt(longstr1.slice(18, 20), 10);
|
||||
console.debug(`longstr1.substring(18, 20) :>>`, `"${longstr1.slice(18, 20)}"`);
|
||||
|
||||
satrec.epochdays = Number.parseFloat(longstr1.slice(20, 32));
|
||||
console.debug(`longstr1.substring(20, 32) :>>`, `"${longstr1.slice(20, 32)}"`);
|
||||
|
||||
satrec.ndot = Number.parseFloat(longstr1.slice(33, 43));
|
||||
console.debug(`longstr1.substring(33, 43) :>>`, `"${longstr1.slice(33, 43)}"`);
|
||||
|
||||
satrec.nddot = Number.parseFloat(`.${Number.parseInt(longstr1.slice(44, 50), 10)}E${longstr1.slice(50, 52)}`);
|
||||
console.debug(`longstr1.substring(44, 50) :>>`, `"${longstr1.slice(44, 50)}"`);
|
||||
console.debug(`longstr1.substring(50, 52) :>>`, `"${longstr1.slice(50, 52)}"`);
|
||||
|
||||
satrec.bstar = Number.parseFloat(
|
||||
`${longstr1.slice(53, 54)}.${Number.parseInt(longstr1.slice(54, 59), 10)}E${longstr1.slice(59, 61)}`,
|
||||
);
|
||||
console.debug(`longstr1.substring(53, 54) :>>`, `"${longstr1.slice(53, 54)}"`);
|
||||
console.debug(`longstr1.substring(54, 59) :>>`, `"${longstr1.slice(54, 59)}"`);
|
||||
console.debug(`longstr1.substring(59, 61) :>>`, `"${longstr1.slice(59, 61)}"`);
|
||||
|
||||
// satrec.satnum = longstr2.substring(2, 7);
|
||||
satrec.inclo = Number.parseFloat(longstr2.slice(8, 16));
|
||||
console.debug(`longstr2.substring(8, 16) :>>`, `"${longstr2.slice(8, 16)}"`);
|
||||
|
||||
satrec.nodeo = Number.parseFloat(longstr2.slice(17, 25));
|
||||
console.debug(`longstr2.substring(17, 25) :>>`, `"${longstr2.slice(17, 25)}"`);
|
||||
|
||||
satrec.ecco = Number.parseFloat(`.${longstr2.slice(26, 33)}`);
|
||||
console.debug(`longstr2.substring(26, 33) :>>`, `"${longstr2.slice(26, 33)}"`);
|
||||
satrec.argpo = Number.parseFloat(longstr2.slice(34, 42));
|
||||
console.debug(`longstr2.substring(34, 42) :>>`, `"${longstr2.slice(34, 42)}"`);
|
||||
satrec.mo = Number.parseFloat(longstr2.slice(43, 51));
|
||||
console.debug(`longstr2.substring(43, 51) :>>`, `"${longstr2.slice(43, 51)}"`);
|
||||
satrec.no = Number.parseFloat(longstr2.slice(52, 63));
|
||||
console.debug(`longstr2.substring(52, 63) :>>`, `"${longstr2.slice(52, 63)}"`);
|
||||
|
||||
console.groupEnd();
|
||||
return satrec;
|
||||
}
|
||||
let line1 = '';
|
||||
line1 += '1'; //
|
||||
line1 += ' '; //
|
||||
line1 += '62949'; // 卫星编号
|
||||
line1 += 'U'; // 卫星分类
|
||||
line1 += ' ';
|
||||
line1 += '25';
|
||||
line1 += '029';
|
||||
line1 += 'B'.padEnd(3, ' ');
|
||||
line1 += ' ';
|
||||
line1 += '25070.91668981'; // 🛰️ 历元时间 25070.91668981(年份 2025,第 70 天加 0.91668981 天)
|
||||
line1 += ' ';
|
||||
line1 += '.00039463'.padStart(10, ' '); //
|
||||
line1 += '00000+0'.padStart(9, ' '); //
|
||||
line1 += '99294-3'.padStart(9, ' '); //
|
||||
line1 += ' ';
|
||||
line1 += '0';
|
||||
line1 += ' ';
|
||||
line1 += '999'.padStart(4, ' ');
|
||||
line1 += '0';
|
||||
|
||||
let line2 = '';
|
||||
line2 += '2';
|
||||
line2 += ' ';
|
||||
line2 += '62949';
|
||||
line2 += ' ';
|
||||
line2 += '53.1596'.padStart(8, ' '); // 🛰️ 轨道倾角(度)
|
||||
line2 += ' ';
|
||||
line2 += '120.9032'; // 🛰️ 升交点赤经(度)
|
||||
line2 += ' ';
|
||||
line2 += '0001355'; // 🛰️ 偏心率
|
||||
line2 += ' ';
|
||||
line2 += '101.1211'; // 🛰️ 近地点角距(度)
|
||||
line2 += ' ';
|
||||
line2 += '35.9659'.padStart(8, ' '); // 🛰️ 平近点角(度)
|
||||
line2 += ' ';
|
||||
line2 += '15.39574303'; // 🛰️ 平均运动(圈/天)
|
||||
line2 += '580'.padStart(5, ' ');
|
||||
line2 += '9';
|
||||
|
||||
// console.debug(`xxxline2 :>>`, `"${line2}"`);
|
||||
// console.debug('正确↓');
|
||||
// console.debug(`longstr2 :>>`, `"${longstr2}"`);
|
||||
// twoline2satrecFake('', line2);
|
||||
// twoline2satrecFake('', longstr2);
|
||||
// const numbers = '0123456789';
|
||||
// twoline2satrecFake(
|
||||
// `${numbers}${numbers}${numbers}${numbers}${numbers}${numbers}`,
|
||||
// '',
|
||||
// );
|
||||
|
||||
// if ($__DEV__) {
|
||||
// const tle = TLE_LIST[0] as string;
|
||||
// const line1 = tle.split('\n')[1] as string;
|
||||
// const line2 = tle.split('\n')[2] as string;
|
||||
// console.clear();
|
||||
// twoline2satrecFake(line1, line2);
|
||||
// }
|
||||
|
||||
// if ($__DEV__) {
|
||||
// const satrec2 = twoline2satrec(
|
||||
// `1 62949 25070.91668981 .00039463 00000+0 99294-3 `,
|
||||
// `2 53.1596 120.9032 0001355 101.1211 35.9659 15.39574303 `,
|
||||
// );
|
||||
// const satrec1_str = JSON.stringify(satrec1, null, 2);
|
||||
// const satrec2_str = JSON.stringify(satrec2, null, 2);
|
||||
// console.debug(`satrec1_str === satrec2_str :>>`, satrec1_str === satrec2_str);
|
||||
// }
|
||||
|
||||
// longstr1.substring(2, 7) :>> "63157"
|
||||
// longstr1.substring(18, 20) :>> "25"
|
||||
// longstr1.substring(20, 32) :>> "069.35219743"
|
||||
// longstr1.substring(33, 43) :>> " .00000545"
|
||||
// longstr1.substring(44, 50) :>> "-65692"
|
||||
// longstr1.substring(50, 52) :>> "-6"
|
||||
// longstr1.substring(53, 54) :>> " "
|
||||
// longstr1.substring(54, 59) :>> "00000"
|
||||
// longstr1.substring(59, 61) :>> "+0"
|
||||
//
|
||||
// longstr2.substring(8, 16) :>> " 19.0062"
|
||||
// longstr2.substring(17, 25) :>> " 64.6364"
|
||||
// longstr2.substring(26, 33) :>> "7327661"
|
||||
// longstr2.substring(34, 42) :>> "179.7218"
|
||||
// longstr2.substring(43, 51) :>> "141.4162"
|
||||
// longstr2.substring(52, 63) :>> " 2.26206155"
|
115
src/pages/_cesium-demos/demo_01_OrbitGeneration.ts
Normal file
115
src/pages/_cesium-demos/demo_01_OrbitGeneration.ts
Normal file
@ -0,0 +1,115 @@
|
||||
import type { Viewer } from 'cesium';
|
||||
|
||||
import * as Cesium from 'cesium';
|
||||
import { eciToEcf, gstime, propagate, twoline2satrec } from 'satellite.js';
|
||||
|
||||
export async function demo_01_OrbitGeneration(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 tleLine1 = lines[1];
|
||||
const tleLine2 = lines[2];
|
||||
|
||||
// 创建卫星记录
|
||||
const satrec = twoline2satrec(tleLine1, tleLine2);
|
||||
console.debug(`satrec :>>`, satrec);
|
||||
|
||||
// 创建轨道点
|
||||
const pointsArray = [];
|
||||
const totalMinutes = 1440; // 一天的分钟数
|
||||
const timeStepInMinutes = 10;
|
||||
|
||||
// 当前时间
|
||||
const now = new Date();
|
||||
|
||||
await new Promise((resolve) => setTimeout(resolve, 500));
|
||||
// 计算一条完整的轨道
|
||||
for (let index = 0; index < totalMinutes; index += timeStepInMinutes) {
|
||||
// 创建时间点
|
||||
const time = new Date(now.getTime() + index * 60_000);
|
||||
|
||||
// 获取卫星位置
|
||||
const positionAndVelocity = propagate(satrec, time);
|
||||
|
||||
// 有时可能返回false
|
||||
const position = positionAndVelocity.position;
|
||||
if (typeof position === 'boolean') {
|
||||
console.error('Error calculating satellite position');
|
||||
return;
|
||||
}
|
||||
// 转换为千米
|
||||
const gmst = gstime(time);
|
||||
const p = eciToEcf(position, gmst);
|
||||
|
||||
// 添加到点数组
|
||||
pointsArray.push(
|
||||
Cesium.Cartesian3.fromDegrees(
|
||||
Cesium.Math.toDegrees(Math.atan2(p.y, p.x)),
|
||||
Cesium.Math.toDegrees(Math.atan2(p.z, Math.hypot(p.x, p.y))),
|
||||
Math.hypot(p.x, p.y, p.z) * 1000, // 转换为米
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// 获取当前位置用于放置卫星模型
|
||||
const time = new Date();
|
||||
const positionAndVelocity = propagate(satrec, time);
|
||||
const position = positionAndVelocity.position;
|
||||
const gmst = gstime(time);
|
||||
if (typeof position === 'boolean') {
|
||||
console.error('Error calculating satellite position');
|
||||
return;
|
||||
}
|
||||
const p = eciToEcf(position, gmst);
|
||||
|
||||
const currentPosition = Cesium.Cartesian3.fromDegrees(
|
||||
Cesium.Math.toDegrees(Math.atan2(p.y, p.x)),
|
||||
Cesium.Math.toDegrees(Math.atan2(p.z, Math.hypot(p.x, p.y))),
|
||||
Math.hypot(p.x, p.y, p.z) * 1000, // 转换为米
|
||||
);
|
||||
|
||||
viewer.entities.add({
|
||||
name: `${satelliteName} Orbit`,
|
||||
polyline: {
|
||||
material: new Cesium.PolylineGlowMaterialProperty({
|
||||
color: Cesium.Color.BLUE,
|
||||
glowPower: 0.2,
|
||||
}),
|
||||
positions: pointsArray,
|
||||
width: 2,
|
||||
},
|
||||
});
|
||||
|
||||
// 添加卫星实体
|
||||
const satellite = viewer.entities.add({
|
||||
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: satelliteName,
|
||||
point: {
|
||||
color: Cesium.Color.YELLOW,
|
||||
outlineColor: Cesium.Color.WHITE,
|
||||
outlineWidth: 2,
|
||||
pixelSize: 10,
|
||||
},
|
||||
position: currentPosition,
|
||||
// billboard: {
|
||||
// image: '/assets/satellite.png', // 您需要添加一个卫星图标
|
||||
// scale: 0.5,
|
||||
// },
|
||||
});
|
||||
|
||||
// 将相机定位到卫星
|
||||
viewer.flyTo(satellite, { duration: 3 });
|
||||
}
|
222
src/pages/_cesium-demos/demo_02_Track.ts
Normal file
222
src/pages/_cesium-demos/demo_02_Track.ts
Normal file
@ -0,0 +1,222 @@
|
||||
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,
|
||||
tle = `DEMO [测试]
|
||||
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 satelliteColor = Cesium.Color.fromRandom({ alpha: 1 });
|
||||
const pathColor = Cesium.Color.fromRandom({ alpha: 1 });
|
||||
const orbitColor = Cesium.Color.fromRandom({ alpha: 1 });
|
||||
const coverageColor = Cesium.Color.fromRandom({ alpha: 1 });
|
||||
|
||||
// 创建卫星实体
|
||||
const satelliteEntity = viewer.entities.add({
|
||||
id: `${satelliteName}-satellite`,
|
||||
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: `${satelliteName} Satellite`,
|
||||
// 卫星轨迹
|
||||
path: {
|
||||
material: new Cesium.PolylineGlowMaterialProperty({
|
||||
color: pathColor,
|
||||
glowPower: 0.2,
|
||||
}),
|
||||
resolution: 1,
|
||||
width: 2,
|
||||
},
|
||||
// 使用简单点图形表示卫星
|
||||
point: {
|
||||
color: satelliteColor,
|
||||
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 index = 0; index <= totalSeconds; index += timeStepInSeconds) {
|
||||
const time = Cesium.JulianDate.addSeconds(startTime, index, 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: `${satelliteName}-orbit`,
|
||||
name: `${satelliteName} Full Orbit`,
|
||||
polyline: {
|
||||
clampToGround: false,
|
||||
material: new Cesium.PolylineDashMaterialProperty({
|
||||
color: orbitColor,
|
||||
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: coverageColor.withAlpha(0.2),
|
||||
outline: true,
|
||||
outlineColor: coverageColor.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: `${satelliteName}-coverage`,
|
||||
name: `${satelliteName} 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: coverageColor.withAlpha(0.2),
|
||||
outline: true,
|
||||
outlineColor: coverageColor.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: `${satelliteName}-coverage-ground`,
|
||||
name: `${satelliteName} 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;
|
||||
}
|
53
src/pages/_cesium-demos/demo_03_卫星加站点.ts
Normal file
53
src/pages/_cesium-demos/demo_03_卫星加站点.ts
Normal file
@ -0,0 +1,53 @@
|
||||
import type { Viewer } from 'cesium';
|
||||
|
||||
import * as Cesium from 'cesium';
|
||||
|
||||
export function demo_卫星加站点(viewer: Viewer) {
|
||||
// 1. 添加一个卫星
|
||||
const satellite = viewer.entities.add({
|
||||
label: {
|
||||
font: '14pt monospace',
|
||||
pixelOffset: new Cesium.Cartesian2(0, 20),
|
||||
text: '卫星',
|
||||
},
|
||||
name: '测试卫星',
|
||||
point: {
|
||||
color: Cesium.Color.RED,
|
||||
pixelSize: 10, // 卫星显示为一个点
|
||||
},
|
||||
position: Cesium.Cartesian3.fromDegrees(114, 40, 500_000), // 经度、纬度、高度(500km)
|
||||
});
|
||||
|
||||
// 2. 添加一个地面检测站点
|
||||
const detectionSite = viewer.entities.add({
|
||||
label: {
|
||||
font: '14pt monospace',
|
||||
pixelOffset: new Cesium.Cartesian2(0, 20),
|
||||
text: '检测站',
|
||||
},
|
||||
name: '检测站点',
|
||||
point: {
|
||||
color: Cesium.Color.BLUE,
|
||||
pixelSize: 8,
|
||||
},
|
||||
position: Cesium.Cartesian3.fromDegrees(116, 39, 0), // 北京附近的一个地面站点
|
||||
});
|
||||
|
||||
// 3. 可选:绘制卫星与站点之间的连线
|
||||
viewer.entities.add({
|
||||
polyline: {
|
||||
material: Cesium.Color.YELLOW.withAlpha(0.5),
|
||||
positions: new Cesium.CallbackProperty(() => {
|
||||
const satellitePosition = satellite.position?.getValue(Cesium.JulianDate.now());
|
||||
const sitePosition = detectionSite.position?.getValue(Cesium.JulianDate.now());
|
||||
|
||||
if (!satellitePosition || !sitePosition) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return [satellitePosition, sitePosition];
|
||||
}, false),
|
||||
width: 2,
|
||||
},
|
||||
});
|
||||
}
|
108
src/pages/_cesium-demos/归档/SatelliteEntity.ts
Normal file
108
src/pages/_cesium-demos/归档/SatelliteEntity.ts
Normal file
@ -0,0 +1,108 @@
|
||||
import type { Cartesian3 } from 'cesium';
|
||||
|
||||
import * as Cesium from 'cesium';
|
||||
import { propagate, type SatRec, twoline2satrec } from 'satellite.js';
|
||||
|
||||
class SatelliteEntity {
|
||||
private leadTime!: number;
|
||||
private name!: string;
|
||||
private satrec: SatRec;
|
||||
private stepSeconds!: number;
|
||||
private tleLine1!: string;
|
||||
private tleLine2!: string;
|
||||
private totalSeconds!: number;
|
||||
private trailTime!: number;
|
||||
|
||||
constructor(tle2LineString = '' /* , options = {} */) {
|
||||
const [name, tleLine1, tleLine2] = this._checkTle(tle2LineString);
|
||||
const circle = Number(tleLine2.slice(52, 64));
|
||||
|
||||
this.name = name.trim();
|
||||
this.tleLine1 = tleLine1.trim();
|
||||
this.tleLine2 = tleLine2.trim();
|
||||
this.satrec = twoline2satrec(this.tleLine1, this.tleLine2);
|
||||
|
||||
this.totalSeconds = 86_400; // 24小时
|
||||
this.stepSeconds = 100;
|
||||
this.leadTime = Math.round((24 * 3600) / circle);
|
||||
this.trailTime = 0;
|
||||
}
|
||||
|
||||
_checkTle(tle2LineString: string) {
|
||||
const elements = tle2LineString.split('\n');
|
||||
if (elements.length !== 3) throw new Error('tle data error');
|
||||
return elements as [string, string, string];
|
||||
}
|
||||
|
||||
// 创建PositionProperty
|
||||
_getPositionProperty() {
|
||||
const start = Cesium.JulianDate.fromIso8601(new Date().toISOString());
|
||||
const positionProperty = new Cesium.SampledPositionProperty(Cesium.ReferenceFrame.INERTIAL);
|
||||
|
||||
const now = Date.now();
|
||||
for (let index = 0; index < this.totalSeconds / this.stepSeconds; index++) {
|
||||
const sateTime = new Date(now + index * this.stepSeconds * 1000);
|
||||
const sateCoord = this.getPositionEci(sateTime);
|
||||
if (!sateCoord) continue;
|
||||
const cesiumTime = Cesium.JulianDate.addSeconds(start, index * this.stepSeconds, new Cesium.JulianDate());
|
||||
const cesiumPosition = {
|
||||
x: sateCoord.x * 1000,
|
||||
y: sateCoord.y * 1000,
|
||||
z: sateCoord.z * 1000,
|
||||
} as Cartesian3;
|
||||
positionProperty.addSample(cesiumTime, cesiumPosition);
|
||||
}
|
||||
return positionProperty;
|
||||
}
|
||||
|
||||
// 创建卫星实例
|
||||
createSatelliteEntity() {
|
||||
const start = Cesium.JulianDate.fromIso8601(new Date().toISOString());
|
||||
const stop = Cesium.JulianDate.addSeconds(start, this.totalSeconds, new Cesium.JulianDate());
|
||||
const color = Cesium.Color.fromRandom({ alpha: 1 });
|
||||
const satelliteEntity = {
|
||||
availability: new Cesium.TimeIntervalCollection([new Cesium.TimeInterval({ start, stop })]),
|
||||
description: this.name,
|
||||
label: {
|
||||
backgroundColor: new Cesium.Color(0.165, 0.165, 0.165, 0.5),
|
||||
backgroundPadding: new Cesium.Cartesian2(4, 4),
|
||||
fillColor: Cesium.Color.WHITE,
|
||||
font: '12px sans-serif',
|
||||
horizontalOrigin: Cesium.HorizontalOrigin.LEFT,
|
||||
outlineWidth: 1,
|
||||
pixelOffset: new Cesium.Cartesian2(0, 5),
|
||||
showBackground: true,
|
||||
text: this.name,
|
||||
verticalOrigin: Cesium.VerticalOrigin.TOP,
|
||||
// distanceDisplayCondition: new Cesium.DistanceDisplayCondition(10.0, 50000000),
|
||||
},
|
||||
name: this.name,
|
||||
path: new Cesium.PathGraphics({
|
||||
leadTime: this.leadTime,
|
||||
material: color,
|
||||
show: true,
|
||||
trailTime: this.trailTime,
|
||||
width: 0.5,
|
||||
}),
|
||||
point: {
|
||||
color,
|
||||
pixelSize: 8,
|
||||
// scaleByDistance: new Cesium.NearFarScalar(1.5e3, 1, 8.0e8, 0.5),
|
||||
},
|
||||
position: this._getPositionProperty(),
|
||||
};
|
||||
return satelliteEntity;
|
||||
}
|
||||
|
||||
// 获取地心惯性坐标系坐标
|
||||
getPositionEci(date: Date) {
|
||||
const result = propagate(this.satrec, date);
|
||||
// 检查position是否为对象且不是true
|
||||
if (typeof result.position !== 'object' || result.position === null) {
|
||||
return null;
|
||||
}
|
||||
return result.position;
|
||||
}
|
||||
}
|
||||
|
||||
export default SatelliteEntity;
|
Reference in New Issue
Block a user