// 全局变量 let currentModule = 'data-reception'; let dataStreamInterval; let timeUpdateInterval; let processingTaskInterval; // DOM加载完成后初始化 document.addEventListener('DOMContentLoaded', function() { initializeApp(); }); // 应用初始化 function initializeApp() { setupNavigation(); setupTimeDisplay(); setupDataStream(); setupProcessingNodes(); setupVisualizationControls(); setupDataPoints(); console.log('海洋数据系统DEMO已启动'); } // 设置导航功能 function setupNavigation() { const navItems = document.querySelectorAll('.nav-item'); const modules = document.querySelectorAll('.module'); navItems.forEach(item => { item.addEventListener('click', function() { const targetModule = this.getAttribute('data-module'); // 更新导航状态 navItems.forEach(nav => nav.classList.remove('active')); this.classList.add('active'); // 切换模块显示 modules.forEach(module => module.classList.remove('active')); document.getElementById(targetModule).classList.add('active'); currentModule = targetModule; // 根据模块执行特定初始化 switch(targetModule) { case 'data-reception': startDataReception(); break; case 'data-processing': startProcessingMonitor(); break; case 'data-visualization': startVisualization(); break; } console.log(`切换到模块: ${targetModule}`); }); }); } // 设置时间显示 function setupTimeDisplay() { const timeDisplay = document.getElementById('currentTime'); function updateTime() { const now = new Date(); const timeString = now.toLocaleTimeString('zh-CN', { hour12: false, hour: '2-digit', minute: '2-digit', second: '2-digit' }); timeDisplay.textContent = timeString; } updateTime(); timeUpdateInterval = setInterval(updateTime, 1000); } // 设置数据流显示 // 数据流管理类 class DataStreamManager { constructor() { this.container = null; this.interval = null; this.maxItems = 12; this.updateFrequency = 500; // 1秒更新一次 this.isRunning = false; } init() { this.container = document.querySelector('.data-stream'); if (!this.container) { console.error('数据流容器未找到'); return false; } // 清空容器 this.container.innerHTML = ''; // 初始化几个数据项 this.generateInitialData(); // 开始定期更新 this.start(); console.log('数据流管理器初始化完成'); return true; } generateInitialData() { // 生成3-5个初始数据项 const initialCount = Math.floor(Math.random() * 3) + 3; for (let i = 0; i < initialCount; i++) { setTimeout(() => { this.addDataItem(); }, i * 300); } } addDataItem() { if (!this.container) return; // 随机选择设备 const device = oceanDevices[Math.floor(Math.random() * oceanDevices.length)]; const param = device.parameters[Math.floor(Math.random() * device.parameters.length)]; const value = this.generateValue(param); const timestamp = new Date().toLocaleTimeString('zh-CN', { hour12: false, hour: '2-digit', minute: '2-digit', second: '2-digit' }); // 创建数据项 const item = this.createDataItem(device, param, value, timestamp); // 添加到容器顶部 this.container.insertBefore(item, this.container.firstChild); // 限制数量 this.limitItems(); // 触发进入动画 requestAnimationFrame(() => { item.style.opacity = '1'; item.style.transform = 'translateX(0)'; }); } createDataItem(device, param, value, timestamp) { const item = document.createElement('div'); item.className = `stream-item ${this.getDeviceClass(device.type)}`; // 参数名称映射 const paramNames = { 'time': '时间', 'lng_lat': '经纬度', 'temp': '温度', 'cond': '电导率', 'depth': '深度', 'salinity': '盐度', 'flow_velocity': '流速', 'flow_direction': '流向', 'gravity': '重力', 'magnetic_field_1': '磁场强度1', 'magnetic_field_2': '磁场强度2', 'wave_height_max': '最大波高', 'wave_height_effective': '有效波高', 'cycle_max': '最大周期', 'wind_speed': '风速', 'wind_direction': '风向', 'air_pressure': '气压', 'humidity': '湿度', 'visibility': '能见度', 'cloud_height_1': '云高1', 'irradiance': '辐照度', 'turbidity': '浊度', 'chlorophyll': '叶绿素' }; item.innerHTML = `
${this.getDeviceIcon(device.type)}
${device.name}
${paramNames[param] || param}
${value}
${timestamp}
`; // 添加点击事件 item.addEventListener('click', () => { this.showItemDetails(device, param, value, timestamp); }); return item; } limitItems() { const items = this.container.querySelectorAll('.stream-item'); if (items.length > this.maxItems) { const excess = items.length - this.maxItems; for (let i = 0; i < excess; i++) { const lastItem = items[items.length - 1 - i]; lastItem.classList.add('removing'); setTimeout(() => { if (lastItem.parentNode) { lastItem.remove(); } }, 300); } } } generateValue(param) { const valueRanges = { 'temp': () => (Math.random() * 30 + 5).toFixed(1) + '°C', 'depth': () => (Math.random() * 6000 + 10).toFixed(0) + 'm', 'flow_velocity': () => (Math.random() * 2 + 0.1).toFixed(2) + 'm/s', 'wind_speed': () => (Math.random() * 20 + 2).toFixed(1) + 'm/s', 'air_pressure': () => (Math.random() * 50 + 1000).toFixed(1) + 'hPa', 'humidity': () => (Math.random() * 40 + 40).toFixed(0) + '%', 'wave_height_max': () => (Math.random() * 8 + 0.5).toFixed(1) + 'm', 'salinity': () => (Math.random() * 5 + 30).toFixed(2) + 'psu', 'gravity': () => (Math.random() * 0.1 + 9.8).toFixed(3) + 'm/s²', 'visibility': () => (Math.random() * 20 + 5).toFixed(1) + 'km', 'cloud_height_1': () => (Math.random() * 3000 + 500).toFixed(0) + 'm' }; return valueRanges[param] ? valueRanges[param]() : (Math.random() * 100).toFixed(2); } getDeviceClass(type) { const classMap = { 'CTD': 'device-ctd', 'ADCP_38K': 'device-adcp', 'ADCP_150K': 'device-adcp', 'GRAVITY': 'device-gravity', 'MAGNETIC': 'device-magnetic', 'WAVE_GAUGE': 'device-wave', 'DEPTH_SOUNDER': 'device-depth', 'WAVE_RADAR': 'device-radar', 'MULTI_SENSOR': 'device-multi', 'WAVE_BUOY': 'device-buoy', 'BEIDOU': 'device-beidou', 'WIND_PROFILER': 'device-wind', 'WEATHER_STATION': 'device-weather', 'SKY_SCANNER': 'device-sky', 'TURBULENCE': 'device-turbulence', 'LIDAR_WIND': 'device-lidar', 'VISIBILITY': 'device-visibility', 'CLOUD_HEIGHT': 'device-cloud', 'NAVIGATION': 'device-navigation' }; return classMap[type] || 'device-default'; } getDeviceIcon(type) { const iconMap = { 'CTD': '🌊', 'ADCP_38K': '🌀', 'ADCP_150K': '🌀', 'GRAVITY': '⚖️', 'MAGNETIC': '🧲', 'WAVE_GAUGE': '〰️', 'DEPTH_SOUNDER': '📏', 'WAVE_RADAR': '📡', 'MULTI_SENSOR': '🔬', 'WAVE_BUOY': '🛟', 'BEIDOU': '🛰️', 'WIND_PROFILER': '💨', 'WEATHER_STATION': '🌤️', 'SKY_SCANNER': '🌌', 'TURBULENCE': '🌪️', 'LIDAR_WIND': '🔦', 'VISIBILITY': '👁️', 'CLOUD_HEIGHT': '☁️', 'NAVIGATION': '🧭' }; return iconMap[type] || '📊'; } showItemDetails(device, param, value, timestamp) { // 创建模态框 const modal = document.createElement('div'); modal.className = 'stream-detail-modal'; modal.style.cssText = ` position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0, 0, 0, 0.8); display: flex; align-items: center; justify-content: center; z-index: 1000; backdrop-filter: blur(5px); `; const content = document.createElement('div'); content.style.cssText = ` background: linear-gradient(135deg, #1e3a8a 0%, #0f172a 100%); border-radius: 16px; padding: 24px; max-width: 400px; width: 90%; border: 1px solid rgba(255, 255, 255, 0.2); box-shadow: 0 20px 40px rgba(0, 0, 0, 0.5); `; content.innerHTML = `
${this.getDeviceIcon(device.type)}

${device.name}

设备ID: ${device.id}

参数 ${param}
数值 ${value}
时间 ${timestamp}
`; modal.appendChild(content); document.body.appendChild(modal); // 点击背景关闭 modal.addEventListener('click', (e) => { if (e.target === modal) modal.remove(); }); } start() { if (this.isRunning) return; this.isRunning = true; this.interval = setInterval(() => { this.addDataItem(); }, this.updateFrequency); console.log('数据流开始运行'); } stop() { if (this.interval) { clearInterval(this.interval); this.interval = null; } this.isRunning = false; console.log('数据流已停止'); } restart() { this.stop(); setTimeout(() => { this.start(); }, 100); } } // 创建全局数据流管理器实例 const dataStreamManager = new DataStreamManager(); // 重新定义setupDataStream函数 function setupDataStream() { return dataStreamManager.init(); } // 设置数据点交互 function setupDataPoints() { const dataPoints = document.querySelectorAll('.data-point'); const sourceDetails = document.getElementById('sourceDetails'); const sourceInfo = { '浮标-001': { location: '北纬 25.2°, 东经 120.5°', status: '在线', lastUpdate: '2分钟前', type: '海洋浮标' }, '卫星-A': { location: '轨道高度 705km', status: '在线', lastUpdate: '1分钟前', type: '海洋观测卫星' }, '监测站-B': { location: '北纬 22.8°, 东经 118.3°', status: '在线', lastUpdate: '30秒前', type: '海岸监测站' }, '船舶-C': { location: '北纬 24.1°, 东经 119.7°', status: '在线', lastUpdate: '5分钟前', type: '科考船' } }; dataPoints.forEach(point => { point.addEventListener('click', function() { const sourceName = this.getAttribute('data-source'); const info = sourceInfo[sourceName]; if (info && sourceDetails) { sourceDetails.innerHTML = `
数据源: ${sourceName}
类型: ${info.type}
位置: ${info.location}
状态: ${info.status}
最后更新: ${info.lastUpdate}
`; } // 高亮效果 dataPoints.forEach(p => p.classList.remove('highlighted')); this.classList.add('highlighted'); }); }); } // 设置处理节点交互 function setupProcessingNodes() { const nodes = document.querySelectorAll('.pipeline-node'); const nodeTitle = document.getElementById('nodeTitle'); const nodeDetails = document.getElementById('nodeDetails'); const nodeInfo = { 'input': { title: '原始数据输入', details: `

功能: 接收来自各种数据源的原始海洋数据

数据类型: 温度、盐度、叶绿素、波高等

当前状态: 正常运行

处理速率: 15.2 MB/s

` }, 'clean': { title: '数据清洗', details: `

功能: 去除异常值、填补缺失数据

质量指标:

算法: 3σ准则 + 卡尔曼滤波

` }, 'format': { title: '数据格式化', details: `

功能: 统一数据格式和坐标系统

输出格式: NetCDF-4

坐标系: WGS84

时间标准: UTC

处理延迟: < 5秒

` }, 'interpolation': { title: '三维插值', details: `

功能: 生成规则网格的三维温度场

插值方法: 克里金插值

质量评估:

网格分辨率: 0.1° × 0.1°

` }, 'analysis': { title: '叶绿素反演', details: `

功能: 基于多光谱数据反演叶绿素浓度

算法: OC4v6 + 神经网络

波段: 443, 490, 510, 555 nm

精度: ±0.3 mg/m³

覆盖范围: 全球海域

` }, 'output': { title: '产品数据库', details: `

功能: 存储最终处理产品

存储格式: HDF5 + 元数据

数据产品:

更新频率: 每小时

` } }; nodes.forEach(node => { node.addEventListener('click', function() { const step = this.getAttribute('data-step'); const info = nodeInfo[step]; if (info && nodeTitle && nodeDetails) { nodeTitle.textContent = info.title; nodeDetails.innerHTML = info.details; } // 高亮效果 nodes.forEach(n => n.classList.remove('highlighted')); this.classList.add('highlighted'); }); }); } // 设置可视化控件 function setupVisualizationControls() { const playButton = document.querySelector('#data-visualization .btn-primary'); const dataSelect = document.querySelector('#data-visualization .control-select'); if (playButton) { playButton.addEventListener('click', function() { const icon = this.querySelector('i'); const text = this.querySelector('span') || this.childNodes[this.childNodes.length - 1]; if (icon.classList.contains('fa-play')) { icon.classList.remove('fa-play'); icon.classList.add('fa-pause'); if (text) text.textContent = ' 暂停动画'; startTemperatureAnimation(); } else { icon.classList.remove('fa-pause'); icon.classList.add('fa-play'); if (text) text.textContent = ' 播放动画'; stopTemperatureAnimation(); } }); } if (dataSelect) { dataSelect.addEventListener('change', function() { updateVisualizationData(this.value); }); } } // 启动数据接收 function startDataReception() { console.log('数据接收模块已激活'); // 重新启动数据流(如果已停止) if (!dataStreamInterval) { setupDataStream(); } } // 启动处理监控 function startProcessingMonitor() { console.log('数据处理模块已激活'); startProcessingTask(); } // 启动可视化 function startVisualization() { console.log('数据可视化模块已激活'); updateMetrics(); } // 启动处理任务演示 function startProcessingTask() { const nodes = document.querySelectorAll('.pipeline-node'); const logContent = document.getElementById('logContent'); function addLogEntry(message, type = 'info') { if (!logContent) return; const now = new Date(); const timeString = now.toLocaleTimeString('zh-CN', { hour12: false, hour: '2-digit', minute: '2-digit', second: '2-digit' }); const logEntry = document.createElement('div'); logEntry.className = `log-entry ${type}`; logEntry.textContent = `[${timeString}] ${message}`; logContent.insertBefore(logEntry, logContent.firstChild); // 限制日志条数 while (logContent.children.length > 20) { logContent.removeChild(logContent.lastChild); } } function simulateProcessing() { const messages = [ { msg: '数据块 #A7B2 处理完成', type: 'info' }, { msg: '三维插值算法执行成功', type: 'info' }, { msg: '叶绿素反演计算完成', type: 'info' }, { msg: '警告: 数据源 浮标-003 信号弱', type: 'warning' }, { msg: '产品数据已存储到数据库', type: 'info' }, { msg: '质量检验通过率: 98.5%', type: 'info' } ]; const randomMessage = messages[Math.floor(Math.random() * messages.length)]; addLogEntry(randomMessage.msg, randomMessage.type); } // 定期添加日志 processingTaskInterval = setInterval(simulateProcessing, 5000); // 初始日志 setTimeout(() => addLogEntry('南海温度场与叶绿素产品生成任务启动'), 1000); } // 温度动画控制 let temperatureAnimationInterval; function startTemperatureAnimation() { const temperatureZones = document.querySelectorAll('.temperature-zone'); temperatureAnimationInterval = setInterval(() => { temperatureZones.forEach(zone => { // 随机改变温度区域的大小和位置 const newTop = Math.random() * 70 + 10; const newLeft = Math.random() * 70 + 10; const newWidth = Math.random() * 20 + 15; const newHeight = Math.random() * 15 + 10; zone.style.top = newTop + '%'; zone.style.left = newLeft + '%'; zone.style.width = newWidth + '%'; zone.style.height = newHeight + '%'; }); }, 2000); } function stopTemperatureAnimation() { if (temperatureAnimationInterval) { clearInterval(temperatureAnimationInterval); temperatureAnimationInterval = null; } } // 更新可视化数据 function updateVisualizationData(dataType) { const metrics = document.querySelectorAll('.metric-value'); const dataValues = { '温度数据': ['24.5°C', '2.8m', '3'], '盐度数据': ['34.2‰', '1.9m', '2'], '叶绿素数据': ['0.8mg/m³', '2.1m', '4'] }; const values = dataValues[dataType] || dataValues['温度数据']; metrics.forEach((metric, index) => { if (values[index]) { metric.textContent = values[index]; } }); console.log(`切换到${dataType}显示`); } // 更新指标数据 function updateMetrics() { const statValues = document.querySelectorAll('.stat-value'); // 模拟数据更新 setInterval(() => { statValues.forEach(stat => { const currentText = stat.textContent; if (currentText.includes('MB/s')) { const newValue = (Math.random() * 10 + 10).toFixed(1); stat.textContent = newValue + ' MB/s'; } else if (currentText.includes('°C')) { const newValue = (Math.random() * 5 + 22).toFixed(1); stat.textContent = newValue + '°C'; } else if (!isNaN(parseInt(currentText))) { const baseValue = parseInt(currentText); const variation = Math.floor(Math.random() * 10 - 5); stat.textContent = Math.max(0, baseValue + variation); } }); }, 10000); } // 添加CSS类用于高亮效果 const style = document.createElement('style'); style.textContent = ` .data-point.highlighted { transform: scale(1.5); box-shadow: 0 0 20px var(--accent-green); z-index: 20; } .pipeline-node.highlighted { transform: translateY(-5px) scale(1.05); box-shadow: 0 15px 40px rgba(0, 212, 255, 0.4); border-color: var(--accent-blue); } .node-info { line-height: 1.6; } .node-info ul { margin: 0.5rem 0; padding-left: 1rem; } .node-info li { margin: 0.25rem 0; color: var(--text-secondary); } .node-info strong { color: var(--accent-blue); } `; document.head.appendChild(style); // 清理函数 function cleanup() { if (dataStreamInterval) clearInterval(dataStreamInterval); if (timeUpdateInterval) clearInterval(timeUpdateInterval); if (processingTaskInterval) clearInterval(processingTaskInterval); if (temperatureAnimationInterval) clearInterval(temperatureAnimationInterval); } // 页面卸载时清理 window.addEventListener('beforeunload', cleanup); // 导出主要函数供调试使用 window.OceanDemo = { switchModule: function(moduleName) { const navItem = document.querySelector(`[data-module="${moduleName}"]`); if (navItem) navItem.click(); }, startProcessingDemo: startProcessingTask, updateVisualization: updateVisualizationData, cleanup: cleanup }; console.log('海洋数据系统DEMO脚本加载完成'); // 真实海洋设备数据配置 const oceanDevices = [ { id: 'ctd_001', name: 'CTD温盐深仪', type: 'CTD', position: { lat: 35.2, lng: 120.5 }, parameters: ['time', 'lng_lat', 'temp', 'cond', 'depth'], status: 'online', color: '#00ff88' }, { id: 'adcp_38k_001', name: '船载ADCP系统(38k)', type: 'ADCP_38K', position: { lat: 36.1, lng: 121.2 }, parameters: ['data_time', 'lng_lat', 'depth', 'flow_velocity', 'flow_direction', 'flow_velocity_e', 'flow_velocity_n', 'flow_velocity_v'], status: 'online', color: '#0088ff' }, { id: 'adcp_150k_001', name: '船载ADCP系统(150k)', type: 'ADCP_150K', position: { lat: 34.8, lng: 119.8 }, parameters: ['data_time', 'lng_lat', 'depth', 'flow_velocity', 'flow_direction', 'flow_velocity_e', 'flow_velocity_n', 'flow_velocity_v'], status: 'online', color: '#0066ff' }, { id: 'gravity_001', name: '重力仪', type: 'GRAVITY', position: { lat: 35.5, lng: 120.8 }, parameters: ['data_time', 'lng_lat', 'gravity'], status: 'online', color: '#ff6600' }, { id: 'magnetic_001', name: '磁力仪', type: 'MAGNETIC', position: { lat: 36.3, lng: 121.5 }, parameters: ['data_time', 'lng_lat', 'magnetic_field_1', 'magnetic_field_2'], status: 'online', color: '#ff0066' }, { id: 'wave_gauge_001', name: '波潮仪', type: 'WAVE_GAUGE', position: { lat: 35.8, lng: 120.2 }, parameters: ['data_time', 'lng_lat', 'wave_height_max', 'cycle_max', 'wave_height_1_10', 'cycle_1_10', 'wave_height_effective', 'cycle_effective', 'wave_height_average', 'cycle_average'], status: 'online', color: '#00ffff' }, { id: 'depth_sounder_001', name: '6000米测深仪', type: 'DEPTH_SOUNDER', position: { lat: 34.5, lng: 119.5 }, parameters: ['data_time', 'lng_lat', 'depth_f', 'depth_m'], status: 'online', color: '#8800ff' }, { id: 'wave_radar_001', name: '测波雷达', type: 'WAVE_RADAR', position: { lat: 36.5, lng: 122.1 }, parameters: ['data_time', 'lng_lat', 'wave_height_effective', 'cycle_tm2', 'direction_peak', 'cycle_peak', 'wavelength_peak', 'direction_swell_primary', 'cycle_swell_primary', 'wavelength_swell_primary', 'direction_wind_wave', 'cycle_wind_wave', 'wavelength_wind_wave', 'current_direction', 'current_velocity', 'encounter_current_direction', 'encounter_current_velocity'], status: 'online', color: '#ffff00' }, { id: 'multi_sensor_001', name: '表层多要素自动测定系统', type: 'MULTI_SENSOR', position: { lat: 35.1, lng: 119.9 }, parameters: ['data_time', 'lng_lat', 'temp', 'cond', 'salinity', 'chlorophyll', 'cdom', 'turbidity'], status: 'online', color: '#00ff00' }, { id: 'wave_buoy_001', name: '小型波浪浮标', type: 'WAVE_BUOY', position: { lat: 34.9, lng: 120.7 }, parameters: ['time', 'lng_lat', 'wave_height_average', 'cycle_average', 'wave_height_max', 'cycle_max', 'wave_height_1_10', 'cycle_1_10', 'wave_height_effective'], status: 'online', color: '#ff8800' }, { id: 'beidou_001', name: '北斗探测系统', type: 'BEIDOU', position: { lat: 36.0, lng: 121.8 }, parameters: ['data_time', 'lng_lat', 'temp', 'relative_humidity', 'air_pressure', 'wind_speed', 'height', 'wind_direction'], status: 'online', color: '#ff4400' }, { id: 'wind_profiler_001', name: '舰载风廓线雷达', type: 'WIND_PROFILER', position: { lat: 35.7, lng: 121.0 }, parameters: ['time', 'lng_lat', 'height', 'wind_direction', 'wind_speed', 'wind_speed_v', 'reliability_l', 'reliability_v', 'cn2'], status: 'online', color: '#4400ff' }, { id: 'weather_station_001', name: '船载自动气象站', type: 'WEATHER_STATION', position: { lat: 34.7, lng: 119.3 }, parameters: ['data_time', 'lng_lat', 'wind_speed_realtime', 'wind_direction_realtime', 'wind_speed_average', 'wind_direction_average', 'wind_speed_max', 'wind_direction_max', 'time_wind_speed_max', 'temperature', 'humidity', 'pressure', 'rainfall_minute', 'rainfall_hour', 'rainfall_day', 'status_sensor'], status: 'online', color: '#88ff00' }, { id: 'sky_scanner_001', name: '全天空背景扫描仪', type: 'SKY_SCANNER', position: { lat: 36.2, lng: 120.3 }, parameters: ['data_time', 'lng_lat', 'irradiance', 'radiance'], status: 'online', color: '#ff0088' }, { id: 'turbulence_001', name: '大气湍流谱仪', type: 'TURBULENCE', position: { lat: 35.4, lng: 121.7 }, parameters: ['data_time', 'lng_lat', 'temp', 'air_pressure', 'cn2'], status: 'online', color: '#0044ff' }, { id: 'lidar_wind_001', name: '激光测风雷达', type: 'LIDAR_WIND', position: { lat: 34.6, lng: 120.1 }, parameters: ['data_time', 'lng_lat', 'height', 'wind_speed_l', 'wind_direction_l', 'wind_speed_v'], status: 'online', color: '#ff6600' }, { id: 'visibility_001', name: '天气现象及能见度仪', type: 'VISIBILITY', position: { lat: 35.9, lng: 119.6 }, parameters: ['data_time', 'lng_lat', 'height', 'visibility', 'azimuth_angle', 'elevation'], status: 'online', color: '#66ff00' }, { id: 'cloud_height_001', name: '激光云高仪', type: 'CLOUD_HEIGHT', position: { lat: 36.4, lng: 120.9 }, parameters: ['data_time', 'lng_lat', 'cloud_height_1', 'cloud_height_2', 'cloud_height_3', 'cloud_height_4', 'cloud_height_5', 'cloud_thickness_1', 'cloud_thickness_2', 'cloud_thickness_3', 'cloud_thickness_4', 'cloud_thickness_5'], status: 'online', color: '#00aaff' }, { id: 'navigation_001', name: '船舶航行数据', type: 'NAVIGATION', position: { lat: 35.3, lng: 121.3 }, parameters: ['POS惯导', 'GNSS', '计程仪', '电罗经', '测深仪'], status: 'online', color: '#aa00ff' } ]; function initDataReception() { const earthContainer = document.getElementById('earth-container'); if (!earthContainer) return; // 清空容器 earthContainer.innerHTML = ''; // 创建地球 const earth = document.createElement('div'); earth.className = 'earth'; earthContainer.appendChild(earth); // 添加真实设备数据源点 oceanDevices.forEach((device, index) => { const point = document.createElement('div'); point.className = 'data-source-point'; point.style.backgroundColor = device.color; point.style.boxShadow = `0 0 20px ${device.color}`; // 根据经纬度计算位置(简化的球面投影) const x = 50 + (device.position.lng - 120) * 8; // 相对于120度经线 const y = 50 - (device.position.lat - 35) * 8; // 相对于35度纬线 point.style.left = `${Math.max(10, Math.min(90, x))}%`; point.style.top = `${Math.max(10, Math.min(90, y))}%`; point.setAttribute('data-device-id', device.id); point.setAttribute('data-device-name', device.name); point.setAttribute('data-device-type', device.type); // 添加点击事件 point.addEventListener('click', () => showDeviceDetails(device)); earthContainer.appendChild(point); // 创建数据流线 const streamLine = document.createElement('div'); streamLine.className = 'data-stream-line'; streamLine.style.background = `linear-gradient(45deg, transparent, ${device.color}, transparent)`; streamLine.style.left = point.style.left; streamLine.style.top = point.style.top; streamLine.style.animationDelay = `${index * 0.2}s`; earthContainer.appendChild(streamLine); }); // 更新统计信息 updateReceptionStats(); } function updateReceptionStats() { const totalDevices = oceanDevices.length; const onlineDevices = oceanDevices.filter(d => d.status === 'online').length; const totalParams = oceanDevices.reduce((sum, device) => sum + device.parameters.length, 0); // 更新统计显示 const statsElements = { 'total-received': `${(Math.random() * 50000 + 150000).toFixed(0)}`, 'reception-rate': `${(Math.random() * 200 + 800).toFixed(0)} 包/秒`, 'online-sources': `${onlineDevices}/${totalDevices}`, 'data-types': `${totalParams} 种参数` }; Object.entries(statsElements).forEach(([id, value]) => { const element = document.getElementById(id); if (element) element.textContent = value; }); } // 底部数据流面板管理 class BottomDataStreamManager { constructor() { this.isCollapsed = false; this.dataStreamManager = new DataStreamManager(); this.init(); } init() { this.setupEventListeners(); this.startDataStream(); } setupEventListeners() { const streamHeader = document.querySelector('.stream-header'); const streamToggle = document.querySelector('.stream-toggle'); if (streamHeader) { streamHeader.addEventListener('click', () => this.togglePanel()); } if (streamToggle) { streamToggle.addEventListener('click', (e) => { e.stopPropagation(); this.togglePanel(); }); } } togglePanel() { const panel = document.querySelector('.bottom-data-stream'); if (panel) { this.isCollapsed = !this.isCollapsed; panel.classList.toggle('collapsed', this.isCollapsed); // 更新按钮文本 const toggleText = document.querySelector('.stream-toggle span'); if (toggleText) { toggleText.textContent = this.isCollapsed ? '展开' : '收起'; } } } startDataStream() { // 启动数据流管理器 this.dataStreamManager.start(); // 每1秒添加新数据 - 加快生成速度 setInterval(() => { this.addNewDataItem(); }, 1000); } addNewDataItem() { const container = document.querySelector('.data-stream.horizontal'); if (!container) return; const newItem = this.dataStreamManager.generateDataItem(); const itemElement = this.createStreamItemElement(newItem); // 为新卡片添加特殊类名 itemElement.classList.add('new-item'); // 为现有卡片添加推动效果 const existingItems = container.querySelectorAll('.stream-item'); existingItems.forEach((item, index) => { if (index < 5) { // 对前5个卡片添加推动效果 // 先移除可能存在的重置类 item.classList.remove('reset-position'); // 添加推动效果 setTimeout(() => { item.classList.add('push-right'); }, 50); // 小延迟确保DOM更新 } }); // 添加到容器开头 container.insertBefore(itemElement, container.firstChild); // 延迟重置卡片位置,创建流动效果 setTimeout(() => { const allItems = container.querySelectorAll('.stream-item'); allItems.forEach((item, index) => { if (item.classList.contains('push-right')) { item.classList.remove('push-right'); item.classList.add('reset-position'); } }); }, 100); // 限制显示的数据项数量,为超出的卡片添加淡出动画 setTimeout(() => { const items = container.querySelectorAll('.stream-item'); if (items.length > 20) { const itemToRemove = items[items.length - 1]; itemToRemove.classList.add('fade-out'); // 动画完成后移除元素 setTimeout(() => { if (itemToRemove.parentNode) { itemToRemove.remove(); } }, 500); } }, 200); // 清理类名 setTimeout(() => { itemElement.classList.remove('new-item'); const allItems = container.querySelectorAll('.stream-item'); allItems.forEach(item => { item.classList.remove('reset-position'); }); }, 800); // 自动滚动到最新数据 container.scrollLeft = 0; } createStreamItemElement(data) { const item = document.createElement('div'); item.className = 'stream-item'; // 设备类型颜色 const deviceColors = { 'temperature': { bg: 'rgba(239, 68, 68, 0.2)', border: '#ef4444', icon: '🌡️' }, 'pressure': { bg: 'rgba(59, 130, 246, 0.2)', border: '#3b82f6', icon: '📊' }, 'salinity': { bg: 'rgba(34, 197, 94, 0.2)', border: '#22c55e', icon: '🧂' }, 'ph': { bg: 'rgba(168, 85, 247, 0.2)', border: '#a855f7', icon: '⚗️' }, 'oxygen': { bg: 'rgba(6, 182, 212, 0.2)', border: '#06b6d4', icon: '💨' }, 'turbidity': { bg: 'rgba(245, 158, 11, 0.2)', border: '#f59e0b', icon: '🌊' } }; const colorInfo = deviceColors[data.type] || deviceColors['temperature']; item.innerHTML = `
${colorInfo.icon}
${data.deviceName}
${data.parameter}
${data.value}
${data.timestamp}
`; return item; } } // 初始化底部数据流面板 document.addEventListener('DOMContentLoaded', () => { // 检查是否存在底部数据流面板 if (document.querySelector('.bottom-data-stream')) { new BottomDataStreamManager(); } }); function showDeviceDetails(device) { // 创建设备详情弹窗 const modal = document.createElement('div'); modal.className = 'device-modal'; modal.innerHTML = `

${device.name}

×

设备ID: ${device.id}

设备类型: ${device.type}

位置: ${device.position.lat}°N, ${device.position.lng}°E

状态: ${device.status === 'online' ? '在线' : '离线'}

测量参数:

${device.parameters.map(param => `${param}`).join('')}

实时数据:

数据包数: ${Math.floor(Math.random() * 1000 + 500)}
传输速率: ${Math.floor(Math.random() * 50 + 20)} KB/s
数据质量: ${(Math.random() * 10 + 90).toFixed(1)}%
`; document.body.appendChild(modal); // 关闭弹窗事件 const closeBtn = modal.querySelector('.device-modal-close'); closeBtn.addEventListener('click', () => { document.body.removeChild(modal); }); modal.addEventListener('click', (e) => { if (e.target === modal) { document.body.removeChild(modal); } }); } // 更新数据流显示函数 function updateDataStream() { const streamContainer = document.querySelector('.data-stream'); if (!streamContainer) return; // 随机选择一个设备 const device = oceanDevices[Math.floor(Math.random() * oceanDevices.length)]; const param = device.parameters[Math.floor(Math.random() * device.parameters.length)]; const value = generateRandomValue(param); const now = new Date(); const timestamp = now.toLocaleTimeString('zh-CN', { hour12: false, hour: '2-digit', minute: '2-digit', second: '2-digit' }); // 获取参数的中文名称 const paramNames = { 'time': '时间', 'lng_lat': '经纬度', 'temp': '温度', 'cond': '电导率', 'depth': '深度', 'salinity': '盐度', 'flow_velocity': '流速', 'flow_direction': '流向', 'gravity': '重力', 'magnetic_field_1': '磁场强度1', 'magnetic_field_2': '磁场强度2', 'wave_height_max': '最大波高', 'wave_height_effective': '有效波高', 'cycle_max': '最大周期', 'wind_speed': '风速', 'wind_direction': '风向', 'air_pressure': '气压', 'humidity': '湿度', 'visibility': '能见度', 'cloud_height_1': '云高1', 'irradiance': '辐照度', 'turbidity': '浊度', 'chlorophyll': '叶绿素' }; // 创建数据流项目 const streamItem = document.createElement('div'); streamItem.className = `stream-item ${getDeviceTypeClass(device.type)}`; streamItem.innerHTML = `
${deviceTypeIcons[device.type] || '📊'}
${device.name}
${paramNames[param] || param}
${value}
${timestamp}
`; // 添加点击事件 streamItem.addEventListener('click', () => { showStreamItemDetails(device, param, value, timestamp); }); // 添加到流中 streamContainer.insertBefore(streamItem, streamContainer.firstChild); // 限制显示的项目数量 const items = streamContainer.querySelectorAll('.stream-item'); if (items.length > 12) { const lastItem = items[items.length - 1]; lastItem.classList.add('removing'); setTimeout(() => { if (lastItem.parentNode) { lastItem.remove(); } }, 300); } } // 显示数据流项目详细信息 function showStreamItemDetails(device, param, value, timestamp) { const modal = document.createElement('div'); modal.className = 'stream-detail-modal'; modal.style.cssText = ` position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0, 0, 0, 0.8); display: flex; align-items: center; justify-content: center; z-index: 1000; backdrop-filter: blur(5px); `; const content = document.createElement('div'); content.style.cssText = ` background: linear-gradient(135deg, #1e3a8a 0%, #0f172a 100%); border-radius: 16px; padding: 24px; max-width: 400px; width: 90%; border: 1px solid rgba(255, 255, 255, 0.2); box-shadow: 0 20px 40px rgba(0, 0, 0, 0.5); `; content.innerHTML = `
${deviceTypeIcons[device.type] || '📊'}

${device.name}

设备ID: ${device.id}

参数 ${param}
数值 ${value}
时间 ${timestamp}
位置 ${device.position.lat.toFixed(2)}°N, ${device.position.lng.toFixed(2)}°E
`; modal.appendChild(content); document.body.appendChild(modal); // 点击背景关闭 modal.addEventListener('click', (e) => { if (e.target === modal) { modal.remove(); } }); } // 更新数据源点渲染函数 function renderDataSourcePoints() { const earthSphere = document.querySelector('.earth-sphere'); if (!earthSphere) return; // 清除现有的数据点 earthSphere.querySelectorAll('.data-source-point').forEach(point => point.remove()); earthSphere.querySelectorAll('.data-stream-line').forEach(line => line.remove()); oceanDevices.forEach((device, index) => { // 创建数据源点 const point = document.createElement('div'); point.className = 'data-source-point'; point.style.backgroundColor = deviceTypeColors[device.type] || '#00d4ff'; point.style.color = deviceTypeColors[device.type] || '#00d4ff'; // 根据经纬度计算位置(简化的球面投影) const x = ((device.longitude + 180) / 360) * 100; const y = ((90 - device.latitude) / 180) * 100; point.style.left = `${x}%`; point.style.top = `${y}%`; // 添加点击事件 point.addEventListener('click', () => showDeviceDetails(device)); // 添加悬停提示 point.title = `${device.name} (${device.type})`; earthSphere.appendChild(point); // 创建数据流线 if (Math.random() < 0.3) { // 30%的概率显示数据流 const streamLine = document.createElement('div'); streamLine.className = 'data-stream-line'; streamLine.style.background = `linear-gradient(to top, ${deviceTypeColors[device.type] || '#00d4ff'}, transparent)`; streamLine.style.left = `${x}%`; streamLine.style.top = `${y}%`; streamLine.style.animationDelay = `${Math.random() * 3}s`; earthSphere.appendChild(streamLine); } }); } // 更新设备详情显示函数 function showDeviceDetails(device) { // 创建模态框 const modal = document.createElement('div'); modal.className = 'device-modal'; modal.innerHTML = `

${deviceTypeIcons[device.type] || '📊'} ${device.name}

×

设备类型: ${device.type}

位置: ${device.latitude.toFixed(4)}°N, ${device.longitude.toFixed(4)}°E

状态: ${device.status === 'online' ? '在线' : '离线'}

设备ID: ${device.id}

监测参数

${device.parameters.map(param => `${param}`).join('')}

实时数据

${device.parameters.slice(0, 6).map(param => `
${param} ${generateRandomValue(param)}
`).join('')}
`; // 添加关闭事件 modal.querySelector('.device-modal-close').addEventListener('click', () => { modal.remove(); }); modal.addEventListener('click', (e) => { if (e.target === modal) { modal.remove(); } }); document.body.appendChild(modal); } function generateRandomValue(param) { const valueMap = { 'temp': `${(Math.random() * 30 + 5).toFixed(2)}°C`, 'temperature': `${(Math.random() * 30 + 5).toFixed(2)}°C`, 'depth': `${(Math.random() * 6000).toFixed(1)}m`, 'wind_speed': `${(Math.random() * 20 + 2).toFixed(1)}m/s`, 'wave_height_max': `${(Math.random() * 8 + 1).toFixed(2)}m`, 'pressure': `${(Math.random() * 50 + 1000).toFixed(1)}hPa`, 'humidity': `${(Math.random() * 40 + 40).toFixed(1)}%`, 'salinity': `${(Math.random() * 5 + 30).toFixed(2)}‰`, 'flow_velocity': `${(Math.random() * 3).toFixed(2)}m/s`, 'gravity': `${(Math.random() * 0.1 + 9.8).toFixed(4)}m/s²`, 'visibility': `${(Math.random() * 20 + 5).toFixed(1)}km`, 'irradiance': `${(Math.random() * 1000 + 200).toFixed(1)}W/m²` }; // 检查参数名是否包含特定关键词 for (const [key, value] of Object.entries(valueMap)) { if (param.includes(key)) { return value; } } // 默认返回数值 return `${(Math.random() * 100).toFixed(2)}`; } // 数据处理模块 function initDataProcessing() { const pipelineContainer = document.querySelector('.pipeline-container'); if (!pipelineContainer) return; // 清空现有内容 pipelineContainer.innerHTML = ''; // 创建处理流水线 const pipelineFlow = document.createElement('div'); pipelineFlow.className = 'pipeline-flow'; // 定义处理节点(基于真实海洋数据处理流程) const processingNodes = [ { id: 'data-ingestion', type: 'input-node', icon: 'fas fa-download', title: '数据接收', description: '接收来自19种海洋观测设备的原始数据' }, { id: 'data-validation', type: 'process-node', icon: 'fas fa-check-circle', title: '数据验证', description: '验证CTD、ADCP、重力仪等设备数据的完整性和准确性' }, { id: 'quality-control', type: 'process-node', icon: 'fas fa-filter', title: '质量控制', description: '对温度、盐度、流速等参数进行质量检查和异常值过滤' }, { id: 'data-calibration', type: 'compute-node', icon: 'fas fa-cogs', title: '数据校准', description: '校准传感器漂移,统一时间基准和坐标系统' }, { id: 'oceanographic-analysis', type: 'compute-node', icon: 'fas fa-chart-line', title: '海洋学分析', description: '计算海水密度、声速、地转流等海洋学参数' }, { id: 'data-fusion', type: 'compute-node', icon: 'fas fa-layer-group', title: '数据融合', description: '融合多源观测数据,生成综合海洋环境场' }, { id: 'data-output', type: 'output-node', icon: 'fas fa-upload', title: '数据输出', description: '输出标准格式的海洋数据产品和可视化结果' } ]; // 创建节点元素 processingNodes.forEach((node, index) => { const nodeElement = document.createElement('div'); nodeElement.className = `pipeline-node ${node.type}`; nodeElement.id = node.id; nodeElement.innerHTML = ` ${node.title}
`; // 添加点击事件 nodeElement.addEventListener('click', () => showNodeDetails(node)); pipelineFlow.appendChild(nodeElement); // 添加连接线(除了最后一个节点) if (index < processingNodes.length - 1) { const connection = document.createElement('div'); connection.className = 'pipeline-connection'; connection.style.left = `${(index + 1) * 150 - 75}px`; connection.style.width = '150px'; pipelineFlow.appendChild(connection); } }); pipelineContainer.appendChild(pipelineFlow); // 更新处理统计 updateProcessingStats(); // 启动处理监控 startProcessingMonitor(); } // 显示节点详情 function showNodeDetails(node) { const detailsPanel = document.querySelector('.node-details'); if (!detailsPanel) return; // 根据节点类型显示不同的详细信息 let detailsContent = ''; switch(node.id) { case 'data-ingestion': detailsContent = `

数据接收节点

${node.description}

处理速度: 1000条/秒

数据格式: NetCDF, ASCII, Binary

`; break; case 'data-validation': detailsContent = `

数据验证节点

${node.description}

验证规则: 127项

通过率: 98.5%

`; break; case 'quality-control': detailsContent = `

质量控制节点

${node.description}

QC标准: IOC/UNESCO

处理算法: 3σ准则, Hampel滤波

`; break; case 'data-calibration': detailsContent = `

数据校准节点

${node.description}

校准精度: ±0.001°C (温度)

时间精度: ±1ms

`; break; case 'oceanographic-analysis': detailsContent = `

海洋学分析节点

${node.description}

计算模型: TEOS-10

分析算法: 动力高度法, 梯度检测

`; break; case 'data-fusion': detailsContent = `

数据融合节点

${node.description}

融合方法: 变分同化

网格分辨率: 0.1° × 0.1°

`; break; case 'data-output': detailsContent = `

数据输出节点

${node.description}

输出格式: NetCDF-4, HDF5, GeoTIFF

产品类型: L1, L2, L3级数据产品

`; break; default: detailsContent = `

${node.title}

${node.description}

`; } detailsPanel.innerHTML = detailsContent; } // 更新处理统计 function updateProcessingStats() { const stats = [ { label: '处理任务', value: Math.floor(Math.random() * 50) + 150, id: 'processing-tasks' }, { label: '完成任务', value: Math.floor(Math.random() * 30) + 120, id: 'completed-tasks' }, { label: '等待队列', value: Math.floor(Math.random() * 20) + 10, id: 'pending-tasks' }, { label: '错误任务', value: Math.floor(Math.random() * 5) + 2, id: 'error-tasks', error: true } ]; stats.forEach(stat => { const element = document.getElementById(stat.id); if (element) { const numberElement = element.querySelector('.stat-number'); if (numberElement) { numberElement.textContent = stat.value; if (stat.error) { element.classList.add('error'); } } } }); // 更新资源监控 updateResourceMonitor(); } // 更新资源监控 function updateResourceMonitor() { const resources = [ { id: 'cpu-usage', value: Math.floor(Math.random() * 30) + 45 }, { id: 'memory-usage', value: Math.floor(Math.random() * 20) + 60 }, { id: 'storage-usage', value: Math.floor(Math.random() * 15) + 35 } ]; resources.forEach(resource => { const element = document.getElementById(resource.id); if (element) { const progressRing = element.querySelector('.progress-ring'); const progressValue = element.querySelector('.progress-value'); if (progressRing && progressValue) { // 更新环形进度条 const percentage = resource.value; progressRing.style.background = `conic-gradient(var(--accent-blue) ${percentage * 3.6}deg, rgba(255, 255, 255, 0.1) ${percentage * 3.6}deg)`; progressValue.textContent = `${percentage}%`; } } }); } // 启动处理监控 function startProcessingMonitor() { // 模拟处理日志 const logContent = document.querySelector('.log-content'); if (!logContent) return; const logMessages = [ { type: 'info', message: '[INFO] 开始处理CTD数据批次 #2024-001' }, { type: 'info', message: '[INFO] 数据验证完成,通过率: 98.7%' }, { type: 'warning', message: '[WARN] ADCP设备 #03 数据延迟 5分钟' }, { type: 'info', message: '[INFO] 质量控制完成,标记异常值 12个' }, { type: 'info', message: '[INFO] 海洋学参数计算完成' }, { type: 'error', message: '[ERROR] 重力仪数据校准失败,重试中...' }, { type: 'info', message: '[INFO] 数据融合完成,生成L3级产品' }, { type: 'info', message: '[INFO] 输出NetCDF文件: ocean_data_20240115.nc' } ]; let logIndex = 0; const addLogEntry = () => { if (logIndex < logMessages.length) { const log = logMessages[logIndex]; const logEntry = document.createElement('div'); logEntry.className = `log-entry ${log.type}`; logEntry.textContent = `${new Date().toLocaleTimeString()} ${log.message}`; logContent.appendChild(logEntry); logContent.scrollTop = logContent.scrollHeight; logIndex++; setTimeout(addLogEntry, Math.random() * 3000 + 2000); } else { // 重置日志 setTimeout(() => { logContent.innerHTML = ''; logIndex = 0; addLogEntry(); }, 5000); } }; addLogEntry(); } // 设备类型颜色映射 const deviceTypeColors = { 'CTD': '#00ff88', 'ADCP': '#0088ff', 'Gravity': '#ff6600', 'Magnetic': '#ff0066', 'Wave': '#00ffff', 'Depth': '#8800ff', 'Radar': '#ffff00', 'Multi': '#00ff00', 'Buoy': '#ff8800', 'Beidou': '#ff4400', 'Wind': '#4400ff', 'Weather': '#88ff00', 'Sky': '#ff0088', 'Turbulence': '#0044ff', 'Lidar': '#ff6600', 'Visibility': '#66ff00', 'Cloud': '#00aaff', 'Navigation': '#aa00ff' }; // 设备类型图标映射 const deviceTypeIcons = { 'CTD': '🌊', 'ADCP': '🌀', 'Gravity': '⚖️', 'Magnetic': '🧲', 'Wave': '〰️', 'Depth': '📏', 'Radar': '📡', 'Multi': '🔬', 'Buoy': '🛟', 'Beidou': '🛰️', 'Wind': '💨', 'Weather': '🌤️', 'Sky': '🌌', 'Turbulence': '🌪️', 'Lidar': '🔦', 'Visibility': '👁️', 'Cloud': '☁️', 'Navigation': '🧭' }; // 获取设备类型的CSS类名 function getDeviceTypeClass(deviceType) { const typeMap = { 'CTD': 'ctd', 'ADCP': 'adcp', 'Gravity': 'gravity', 'Magnetic': 'magnetic', 'Wave': 'wave', 'Depth': 'depth', 'Radar': 'radar', 'Multi': 'multi', 'Buoy': 'buoy', 'Beidou': 'beidou', 'Wind': 'wind', 'Weather': 'weather', 'Sky': 'sky', 'Turbulence': 'turbulence', 'Lidar': 'lidar', 'Visibility': 'visibility', 'Cloud': 'cloud', 'Navigation': 'navigation' }; return `device-${typeMap[deviceType] || 'default'}`; } // 更新数据流显示函数 function updateDataStream() { const streamContainer = document.querySelector('.data-stream'); if (!streamContainer) return; // 随机选择一个设备 const device = oceanDevices[Math.floor(Math.random() * oceanDevices.length)]; const param = device.parameters[Math.floor(Math.random() * device.parameters.length)]; const value = generateRandomValue(param); const time = new Date().toLocaleTimeString(); // 创建数据流项目 const streamItem = document.createElement('div'); streamItem.className = `stream-item ${getDeviceTypeClass(device.type)}`; streamItem.innerHTML = `
${time}
${deviceTypeIcons[device.type] || '📊'} ${device.name}
${param}
${value}
`; // 添加到流中 streamContainer.insertBefore(streamItem, streamContainer.firstChild); // 限制显示的项目数量 const items = streamContainer.querySelectorAll('.stream-item'); if (items.length > 10) { items[items.length - 1].remove(); } // 添加进入动画 streamItem.style.opacity = '0'; streamItem.style.transform = 'translateX(-20px)'; setTimeout(() => { streamItem.style.transition = 'all 0.3s ease'; streamItem.style.opacity = '1'; streamItem.style.transform = 'translateX(0)'; }, 50); } // 更新数据源点渲染函数 function renderDataSourcePoints() { const earthSphere = document.querySelector('.earth-sphere'); if (!earthSphere) return; // 清除现有的数据点 earthSphere.querySelectorAll('.data-source-point').forEach(point => point.remove()); earthSphere.querySelectorAll('.data-stream-line').forEach(line => line.remove()); oceanDevices.forEach((device, index) => { // 创建数据源点 const point = document.createElement('div'); point.className = 'data-source-point'; point.style.backgroundColor = deviceTypeColors[device.type] || '#00d4ff'; point.style.color = deviceTypeColors[device.type] || '#00d4ff'; // 根据经纬度计算位置(简化的球面投影) const x = ((device.longitude + 180) / 360) * 100; const y = ((90 - device.latitude) / 180) * 100; point.style.left = `${x}%`; point.style.top = `${y}%`; // 添加点击事件 point.addEventListener('click', () => showDeviceDetails(device)); // 添加悬停提示 point.title = `${device.name} (${device.type})`; earthSphere.appendChild(point); // 创建数据流线 if (Math.random() < 0.3) { // 30%的概率显示数据流 const streamLine = document.createElement('div'); streamLine.className = 'data-stream-line'; streamLine.style.background = `linear-gradient(to top, ${deviceTypeColors[device.type] || '#00d4ff'}, transparent)`; streamLine.style.left = `${x}%`; streamLine.style.top = `${y}%`; streamLine.style.animationDelay = `${Math.random() * 3}s`; earthSphere.appendChild(streamLine); } }); } // 更新设备详情显示函数 function showDeviceDetails(device) { // 创建模态框 const modal = document.createElement('div'); modal.className = 'device-modal'; modal.innerHTML = `

${deviceTypeIcons[device.type] || '📊'} ${device.name}

×

设备类型: ${device.type}

位置: ${device.latitude.toFixed(4)}°N, ${device.longitude.toFixed(4)}°E

状态: ${device.status === 'online' ? '在线' : '离线'}

设备ID: ${device.id}

监测参数

${device.parameters.map(param => `${param}`).join('')}

实时数据

${device.parameters.slice(0, 6).map(param => `
${param} ${generateRandomValue(param)}
`).join('')}
`; // 添加关闭事件 modal.querySelector('.device-modal-close').addEventListener('click', () => { modal.remove(); }); modal.addEventListener('click', (e) => { if (e.target === modal) { modal.remove(); } }); document.body.appendChild(modal); }