Python+networkX+OpenStreetMap实现交通数据可视化(二):OpenStreetMap+Python画城市交通路网图

Python+networkX+OpenStreetMap实现交通数据可视化(二):OpenStreetMap+Python画城市交通路网图

我们仍然以manhattan为例。
一般情况下,我们也许会选用ArcGIS, pyechart, folium等工具来做交通的可视化,但是这些方法和算法嵌套的话,并不是很方便。本文介绍的PythonnetworkX库可以非常方便的来以代码的形式绘制城市的交通网络图。这样的好处在于:

  1. 个性化的增删地图数据
  2. 个性化得地图显示:线条粗细,颜色
  3. 增加删改额外信息

在此之前,我们需要利用上一篇博文:
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)

可以看到,首先是一堆标识信息,用来标识这个城市是哪里,有多少人口什么的,这些我们不关心。

再往下走,就是一系列的这样格式的信息:

/*这是路网节点信息*/
  <node id="30807307" lat="40.7921336" lon="-73.9621830"/>
  <node id="30807308" lat="40.7920236" lon="-73.9621757"/>
  ……
  ……
  /*这是路的信息*/
  <way id="247839519">
    <nd ref="2547000603"/>
    <nd ref="2547000609"/>
    <nd ref="2547000630"/>
    <nd ref="2547000628"/>
    <nd ref="2547000623"/>
    <nd ref="2547000603"/>
    <tag k="addr:housenumber" v="15"/>
    <tag k="addr:postcode" v="10012"/>
    <tag k="addr:street" v="Bleecker Street"/>
    <tag k="building" v="yes"/>
    <tag k="height" v="12.0"/>
    <tag k="nycdoitt:bin" v="1008444"/>
  </way>
/*这是路之间的关系信息*/
  <relation id="61320">
    <member type="node" ref="316976705" role="admin_centre"/>
    <member type="node" ref="316976734" role="label"/>
    <member type="way" ref="37573502" role="outer"/>
    ……
  </relation >    

可以看到,我们可以通过提取nodewayrelation的信息,来绘制路网图。在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()  

将node加入到有向图对象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() 

在这里插入图片描述

将edge加入到有向图对象nx.DiGraph()

地图的结点加完了,下面来将edge循环加入进去。为了便于理解,我们用比较麻烦的循环。

在做之前我们先看一下一个way是怎么构成的:
在这里插入图片描述
可以看到,先给了wayID,之后是一系列的结点node的依次连接,构成一条way,因此我们提取这些way中的nodeid,然后循环构成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()   

然后效果就是下面的样子

在这里插入图片描述
当然,你还可以调控相关参数,搞出一些花里胡哨的小图,比如:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/HsinglukLiu/article/details/107814392
今日推荐