diff --git a/src/pages/cesium/cesium-helper/00.cesium-init.VIEWER_OPTIONS.ts b/src/pages/cesium/cesium-helper/00.cesium-init.VIEWER_OPTIONS.ts new file mode 100644 index 0000000..f67d760 --- /dev/null +++ b/src/pages/cesium/cesium-helper/00.cesium-init.VIEWER_OPTIONS.ts @@ -0,0 +1,31 @@ +import type { Viewer } from 'cesium'; + +import * as Cesium from 'cesium'; + +export const VIEWER_OPTIONS: Viewer.ConstructorOptions = { + 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')), + ), + baseLayerPicker: !true, + fullscreenButton: !true, // 全屏按钮 + geocoder: !true, // = IonGeocodeProviderType.DEFAULT] - 在使用Geocoder小部件进行搜索时使用的地理编码服务或服务。如果设置为false,则不会创建Geocoder小部件。 + + // globe: false, // 地球 + homeButton: true, // Home按钮 + infoBox: false, // InfoBox小部件。 + navigationHelpButton: false, // 是否显示导航帮助按钮 + orderIndependentTranslucency: false, // 顺序无关透明度 + projectionPicker: !true, // 投影选择器 + requestRenderMode: !true, // 如果为真,渲染帧将仅在场景内部发生变化时需要时发生。启用此功能可以减少应用程序的CPU/GPU使用率,并在移动设备上节省更多电量,但在此模式下需要使用{@link Scene#requestRender}显式渲染新帧。在API的其他部分对场景进行更改后,在许多情况下都需要这样做。请参阅{@link https://cesium.com/blog/2018/01/24/cesium-scene-rendering-performance/|使用显式渲染提高性能}。 + sceneModePicker: true, // 是否显示场景模式选择器(2D/3D切换) + selectionIndicator: true, + shadows: true, // Determines if shadows are cast by light sources. + + /* animationContainer: !true, */ + /* timelineContainer: true, */ + /* bottomContainer: document.createElement('p'), // The DOM element or ID that will contain the bottomContainer. If not specified, the bottomContainer is added to the widget itself. */ + shouldAnimate: !true, + showRenderLoopErrors: true, // 如果为真,当发生渲染循环错误时,此小部件将自动向用户显示包含错误的HTML面板。 + timeline: true, +}; diff --git a/src/pages/cesium/cesium-helper/00.cesium-init.globe.ts b/src/pages/cesium/cesium-helper/00.cesium-init.globe.ts new file mode 100644 index 0000000..6cbcac9 --- /dev/null +++ b/src/pages/cesium/cesium-helper/00.cesium-init.globe.ts @@ -0,0 +1,37 @@ +import * as Cesium from 'cesium'; + +const provider = new Cesium.UrlTemplateImageryProvider({ + maximumLevel: 18, + minimumLevel: 3, + url: 'https://webst02.is.autonavi.com/appmaptile?style=6&x={x}&y={y}&z={z}', +}); + +export function configureMapTile(viewer: Cesium.Viewer) { + if (viewer.baseLayerPicker) { + // 如果有底图选择器 + const customLayerViewModel = new Cesium.ProviderViewModel({ + category: 'Cesium ion', // 或 'Other 、Cesium ion'、'Bing Maps' 等 + creationFunction() { + return provider; + }, + iconUrl: 'gaodeImage.png', + name: '高德地图', + tooltip: '高德地图', + }); + // 设置高德地图为默认图层 + viewer.baseLayerPicker.viewModel.imageryProviderViewModels.unshift( + customLayerViewModel, + ); + const selectedViewModel = + viewer.baseLayerPicker.viewModel.imageryProviderViewModels[0]; + if (!selectedViewModel) { + console.error('未找到默认底图'); + return; + } + viewer.baseLayerPicker.viewModel.selectedImagery = selectedViewModel; + } else { + // 如果没有底图选择器 + viewer.imageryLayers.removeAll(); + viewer.imageryLayers.addImageryProvider(provider); + } +} diff --git a/src/pages/cesium/cesium-helper/00.cesium-init.ts b/src/pages/cesium/cesium-helper/00.cesium-init.ts index 0587857..0ac272b 100644 --- a/src/pages/cesium/cesium-helper/00.cesium-init.ts +++ b/src/pages/cesium/cesium-helper/00.cesium-init.ts @@ -1,7 +1,7 @@ -// TODO: 干掉 api.cesium.com -// XXX: ion-sdk ??? - import * as Cesium from 'cesium'; + +import { VIEWER_OPTIONS } from './00.cesium-init.VIEWER_OPTIONS'; + import 'cesium/Build/Cesium/Widgets/widgets.css'; Object.assign(window, { Cesium }); @@ -9,42 +9,28 @@ Object.assign(window, { Cesium }); _configureCesium(); export function cesium_init(container: Element) { - const viewer = new Cesium.Viewer(container, { - 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')), - ), - baseLayerPicker: !true, - geocoder: !true, // = IonGeocodeProviderType.DEFAULT] - 在使用Geocoder小部件进行搜索时使用的地理编码服务或服务。如果设置为false,则不会创建Geocoder小部件。 - }, - fullscreenButton: !true, // 全屏按钮 - // globe: false, // 地球 - homeButton: true, // Home按钮 - infoBox: false, // InfoBox小部件。 - navigationHelpButton: false, // 是否显示导航帮助按钮 - orderIndependentTranslucency: false, // 顺序无关透明度 - projectionPicker: !true, // 投影选择器 - requestRenderMode: !true, // 如果为真,渲染帧将仅在场景内部发生变化时需要时发生。启用此功能可以减少应用程序的CPU/GPU使用率,并在移动设备上节省更多电量,但在此模式下需要使用{@link Scene#requestRender}显式渲染新帧。在API的其他部分对场景进行更改后,在许多情况下都需要这样做。请参阅{@link https://cesium.com/blog/2018/01/24/cesium-scene-rendering-performance/|使用显式渲染提高性能}。 - sceneModePicker: true, // 是否显示场景模式选择器(2D/3D切换) - selectionIndicator: true, - shadows: true, // Determines if shadows are cast by light sources. - /* animationContainer: !true, */ - /* timelineContainer: true, */ - /* bottomContainer: document.createElement('p'), // The DOM element or ID that will contain the bottomContainer. If not specified, the bottomContainer is added to the widget itself. */ - shouldAnimate: !true, - showRenderLoopErrors: true, // 如果为真,当发生渲染循环错误时,此小部件将自动向用户显示包含错误的HTML面板。 - timeline: true, // If set to false, the Timeline widget will not be created. - }); + const viewer = new Cesium.Viewer(container, VIEWER_OPTIONS); viewer.scene.debugShowFramesPerSecond = true; - // _configureMapTile(viewer); - return viewer; } function _configureCesium() { + if (document.querySelector('#hide-cesium-viewer-bottom') === null) { + document.head.append( + Object.assign(document.createElement('style'), { + id: 'hide-cesium-viewer-bottom', + innerHTML: ` +.cesium-viewer-bottom { + display: none !important; +} +`.trim(), + type: 'text/css', + }), + ); + } + /* 时间日期格式化 */ { const minutes = 0 - new Date().getTimezoneOffset(); // 0 - (-480); @@ -74,49 +60,3 @@ function _configureCesium() { 60, // 北纬 ); } - -function _configureMapTile(viewer: Cesium.Viewer) { - const provider = new Cesium.UrlTemplateImageryProvider({ - maximumLevel: 18, - minimumLevel: 3, - url: 'https://webst02.is.autonavi.com/appmaptile?style=6&x={x}&y={y}&z={z}', - }); - - if (viewer.baseLayerPicker) { - // 如果有底图选择器 - const customLayerViewModel = new Cesium.ProviderViewModel({ - category: 'Cesium ion', // 或 'Other 、Cesium ion'、'Bing Maps' 等 - creationFunction() { - return provider; - }, - iconUrl: 'gaodeImage.png', - name: '高德地图', - tooltip: '高德地图', - }); - // 设置高德地图为默认图层 - viewer.baseLayerPicker.viewModel.imageryProviderViewModels.unshift(customLayerViewModel); - const selectedViewModel = viewer.baseLayerPicker.viewModel.imageryProviderViewModels[0]; - if (!selectedViewModel) { - console.error('未找到默认底图'); - return; - } - viewer.baseLayerPicker.viewModel.selectedImagery = selectedViewModel; - } else { - // 如果没有底图选择器 - viewer.imageryLayers.removeAll(); - viewer.imageryLayers.addImageryProvider(provider); - } -} - -function _flyToDemo(viewer: Cesium.Viewer) { - // 将三维球定位到中国 - viewer.camera.flyTo({ - complete: function complete() {}, - destination: Cesium.Cartesian3.fromDegrees(103.84, 31.15, 17_850_000), - orientation: { - heading: Cesium.Math.toRadians(348.420_294_285_197_8), - pitch: Cesium.Math.toRadians(-89.740_266_879_720_41), - roll: Cesium.Math.toRadians(0), - }, - }); -} diff --git a/src/pages/cesium/README.md b/src/pages/cesium/cesium-helper/README.md similarity index 100% rename from src/pages/cesium/README.md rename to src/pages/cesium/cesium-helper/README.md diff --git a/src/pages/cesium/cesium-helper/demo_01_OrbitGeneration.ts b/src/pages/cesium/cesium-helper/demo_01_OrbitGeneration.ts index f094458..57f7da0 100644 --- a/src/pages/cesium/cesium-helper/demo_01_OrbitGeneration.ts +++ b/src/pages/cesium/cesium-helper/demo_01_OrbitGeneration.ts @@ -9,14 +9,14 @@ export async function demo_01_OrbitGeneration(viewer: Viewer) { 2 62879 42.9977 257.3937 0001725 269.2925 90.7748 15.77864921 5143`; // 解析TLE数据 - const lines = tle.split('\n'); + 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); + console.debug(`satrec :>>`, satrec); // 创建轨道点 const pointsArray = []; @@ -30,7 +30,7 @@ export async function demo_01_OrbitGeneration(viewer: Viewer) { // 计算一条完整的轨道 for (let i = 0; i < totalMinutes; i += timeStepInMinutes) { // 创建时间点 - const time = new Date(now.getTime() + i * 60000); + const time = new Date(now.getTime() + i * 60_000); // 获取卫星位置 const positionAndVelocity = propagate(satrec, time); @@ -49,8 +49,8 @@ export async function demo_01_OrbitGeneration(viewer: Viewer) { pointsArray.push( Cesium.Cartesian3.fromDegrees( Cesium.Math.toDegrees(Math.atan2(p.y, p.x)), - Cesium.Math.toDegrees(Math.atan2(p.z, Math.sqrt(p.x * p.x + p.y * p.y))), - Math.sqrt(p.x * p.x + p.y * p.y + p.z * p.z) * 1000, // 转换为米 + Cesium.Math.toDegrees(Math.atan2(p.z, Math.hypot(p.x, p.y))), + Math.hypot(p.x, p.y, p.z) * 1000, // 转换为米 ), ); } @@ -68,11 +68,10 @@ export async function demo_01_OrbitGeneration(viewer: Viewer) { const currentPosition = Cesium.Cartesian3.fromDegrees( Cesium.Math.toDegrees(Math.atan2(p.y, p.x)), - Cesium.Math.toDegrees(Math.atan2(p.z, Math.sqrt(p.x * p.x + p.y * p.y))), - Math.sqrt(p.x * p.x + p.y * p.y + p.z * p.z) * 1000, // 转换为米 + Cesium.Math.toDegrees(Math.atan2(p.z, Math.hypot(p.x, p.y))), + Math.hypot(p.x, p.y, p.z) * 1000, // 转换为米 ); - await PromiseConfirmationService({ message: '创建轨道线' }); viewer.entities.add({ name: `${satelliteName} Orbit`, polyline: { @@ -85,7 +84,6 @@ export async function demo_01_OrbitGeneration(viewer: Viewer) { }, }); - await PromiseConfirmationService({ message: '添加卫星实体' }); // 添加卫星实体 const satellite = viewer.entities.add({ label: { @@ -113,6 +111,5 @@ export async function demo_01_OrbitGeneration(viewer: Viewer) { }); // 将相机定位到卫星 - await PromiseConfirmationService({ message: '飞向卫星' }); viewer.flyTo(satellite, { duration: 3 }); } diff --git a/src/pages/cesium/cesium-helper/demo_02_Track.ts b/src/pages/cesium/cesium-helper/demo_02_Track.ts index ab2d17a..1aba5dd 100644 --- a/src/pages/cesium/cesium-helper/demo_02_Track.ts +++ b/src/pages/cesium/cesium-helper/demo_02_Track.ts @@ -11,8 +11,8 @@ export async function demo_02_Track(viewer: Viewer) { 2 62879 42.9977 257.3937 0001725 269.2925 90.7748 15.77864921 5143`; // 解析TLE数据 - const lines = tle.split('\n'); - const satelliteName = lines[0].trim(); + const lines = tle.split('\n') as [string, string, string]; + const satelliteName = lines[0]?.trim(); const satrec = twoline2satrec(lines[1], lines[2]); // 创建卫星实体 @@ -51,7 +51,11 @@ 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(); @@ -69,34 +73,35 @@ export async function demo_02_Track(viewer: Viewer) { // 用于存储轨道点的数组(用于显示完整轨道) const orbitPositions: Cesium.Cartesian3[] = []; - // 保存卫星高度数据,用于覆盖范围计算 - let satelliteAltitude = 550000; // 默认高度为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); // 计算卫星位置 const positionAndVelocity = propagate(satrec, jsDate); - if (typeof positionAndVelocity.position !== 'boolean') { + 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; + const cesiumPosition = new Cesium.Cartesian3( + position.x * 1000, + position.y * 1000, + position.z * 1000, + ); // 保存位置用于绘制完整轨道 orbitPositions.push(cesiumPosition); // 添加位置样本 positionProperty.addSample(time, cesiumPosition); - } else { - console.error('Error calculating satellite position'); } } @@ -108,7 +113,7 @@ export async function demo_02_Track(viewer: Viewer) { clampToGround: false, material: new Cesium.PolylineDashMaterialProperty({ color: Cesium.Color.CYAN, - dashLength: 8.0, + dashLength: 8, }), positions: orbitPositions, width: 1, @@ -117,100 +122,74 @@ 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; // 覆盖角度(度) - const _coverageRadius = satelliteAltitude * Math.tan(Cesium.Math.toRadians(coverageAngle)); // 创建覆盖范围实体 - const _coverageEntity = 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(10000, 10000, 10000); - - // 获取当前卫星高度 - 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()); - - // 计算从卫星到地球中心的方向 - const _direction = 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.0), + 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 10000; + 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); - if (!satPosition) return 10000; + 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 - // 设置相机自动跟踪卫星 - viewer.trackedEntity = satelliteEntity; - } + // https://github.com/CesiumGS/cesium/issues/8350 + // 设置相机自动跟踪卫星 + viewer.trackedEntity = satelliteEntity; // 开始动画 viewer.clock.shouldAnimate = true;