Openlayers4中风场的实现

概述:

本文讲述如何在Openlayers4中结合canvas实现风场的展示。

效果:

代码:

1、数据结构

[lat, lon, wd, ws],其中:lat为纬度;lon为经度;wd为风向;ws为风速。

2、wind扩展

/**
 * @author lzugis
 * @Date
 * 1.计算矩形4个角的canvas坐标x、y,初始化该区域所有网格点,间距可根据map index设置
 * 2.将已有的站点经纬度转换为canvas坐标
 * 3.插值法计算出每个网格点的风向、风速
 * 4.在该网格区域随机生成width*8个点,重复运动
 */

var Windy = function (options) {
    var MAX_PARTICLE_AGE = 100; //粒子最大运动次数
    var FRAME_RATE = 20; //重绘帧数
    var PARTICLE_MULTIPLIER = 8;

    var canvas = options.canvas;
    var width = canvas.width;
    var height = canvas.height;
    var ctx = canvas.getContext('2d');
    ctx.lineWidth = .8;
    ctx.fillStyle = 'rgba(0,0,0,.97)';
    // ctx.strokeStyle = 'rgba(38,173,133,0.8)';

    var buildBounds = function (extent, callback) {
        var upperLeft = extent[0];
        var lowerRight = extent[1];
        var bounds = {
            x: upperLeft[0],
            y: upperLeft[1],
            xmax: lowerRight[0],
            ymax: lowerRight[1],
            width: lowerRight[0] - upperLeft[0],
            height: lowerRight[1] - upperLeft[1]
        };
        callback(bounds);
    }

    var createField = function (columns, bounds, callback) {
        function vector(x, y) {
            var column = columns[Math.floor(x)];
            return column && column[Math.floor(y)];
        }

        vector.release = function () {
            columns = [];
        }

        vector.randomize = function (o) {
            var x = Math.floor(Math.floor(Math.random() * bounds.width) + bounds.x);
            var y = Math.floor(Math.floor(Math.random() * bounds.height) + bounds.y);
            o.x = x;
            o.y = y;
            return o;
        }
        callback(bounds, vector);
    };

    var interpolateGrid = function (bounds, stationPoints, callback) {
        var columns = [];
        var x = bounds.x;

        function interpolateColumn(x) {
            var column = [];
            for (var y = bounds.y; y < bounds.ymax; y += 2) {
                var wind = interpolate(x, y);
                column[y + 1] = column[y] = wind;
            }
            columns[x + 1] = columns[x] = column;
        }

        function interpolate(x, y) {
            var angle0 = 0,
                angle1 = 0,
                speed0 = 0,
                speed1 = 0,
                wind = {};
            stationPoints.forEach(function (s) {
                angle0 += s.angle * 1.0 / ((y - s.y) * (y - s.y) + (x - s.x) * (x - s.x));
                angle1 += 1.0 / ((y - s.y) * (y - s.y) + (x - s.x) * (x - s.x));

                speed0 += s.speed * 1.0 / ((y - s.y) * (y - s.y) + (x - s.x) * (x - s.x));
                speed1 += 1.0 / ((y - s.y) * (y - s.y) + (x - s.x) * (x - s.x));

                if (angle1 != 0) {
                    wind.angle = angle0 / angle1;
                }
                if (speed1 != 0) {
                    wind.speed = speed0 / speed1;
                }
            });
            return wind;
        }

        (function batchInterpolate() {
            var start = Date.now();
            while (x < bounds.xmax) {
                interpolateColumn(x);
                x += 2;
                if ((Date.now() - start) > 1000) { //MAX_TASK_TIME
                    setTimeout(batchInterpolate, 25);
                    return;
                }
            }
            createField(columns, bounds, callback);
        })();
    };

    var animate = function (bounds, vector) {
        var particleCount = Math.round(bounds.width * PARTICLE_MULTIPLIER);
        var particles = [];
        for (var i = 0; i < particleCount; i++) {
            particles.push(vector.randomize({
                age: Math.floor(Math.random() * MAX_PARTICLE_AGE)
            }));
        }

        function evolve() {
            particles.forEach(function (particle, i) {
                if (particle.age > MAX_PARTICLE_AGE) {
                    particle = vector.randomize({
                        age: 0
                    });
                    particles.splice(i, 1, particle);
                }
                var x = particle.x;
                var y = particle.y;
                var v = vector(x, y);
                if (v) {
                    var xe = x - v.speed * Math.sin(Math.PI / 180 * (180 - v.angle));
                    var ye = y - v.speed * Math.cos(Math.PI / 180 * (180 - v.angle));
                    var nextPoint = vector(xe, ye);
                    if (nextPoint) {
                        particle.xe = xe;
                        particle.ye = ye;
                    } else {
                        var newParticle = vector.randomize({
                            age: Math.floor(Math.random() * MAX_PARTICLE_AGE)
                        });
                        particles.splice(i, 1, newParticle);
                    }
                } else {
                    particle.age = MAX_PARTICLE_AGE;
                }
                particle.age += 1;
            });
        }

        function render() {
            var prev = ctx.globalCompositeOperation;
            ctx.globalCompositeOperation = "destination-in";
            ctx.fillRect(0, 0, width, height);
            ctx.globalCompositeOperation = prev;

            ctx.beginPath();
            // ctx.strokeStyle = 'rgba(23,139,231,.8)';
            particles.forEach(function (particle, i) {
                ctx.moveTo(particle.x, particle.y);
                ctx.lineTo(particle.xe, particle.ye);
                particle.x = particle.xe;
                particle.y = particle.ye;
            });
            ctx.stroke();
        }

        (function frame() {
            try {
                windy.timer = setTimeout(function () {
                    requestAnimationFrame(frame);
                    evolve();
                    render();
                }, 1000 / FRAME_RATE);
            } catch (e) {
                console.error(e);
            }
        })();
    };

    var start = function (extent, stationPoints) {
        stop();
        buildBounds(extent, function (bounds) {
            interpolateGrid(bounds, stationPoints, function (bounds, vector) {
                windy.vector = vector;
                animate(bounds, vector);
            });
        });
    };

    var stop = function () {
        ctx.clearRect(0, 0, width, height);
        if (windy.vector) windy.vector.release();
        if (windy.timer) clearTimeout(windy.timer);
    };

    var change = function (options) {
        ctx.lineWidth = options.size;
        ctx.strokeStyle = options.color;
    }

    var windy = {
        options: options,
        start: start,
        stop: stop,
        change: change
    };

    return windy;
};

window.requestAnimationFrame = (function () {
    return window.requestAnimationFrame ||
        window.webkitRequestAnimationFrame ||
        window.mozRequestAnimationFrame ||
        window.oRequestAnimationFrame ||
        window.msRequestAnimationFrame ||
        function (callback) {
            window.setTimeout(callback, 1000 / 20);
        };
})();

3、调用实现

        var canvas, windy;
		function addWindMap(){
            canvas = document.createElement('canvas');
            canvas.id = "windCanvas";
            canvas.width = map.getSize()[0];
            canvas.height = map.getSize()[1];
            canvas.style.position = 'absolute';
            canvas.style.top = 0;
            canvas.style.left = 0;
            map.getViewport().appendChild(canvas);
            windy = new Windy({
                map: map,
                canvas: canvas,
                data: windData
            });
            var options = {
                size: .8,
                color: 'rgba(71,160,233,0.8)',
            };
            windy.change(options);
            windDraw();
            map.getView().on('propertychange',function(){
                windy.stop();
                $(canvas).hide();
            });
            map.on("moveend",function(){
                windDraw();
            });
		}
		function windDraw(){
            $(canvas).show();
            var bounds = map.getView().calculateExtent();
            var _min = [bounds[0], bounds[1]];
            var _max = [bounds[2], bounds[3]];
            var py = map.getPixelFromCoordinate([bounds[0], bounds[3]]); //经纬度转成屏幕坐标
            canvas.style.left = py.x + 'px';
            canvas.style.top = py.y + 'px';
            var points = invertLatLon(py); //所有站点经纬度转为canvas坐标
            var min = map.getPixelFromCoordinate(_min);
            var max = map.getPixelFromCoordinate(_max);
            var extent = [
                [min[0] - py[0], max[1] - py[1]],
                [max[0] - py[0], min[1] - py[1]]
            ];
            windy.start(extent, points);
		}

        function invertLatLon (py) {
            var points = [];
            windData.forEach(function (station) {
                var px = map.getPixelFromCoordinate([station[1], station[0]]);
                points.push({
                    x: px[0]-py[0],
                    y: px[1]-py[1],
                    angle: station[2],
                    speed: station[3]
                });
            });
            return points;
        }

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Modeng的专栏

canvas学习总结五:线段的端点与连接点

版权声明:本文为原创文章发布于公众号:Modeng , 你可以随意转载但请务必注明出处!!! https://blog.csdn.net/qq_32135...

9020
来自专栏菩提树下的杨过

Flash/Flex学习笔记(48):反向运动学(下)

先要复习一下三角函数与余弦定理: 对于直角三角形,三边长a,b,c与三个角A,B,C的关系如下: ? 正弦函数: ? 余弦函数: ? 正切函数: ? 反正切函数...

249100
来自专栏Android知识点总结

看得见的数据结构Android版之栈篇

进入和弹出动画为了好区分,用两个 ValueAnimator 控制,下面是成员变量

12820
来自专栏向治洪

React Native之TextInput组件实现联想输入

TextInput组件是最基本的组件,相关介绍请查看TextInput组件介绍 输入框组件属性 输入框组件的主要属性如下: autoCapitalize : ...

253100
来自专栏IMWeb前端团队

transform 的副作用

本文作者:IMWeb elvin 原文出处:IMWeb社区 未经同意,禁止转载 transform 想必大家都很熟悉,可以通过其转换(translat...

22490
来自专栏一心无二用,本人只专注于基础图像算法的实现与优化。

SSE图像算法优化系列六:OpenCv关于灰度积分图的SSE代码学习和改进。

  最近一直沉迷于SSE方面的优化,实在找不到想学习的参考资料了,就拿个笔记本放在腿上翻翻OpenCv的源代码,无意中看到了OpenCv中关于积分图的代码,仔细...

500100
来自专栏everhad

ImageView缩放选项

ImageView.ScaleType 将图片边界缩放到所在view边界时的缩放选项。 Options for scaling the bounds of a...

26370
来自专栏菩提树下的杨过

Flash/Flex学习笔记(49):3D基础

之前我们所做的动画都是基于x,y二维坐标轴的,在三维动画中我们还需要增加一个垂直于屏幕“向里”或“向外”的Z轴,那么z轴到底是应该向外,还是向里呢?这个其实无所...

19260
来自专栏逍遥剑客的游戏开发

M2文件头

12320
来自专栏菩提树下的杨过

Silverlight:Mouse Avoiding 躲避鼠标效果

昨晚在一国外博客上(从域名后缀pl上猜想应该是波兰)看到这种效果(Mouse Avoid 躲避鼠标),是基于Flash/AS3开发的,这个示例把弹性运动,摩擦力...

22670

扫码关注云+社区

领取腾讯云代金券