From 35a658bfe486b05afa2ef2803e08aea6c312b4e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=A5=E6=B5=A9?= Date: Fri, 7 Mar 2025 15:27:21 +0800 Subject: [PATCH] feat: /cesium --- .../cesium/cesium-helper/demo_02_Track.ts | 97 +++++++++++-------- 1 file changed, 58 insertions(+), 39 deletions(-) diff --git a/src/pages/cesium/cesium-helper/demo_02_Track.ts b/src/pages/cesium/cesium-helper/demo_02_Track.ts index 1aba5dd..a66cb81 100644 --- a/src/pages/cesium/cesium-helper/demo_02_Track.ts +++ b/src/pages/cesium/cesium-helper/demo_02_Track.ts @@ -51,11 +51,7 @@ export async function demo_02_Track(viewer: Viewer) { 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(), - ); + const endTime = Cesium.JulianDate.addSeconds(startTime, totalSeconds, new Cesium.JulianDate()); // 设置时钟范围 viewer.clock.startTime = startTime.clone(); @@ -73,13 +69,12 @@ export async function demo_02_Track(viewer: Viewer) { // 用于存储轨道点的数组(用于显示完整轨道) 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 time = Cesium.JulianDate.addSeconds(startTime, i, new Cesium.JulianDate()); const jsDate = Cesium.JulianDate.toDate(time); // 计算卫星位置 @@ -91,11 +86,11 @@ export async function demo_02_Track(viewer: Viewer) { const position = eciToEcf(positionAndVelocity.position, gmst); // 转换为Cesium坐标(单位:米) - const cesiumPosition = new Cesium.Cartesian3( - position.x * 1000, - position.y * 1000, - position.z * 1000, - ); + 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); @@ -122,16 +117,52 @@ export async function demo_02_Track(viewer: Viewer) { // 设置卫星的位置 satelliteEntity.position = positionProperty; - satelliteEntity.orientation = new Cesium.VelocityOrientationProperty( - 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({ @@ -143,51 +174,39 @@ export async function demo_02_Track(viewer: Viewer) { outlineColor: Cesium.Color.BLUE.withAlpha(0.8), outlineWidth: 2, semiMajorAxis: new Cesium.CallbackProperty(() => { - const satPosition = satelliteEntity.position?.getValue( - viewer.clock.currentTime, - ); + 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)) - ); + return cartographic.height * Math.tan(Cesium.Math.toRadians(coverageAngle)); }, false), semiMinorAxis: new Cesium.CallbackProperty(() => { - const satPosition = satelliteEntity.position?.getValue( - viewer.clock.currentTime, - ); + 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)) - ); + 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, - ); + 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, - ); + return Cesium.Cartesian3.fromRadians(cartographic.longitude, cartographic.latitude, 0); }, false), }); - // https://github.com/CesiumGS/cesium/issues/8350 + // - 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;