h-cesium-viewer
This commit is contained in:
@ -1,4 +0,0 @@
|
||||
- 卫星覆盖范围: 当前实现是基于卫星高度的一个简化估算(altitude * 0.8),代码注释也指明了这一点。这种方法不够精确,仅能提供一个大致的视觉参考。精确的覆盖范围计算需要考虑卫星的视场角 (FOV) 或波束宽度等具体参数。
|
||||
- TLE 数据 → 轨道 → 位置 & 速度
|
||||
- 大小 → 需要外部规格数据
|
||||
- 精确姿态 → 需要专门的 ADCS 数据 (TLE 不提供)
|
4
src/components/h-cesium-viewer/_资料/README copy.md
Normal file
4
src/components/h-cesium-viewer/_资料/README copy.md
Normal file
@ -0,0 +1,4 @@
|
||||
- 卫星覆盖范围: 当前实现是基于卫星高度的一个简化估算(altitude \* 0.8),代码注释也指明了这一点。这种方法不够精确,仅能提供一个大致的视觉参考。精确的覆盖范围计算需要考虑卫星的视场角 (FOV) 或波束宽度等具体参数。
|
||||
- TLE 数据 → 轨道 → 位置 & 速度
|
||||
- 大小 → 需要外部规格数据
|
||||
- 精确姿态 → 需要专门的 ADCS 数据 (TLE 不提供)
|
@ -1,31 +1,24 @@
|
||||
import * as Cesium from 'cesium';
|
||||
import { eciToEcf, gstime, propagate, type SatRec, twoline2satrec } from 'satellite.js';
|
||||
|
||||
import { VIEWER_OPTIONS_FN } from './VIEWER_OPTIONS';
|
||||
import type { GroundStationOptions, SatelliteOptions } from './h-cesium-viewer-class.types'; // 2小时
|
||||
|
||||
export interface GroundStationOptions {
|
||||
height?: number; // 可选高度,默认为0
|
||||
id: string; // 站点的唯一标识符
|
||||
latitude: number;
|
||||
longitude: number;
|
||||
name: string;
|
||||
pixelSize?: number; // 点的可选像素大小
|
||||
}
|
||||
import { VIEWER_OPTIONS_FN } from './helper/_VIEWER_OPTIONS';
|
||||
import { configureCesium } from './helper/configureCesium';
|
||||
import { configureTimeLine } from './helper/configureTimeLine';
|
||||
|
||||
// 卫星选项接口
|
||||
export interface SatelliteOptions {
|
||||
id: string; // 卫星的唯一标识符
|
||||
orbitDurationHours?: number; // 轨道显示时长(小时),默认为 2
|
||||
showOrbit?: boolean; // 是否显示完整轨道线,默认为 true
|
||||
timeStepSeconds?: number; // 轨道计算步长(秒),默认为 30
|
||||
tle: string; // 包含卫星名称和两行 TLE 数据的字符串,格式如下:
|
||||
// NAME
|
||||
// TLE1
|
||||
// TLE2
|
||||
}
|
||||
const 默认轨道时长秒 = 2 * 60 * 60;
|
||||
|
||||
export { type GroundStationOptions, type SatelliteOptions } from './h-cesium-viewer-class.types';
|
||||
|
||||
Cesium.Ion.defaultAccessToken = import.meta.env.VITE_CESIUM_ION_TOKEN; // 用了离线地图的情况是不需要的。
|
||||
|
||||
Object.assign(globalThis, { Cesium });
|
||||
|
||||
configureCesium();
|
||||
|
||||
export class HCesiumViewerCls {
|
||||
private viewer: Cesium.Viewer | null = null;
|
||||
viewer: Cesium.Viewer | null = null;
|
||||
// 用于存储当前地面站实体的 Map
|
||||
currentStationEntities: Map<string, Cesium.Entity> = new Map();
|
||||
// 用于存储当前卫星实体的 Map (包括轨道实体)
|
||||
@ -45,6 +38,10 @@ export class HCesiumViewerCls {
|
||||
initCesiumViewer(container: ConstructorParameters<typeof Cesium.Viewer>[0]) {
|
||||
this.viewer = new Cesium.Viewer(container, VIEWER_OPTIONS_FN());
|
||||
|
||||
configureTimeLine(this.viewer);
|
||||
|
||||
this.viewer.scene.debugShowFramesPerSecond = true;
|
||||
|
||||
// 初始化时清空可能存在的旧实体引用
|
||||
this.currentStationEntities.clear();
|
||||
this.currentSatelliteEntities.clear();
|
||||
@ -158,7 +155,7 @@ export class HCesiumViewerCls {
|
||||
const {
|
||||
id,
|
||||
tle,
|
||||
orbitDurationHours = 2, // 默认轨道时长 2 小时
|
||||
orbitDurationSeconds = 默认轨道时长秒,
|
||||
timeStepSeconds = 30, // 默认步长 30 秒
|
||||
showOrbit = true, // 默认显示轨道
|
||||
} = options;
|
||||
@ -215,18 +212,17 @@ export class HCesiumViewerCls {
|
||||
color: randomBaseColor, // 使用随机基色
|
||||
}),
|
||||
width: 2,
|
||||
leadTime: (orbitDurationHours * 3600) / 2, // 显示未来一半时间的轨迹
|
||||
trailTime: (orbitDurationHours * 3600) / 2, // 显示过去一半时间的轨迹
|
||||
leadTime: orbitDurationSeconds / 2, // 显示未来一半时间的轨迹
|
||||
trailTime: orbitDurationSeconds / 2, // 显示过去一半时间的轨迹
|
||||
},
|
||||
});
|
||||
|
||||
// --- 计算轨道 ---
|
||||
const totalSeconds = orbitDurationHours * 60 * 60;
|
||||
const startTime = this.viewer.clock.currentTime; // 使用当前 viewer 的时间作为起点
|
||||
const positionProperty = new Cesium.SampledPositionProperty();
|
||||
const orbitPositions: Cesium.Cartesian3[] = []; // 用于存储完整轨道点
|
||||
|
||||
for (let i = 0; i <= totalSeconds; i += timeStepSeconds) {
|
||||
for (let i = 0; i <= orbitDurationSeconds; i += timeStepSeconds) {
|
||||
const time = Cesium.JulianDate.addSeconds(startTime, i, new Cesium.JulianDate());
|
||||
const jsDate = Cesium.JulianDate.toDate(time);
|
||||
|
||||
@ -311,7 +307,7 @@ export class HCesiumViewerCls {
|
||||
ellipse: {
|
||||
semiMajorAxis: coverageRadius,
|
||||
semiMinorAxis: coverageRadius, // 假设圆形覆盖
|
||||
material: randomBaseColor.withAlpha(0.2), // 基于随机基色的半透明填充
|
||||
material: randomBaseColor.withAlpha(0.2 + 0.3), // 基于随机基色的半透明填充
|
||||
heightReference: Cesium.HeightReference.CLAMP_TO_GROUND, // 贴合地形
|
||||
outline: true,
|
||||
outlineColor: randomBaseColor.withAlpha(0.8), // 基于随机基色的较深半透明轮廓
|
||||
|
@ -0,0 +1,20 @@
|
||||
export interface GroundStationOptions {
|
||||
height?: number; // 可选高度,默认为0
|
||||
id: string; // 站点的唯一标识符
|
||||
latitude: number;
|
||||
longitude: number;
|
||||
name: string;
|
||||
pixelSize?: number; // 点的可选像素大小
|
||||
}
|
||||
|
||||
// 卫星选项接口
|
||||
export interface SatelliteOptions {
|
||||
id: string; // 卫星的唯一标识符
|
||||
orbitDurationSeconds?: number; // 轨道显示时长
|
||||
showOrbit?: boolean; // 是否显示完整轨道线,默认为 true
|
||||
timeStepSeconds?: number; // 轨道计算步长(秒),默认为 30
|
||||
tle: string; // 包含卫星名称和两行 TLE 数据的字符串,格式如下:
|
||||
// NAME
|
||||
// TLE1
|
||||
// TLE2
|
||||
}
|
@ -1,26 +1,6 @@
|
||||
import * as Cesium from 'cesium';
|
||||
|
||||
import { VIEWER_OPTIONS } from './00.cesium-init.VIEWER_OPTIONS';
|
||||
|
||||
import 'cesium/Build/Cesium/Widgets/widgets.css';
|
||||
|
||||
Cesium.Ion.defaultAccessToken = import.meta.env.VITE_CESIUM_ION_TOKEN; // 用了离线地图的情况是不需要的。
|
||||
|
||||
Object.assign(globalThis, { Cesium });
|
||||
|
||||
_configureCesium();
|
||||
|
||||
export function cesium_init(container: Element) {
|
||||
const viewer = new Cesium.Viewer(container, VIEWER_OPTIONS());
|
||||
|
||||
viewer.scene.debugShowFramesPerSecond = true;
|
||||
|
||||
initTimeLine(viewer);
|
||||
|
||||
return viewer;
|
||||
}
|
||||
|
||||
function _configureCesium() {
|
||||
export function configureCesium() {
|
||||
if (document.querySelector('#hide-cesium-viewer-bottom') === null) {
|
||||
document.head.append(
|
||||
Object.assign(document.createElement('style'), {
|
||||
@ -64,15 +44,3 @@ function _configureCesium() {
|
||||
60, // 北纬
|
||||
);
|
||||
}
|
||||
|
||||
function initTimeLine(viewer: Cesium.Viewer, totalSeconds = /* 默认场景的时间跨度 */ 24 * 60 * 60) {
|
||||
const start = Cesium.JulianDate.fromIso8601(new Date().toISOString());
|
||||
const stop = Cesium.JulianDate.addSeconds(start, totalSeconds, new Cesium.JulianDate());
|
||||
viewer.clock.startTime = start.clone();
|
||||
viewer.clock.stopTime = stop.clone();
|
||||
viewer.clock.currentTime = start.clone();
|
||||
viewer.timeline.zoomTo(start, stop);
|
||||
viewer.clock.multiplier = 1;
|
||||
viewer.clock.shouldAnimate = true;
|
||||
viewer.clock.clockRange = Cesium.ClockRange.LOOP_STOP;
|
||||
}
|
20
src/components/h-cesium-viewer/helper/configureTimeLine.ts
Normal file
20
src/components/h-cesium-viewer/helper/configureTimeLine.ts
Normal file
@ -0,0 +1,20 @@
|
||||
import * as Cesium from 'cesium';
|
||||
|
||||
const 默认场景的时间跨度 = 2 * 60 * 60; // 2小时
|
||||
|
||||
export function configureTimeLine(viewer: Cesium.Viewer, totalSeconds = 默认场景的时间跨度) {
|
||||
const start = Cesium.JulianDate.fromIso8601(new Date().toISOString());
|
||||
const stop = Cesium.JulianDate.addSeconds(start, totalSeconds, new Cesium.JulianDate());
|
||||
|
||||
// 设置时钟范围
|
||||
viewer.clock.startTime = start.clone();
|
||||
viewer.clock.stopTime = stop.clone();
|
||||
viewer.clock.currentTime = start.clone();
|
||||
viewer.clock.clockRange = Cesium.ClockRange.LOOP_STOP;
|
||||
viewer.clock.multiplier = 30; // 30倍速播放
|
||||
|
||||
// 设置时间轴范围
|
||||
viewer.timeline.zoomTo(start, stop);
|
||||
|
||||
viewer.clock.shouldAnimate = true;
|
||||
}
|
@ -1,33 +0,0 @@
|
||||
import type { Viewer } from 'cesium';
|
||||
|
||||
import * as Cesium from 'cesium';
|
||||
|
||||
export const VIEWER_OPTIONS = (): Viewer.ConstructorOptions => {
|
||||
return {
|
||||
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: false,
|
||||
fullscreenButton: !true, // 全屏按钮
|
||||
geocoder: false, // = 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,
|
||||
};
|
||||
};
|
@ -1,170 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import type { Entity, Viewer } from 'cesium';
|
||||
|
||||
import { FilterOutlined, SearchOutlined } from '@ant-design/icons-vue';
|
||||
import { computed, reactive, ref, watchEffect } from 'vue';
|
||||
|
||||
import SatelliteEntity from '../cesium-helper/SatelliteEntity';
|
||||
|
||||
const { tleList, viewer } = defineProps<{
|
||||
tleList: string[];
|
||||
viewer: null | Viewer;
|
||||
}>();
|
||||
|
||||
// 提取卫星名称的函数
|
||||
const getSatelliteName = (tle: string) => {
|
||||
return (tle.split('\n') as [string, string, string])[0].trim();
|
||||
};
|
||||
|
||||
// 将satellites改为计算属性,响应tleList的变化
|
||||
const satellites = computed(() => [...new Set(tleList)]);
|
||||
|
||||
// 创建Map存储卫星实体
|
||||
const satelliteEntities = ref<Map<string, Entity>>(new Map());
|
||||
|
||||
// 创建Set存储已选中的卫星TLE
|
||||
const selectedSatellites = ref<Set<string>>(new Set());
|
||||
|
||||
const searchText = ref('');
|
||||
|
||||
const state = reactive({
|
||||
checkAll: false,
|
||||
indeterminate: false,
|
||||
});
|
||||
|
||||
// 计算属性
|
||||
const selectedCount = computed(() => selectedSatellites.value.size);
|
||||
const totalCount = computed(() => satellites.value.length);
|
||||
|
||||
// 过滤后的卫星列表 - 添加记忆化以提高性能
|
||||
const filteredSatellites = computed(() => {
|
||||
if (!searchText.value) return satellites.value;
|
||||
|
||||
const searchLower = searchText.value.toLowerCase();
|
||||
return satellites.value.filter((tle) => getSatelliteName(tle).toLowerCase().includes(searchLower));
|
||||
});
|
||||
|
||||
// 判断卫星是否被选中
|
||||
const isSatelliteSelected = (tle: string) => {
|
||||
return selectedSatellites.value.has(tle);
|
||||
};
|
||||
|
||||
// 监听选中状态变化,更新全选和半选状态
|
||||
watchEffect(() => {
|
||||
const count = selectedCount.value;
|
||||
const filteredCount = filteredSatellites.value.length;
|
||||
state.indeterminate = count > 0 && count < filteredCount;
|
||||
state.checkAll = count === filteredCount && filteredCount > 0;
|
||||
});
|
||||
|
||||
// 更新卫星实体(添加或移除)
|
||||
const updateSatelliteEntity = (tle: string, selected: boolean) => {
|
||||
if (!viewer) return;
|
||||
|
||||
const satelliteName = getSatelliteName(tle);
|
||||
|
||||
if (selected) {
|
||||
// 添加卫星到viewer
|
||||
try {
|
||||
const satelliteObject = new SatelliteEntity(tle);
|
||||
const cesiumSateEntity = satelliteObject.createSatelliteEntity();
|
||||
const result = viewer.entities.add(cesiumSateEntity);
|
||||
satelliteEntities.value.set(tle, result);
|
||||
} catch (error) {
|
||||
console.error(`添加卫星 ${satelliteName} 失败:`, error);
|
||||
}
|
||||
} else {
|
||||
// 从viewer中移除卫星
|
||||
const entity = satelliteEntities.value.get(tle);
|
||||
if (entity) {
|
||||
viewer.entities.remove(entity);
|
||||
satelliteEntities.value.delete(tle);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 切换卫星选中状态
|
||||
const toggleSatellite = (tle: string) => {
|
||||
const isSelected = isSatelliteSelected(tle);
|
||||
|
||||
if (isSelected) {
|
||||
selectedSatellites.value.delete(tle);
|
||||
} else {
|
||||
selectedSatellites.value.add(tle);
|
||||
}
|
||||
|
||||
updateSatelliteEntity(tle, !isSelected);
|
||||
};
|
||||
|
||||
// 全选/取消全选
|
||||
const onCheckAllChange = (e: { target: { checked: boolean } }) => {
|
||||
const checked = e.target.checked;
|
||||
|
||||
for (const tle of filteredSatellites.value) {
|
||||
const isCurrentlySelected = isSatelliteSelected(tle);
|
||||
|
||||
if (isCurrentlySelected !== checked) {
|
||||
if (checked) {
|
||||
selectedSatellites.value.add(tle);
|
||||
} else {
|
||||
selectedSatellites.value.delete(tle);
|
||||
}
|
||||
|
||||
updateSatelliteEntity(tle, checked);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="SatelliteSelectorPanel">
|
||||
<APopover :overlay-style="{ width: '320px' }" placement="bottomLeft" trigger="click">
|
||||
<template #title>
|
||||
选择卫星
|
||||
<span class="text-xs font-normal text-gray-700 dark:text-gray-400">{{
|
||||
`(已选择: ${selectedCount}/${totalCount})`
|
||||
}}</span>
|
||||
</template>
|
||||
<template #content>
|
||||
<!-- 搜索框 -->
|
||||
<AInput v-model:value="searchText" allow-clear placeholder="搜索卫星">
|
||||
<template #prefix><SearchOutlined /></template>
|
||||
</AInput>
|
||||
|
||||
<ADivider class="my-3" />
|
||||
|
||||
<!-- 无结果提示 -->
|
||||
<div v-if="filteredSatellites.length === 0" class="py-4 text-center text-gray-500">没有找到匹配的卫星</div>
|
||||
|
||||
<!-- 全选选项 -->
|
||||
<ACheckbox
|
||||
v-if="filteredSatellites.length > 0"
|
||||
:checked="state.checkAll"
|
||||
:indeterminate="state.indeterminate"
|
||||
@change="onCheckAllChange"
|
||||
>
|
||||
全选
|
||||
</ACheckbox>
|
||||
|
||||
<!-- 卫星列表 -->
|
||||
<div class="satellite-list max-h-60 overflow-y-auto pr-1">
|
||||
<ACheckbox
|
||||
v-for="tle in filteredSatellites"
|
||||
:key="tle"
|
||||
:checked="isSatelliteSelected(tle)"
|
||||
class="my-2 flex w-full"
|
||||
@change="() => toggleSatellite(tle)"
|
||||
>
|
||||
{{ getSatelliteName(tle) }}
|
||||
</ACheckbox>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- 触发按钮 -->
|
||||
<AButton>
|
||||
<template #icon><FilterOutlined /></template>
|
||||
<span v-if="selectedCount > 0" class="ml-1">({{ selectedCount }})</span>
|
||||
</AButton>
|
||||
</APopover>
|
||||
</div>
|
||||
</template>
|
@ -1,78 +0,0 @@
|
||||
<script setup lang="ts">
|
||||
import type { Viewer } from 'cesium';
|
||||
|
||||
import { TLE_LIST } from './cesium-demos/_TLE_DATA';
|
||||
import { demo_卫星加站点 } from './cesium-demos/demo_03_卫星加站点';
|
||||
import { cesium_init } from './cesium-helper/00.cesium-init';
|
||||
import SatelliteSelector from './components/SatelliteSelector.vue';
|
||||
|
||||
// Cesium相关
|
||||
let viewer = $ref<null | Viewer>(null);
|
||||
|
||||
const tleList = ref([TLE_LIST[0]] as string[]);
|
||||
const spinning = ref(false);
|
||||
|
||||
// 模拟接口获取TLE数据
|
||||
const fetchTLEData = async () => {
|
||||
spinning.value = true; // 开始加载,设置状态为true
|
||||
await new Promise((resolve) => setTimeout(resolve, 2000)); // 模拟2秒延迟
|
||||
tleList.value = TLE_LIST.slice(0, 2); // 设置TLE数据
|
||||
spinning.value = false; // 加载完成,设置状态为false
|
||||
};
|
||||
|
||||
// 初始化Cesium
|
||||
onMounted(async () => {
|
||||
fetchTLEData();
|
||||
|
||||
const container = document.querySelector('#cesiumContainer');
|
||||
if (!container) return;
|
||||
|
||||
viewer = cesium_init(container);
|
||||
|
||||
Object.assign(globalThis, { viewer });
|
||||
|
||||
demo_卫星加站点(viewer);
|
||||
});
|
||||
|
||||
// 清理资源
|
||||
onBeforeUnmount(() => {
|
||||
if (viewer) {
|
||||
// 销毁viewer
|
||||
viewer.destroy();
|
||||
viewer = null;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div id="cesiumContainer" class="absolute top-0 left-0 right-0 bottom-0">
|
||||
<ASpin
|
||||
size="large"
|
||||
v-if="spinning"
|
||||
class="z-101 absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2"
|
||||
/>
|
||||
|
||||
<div class="z-100 absolute left-4 top-4 flex flex-col gap-4">
|
||||
<SatelliteSelector :viewer="viewer" :tle-list />
|
||||
<AButton
|
||||
@click="
|
||||
() => {
|
||||
tleList = TLE_LIST;
|
||||
}
|
||||
"
|
||||
>
|
||||
<template #icon> <ReloadOutlined /> </template>3 个卫星
|
||||
</AButton>
|
||||
|
||||
<AButton
|
||||
@click="
|
||||
() => {
|
||||
tleList = TLE_LIST.splice(0, 2);
|
||||
}
|
||||
"
|
||||
>
|
||||
<template #icon> <ReloadOutlined /> </template>2 个卫星
|
||||
</AButton>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
@ -1 +0,0 @@
|
||||
- https://satnogs.org
|
@ -5,19 +5,26 @@ import { eciToEcf, gstime, propagate, twoline2satrec } from 'satellite.js';
|
||||
/**
|
||||
* 未来2小时内每30秒的位置
|
||||
*/
|
||||
export async function demo_02_Track(viewer: Viewer) {
|
||||
const tle = `STARLINK-11371 [DTC]
|
||||
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`;
|
||||
|
||||
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: 'STARLINK-11371',
|
||||
id: `${satelliteName}-satellite`,
|
||||
label: {
|
||||
fillColor: Cesium.Color.WHITE,
|
||||
font: '14pt sans-serif',
|
||||
@ -28,11 +35,11 @@ export async function demo_02_Track(viewer: Viewer) {
|
||||
text: satelliteName,
|
||||
verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
|
||||
},
|
||||
name: 'STARLINK-11371',
|
||||
name: `${satelliteName} Satellite`,
|
||||
// 卫星轨迹
|
||||
path: {
|
||||
material: new Cesium.PolylineGlowMaterialProperty({
|
||||
color: Cesium.Color.BLUE,
|
||||
color: pathColor,
|
||||
glowPower: 0.2,
|
||||
}),
|
||||
resolution: 1,
|
||||
@ -40,7 +47,7 @@ export async function demo_02_Track(viewer: Viewer) {
|
||||
},
|
||||
// 使用简单点图形表示卫星
|
||||
point: {
|
||||
color: Cesium.Color.YELLOW,
|
||||
color: satelliteColor,
|
||||
outlineColor: Cesium.Color.WHITE,
|
||||
outlineWidth: 2,
|
||||
pixelSize: 10,
|
||||
@ -102,12 +109,12 @@ export async function demo_02_Track(viewer: Viewer) {
|
||||
|
||||
// 添加完整轨道线
|
||||
viewer.entities.add({
|
||||
id: 'STARLINK-11371-orbit',
|
||||
name: 'STARLINK-11371 Full Orbit',
|
||||
id: `${satelliteName}-orbit`,
|
||||
name: `${satelliteName} Full Orbit`,
|
||||
polyline: {
|
||||
clampToGround: false,
|
||||
material: new Cesium.PolylineDashMaterialProperty({
|
||||
color: Cesium.Color.CYAN,
|
||||
color: orbitColor,
|
||||
dashLength: 8,
|
||||
}),
|
||||
positions: orbitPositions,
|
||||
@ -129,9 +136,9 @@ export async function demo_02_Track(viewer: Viewer) {
|
||||
// 创建覆盖范围实体
|
||||
viewer.entities.add({
|
||||
ellipsoid: {
|
||||
material: Cesium.Color.BLUE.withAlpha(0.2),
|
||||
material: coverageColor.withAlpha(0.2),
|
||||
outline: true,
|
||||
outlineColor: Cesium.Color.BLUE.withAlpha(0.8),
|
||||
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);
|
||||
@ -149,8 +156,8 @@ export async function demo_02_Track(viewer: Viewer) {
|
||||
slicePartitions: 24,
|
||||
stackPartitions: 16,
|
||||
},
|
||||
id: 'STARLINK-11371-coverage',
|
||||
name: 'STARLINK-11371 Coverage',
|
||||
id: `${satelliteName}-coverage`,
|
||||
name: `${satelliteName} Coverage`,
|
||||
orientation: new Cesium.CallbackProperty(() => {
|
||||
// 确保椭球体始终指向地球中心
|
||||
const position = satelliteEntity.position?.getValue(viewer.clock.currentTime);
|
||||
@ -169,9 +176,9 @@ export async function demo_02_Track(viewer: Viewer) {
|
||||
ellipse: {
|
||||
granularity: Cesium.Math.toRadians(1),
|
||||
height: 0,
|
||||
material: Cesium.Color.BLUE.withAlpha(0.2),
|
||||
material: coverageColor.withAlpha(0.2),
|
||||
outline: true,
|
||||
outlineColor: Cesium.Color.BLUE.withAlpha(0.8),
|
||||
outlineColor: coverageColor.withAlpha(0.8),
|
||||
outlineWidth: 2,
|
||||
semiMajorAxis: new Cesium.CallbackProperty(() => {
|
||||
const satPosition = satelliteEntity.position?.getValue(viewer.clock.currentTime);
|
||||
@ -192,8 +199,8 @@ export async function demo_02_Track(viewer: Viewer) {
|
||||
return cartographic.height * Math.tan(Cesium.Math.toRadians(coverageAngle));
|
||||
}, false),
|
||||
},
|
||||
id: 'STARLINK-11371-ground-coverage',
|
||||
name: 'STARLINK-11371 Ground Coverage',
|
||||
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();
|
||||
@ -208,7 +215,7 @@ export async function demo_02_Track(viewer: Viewer) {
|
||||
// - 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.trackedEntity = satelliteEntity;
|
||||
|
||||
// 开始动画
|
||||
viewer.clock.shouldAnimate = true;
|
1
typed-router.d.ts
vendored
1
typed-router.d.ts
vendored
@ -33,7 +33,6 @@ declare module 'vue-router/auto-routes' {
|
||||
'PageStyle': RouteRecordInfo<'PageStyle', '/Page/Style', Record<never, never>, Record<never, never>>,
|
||||
'PkgsUsageI18n': RouteRecordInfo<'PkgsUsageI18n', '/PkgsUsage/I18n', Record<never, never>, Record<never, never>>,
|
||||
'PkgsUsageTsEnumUtil': RouteRecordInfo<'PkgsUsageTsEnumUtil', '/PkgsUsage/ts-enum-util', Record<never, never>, Record<never, never>>,
|
||||
'SatelliteCesium': RouteRecordInfo<'SatelliteCesium', '/Satellite/Cesium', Record<never, never>, Record<never, never>>,
|
||||
'UIComponentsAntdV': RouteRecordInfo<'UIComponentsAntdV', '/UI-components/AntdV', Record<never, never>, Record<never, never>>,
|
||||
'UIComponentsComponents': RouteRecordInfo<'UIComponentsComponents', '/UI-components/Components', Record<never, never>, Record<never, never>>,
|
||||
'UIComponentsInfiniteLoading': RouteRecordInfo<'UIComponentsInfiniteLoading', '/UI-components/infinite-loading', Record<never, never>, Record<never, never>>,
|
||||
|
Reference in New Issue
Block a user