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.

695 lines
23 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://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>
<!-- 确保这些文件路径正确 -->
<script src="./model/mars3d-cesium/Build/Cesium/Cesium.js"></script>
<link href="./model/mars3d-cesium/Build/Cesium/Widgets/widgets.css" rel="stylesheet">
<script src="./model/mars3d/mars3d.js"></script>
<link href="./model/mars3d/mars3d.css" rel="stylesheet">
<!-- Mars3D热力图插件 - 使用正确的CDN路径 -->
<script src="https://cdn.jsdelivr.net/npm/mars3d-plugin-heatmap/dist/mars3d-plugin-heatmap.min.js"></script>
<style>
/* 保持原有样式不变 */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: 'Segoe UI', Arial, sans-serif;
}
body {
background: linear-gradient(135deg, #0c1a2d 0%, #1a3a5f 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;
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: 3vw;
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;
}
.container {
display: flex;
height: calc(100vh - 8vh - 20px);
position: relative;
margin-bottom: 20px;
}
/* 地球容器样式 */
.earth-container {
flex: 1;
height: 100%;
position: relative;
background: #0c1a2d;
}
#mars3dMap {
width: 100%;
height: 100%;
display: block;
}
/* 右侧控制面板 */
.control-panel {
width: 300px;
height: 100%;
padding: 20px;
background: rgba(12, 42, 73, 0.7);
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: 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;
}
/* 控制组样式 */
.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);
}
.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);
}
/* 按钮样式 */
.btn-group {
display: flex;
gap: 10px;
margin-bottom: 15px;
}
.btn {
padding: 8px 15px;
background: rgba(25, 55, 95, 0.6);
border: 1px solid rgba(79, 195, 247, 0.5);
border-radius: 4px;
color: #e0f0ff;
cursor: pointer;
transition: all 0.3s ease;
flex: 1;
text-align: center;
}
.btn:hover {
background: rgba(79, 195, 247, 0.2);
}
.btn.active {
background: rgba(79, 195, 247, 0.5);
border-color: #4fc3f7;
}
/* 滑块样式 */
.slider-container {
margin-bottom: 15px;
}
.slider-label {
display: flex;
justify-content: space-between;
margin-bottom: 5px;
font-size: 0.9rem;
}
.slider-name {
color: #a0d2ff;
}
.slider-value {
color: #4fc3f7;
}
.slider {
width: 100%;
height: 8px;
background: rgba(25, 55, 95, 0.6);
border-radius: 4px;
outline: none;
-webkit-appearance: none;
}
.slider::-webkit-slider-thumb {
-webkit-appearance: none;
width: 18px;
height: 18px;
border-radius: 50%;
background: #4fc3f7;
cursor: pointer;
}
/* 图例样式 */
.legend {
margin-top: 20px;
padding: 10px;
background: rgba(25, 55, 95, 0.6);
border-radius: 8px;
}
.legend-title {
font-size: 1rem;
color: #4fc3f7;
margin-bottom: 10px;
text-align: center;
}
.legend-gradient {
height: 20px;
background: linear-gradient(to right, blue, cyan, green, yellow, red);
border-radius: 4px;
margin-bottom: 8px;
}
.legend-labels {
display: flex;
justify-content: space-between;
font-size: 0.8rem;
color: #a0d2ff;
}
/* 热力图信息面板 */
.heatmap-info {
margin-top: 15px;
padding: 10px;
background: rgba(25, 55, 95, 0.6);
border-radius: 8px;
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;
}
</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="earth-container">
<div id="mars3dMap"></div>
</div>
<!-- 右侧控制面板 -->
<div class="control-panel">
<div class="card-header">
<span>热力图控制面板</span>
</div>
<div class="control-group">
<div class="control-title">热力图类型</div>
<div class="btn-group">
<div class="btn active" data-type="temperature">海水温度</div>
<div class="btn" data-type="oxygen">溶解氧</div>
<div class="btn" data-type="chlorophyll">叶绿素</div>
</div>
</div>
<div class="control-group">
<div class="control-title">热力图参数</div>
<div class="slider-container">
<div class="slider-label">
<span class="slider-name">透明度</span>
<span class="slider-value" id="opacity-value">0.7</span>
</div>
<input type="range" min="0" max="1" step="0.1" value="0.7" class="slider" id="opacity-slider">
</div>
<div class="slider-container">
<div class="slider-label">
<span class="slider-name">半径</span>
<span class="slider-value" id="radius-value">25</span>
</div>
<input type="range" min="10" max="50" step="1" value="25" class="slider" id="radius-slider">
</div>
<div class="slider-container">
<div class="slider-label">
<span class="slider-name">模糊度</span>
<span class="slider-value" id="blur-value">0.8</span>
</div>
<input type="range" min="0" max="1" step="0.1" value="0.8" class="slider" id="blur-slider">
</div>
</div>
<div class="control-group">
<div class="control-title">图例</div>
<div class="legend">
<div class="legend-title">数值强度</div>
<div class="legend-gradient"></div>
<div class="legend-labels">
<span></span>
<span></span>
<span></span>
</div>
</div>
</div>
<div class="control-group">
<div class="control-title">热力图信息</div>
<div class="heatmap-info">
<div class="info-item">
<span class="info-label">当前类型:</span>
<span class="info-value" id="current-type">海水温度</span>
</div>
<div class="info-item">
<span class="info-label">数据点数:</span>
<span class="info-value" id="data-count">256</span>
</div>
<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" id="update-time">15:18:00</span>
</div>
</div>
</div>
</div>
</div>
<script>
// 全局变量
let map;
let heatmapLayer;
let currentHeatmapType = 'temperature';
// 连云港近海坐标范围
const lianyungangBounds = {
minLng: 118.5,
maxLng: 120.0,
minLat: 34.0,
maxLat: 35.2
};
// 热力图数据
const heatmapData = {
temperature: generateHeatmapData(18, 28), // 温度范围18-28°C
oxygen: generateHeatmapData(5, 9), // 溶解氧范围5-9mg/L
chlorophyll: generateHeatmapData(2, 8) // 叶绿素范围2-8μg/L
};
// 初始化页面
document.addEventListener('DOMContentLoaded', function () {
initDateTime();
initMap();
initHeatmap();
initEventListeners();
});
// 初始化日期时间
function initDateTime() {
function updateDateTime() {
const now = new Date();
const dateStr = now.getFullYear() + '年' +
(now.getMonth() + 1) + '月' +
now.getDate() + '日';
const timeStr = now.toTimeString().substr(0, 8);
document.getElementById('current-date').textContent = dateStr;
document.getElementById('current-time').textContent = timeStr;
document.getElementById('update-time').textContent = timeStr;
}
updateDateTime();
setInterval(updateDateTime, 1000);
}
// 初始化地图
function initMap() {
// 创建地图
const mapOptions = {
basemaps: [
{
name: '电子地图',
type: 'tdt',
layer: 'vec',
show: true
},
{
name: '影像地图',
type: 'tdt',
layer: 'img',
show: false
}
],
scene: {
center: { lat: 34.6, lng: 119.2, alt: 80000, heading: 0, pitch: -45 },
showSun: true,
showMoon: true,
showSkyBox: true,
showSkyAtmosphere: true,
fog: true,
fxaa: true,
globe: {
showGroundAtmosphere: true,
depthTestAgainstTerrain: false
},
cameraController: {
enableZoom: true,
enableRotate: true,
enableTilt: true,
enableTranslate: true
}
},
control: {
baseLayerPicker: true,
homeButton: true,
sceneModePicker: true,
navigationHelpButton: true,
fullscreenButton: true
}
};
// 初始化地图
map = new mars3d.Map('mars3dMap', mapOptions);
}
// 初始化热力图
function initHeatmap() {
// 检查热力图插件是否可用
if (typeof mars3d.layer.HeatLayer === 'undefined') {
console.error('热力图插件未正确加载,使用备用方案');
createCustomHeatmap();
return;
}
// 创建热力图图层
heatmapLayer = new mars3d.layer.HeatLayer({
name: '生态热力图',
// 热力图样式配置
style: {
radius: 25,
opacity: 0.7,
blur: 0.8,
gradient: {
0.0: 'blue',
0.2: 'cyan',
0.4: 'green',
0.6: 'yellow',
0.8: 'orange',
1.0: 'red'
}
}
});
// 添加热力图图层到地图
map.addLayer(heatmapLayer);
// 加载初始数据
updateHeatmapData(currentHeatmapType);
}
// 备用方案:创建自定义热力图效果
function createCustomHeatmap() {
console.log('使用自定义热力图实现');
// 使用点图层模拟热力图效果
heatmapLayer = new mars3d.layer.GraphicLayer({
name: '生态热力图'
});
map.addLayer(heatmapLayer);
// 加载初始数据
updateHeatmapData(currentHeatmapType);
}
// 生成热力图数据
function generateHeatmapData(minValue, maxValue) {
const data = [];
const pointCount = 256; // 数据点数量
for (let i = 0; i < pointCount; i++) {
// 在连云港范围内随机生成坐标
const lng = lianyungangBounds.minLng +
Math.random() * (lianyungangBounds.maxLng - lianyungangBounds.minLng);
const lat = lianyungangBounds.minLat +
Math.random() * (lianyungangBounds.maxLat - lianyungangBounds.minLat);
// 生成随机值,但模拟一些热点区域
let value;
const hotspotChance = Math.random();
if (hotspotChance < 0.1) {
// 10%的概率生成热点(高值)
value = minValue + 0.7 * (maxValue - minValue) +
Math.random() * 0.3 * (maxValue - minValue);
} else if (hotspotChance < 0.2) {
// 10%的概率生成冷点(低值)
value = minValue + Math.random() * 0.3 * (maxValue - minValue);
} else {
// 80%的概率生成正常值
value = minValue + Math.random() * (maxValue - minValue);
}
data.push({
lng: lng,
lat: lat,
value: value
});
}
return data;
}
// 更新热力图数据
function updateHeatmapData(type) {
if (!heatmapLayer || !heatmapData[type]) return;
// 清除现有数据
heatmapLayer.clear();
// 如果是自定义热力图实现
if (heatmapLayer.constructor.name === 'GraphicLayer') {
// 使用点图层模拟热力图
const data = heatmapData[type];
const minValue = Math.min(...data.map(d => d.value));
const maxValue = Math.max(...data.map(d => d.value));
data.forEach(point => {
// 根据数值大小计算颜色和大小
const normalizedValue = (point.value - minValue) / (maxValue - minValue);
const color = getColorByValue(normalizedValue);
const size = 8 + normalizedValue * 12;
const graphic = new mars3d.graphic.PointEntity({
position: [point.lng, point.lat],
style: {
pixelSize: size,
color: color,
outlineColor: '#ffffff',
outlineWidth: 1,
opacity: 0.7
},
attr: point
});
heatmapLayer.addGraphic(graphic);
});
} else {
// 使用真正的热力图图层
heatmapLayer.setData(heatmapData[type]);
}
// 更新信息面板
document.getElementById('current-type').textContent = getHeatmapTypeName(type);
document.getElementById('data-count').textContent = heatmapData[type].length;
// 更新按钮状态
document.querySelectorAll('.btn-group .btn').forEach(btn => {
if (btn.dataset.type === type) {
btn.classList.add('active');
} else {
btn.classList.remove('active');
}
});
console.log(`已切换到${getHeatmapTypeName(type)}热力图`);
}
// 根据数值获取颜色
function getColorByValue(value) {
if (value < 0.2) return '#0000ff'; // 蓝色
if (value < 0.4) return '#00ffff'; // 青色
if (value < 0.6) return '#00ff00'; // 绿色
if (value < 0.8) return '#ffff00'; // 黄色
return '#ff0000'; // 红色
}
// 获取热力图类型名称
function getHeatmapTypeName(type) {
const names = {
temperature: '海水温度',
oxygen: '溶解氧',
chlorophyll: '叶绿素'
};
return names[type] || '未知类型';
}
// 初始化事件监听器
function initEventListeners() {
// 热力图类型切换
document.querySelectorAll('.btn-group .btn').forEach(btn => {
btn.addEventListener('click', function() {
const type = this.dataset.type;
currentHeatmapType = type;
updateHeatmapData(type);
});
});
// 透明度滑块
const opacitySlider = document.getElementById('opacity-slider');
const opacityValue = document.getElementById('opacity-value');
opacitySlider.addEventListener('input', function() {
const value = this.value;
opacityValue.textContent = value;
if (heatmapLayer && heatmapLayer.style) {
heatmapLayer.style.opacity = parseFloat(value);
}
});
// 半径滑块
const radiusSlider = document.getElementById('radius-slider');
const radiusValue = document.getElementById('radius-value');
radiusSlider.addEventListener('input', function() {
const value = this.value;
radiusValue.textContent = value;
if (heatmapLayer && heatmapLayer.style) {
heatmapLayer.style.radius = parseInt(value);
}
});
// 模糊度滑块
const blurSlider = document.getElementById('blur-slider');
const blurValue = document.getElementById('blur-value');
blurSlider.addEventListener('input', function() {
const value = this.value;
blurValue.textContent = value;
if (heatmapLayer && heatmapLayer.style) {
heatmapLayer.style.blur = parseFloat(value);
}
});
}
</script>
</body>
</html>