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

<!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>