读取地图`XML`文件
用`networkX`构建交通网络结构
导包
将node加入到有向图对象`nx.DiGraph() `中
将edge加入到有向图对象`nx.DiGraph() `中
画出交通网络图
上一篇文章,我们讲到下载OpenstreetMap的地图数据。
文章链接:
Python+networkX+OpenStreetMap实现交通数据可视化(一):用OpenStreetMap下载地图数据
接着上一篇文章,我们继续来看如何将交通数据可视化。
我们仍然以manhattan为例。一般情况下,我们也许会选用ArcGIS
, pyechart
, folium
等工具来做交通的可视化,但是这些方法和算法嵌套的话,并不是很方便。本文介绍的Python
的networkX
库可以非常方便的来以代码的形式绘制城市的交通网络图。这样的好处在于:
在此之前,我们需要利用上一篇博文:Python+OpenStreetMap实现交通数据可视化(一):用OpenStreetMap下载地图数据manhattan的路网数据,是XML
格式的,大小为94.9 MB。接下来我们用Python
读取该数据,并用networkX
包将路网数据绘制成下面的样子
是不是觉得恰好符合你的需求?那接下来就来一起把它画出来。
XML
文件首先需要准备好已经下载好的xml
格式的地图数据文件,我这里是interpreter_manhattan
。然后直接用python
进行读取。Python
有一个模块xml
中的xml.dom.minidom
能够非常方便的读取出xml
中的信息。具体代码如下:
import xml.dom.minidom
# dom = xml.dom.minidom.parse('map_data/interpreter_beijing')
# dom = xml.dom.minidom.parse('map_data/interpreter_shenzhen')
# dom = xml.dom.minidom.parse('map_data/interpreter_shanghai')
# dom = xml.dom.minidom.parse('map_data/interpreter_guangzhou')
# dom = xml.dom.minidom.parse('map_data/interpreter_hangzhou')
# dom = xml.dom.minidom.parse('map_data/interpreter_chengdu')
dom = xml.dom.minidom.parse('map_data/interpreter_manhattan')
root = dom.documentElement
root
print(root.nodeName)
print(root.nodeValue)
print(root.nodeType)
print(root.ELEMENT_NODE)
[Out]: osm
None
1
1
上述代码直接调用xml.dom.minidom.parse()
方法,将xml
文件读取成一个对象,所有数据全部存储到dom
中,我们提取用dom.documentElement
提取该对象的所有元素,然后进行之后的操作。
在这之前,我们首先来了解一下xml
地图数据文件的结构(manhattan)
可以看到,首先是一堆标识信息,用来标识这个城市是哪里,有多少人口什么的,这些我们不关心。
再往下走,就是一系列的这样格式的信息:
/*这是路网节点信息*/
……
……
/*这是路的信息*/
way>
/*这是路之间的关系信息*/
……
relation >
可以看到,我们可以通过提取node
、way
和relation
的信息,来绘制路网图。在node
中,除了id
,有的还会有一些tag
,这些tag
里面记录了这个标签类别的信息。比如node
里面的tag
记录的是node
的其他属性,way
里面的tag
记录的是way
的其他属性。这里我们只读取基本的经纬度信息。
networkX
构建交通网络结构networkX
是处理吞的一个非常强大的工具包,具体用法参见相关教程。
import networkx as nx
import matplotlib.pyplot as plt
import copy
# 构建有向图对象
Map = nx.DiGraph()
nx.DiGraph()
中node = root.getElementsByTagName('node')
print(node[0].nodeName)
pos_location = {} # position of all nodes in the graph" to draw the figure
loc = []
for i in range(len(node)):
ID = node[i].getAttribute('id')
lat = float(node[i].getAttribute('lat'))
lon = float(node[i].getAttribute('lon'))
# print(ID, lat, lon)
Map.add_node(ID
, ID = ID
, lat = lat
, lon = lon
)
pos_location[ID] = (lon, lat)
loc.append([lon, lat])
这里可以象征性的看看图中的数据
import pandas as pd
LOC = pd.DataFrame(loc)
LOC.describe()
在这里插入图片描述
nx.DiGraph()
中地图的结点加完了,下面来将edge
循环加入进去。为了便于理解,我们用比较麻烦的循环。
在做之前我们先看一下一个way
是怎么构成的:可以看到,先给了way
的ID
,之后是一系列的结点node
的依次连接,构成一条way
,因此我们提取这些way
中的node
的id
,然后循环构成edge
加入到之前创建的Map = nx.DiGraph()
即可。(这里的方法比较笨,大神可自行优化)
首先我们需要得到way
下面的标签名为nd
的元素,然后获得nd
的属性ref
中的具体的id
,这里用函数:way.getElementsByTagName('nd')[0].getAttribute('ref')
完整代码如下:
way_set = root.getElementsByTagName('way')
# print(way_set[0].nodeName)
for way in way_set:
previous_node = start_node_id = way.getElementsByTagName('nd')[0].getAttribute('ref')
end_node_id = way.getElementsByTagName('nd')[-1].getAttribute('ref')
for sub_node in way.getElementsByTagName('nd'):
current_node_id = sub_node.getAttribute('ref')
if(current_node_id != start_node_id):
Map.add_edge(previous_node, current_node_id
,way_type = 0
)
previous_node = current_node_id
# print(sub_node.getAttribute('ref'))
这里,我们就把所有的信息添加完成了,但是画出来图发现,有一些路都延伸到纽约其他地方去了,这些我们不要,因此我们可以手动设置限制,把一些超出预期范围的node
移除掉(该步骤可有可无)
G2 = copy.deepcopy(Map)
for node in Map.nodes:
if((Map.nodes[node]['lat'] >= 40.8815 or Map.nodes[node]['lat'] <= 40.6960) and node in G2.nodes):
G2.remove_node(node)
if((Map.nodes[node]['lon'] <= -74.0327 or Map.nodes[node]['lon'] >= -73.9078) and node in G2.nodes):
G2.remove_node(node)
G3 = nx.to_undirected(G2)
完成上面的操作,就可以愉快的画图了
plt.rcParams['figure.figsize'] = (20, 20) # 单位是inches
nx.draw(G3
, pos=pos_location
# , with_labels = True
, node_size = 0.0001
, node_color = 'grey' #'#FF8000' #'#6DCAF2' # '#FF8000' # '#6DCAF2' # '#B9F1E5' #'grey' # '#FFBFBF' # 'k' #nodes_col.values() #'y'
, width = 0.5 # default = 1.0 , Line width of edges
# , font_size = 4
# , font_family = 'arial'
, edge_color = 'grey' # b, k, m, g,
)
fig_name = 'result_figures/manhattan_map_dpi200.jpg'
plt.savefig(fig_name, dpi=200)
plt.show()
然后效果就是下面的样子
当然,你还可以调控相关参数,搞出一些花里胡哨的小图,比如:
作者:刘兴禄,清华大学,清华伯克利深圳学院 (博士在读)
我的CSDN主页:
https://blog.csdn.net/HsinglukLiu?spm=1010.2135.3001.5113
欢迎前往关注!