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

相关推荐
lichenyang45316 分钟前
从0开始的中后台管理系统-5(userList页面功能实现)
前端·javascript·vue.js
熊猫钓鱼>_>1 小时前
腾讯云EdgeOne Pages深度使用指南
javascript·云计算·腾讯云
胡gh1 小时前
什么是瀑布流?用大白话给你讲明白!
前端·javascript·面试
teeeeeeemo1 小时前
一些js数组去重的实现算法
开发语言·前端·javascript·笔记·算法
掘金安东尼1 小时前
前端周刊第426期(2025年8月4日–8月10日)
前端·javascript·面试
Abadbeginning1 小时前
FastSoyAdmin导出excel报错‘latin-1‘ codec can‘t encode characters in position 41-54
前端·javascript·后端
Mintopia2 小时前
🎭《哈姆雷特》如果会写 React:useChat 自定义 Hook 的 AI 炼金术
前端·javascript·aigc
汪子熙2 小时前
引起 Angular NG0205 错误的一种可能的原因
前端·javascript
我叫黑大帅3 小时前
前端单词查询功能是怎么搞出来的?😂
前端·javascript