Python处理OpenStreetMap(OSM)数据

时间:2020-9-17 作者:admin

问题

做项目的时候发现osm自定义区域导出后,还是会有延伸到区域外的数据,且会影响其他软件导入osm后的锚点,如下图,黑色背景的区域全是多余的数据

推测官网导出的规则为:只要某条道路的其中一部分存在于我们选择的区域内,osm就会导出该条道路包含的全部节点。

这个问题似乎没办法避免,于是想到了用python手动处理一下osm数据,将多余节点删除。

分析

首先进行分析,我们先观察一下osm的数据格式

发现osm数据采用的也是xml格式,所以我们可以使用ElementTree API来处理

我们还能看到,其中第3行bounds正是我们所设置的导出区域范围,而其他node也有它们自己所在的经纬度信息

所以方法很简单:我们先获取到bounds的经纬信息,再用它来遍历所有node做对比,删除经纬度不在范围内的行即可。

注意在所有node列出后,还有一些way块。经过对比可发现,其中子节点nd的ref属性正对应前面node的id属性,说明从这里开始的数据是在引用前面的节点来构成道路。所以我们删了某个node后同样需要到后面把它的引用也删掉,否则后续导入就会报错!

分析完毕,下面直接上代码。

代码

#author:dizzyding
#date:2020/9/16
#verison:python3.8

import xml.etree.ElementTree as xee
 
def osmProcess():
    # 读取文件
    domTree = xee.parse("osm.osm")

    # 获得所有节点的内容
    root = domTree.getroot()

    # 获得所选区域的经纬度范围
    bound = root.findall("bounds")
    maxLat = float(bound[0].get("maxlat"))
    maxLon = float(bound[0].get("maxlon"))
    minLat = float(bound[0].get("minlat"))
    minLon = float(bound[0].get("minlon"))
    # 输出所选区域的经纬度范围
    print('Bounds:' + '\n' + 'minLat: ' + str(minLat) + '\n' + 'maxLat: ' + str(maxLat) + '\n' + 
    'minLon: ' + str(minLon) + '\n' + 'maxLon: ' + str(maxLon) + '\n' +'Nodes: ')

    # 存储不在所选区域内的node ID
    IDlist = []

    # 逐个检查node
    nodes = root.findall("node")
    for node in nodes:
        # 当前节点的经纬度和ID
        Lat = float(node.get("lat"))
        Lon = float(node.get("lon"))
        ID = node.get("id")
        #输出node信息
        print('nodeID:' + ID + ', Lat:' + str(Lat) + ', Lon:' + str(Lon) + ', Bound: ' , end='')
        # 判断
        if Lat < minLat or Lat > maxLat or Lon < minLon or Lon > maxLon:
            root.remove(node)
            IDlist.append(ID)
            #输出bound比对情况,若Lat和Lon均不符合则只输出Lat
            if Lat < minLat:
                print('Lat < min')
            elif Lat > maxLat:
                print('Lat > max')
            elif Lon < minLon:
                print('Lon < min')
            elif Lon > maxLon:
                print('Lon > max')
        else:
            print('Satisfied')

    # 删除不在所选区域内的node在后续道路的参照行
    ways = root.findall("way")
    for ID in IDlist:
        for way in ways:
            refnodes = way.findall("nd")
            for node in refnodes:
                if node.get("ref") == ID:
                    way.remove(node)

    # 输出文件
    domTree.write("out.osm",encoding="utf8")
 
if __name__=="__main__":
    osmProcess()

效果

运行完后效果是这样的,超出的节点全被干掉了

丢进Houdini里也能直接居中对齐了,不用再手动调参数

声明:本文内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎进行举报,并提供相关证据,工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。