Leaflet -- 绘制台风风圈
- 实现效果
- 实现思路
- 核心代码
- 全部代码
- 结束语
实现效果
实现思路
我们先来看一下,后端返回的数据格式。下面是台风轨迹列表中的一条记录,红框的字段就是画台风风圈的关键字段:se、se、ne、nw分别代表当前点位台风风圈的东南、西南、东北、西北四个方向的影响半径。
html的图形的绘制,svg和canvas。最开始选择的是canvas,绘制好了之后,在地图的位移时要重新计算和绘制实现效果不理想。
之后看了下leaflet的源码发现L.Polygon 是继承自L.Path对象。而L.Path本身又是,基于SVG实现的。那就好办了,我们继承L.Polygon再写个类L.Typhoon
L.Typhoon类中,两个最核心方法:一个是计算风圈四个方向半径,一个是绘制台风风圈: 基于SVG的path元素来实现的,下面是代码:
核心代码
- 计算风圈半径:台风中心经纬度和半径计算屏幕坐标
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | projectLatlngs: function () { try { var e = this._latlng; this._point = this._map.latLngToLayerPoint(e); var t_northeast = this._getLngRadius(this._getLatRadius(this._circle.ne * 1000)), i_northeast = this._map.latLngToLayerPoint([e.lat, e.lng - t_northeast]); this._radius_northeast = Math.max(this._point.x - i_northeast.x, 1); var t_southeast = this._getLngRadius(this._getLatRadius(this._circle.se * 1000)), i_southeast = this._map.latLngToLayerPoint([e.lat, e.lng - t_southeast]); this._radius_southeast = Math.max(this._point.x - i_southeast.x, 1); var t_southwest = this._getLngRadius(this._getLatRadius(this._circle.sw * 1000)), i_southwest = this._map.latLngToLayerPoint([e.lat, e.lng - t_southwest]); this._radius_southwest = Math.max(this._point.x - i_southwest.x, 1); var t_northwest = this._getLngRadius(this._getLatRadius(this._circle.nw * 1000)), i_northwest = this._map.latLngToLayerPoint([e.lat, e.lng - t_northwest]); this._radius_northwest = Math.max(this._point.x - i_northwest.x, 1) } catch (e) { this._radius_northeast = null; this._radius_southeast = null; this._radius_southwest = null; this._radius_northwest = null } }, |
- 绘制台风风圈:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | getTyphoonPath: function () { if (this._radius_northeast && this._radius_southeast && this._radius_southwest && this._radius_northwest) { var t = this._point; var e_northeast = this._radius_northeast; var path_svg = "M" + t.x + "," + (t.y - e_northeast); var path_vml = "M" + t.x + "," + (t.y - e_northeast); path_svg += "A" + e_northeast + "," + e_northeast + ",0,0,1," + (t.x + e_northeast) + "," + t.y; path_vml += " ae " + t.x + "," + t.y + " " + e_northeast + "," + e_northeast + " " + 65535 * 450 + "," + -5898150; var e_southeast = this._radius_southeast; path_svg += "L" + (t.x + e_southeast) + "," + t.y; path_svg += "A" + e_southeast + "," + e_southeast + ",0,0,1," + t.x + "," + (t.y + e_southeast); path_vml += " ae " + t.x + "," + t.y + " " + e_southeast + "," + e_southeast + " " + 65535 * 360 + "," + -5898150; var e_southwest = this._radius_southwest; path_svg += "L" + t.x + "," + (t.y + e_southwest); path_svg += "A" + e_southwest + "," + e_southwest + ",0,0,1," + (t.x - e_southwest) + "," + t.y; path_vml += " ae " + t.x + "," + t.y + " " + e_southwest + "," + e_southwest + " " + 65535 * 270 + "," + -5898150; var e_northwest = this._radius_northwest; path_svg += "L" + (t.x - e_northwest) + "," + t.y; path_svg += "A" + e_northwest + "," + e_northwest + ",0,0,1," + t.x + "," + (t.y - e_northwest) + "z"; path_vml += " ae " + t.x + "," + t.y + " " + e_northwest + "," + e_northwest + " " + 65535 * 180 + "," + -5898150 + "X"; this.svgPath = L.Browser.svg ? path_svg : path_vml return L.Browser.svg ? path_svg : path_vml } return "" }, |
全部代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 | (function () { L.Typhoon = L.Polygon.extend({ initialize: function (t, e, i) { L.Polygon.prototype.initialize.call(this, e), this._latlng = L.latLng(t), this._circle = e,this._style = i; }, options: {fill: !0}, projectLatlngs: function () { try { var e = this._latlng; this._point = this._map.latLngToLayerPoint(e); var t_northeast = this._getLngRadius(this._getLatRadius(this._circle.ne * 1000)), i_northeast = this._map.latLngToLayerPoint([e.lat, e.lng - t_northeast]); this._radius_northeast = Math.max(this._point.x - i_northeast.x, 1); var t_southeast = this._getLngRadius(this._getLatRadius(this._circle.se * 1000)), i_southeast = this._map.latLngToLayerPoint([e.lat, e.lng - t_southeast]); this._radius_southeast = Math.max(this._point.x - i_southeast.x, 1); var t_southwest = this._getLngRadius(this._getLatRadius(this._circle.sw * 1000)), i_southwest = this._map.latLngToLayerPoint([e.lat, e.lng - t_southwest]); this._radius_southwest = Math.max(this._point.x - i_southwest.x, 1); var t_northwest = this._getLngRadius(this._getLatRadius(this._circle.nw * 1000)), i_northwest = this._map.latLngToLayerPoint([e.lat, e.lng - t_northwest]); this._radius_northwest = Math.max(this._point.x - i_northwest.x, 1) } catch (e) { this._radius_northeast = null; this._radius_southeast = null; this._radius_southwest = null; this._radius_northwest = null } }, getTyphoonPath: function () { if (this._radius_northeast && this._radius_southeast && this._radius_southwest && this._radius_northwest) { var t = this._point; var e_northeast = this._radius_northeast; var path_svg = "M" + t.x + "," + (t.y - e_northeast); var path_vml = "M" + t.x + "," + (t.y - e_northeast); path_svg += "A" + e_northeast + "," + e_northeast + ",0,0,1," + (t.x + e_northeast) + "," + t.y; path_vml += " ae " + t.x + "," + t.y + " " + e_northeast + "," + e_northeast + " " + 65535 * 450 + "," + -5898150; var e_southeast = this._radius_southeast; path_svg += "L" + (t.x + e_southeast) + "," + t.y; path_svg += "A" + e_southeast + "," + e_southeast + ",0,0,1," + t.x + "," + (t.y + e_southeast); path_vml += " ae " + t.x + "," + t.y + " " + e_southeast + "," + e_southeast + " " + 65535 * 360 + "," + -5898150; var e_southwest = this._radius_southwest; path_svg += "L" + t.x + "," + (t.y + e_southwest); path_svg += "A" + e_southwest + "," + e_southwest + ",0,0,1," + (t.x - e_southwest) + "," + t.y; path_vml += " ae " + t.x + "," + t.y + " " + e_southwest + "," + e_southwest + " " + 65535 * 270 + "," + -5898150; var e_northwest = this._radius_northwest; path_svg += "L" + (t.x - e_northwest) + "," + t.y; path_svg += "A" + e_northwest + "," + e_northwest + ",0,0,1," + t.x + "," + (t.y - e_northwest) + "z"; path_vml += " ae " + t.x + "," + t.y + " " + e_northwest + "," + e_northwest + " " + 65535 * 180 + "," + -5898150 + "X"; this.svgPath = L.Browser.svg ? path_svg : path_vml return L.Browser.svg ? path_svg : path_vml } return "" }, beforeAdd: function (map) { this._renderer = map.getRenderer(this); }, onAdd: function (map) { this.projectLatlngs(); this.getTyphoonPath(); this._renderer._initPath(this); this._reset(); this._path.setAttribute('d',this.svgPath); this._renderer._addPath(this); this._setStyle(this._style); }, _setStyle: function (style) { L.setOptions(this, style); if (this._renderer) { this._renderer._updateStyle(this); } return this; }, _getLatRadius: function (r) { return r / 40075017 * 360 }, _getLngRadius: function (lr) { return lr / Math.cos(Math.PI / 180 * this._latlng.lat) } }); L.typhoon = function (t, e, i) { return new L.Typhoon(t, e, i) } })(); |
结束语
以上就是台风风圈的绘制实现及代码。本文是基于leaflet v1.6.0的完成的,低版本的leaflet是否可以用并未进行测试,请自行进行测试或对代码进行修改。