feat: 添加 Cesium 相关功能和配置,更新样式及文档
Some checks failed
/ depcheck (push) Successful in 2m14s
/ build-and-deploy-to-vercel (push) Failing after 51s
/ surge (push) Successful in 2m31s
/ playwright (push) Successful in 1m21s

This commit is contained in:
mini2024
2025-03-05 00:42:30 +08:00
parent 49a7ef0dae
commit dc318c04e3
12 changed files with 540 additions and 3 deletions

View File

@ -13,4 +13,5 @@
.layout-main {
flex: 1 1 auto;
padding-bottom: 2rem;
position: relative;
}

View File

@ -0,0 +1,10 @@
## 配置项目
- https://github.dev/CesiumGS/cesium-vite-example
- https://cesium.com/blog/2024/02/13/configuring-vite-or-webpack-for-cesiumjs/
- https://cesium.com/learn/cesiumjs-learn/cesiumjs-quickstart/
- vite-plugin-cesium
## 参考
- https://www.npmjs.com/package/vue-cesium
- https://zouyaoji.top/vue-cesium/#/zh-CN/component/controls/vc-navigation
- https://cesium.pages.dev/

View File

@ -0,0 +1,83 @@
import * as Cesium from 'cesium';
import 'cesium/Build/Cesium/Widgets/widgets.css';
// Cesium.Ion.defaultAccessToken =
// 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiJiYjZmMWM4Ny01YzQ4LTQ3MzUtYTI5Mi1hNTgyNjdhMmFiMmMiLCJpZCI6NjIwMjgsImlhdCI6MTYyNjY3MTMxNX0.5SelYUyzXWRoMyjjFvmFIAoPtWlJPQMjsVl2e_jQe-c';
export function cesium_init() {
// 复写原型方法 用于timeline组件日期格式化
// @ts-expect-error node_modules/@cesium/widgets/Source/Timeline/Timeline.js
Cesium.Timeline.prototype.makeLabel = function (time) {
let minutes = 0 - new Date().getTimezoneOffset();
let dataZone8 = Cesium.JulianDate.addMinutes(time, minutes, new Cesium.JulianDate());
return Cesium.JulianDate.toIso8601(dataZone8).slice(0, 19);
};
// cesium-viewer-bottom
const viewer = new Cesium.Viewer('cesiumContainer', {
/* 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,
// globe: false, // 地球
baseLayerPicker: true,
homeButton: true, // Home按钮
fullscreenButton: !true, // 全屏按钮
geocoder: true, // = IonGeocodeProviderType.DEFAULT] - 在使用Geocoder小部件进行搜索时使用的地理编码服务或服务。如果设置为false则不会创建Geocoder小部件。
infoBox: true, // InfoBox小部件。
navigationHelpButton: !true, // 是否显示导航帮助按钮
projectionPicker: !true, // 投影选择器
sceneModePicker: true, // 是否显示场景模式选择器(2D/3D切换)
animation: true, // 是否创建动画小部件
timeline: true, // If set to false, the Timeline widget will not be created.
selectionIndicator: true,
requestRenderMode: !true, // 如果为真渲染帧将仅在场景内部发生变化时需要时发生。启用此功能可以减少应用程序的CPU/GPU使用率并在移动设备上节省更多电量但在此模式下需要使用{@link Scene#requestRender}显式渲染新帧。在API的其他部分对场景进行更改后在许多情况下都需要这样做。请参阅{@link https://cesium.com/blog/2018/01/24/cesium-scene-rendering-performance/|使用显式渲染提高性能}。
showRenderLoopErrors: true, // 如果为真当发生渲染循环错误时此小部件将自动向用户显示包含错误的HTML面板。
orderIndependentTranslucency: false, // 顺序无关透明度
shadows: true, // Determines if shadows are cast by light sources.
});
/* if ($__DEV__) */ viewer.scene.debugShowFramesPerSecond = true;
// 时间格式化
let minutes = 0 - new Date().getTimezoneOffset(); // 0 - (-480);
viewer.animation.viewModel.timeFormatter = function (date, _viewModel) {
let dataZone8 = Cesium.JulianDate.addMinutes(date, minutes, new Cesium.JulianDate());
return Cesium.JulianDate.toIso8601(dataZone8).slice(11, 19);
};
viewer.animation.viewModel.dateFormatter = function (date, _viewModel) {
let dataZone8 = Cesium.JulianDate.addMinutes(date, minutes, new Cesium.JulianDate());
return Cesium.JulianDate.toIso8601(dataZone8).slice(0, 10);
};
//高德卫星地图
let gaoDeSatelliteImgLayer = new Cesium.UrlTemplateImageryProvider({
url: 'https://webst02.is.autonavi.com/appmaptile?style=6&x={x}&y={y}&z={z}',
minimumLevel: 3,
maximumLevel: 18,
tilingScheme: new Cesium.WebMercatorTilingScheme(),
});
const customLayerViewModel = new Cesium.ProviderViewModel({
name: '高德地图',
iconUrl: 'gaodeImage.png',
tooltip: '高德地图',
category: 'Cesium ion', // 或 'Other 、Cesium ion'、'Bing Maps' 等
creationFunction: function () {
return gaoDeSatelliteImgLayer;
},
});
// 设置高德地图为默认图层
viewer.baseLayerPicker.viewModel.imageryProviderViewModels.unshift(customLayerViewModel);
const selectedViewModel = viewer.baseLayerPicker.viewModel.imageryProviderViewModels[0];
viewer.baseLayerPicker.viewModel.selectedImagery = selectedViewModel;
Cesium.Camera.DEFAULT_VIEW_RECTANGLE = Cesium.Rectangle.fromDegrees(
75.0, // 西经
10.0, // 南纬
140.0, // 东经
60.0, // 北纬
);
return viewer;
}

View File

@ -0,0 +1,118 @@
import type { Viewer } from 'cesium';
import * as Cesium from 'cesium';
import { twoline2satrec, propagate, gstime, eciToEcf } from 'satellite.js';
export async function demoOrbitGeneration(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 tleLine1 = lines[1];
const tleLine2 = lines[2];
// 创建卫星记录
const satrec = twoline2satrec(tleLine1, tleLine2);
// 创建轨道点
const pointsArray = [];
const totalMinutes = 1440; // 一天的分钟数
const timeStepInMinutes = 10;
// 当前时间
const now = new Date();
// 计算一条完整的轨道
for (let i = 0; i < totalMinutes; i += timeStepInMinutes) {
// 创建时间点
const time = new Date(now.getTime() + i * 60000);
// 获取卫星位置
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.sqrt(p.x * p.x + p.y * p.y))),
Math.sqrt(p.x * p.x + p.y * p.y + p.z * 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.sqrt(p.x * p.x + p.y * p.y))),
Math.sqrt(p.x * p.x + p.y * p.y + p.z * p.z) * 1000, // 转换为米
);
// 创建轨道线
const orbitPath = viewer.entities.add({
name: `${satelliteName} Orbit`,
polyline: {
positions: pointsArray,
width: 2,
material: new Cesium.PolylineGlowMaterialProperty({
glowPower: 0.2,
color: Cesium.Color.BLUE,
}),
},
});
// 添加卫星实体
const satellite = viewer.entities.add({
name: satelliteName,
position: currentPosition,
point: {
pixelSize: 10,
color: Cesium.Color.YELLOW,
outlineColor: Cesium.Color.WHITE,
outlineWidth: 2,
},
label: {
text: satelliteName,
font: '14pt sans-serif',
style: Cesium.LabelStyle.FILL_AND_OUTLINE,
fillColor: Cesium.Color.WHITE,
outlineColor: Cesium.Color.BLACK,
outlineWidth: 2,
verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
pixelOffset: new Cesium.Cartesian2(0, -10),
},
// billboard: {
// image: '/assets/satellite.png', // 您需要添加一个卫星图标
// scale: 0.5,
// },
});
// 将相机定位到卫星
viewer.flyTo(satellite, { duration: 3 });
return {
orbitPath,
satellite,
};
}

View File

@ -0,0 +1,30 @@
<script setup lang="ts">
import type { Viewer } from 'cesium';
import { cesium_init } from './cesium-helper/00.cesium-init';
import { demoOrbitGeneration } from './cesium-helper/01.x';
let viewer: Viewer;
onMounted(async () => {
viewer = cesium_init();
Object.assign(globalThis, { viewer });
demoOrbitGeneration(viewer);
});
</script>
<template>
<div id="cesiumContainer"></div>
</template>
<style>
#cesiumContainer {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
.cesium-viewer-bottom {
display: none;
}
</style>