如何使用networkx绘制带节点坐标的模拟路网

        NetworkX是一个很强大的python网络处理工具包,内置了常用的图和复杂网络分析方法。一般网络处理不关心节点的坐标,因为网络分析通常只关心节点的连通关系和边的权值属性,一般并不涉及坐标值。但是在对路网进行模拟的时候,大多需要将网络直观地呈现出来,这是个简单但是网络上的文章鲜有涉及的问题。针对网络绘制,NetworkX提供了一个简单的方法,此方法依赖于matplotlib中的pyplot函数,于是在开头需要导入以下两个工具包:

import networkx as nx
import matplotlib.pyplot as plt    

    NetworkX和matplotlib的安装和配置请查阅网络教程,假设以下是路网,中间褐色的点是村庄,绿色的方块是城市,路网的节点由村庄、城市和交叉节点组成。

图1 实际路网
对上面的路网中的节点进行人工编号:
图2 节点编号

    在图2中,笔者将村庄编码为1-29的29个点,交叉点编码为1021-1024共24个点,为了使图更为紧凑,还调整了原图,添加了两个交叉点1025和1026,城市节点编码为'a-e'。准备工作做好以后,开始使用networkx新建网络。

roads=nx.Graph()#新建一个无向图

    将附带人口属性的城市节点添加到网络中    

#城市的人口数据
rcapacity=[24115, 15092, 16194, 22563, 19311]
rnlist=['a','b','c','d','e'] #城市节点列表
#将城市添加到网络中
for i in range(0,5):
    roads.add_node(rnlist[i],capacity=rcapacity[i])

    将附带人口属性的村庄节点添加到网络中:

npop=[637, 1517, 4683, 1860, 1102, 4684, 1233, 4421, 2286, 529, 4489, 3071, 4268,\
    2542, 3465, 4751, 2284, 2220, 4488, 4483, 3804, 2455, 4895, 1608, 2718, 1459,\
    2221, 3162]
#村庄节点
for i in range(1,29):  
    nnlist.append(i)   
    roads.add_node(i,population=npop[i-1])
将交叉点加入网络
for j in range(1001,1025):
	vnlist.append(j)
roads.add_nodes_from(vnlist)
#与村庄相关的边
nedges=[(1,2),[1,1008],(2,1),(2,3),(3,4),(3,2),(4,3),(4,5),(4,1001),(5,4),(5,1003),\
           (6,1005),(6,'a'),(7,'a'),(7,1011),(8,1004),(9,1004),(9,'b'),\
          (10,13),(10,1009),(11,'c'),(11,1010),(12,13),(12,1010),(13,10),(13,12),\
           (13,14),(13,1007),(14,15),(14,13),(15,1014),(15,1012),(16,1011),(16,1012)\
          ,(17,18),(17,1018),(17,1014),(18,19),(18,1010),(18,1015),(20,1017),(20,1018),\
        (21,1017),(21,22),(22,1016),(22,23),(24,1019),(24,1018),(25,26),(26,25)\
 ,(26,1019),(26,1013),(27,1022),(27,1024),(28,1021),(28,1023)]

#与交叉点相关的边
vedges=[(1001,4),(1001,1004),(1001,1002),(1002,1003),(1002,'a'),(1003,1002),(1003,1005),\
                (1004,9),(1004,1001),(1004,8),(1005,4),(1005,6),(1005,1003),(1005,1006),\
                (1006,1007),(1006,1005),(1007,13),(1007,1006),(1008,1009),\
                (1008,1),(1009,'c'),(1009,10),(1010,18),(1010,11),(1010,12),(1011,16),\
                (1011,7),(1012,16),(1012,1013),(1012,15),(1013,1014),(1013,26),(1013,1012),(1014,15),\
                (1014,17),(1015,'c'),(1015,18),(1015,'d'),(1016,'d'),(1016,22),(1017,20),(1017,19),\
                (1017,'d'),(1017,21),(1018,24),(1018,17),(1018,20),(1019,24),(1019,'e'),(1020,'e'),\
                (1020,1023),(1021,'e'),(1021,28),(1021,1022),(1022,8),(1022,27),(1023,28),(1023,1024),\
                (1024,27),(1024,'b')]

#对原图进行了一些调整,便于显示,添加了几个节点
roads.add_nodes_from([1025,1026])
tedges=[(1025,1006),(1025,1007),(1025,1011),(1026,23),(1026,1019),(1026,'e'),\
                (1026,1020)]
                

上面的代码是边的列表,注意因为是无向图,nedges和vedges中有的边是重复的,笔者为免遗漏,统计了每个点的关联边,无向图会忽略重复的边。下面的代码将这些边加入到网络:

#边的集合
edges=nedges+vedges+tedges;
for e in edges:
        roads.add_edge(e[0],e[1],length=CalLength(e[0],e[1],pos))

这样的话,路网就建立完成了,我们调用绘制语句,会得到下面的结果:

nx.draw(roads)
plt.show()

                                                                                                              图3 绘制图

对于坐标数据文件,需要借助numpy这个专用的数组处理工具读到数组中:
import numpy as np
上面的语句导入numpy工具,下面的语句读取坐标数据:
nodes=np.loadtxt('E:/data0730/nodes.txt',dtype=np.int32) #转移单元节点坐标
vnodes=np.loadtxt('E:/data0730/vnodes.txt',dtype=np.int32) #交叉节点坐标
receivers=np.loadtxt('E:/data0730/receivers.txt',dtype=np.int32) #城市节点坐标
    这里要注意,python在读取路径的时候会把默认的windows路径的/当成转义符号,读取文件的时候需要使用//或者使用linux方式进行路径表达,同时需要明确指定数据类型dtype,否则会报错。 这些点的坐标如果有已知的数据,可以直接建数组写入。笔者这里使用的是基于EmguCV(OpenCV的.NET封装)的小程序,通过手动依次点选各个节点从而把坐标写入到文本文件中,生成三个文本文件。
     图3在绘制的时候没有考虑坐标布局,点是按照默认的布局进行分布的,其实在计算的时候并比影响。为了绘制出正确的路网图,需要使用到下面三个函数:    
draw_networkx_nodes 用于绘制节点
draw_networkx_edges 用于绘制边
draw_networkx_labels 用于绘制标识
下面是绘制节点的函数
def draw_networkx_nodes(G, pos,
                        nodelist=None,
                        node_size=300,
                        node_color='r',
                        node_shape='o',
                        alpha=1.0,
                        cmap=None,
                        vmin=None,
                        vmax=None,
                        ax=None,
                        linewidths=None,
                        label=None,
                        **kwds):
    G是待绘制的图,pos是坐标值列表,以字典的形式存在,也可是spring_layout , random_layout,circle_layout,shell_layout以下几种布局模式,nodelist是待绘制的节点列表,后面的值可以顾其名而思其义。pos可以是精确的nodelist里面的坐标值,也可以有冗余数据,但是必须包含nodelist里面的每一个节点的坐标数据。pos通过下面的形式构建字典:
#获取节点与坐标之间的映射关系,用字典表示
rpos=dict(zip(rnlist,receivers))
npos=dict(zip(nnlist,nodes))
vpos=dict(zip(vnlist,vnodes))
#将所有的节点坐标合并到一个列表
pos={}
pos=rpos.copy()
pos.update(npos)
pos.update(vpos)
#额外添加的节点也要加入字典
tdict={1025:(286,126),1026:(260,319)}
pos.update(tdict)
绘制边和标识的函数参数大体相似:
draw_networkx_edges(G, pos, edgelist=None, width=1.0, edge_color='k', style='solid', alpha=1.0, edge_cmap=None, edge_vmin=None, edge_vmax=None, ax=None, arrows=True, label=None, **kwds)
draw_networkx_labels(G, pos, labels=None, font_size=12, font_color='k', font_family='sans-serif', font_weight='normal', alpha=1.0, bbox=None, ax=None, **kwds)
下面是具体的绘制语句:
 #分布绘制村庄、交叉点、城市
 nx.draw_networkx_nodes(roads,rpos,rnlist,node_size=50,node_color='r',with_labels=True)
 nx.draw_networkx_nodes(roads,npos,nnlist,node_size=20,node_color='g',with_labels=True)
 nx.draw_networkx_nodes(roads,vpos,vnlist,node_size=10,node_color='b',with_labels=True)

 #绘制边
 nx.draw_networkx_edges(roads,pos,nedges)
 nx.draw_networkx_edges(roads,pos,vedges)
 nx.draw_networkx_edges(roads,pos,tedges)

 #标志字典,构建节点和标识点之间的映射关系,这里直接用节点名作为标识
 nlabels=dict(zip(nnlist,nnlist))
 rlabels=dict(zip(rnlist,rnlist))

 #个性化标志
 nx.draw_networkx_labels(roads,rpos,rlabels,font_size=20)
 nx.draw_networkx_labels(roads,npos,nlabels,font_size=10)
以上代码完成以后,使用下面的代码输出绘制结果:
plt.show()
其结果如下图:
图4 绘制结果

    从本文可以看出networkx提供的绘制方法还是很完善的,可以任意定制节点和边的大小、颜色和形状,以及其标识的属性和形状。本来笔者做实验都是使用的matlab,深感于networkx在网络分析方面的强大,因此投入到python的阵营。如果需要进行网络处理和呈现,networkx会是一个很好的备选工具。







  • 13
    点赞
  • 82
    收藏
    觉得还不错? 一键收藏
  • 16
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 16
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值