feat: 添加轨道生成演示功能
This commit is contained in:
@ -1,29 +1,23 @@
|
||||
// TODO: 干掉 api.cesium.com
|
||||
// XXX: ion-sdk ???
|
||||
|
||||
import * as Cesium from 'cesium';
|
||||
import 'cesium/Build/Cesium/Widgets/widgets.css';
|
||||
|
||||
// Cesium.Ion.defaultAccessToken =
|
||||
// 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiJiYjZmMWM4Ny01YzQ4LTQ3MzUtYTI5Mi1hNTgyNjdhMmFiMmMiLCJpZCI6NjIwMjgsImlhdCI6MTYyNjY3MTMxNX0.5SelYUyzXWRoMyjjFvmFIAoPtWlJPQMjsVl2e_jQe-c';
|
||||
Object.assign(window, { Cesium });
|
||||
|
||||
export function cesium_init() {
|
||||
// 复写原型方法 用于timeline组件日期格式化;
|
||||
// @ts-expect-error node_modules/@cesium/widgets/Source/Timeline/Timeline.js
|
||||
Cesium.Timeline.prototype.makeLabel = function (time) {
|
||||
const minutes = 0 - new Date().getTimezoneOffset();
|
||||
const dataZone8 = Cesium.JulianDate.addMinutes(time, minutes, new Cesium.JulianDate());
|
||||
return Cesium.JulianDate.toIso8601(dataZone8).slice(0, 19);
|
||||
};
|
||||
_configureCesium();
|
||||
|
||||
// cesium-viewer-bottom
|
||||
|
||||
const viewer = new Cesium.Viewer('cesiumContainer', {
|
||||
animation: true, // 是否创建动画小部件
|
||||
// globe: false, // 地球
|
||||
baseLayerPicker: true,
|
||||
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
|
||||
baseLayerPicker: !true,
|
||||
fullscreenButton: !true, // 全屏按钮
|
||||
geocoder: true, // = IonGeocodeProviderType.DEFAULT] - 在使用Geocoder小部件进行搜索时使用的地理编码服务或服务。如果设置为false,则不会创建Geocoder小部件。
|
||||
geocoder: !true, // = IonGeocodeProviderType.DEFAULT] - 在使用Geocoder小部件进行搜索时使用的地理编码服务或服务。如果设置为false,则不会创建Geocoder小部件。
|
||||
// globe: false, // 地球
|
||||
homeButton: true, // Home按钮
|
||||
infoBox: true, // InfoBox小部件。
|
||||
navigationHelpButton: !true, // 是否显示导航帮助按钮
|
||||
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/|使用显式渲染提高性能}。
|
||||
@ -33,51 +27,87 @@ export function cesium_init() {
|
||||
/* 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,
|
||||
shouldAnimate: !true,
|
||||
showRenderLoopErrors: true, // 如果为真,当发生渲染循环错误时,此小部件将自动向用户显示包含错误的HTML面板。
|
||||
timeline: true, // If set to false, the Timeline widget will not be created.
|
||||
});
|
||||
/* if ($__DEV__) */ viewer.scene.debugShowFramesPerSecond = true;
|
||||
|
||||
// 时间格式化
|
||||
const minutes = 0 - new Date().getTimezoneOffset(); // 0 - (-480);
|
||||
viewer.animation.viewModel.timeFormatter = function (date, _viewModel) {
|
||||
const dataZone8 = Cesium.JulianDate.addMinutes(date, minutes, new Cesium.JulianDate());
|
||||
return Cesium.JulianDate.toIso8601(dataZone8).slice(11, 19);
|
||||
};
|
||||
viewer.animation.viewModel.dateFormatter = function (date, _viewModel) {
|
||||
const dataZone8 = Cesium.JulianDate.addMinutes(date, minutes, new Cesium.JulianDate());
|
||||
return Cesium.JulianDate.toIso8601(dataZone8).slice(0, 10);
|
||||
};
|
||||
viewer.scene.debugShowFramesPerSecond = true;
|
||||
|
||||
//高德卫星地图
|
||||
const gaoDeSatelliteImgLayer = new Cesium.UrlTemplateImageryProvider({
|
||||
maximumLevel: 18,
|
||||
minimumLevel: 3,
|
||||
tilingScheme: new Cesium.WebMercatorTilingScheme(),
|
||||
url: 'https://webst02.is.autonavi.com/appmaptile?style=6&x={x}&y={y}&z={z}',
|
||||
});
|
||||
_configureMapTile(viewer);
|
||||
|
||||
const customLayerViewModel = new Cesium.ProviderViewModel({
|
||||
category: 'Cesium ion', // 或 'Other 、Cesium ion'、'Bing Maps' 等
|
||||
creationFunction: function () {
|
||||
return gaoDeSatelliteImgLayer;
|
||||
},
|
||||
iconUrl: 'gaodeImage.png',
|
||||
name: '高德地图',
|
||||
tooltip: '高德地图',
|
||||
});
|
||||
// 设置高德地图为默认图层
|
||||
viewer.baseLayerPicker.viewModel.imageryProviderViewModels.unshift(customLayerViewModel);
|
||||
const selectedViewModel = viewer.baseLayerPicker.viewModel.imageryProviderViewModels[0];
|
||||
viewer.baseLayerPicker.viewModel.selectedImagery = selectedViewModel;
|
||||
return viewer;
|
||||
}
|
||||
|
||||
function _configureCesium() {
|
||||
/* 时间日期格式化 */ {
|
||||
const minutes = 0 - new Date().getTimezoneOffset(); // 0 - (-480);
|
||||
|
||||
// Animation 的时间日期格式化
|
||||
Cesium.AnimationViewModel.defaultDateFormatter = function (date) {
|
||||
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());
|
||||
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());
|
||||
return Cesium.JulianDate.toIso8601(dataZone8).slice(0, 19);
|
||||
};
|
||||
}
|
||||
|
||||
// 默认视图区域
|
||||
Cesium.Camera.DEFAULT_VIEW_RECTANGLE = Cesium.Rectangle.fromDegrees(
|
||||
75.0, // 西经
|
||||
10.0, // 南纬
|
||||
140.0, // 东经
|
||||
60.0, // 北纬
|
||||
);
|
||||
|
||||
return viewer;
|
||||
}
|
||||
|
||||
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) {
|
||||
// 如果没有底图选择器
|
||||
viewer.imageryLayers.removeAll();
|
||||
viewer.imageryLayers.addImageryProvider(provider);
|
||||
} else {
|
||||
// 如果有底图选择器
|
||||
const customLayerViewModel = new Cesium.ProviderViewModel({
|
||||
category: 'Cesium ion', // 或 'Other 、Cesium ion'、'Bing Maps' 等
|
||||
creationFunction: function () {
|
||||
return provider;
|
||||
},
|
||||
iconUrl: 'gaodeImage.png',
|
||||
name: '高德地图',
|
||||
tooltip: '高德地图',
|
||||
});
|
||||
// 设置高德地图为默认图层
|
||||
viewer.baseLayerPicker.viewModel.imageryProviderViewModels.unshift(customLayerViewModel);
|
||||
const selectedViewModel = viewer.baseLayerPicker.viewModel.imageryProviderViewModels[0];
|
||||
viewer.baseLayerPicker.viewModel.selectedImagery = selectedViewModel;
|
||||
}
|
||||
}
|
||||
|
||||
function _flyToDemo(viewer: Cesium.Viewer) {
|
||||
// 将三维球定位到中国
|
||||
viewer.camera.flyTo({
|
||||
complete: function complete() {},
|
||||
destination: Cesium.Cartesian3.fromDegrees(103.84, 31.15, 17850000),
|
||||
orientation: {
|
||||
heading: Cesium.Math.toRadians(348.4202942851978),
|
||||
pitch: Cesium.Math.toRadians(-89.74026687972041),
|
||||
roll: Cesium.Math.toRadians(0),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ import type { Viewer } from 'cesium';
|
||||
import * as Cesium from 'cesium';
|
||||
import { eciToEcf, gstime, propagate, twoline2satrec } from 'satellite.js';
|
||||
|
||||
export async function demoOrbitGeneration(viewer: Viewer) {
|
||||
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`;
|
||||
@ -16,6 +16,7 @@ export async function demoOrbitGeneration(viewer: Viewer) {
|
||||
|
||||
// 创建卫星记录
|
||||
const satrec = twoline2satrec(tleLine1, tleLine2);
|
||||
console.debug(`satrec :>> `, satrec);
|
||||
|
||||
// 创建轨道点
|
||||
const pointsArray = [];
|
214
src/pages/cesium/cesium-helper/demo_02_Track.ts
Normal file
214
src/pages/cesium/cesium-helper/demo_02_Track.ts
Normal file
@ -0,0 +1,214 @@
|
||||
import type { Viewer } from 'cesium';
|
||||
|
||||
import * as Cesium from 'cesium';
|
||||
import { eciToEcf, gstime, propagate, twoline2satrec } from 'satellite.js';
|
||||
|
||||
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');
|
||||
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[] = [];
|
||||
|
||||
// 保存卫星高度数据,用于覆盖范围计算
|
||||
let satelliteAltitude = 550000; // 默认高度为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') {
|
||||
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);
|
||||
} else {
|
||||
console.error('Error calculating satellite position');
|
||||
}
|
||||
}
|
||||
|
||||
// 添加完整轨道线
|
||||
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.0,
|
||||
}),
|
||||
positions: orbitPositions,
|
||||
width: 1,
|
||||
},
|
||||
});
|
||||
|
||||
// 设置卫星的位置
|
||||
satelliteEntity.position = 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),
|
||||
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 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 10000;
|
||||
|
||||
// 计算卫星高度
|
||||
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),
|
||||
});
|
||||
|
||||
// 设置相机自动跟踪卫星
|
||||
viewer.trackedEntity = satelliteEntity;
|
||||
|
||||
// 开始动画
|
||||
viewer.clock.shouldAnimate = true;
|
||||
|
||||
return satelliteEntity;
|
||||
}
|
@ -2,14 +2,16 @@
|
||||
import type { Viewer } from 'cesium';
|
||||
|
||||
import { cesium_init } from './cesium-helper/00.cesium-init';
|
||||
import { demoOrbitGeneration } from './cesium-helper/01.x';
|
||||
import { demo_01_OrbitGeneration } from './cesium-helper/demo_01_OrbitGeneration';
|
||||
import { demo_02_Track } from './cesium-helper/demo_02_Track';
|
||||
|
||||
let viewer: Viewer;
|
||||
onMounted(async () => {
|
||||
viewer = cesium_init();
|
||||
viewer = cesium_init(document.getElementById('cesiumContainer')!);
|
||||
Object.assign(globalThis, { viewer });
|
||||
|
||||
demoOrbitGeneration(viewer);
|
||||
// demo_01_OrbitGeneration(viewer);
|
||||
demo_02_Track(viewer);
|
||||
});
|
||||
</script>
|
||||
|
||||
|
2
typed-router.d.ts
vendored
2
typed-router.d.ts
vendored
@ -21,7 +21,7 @@ declare module 'vue-router/auto-routes' {
|
||||
'Root': RouteRecordInfo<'Root', '/', Record<never, never>, Record<never, never>>,
|
||||
'$Path': RouteRecordInfo<'$Path', '/:path(.*)', { path: ParamValue<true> }, { path: ParamValue<false> }>,
|
||||
'AntdV': RouteRecordInfo<'AntdV', '/AntdV', Record<never, never>, Record<never, never>>,
|
||||
'API': RouteRecordInfo<'API', '/API', Record<never, never>, Record<never, never>>,
|
||||
'Api': RouteRecordInfo<'Api', '/api', Record<never, never>, Record<never, never>>,
|
||||
'Cesium': RouteRecordInfo<'Cesium', '/cesium', Record<never, never>, Record<never, never>>,
|
||||
'DataLoadersId': RouteRecordInfo<'DataLoadersId', '/data-loaders/:id', { id: ParamValue<true> }, { id: ParamValue<false> }>,
|
||||
'DataLoadersIdSub1UserId': RouteRecordInfo<'DataLoadersIdSub1UserId', '/data-loaders/:id/sub-1/:userId', { id: ParamValue<true>, userId: ParamValue<true> }, { id: ParamValue<false>, userId: ParamValue<false> }>,
|
||||
|
Reference in New Issue
Block a user