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

相关推荐
一只爱吃糖的小羊16 小时前
从 AnyScript 到 TypeScript:如何利用 Type Guards 与 Type Predicates 实现精准的类型锁死
前端·javascript·typescript
持续升级打怪中17 小时前
ES6 Promise 完全指南:从入门到精通
前端·javascript·es6
wulijuan88866617 小时前
Web Worker
前端·javascript
老朋友此林17 小时前
React Hook原理速通笔记1(useEffect 原理、使用踩坑、渲染周期、依赖项)
javascript·笔记·react.js
克里斯蒂亚诺更新17 小时前
vue3使用pinia替代vuex举例
前端·javascript·vue.js
冰暮流星17 小时前
javascript赋值运算符
开发语言·javascript·ecmascript
grasperp17 小时前
3DTiles数据切片工具,支持LAS、OBJ、FBX 3DTiles怎么切片?3DTiles切片
cesium·3dtiles·三维gis·3dtiles切片·数据切片
西凉的悲伤18 小时前
html制作太阳系行星运行轨道演示动画
前端·javascript·html·行星运行轨道演示动画
低保和光头哪个先来18 小时前
源码篇 实例方法
前端·javascript·vue.js
你真的可爱呀18 小时前
自定义颜色选择功能
开发语言·前端·javascript