离线OSM数据条件下,出租车轨迹数据的地图匹配

离线OSM数据条件下,出租车轨迹数据的地图匹配

前言:本人是在学习交通数据小旭学长写的Python出租车GPS数据的路网匹配(TransBigData+leuvenmapmatching)过程中发现osmnx库由于受到网络限制很难较为完整的获取一个城市或更大范围的路网数据。为了解决这个问题,本人采用了osm2gmns库,构建网络并进行地图匹配,实现了和交通数据小旭学长一样的效果。

01 OSM路网数据的获取

现状osm路网数据获取方法较多,但是对于指定城市或者省份数据较难获取,一般要么太大(国家),要么太小(城区范围),因此本人采用了先采集较大范围数据,再进行裁剪的方法进行处理。具体处理方法如下:
登录OSM网站,选择[Geofabrik下载](定期更新的洲、 国家和特定城市的摘录)选择你要选择的地区,选择.osm.pbf下载。
OSM界面
选择下载

02 使用osmconvert软件,对数据进行切割

在使用之前先获取所需边界的shp文件,比如深圳市的区域范围。确保是WGS84坐标系的

一般这种数据很容易找到,推荐一个

然后编写如下代码:构建.poly文件

输入.shp文件,用如下代码生成深圳市的边界文件.poly格式的,然后保证与osmconvert64-0.8.8p.exe在同一文件夹内。

import geopandas as gpd
import os

from shapely.geometry import Polygon, MultiPolygon

def write_poly(polygons, output_file):
    with open(output_file, "w") as f:
        c = 1
        for polygon in polygons:
            if isinstance(polygon, Polygon):
                poly_ext = list(polygon.exterior.coords)
                f.write("{}\n".format(c))
                f.write("1\n")
                for coord in poly_ext:
                    f.write("\t{} {}\n".format(coord[0], coord[1]))
                f.write("END\n")
            elif isinstance(polygon, MultiPolygon):
                for poly in polygon.geoms:
                    poly_ext = list(poly.exterior.coords)
                    f.write("{}\n".format(c))
                    f.write("1\n")
                    for coord in poly_ext:
                        f.write("\t{} {}\n".format(coord[0], coord[1]))
                    f.write("END\n")
                    c += 1
        f.write("END\n")

shp_file = "深圳市.shp"
output_file = "shenzhen_polyfile.poly"

gdf = gpd.read_file(shp_file)
crs_4326 = {"init": "epsg:4326"}
gdf = gdf.to_crs(crs_4326)  # 将坐标系统转换为WGS84 (EPSG:4326)如果需要
polygons = gdf.geometry
write_poly(polygons, output_file)

使用osmconvert对深圳市路网进行提取

首先获取osmconvert软件,获取方法很多,推荐1个然后运行osmconvert,进入如下界面osmconvert界面
然后输入a,将之前下载好的.osm.pbf文件名称输入(注意:文件也要与软件在同一文件夹内)在这2里插入图片描述
回车输入后,选择4,再将刚才准备好的边界区域.poly文件输入在这里插入图片描述
回车后就可以得到处理好的只包含深圳范围的.osm.pbf文件

03 路网数据的处理

001 首先将.pbf格式的osm路网数据,转化为link和节点

在这里选择的是osm2gmns库,它可以将osm路网进行选择性清洗,选择你想要的类型的路并构建为路网,具体参数大家可以去库的官网了解,他会输出一个link.csv和node.csv,这是构建路网的关键

import osm2gmns as og
net = og.getNetFromFile('shenzhen-latest.osm_01.pbf', network_types='auto',link_types={'motorway', 'trunk', 'primary', 'secondary', 'tertiary', 'residential', 'service', 'unclassified', 'connector'},strict_mode=False,combine=True,)
og.consolidateComplexIntersections(net, auto_identify=True)
og.outputNetToCSV(net)

002 构建图

import pandas as pd
import networkx as nx

node_data  = pd.read_csv('node.csv', encoding='gbk')
link_data  = pd.read_csv('link.csv', encoding='gbk')

G = nx.Graph()
G.graph["crs"] = "epsg:4326"

for idx, row in node_data.iterrows():
    node_id = row['node_id']
    node_lon = row['x_coord']
    node_lat = row['y_coord']
    G.add_node(node_id, lon=node_lon, lat=node_lat)

for idx, row in link_data.iterrows():
    link_id = row['link_id']
    link_from = row['from_node_id']
    link_to = row['to_node_id']
    G.add_edge(link_from, link_to, id=link_id)

003 GPS数据读取

这里的数据是从交通数据小旭学长写的Python出租车GPS数据的路网匹配(TransBigData+leuvenmapmatching)里面分享的链接中获得的,大家可以自行下载。

import transbigdata as tbd
import pandas as pd
# 地图匹配包
from leuvenmapmatching.matcher.distance import DistanceMatcher
from leuvenmapmatching.map.inmem import InMemMap
from leuvenmapmatching import visualization as mmviz
 
#读取数据
data = pd.read_csv('TaxiData-Sample.csv',header = None)
data.columns = ['VehicleNum','Time','Lng','Lat','OpenStatus','Speed']

#从GPS数据提取OD与路径GPS
oddata = tbd.taxigps_to_od(data,col = ['VehicleNum','Time','Lng','Lat','OpenStatus'])
data_deliver,data_idle = tbd.taxigps_traj_point(data,oddata,col=['VehicleNum', 'Time', 'Lng', 'Lat', 'OpenStatus'])

004 构建leuvenmapmatching所需的地图网络(用于地图匹配)

from leuvenmapmatching.map.inmem import InMemMap

# 创建一个 InMemMap 对象    
map_con = InMemMap(G, use_latlon=True, use_rtree=True, index_edges=True)

# 遍历 link_data 添加节点和边
for _, row in link_data.iterrows():
    link_id = row['link_id']
    link_from = row['from_node_id']
    link_to = row['to_node_id']

    # 提取节点的经纬度       
    lat1, lon1 = G.nodes[link_from]['lat'], G.nodes[link_from]['lon']
    lat2, lon2 = G.nodes[link_to]['lat'], G.nodes[link_to]['lon']

    # 在 map_con 中添加节点
    map_con.add_node(link_from, (lat1, lon1))
    map_con.add_node(link_to, (lat2, lon2))

    # 连接这两个节点
    map_con.add_edge(link_from, link_to)

# 清除不必要的信息以提高性能
map_con.purge() 

005 地图匹配

#用transbigdata提取出行轨迹
import geopandas as gpd
tmp_gdf = data_deliver[data_deliver['ID'] == 22].sort_values(by = 'Time')
#轨迹增密
tmp_gdf = tbd.traj_densify(tmp_gdf,col = ['ID', 'Time', 'Lng', 'Lat'],timegap = 15)
#转换轨迹的坐标系为地理坐标系
tmp_gdf['geometry'] = gpd.points_from_xy(tmp_gdf['Lng'],tmp_gdf['Lat'])
tmp_gdf = gpd.GeoDataFrame(tmp_gdf)
tmp_gdf.crs = {'init':'epsg:4326'}
#tmp_gdf = tmp_gdf.to_crs(2416)
#获得轨迹点
path = list(zip(tmp_gdf.geometry.y, tmp_gdf.geometry.x))
#构建地图匹配工具
matcher = DistanceMatcher(map_con, 
                          max_dist=500, 
                          max_dist_init=170, 
                          min_prob_norm=0.0001,
                        non_emitting_length_factor=0.95,
                        obs_noise=50, 
                          obs_noise_ne=50,
                              dist_noise=50,
                              max_lattice_width=20,
                              non_emitting_states=True)
#进行地图匹配
states, _ = matcher.match(path, unique=False)
#绘制底图匹配结果
mmviz.plot_map(map_con, matcher=matcher,
               show_labels=False, show_matching=True,#show_graph=False,
               filename=None)

获得如下结果图
在这里插入图片描述

006 获取地图匹配路径

注意:该方法通过matcher.path_pred_onlynodes获得匹配的每个路段节点编号,命名为u,由于路段是前后相接的因此错位合并后得到每个路段的起始和终点路段节点编号

#该方法通过matcher.path_pred_onlynodes获得匹配的每个路段节点编号,命名为u,由于路段是前后相接的因此错位合并后得到每个路段的起始和终点路段节点编号
pathdf = pd.DataFrame(matcher.path_pred_onlynodes,columns = ['u'])
pathdf['v'] = pathdf['u'].shift(-1)
pathdf = pathdf[-pathdf['v'].isnull()]
#使用pd.merge与link表的起始终止节点进行匹配,从而得到匹配路段的地理信息
from shapely import wkt
# 需要确保CSV文件中的"geometry"列已经以合适的格式存储了几何数据(例如,以"Well-Known Text (WKT)格式存储)
link_data['geometry'] = link_data['geometry'].apply(wkt.loads)
pathgdf = pd.merge(pathdf,link_data.reset_index(),left_on=['u','v'],right_on=['from_node_id','to_node_id'])
pathgdf = gpd.GeoDataFrame(pathgdf,geometry='geometry')#生成Geopandas文件
pathgdf.plot()

运行结果如下为匹配的路段
在这里插入图片描述

007叠加路网可视化

首先要获得所有路网路段的中心点,方便后面筛选要显示的路段


#link_data['geometry'] = link_data['geometry'].apply(wkt.loads)
#获得所有线段中心点
from shapely.geometry import Point

# 创建空的列表以存储经度和纬度值
lons = []
lats = []

# 对于表中的每一行
for geometry in link_data['geometry']:
    # 计算中心点
    centroid = geometry.centroid

    # 将经度和纬度值添加到相应的列表中
    lons.append(centroid.x)
    lats.append(centroid.y)

# 将经度和纬度值添加到表中
link_data['lon'] = lons
link_data['lat'] = lats

然后进行轨迹匹配可视化

#与路网一起可视化
import matplotlib as mpl
import matplotlib.pyplot as plt
 
fig     = plt.figure(1,(8,8),dpi = 100)    
ax      = plt.subplot(111)
plt.sca(ax)
fig.tight_layout(rect = (0.05,0.1,1,0.9))
#设定可视化边界
bounds = pathgdf.unary_union.bounds
gap = 0.003
bounds = [bounds[0]-gap,bounds[1]-gap,bounds[2]+gap,bounds[3]+gap]
#绘制匹配的路径
pathgdf.plot(ax = ax,zorder = 1)
#绘制底图路网

link_data = gpd.GeoDataFrame(link_data,geometry='geometry')
tbd.clean_outofbounds(link_data,bounds,col = ['lon','lat']).plot(ax = ax,color = '#333',lw = 0.1)
#绘制GPS点
tmp_gdf.to_crs(4326).plot(ax = ax,color = 'r',markersize = 5,zorder = 2)
plt.axis('off')
plt.xlim(bounds[0],bounds[2])
plt.ylim(bounds[1],bounds[3])
plt.show()

在这里插入图片描述最后,对前人的无私分享表示感谢!
在这里插入图片描述

  • 7
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 12
    评论
以下是一个使用隐马尔可夫模型进行GPS轨迹地图匹配Python 代码示例: ```python import numpy as np from hmmlearn import hmm import geopy.distance # 生成隐马尔可夫模型 model = hmm.GaussianHMM(n_components=3, covariance_type="full") # 定义观测序列和状态序列 observations = np.array([[1.5, 2.0], [3.0, 4.5], [2.5, 3.0], [1.0, 1.5]]) states = np.array([1, 2, 3, 2]) # 训练隐马尔可夫模型 model.fit(observations) # 加载地图数据 map_data = load_map_data('map.osm') # 进行轨迹地图匹配 matched_states = [] for i in range(len(observations)): min_distance = float('inf') matched_state = -1 for j in range(len(map_data)): distance = geopy.distance.distance(observations[i], map_data[j]).km if distance < min_distance: min_distance = distance matched_state = j matched_states.append(matched_state) print("轨迹地图匹配结果:", matched_states) ``` 在这个示例中,我们使用 `hmmlearn` 库生成了一个包含3个状态的隐马尔可夫模型,并使用一个包含4个轨迹点的观测序列作为训练数据进行训练。然后,我们加载了地图数据,并使用轨迹点和地图数据之间的距离(例如使用Geopy库的 `distance` 函数)进行匹配,得到了匹配的状态序列。 在实际应用中,我们需要使用包含更多轨迹点和更复杂状态的数据进行训练,并根据实际需求进行调参和优化。同时,我们还需要根据具体的地图数据匹配算法选择合适的距离度量和匹配策略,以提高匹配精度。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值