本文是使用Leaflet开发某个台风项目过程中遇到的一些问题和解决方法的记录。
Leaflet中使用复选框控制图层显示 Leaflet本身有图层控制的支持,但最近用Leaflet做台风的小项目,其中一个细节是使用复选框控制图层显示与否,解决方法作如下记录。在表格中的”选择”列是对应一个台风图层的复选框,通过勾选来展示和隐藏本图层。Leaflet中的图层有其所属的leaflet_id,是为了保证图层的唯一性,但要选中复选框来操作对应的图层,显然leaflet_id帮不上什么忙了。
于是,在绘制图层时给图层绑定一个自定义的layerId 。
1 2 3 4 5 var id = 202002 ;var lyr_point = L.circle(……);var lyr_polyline = L.polyline(……);var lyr = L.featureGroup([lyr_point,lyr_polyline]); lyr.options.layerId = id;
js创建复选框时,给每一个复选框绑定id,值即为对应台风的id。要展示图层时,查看当前选中复选框是否选中,若选中则根据id构造url绘制;要隐藏图层时,遍历图层组,若传入的id与某一个图层的layerId匹配,则隐藏该图层。复选框绑定的函数框架如下,这样就可以实现使用复选框控制图层了。
1 2 3 4 5 6 7 8 9 10 11 function showPath (id ) { if (document .getElementById(id.toString()).checked){ }else { lyrGroup.eachLayer(function (layer ) { if (id == layer.options.LayersID){ lyr.removeLayer(layer); } } } }
台风风圈的绘制 Leaflet中绘制台风风圈,网上有一篇博客 实现的比较完整,具体思路是扩展L.Polygon类,使用SVG的path绘制。但我这里调用这个扩展类绘制的风圈不稳定,在地图缩放、拖动,窗口大小的改变都会是风圈图层消失。在浏览器中查看,发现上述操作是SVG的path里的d属性清零了,百思不得其解,于是乎放弃这个方法,改使用turf.js绘制风圈。
turf.js是浏览器和Node.js环境下的高级地理空间分析的js库,里面实现了很多常见的空间分析,比如缓冲区、点在多边形内等。我这里用lineArc函数 ,指定中心点、半径、起始和终止角度后,它可以绘制一段圆弧,注意这里的圆弧是由计算出来的很多点拟合而成的,并不是真正的绘制了一条圆弧。其中options参数中的step默认为64,如果想要展示效果更顺滑一点,可以选择拟合点数。
分别指定东北,东南,西南,西北四个方向上的半径长度,单位默认是kilometer,生成四段圆弧,再把四段圆弧的坐标都push进一个数组,利用lineString函数生成线要素,再利用lineToPolygon函数转换为多边形。
最后,使用L.geoJSON将风圈图层添加到地图上。实现函数贴在下方。
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 function drawTyphoonCircle (p,lyr ) { var center = turf.point([p.longitude, p.latitude]); var r_ne,r_se,r_sw,r_nw; if (p.radius7_quad && p.radius7){ r_ne = p.radius7_quad.ne; r_se = p.radius7_quad.se; r_sw = p.radius7_quad.sw; r_nw = p.radius7_quad.nw; }else { return ; } var options = {number :2048 }; var arc_ne = turf.lineArc(center, r_ne, 0 , 89.9 ,options); var arc_se = turf.lineArc(center, r_se, 90 , 179.9 ,options); var arc_sw = turf.lineArc(center, r_sw, 180 , 269.9 ,options); var arc_nw = turf.lineArc(center, r_nw, 270 , 360.1 ,options); var arcs = []; arcs.push(arc_ne,arc_se,arc_sw,arc_nw); var myStyle = { "color" : "#ccffcc" , "weight" : 2 , "fillColor" :"#ccffcc" }; var typhoonCircleCoords = []; for (var i=0 ;i<arcs.length;i++){ var rawCoords1 = arcs[i].geometry.coordinates; for (var j=0 ;j<rawCoords1.length;j++){ typhoonCircleCoords.push(rawCoords1[j]); } } var lineAll = turf.lineString(typhoonCircleCoords); var typhoonCirclePolygon = turf.lineToPolygon(lineAll); L.geoJSON(typhoonCirclePolygon,{style :myStyle}).addTo(lyr); }
给表格添加滚动条,设置后无效 overflow-y设置为true后,要设置height为一个固定的值,比如600px。
Leaflet中加载Mapbox自定义地图 使用L.tileLayer创建,url template中的username是Mapbox的注册账户的用户名。在https://studio.mapbox.com/ 中自定义图层的分享按钮处点击,即可看到style_id和Access Token。
1 2 3 4 5 6 7 var mymap = L.map('map' ).setView([20.557212 ,126.402354 ],3.5 ); L.tileLayer('https://api.mapbox.com/styles/v1/{username}/{style_id}/tiles/512/{z}/{x}/{y}?access_token={accessToken}' ,{ username :'whitedreamer' , style_id :'cjn64ahui0ycg2rq72fer5a3r' , accessToken :'pk.eyJ1Ijoid2hpdGVkcmVhbWVyIiwiYSI6ImNqbjN4azFjcDAwbG0zcG52aGc3M2x0M2sifQ.CYsl1oXDVr1PWgx4z6lSeg' }).addTo(mymap);
单击表格行等同于单击行内的复选框,选中图层 这个功能是问题1的改进版,需要修改原有的代码逻辑。首先,删除复选框的onclick绑定的事件,然后改写showPath函数:根据图层id(等同于checkbox的id)绘制台风路径,增加hidePath函数:根据图层id隐藏台风路径;最后,编写selectARow函数,获取所点击表格行中的复选框id,根据id判断图层是否已绘制,若未绘制则绘制该图层,若已绘制则取消绘制。
注意:selectARow函数根据id判断复选框是否被选中来判断图层是否已绘制,存在逻辑矛盾,因为点击复选框是点击就绘制,取消就取消绘制;而点击表格行是若未选中则选中后绘制,若选中了则取消选中然后取消绘制。这两个逻辑相反,无法调和,故采取上述逻辑。
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 function showPath (id ) { lyr_num += 1 ; if (lyr_num > 4 ){ alert('Layers More Than 4.' ); } var url = 'https://www.readearth.com/typhoon/' +id.toString().slice(0 ,4 )+'/' +id+'.json' ; draw(url,id); $('.badge' )[0 ].innerHTML=lyr_num; ids.push(id); }function hidePath (id ) { lyr.eachLayer(function (layer ) { if (id == layer.options.LayersID){ lyr.removeLayer(layer); lyr_num -= 1 ; $('.badge' )[0 ].innerHTML=lyr_num; } }); ids.forEach(function (item, index ) { if (item == id) { ids.splice(index, 1 ); } }); }function selectARow ( ) { $('.table' ).on('click' ,'tr' ,function ( ) { var td_checkbox = $(this )[0 ].children[3 ]; var checkbox = td_checkbox.children[0 ]; var id = parseInt (checkbox.id); var layers = lyr.getLayers(); var flag = 0 ; if (layers.length){ lyr.eachLayer(function (layer ) { if (id == layer.options.LayersID){ flag = 1 ; } }) } if (flag){ checkbox.checked = false ; hidePath(id); }else { checkbox.checked = true ; showPath(id); } }) }
一个div浮在一个div之上 假设div1在底下,div2浮在上方,则css应该这么写。注意:div1与div2之间是平行关系,不是嵌套关系,但div1定义要在div2之上。
1 2 3 4 5 6 7 8 #div1 { position :absolute; }#div2 { positon:relative; z-index :1 /*div2的z-index大于div1的z-index即可,默认div1的z-index为0 */ }
Echarts绘制图表的依赖 从Echarts官网下载的官方案例会引入很多文件,一般的图表,像饼图、条形图、折线图等等,只需要引入一个文件,即:
1 2 <!-- 引入echarts.js --><script type ='text/javascript' src ='https://cdn.bootcss.com/echarts/4.2.1-rc1/echarts.min.js' > </script >
推荐下载下来本地引用,在线引用略慢。