最近用到Power BI来做数据报表的呈现,这个报表的目的是展现车辆在业务使用过程中的消息发送的时延。服务器端接收车辆上报的消息的时候,会根据消息生成的时间戳和接收到消息的时间戳来计算传输时延。对于计算后的时延,按照每小时的粒度来进行聚合,并且呈现在地图上。

这个报表会把数据展现在地图中,地图可以分级来进行聚合呈现,例如地图可以按照区域来进行指标的呈现,当点击区域的时候,可以进入到第二级,按照网格(例如200*200米)来进行呈现。

这里我采用了Power BI的Mapbox控件来进行开发。

1. 地图数据的准备

需要准备两个级别的地图数据。第一个级别是区域的数据,第二个级别是网格的数据

1.1 区域数据的准备

首先准备区域的数据,这里我以瑞典首都斯德哥尔摩市为例,从wiki page https://en.wikipedia.org/wiki/Districts_of_Sweden#Stockholm可以查到斯市共分为了14个区,然后可以上https://osm-boundaries.com/Map这个网站找到对应的这些区域的多边形的坐标数据(在这个网站中斯市只有13个区),下载之后得到geojson格式的文件,里面定义了每个区的多边形的坐标点,按照逆时针的顺序来排序。打开这个文件,可以看到里面的Feature的geometry的类型是polygon,这里需要更改为MultiPolygon的类型,并且把coordinates的数组增加多一个[]层级,不然Mapbox处理会有问题。

区域的Geojson文件准备好之后,就可以登录上Mapbox的网站https://studio.mapbox.com/tilesets/,用mapbox studio来创建新的tileset了。

1.2 网格数据的准备

现在准备第二个级别的网格数据。这里用到了http://turfjs.org/这个工具,提供了javascript来方便的对地图进行网格切分,只需要指定一个BBOX,网格大小,就可以进行切分,切分的网格有多种不同的形状,正方形,六角形,三角形等等。非常方便。

以下代码是调用turf的javascript脚本来进行网格切分的示例。

<!DOCTYPE html>
<html>
<head><meta http-equiv="Content-Type" content="text/html; charset=utf-8" /><meta name="viewport" content="initial-scale=1.0, user-scalable=no" /><style type="text/css">body, html,#allmap {width: 100%;height: 100%;overflow: hidden;margin:0;font-family:"微软雅黑";}</style><script src='https://unpkg.com/@turf/turf/turf.min.js'></script><title>turf</title>
</head>
<body><script type="text/javascript">var bbox = [14.29, 57.669, 19.124, 60.555];var cellSide = 0.2;var options = {units: 'kilometers'};var squareGrid = turf.squareGrid(bbox, cellSide, options);console.log(squareGrid.features.length);console.log(JSON.stringify(squareGrid));</script>
</body>

把这个HTML文件放到Web服务器,然后打开这个HTML,在浏览器的控制台可以看到切分的网格数据的输出。把这些数据拷贝下来,保存为Geojson格式的文件。这里同样需要对Geojson文件进行修改,把Polygon的类型改为Multipolygon,以及给Coordinates数组增加多一个列表层级。

1.3 道路数据的准备

因为车辆是跑在道路上的,因此发送的位置坐标都应该是落在道路上的。因此我们需要把这些区域内的道路数据抓取出来。这里用到Openstreetmap的数据来提取道路信息。

在Openstreetmap上下载斯德哥尔摩的地图数据,数据格式是XML,里面的节点如果tag是node,表示是一个具体的点和对应的坐标,如果tag是way,需要进一步看其属性是否是highway,如果是就代表是一条道路,再获取其包含的node,这样就可以提取出这条道路的所有坐标点了。

为了之后能方便进行地图区域的处理,采用Google的S2地理库来进行处理,把提取出来的道路坐标点会转换为s2point的对象,然后把这条道路转换为s2polyline。以下python代码是处理过程

tree = ET.ElementTree(file = 'stockholm/stockholm_map.osm')
root = tree.getroot()
nodes = {}
for child in root:if child.tag=='node':nodes[child.attrib['id']] = (float(child.attrib['lat']), float(child.attrib['lon']))
ways = {}
ways_type = {'motorway':[], 'trunk':[], 'primary':[], 'secondary':[]}
for child in root:if child.tag=='way':waytag = child.findall("tag")isRoad = FalseroadPosition = []for item in waytag:if item.attrib['k'] == 'highway':if item.attrib['v'] in ['motorway', 'trunk', 'primary', 'secondary']:isRoad = Trueways_type[item.attrib['v']].append(child.attrib['id'])breaks2points = []if isRoad:nds = child.findall("nd")ways[child.attrib['id']] = []for nd in nds:s2point = s2.S2LatLng.FromDegrees(nodes[nd.attrib['ref']][0], nodes[nd.attrib['ref']][1]).ToPoint()s2points.append(s2point)s2polyline = s2.S2Polyline()s2polyline.InitFromS2Points(s2points)ways[child.attrib['id']] = s2polyline

1.4 区域,网格和道路的处理

需要把区域,网格以及道路的信息都关联起来,就是说根据区域来判断有哪些网格是落在区域里面的,然后判断哪些网格是覆盖道路的。

首先是把区域的数据转换为S2的s2loop

with open('stockholm/district/district.geojson', 'r') as f:all_district = json.loads(f.readlines()[0])s2districts = {}
for feature in all_district['features']:coords = feature['geometry']['coordinates'][0][0]s2points = [s2.S2LatLng.FromDegrees(a[1],a[0]).ToPoint() for a in coords]s2loop = s2.S2Loop(s2points[:-1])s2loop.Normalize()s2districts[feature['properties']['name']] = s2loop

把网格的数据也转换为s2loop,然后判断哪些网格是包含在区域里面的,对这些在区域里面的s2loop,转换为s2polygon

with open('stockholm/stockholm_all_grids.geojson', 'r') as f:all_grids = json.loads(f.readlines()[0])selected_grids = {}
s2grids = []
gridid = 0
for feature in all_grids['features']:coords = feature['geometry']['coordinates'][0]s2points = [s2.S2LatLng.FromDegrees(a[1],a[0]).ToPoint() for a in coords]s2loop = s2.S2Loop(s2points[:-1])s2loop.Normalize()for districtname in s2districts:districtloop = s2districts[districtname]if districtloop.Contains(s2loop):feature['properties']['name'] = districtnamefeature['properties']['gridid'] = 'grid'+str(gridid)feature['s2polygon'] = s2.S2Polygon(s2loop)selected_grids['grid'+str(gridid)]=featuregridid += 1

判断上一步得到的网格中,哪些是覆盖道路的,这里用到了s2polygon的IntersectWithPolyline方法,来判断道路的polyline和网格的polygon是否相交,如果相交,那么返回的就是相交的polyline。

new_selected_grids = []
for k,v in tqdm(ways.items(), total=len(ways)):for grid in selected_grids:if len(selected_grids[grid]['s2polygon'].IntersectWithPolyline(v))>0:new_selected_grids.append(selected_grids[grid])del selected_grids[grid]break  

最后就是把这些网格写入为一个geojson的文件,这个文件制作完成之后,同样需要用Mapbox studio来创建新的tileset。

way_grids = {}
way_grids["type"] = "FeatureCollection"
way_grids["features"] = []
for grid in new_selected_grids:feature = grid.copy()feature["geometry"]["type"] = "MultiPolygon"feature["geometry"]["coordinates"] = [feature["geometry"]["coordinates"]]del feature["s2polygon"]way_grids["features"].append(feature)
way_grids_str = json.dumps(way_grids)
with open("stockholm/way_grids_1209.geojson", "w") as f:f.write(way_grids_str)

2. 测量数据的准备

有了网格和区域的数据之后,我们就可以准备测量数据了。这里模拟随机生成了一些测量数据,并关联到网格和区域中。因为我们是要制作二级地图,因此数据里面需要包括三个字段,分别是区域名称,网格名称和测量值。以下代码是随机生成一些测量数据

simulate_data = 'district,gridid,latency'
for grid in way_grids["features"]:district = grid["properties"]["name"]gridid = grid["properties"]["gridid"]latency = random.randint(60, 150)simulate_data += "\n"simulate_data += district+","+gridid+","+str(latency)
with open("stockholm/simulate_data_1209.csv", "w") as f:f.write(simulate_data)

3. 报表的制作

PowerBI里面有多个地图控件,例如Azure Map, ArcGIS map, Filled Map等等,但是能做分级地图的似乎只有Mapbox以及Drilldown Choropleth,其中Mapbox功能更强一些,因此这里选用Mapbox。Mapbox提供了比较友好的收费模式,在地图访问量小于50000的时候是免费的。

打开PowerBI desktop,默认是没有mapbox的,在Visualizations里面选择Get more visuals,然后在maps里面查找mapbox并安装。

点击Mapbox控件,点击Get Data,选择我们之前制作的模拟数据。然后在Mapbox的Fields里面,把数据的district和grid两个字段拖动到location,把latency字段拖动到color

点击Format,关闭Circle,开启Choropleth,Number of levels选择为2,然后选择level 1,Data Level选择Custom Tileset,Vector URL选择在Mapbox创建的区域Tileset的名称,Source Layer name选择Tileset的layter,Vector property输入district。之后按照同样的过程来设置level 2,只是换成网格Tileset。

最后的效果如下

stockholm

利用Power BI制作分级地图报表相关推荐

  1. 利用Power BI制作RFM客户分析模型

    RFM客户分析模型 RMF模型是一种常见的客户分析模型.RFM是三个英文单词的简称,分别是最近一次消费 (Recency), 消费频率 (Frequency)以及消费金额(Monetary).RMF模 ...

  2. 利用Power BI形状地图制作着色中国地图

    由于Power BI自带的地图是全球地图,看中国的业务会不太方便,如何利用 Power BI 自带的形状地图来做着色的中国地图 步骤: 1 在地图选择器中下载中国地图json文件 地图选择器 2 需要 ...

  3. power bi 地图_如何使用Power BI创建地理地图-填充地图和气泡地图

    power bi 地图 该项目 (The project) This is the first article of a series dedicated to discovering geograp ...

  4. 利用Power BI自定义图表,原来图片还可以这么玩

    Power BI ​制作可视化报告时,为了展示效果,有时候需要用图片来展示,在 Power BI 中,关于图片的自定义视觉对象主要有下面三个,利用他们可以很轻松的进行图片可视化. 下面来看看这些视觉对 ...

  5. 利用Power BI计算组,设计个性化数据标签

    利用Power BI计算组,设计个性化数据标签 - 知乎 (zhihu.com)https://zhuanlan.zhihu.com/p/405532292

  6. 用Power BI 做ERP系统报表(小微企业管家)

    ERP系统在生产型企业使用很广泛,主要以订单为中心.这是ERP围绕的核心,销售行为产生订单,生产行为处理订单,发货行为完成订单.所以,这三种行为都属于事实表,其他相关表为维度表,如员工表,客户表. 要 ...

  7. 利用Power BI行级安全性,限制用户访问权限

    Power BI报告有多种分享协作方式,不仅可以让用户查看报告的所有数据,还可以根据用户角色进行数据限制,让特定用户只查看部分与之相关的数据. 按角色分享用到的就是PowerBI中的行级安全性(Row ...

  8. Power BI Paginated Reports分页报表

    为什么有时候使用分页报表,不直接使用power bi的报表.在SQL Server Reporting Services(SSRS)里就有分页报表的功能. 分页报表最适合需要为打印或 生成 PDF形式 ...

  9. powerbi python词云图_使用Power BI制作可爱的词云图

    不少星友曾问起PowerBI是否可以生成词云图,足见该图的流行度,在PowerBI中有一个专门的自定义视觉对象可以生成词云图:Word Cloud,这里就简单介绍一下该图的做法. 首先需要导入该自定义 ...

  10. 网站日志分析(二)——利用Quick BI制作企业化报表分析

    使用QuickBI展示分析数据 本文接上篇网站日志分析(一) • 在上一篇数据已经处理完毕,各种需要的表页已经生成 • 对于处理完的数据,下面将使用Quick BI进行编辑并以图表的形式进行展示. • ...

最新文章

  1. Thrust快速入门教程(二)——Vector的使用
  2. EOJ Monthly 2019.2 (based on February Selection) D 进制转换 【数学 进制转换】
  3. 计算机等级考试试题4,计算机等级考试二级模拟试题4
  4. 李开复:AI能在15年内取代40%~50%岗位
  5. cstring移除指定字符串_从String中移除空白字符的多种方式!?差别竟然这么大!...
  6. python的内存泄露_Python 程序的内存泄露,教你一招来解决?
  7. 已完成私有化交易 “网红第一股”如涵退市
  8. I00007 打印菱形字符图案
  9. mobile兼容性调整,根据rem,字体大小,视窗宽度
  10. 使用Nginx实现负载均衡
  11. 计算机网络工程师中级软考试题及答案,软考中级历年真题+章节题库
  12. linux设计论文题目,计算机linux本科毕业论文题目
  13. JavaScript入门教程
  14. 高通9008端口刷linux,高通命令进入9008端口方式汇总
  15. VFP命令,DBF数据内部函数
  16. java clh_CLH lock 原理及JAVA实现
  17. 如何有效开展小组教学_如何有效开展小组合作学习
  18. pycharm关联git
  19. 【FXCG】波段操作的四个步骤
  20. c++整人代码,超级加倍,让人承认我是大傻猪

热门文章

  1. 深度学习#1.有监督学习和无监督学习
  2. 新一代视频编码标准:HEVC、AVS2和AV1性能对比报告
  3. iphone6连接电脑后计算机不显示器,iPhone6怎么无故显示屏不亮了
  4. 【python】google的经纬度定位查询API
  5. APICloud 上传文件到云数据库2.0的代码实现
  6. IT行业未来发展前景如何?
  7. 如果编程语言是一门武功绝学
  8. 201万年薪,华为天才少年路径可以复制吗
  9. ArcGIS API for JavaScript三维管网之三维模型制作(无插件)
  10. 360软件小助手-壁纸存储路径