cesium 实现风场图效果

1,486 阅读1分钟

实现效果以下面参考github效果为准:

本篇实现 cesium 风场图功能,效果图如下:

实现思路:

  • 场数据源获取:
    天气数据由全球预报系统(GFS)生成, 由美国国家气象局管理。 预测每天产生四次,并可用于 从NOMADS下载。 这些文件位于GRIB2 格式并包含超过300条记录。 我们只需要这些记录中的一小部分就可以在特定的等压线上可视化风资料。 下面的命令下载 1000 hPa风向量,并使用grib2json将它们转换为JSON格式。
  • 屏幕上生成一系列随机粒子位置并绘制粒子
  • 对于每一个粒子,查询风数据以获得其当前位置的粒子速度,并相应地移动它
  • 将一小部分粒子重置为一个随机的位置。这就确保了风从不会变成空的区域
  • 淡出当前屏幕,并在顶部绘制新定位的粒子

核心代码

  • 风场图初始化调用

    function Draw() {
        $.ajax({
            type: "get",
            url: "sampledata/wind/gfs20171227.json",//请求风场数据源json
            dataType: "json",
            success: function (response) {
                var header = response[0].header;
                windy = new Windy(response, viewer);
                redraw();
            },
            error: function (errorMsg) {
                alert("请求数据失败1!");
            }
        });
    }
    
    var timer = null;
    //加载风场图
    Draw();
    function redraw() {
        timer = setInterval(function () {
            windy.animate();
        }, 300);
    }
    
  • Windy风场

    var _primitives = null; var SPEED_RATE = 0.15; var PARTICLES_NUMBER =2000;//默认2000 var MAX_AGE = 10; var BRIGHTEN = 1.5;

    var Windy = function (json, cesiumViewer) { this.windData = json; this.windField = null; this.particles = []; this.lines = null; _primitives = cesiumViewer.scene.primitives; this._init(); }; Windy.prototype = { constructor: Windy, _init: function () { // 创建风场网格 this.windField = this.createField(); // 创建风场粒子 for (var i = 0; i < PARTICLES_NUMBER; i++) { this.particles.push(this.randomParticle(new Particle())); } }, createField: function () { var data = this._parseWindJson(); return new WindField(data); }, animate: function () { var self = this, field = self.windField, particles = self.particles;

        var instances = [],
            nextX = null,
            nextY = null,
            xy = null,
            uv = null;
        particles.forEach(function (particle) {
            if (particle.age <= 0) {
                self.randomParticle(particle);
            }
            if (particle.age > 0) {
                var x = particle.x,
                    y = particle.y;
    
                if (!field.isInBound(x, y)) {
                    particle.age = 0;
                } else {
                    uv = field.getIn(x, y);
                    nextX = x +  SPEED_RATE * uv[0];
                    nextY = y +  SPEED_RATE * uv[1];
                    particle.path.push(nextX, nextY);
                    particle.x = nextX;
                    particle.y = nextY;
                    instances.push(self._createLineInstance(self._map(particle.path), particle.age / particle.birthAge));
                    particle.age--;
                }
            }
        });
        if (instances.length <= 0) this.removeLines();
        self._drawLines(instances);
    },
    _parseWindJson: function () {
        var uComponent = null,
            vComponent = null,
            header = null;
        this.windData.forEach(function (record) {
            var type = record.header.parameterCategory + "," + record.header.parameterNumber;
            switch (type) {
                case "2,2":
                    uComponent = record['data'];
                    header = record['header'];
                    break;
                case "2,3":
                    vComponent = record['data'];
                    break;
                default:
                    break;
            }
        });
        return {
            header: header,
            uComponent: uComponent,
            vComponent: vComponent
        };
    },
    removeLines: function () {
        if (this.lines) {
            _primitives.remove(this.lines);
            this.lines.destroy();
        }
    },
    //求路径上点
    _map: function (arr) {
        var length = arr.length,
            field = this.windField,
            dx = field.dx,
            dy = field.dy,
            west = field.west,
            south = field.north,
            newArr = [];
        for (var i = 0; i <= length - 2; i += 2) {
            newArr.push(
                west + arr[i] * dx,
                south - arr[i + 1] * dy
            )
        }
        return newArr;
    },
    _createLineInstance: function (positions, ageRate) {
        var colors = [],
            length = positions.length,
            count = length / 2;
        for (var i = 0; i < length; i++) {
            colors.push(Cesium.Color.WHITE.withAlpha(i / count * ageRate * BRIGHTEN));
        }
        return new Cesium.GeometryInstance({
            geometry: new Cesium.PolylineGeometry({
                positions: Cesium.Cartesian3.fromDegreesArray(positions),
                colors: colors,
                width: 1.5,
                colorsPerVertex: true
            })
        });
    },
    _drawLines: function (lineInstances) {
        this.removeLines();
        var linePrimitive = new Cesium.Primitive({
            appearance: new Cesium.PolylineColorAppearance({
                translucent: true
            }),
            geometryInstances: lineInstances,
            asynchronous: false
        });
        this.lines = _primitives.add(linePrimitive);
    },
    randomParticle: function (particle) {
        var safe = 30,x, y;
    
        do {
            x = Math.floor(Math.random() * (this.windField.cols - 2));
            y = Math.floor(Math.random() * (this.windField.rows - 2));
        } while (this.windField.getIn(x, y)[2] <= 0 && safe++ < 30);
    
        particle.x = x;
        particle.y = y;
        particle.age = Math.round(Math.random() * MAX_AGE);//每一次生成都不一样
        particle.birthAge = particle.age;
        particle.path = [x, y];
        return particle;
    }
    

    };

link==>