2022年 11月 8日

python将osm文件转换为可应用于matsim的network文件

参考了许多文章,包括b站up主老程序员老关、csdn:Liu Zhian还有很多边边角角的代码、文章,孩子为写毕业论文踩碎了心。

文章目录

  • 1.将osm文件有效数据提取到csv
  • 2.将提取出的数据转换为network文件所需数据
  • 3.将数据转化为network.xml文件
  • 4终于搞完这个17w行的数据了呜呜呜 喜大普奔!!!

1.将osm文件有效数据提取到csv

osm文件有两大部分,分开操作,将其分为两个文件
在这里插入图片描述
分成两个文件:
在这里插入图片描述
下面是两个文件

<?xml version='1.0' encoding='UTF-8'?>
<nodes>
  <node id='-101752' lat='40.6592449' lon='109.7662487' />
  <node id='-101753' lat='40.6587893' lon='109.7727179' />
  <node id='-101755' lat='40.6585103' lon='109.7767641' />
  <node id='-101756' lat='40.6581603' lon='109.7838497' />
  <node id='-101757' lat='40.6581441' lon='109.7841119' />
  <node id='-101758' lat='40.6459848' lon='109.886676' />
  <node id='-101759' lat='40.6456042' lon='109.8865427' />
  <node id='-101760' lat='40.6426309' lon='109.8838162' />
  <node id='-101761' lat='40.6425291' lon='109.8836902' />
  <node id='-101762' lat='40.6424558' lon='109.8835789' />
  <node id='-101763' lat='40.6424222' lon='109.8833187' />
  <node id='-101764' lat='40.6426996' lon='109.8780347' />
</nodes>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
<?xml version='1.0' encoding='UTF-8'?>
<links>
  <way id='-101783' action='modify'>
    <nd ref='-101752' />
    <nd ref='-101753' />
    <nd ref='-101755' />
    <nd ref='-101756' />
    <nd ref='-101757' />
    <tag k='highway' v='primary' />
    <tag k='osm_id' v='13456925' />
    <tag k='z_order' v='7' />
  </way>
  <way id='-101785' action='modify'>
    <nd ref='-101758' />
    <nd ref='-101759' />
    <nd ref='-101760' />
    <nd ref='-101761' />
    <nd ref='-101762' />
    <nd ref='-101763' />
    <nd ref='-101764' />
    <nd ref='-101767' />
    <nd ref='-101768' />
    <nd ref='-101769' />
    <nd ref='-101771' />
    <nd ref='-101772' />
    <nd ref='-101773' />
    <nd ref='-101774' />
    <nd ref='-101775' />
    <nd ref='-101777' />
    <nd ref='-101778' />
    <tag k='highway' v='primary' />
    <tag k='osm_id' v='13457299' />
    <tag k='z_order' v='7' />
  </way>
</links>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35

下面是python处理代码:

#生成nodes部分
import xml.etree.ElementTree as ET
tree = ET.parse('nodes.xml')#输入xml文档树
print(type(tree))#xml.etree.ElementTree.ElementTree
root = tree.getroot()#root是根元素
print(type(root))#xml.etree.ElementTree.ElementTree
print(root.tag) #notes
with open("nodes.txt","w")as f:
    for index,child,in enumerate(root):
        f.write('{0}'.format(child.attrib))
        f.write('\n')       
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
#生成links部分
from xml.dom.minidom import parse
def readXML():
    domTree = parse("./links.xml")
	# 文档根元素
    rootNode = domTree.documentElement
    print(rootNode.nodeName)
	# 所有顾客
    links = rootNode.getElementsByTagName("way")
    with open("links.txt","w")as f:
        f.write("****所有way信息****")
        f.write('\n')
        for way in links:
            ways = way.getElementsByTagName("nd")
            if way.hasAttribute("id"):
                f.write('\n')
                f.write('id:{0}'.format(way.getAttribute("id")))
                # name 元素
                for nd in ways:
                    if nd.hasAttribute("ref"):                    
                        f.write('ref:{0}'.format(nd.getAttribute("ref")))
if __name__ == '__main__':
    readXML()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

将生成的结果处理一下,去掉无用字符,就得到数据啦,再把数据做成逗号分隔格式,就可存入csv保存了。


2.将提取出的数据转换为network文件所需数据

提取出的数据中,nodes部分需要将地理坐标系转换为投影坐标系;links部分需要将站点编号与地理坐标对应,从而求出每条路径长度。

  • nodes与links的目标格式:

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

  • nodes部分坐标系转换
    直接暴力套用MATLAB代码:(生成的XY就是平面坐标系矩阵)
%% 转化坐标系,需要参数为点的经纬度矩阵xy
%MATLAB程序实现经纬度转换成平面尔坐标:
M_PI=3.14159265358979323846;
L = 6381372 * M_PI * 2; %地球周长  
W = L; % 平面展开后,x轴等于周长  
H = L / 2; % y轴约等于周长一半  
mill = 2.3; % 米勒投影中的一个常数,范围大约在正负2.3之间  
n=size(xy,1);
%lon=120.7015202;%经度
%lat=36.37423;%纬度
new_xy=[];
XY=[0,0];
for i =1:n
    lon=xy(i,1);
    lat=xy(i,2);
    x = lon * M_PI / 180; % 将经度从度数转换为弧度  
    y = lat * M_PI / 180; %将纬度从度数转换为弧度  
    y1 = 1.25 * log(tan(0.25 * M_PI + 0.4 * y)); % 米勒投影的转换  
    % 弧度转为实际距离  
    dikaerX = (W / 2) + (W / (2 * M_PI)) * x ; %笛卡尔坐标x
    dikaerY = (H / 2) - (H / (2 * mill)) * y1 ;%笛卡尔坐标y
    new_xy(i,1)=dikaerX;
    new_xy(i,2)=dikaerY;
    XY=[XY;new_xy(i,1),new_xy(i,2)];
end
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • link部分的from-to数据处理(目的:生成每个路段的起讫点序号)
from xml.etree.ElementTree import Element, ElementTree, tostring,SubElement
from itertools import islice   #方便csv文件去表头工作
import argparse
import os
import csv

with open('links.txt','r')as f:
    reader = csv.reader(f)
    #line_count=0
    
    count=len(open(r"links.txt",'rU').readlines())#获取行数
    print("行数:",count)
    
    rows=[row for row in  reader]
    print(rows[0])
    print(len(rows[0]))
    print(type(rows[0]))
    print(rows[0][1])
    
    with open ("links2.txt","w")as f:
        for i in range(count):
            length=len(rows[i])-2
            for j in range(length):
                f.write('{0}'.format(rows[i][j+1])+','+'{0}'.format(rows[i][j+2]))
                f.write('\n')
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • links部分的length距离转化(目的:求出每个路段的距离)

links现有数据为每段路的起始点编号,让它与nodes数据中的地理位置对应,计算出每个路段的长度就OK啦,这里用MATLAB计算。(对于比较精细的osm地图误差小)
代码需要的数据:xy
在这里插入图片描述

代码需要的数据:links2
在这里插入图片描述

%% 输出起讫点(from-to)的坐标
clear
load("data.mat")
[n,~]=size(links2);
for i=1:n
    from=links2(i,1);%起点编号
    to=links2(i,2);%讫点编号
    [a,b]=find(xy(:,1)==from);x_from=xy(a,3);%x_from是起点横坐标
    [a,b]=find(xy(:,1)==from);y_from=xy(a,2);%x_from是起点横坐标
    [a,b]=find(xy(:,1)==to);x_to=xy(a,3);%x_from是起点横坐标
    [a,b]=find(xy(:,1)==to);y_to=xy(a,2);%x_from是起点横坐标
    links2(i,3)=x_from;links2(i,4)=y_from;links2(i,5)=x_to;links2(i,6)=y_to;
    %将起讫点横纵坐标放入线段起讫点编号的后面。
end
save('data.mat','links2','xy');

%% 计算距离  
clear
load("data.mat")
[n,~]=size(links2);
for i=1:n
    links2(i,7)=((links2(i,3)-links2(i,5))^2+(links2(i,4)-links2(i,6))^2)^0.5;
end
save('finish','links2');    
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

保存到工作区的“finish.mat”就是最终数据了,第七列为距离。
最后是这样的

到此为止,所有network文件需要的数据已经准备好啦!


3.将数据转化为network.xml文件

剩下的部分就简单多了,依旧是nodes部分与links部分分开操作。思路是用python把每行数据前后都写上字符串输出。

  • nodes部分python代码
from itertools import islice   #方便csv文件去表头工作
import argparse
import os
import csv
with open('nodes_finish.txt','r')as f:
    reader = csv.reader(f)
   
    count=len(open(r"nodes_finish.txt",'rU').readlines())#获取行数
    print("行数:",count)
    
    rows=[row for row in  reader]  
    print(rows[0])    
    with open ("network_nodes.txt","w")as f:
        for i in range(count):
            f.write('<node id=\"'+'{0}'.format(rows[i][0])+'\"'+' '+'x=\"'+'{0}'.format(rows[i][2])+'\"'+' '+'y=\"'+'{0}'.format(rows[i][1])+'\"'+' />')
            f.write('\n')

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

生成文件改成xml格式如下图:
在这里插入图片描述

  • links部分python代码
from itertools import islice   #方便csv文件去表头工作
import argparse
import os
import csv
with open('links_finish.txt','r')as f:
    reader = csv.reader(f)
   
    count=len(open(r"links_finish.txt",'rU').readlines())#获取行数
    print("行数:",count)
    
    rows=[row for row in  reader]  
    print(rows[0])    
    with open ("network_links.txt","w")as f:
        for i in range(count):
            f.write('<link id=\"'+'{0}'.format(i)+'\"'+' '+'from=\"'+'{0}'.format(rows[i][0])+'\"'+' '+'to=\"'+'{0}'.format(rows[i][1])+'\"')
            f.write(' '+'length=\"'+'{0}'.format(rows[i][6])+'" capacity="1000" freespeed="100" modes="car" permlanes="1" '+'/>')
            f.write('\n')
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

将生成文件格式改为xml(如下图),就Ok啦
在这里插入图片描述

  • nodes与links二者合并!
    当然是复制粘贴,然后改缩进啦!
    在这里插入图片描述

4终于搞完这个17w行的数据了呜呜呜 喜大普奔!!!

*交通仿真老师带我们领进了matsim的门,但是matsim文件用的是Java语言,越看头越大!都说python很简单,灵(tu)光(ran)一(nao)闪(can):那我就用python搞这个呗…结果就入了python+matsim的破坑,而且坑里就我一人…在摸索中爬行,如有纰漏希望好心人指出,也希望能给其他不小心入错坑的惨宝们参考。鞠躬.jpg
在这里插入图片描述