Cesium粒子系统模拟风场动态效果

基于 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);

代码功能说明:

  1. 三维风场可视化‌:基于Cesium粒子系统实现动态风场效果,通过颜色梯度表示风速强度15
  2. 交互式控制面板‌:提供粒子数量、风速比例和透明度等参数的实时调整功能1
  3. 响应式设计‌:适配不同屏幕尺寸,控制面板采用浮动布局5
  4. 数据加载‌:支持从远程JSON接口加载风场数据,格式符合气象数据标准23
  5. 性能优化‌:通过限制粒子数量和帧率确保流畅渲染4

实现原理:

  • 使用CesiumWind库处理风场数据并生成粒子动画1
  • 通过WebGL着色器实现高效的风向粒子渲染5
  • 采用三次卷积插值法计算粒子运动轨迹4
  • 颜色映射方案基于气象学标准色标

官方气象数据平台

  1. 美国国家气象服务中心(NCEP)
    • 访问官网:https://nomads.ncep.noaa.gov
    • 选择 ‌0.25°高精度数据‌,按时间/区域筛选GRIB2格式风场数据(含U/V分量)1
    • 支持下载全球每日4次更新的实时气象数据

🔧 数据转换工具

  1. grib2json转换工具
    • GitHub项目地址:https://github.com/cambecc/grib2json1

    • 功能:将下载的GRIB2原始数据转为标准JSON格式

    • 转换命令示例:

      复制代码

      bashCopy Code

      grib2json -d -n -o wind.json input.grib2

相关推荐
zandy101116 小时前
衡石 Agentic BI的ReAct 推理框架在 Agentic BI 中的工程化实践
前端·javascript·react.js
郝亚军17 小时前
webstorm如何创建vue 3.js
javascript·vue.js·webstorm
এ慕ོ冬℘゜17 小时前
jQuery attr() 方法超详细讲解:属性获取、赋值、实战踩坑全解
前端·javascript·jquery
Hilaku18 小时前
为什么大厂对前端算法要求极高?
前端·javascript·程序员
你可如意18 小时前
从Promise开始,一篇让你彻底建立起JS的异步编程模型
javascript
用户842981424181019 小时前
AI时代,JS混淆加密还有用吗?
前端·javascript
用户831348593069819 小时前
Cesium 实现行政区内部遮罩
vue.js·webgl·cesium
申君健248641827720219 小时前
MCP TypeScript SDK v2 完整升级变化说明
javascript
Ivanqhz20 小时前
Rust Lazy浅析
java·javascript·rust
铁皮饭盒21 小时前
面试官:如何用 Bun + JS 实现安全的文件 MCP 工具集
前端·javascript·后端