You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2016 lines
81 KiB
HTML

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>全域异构数据</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
<!-- <script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/controls/OrbitControls.min.js"></script> -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/echarts/5.4.0/echarts.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/echarts-gl/2.0.0/echarts-gl.min.js"></script>
<!-- 在 text.html 的 <head> 部分添加 CesiumJS 库引用 -->
<!-- <script src="https://cesium.com/downloads/cesiumjs/releases/1.100/Build/Cesium/Cesium.js"></script>
<link href="https://cesium.com/downloads/cesiumjs/releases/1.100/Build/Cesium/Widgets/widgets.css" rel="stylesheet"> -->
<style>
/* 控制面板内容区域添加滚动条 */
.control-panel-content {
flex: 1;
overflow-y: auto;
padding-right: 10px;
margin-right: -10px;
}
/* 美化控制面板内容区域的滚动条 */
.control-panel-content::-webkit-scrollbar {
width: 4px;
}
.control-panel-content::-webkit-scrollbar-track {
background: rgba(12, 42, 73, 0.3);
border-radius: 4px;
}
.control-panel-content::-webkit-scrollbar-thumb {
background: rgba(79, 195, 247, 0.5);
border-radius: 4px;
}
.control-panel-content::-webkit-scrollbar-thumb:hover {
background: rgba(79, 195, 247, 0.8);
}
/* 替换原有的过滤器样式为以下代码 */
/* 修改过滤器相关样式 */
.filter-options {
display: flex;
flex-direction: column;
gap: 12px;
}
.filter-option {
display: flex;
align-items: center;
position: relative;
padding-left: 30px;
margin-bottom: 5px;
cursor: pointer;
transition: all 0.3s ease;
}
.filter-option:hover {
transform: translateX(5px);
background: rgba(40, 80, 130, 0.3);
border-radius: 4px;
}
.filter-option input[type="checkbox"] {
position: absolute;
opacity: 0;
cursor: pointer;
height: 0;
width: 0;
}
.filter-option .checkmark {
position: absolute;
left: 0;
height: 20px;
width: 20px;
background-color: rgba(25, 55, 95, 0.6);
border: 2px solid #4fc3f7;
border-radius: 4px;
transition: all 0.3s ease;
pointer-events: all;
/* 确保可以点击 */
}
.filter-option:hover .checkmark {
background-color: rgba(79, 195, 247, 0.2);
}
.filter-option input:checked~.checkmark {
background-color: #4fc3f7;
}
.filter-option .checkmark:after {
content: "";
position: absolute;
display: none;
}
.filter-option input:checked~.checkmark:after {
display: block;
}
.filter-option .checkmark:after {
left: 6px;
top: 2px;
width: 6px;
height: 10px;
border: solid white;
border-width: 0 2px 2px 0;
transform: rotate(45deg);
}
.filter-option label {
font-size: 0.95rem;
color: #e0f0ff;
padding: 5px 0;
cursor: pointer;
width: 100%;
margin-left: 10px;
user-select: none;
/* 防止文本选择 */
}
.control-title {
font-size: 1.1rem;
margin: 15px 0 10px 0;
color: #4fc3f7;
padding-bottom: 5px;
border-bottom: 1px solid rgba(79, 195, 247, 0.3);
}
.control-group {
margin-bottom: 20px;
padding: 10px;
background: rgba(16, 36, 62, 0.3);
border-radius: 8px;
border: 1px solid rgba(64, 156, 255, 0.2);
}
/* 优化滚动条以确保内容可见 */
/* .info-panel {
overflow-y: auto;
} */
/* 美化滚动条 */
.info-panel::-webkit-scrollbar {
width: 4px;
}
.info-panel::-webkit-scrollbar-track {
background: rgba(12, 42, 73, 0.3);
border-radius: 4px;
}
.info-panel::-webkit-scrollbar-thumb {
background: rgba(79, 195, 247, 0.5);
border-radius: 4px;
}
.info-panel::-webkit-scrollbar-thumb:hover {
background: rgba(79, 195, 247, 0.8);
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: 'Segoe UI', Arial, sans-serif;
}
body {
background: url('/assets/img/bg.png') no-repeat;
background-size: 100% 100%;
color: #e0f0ff;
overflow: hidden;
height: 100vh;
}
/* 新增顶部标题栏样式 */
.topDiv {
width: 100%;
height: 8vh;
background: url('/assets/img/topBg.png') top no-repeat;
background-size: 100% 115%;
display: flex;
justify-content: center;
z-index: 990;
position: relative;
/* background-color: rgba(12, 42, 73, 0.8); */
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
}
.topDiv .center {
font-family: PangMenZhengDao, 'Source Han Sans CN';
font-weight: 400;
font-size: 4.1vh;
color: #ffffff;
align-self: center;
margin-top: -40px;
}
.topDiv .left {
position: absolute;
top: 4vh;
left: 16vw;
color: #ffffff;
}
.topDiv .left span:nth-child(1) {
font-weight: bold;
font-size: 0.8vw;
margin-right: 0.5vh;
}
.topDiv .left span:nth-child(2) {
font-weight: bold;
font-size: 1.2vw;
}
.topDiv .right {
position: absolute;
top: 3vh;
right: 0;
color: #ffffff;
display: flex;
align-items: center;
}
.topDiv .right img:nth-child(1) {}
.topDiv .right span {
font-weight: bold;
font-size: 1.2vw;
}
.topDiv .icon-home {
position: absolute;
top: 1vh;
right: 8vw;
width: 4vh;
cursor: pointer;
}
.container {
display: flex;
height: calc(100vh - 8vh - 20px);
position: relative;
margin-bottom: 20px;
}
/* 左侧信息面板 */
.info-panel {
width: 300px;
height: 100%;
padding: 20px;
background: rgba(12, 42, 73, 0.3);
backdrop-filter: blur(10px);
border: 1px solid rgba(64, 156, 255, 0.3);
box-shadow: 5px 0 15px rgba(0, 0, 0, 0.3);
z-index: 10;
border-radius: 8px;
position: absolute;
left: 15px;
top: 0;
display: flex;
flex-direction: column;
}
/* 右侧控制面板 */
.control-panel {
width: 280px;
height: 100%;
padding: 20px;
background: rgba(12, 42, 73, 0.3);
backdrop-filter: blur(10px);
border: 1px solid rgba(64, 156, 255, 0.3);
box-shadow: -5px 0 15px rgba(0, 0, 0, 0.3);
z-index: 10;
border-radius: 8px;
position: absolute;
right: 15px;
top: 0;
display: flex;
flex-direction: column;
}
/* 卡片头部样式 */
/* .card-header {
height: 40px;
background: linear-gradient(to right, #1a3a5f, #2a5a8f);
display: flex;
justify-content: center;
align-items: center;
border-radius: 8px 8px 0 0 !important;
border-bottom: 1px solid #4fc3f7;
padding: 12px 20px;
position: relative;
margin: -20px -20px 20px -20px;
}
.card-header span {
font-size: 1.2rem;
letter-spacing: 1px;
font-family: 'Microsoft YaHei';
font-weight: 400;
color: #ffffff;
} */
.stats-container {
display: flex;
flex-direction: column;
gap: 15px;
margin-bottom: 25px;
}
.stat-card {
background: rgba(25, 55, 95, 0.6);
/* background: url('/assets/img/common/kva2.png') no-repeat; */
/* background-size: 100% 100%; */
border-radius: 10px;
padding: 15px;
border-left: 4px solid #4fc3f7;
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.2);
}
.stat-label {
font-size: 0.9rem;
color: #a0d2ff;
margin-bottom: 5px;
}
.stat-value {
font-size: 1.8rem;
font-weight: bold;
color: #4fc3f7;
}
.data-list {
flex: 1;
overflow-y: auto;
margin-top: 10px;
padding-right: 5px;
}
.data-item {
min-width: 200px;
background: rgba(25, 55, 95, 0.6);
border-radius: 8px;
padding: 10px;
border-left: 3px solid #4fc3f7;
transition: all 0.3s ease;
margin-bottom: 10px;
cursor: pointer;
}
.data-item:hover {
transform: translateX(5px);
background: rgba(40, 80, 130, 0.7);
}
.stream-device-info {
display: flex;
align-items: center;
margin-bottom: 8px;
}
.stream-device-icon {
font-size: 1.5rem;
margin-right: 10px;
}
.stream-device-details {
flex: 1;
}
.stream-device-name {
font-weight: bold;
color: #4fc3f7;
font-size: 0.9rem;
}
.stream-parameter {
font-size: 0.8rem;
color: #a0d2ff;
}
.stream-value-section {
text-align: right;
}
.stream-value {
font-weight: bold;
font-size: 1.1rem;
font-family: 'Courier New', monospace;
}
.stream-timestamp {
font-size: 0.7rem;
color: #81d4fa;
}
.control-group {
margin-bottom: 25px;
}
.control-title {
font-size: 1.2rem;
margin-bottom: 15px;
color: #4fc3f7;
}
.toggle-switch {
display: flex;
align-items: center;
margin-bottom: 20px;
}
.switch-label {
margin-right: 10px;
font-size: 1rem;
}
.switch {
position: relative;
display: inline-block;
width: 60px;
height: 30px;
}
.switch input {
opacity: 0;
width: 0;
height: 0;
}
.slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #2c3e50;
transition: .4s;
border-radius: 34px;
}
.slider:before {
position: absolute;
content: "";
height: 22px;
width: 22px;
left: 4px;
bottom: 4px;
background-color: white;
transition: .4s;
border-radius: 50%;
}
input:checked+.slider {
background-color: #4fc3f7;
}
input:checked+.slider:before {
transform: translateX(30px);
}
.filter-options {
display: flex;
flex-direction: column;
gap: 10px;
}
.filter-option {
display: flex;
align-items: center;
}
.filter-option input {
margin-right: 10px;
}
/* 地球容器样式 */
.earth-container {
flex: 1;
height: 100%;
position: relative;
margin: 0 300px;
background: #0c1a2d;
}
#earthCanvas {
width: 100%;
height: 100%;
display: block;
}
/* 信息卡片样式 */
.info-card {
position: absolute;
background: rgba(16, 36, 62, 0.95);
backdrop-filter: blur(10px);
border-radius: 10px;
padding: 15px;
box-shadow: 0 5px 20px rgba(0, 0, 0, 0.4);
border: 1px solid rgba(64, 156, 255, 0.3);
z-index: 1000;
width: 250px;
transform: translate(-50%, -100%);
margin-top: -20px;
}
.info-card::before {
content: "";
position: absolute;
top: 100%;
left: 50%;
transform: translateX(-50%);
border-width: 10px;
border-style: solid;
border-color: rgba(16, 36, 62, 0.95) transparent transparent transparent;
}
/* 卡片头部样式 */
.card-header {
height: 40px;
background: url('/assets/img/common/homeTitle2.png') no-repeat;
background-position: center;
background-size: 108% 120%;
display: flex;
justify-content: center;
align-items: center;
border-radius: 8px 8px 0 0 !important;
border-bottom: 1px solid var(--light-blue);
padding: 32px 20px;
position: relative;
margin: -20px -20px 0px -20px;
}
.card-header span {
font-size: 1.2rem;
letter-spacing: 1px;
font-family: 'YouSheBiaoTiHei', 'Microsoft YaHei';
font-weight: 400;
color: #ffffff;
}
.info-card-title {
font-size: 1.1rem;
color: #4fc3f7;
margin: 0;
}
.info-card-close {
background: none;
border: none;
color: #a0d2ff;
font-size: 1.2rem;
cursor: pointer;
padding: 0;
width: 24px;
height: 24px;
display: flex;
align-items: center;
justify-content: center;
}
.info-card-close:hover {
color: #ffffff;
}
.info-card-content {
font-size: 0.9rem;
}
.info-item {
margin: 8px 0;
display: flex;
}
.info-label {
width: 70px;
color: #a0d2ff;
font-weight: bold;
}
.info-value {
flex: 1;
color: #e0f0ff;
word-break: break-all;
}
.status-online {
color: #4caf50;
font-weight: bold;
}
.status-offline {
color: #f44336;
font-weight: bold;
}
/* 响应式调整 */
@media (max-width: 1200px) {
.info-panel,
.control-panel {
width: 260px;
}
.earth-container {
margin: 0 275px;
}
}
@media (max-width: 900px) {
.info-panel,
.control-panel {
width: 220px;
padding: 15px;
}
.earth-container {
margin: 0 235px;
}
.stat-value {
font-size: 1.5rem;
}
}
/* 滚动条样式 */
::-webkit-scrollbar {
width: 4px;
height: 4px;
}
::-webkit-scrollbar-track {
background: rgba(12, 42, 73, 0.3);
border-radius: 4px;
}
::-webkit-scrollbar-thumb {
background: rgba(106, 149, 201, 0.5);
border-radius: 4px;
}
::-webkit-scrollbar-thumb:hover {
background: rgba(106, 149, 201, 0.7);
}
</style>
</head>
<body>
<!-- 顶部标题栏 -->
<div class="topDiv">
<div class="left">
<span id="current-date">2025年9月17日</span>
<span id="current-time">15:18:00</span>
</div>
<div class="center">全域异构数据</div>
<div class="right">
<img src="/common/images/logo_gt.png" alt="" />
</div>
</div>
<div class="container">
<!-- 左侧信息面板 -->
<!-- 修改左侧信息面板的结构 -->
<div class="info-panel">
<div class="card-header">
<span>实时数据监控</span>
</div>
<div class="stats-container">
<div class="stat-card">
<div class="stat-label">今日接收总量</div>
<div class="stat-value" id="totalReceived" style="color: #5ef6fb;">12,847</div>
</div>
<div class="stat-card">
<div class="stat-label">当前接收速率</div>
<div class="stat-value" id="receiveRate" style="color: #ddf865;">342/分钟</div>
</div>
<div class="stat-card">
<div class="stat-label">数据源在线数量</div>
<div class="stat-value" id="onlineSources" style="color: #f44336;">86</div>
</div>
</div>
<div class="card-header">
<span>控制面板</span>
</div>
<!-- 控制面板内容区域添加滚动条 -->
<div class="control-panel-content">
<!-- 替换原有的数据源过滤器部分 -->
<div class="control-group">
<h3 class="control-title">数据源过滤器</h3>
<div class="filter-options">
<div class="filter-option">
<input type="checkbox" id="filterBuoy" checked>
<span class="checkmark"></span>
<label for="filterBuoy">海洋浮标</label>
</div>
<div class="filter-option">
<input type="checkbox" id="filterSatellite" checked>
<span class="checkmark"></span>
<label for="filterSatellite">卫星</label>
</div>
<div class="filter-option">
<input type="checkbox" id="filterStation" checked>
<span class="checkmark"></span>
<label for="filterStation">监测站</label>
</div>
<div class="filter-option">
<input type="checkbox" id="filterShip" checked>
<span class="checkmark"></span>
<label for="filterShip">船舶</label>
</div>
</div>
</div>
<div class="control-group">
<h3 class="control-title">设备类型过滤器</h3>
<div class="filter-options">
<div class="filter-option">
<input type="checkbox" id="filterCTD" checked>
<span class="checkmark"></span>
<label for="filterCTD">CTD温盐深仪</label>
</div>
<div class="filter-option">
<input type="checkbox" id="filterADCP38K" checked>
<span class="checkmark"></span>
<label for="filterADCP38K">ADCP系统(38k)</label>
</div>
<div class="filter-option">
<input type="checkbox" id="filterADCP150K" checked>
<span class="checkmark"></span>
<label for="filterADCP150K">ADCP系统(150k)</label>
</div>
<div class="filter-option">
<input type="checkbox" id="filterGravity" checked>
<span class="checkmark"></span>
<label for="filterGravity">重力仪</label>
</div>
<div class="filter-option">
<input type="checkbox" id="filterMagnetic" checked>
<span class="checkmark"></span>
<label for="filterMagnetic">磁力仪</label>
</div>
<div class="filter-option">
<input type="checkbox" id="filterWaveGauge" checked>
<span class="checkmark"></span>
<label for="filterWaveGauge">波潮仪</label>
</div>
<div class="filter-option">
<input type="checkbox" id="filterDepthSounder" checked>
<span class="checkmark"></span>
<label for="filterDepthSounder">6000米测深仪</label>
</div>
<div class="filter-option">
<input type="checkbox" id="filterWaveRadar" checked>
<span class="checkmark"></span>
<label for="filterWaveRadar">测波雷达</label>
</div>
<div class="filter-option">
<input type="checkbox" id="filterMultiSensor" checked>
<span class="checkmark"></span>
<label for="filterMultiSensor">多要素测定系统</label>
</div>
<div class="filter-option">
<input type="checkbox" id="filterWaveBuoy" checked>
<span class="checkmark"></span>
<label for="filterWaveBuoy">小型波浪浮标</label>
</div>
<div class="filter-option">
<input type="checkbox" id="filterBeidou" checked>
<span class="checkmark"></span>
<label for="filterBeidou">北斗探测系统</label>
</div>
<div class="filter-option">
<input type="checkbox" id="filterWindProfiler" checked>
<span class="checkmark"></span>
<label for="filterWindProfiler">风廓线雷达</label>
</div>
<div class="filter-option">
<input type="checkbox" id="filterWeatherStation" checked>
<span class="checkmark"></span>
<label for="filterWeatherStation">自动气象站</label>
</div>
</div>
</div>
<div class="control-group">
<h3 class="control-title">参数类型过滤器</h3>
<div class="filter-options">
<div class="filter-option">
<input type="checkbox" id="filterBasic" checked>
<span class="checkmark"></span>
<label for="filterBasic">基础参数</label>
</div>
<div class="filter-option">
<input type="checkbox" id="filterHydro" checked>
<span class="checkmark"></span>
<label for="filterHydro">水文参数</label>
</div>
<div class="filter-option">
<input type="checkbox" id="filterWave" checked>
<span class="checkmark"></span>
<label for="filterWave">波浪参数</label>
</div>
<div class="filter-option">
<input type="checkbox" id="filterMeteor" checked>
<span class="checkmark"></span>
<label for="filterMeteor">气象参数</label>
</div>
<div class="filter-option">
<input type="checkbox" id="filterGeo" checked>
<span class="checkmark"></span>
<label for="filterGeo">地球物理参数</label>
</div>
</div>
</div>
</div>
</div>
<!-- 地球容器 -->
<div class="earth-container">
<div id="earthGlobe" style="width:100%; height:100%;"></div>
</div>
<!-- 右侧控制面板 -->
<div class="control-panel">
<div class="card-header">
<span>实时数据流</span>
</div>
<div class="data-list" id="dataList">
<!-- 数据项将通过JavaScript动态添加 -->
</div>
</div>
</div>
<script>
// 全局变量
let chart;
let isSimulationRunning = true;
let flyLineData = [];
let dataPoints = [];
// 在全局变量部分添加新变量
let activeFlyLines = []; // 存储当前活动的飞线
let flyLineTimers = []; // 存储飞线计时器
// 初始化函数
function init() {
// 更新当前时间
updateCurrentTime();
setInterval(updateCurrentTime, 1000);
// 初始化 ECharts 3D 地球
initEarthGlobe();
// 初始化UI事件
initUIEvents();
// 开始模拟数据
startDataSimulation();
}
// 更新当前时间
function updateCurrentTime() {
const now = new Date();
const dateStr = `${now.getFullYear()}${now.getMonth() + 1}${now.getDate()}`;
const timeStr = now.toLocaleTimeString('zh-CN', { hour12: false });
document.getElementById('current-date').textContent = dateStr;
document.getElementById('current-time').textContent = timeStr;
}
// 修改 initEarthGlobe 函数中的系列配置
function initEarthGlobe() {
// 获取地球容器
const container = document.querySelector('.earth-container');
// 创建 ECharts 实例
chart = echarts.init(container, null, { renderer: 'webgl' });
// 模拟数据源位置
const dataSources = [
{ type: 'buoy', lat: 40.7128, lng: -74.0060, id: 'B001', name: '纽约浮标' },
{ type: 'satellite', lat: 0, lng: -75, id: 'S001', name: '赤道卫星' },
{ type: 'station', lat: 51.5074, lng: -0.1278, id: 'ST001', name: '伦敦监测站' },
{ type: 'ship', lat: 35.6762, lng: 139.6503, id: 'SH001', name: '东京船舶' },
{ type: 'buoy', lat: -33.8688, lng: 151.2093, id: 'B002', name: '悉尼浮标' },
{ type: 'satellite', lat: 30, lng: 120, id: 'S002', name: '亚洲卫星' },
{ type: 'station', lat: -23.5505, lng: -46.6333, id: 'ST002', name: '圣保罗监测站' },
{ type: 'ship', lat: 55.7558, lng: 37.6173, id: 'SH002', name: '莫斯科船舶' },
{ type: 'buoy', lat: 28.6139, lng: 77.2090, id: 'B003', name: '新德里浮标' },
{ type: 'satellite', lat: -30, lng: 60, id: 'S003', name: '印度洋卫星' },
// 中国附近海域新增数据源
{ type: 'buoy', lat: 20.0, lng: 115.0, id: 'B004', name: '南海浮标1' },
{ type: 'buoy', lat: 15.0, lng: 110.0, id: 'B005', name: '南海浮标2' },
{ type: 'buoy', lat: 25.0, lng: 120.0, id: 'B006', name: '东海浮标1' },
{ type: 'buoy', lat: 30.0, lng: 125.0, id: 'B007', name: '东海浮标2' },
{ type: 'buoy', lat: 35.0, lng: 122.0, id: 'B008', name: '黄海浮标' },
{ type: 'buoy', lat: 38.0, lng: 118.0, id: 'B009', name: '渤海浮标' },
{ type: 'ship', lat: 22.0, lng: 118.0, id: 'SH003', name: '南海科考船' },
{ type: 'ship', lat: 28.0, lng: 123.0, id: 'SH004', name: '东海巡逻船' },
{ type: 'station', lat: 31.0, lng: 121.0, id: 'ST003', name: '上海监测站' },
{ type: 'station', lat: 23.0, lng: 113.0, id: 'ST004', name: '广州监测站' },
{ type: 'station', lat: 39.0, lng: 117.0, id: 'ST005', name: '天津监测站' },
{ type: 'satellite', lat: 25.0, lng: 115.0, id: 'S004', name: '南海卫星' },
{ type: 'satellite', lat: 35.0, lng: 120.0, id: 'S005', name: '东海卫星' },
{ type: 'buoy', lat: 18.0, lng: 108.0, id: 'B010', name: '南海南部浮标' },
{ type: 'buoy', lat: 27.0, lng: 128.0, id: 'B011', name: '东海东北浮标' },
{ type: 'ship', lat: 20.0, lng: 105.0, id: 'SH005', name: '北部湾科考船' },
{ type: 'station', lat: 18.0, lng: 109.0, id: 'ST006', name: '海南监测站' },
{ type: 'buoy', lat: 24.0, lng: 122.0, id: 'B012', name: '台湾海峡浮标' },
{ type: 'ship', lat: 32.0, lng: 130.0, id: 'SH006', name: '日本海科考船' },
{ type: 'station', lat: 26.0, lng: 100.0, id: 'ST007', name: '云南地震监测站' },
// 南海新增5个点位
{ type: 'buoy', lat: 12.0, lng: 114.0, id: 'B013', name: '南海中部浮标' },
{ type: 'ship', lat: 9.0, lng: 112.0, id: 'SH007', name: '南海南部科考船' },
{ type: 'station', lat: 18.0, lng: 116.0, id: 'ST008', name: '南海西部监测站' },
{ type: 'satellite', lat: 15.0, lng: 120.0, id: 'S006', name: '南海东部卫星' },
{ type: 'buoy', lat: 22.0, lng: 118.0, id: 'B014', name: '南海北部浮标' },
// 印度洋新增5个点位
{ type: 'buoy', lat: -5.0, lng: 75.0, id: 'B015', name: '印度洋西部浮标' },
{ type: 'ship', lat: -2.0, lng: 80.0, id: 'SH008', name: '印度洋中部科考船' },
{ type: 'station', lat: 1.0, lng: 85.0, id: 'ST009', name: '印度洋东部监测站' },
{ type: 'satellite', lat: -3.0, lng: 70.0, id: 'S007', name: '印度洋西南卫星' },
{ type: 'buoy', lat: 3.0, lng: 90.0, id: 'B016', name: '印度洋东北浮标' },
// 额外新增南海数据点5个
{ type: 'buoy', lat: 16.0, lng: 118.0, id: 'B017', name: '南海东南浮标' },
{ type: 'ship', lat: 13.0, lng: 116.0, id: 'SH009', name: '南海中部科考船' },
{ type: 'station', lat: 21.0, lng: 113.0, id: 'ST010', name: '南海西北监测站' },
{ type: 'satellite', lat: 10.0, lng: 110.0, id: 'S008', name: '南海西南卫星' },
{ type: 'buoy', lat: 19.0, lng: 122.0, id: 'B018', name: '南海东北浮标' },
// 额外新增印度洋数据点5个
{ type: 'buoy', lat: -1.0, lng: 72.0, id: 'B019', name: '印度洋西北浮标' },
{ type: 'ship', lat: 2.0, lng: 78.0, id: 'SH010', name: '印度洋东部科考船' },
{ type: 'station', lat: -4.0, lng: 82.0, id: 'ST011', name: '印度洋东南监测站' },
{ type: 'satellite', lat: 0.0, lng: 68.0, id: 'S009', name: '印度洋北部卫星' },
{ type: 'buoy', lat: 4.0, lng: 95.0, id: 'B020', name: '印度洋东北浮标' }
];
// 模拟数据源位置
// 模拟数据源位置
// const dataSources = [
// { type: 'buoy', lat: 40.7128, lng: -74.0060, id: 'B001', name: '纽约浮标' },
// { type: 'satellite', lat: 0, lng: -75, id: 'S001', name: '赤道卫星' },
// { type: 'station', lat: 51.5074, lng: -0.1278, id: 'ST001', name: '伦敦监测站' },
// { type: 'ship', lat: 35.6762, lng: 139.6503, id: 'SH001', name: '东京船舶' },
// { type: 'buoy', lat: -33.8688, lng: 151.2093, id: 'B002', name: '悉尼浮标' },
// { type: 'satellite', lat: 30, lng: 120, id: 'S002', name: '亚洲卫星' },
// { type: 'station', lat: -23.5505, lng: -46.6333, id: 'ST002', name: '圣保罗监测站' },
// { type: 'ship', lat: 55.7558, lng: 37.6173, id: 'SH002', name: '莫斯科船舶' },
// { type: 'buoy', lat: 28.6139, lng: 77.2090, id: 'B003', name: '新德里浮标' },
// { type: 'satellite', lat: -30, lng: 60, id: 'S003', name: '印度洋卫星' },
// // 中国附近海域新增数据源
// { type: 'buoy', lat: 20.0, lng: 115.0, id: 'B004', name: '南海浮标1' },
// { type: 'buoy', lat: 15.0, lng: 110.0, id: 'B005', name: '南海浮标2' },
// { type: 'buoy', lat: 25.0, lng: 120.0, id: 'B006', name: '东海浮标1' },
// { type: 'buoy', lat: 30.0, lng: 125.0, id: 'B007', name: '东海浮标2' },
// { type: 'buoy', lat: 35.0, lng: 122.0, id: 'B008', name: '黄海浮标' },
// { type: 'buoy', lat: 38.0, lng: 118.0, id: 'B009', name: '渤海浮标' },
// { type: 'ship', lat: 22.0, lng: 118.0, id: 'SH003', name: '南海科考船' },
// { type: 'ship', lat: 28.0, lng: 123.0, id: 'SH004', name: '东海巡逻船' },
// { type: 'station', lat: 31.0, lng: 121.0, id: 'ST003', name: '上海监测站' },
// { type: 'station', lat: 23.0, lng: 113.0, id: 'ST004', name: '广州监测站' },
// { type: 'station', lat: 39.0, lng: 117.0, id: 'ST005', name: '天津监测站' },
// { type: 'satellite', lat: 25.0, lng: 115.0, id: 'S004', name: '南海卫星' },
// { type: 'satellite', lat: 35.0, lng: 120.0, id: 'S005', name: '东海卫星' },
// { type: 'buoy', lat: 18.0, lng: 108.0, id: 'B010', name: '南海南部浮标' },
// { type: 'buoy', lat: 27.0, lng: 128.0, id: 'B011', name: '东海东北浮标' },
// { type: 'ship', lat: 20.0, lng: 105.0, id: 'SH005', name: '北部湾科考船' },
// { type: 'station', lat: 18.0, lng: 109.0, id: 'ST006', name: '海南监测站' },
// { type: 'buoy', lat: 24.0, lng: 122.0, id: 'B012', name: '台湾海峡浮标' },
// { type: 'ship', lat: 32.0, lng: 130.0, id: 'SH006', name: '日本海科考船' },
// { type: 'station', lat: 26.0, lng: 100.0, id: 'ST007', name: '云南地震监测站' },
// // 南海新增5个点位
// { type: 'buoy', lat: 12.0, lng: 114.0, id: 'B013', name: '南海中部浮标' },
// { type: 'ship', lat: 9.0, lng: 112.0, id: 'SH007', name: '南海南部科考船' },
// { type: 'station', lat: 18.0, lng: 116.0, id: 'ST008', name: '南海西部监测站' },
// { type: 'satellite', lat: 15.0, lng: 120.0, id: 'S006', name: '南海东部卫星' },
// { type: 'buoy', lat: 22.0, lng: 118.0, id: 'B014', name: '南海北部浮标' },
// { type: 'ship', lat: 24.0, lng: 114.0, id: 'B013', name: '南海中部浮标' },
// { type: 'satellite', lat: 26.0, lng: 120.0, id: 'S006', name: '南海东部卫星' },
// // 印度洋新增5个点位
// { type: 'buoy', lat: -5.0, lng: 75.0, id: 'B015', name: '印度洋西部浮标' },
// { type: 'ship', lat: -2.0, lng: 80.0, id: 'SH008', name: '印度洋中部科考船' },
// { type: 'station', lat: 1.0, lng: 85.0, id: 'ST009', name: '印度洋东部监测站' },
// { type: 'satellite', lat: -3.0, lng: 60.0, id: 'S007', name: '印度洋西南卫星' },
// { type: 'buoy', lat: 3.0, lng: 90.0, id: 'B016', name: '印度洋东北浮标' },
// { type: 'buoy', lat: 4.0, lng: 95.0, id: 'B016', name: '印度洋东北浮标' },
// // // 更均匀分布的额外数据点
// // { type: 'buoy', lat: -10.0, lng: -120.0, id: 'B017', name: '南太平洋浮标' },
// // { type: 'ship', lat: 45.0, lng: -30.0, id: 'SH009', name: '北大西洋科考船' },
// // { type: 'station', lat: -40.0, lng: 170.0, id: 'ST010', name: '新西兰监测站' },
// // { type: 'satellite', lat: 10.0, lng: -160.0, id: 'S008', name: '太平洋岛屿卫星' },
// // { type: 'buoy', lat: 60.0, lng: 20.0, id: 'B018', name: '北欧海浮标' },
// // // 更均匀分布的额外印度洋数据点
// // { type: 'buoy', lat: -20.0, lng: 55.0, id: 'B019', name: '西南印度洋浮标' },
// // { type: 'ship', lat: 15.0, lng: 50.0, id: 'SH010', name: '阿拉伯海科考船' },
// // { type: 'station', lat: -15.0, lng: 125.0, id: 'ST011', name: '澳大利亚监测站' },
// // { type: 'satellite', lat: 20.0, lng: 30.0, id: 'S009', name: '红海卫星' },
// // { type: 'buoy', lat: -35.0, lng: 100.0, id: 'B020', name: '东南印度洋浮标' }
// ];
// 数据接收站位置 (北京坐标)112.88,28.24
const receiverStation = { lat: 28.2442, lng: 112.8874, name: '北京数据接收中心' };
// 生成飞线数据
flyLineData = dataSources.map(source => [
[source.lng, source.lat],
[receiverStation.lng, receiverStation.lat]
]);
// 保存数据点信息
dataPoints = dataSources;
// 生成模拟温度数据
const temperatureData = [];
for (let i = 0; i < 2000; i++) {
const lat = Math.random() * 180 - 90;
const lng = Math.random() * 360 - 180;
const temp = 10 + Math.random() * 25; // 10-35°C
// 使用4个值[经度, 纬度, 高度(设为0使数据贴合地球表面), 温度值]
temperatureData.push([lng, lat, 0, temp]);
}
// 配置项
const option = {
backgroundColor: 'transparent',
globe: {
baseTexture: "/assets/img/earth-topology.png",
// heightTexture: "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTAyNCIgaGVpZ2h0PSI1MTIiIHZpZXdCb3g9IjAgMCAxMDI0IDUxMiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHJlY3Qgd2lkdGg9IjEwMjQiIGhlaWdodD0iNTEyIiBmaWxsPSIjMGMxYTJkIi8+CjxwYXRoIGQ9Ik01MTIgMjU2QzUxMiAzOTcuODgzIDM5Ny44ODMgNTEyIDI1NiA1MTJDMTE0LjExNyA1MTIgMCAzOTcuODgzIDAgMjU2QzAgMTE0LjExNyAxMTQuMTE3IDAgMjU2IDBDMzk3Ljg4MyAwIDUxMiAxMTQuMTE3IDUxMiAyNTZaIiBmaWxsPSIjMWIzZDc5Ii8+Cjwvc3ZnPg==",
displacementScale: 0.04,
displacementQuality: 'medium',
environment: "/assets/img/night-sky.png",
globeRadius: 100,
globeOuterRadius: 120,
shading: 'realistic',
realisticMaterial: {
roughness: 0.8,
metalness: 0
},
postEffect: {
enable: true,
bloom: {
enable: true,
intensity: 0.1
}
},
light: {
main: {
intensity: 2,
shadow: true
},
// ambientCubemap: {
// texture: 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTAyNCIgaGVpZ2h0PSI1MTIiIHZpZXdCb3g9IjAgMCAxMDI0IDUxMiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHJlY3Qgd2lkdGg9IjEwMjQiIGhlaWdodD0iNTEyIiBmaWxsPSIjMGMxYTJkIi8+Cjwvc3ZnPg=='
// }
},
viewControl: {
autoRotate: true,
autoRotateSpeed: 1,
targetCoord: [105, 35]
},
layers: [
{
type: 'blend',
blendTo: 'emission',
texture: "/assets/img/earth-night.jpg",
intensity: 1.5
}
]
},
series: [
// 数据源点系列
{
type: 'scatter3D',
coordinateSystem: 'globe',
blendMode: 'lighter',
symbolSize: 10,
itemStyle: {
color: function (params) {
const source = dataSources[params.dataIndex];
switch (source.type) {
case 'buoy': return '#00e5ff';
case 'satellite': return '#76ff03';
case 'station': return '#ff9100';
case 'ship': return '#ff4081';
default: return '#ffffff';
}
},
opacity: 0.9
},
data: dataSources.map(source => [source.lng, source.lat]),
emphasis: {
itemStyle: {
color: '#ffd700',
borderColor: '#ffd700',
borderWidth: 2
}
}
},
// 接收站点系列
{
type: 'scatter3D',
coordinateSystem: 'globe',
blendMode: 'lighter',
symbolSize: 15,
itemStyle: {
color: '#4fc3f7',
opacity: 0.9
},
data: [[receiverStation.lng, receiverStation.lat]],
emphasis: {
itemStyle: {
color: '#ffd700',
borderColor: '#ffd700',
borderWidth: 2
}
}
},
// 飞线系列
{
type: 'lines3D',
coordinateSystem: 'globe',
effect: {
show: true,
trailWidth: 3, // 稍微增加尾迹宽度
trailLength: 0.05, // 进一步减小尾迹长度,使尾迹更快消失
trailOpacity: 0.6, // 降低尾迹透明度
symbolSize: 1
},
blendMode: 'lighter',
lineStyle: {
width: 1,
color: 'rgb(50, 208, 255)',
opacity: 0.6 // 降低线条透明度
},
data: flyLineData
},
// 温度数据系列
{
type: 'scatter3D',
coordinateSystem: 'globe',
blendMode: 'lighter',
symbolSize: 2,
itemStyle: {
color: function (params) {
// 温度数据使用第4个值索引为3
if (params.value && params.value.length > 3) {
const temp = params.value[3]; // 确保使用正确的索引
if (temp < 15) return '#4A90E2'; // 冷色
if (temp < 20) return '#7ED321'; // 凉色
if (temp < 25) return '#F5A623'; // 暖色
return '#D0021B'; // 热色
}
return '#4A90E2'; // 默认颜色
},
opacity: 0.8
},
data: temperatureData
}
]
};
// 设置配置项
chart.setOption(option);
// 修改点击事件处理函数,增强调试能力
chart.on('click', function (params) {
console.log('图表被点击,完整参数:', JSON.stringify(params, null, 2));
if (params.componentType === 'series') {
if (params.seriesType === 'scatter3D') {
if (params.seriesIndex === 0 && params.dataIndex < dataPoints.length) {
// 点击的是数据源点
console.log('点击数据源点:', params.dataIndex);
const source = dataPoints[params.dataIndex];
showSourceInfo(source, params);
} else if (params.seriesIndex === 1 && params.dataIndex === 0) {
// 点击的是接收站
console.log('点击接收站');
showReceiverInfo(params);
} else if (params.seriesIndex === 3) {
// 点击的是温度数据点
console.log('点击温度数据点:', params.dataIndex);
showTemperatureInfo(params);
} else {
console.log('点击了未处理的scatter3D系列:', params.seriesIndex);
}
} else if (params.seriesType === 'lines3D') {
console.log('点击飞线:', params);
} else {
console.log('点击了其他系列类型:', params.seriesType);
}
} else {
console.log('点击了非系列组件:', params.componentType);
}
});
// 添加鼠标悬停事件
chart.on('mouseover', function (params) {
if (params.seriesType === 'scatter3D' && params.seriesIndex === 0 && params.dataIndex < dataPoints.length) {
// 高亮对应的飞线
highlightFlyLine(params.dataIndex);
}
});
chart.on('mouseout', function () {
// 取消高亮飞线
resetFlyLineHighlight();
});
}
// 添加显示温度信息的函数
function showTemperatureInfo(params, temperatureData) {
// 移除已存在的信息卡片
removeInfoCard();
// 获取温度数据
const data = temperatureData[params.dataIndex];
if (!data || data.length < 4) return;
const lng = data[0];
const lat = data[1];
const temp = data[3];
// 创建信息卡片
const infoCard = document.createElement('div');
infoCard.className = 'info-card';
infoCard.innerHTML = `
<div class="info-card-header">
<h3 class="info-card-title">温度数据</h3>
<button class="info-card-close">&times;</button>
</div>
<div class="info-card-content">
<div class="info-item">
<span class="info-label">位置:</span>
<span class="info-value">纬度: ${lat.toFixed(4)}°<br>经度: ${lng.toFixed(4)}°</span>
</div>
<div class="info-item">
<span class="info-label">温度:</span>
<span class="info-value">${temp.toFixed(1)}°C</span>
</div>
<div class="info-item">
<span class="info-label">状态:</span>
<span class="info-value status-online">在线</span>
</div>
</div>
`;
// 设置位置
infoCard.style.left = `${params.event.event.clientX}px`;
infoCard.style.top = `${params.event.event.clientY}px`;
document.querySelector('.earth-container').appendChild(infoCard);
// 添加关闭按钮事件
infoCard.querySelector('.info-card-close').addEventListener('click', function () {
infoCard.remove();
});
// 点击外部关闭
document.addEventListener('click', function closeCard(e) {
if (!infoCard.contains(e.target) && e.target.closest('.info-card') !== infoCard) {
infoCard.remove();
document.removeEventListener('click', closeCard);
}
});
}
// 高亮飞线
function highlightFlyLine(index) {
if (!chart) return;
const option = chart.getOption();
if (option && option.series && option.series[2]) {
// 修改对应飞线的颜色
option.series[2].lineStyle = {
width: 2,
color: '#ffd700',
opacity: 1
};
chart.setOption(option);
}
}
// 重置飞线高亮
function resetFlyLineHighlight() {
if (!chart) return;
const option = chart.getOption();
if (option && option.series && option.series[2]) {
// 恢复默认飞线样式
option.series[2].lineStyle = {
width: 1,
color: 'rgb(50, 208, 255)',
opacity: 0.8
};
chart.setOption(option);
}
}
// 显示数据源信息
function showSourceInfo(source, event) {
// 移除已存在的信息卡片
removeInfoCard();
// 创建信息卡片
const infoCard = document.createElement('div');
infoCard.className = 'info-card';
// 根据类型获取中文名称
const typeNames = {
'buoy': '海洋浮标',
'satellite': '卫星',
'station': '监测站',
'ship': '船舶'
};
infoCard.innerHTML = `
<div class="info-card-header">
<h3 class="info-card-title">数据源信息</h3>
<button class="info-card-close">&times;</button>
</div>
<div class="info-card-content">
<div class="info-item">
<span class="info-label">ID:</span>
<span class="info-value">${source.id}</span>
</div>
<div class="info-item">
<span class="info-label">名称:</span>
<span class="info-value">${source.name}</span>
</div>
<div class="info-item">
<span class="info-label">类型:</span>
<span class="info-value">${typeNames[source.type] || source.type}</span>
</div>
<div class="info-item">
<span class="info-label">位置:</span>
<span class="info-value">纬度: ${source.lat.toFixed(4)}°, 经度: ${source.lng.toFixed(4)}°</span>
</div>
<div class="info-item">
<span class="info-label">状态:</span>
<span class="info-value status-online">在线</span>
</div>
</div>
`;
// 设置位置
infoCard.style.left = `${event.event.clientX}px`;
infoCard.style.top = `${event.event.clientY}px`;
document.querySelector('.earth-container').appendChild(infoCard);
// 添加关闭按钮事件
infoCard.querySelector('.info-card-close').addEventListener('click', function () {
infoCard.remove();
});
// 点击外部关闭
document.addEventListener('click', function closeCard(e) {
if (!infoCard.contains(e.target)) {
infoCard.remove();
document.removeEventListener('click', closeCard);
}
});
}
// 显示接收站信息(修改版本)
function showReceiverInfo(event) {
// 移除已存在的信息卡片
removeInfoCard();
// 创建信息卡片
const infoCard = document.createElement('div');
infoCard.className = 'info-card';
infoCard.innerHTML = `
<div class="info-card-header">
<h3 class="info-card-title">数据接收站</h3>
<button class="info-card-close">&times;</button>
</div>
<div class="info-card-content">
<div class="info-item">
<span class="info-label">名称:</span>
<span class="info-value">北京数据接收中心</span>
</div>
<div class="info-item">
<span class="info-label">位置:</span>
<span class="info-value">北纬 39.9042°<br>东经 116.4074°</span>
</div>
<div class="info-item">
<span class="info-label">状态:</span>
<span class="info-value status-online">在线</span>
</div>
<div class="info-item">
<span class="info-label">连接数:</span>
<span class="info-value">24</span>
</div>
<div class="info-item">
<span class="info-label">处理能力:</span>
<span class="info-value">1.2 TB/小时</span>
</div>
</div>
`;
// 设置位置(基于鼠标点击位置)
infoCard.style.left = `${event.event.clientX}px`;
infoCard.style.top = `${event.event.clientY}px`;
document.querySelector('.earth-container').appendChild(infoCard);
// 添加关闭按钮事件
infoCard.querySelector('.info-card-close').addEventListener('click', function () {
infoCard.remove();
});
// 点击外部关闭
document.addEventListener('click', function closeCard(e) {
if (!infoCard.contains(e.target) && e.target.closest('.info-card') !== infoCard) {
infoCard.remove();
document.removeEventListener('click', closeCard);
}
});
}
// 移除信息卡片
function removeInfoCard() {
const existingCard = document.querySelector('.info-card');
if (existingCard) {
existingCard.remove();
}
}
// 替换原有的 initUIEvents 函数
function initUIEvents() {
// 为每个过滤器选项添加点击事件处理
document.querySelectorAll('.filter-option').forEach(option => {
const checkbox = option.querySelector('input[type="checkbox"]');
const checkmark = option.querySelector('.checkmark');
const label = option.querySelector('label');
// 点击复选框本身
checkbox.addEventListener('change', function (e) {
filterDataSources();
});
// 点击自定义复选标记
checkmark.addEventListener('click', function (e) {
e.preventDefault();
checkbox.checked = !checkbox.checked;
filterDataSources();
});
// 点击标签
label.addEventListener('click', function (e) {
e.preventDefault();
checkbox.checked = !checkbox.checked;
filterDataSources();
});
});
}
// 过滤数据源
function filterDataSources() {
// 在这个实现中,过滤功能需要重新设置图表选项
// 这里简化处理,实际项目中可以根据过滤条件重新生成数据
console.log('数据源过滤功能已触发');
}
// 开始数据模拟
function startDataSimulation() {
if (!isSimulationRunning) return;
// 更新统计数据
updateStats();
// 添加新的数据项
addDataItem();
// 随机间隔后再次调用
setTimeout(startDataSimulation, 1000 + Math.random() * 2000);
}
// 修改 updateStats 函数
function updateStats() {
// 随机增加接收总量
const totalElement = document.getElementById('totalReceived');
if (totalElement) {
let total = parseInt(totalElement.textContent.replace(',', '')) || 0;
total += Math.floor(Math.random() * 5) + 1;
totalElement.textContent = total.toLocaleString();
}
// 更新接收速率(随机值)
const rateElement = document.getElementById('receiveRate');
if (rateElement) {
rateElement.textContent = `${Math.floor(200 + Math.random() * 200)}/分钟`;
}
// 更新在线数量(动态变化)
const onlineElement = document.getElementById('onlineSources');
if (onlineElement) {
// 获取当前在线数量
let currentOnline = parseInt(onlineElement.textContent) || dataPoints.length;
// 随机小幅变化±3以内
let change = Math.floor(Math.random() * 7) - 3; // -3 到 3 的随机数
// 确保在线数量在合理范围内最小80最大95
let newOnline = currentOnline + change;
newOnline = Math.max(80, Math.min(95, newOnline));
onlineElement.textContent = newOnline;
}
}
// 添加数据项到顶部列表(使用底部数据流的样式)
function addDataItem() {
const dataList = document.getElementById('dataList');
// 随机选择一个设备
const device = oceanDevices[Math.floor(Math.random() * oceanDevices.length)];
const param = device.parameters[Math.floor(Math.random() * device.parameters.length)];
// 生成参数的中文名称映射
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': '叶绿素',
'data_time': '数据时间', 'flow_velocity_e': '东向流速', 'flow_velocity_n': '北向流速',
'flow_velocity_v': '垂向流速', 'wave_height_1_10': '1/10波高', 'cycle_1_10': '1/10周期',
'wave_height_average': '平均波高', 'cycle_average': '平均周期', 'depth_f': '频率深度',
'depth_m': '压力深度', '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': '遭遇流速', 'height': '高度', 'wind_speed_realtime': '实时风速',
'wind_direction_realtime': '实时风向', 'wind_speed_average': '平均风速', 'wind_direction_average': '平均风向',
'wind_speed_max': '最大风速', 'wind_direction_max': '最大风向', 'time_wind_speed_max': '最大风速时间',
'temperature': '温度', 'pressure': '气压', 'rainfall_minute': '分钟降雨', 'rainfall_hour': '小时降雨',
'rainfall_day': '日降雨', 'status_sensor': '传感器状态', 'radiance': '辐射度',
'reliability_l': '水平可靠性', 'reliability_v': '垂直可靠性', 'cn2': '湍流结构常数',
'wind_speed_l': '水平风速', 'wind_direction_l': '水平风向', 'wind_speed_v': '垂直风速',
'azimuth_angle': '方位角', 'elevation': '仰角', 'cloud_height_2': '云高2', 'cloud_height_3': '云高3',
'cloud_height_4': '云高4', 'cloud_height_5': '云高5', 'cloud_thickness_1': '云厚1',
'cloud_thickness_2': '云厚2', 'cloud_thickness_3': '云厚3', 'cloud_thickness_4': '云厚4',
'cloud_thickness_5': '云厚5', 'POS惯导': 'POS惯导', 'GNSS': 'GNSS', '计程仪': '计程仪',
'电罗经': '电罗经', '测深仪': '测深仪', 'relative_humidity': '相对湿度'
};
// 生成值
const value = generateValue(param);
const timestamp = new Date().toLocaleTimeString('zh-CN', {
hour12: false,
hour: '2-digit',
minute: '2-digit',
second: '2-digit'
});
// 创建数据项
const dataItem = document.createElement('div');
dataItem.className = 'data-item';
// 设置容器的左边框颜色与设备颜色一致
dataItem.style.borderLeftColor = device.color;
// 添加数据属性以便点击时使用
dataItem.dataset.deviceName = device.name;
dataItem.dataset.deviceType = device.type;
dataItem.dataset.deviceId = device.id;
dataItem.dataset.param = param;
dataItem.dataset.value = value;
dataItem.dataset.timestamp = timestamp;
dataItem.dataset.position = `${device.position.lat}, ${device.position.lng}`;
dataItem.innerHTML = `
<div class="stream-device-info">
<div class="stream-device-icon">${getDeviceIcon(device.type)}</div>
<div class="stream-device-details">
<div class="stream-device-name">${device.name}</div>
<div class="stream-parameter">${paramNames[param] || param}</div>
</div>
</div>
<div class="stream-value-section">
<div class="stream-value" style="color: ${device.color}">${value}</div>
<div class="stream-timestamp">${timestamp}</div>
</div>
`;
// 添加点击事件
dataItem.addEventListener('click', function (e) {
showDataDetail(this, e);
});
// 添加到列表顶部
dataList.insertBefore(dataItem, dataList.firstChild);
// 限制列表项数量
if (dataList.children.length > 50) {
dataList.removeChild(dataList.lastChild);
}
}
// 生成值的函数
function generateValue(param) {
const valueRanges = {
'temp': () => (Math.random() * 30 + 5).toFixed(1) + '°C',
'temperature': () => (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',
'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',
'turbidity': () => (Math.random() * 10).toFixed(2) + 'NTU',
'chlorophyll': () => (Math.random() * 2).toFixed(2) + 'μg/L'
};
return valueRanges[param] ? valueRanges[param]() : (Math.random() * 100).toFixed(2);
}
// 获取设备图标
function 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] || '📊';
}
// 真实海洋设备数据配置
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 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';
}
// 显示数据详情面板
function showDataDetail(dataItem, event) {
// 移除已存在的详情面板
const existingDetail = document.querySelector('.data-detail-panel');
if (existingDetail) {
existingDetail.remove();
}
// 获取数据
const deviceName = dataItem.dataset.deviceName;
const deviceType = dataItem.dataset.deviceType;
const deviceId = dataItem.dataset.deviceId;
const param = dataItem.dataset.param;
const value = dataItem.dataset.value;
const timestamp = dataItem.dataset.timestamp;
const position = dataItem.dataset.position;
const device = oceanDevices.find(d => d.id === deviceId);
// 创建详情面板
const detailPanel = document.createElement('div');
detailPanel.className = 'data-detail-panel';
// 设置面板位置在点击元素的左侧
const rect = dataItem.getBoundingClientRect();
const containerRect = document.querySelector('.container').getBoundingClientRect();
detailPanel.style.position = 'absolute';
detailPanel.style.top = rect.top - containerRect.top + 'px';
detailPanel.style.left = (rect.left - containerRect.left - 340) + 'px'; // 320px是面板宽度+间距
detailPanel.style.zIndex = '1000';
detailPanel.style.width = '300px';
detailPanel.innerHTML = `
<div class="detail-header">
<h3 class="detail-title">数据详情</h3>
<button class="close-btn" id="closeDetail">&times;</button>
</div>
<div class="detail-content">
<div class="detail-item">
<span class="detail-label">设备名称:</span>
<span class="detail-value">${deviceName}</span>
</div>
<div class="detail-item">
<span class="detail-label">设备类型:</span>
<span class="detail-value">${deviceType}</span>
</div>
<div class="detail-item">
<span class="detail-label">设备ID:</span>
<span class="detail-value">${deviceId}</span>
</div>
<div class="detail-item">
<span class="detail-label">参数:</span>
<span class="detail-value">${param}</span>
</div>
<div class="detail-item">
<span class="detail-label">数值:</span>
<span class="detail-value" style="color: ${device ? device.color : '#4fc3f7'}; font-weight: bold;">${value}</span>
</div>
<div class="detail-item">
<span class="detail-label">时间:</span>
<span class="detail-value">${timestamp}</span>
</div>
<div class="detail-item">
<span class="detail-label">位置:</span>
<span class="detail-value">${position}</span>
</div>
<div class="detail-item">
<span class="detail-label">状态:</span>
<span class="detail-value status-online">在线</span>
</div>
</div>
`;
// 添加到容器中
document.querySelector('.container').appendChild(detailPanel);
// 添加关闭按钮事件
detailPanel.querySelector('#closeDetail').addEventListener('click', function () {
detailPanel.remove();
});
// 点击面板外部关闭
document.addEventListener('click', function closePanel(e) {
if (!detailPanel.contains(e.target) && e.target !== dataItem) {
detailPanel.remove();
document.removeEventListener('click', closePanel);
}
});
}
// 添加样式到页面
const style = document.createElement('style');
style.textContent = `
.data-detail-panel {
background: rgba(16, 36, 62, 0.95);
backdrop-filter: blur(10px);
border-radius: 10px;
padding: 15px;
box-shadow: 0 5px 20px rgba(0, 0, 0, 0.4);
border: 1px solid rgba(64, 156, 255, 0.3);
}
.detail-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 15px;
border-bottom: 1px solid rgba(79, 195, 247, 0.3);
padding-bottom: 10px;
}
.detail-title {
font-size: 1.2rem;
color: #4fc3f7;
margin: 0;
}
.close-btn {
background: none;
border: none;
color: #a0d2ff;
font-size: 1.5rem;
cursor: pointer;
padding: 0;
width: 30px;
height: 30px;
display: flex;
align-items: center;
justify-content: center;
}
.close-btn:hover {
color: #ffffff;
}
.detail-content {
font-size: 0.9rem;
}
.detail-item {
margin: 10px 0;
display: flex;
}
.detail-label {
width: 80px;
color: #a0d2ff;
font-weight: bold;
}
.detail-value {
flex: 1;
color: #e0f0ff;
word-break: break-all;
}
.status-online {
color: #4caf50;
font-weight: bold;
}
@media (max-width: 1200px) {
.data-detail-panel {
width: 250px !important;
left: 10px !important;
}
}
`;
document.head.appendChild(style);
// 页面加载完成后初始化
window.addEventListener('DOMContentLoaded', init);
</script>
</body>
</html>