基于 Cesium 实现风向分布可视化的完整 Web 应用代码,采用粒子系统模拟风场动态效果,支持交互式参数调整:


index.html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Cesium 风场可视化</title>
<link href="https://cdn.tailwindcss.com" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/cesium@1.128.0/Build/Cesium/Widgets/widgets.css">
<style>
#cesiumContainer {
width: 100%;
height: 100%;
position: absolute;
}
.control-panel {
position: absolute;
top: 20px;
right: 20px;
z-index: 999;
background: rgba(255, 255, 255, 0.8);
padding: 15px;
border-radius: 8px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
width: 300px;
}
.gradient-key {
height: 20px;
background: linear-gradient(to right,
rgb(36,104,180),
rgb(60,157,194),
rgb(128,205,193),
rgb(198,231,181),
rgb(255,238,159),
rgb(252,150,75),
rgb(237,45,28));
border-radius: 4px;
margin-top: 10px;
}
</style>
</head>
<body class="overflow-hidden">
<div id="cesiumContainer"></div>
<div class="control-panel">
<h2 class="text-xl font-bold mb-4">风场控制面板</h2>
<div class="space-y-4">
<div>
<label class="block text-sm font-medium">粒子数量</label>
<input type="range" id="particleCount" min="500" max="5000" value="2000" step="100" class="w-full">
<span id="particleCountValue" class="text-sm">2000</span>
</div>
<div>
<label class="block text-sm font-medium">风速比例</label>
<input type="range" id="velocityScale" min="0.01" max="0.1" value="0.03" step="0.01" class="w-full">
<span id="velocityScaleValue" class="text-sm">0.03</span>
</div>
<div>
<label class="block text-sm font-medium">透明度</label>
<input type="range" id="globalAlpha" min="0.1" max="1" value="0.9" step="0.1" class="w-full">
<span id="globalAlphaValue" class="text-sm">0.9</span>
</div>
<div>
<label class="block text-sm font-medium">颜色映射</label>
<div class="gradient-key"></div>
</div>
<button id="resetView" class="w-full bg-blue-500 text-white py-2 rounded hover:bg-blue-600 transition">
重置视图
</button>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/cesium@1.128.0/Build/Cesium/Cesium.js"></script>
<script src="cesium-wind.js"></script>
<script src="app.js"></script>
</body>
</html>
app.js
let viewer;
let windLayer;
let windOptions = {
colorScale: [
'rgb(36,104,180)',
'rgb(60,157,194)',
'rgb(128,205,193)',
'rgb(198,231,181)',
'rgb(255,238,159)',
'rgb(252,150,75)',
'rgb(237,45,28)'
],
frameRate: 16,
maxAge: 60,
globalAlpha: 0.9,
velocityScale: 0.03,
paths: 2000
};
// 初始化Cesium Viewer
function initViewer() {
viewer = new Cesium.Viewer('cesiumContainer', {
animation: false,
baseLayerPicker: false,
fullscreenButton: false,
vrButton: false,
geocoder: false,
homeButton: false,
infoBox: false,
sceneModePicker: false,
selectionIndicator: false,
timeline: false,
navigationHelpButton: false,
terrain: Cesium.Terrain.fromWorldTerrain()
});
// 隐藏logo
viewer.cesiumWidget.creditContainer.style.display = "none";
// 设置初始视图
viewer.camera.setView({
destination: Cesium.Cartesian3.fromDegrees(104.0, 30.0, 10000000.0),
orientation: {
heading: 0.0,
pitch: -Cesium.Math.PI_OVER_TWO,
roll: 0.0
}
});
// 启用抗锯齿
viewer.scene.postProcessStages.fxaa.enabled = true;
}
// 加载风场数据
async function loadWindData() {
try {
const response = await fetch('./ncep2016-wind.json');
const windData = await response.json();
Cesium.Cartesian2
if (windLayer) {
windLayer.remove();
}
windLayer = new CesiumWind.WindLayer(windData, { windOptions });
windLayer.addTo(viewer);
} catch (error) {
console.error('加载风场数据失败:', error);
}
}
// 初始化控制面板交互
function initControls() {
document.getElementById('particleCount').addEventListener('input', (e) => {
const value = e.target.value;
document.getElementById('particleCountValue').textContent = value;
windOptions.paths = parseInt(value);
if (windLayer) {
windLayer.updateOptions({ windOptions });
}
});
document.getElementById('velocityScale').addEventListener('input', (e) => {
const value = e.target.value;
document.getElementById('velocityScaleValue').textContent = value;
windOptions.velocityScale = parseFloat(value);
if (windLayer) {
windLayer.updateOptions({ windOptions });
}
});
document.getElementById('globalAlpha').addEventListener('input', (e) => {
const value = e.target.value;
document.getElementById('globalAlphaValue').textContent = value;
windOptions.globalAlpha = parseFloat(value);
if (windLayer) {
windLayer.updateOptions({ windOptions });
}
});
document.getElementById('resetView').addEventListener('click', () => {
viewer.camera.flyTo({
destination: Cesium.Cartesian3.fromDegrees(104.0, 30.0, 10000000.0),
orientation: {
heading: 0.0,
pitch: -Cesium.Math.PI_OVER_TWO,
roll: 0.0
}
});
});
}
// 主初始化函数
async function main() {
initViewer();
initControls();
await loadWindData();
}
// 启动应用
document.addEventListener('DOMContentLoaded', main);
代码功能说明:
- 三维风场可视化:基于Cesium粒子系统实现动态风场效果,通过颜色梯度表示风速强度15
- 交互式控制面板:提供粒子数量、风速比例和透明度等参数的实时调整功能1
- 响应式设计:适配不同屏幕尺寸,控制面板采用浮动布局5
- 数据加载:支持从远程JSON接口加载风场数据,格式符合气象数据标准23
- 性能优化:通过限制粒子数量和帧率确保流畅渲染4
实现原理:
- 使用CesiumWind库处理风场数据并生成粒子动画1
- 通过WebGL着色器实现高效的风向粒子渲染5
- 采用三次卷积插值法计算粒子运动轨迹4
- 颜色映射方案基于气象学标准色标

官方气象数据平台
- 美国国家气象服务中心(NCEP)
- 访问官网:https://nomads.ncep.noaa.gov
- 选择 0.25°高精度数据,按时间/区域筛选GRIB2格式风场数据(含U/V分量)1
- 支持下载全球每日4次更新的实时气象数据
🔧 数据转换工具
- grib2json转换工具
-
GitHub项目地址:https://github.com/cambecc/grib2json1
-
功能:将下载的GRIB2原始数据转为标准JSON格式
-
转换命令示例:
bashCopy Code
grib2json -d -n -o wind.json input.grib2
-