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.

359 lines
13 KiB
HTML

This file contains ambiguous Unicode characters!

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

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>风速剖面三维图</title>
<style>
body {
margin: 0;
overflow: hidden;
font-family: Arial, sans-serif;
background: linear-gradient(to bottom, #1a237e, #283593);
color: white;
}
#container {
position: relative;
width: 100vw;
height: 100vh;
}
#info-panel {
position: absolute;
top: 20px;
left: 20px;
background: rgba(0, 0, 0, 0.7);
padding: 15px;
border-radius: 8px;
max-width: 300px;
z-index: 100;
}
#controls {
position: absolute;
bottom: 20px;
left: 20px;
background: rgba(0, 0, 0, 0.7);
padding: 15px;
border-radius: 8px;
z-index: 100;
}
button {
background: #3949ab;
color: white;
border: none;
padding: 8px 15px;
margin: 5px;
border-radius: 4px;
cursor: pointer;
}
button:hover {
background: #5c6bc0;
}
.axis-label {
position: absolute;
color: white;
font-size: 14px;
z-index: 10;
}
#time-label {
bottom: 50px;
left: 50%;
transform: translateX(-50%);
}
#height-label {
top: 50%;
left: 30px;
transform: translateY(-50%) rotate(-90deg);
}
#wind-label {
top: 20px;
right: 30px;
}
.legend {
position: absolute;
right: 20px;
bottom: 100px;
background: rgba(0, 0, 0, 0.7);
padding: 10px;
border-radius: 8px;
width: 150px;
}
.legend-item {
display: flex;
align-items: center;
margin: 5px 0;
}
.legend-color {
width: 20px;
height: 20px;
margin-right: 10px;
border-radius: 3px;
}
</style>
</head>
<body>
<div id="container">
<div id="info-panel">
<h3>风速剖面数据</h3>
<p>日期: 2018-10-05 03:00:00</p>
<p>数据来源: CMA</p>
<p>参数: CAMP, MULTI, BRAZY, HONOR, PERSONAL, SERVICE, VOLUME, VARIABILITY</p>
</div>
<div id="controls">
<button id="reset-view">重置视图</button>
<button id="auto-rotate">自动旋转</button>
<button id="toggle-labels">切换标签</button>
</div>
<div class="axis-label" id="time-label">时间轴</div>
<div class="axis-label" id="height-label">高度轴 (m)</div>
<div class="axis-label" id="wind-label">风速 (m/s)</div>
<div class="legend">
<h4>风速图例</h4>
<div class="legend-item">
<div class="legend-color" style="background-color: #00b0ff;"></div>
<span>0-5 m/s</span>
</div>
<div class="legend-item">
<div class="legend-color" style="background-color: #00e676;"></div>
<span>5-10 m/s</span>
</div>
<div class="legend-item">
<div class="legend-color" style="background-color: #ffeb3b;"></div>
<span>10-15 m/s</span>
</div>
<div class="legend-item">
<div class="legend-color" style="background-color: #ff9800;"></div>
<span>15-20 m/s</span>
</div>
<div class="legend-item">
<div class="legend-color" style="background-color: #f44336;"></div>
<span>20+ m/s</span>
</div>
</div>
</div>
<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.js"></script>
<script>
// 初始化场景
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x1a237e);
// 初始化相机 - 调整视角以更好地查看贴地效果
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.set(20, 15, 30);
// 初始化渲染器
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(window.devicePixelRatio);
document.getElementById('container').appendChild(renderer.domElement);
// 添加轨道控制器
const controls = new THREE.OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
controls.dampingFactor = 0.05;
// 添加光源
const ambientLight = new THREE.AmbientLight(0xffffff, 0.6);
scene.add(ambientLight);
const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8);
directionalLight.position.set(50, 50, 50);
scene.add(directionalLight);
// 创建坐标轴
const axesHelper = new THREE.AxesHelper(20);
scene.add(axesHelper);
// 生成模拟风速数据
function generateWindData() {
const data = [];
const timePoints = 24; // 24小时
const heightPoints = 20; // 20个高度点
for (let t = 0; t < timePoints; t++) {
const timeData = [];
for (let h = 0; h < heightPoints; h++) {
// 模拟风速数据,随时间和高度变化
const baseWind = 5 + Math.sin(t / 3) * 3;
const heightEffect = h * 0.5;
const turbulence = Math.sin(t * 0.5 + h * 0.3) * 2;
const windSpeed = baseWind + heightEffect + turbulence;
timeData.push(Math.max(0, windSpeed));
}
data.push(timeData);
}
return data;
}
// 根据风速获取颜色
function getWindColor(speed) {
if (speed < 5) return new THREE.Color(0x00b0ff);
if (speed < 10) return new THREE.Color(0x00e676);
if (speed < 15) return new THREE.Color(0xffeb3b);
if (speed < 20) return new THREE.Color(0xff9800);
return new THREE.Color(0xf44336);
}
// 创建风速剖面可视化
function createWindProfile(data) {
const group = new THREE.Group();
// 创建地面网格平面作为参考
const gridHelper = new THREE.GridHelper(30, 24, 0x444444, 0x222222);
scene.add(gridHelper);
// 创建风速曲面
const geometry = new THREE.BufferGeometry();
const vertices = [];
const colors = [];
const indices = [];
const timePoints = data.length;
const heightPoints = data[0].length;
// 创建顶点和颜色 - 调整坐标系使图形贴合地面
for (let t = 0; t < timePoints; t++) {
for (let h = 0; h < heightPoints; h++) {
const x = t - timePoints / 2; // 时间轴
const z = h * 1.5; // 高度轴 (沿Z轴向上)
const y = data[t][h] * 0.5; // 风速轴 (沿Y轴延伸贴近地面)
vertices.push(x, y, z);
const color = getWindColor(data[t][h]);
colors.push(color.r, color.g, color.b);
}
}
// 创建索引(三角形)
for (let t = 0; t < timePoints - 1; t++) {
for (let h = 0; h < heightPoints - 1; h++) {
const a = t * heightPoints + h;
const b = t * heightPoints + h + 1;
const c = (t + 1) * heightPoints + h;
const d = (t + 1) * heightPoints + h + 1;
indices.push(a, b, d);
indices.push(a, d, c);
}
}
geometry.setIndex(indices);
geometry.setAttribute('position', new THREE.Float32BufferAttribute(vertices, 3));
geometry.setAttribute('color', new THREE.Float32BufferAttribute(colors, 3));
// 计算法向量以获得更好的光照效果
geometry.computeVertexNormals();
const material = new THREE.MeshLambertMaterial({
vertexColors: true,
side: THREE.DoubleSide,
transparent: true,
opacity: 0.8
});
const mesh = new THREE.Mesh(geometry, material);
group.add(mesh);
// 添加数据点标记
for (let t = 0; t < timePoints; t += 3) {
for (let h = 0; h < heightPoints; h += 3) {
const x = t - timePoints / 2;
const z = h * 1.5;
const y = data[t][h] * 0.5;
const sphereGeometry = new THREE.SphereGeometry(0.2, 8, 8);
const sphereMaterial = new THREE.MeshBasicMaterial({
color: getWindColor(data[t][h])
});
const sphere = new THREE.Mesh(sphereGeometry, sphereMaterial);
sphere.position.set(x, y, z);
group.add(sphere);
}
}
scene.add(group);
return group;
}
// 生成数据并创建可视化
const windData = generateWindData();
const windProfile = createWindProfile(windData);
// 添加坐标轴标签
function addAxisLabels() {
// 时间轴标签
for (let i = 0; i <= 24; i += 6) {
const text = document.createElement('div');
text.className = 'axis-label';
text.style.position = 'absolute';
text.style.bottom = '30px';
text.style.left = `${50 + (i - 12) * 10}px`;
text.textContent = `${i}:00`;
document.getElementById('container').appendChild(text);
}
// 高度轴标签
for (let i = 0; i <= 30; i += 10) {
const text = document.createElement('div');
text.className = 'axis-label';
text.style.position = 'absolute';
text.style.top = `${50 + i * 5}px`;
text.style.left = '10px';
text.textContent = `${i * 100}m`;
document.getElementById('container').appendChild(text);
}
}
addAxisLabels();
// 动画循环
function animate() {
requestAnimationFrame(animate);
controls.update();
renderer.render(scene, camera);
}
animate();
// 窗口大小调整
window.addEventListener('resize', () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
});
// 控制按钮功能
document.getElementById('reset-view').addEventListener('click', () => {
controls.reset();
});
let autoRotate = false;
document.getElementById('auto-rotate').addEventListener('click', () => {
autoRotate = !autoRotate;
controls.autoRotate = autoRotate;
document.getElementById('auto-rotate').textContent =
autoRotate ? '停止旋转' : '自动旋转';
});
let labelsVisible = true;
document.getElementById('toggle-labels').addEventListener('click', () => {
labelsVisible = !labelsVisible;
const labels = document.querySelectorAll('.axis-label');
labels.forEach(label => {
if (!label.id) { // 只切换数据标签,不切换轴标题
label.style.display = labelsVisible ? 'block' : 'none';
}
});
document.getElementById('toggle-labels').textContent =
labelsVisible ? '隐藏标签' : '显示标签';
});
</script>
</body>
</html>