利用Python从OSM中进行道路点的提取并导出json文件(附
osm是一个开源地图,数据元素有node、ways、relations。nodes是组成道路网数据的基本元素,它可以组成其它地图结构,如ways道路或者区域;ways是node序列化的数据,ways可以是道路或者是区域,这里我们要用到的node是路的node,所以下面我要做的是如何从osm 的xml格式中提取道路的node。
图1 点、路、区域
osm的xml格式数据如下:
图2 osm的xml格式数据从上面图中,我们可以看到,这条way是highway,是公路。
对osm数据的处理,就是将所有是道路的点给收集起来. 这里我刚刚想起, 能不能先判断是道路,然后再把点添加呢?注意在ways中点是id表示的,所以如果要添加点, 那只能是先把所有的点先存起来形成一个字典.再通过key = id,得到value = {经度、纬度}。这是一种思路,另一种思路是, 先得到所有的点先都添加进来,(注意有些点不是道路上的点),然后把不是道路的点进行剔除。按理来说,这两种方法都是行得通的。所以今天我会依照这两种思路对osm路网数据进行处理。注意这里osm地图是xml格式的,所以需要对其进行Dom/SAX格式解析。需要用到xml.dom.minidom. / xml.parser.expat库。有关对xml格式的处理可以参考前面的博客:python解析xml
下面是第二种思路,先将所有的点(node)全部添加进来,然后根据不是路的信息将node进行剔除。
import json
import xml.dom.minidom
dom = xml.dom.minidom.parse('map_big.osm')
root = dom.documentElement
nodelist = root.getElementsByTagName('node')
waylist = root.getElementsByTagName('way')
node_dic = {}
#统计记录所有node
for node in nodelist:
node_id = node.getAttribute('id')
node_lat = float(node.getAttribute('lat'))
node_lon = float(node.getAttribute('lon'))
node_dic[node_id] = (node_lat, node_lon)
print (len(node_dic))
#排除非路node
for way in waylist:
taglist = way.getElementsByTagName('tag')
road_flag = False
for tag in taglist:
if tag.getAttribute('k') == 'highway':
road_flag = True
if not road_flag:
ndlist = way.getElementsByTagName('nd')
for nd in ndlist:
nd_id = nd.getAttribute('ref')
if nd_id in node_dic:
node_dic.pop(nd_id)
#print len(node_dic)
with open('pure_map_big.json', 'w') as fout:
json.dump(node_dic, fout)
dom对xml的处理,是将文档内容全部加载到内存中,解析形成一棵树,通过对树的操作来对xml数据操作,我们可以调用dom的函数查询或者修改文档内容。它的优点也是它的缺点,正因为所有内容在内存中,所以对于一些大块头的文件,电脑就会吃完内存,然后崩了。
注:这里的所有元素都可以是多个的。可以通过getElementsByTagName就可以得到。
至于是不是道路(highway),可以通过tag==highway来判断,然后把不是道路的点并且存在node_list中的点进行剔除。如此操作完成后,剩下的node就是路上的点了。这个工作为以后地图匹配奠定基础。后期会实现由轨迹点映射到路段上,不过我现在更加明晰,路段是由多个这样node组成。
得到的道路节点数:134447
第一种思路,只将路上的点添加进来。
import json
import xml.dom.minidom
dom = xml.dom.minidom.parse('map_big.osm')
root = dom.documentElement
nodelist = root.getElementsByTagName('node')
waylist = root.getElementsByTagName('way')
node_dic = {}
#统计记录所有node
for node in nodelist:
node_id = node.getAttribute('id')
node_lat = float(node.getAttribute('lat'))
node_lon = float(node.getAttribute('lon'))
node_dic[node_id] = (node_lat, node_lon)
node_dic2={}
#得到路node
for way in waylist:
taglist = way.getElementsByTagName('tag')
road_flag = False
for tag in taglist:
if tag.getAttribute('k') == 'highway':
road_flag = True
break
if road_flag:
ndlist = way.getElementsByTagName('nd')
for nd in ndlist:
nd_id = nd.getAttribute('ref')
node_lat = node_dic[nd_id][0]
node_lon = node_dic[nd_id][1]
node_dic2[nd_id] = (node_lat, node_lon)
print (len(node_dic2))
with open('pure_map_big2.json', 'w') as fout:
json.dump(node_dic2, fout)
相关资料:
Python3 字典 in 操作符
Python3 字典 pop() 方法
Python3 字典 pop() 方法
(唉,Python有的地方都忘了,码一下,别又忘了。。)
上面这两思路的时间复杂度都是相同的。结果不一样。当然,我不是一个个看的,只是大致看了json文件的大小,两个文件不一样,试想一下,哪个大哪个小?要回答这个问题,先看下面这张图。
node分为三份,左边代表地点等其它点,既不是属于way中的道路点,也不属于way中的非路点。刚开始我以为node就分为两部分,要么是路上的点,要么是非路上的点。所以我以为得到路点和从node中剔除非路点是一样的。实则不然,我应该是漏了一些点,就像左边的点。这里是我想到的。
上面是dom来解析xml,如果电脑内存吃紧,估计程序跑起来够呛。(我的内存狂升2G),SAX来处理一下。明天更新。