feat(earth): Modularize 3D Earth page with ES Modules

## Changelog

### New Features
- Modularized 3D earth HTML page from single 1918-line file into ES Modules
- Split CSS into separate module files (base, info-panel, coordinates-display, legend, earth-stats)
- Split JS into separate modules (constants, utils, ui, earth, cables, controls, main)

### 3D Earth Rendering
- Use Three.js r128 (via esm.sh CDN) for color consistency with original
- Earth with 8K satellite texture and proper material settings
- Cloud layer with transparency and additive blending
- Starfield background (8000 stars)
- Latitude/longitude grid lines that rotate with Earth

### Cable System
- Load cable data from geo.json with great circle path calculation
- Support for MultiLineString and LineString geometry types
- Cable color from geo.json properties.color field
- Landing points loading from landing-point-geo.geojson

### User Interactions
- Mouse hover: highlight cable and show details
- Mouse click: lock cable with pulsing glow effect
- Click cable to pause rotation, click elsewhere to resume
- Click rotation toggle button to resume rotation and clear highlight
- Reset view with smooth animation (800ms cubic ease-out)
- Mouse wheel zoom support
- Drag to rotate Earth

### UI/UX Improvements
- Tooltip shows latitude, longitude, and altitude
- Prevent tooltip text selection during drag
- Hide tooltip during drag operation
- Blue border tooltip styling matching original
- Cursor changes to grabbing during drag
- Front-facing cable detection (only detect cables facing camera)

### Bug Fixes
- Grid lines now rotate with Earth (added as Earth child)
- Reset view button now works correctly
- Fixed camera reference in reset view
- Fixed autoRotate state management when clearing locked cable

### Original HTML
- Copied original 3dearthmult.html to public folder for reference
This commit is contained in:
rayd1o
2026-03-11 15:54:50 +08:00
parent 4ada75ca14
commit 6cb4398f3a
15 changed files with 1805 additions and 44 deletions

View File

@@ -1009,50 +1009,51 @@ function calculateDistance(lat1, lon1, lat2, lon2) {
);
}
// 从后端API加载GeoJSON数据
async function loadGeoJSONFromPath() {
try {
const API_PATH = '/api/v1/visualization/geo/cables';
console.log(`正在从 ${API_PATH} 加载GeoJSON数据...`);
showStatusMessage('正在加载电缆数据...', 'warning');
const response = await fetch(API_PATH);
if (!response.ok) {
throw new Error(`HTTP错误: ${response.status} ${response.statusText}`);
}
const geoJsonData = await response.json();
if (geoJsonData.features && Array.isArray(geoJsonData.features)) {
loadCablesFromGeoJSON(geoJsonData);
showStatusMessage(`成功加载 ${geoJsonData.features.length} 条电缆数据`, 'success');
} else {
throw new Error('无效的GeoJSON格式: 缺少features数组');
}
} catch (error) {
console.error('加载GeoJSON数据失败:', error);
const errorEl = document.getElementById('error-message');
errorEl.textContent = `无法加载API数据: ${error.message}`;
errorEl.style.display = 'block';
showStatusMessage('数据加载失败,请检查后端服务', 'error');
setTimeout(() => {
loadFallbackData();
}, 2000);
}
}
async function loadLandingPoints() {
try {
const API_PATH = '/api/v1/visualization/geo/landing-points';
console.log(`正在从 ${API_PATH} 加载登陆点数据...`);
const response = await fetch(API_PATH);
// 从固定路径加载GeoJSON数据
async function loadGeoJSONFromPath() {
try {
console.log(`正在从 ${GEOJSON_PATH} 加载GeoJSON数据...`);
showStatusMessage('正在加载电缆数据...', 'warning');
// 使用fetch API加载本地文件
const response = await fetch(GEOJSON_PATH);
if (!response.ok) {
throw new Error(`HTTP错误: ${response.status} ${response.statusText}`);
}
const geoJsonData = await response.json();
if (geoJsonData.features && Array.isArray(geoJsonData.features)) {
loadCablesFromGeoJSON(geoJsonData);
showStatusMessage(`成功加载 ${geoJsonData.features.length} 条电缆数据`, 'success');
} else {
throw new Error('无效的GeoJSON格式: 缺少features数组');
}
} catch (error) {
console.error('加载GeoJSON数据失败:', error);
// 显示错误信息
const errorEl = document.getElementById('error-message');
errorEl.textContent = `无法加载文件 ${GEOJSON_PATH}: ${error.message}`;
errorEl.style.display = 'block';
showStatusMessage('数据加载失败,请检查文件路径', 'error');
// 加载示例数据作为后备
setTimeout(() => {
loadFallbackData();
}, 2000);
}
}
async function loadLandingPoints() {
try {
console.log(`正在从 ${GEOJSON_PATH} 加载GeoJSON数据...`);
// 方式1从本地文件加载如果你下载了landing_points.geojson
const response = await fetch('./landing-point-geo.geojson');
// 方式2或者直接从API加载取消下面注释
// const response = await fetch('https://services.arcgis.com/6DIQcwlPy8knb6sg/arcgis/rest/services/SubmarineCables/FeatureServer/0/query?where=1%3D1&outFields=*&returnGeometry=true&f=geojson');