查看原文
其他

【数据分享】如何获取全国矢量地铁站点与线路数据?

姚小树 数读城事 2021-09-19
本文作者是【数读城事】1群热心群友姚小树,给大家带来全国矢量地铁站点与路线的获取方法。想直接获取数据的可以在文末找到关键词回复。

前言

【数读城事】学习交流群与群友交流的过程中我注意到地铁数据的获取并不容易,根据传统的POI数据绘制地铁线路费时费力(如:某一地铁站的多个出口,某个在建没有投入使用的站点,一个站点有时是多个线路的中转站)。今天在偶然间我翻阅了百度地图的地铁图JavaScript API,发现通过这个接口可以获取网页版的地铁图,近一步发现该数据是JSON格式的,可以抓取,便编写了一个py脚本获取了全国的地铁站点与线路数据,并写下这篇文章,希望能为大家提供方便。

值得一提的是,本人并不会JAVA,自学过一点点的Python,发现这个接口完全是偶然,代码也有很多不足的地方,结论中指出了一些问题,希望同学们多多批评指正。另外,AK(有浏览器端服务器端的区别,详情见百度开放平台)我就不放出来了,大家改成自己的就行了。

一、服务文档

  • 百度开放平台

http://lbsyun.baidu.com/

  • 地铁图JavaScript API

http://lbsyun.baidu.com/index.php?title=subway

  • 示例DEMO

http://lbsyun.baidu.com/jsdemo.htm#subway0_0

注意:DEMO中如要调用需在URL前加"https:"

二、分析过程

2.1 HTML文件

<!DOCTYPE html><html><head><meta charset="utf-8" /><meta name="viewport" content="initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no" /><title>地铁图展示</title><script type="text/javascript" src="https://api.map.baidu.com/api?type=subway&v=1.0&ak=您的AK"></script><style type="text/css">#container{height:100%}</style></head><body><div id="container"></div><script type="text/javascript">/*** 从所有城市列表中获取北京信息* 结果格式* {* keyword: 'zhengzhou',* name: '郑州',* citycode: '268'* }*//* globals BMapSub */var subwayCityName = '郑州';var list = BMapSub.SubwayCitiesList;var subwaycity = null;for (var i = 0; i < list.length; i++) {if (list[i].name === subwayCityName) {subwaycity = list[i];break;}}// 获取郑州地铁数据-初始化地铁图var subway = new BMapSub.Subway('container', subwaycity.citycode);subway.setZoom(0.5);</script></body></html>

2.2 调用后得到城市地铁图

2.3 分析XHR得到地铁站点与线路JSON

  • 网址示例:

https://api.map.baidu.com/?qt=subways&c=268&format=json&ak=您的AK&v=3.0&from=jsapi&callback=BMapSub._rd._cbk78501

  • 268为郑州城市编码,其他城市可查阅该附件。(附件在分享的文件夹内)

📎BaiduMap_cityCode_1102.txt

  • 删除callback进入网址后得到JSON数据

{ "result": { "type": 920, "error": 0 }, "subways": { "l": [ { "p": [ { "p_xmlattr": { "sid": "河南工业大学", "lb": "河南工业大学", "x": -578, "y": -432.9, "rx": 8, "ry": -10, "st": true, "ex": false, "iu": true, "rc": false, "slb": true, "ln": "郑州市|地铁1号线", "int": "2", "px": 12640317.93, "py": 4116581.76, "uid": "f235db2f54efea3bf942b28b" } }, { "p_xmlattr": { "sid": "郑大科技园", "lb": "郑大科技园", ......
  • 分析网址可以发现1条线路中对应多个地铁站点。

2.4 矢量化思路

2.4.1 HTML转PNG后加载到ARCGIS中描图。

缺陷:描图有误差,加载后需配置,截大图需要借助工具。

2.4.2 JSON数据中点与线抓取至CSV,点集转线。

抓取时构建线路字段,Point To Line(点集转线)工具连接即可。

三、代码实现

3.1 运行代码

3.1.1 导入包

  • transCoordinateSystem来自于@wandergis

  • github地址👇

  • https://github.com/wandergis/coordTransform_py

  • 调用了百度地理坐标转GPS坐标的方法

import requestsimport csvimport pandas as pdimport jsonimport mathfrom transCoordinateSystem import bd09_to_wgs84

3.1.2 设置基本信息

header为CSV中的信息标题,包括索引、百度米制X坐标、百度米制Y坐标、地铁站的名字、地铁线路的名字、线路的序号(方便在GIS中转线)、百度平面X坐标、百度平面Y坐标、GPS坐标X、GPS坐标Y。
base_infor = []lon_lat_infor = []
header = ['num','x','y','poi_name','route_name','route_num','bd_x','bd_y','wgs_x','wgs_y']num = 0bmap_key = '浏览器端AK'ak = '服务器端AK'

3.1.3 定义一个坐标转换方法

为百度米制坐标向百度地理坐标的转换。
def transform_coordinate_batch(coordinates): req_url = 'http://api.map.baidu.com/geoconv/v1/?coords='+coordinates+'&from=6&to=5&ak=' + bmap_key
data = requests.get(req_url) data = data.text data = json.loads(data) coords = '' if data['status'] == 0: result = data['result'] if len(result) > 0: for res in result: lng = str(res['x'])[:10] lat = str(res['y'])[:9] coords = coords + ";" + lng + "," + lat return coords.strip(";")

3.1.4 抓取数据:

在这里的268为郑州市的百度编码,如需其他城市的编码,可见百度开放平台或直接查看文件BaiduMap_cityCode_1102.txt。

URL为上述分析得到的网址,这一步得到了base_infor,接下来需要将百度米制坐标转化为百度平面坐标再转化为WGS坐标。
# 抓取基本数据city = '268' #268为郑州,城市编码见 BaiduMap_cityCode_1102.txturl = 'https://api.map.baidu.com/?qt=subways&c=%s&format=json&ak=%s&v=3.0&from=jsapi'%(city,ak)res = requests.get(url)json_res = res.json()subways = json_res['subways']['l']route_num = 0
for route in subways: route_num += 1 route_name = route['l_xmlattr']['lid'] for route_poi in route['p']: poi = route_poi['p_xmlattr'] poi_name = poi['sid'] if poi_name != '': num += 1 x = poi['px'] y = poi['py'] coordinates = coordinates +str(x)+','+str(y)+';' base_infor.append([num,x,y,poi_name,route_name,route_num])

3.1.5 坐标转换

这一部分得到了转换后的坐标,储存在列表lon_lat_infor中。
# 坐标转换# 调用函数与接口转换米制坐标为百度坐标coordinates_list_num = math.ceil(num/90) #确定调用接口的次数,一次传入坐标对90个。
for item in range(coordinates_list_num): zuobiao = '' for i in numlist_coor[90*(item):90*(item+1)]: zuobiao = zuobiao + i +';' mi_zuobiao = zuobiao[:-1]
result = transform_coordinate_batch(mi_zuobiao) result = result.split(';') mi_list = mi_zuobiao.split(';') for j1 in mi_list: mi_x = j1.split(',')[0] mi_y = j1.split(',')[1] bd_x = result[mi_list.index(j1)].split(',')[0] bd_y = result[mi_list.index(j1)].split(',')[1] # print(bd_x,bd_y) 打印出来看看数据怎么样

#调用transCoordinateSystem实现bd_09转换为WGS84坐标系 coord_wgs84 = bd09_to_wgs84(float(bd_x), float(bd_y)) lon = str(coord_wgs84[0])[:10] lat = str(coord_wgs84[1])[:9] # print(lon,lat) lon_lat_infor.append([bd_x,bd_y,lon,lat])

3.1.6 存储至CSV

前面的分析后我们得到了base_infor和lon_lat_infor两个列表型,转化为DataFrame后合并,得到end_result这个DataFrame,运用pandas.DataFrame.to_csv,存储为subway_zhengzhou.csv。
# 合并结果:base_infor与lon_lat_infor转为DataFrame后合并,写入subway.csv。left = pd.DataFrame(base_infor)right = pd.DataFrame(lon_lat_infor)end_result = pd.concat([left,right],axis=1)print(end_result)pd.DataFrame(end_result).to_csv('subway_zhengzhou.csv',mode ='w',index =False,encoding='gbk',header=header)

3.2 代码运行结果

3.2.1 print一下看看end_result是什么?

效果还可以,数据也没有出现缺失或不对位的情况。
Windows PowerShell版权所有 (C) 2016 Microsoft Corporation。保留所有权利。
PS E:\python> & "C:/Program Files/Python37/python.exe" e:/python/subway.py 0 1 2 3 4 5 0 10 1 12640317.93 4116581.76 河南工业大学 地铁1号线 1 113.536042 34.8279401 2 12640267.80 4115257.87 郑大科技园 地铁1号线 1 113.535598 34.8181372 3 12640225.76 4114004.12 郑州大学 地铁1号线 1 113.535229 34.8088533 4 12640185.96 4112164.74 梧桐街 地铁1号线 1 113.534887 34.7952354 5 12640184.88 4110735.11 兰寨 地铁1号线 1 113.534887 34.784654.. .. ... ... ... ... .. ... ...90 91 12674935.63 4083938.18 康平湖 城郊线 4 113.847217 34.58610691 92 12675649.65 4082749.82 兰河公园 城郊线 4 113.853686 34.57718892 93 12675659.47 4081569.23 恩平湖 城郊线 4 113.853783 34.56842493 94 12675402.83 4079194.33 综合保税区 城郊线 4 113.851480 34.55082794 95 12674873.23 4076049.16 新郑机场 城郊线 4 113.846695 34.527542
[95 rows x 8 columns]

3.2.2 储存的CSV是什么样子的?

这是郑州的地铁数据,看起来还可以,我们加载到GIS中看一看。

3.2.3 加载到GIS中的郑州地铁数据与地铁图JavaScript的区别

底图为天地图,将wgs_x作为经度,wgs_y作为纬度,加载到GIS中,发现与天地图(CGCS2000国家大地坐标系)基本可以对应。
点数据转线后如图所示。

与高德地图公布的地铁图相比,嗯...地图会骗人(@GISer),除了导入GIS平台中的地铁图更加长之外,也出现了一些问题,比如蓝色的5号线没有连上,拐弯处点转线处理的不好,最低端红色的城郊线中有一截本应向左凸出,但是在点转线并没有实现,事实上由于拐弯处没有地铁站,仅靠机器确实无法识别。

四、总结

本文无意中发现了百度地图中地铁数据的接口,并通过编写python脚本实现了抓取,导入GIS平台中发现整体状况较好,但同时也遇到了一些问题。

问题1:GIS中的地理坐标转换太麻烦了!坐标转换是文本的一大难点,虽然编写了脚本,但由于本人水平有限,代码仍有较多冗余。

问题2:为了美观,地铁图的绘制时会刻意改变站点的位置,距离发生变化,但是拓扑关系不会改变。
问题3:地铁数据中仅包括站点,在连接时会出现与实际情况不相符合的情况。
即便如此,通过本文的思路我们可以轻松的获取地铁站点与线路数据,拿到这些数据后,我们通过在GIS中加载底图,将点集转线与实际的地铁线路进行对比仍然能够为研究或规划提供方便。

五、数据分享

增加循环后获得了各个城市的地铁站点与线路的CSV文件,每个城市一个,共41个文件文件名构成为subway+"_"+百度城市代码+城市名称,如需转换成矢量数据,导入GIS软件——Display XY Data——导出为矢量数据——Point To Line——根据实际进行修补。数据获取时间为2020年7月11日。
📎city_subway.7z(文件为csv格式,共41个)
在公众号后台回复“地铁”两个字就会收到数据获取方式,emmmm,就,你们懂的。

防止没看到,再说一遍:

在公众号后台回复“地铁”两个字就会收到数据获取方式,emmmm,就,你们懂的。


那今天就到这里结束啦,欢迎留言讨论。文中的图片未经许可不要随便“引用”。

如果可以的话,希望能够转发分享,点个在看并且点个,给赞赏~~也欢迎规范转载~

也希望大家和我多留言互动啊!(据说这样可以增加我的推送在你的订阅号里出现的概率)

END>


如需全文转载文章、投稿或者合作

可添加微信

(回复超慢!!!)

(不要添加我问各种问题,我大概率不会的==)

(入群请一定要备注入群)

(添加后会在晚上非工作时间通过,请稍安勿躁)

此外,我申请了一个微博账号,虽然现在没啥粉丝==

但我会在上面预告更新的!(还有吐槽和碎碎念)

最后,感谢关注【数读城事】

我会尽量在工作之余努力更新的


使用关键词搜索历史文章请点击【阅读原文

: . Video Mini Program Like ,轻点两下取消赞 Wow ,轻点两下取消在看

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存