fix: 修复3D地球坐标映射多个严重bug
## Bug修复详情 ### 1. 致命错误:球面距离计算 (calculateDistance) - 问题:使用勾股定理计算经纬度距离,在球体表面完全错误 - 修复:改用Haversine公式计算球面大圆距离 - 影响:赤道1度=111km,极地1度=19km,原计算误差巨大 ### 2. 经度范围规范化 (vector3ToLatLon) - 问题:Math.atan2返回[-180°,180°],转换后可能超出标准范围 - 修复:添加while循环规范化到[-180, 180]区间 - 影响:避免本初子午线附近返回360°的异常值 ### 3. 屏幕坐标转换支持非全屏 (screenToEarthCoords) - 问题:假设Canvas永远全屏,非全屏时点击偏移严重 - 修复:新增domElement参数,使用getBoundingClientRect()计算相对坐标 - 影响:嵌入式3D地球组件也能精准拾取 ### 4. 地球旋转时经纬度映射错误 - 问题:Raycaster返回世界坐标,未考虑地球自转 - 修复:使用earth.worldToLocal()转换到本地坐标空间 - 影响:地球旋转时经纬度显示正确跟随 ## 新增功能 - CelesTrak卫星数据采集器 - Space-Track卫星数据采集器 - 卫星可视化模块(500颗,实时SGP4轨道计算) - 海底光缆悬停显示info-card - 统一info-card组件 - 工具栏按钮(Stellarium风格) - 缩放控制(百分比显示) - Docker volume映射(代码热更新) ## 文件变更 - utils.js: 坐标转换核心逻辑修复 - satellites.js: 新增卫星可视化 - cables.js: 悬停交互支持 - main.js: 悬停/锁定逻辑 - controls.js: 工具栏UI - info-card.js: 统一卡片组件 - docker-compose.yml: volume映射 - restart.sh: 简化重启脚本
This commit is contained in:
121
frontend/public/earth/js/info-card.js
Normal file
121
frontend/public/earth/js/info-card.js
Normal file
@@ -0,0 +1,121 @@
|
||||
// info-card.js - Unified info card module
|
||||
|
||||
let currentType = null;
|
||||
|
||||
const CARD_CONFIG = {
|
||||
cable: {
|
||||
icon: '🛥️',
|
||||
title: '电缆详情',
|
||||
className: 'cable',
|
||||
fields: [
|
||||
{ key: 'name', label: '名称' },
|
||||
{ key: 'owner', label: '所有者' },
|
||||
{ key: 'status', label: '状态' },
|
||||
{ key: 'length', label: '长度' },
|
||||
{ key: 'coords', label: '经纬度' },
|
||||
{ key: 'rfs', label: '投入使用' }
|
||||
]
|
||||
},
|
||||
satellite: {
|
||||
icon: '🛰️',
|
||||
title: '卫星详情',
|
||||
className: 'satellite',
|
||||
fields: [
|
||||
{ key: 'name', label: '名称' },
|
||||
{ key: 'norad_id', label: 'NORAD ID' },
|
||||
{ key: 'inclination', label: '倾角', unit: '°' },
|
||||
{ key: 'period', label: '周期', unit: '分钟' },
|
||||
{ key: 'perigee', label: '近地点', unit: 'km' },
|
||||
{ key: 'apogee', label: '远地点', unit: 'km' }
|
||||
]
|
||||
},
|
||||
supercomputer: {
|
||||
icon: '🖥️',
|
||||
title: '超算详情',
|
||||
className: 'supercomputer',
|
||||
fields: [
|
||||
{ key: 'name', label: '名称' },
|
||||
{ key: 'rank', label: '排名' },
|
||||
{ key: 'r_max', label: 'Rmax', unit: 'GFlops' },
|
||||
{ key: 'r_peak', label: 'Rpeak', unit: 'GFlops' },
|
||||
{ key: 'country', label: '国家' },
|
||||
{ key: 'city', label: '城市' }
|
||||
]
|
||||
},
|
||||
gpu_cluster: {
|
||||
icon: '🎮',
|
||||
title: 'GPU集群详情',
|
||||
className: 'gpu_cluster',
|
||||
fields: [
|
||||
{ key: 'name', label: '名称' },
|
||||
{ key: 'country', label: '国家' },
|
||||
{ key: 'city', label: '城市' }
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
export function initInfoCard() {
|
||||
// Close button removed - now uses external clear button
|
||||
}
|
||||
|
||||
export function setInfoCardNoBorder(noBorder = true) {
|
||||
const card = document.getElementById('info-card');
|
||||
if (card) {
|
||||
card.classList.toggle('no-border', noBorder);
|
||||
}
|
||||
}
|
||||
|
||||
export function showInfoCard(type, data) {
|
||||
const config = CARD_CONFIG[type];
|
||||
if (!config) {
|
||||
console.warn('Unknown info card type:', type);
|
||||
return;
|
||||
}
|
||||
|
||||
currentType = type;
|
||||
const card = document.getElementById('info-card');
|
||||
const icon = document.getElementById('info-card-icon');
|
||||
const title = document.getElementById('info-card-title');
|
||||
const content = document.getElementById('info-card-content');
|
||||
|
||||
card.className = 'info-card ' + config.className;
|
||||
icon.textContent = config.icon;
|
||||
title.textContent = config.title;
|
||||
|
||||
let html = '';
|
||||
for (const field of config.fields) {
|
||||
let value = data[field.key];
|
||||
|
||||
if (value === undefined || value === null || value === '') {
|
||||
value = '-';
|
||||
} else if (typeof value === 'number') {
|
||||
value = value.toLocaleString();
|
||||
}
|
||||
|
||||
if (field.unit && value !== '-') {
|
||||
value = value + ' ' + field.unit;
|
||||
}
|
||||
|
||||
html += `
|
||||
<div class="info-card-property">
|
||||
<span class="info-card-label">${field.label}</span>
|
||||
<span class="info-card-value">${value}</span>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
content.innerHTML = html;
|
||||
card.style.display = 'block';
|
||||
}
|
||||
|
||||
export function hideInfoCard() {
|
||||
const card = document.getElementById('info-card');
|
||||
if (card) {
|
||||
card.style.display = 'none';
|
||||
}
|
||||
currentType = null;
|
||||
}
|
||||
|
||||
export function getCurrentType() {
|
||||
return currentType;
|
||||
}
|
||||
Reference in New Issue
Block a user