leaflet实现风场流动

Posted 一名GISer

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了leaflet实现风场流动相关的知识,希望对你有一定的参考价值。

概述:本文基于《Openlayer4中风场的实现》一文,使用leaflet实现流动风场的效果。

废话不多说,下面贴上代码:

1、wind算法及扩展:

  1 /**
  2  * @author
  3  * @Date
  4  * 1.计算矩形4个角的canvas坐标x、y,初始化该区域所有网格点,间距可根据map index设置
  5  * 2.将已有的站点经纬度转换为canvas坐标
  6  * 3.插值法计算出每个网格点的风向、风速
  7  * 4.在该网格区域随机生成width*8个点,重复运动
  8  */
  9 
 10 var Windy = function (options) {
 11     var MAX_PARTICLE_AGE = 100; //粒子最大运动次数
 12     var FRAME_RATE = 20; //重绘帧数
 13     var PARTICLE_MULTIPLIER = 8;
 14 
 15     var canvas = options.canvas;
 16     var width = canvas.width;
 17     var height = canvas.height;
 18     var ctx = canvas.getContext(\'2d\');
 19     ctx.lineWidth = .8;
 20     ctx.fillStyle = \'rgba(0,0,0,.97)\';
 21     // ctx.strokeStyle = \'rgba(38,173,133,0.8)\';
 22 
 23     var buildBounds = function (extent, callback) {
 24         var upperLeft = extent[0];
 25         var lowerRight = extent[1];
 26         var bounds = {
 27             x: upperLeft[0],
 28             y: upperLeft[1],
 29             xmax: lowerRight[0],
 30             ymax: lowerRight[1],
 31             width: lowerRight[0] - upperLeft[0],
 32             height: lowerRight[1] - upperLeft[1]
 33         };
 34         callback(bounds);
 35     }
 36 
 37     var createField = function (columns, bounds, callback) {
 38         function vector(x, y) {
 39             var column = columns[Math.floor(x)];
 40             return column && column[Math.floor(y)];
 41         }
 42 
 43         vector.release = function () {
 44             columns = [];
 45         }
 46 
 47         vector.randomize = function (o) {
 48             var x = Math.floor(Math.floor(Math.random() * bounds.width) + bounds.x);
 49             var y = Math.floor(Math.floor(Math.random() * bounds.height) + bounds.y);
 50             o.x = x;
 51             o.y = y;
 52             return o;
 53         }
 54         callback(bounds, vector);
 55     };
 56 
 57     var interpolateGrid = function (bounds, stationPoints, callback) {
 58         var columns = [];
 59         var x = bounds.x;
 60 
 61         function interpolateColumn(x) {
 62             var column = [];
 63             for (var y = bounds.y; y < bounds.ymax; y += 2) {
 64                 var wind = interpolate(x, y);
 65                 column[y + 1] = column[y] = wind;
 66             }
 67             columns[x + 1] = columns[x] = column;
 68         }
 69 
 70         function interpolate(x, y) {
 71             var angle0 = 0,
 72                 angle1 = 0,
 73                 speed0 = 0,
 74                 speed1 = 0,
 75                 wind = {};
 76             stationPoints.forEach(function (s) {
 77                 angle0 += s.angle * 1.0 / ((y - s.y) * (y - s.y) + (x - s.x) * (x - s.x));
 78                 angle1 += 1.0 / ((y - s.y) * (y - s.y) + (x - s.x) * (x - s.x));
 79 
 80                 speed0 += s.speed * 1.0 / ((y - s.y) * (y - s.y) + (x - s.x) * (x - s.x));
 81                 speed1 += 1.0 / ((y - s.y) * (y - s.y) + (x - s.x) * (x - s.x));
 82 
 83                 if (angle1 != 0) {
 84                     wind.angle = angle0 / angle1;
 85                 }
 86                 if (speed1 != 0) {
 87                     wind.speed = speed0 / speed1;
 88                 }
 89             });
 90             return wind;
 91         }
 92 
 93         (function batchInterpolate() {
 94             var start = Date.now();
 95             while (x < bounds.xmax) {
 96                 interpolateColumn(x);
 97                 x += 2;
 98                 if ((Date.now() - start) > 1000) { //MAX_TASK_TIME
 99                     setTimeout(batchInterpolate, 25);
100                     return;
101                 }
102             }
103             createField(columns, bounds, callback);
104         })();
105     };
106 
107     var animate = function (bounds, vector) {
108         var particleCount = Math.round(bounds.width * PARTICLE_MULTIPLIER);
109         var particles = [];
110         for (var i = 0; i < particleCount; i++) {
111             particles.push(vector.randomize({
112                 age: Math.floor(Math.random() * MAX_PARTICLE_AGE)
113             }));
114         }
115 
116         function evolve() {
117             particles.forEach(function (particle, i) {
118                 if (particle.age > MAX_PARTICLE_AGE) {
119                     particle = vector.randomize({
120                         age: 0
121                     });
122                     particles.splice(i, 1, particle);
123                 }
124                 var x = particle.x;
125                 var y = particle.y;
126                 var v = vector(x, y);
127                 if (v) {
128                     var xe = x - v.speed * Math.sin(Math.PI / 180 * (180 - v.angle));
129                     var ye = y - v.speed * Math.cos(Math.PI / 180 * (180 - v.angle));
130                     var nextPoint = vector(xe, ye);
131                     if (nextPoint) {
132                         particle.xe = xe;
133                         particle.ye = ye;
134                     } else {
135                         var newParticle = vector.randomize({
136                             age: Math.floor(Math.random() * MAX_PARTICLE_AGE)
137                         });
138                         particles.splice(i, 1, newParticle);
139                     }
140                 } else {
141                     particle.age = MAX_PARTICLE_AGE;
142                 }
143                 particle.age += 1;
144             });
145         }
146 
147         function render() {
148             var prev = ctx.globalCompositeOperation;
149             ctx.globalCompositeOperation = "destination-in";
150             ctx.fillRect(0, 0, width, height);
151             ctx.globalCompositeOperation = prev;
152 
153             ctx.beginPath();
154             // ctx.strokeStyle = \'rgba(23,139,231,.8)\';
155             particles.forEach(function (particle, i) {
156                 ctx.moveTo(particle.x, particle.y);
157                 ctx.lineTo(particle.xe, particle.ye);
158                 particle.x = particle.xe;
159                 particle.y = particle.ye;
160             });
161             ctx.stroke();
162         }
163 
164         (function frame() {
165             try {
166                 windy.timer = setTimeout(function () {
167                     requestAnimationFrame(frame);
168                     evolve();
169                     render();
170                 }, 1000 / FRAME_RATE);
171             } catch (e) {
172                 console.error(e);
173             }
174         })();
175     };
176 
177     var start = function (extent, stationPoints) {
178         stop();
179         buildBounds(extent, function (bounds) {
180             interpolateGrid(bounds, stationPoints, function (bounds, vector) {
181                 windy.vector = vector;
182                 animate(bounds, vector);
183             });
184         });
185     };
186 
187     var stop = function () {
188         ctx.clearRect(0, 0, width, height);
189         if (windy.vector) windy.vector.release();
190         if (windy.timer) clearTimeout(windy.timer);
191     };
192 
193     var change = function (options) {
194         ctx.lineWidth = options.size;
195         ctx.strokeStyle = options.color;
196     }
197 
198     var windy = {
199         options: options,
200         start: start,
201         stop: stop,
202         change: change
203     };
204 
205     return windy;
206 };
207 
208 window.requestAnimationFrame = (function () {
209     return window.requestAnimationFrame ||
210         window.webkitRequestAnimationFrame ||
211         window.mozRequestAnimationFrame ||
212         window.oRequestAnimationFrame ||
213         window.msRequestAnimationFrame ||
214         function (callback) {
215             window.setTimeout(callback, 1000 / 20);
216         };
217 })();

2、前端调用

  1 function addWindMap(){
  2             canvas = document.createElement(\'canvas\');
  3             canvas.id = "windCanvas";          
  4             canvas.width = map.getSize().x;
  5             canvas.height = map.getSize().y;           
  6             canvas.style.position = \'absolute\';
  7             canvas.style.top = 0;
  8             canvas.style.left = 0;
  9 
 10             map.getContainer().appendChild(canvas);
 11             windy = new Windy({
 12                 map: map,
 13                 canvas: canvas,
 14                 data: windData
 15             });
 16 
 17             function choice(data){
 18                 for(var i=0;i<data.length;i++){
 19                     if(data[i][3]>300){
 20                          return options2;
 21                     }else{
 22                         return options;
 23                     }
 24                 }
 25             }
 26            
 27             var options = {
 28                size: .8,//控制线条粗细
 29                color: \'rgba(71,160,233,0.8)\',//控制线条颜色                                         
 30             };
 31             windy.change(options);
 32             windDraw();
 33 
 34             map.on(\'movestart\',function(){
 35                 windy.stop();
 36                 $(canvas).hide();
 37             });
 38             map.on("moveend",function(){
 39                 windDraw();
 40             });
 41         }
 42         function windDraw(){
 43             $(canvas).show();
 44             //leaflet中getBounds方法获得的是左上角和右下角的坐标
 45 //          var bounds = map.getBounds();
 46
 47 //          var bounds._southWest.lng = 112;
 48 //          var bounds._southWest.lat = 21;
 49 //          var bounds._northEast.lng = 115;
 50 //          var bounds._northEast.lat = 23;
 51             //设置bounds的经纬度范围可使其在指定范围内显示风场
 52             var bounds = [109,20,118,25];
 53 //          var _min = [bounds._southWest.lng, bounds._southWest.lat];
 54 //          var _max = [bounds._northEast.lng, bounds._northEast.lat];
 55             var _min = [bounds[0],bounds[1]];
 56             var _max = [bounds[2],bounds[3]];
 57             
 58             _min.reverse();
 59             _max.reverse();
 60             var py = [0,0];
 61             canvas.style.left = py[0] + \'px\';
 62             canvas.style.top = py[1] + \'px\';           
 63             var points = invertLatLon(py); //所有站点经纬度转为canvas坐标
 64             var min = map.latLngToContainerPoint(_min);
 65             var max = map.latLngToContainerPoint(_max);
 66 
 67             var extent = [
 68                 [min.x - py[0], max.y - py[1]],
 69                 [max.x - py[0], min.y - py[1]]
 70             ];
 71 
 72             windy.start(extent, points);
 73         }
 74 
 75         function invertLatLon (py) {
 76             var points = [];
 77             windData.forEach(function (station) {               
 78                 var px = map.latLngToContainerPoint([station[0], station[1]]);
 79 
 80                 var x = px.x-py[0];
 81                 var y = px.y-py[1];
 82                 var angle = station[2];
 83                 var speed = station[3];
 84                
 85                 points.push({
 86                      x,y,angle,speed
 87                 });
 88              
 89             });
 90             return points;
 91             
 92         }
 93         function drawcircle(latlng){      
 94         var circle = L.circle(latlng, {
 95             color: \'red\',
 96             fillColor: \'#f03\',
 97             fillOpacity: 0.5,
 98             radius: 500
 99 }).addTo(map);
100          
101         }
102          for(var i=0;i<windData.length;i++){
103              drawcircle([windData[i][0],windData[i][1]]);
104          }

其实整体代码与原博主贴出的相差无几,只是有几处方法的调用上,openlayer与leaflet的方法不同,如:

1、第10行map.getContainer().appendChild(canvas);这是调用了leaflet中的getContainer的方法;而在原贴中是map.getViewport().appendChild(canvas);则是调用了openlayer中getViewport的方法;

2、第42行之后的windDraw方法,获取bounds的经纬度并将其转化为屏幕坐标,在leaflet中是调用getBounds来获取bounds的经纬度,再通过latLngToContainerPoint方法将其经纬度转换为屏幕坐标;在openlayer中则是通过map.getView().calculateExtent();来获取bounds的经纬度,再通过getPixelFromCoordinate()方法将经纬度转换为屏幕坐标;

展现效果:

 

(红色的点为原始数据的所在点)

 

声明:本文是基于《Openlayer4中风场的实现》一文

原博文地址:http://blog.csdn.net/GISShiXiSheng/article/details/78130113?locationNum=8&fps=1

原博主联系方式:

q       q:1004740957

e-mail:niujp08@qq.com

微信公众号:lzugis15

以上是关于leaflet实现风场流动的主要内容,如果未能解决你的问题,请参考以下文章

48.(leaflet之家)leaflet实现正向匹配查询(ElasticSearch技术实现)(输入中文关键字查询地址信息)

leaflet 结合 d3.js 实现 geojson 数据地形剖面分析(附源码下载)

leaflet实现简单量算工具功能(附源码下载)

leaflet结合geoserver利用WFS服务实现图层删除功能(附源码下载)

风场可视化:随机重置

leaflet结合geoserver利用WFS服务实现图层新增功能(附源码下载)