大家好,我是凹凸数据的热心读者
最近我在设计公司的【可视化大屏】踩了好多坑,今天正好有机会把这些内容分享给大家。
序言
本文的可视化大屏是利用帆软report大屏模板实现,知识点大致分为【Python可视化模块plotly实现航线轨迹地图】,【帆软网页框插件】,【利用js代码定时刷新】 三部分内容构成,希望能为读者在企业实践中提供一些思路。
首先我们先看下效果图,数据是脱敏后的,由于脱敏数据导致部分格式变得奇怪,还请谅解。
而本文主要介绍的是右下角的地图,我们可以看到地图图层是非常美观的,而轨迹线条也是比较舒服的(不舒服的可能是配色,还请谅解)。而我们也来对比之前文章中的一些地图:
比如文章 Python制作可视化大屏全流程! 中的地图,是不是比这个地图更有质感一些?
比如文章用Python 绘制属于你的世界地图 中的地图如下,是不是比这个地图更美观?
项目背景
我拿到的需求其实是这样的,需要在地图上将我司船舶的轨迹展示出来。听起来很简单,一开始我也是这样想的。因为我司用的BI工具是帆软report,通过阅读官网,发现有一种流向地图可供选择,如下图:
仔细观察可以看到,这种轨迹是两点之前的弧线,适合用来展示航空的航线或者起点终点方向类型轨迹,而加载了插件后发现,其功能是无法支持船舶轨迹的数据的,所以该插件被pass掉了。
(想具体了解流向地图可以跳转到官网:https://help.fanruan.com/finereport/doc-view-1956.html)
百度已经无法找到合适的资源了,于是我又转向了微信公众号搜索,我发现了folium这个地图可视化的库。它的可视化地图让人着迷,也支持不同的瓦片(高德,谷歌,也有内置的)风格供你选择,可以在地图上描绘点,圈,直线,热力图等风格的图片,但是如何将轨迹在地图上描绘出来,不论是如何搜索技术文章,也无论是看官方文档,能实现轨迹的只能通过点来描绘,当点足够密集,就相当于轨迹了,类似于中国台风网这种图的样式,如下图。
但现实情况是,我们船舶的轨迹是没有如此频繁的轨迹数据的,那么folium包也没有办法实现我的需求。但是,通过folium模块,我了解到生成的地图是可以生成图片或者
html文件
的,而我也了解到帆软是有网页框插件可以内置html文件的,这也为后来的效果埋下了伏笔。想了解folium包可以去folium官网:http://python-visualization.github.io/folium/ 观看文档来具体了解详情。
终于,“功夫不负有心人”,plotly这个可视化的包被我找到了。大家可以去百度或者微信公众号去搜索,内容是95%雷同的,用的数据可能都是一样的,而代码的出处就来自于plotly的官方文档[1]。而从文章中可以了解到,我需要的轨迹的地图,美观的地图是可以画出来的,开森!
实践之轨迹地图
轨迹地图使用plotly包,具体脚本如下,数据为自己模拟数据。
import os
import plotly.graph_objects as go
import plotly as py
url = r'D:\working_directory\task\文章\shipping_line.html'
mapbox_access_key = '需要你自己去mapbox的网站去申请一个账号'
style = 'streets'
# 采用的风格为streets类型
fig = go.Figure()
color_map = ['#7bd3f6', '#bcbd22', '#17becf','#d62728']
lat_li = []
lng_li = []
for item in [(122.180204,30.806457),(123.256865,29.762908),(123.366728,28.631261),(122.048368,26.350981),(120.048857,24.705378),(119.060087,22.835386),(119.543486,19.433918),(119.960966,16.276336),(116.159697,11.413759),(110.183134,5.067372),(105.173368,0.064226),(101.833525,1.909575),(101.833525,1.909575),(101.833525,1.909575),(88.122587,3.840643),(80.300322,5.154913),(76.433134,7.338996),(71.247587,13.900433),(63.425322,22.246862),(56.394072,25.857701)]:
lng_li.append(item[0])
lat_li.append(item[1])
fig.add_trace(go.Scattermapbox(
name='vessel',
mode='markers+lines',
lon=lng_li,
lat=lat_li,
marker={'size': 2, 'color': color_map[3]},
showlegend=False
))
fig.update_layout(
margin={'l': 0, 't': 0, 'b': 0, 'r': 0}
, mapbox={
'center': {'lon': 90, 'lat': 8},
'zoom': 1
, 'style': style
, 'accesstoken': mapbox_access_key}
)
if os.path.isfile(url):
os.remove(url)
py.offline.plot(fig, filename=url, auto_open=False)
# 保存为html文件
复制
效果图如下:
大概解释一下plotly画图的脚本:
- mapbox_access_key:
需要你去(mapbox官网:https://account.mapbox.com/) 去注册一个账号,可以获得一个免费的token
- style:
可以有多重不同的地图图层,[basic, streets, outdoors, light, dark, satellite, satellite-streets]这些图层都是你可以选择的
- go.Scattermapbox
经纬度需要将坐标的经纬度依次写入到经度列表和纬度列表中线型是用点+线的方式,这样才不会因为点稀疏导致轨迹呈现离散的形式。
showlegend=False
是不需要显示图例,因为在帆软网页框中展示图例,地图会被图例占据50%的版本
- fig.update_layout
参数center是用来显示地图的中心位置,比如上图以印度洋的某点为中心,这样中国的位置会相对美观
- py.offline.plot
auto_open=False
表示保存为html文件,但是不再默认打开html文件,因为默认是保存html文件并在浏览器中打开该文件
看似我们的轨迹已经完成了,而实际上,bug仍然没有显现。熟悉船舶航线的人会知道,中国有到美国洛杉矶,到南美智利的航线,或者到巴西圣保罗的航线,这些航线有一个特点:就是都需要穿过180度经线,当我们按照上述方法来绘制轨迹的时候,会出现丑到爆炸的轨迹,具体脚本不再赘述,模拟坐标轨迹如下:
[(122.667875,31.109411),(128.996,33.779169),
(136.203031,40.094902),(140.246,44.887031),
(142.179594,45.25944),(151.671781,46.24067),
(165.206937,45.25944),(-168.425875,44.008639),(-156.648531,46.24067),(-139.773531,41.029663),(-125.359469,33.339729),(-115.164156,27.741908)]
复制
效果图如下所示:
轨迹竟然!变!直!了!!!
而解决的办法其实就是分段,如果两点之前的差值的绝对值大于300(一个粗略的估计值,比如从150到-151度,我们就认为跨过了180度经度线),那么我们就可以认为轨迹是跨过了180度经度线,那么轨迹要重新开始画,与此同时,需要保证跨越180度经度线的轨迹,分开画但是颜色一致,需要我们在上述脚本上做一个小的处理,即套一个分支语句,用以判断是否跨过180度经线,跨过了即要重新开始描点;否则继续描点,但是要保证:点线的颜色是一致的。
此处可以留个作业给大家了
那么我们的轨迹html文件已经生成了,轨迹变直线的bug也解决了,接下来就是帆软report的网页框的内容了。
实践之帆软网页框
帆软report提供了插件——网页框插件,官网网页框控件[2],感兴趣的同学可以去浏览下,个人用户可以申请免费版本。
但此时,问题又来了,这个网页框如何嵌入html文件呢?
官网是这样描述嵌入html文件的:
当前工程下的页面:页面保存在%FR_HOME%\webapps\webroot 目录下的页面 。
在地址栏输入:${contextPath}/页面名称,contextPath 意指 /webroot,绝对路径的服务器别名,即虚拟目录。
例如将页面保存在%FR_HOME%\webapps\webroot 目录下,输入地址:${contextPath}/1.html
说了这么多,也不知道你有没有听明白。我用通俗的话解释一下,先找到你的帆软report软件的安装路径,将html文件放置在路径
.\webapps\webroot
下,这样report服务器就能够读到你的html文件了。
在上述网页框 位置,输入以下html路径
http://localhost:8075/webroot/your_file_name.html
我们发现帆软report已经可以读到你的html文件了,如文章一开始的动图。
实践之帆软网页框定时刷新
经过了找合适的地图可视化包,找嵌入地图插件网页框,以为我们的任务就完成了。但是,并没有。
找遍官方文档,发现网页框是无法实现自动更新的;
更新数据,重新生成html文件,发现网页框是无法实现自动切换html新文件的内容;
这两个bug直接让我前面做的前功尽弃。
但是我不甘心,通过百度过程中,我发现帆软很多的动画,刷新功能是通过前端Js代码来完成的,也觉得尽管网页框没有配直接的刷新功能,但是不是可以通过Js前端的代码来实现定时刷新的功能呢?
通过咨询前端的同事,对方给予了肯定的回答。中间经历了无数的波折,形成了如下的初始代码:
function setUrl(){
//根据iframe name 触发刷新
var iframDom =document.getElementsByClassName("fr_iframeeditor")
if(iframDom){
iframDom=$(iframDom[1])
var src= iframDom.attr("src")
src=src+new Date().getTime()
iframDom.attr("src",src)
}
}
// 原始
setTimeout(function() {
setUrl()
}, 500);
var timer = setInterval(setUrl, 500000)
复制
通过打印效果,脚本运行正常,网页框也刷新了,后端html文件也变化了,但是前端却始终没有更新内容。经过不断调试,我们猜测是因为前后两次刷新网页框的配置是相同的(也就是配置的html文件的url是相同的),导致网页框无法更新html文件。个人认为,这是网页框的一个bug,因此我们对网页框的html文件的路径进行了修改
http://localhost:8075/webroot/your_file_name.html?time=1234569854562
增加了加粗部分的内容,该部分内容是我们随意加的一个时间戳,这样不影响我们读取html文件,又能保证在网页框中配置的url连接:初始化的与自动生成的html文件的连接是不同的。与此同时,需要对js代码进行稍微的修改,如下:
function setUrl(){
//根据iframe name 触发刷新
var iframDom =document.getElementsByClassName("fr_iframeeditor")
if(iframDom){
iframDom=$(iframDom[1])
var src= iframDom.attr("src")
src=src.substring(0,src.length-13)
src=src+new Date().getTime()
iframDom.attr("src",src)
}
}
// 原始
setTimeout(function() {
setUrl()
}, 500);
var timer = setInterval(setUrl, 500000)
复制
修改的内容是对src的路径进行一个剪切,
src.substring(0,src.length-13)
,把时间戳给去掉,这样就保证了初始化的html配置url,跟每次刷新请求的url不一致,正是这种不一致,达到了刷新地图的目的。
结语
以上大屏就制作完成了,这些内容也是我在设计公司可视化大屏过程中遇到问题,解决问题的思路。
当然,文章一定还有欠缺的部分,但是这种企业中做可视化大屏的思路,还是有一些借鉴意义的,期待跟各位大佬交流。
另外就是plotly这个Python的包,如果大家有涉及到地图的可视化,强烈推荐大家来尝试,好看到爆炸。小五哥也在历史的推文中有推荐过这个库。
好的,以上,祝大家学有所成,我是热心读者,我们下期见。
参考资料
[1]plotly的官方文档: https://chart-studio.plotly.com/feed/
[2]帆软官网网页框控件: https://help.fanruan.com/finereport/doc-view-3491.html