|
|
<!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">×</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">×</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">×</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">×</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> |