【问题标题】:OSMnx: Shortest path for a set of origins and destinationsOSMnx:一组起点和终点的最短路径
【发布时间】:2019-10-06 13:05:47
【问题描述】:

假设我有一个包含起点和终点的点 shapefile。

1) 如何使用 nx.shortest_path 计算每个起点和终点? 2)如何将对应的路由保存为shapefile?我检查了Save a route and conserve its curvature with Python OSMnx,它显示了如何为路线获取 MultiLineString,但没有显示如何导出路线。

【问题讨论】:

    标签: python networkx polyline osmnx


    【解决方案1】:

    以下步骤可行:

    1. 用 geopandas 打开你的 shapefile
    2. 对于 shapefile 中的每个原点,使用 OSMnx 找到 nearest network node,然后对每个目标点执行相同操作
    3. 使用 nx.shortest_path 计算每个起点/终点节点对之间的最短路径
    4. 保存路线to a MultiLineStringwkt
    5. 由于 wkt 是纯文本,只需使用任何 Python 序列化方法将此文本保存到磁盘即可。或者将所有 MultiLineStrings 本身组装成 geopandas GeoSeries 并将其作为 shapefile 或 GeoJSON 文件保存到磁盘。

    【讨论】:

    • 我在 for 循环中使用pathDistance = nx.shortest_path_length(G, orig_node, dest_node, weight='length') 来收集主页上的距离。但是在重新加载主页时需要 12 分钟。我想获得最新的值,但无论如何可以减少时间。减少它,使其只需要 15 秒或更短的时间。
    • 请注意,您现在还可以使用 OSMnx 多处理 osmnx.readthedocs.io/en/stable/… 解决并行中的最短路径
    【解决方案2】:

    感谢@gboeing 提供的步骤。还添加了对我有用的代码 sn-ps 来实现概述的解决方案。

    from shapely.geometry import shape
    from shapely.geometry import LineString
    
    from osgeo import ogr, osr
    import geopandas as gpd
    import pandas as pd
    import time
    import networkx as nx
    import osmnx as ox
    
    
    G= ox.graph_from_place('Bangalore, India')
    
    fig, ax = ox.plot_graph(G)
    
    #Read the Origin-Destination csv
    od_table = 'E:/OD_pairs1.csv'
    df = pd.read_csv(od_table)
    

    以下是我的输入 CSV 与原点 (o_long, o_lat) 和目标 (d_long, d_lat) 点坐标的样子:

    df
    
    Out[]:  o_id  o_long    o_lat      d_id d_long      d_lat
            o1  77.548349   12.996800   d1  77.554137   12.995225
            o2  77.555820   13.009082   d2  77.570458   12.995690
            o3  77.576325   13.014630   d3  77.583616   13.009188
            o4  77.564848   12.990121   d4  77.551316   12.988570
            o5  77.590529   12.992340   d5  77.598469   12.988289
    

    答案的第 1 部分(计算路线的方法): (在 shortestpath() 方法中,如果您不想将 O&D 点到最近网络节点的距离添加到路由长度中,您可能希望返回 length 变量而不是 total_length。)

    def nodes_to_linestring(path):
        coords_list = [(G.nodes[i]['x'], G.nodes[i]['y']) for i in path ]
        #print(coords_list)
        line = LineString(coords_list)
        
        return(line)
    
    def shortestpath(o_lat, o_long, d_lat, d_long):
        
        nearestnode_origin, dist_o_to_onode = ox.distance.get_nearest_node(G, (o_lat, o_long), method='haversine', return_dist=True)
        nearestnode_dest, dist_d_to_dnode = ox.distance.get_nearest_node(G, (d_lat, d_long), method='haversine', return_dist=True)
        
        #Add up distance to nodes from both o and d ends. This is the distance that's not covered by the network
        dist_to_network = dist_o_to_onode + dist_d_to_dnode
        
        shortest_p = nx.shortest_path(G,nearestnode_origin, nearestnode_dest) 
        
        route = nodes_to_linestring(shortest_p) #Method defined above
        
        # Calculating length of the route requires projection into UTM system.  
        inSpatialRef = osr.SpatialReference()
        inSpatialRef.ImportFromEPSG(4326)
        outSpatialRef = osr.SpatialReference()
        outSpatialRef.ImportFromEPSG(32643)
        coordTransform = osr.CoordinateTransformation(inSpatialRef, outSpatialRef)
        
        #route.wkt returns wkt of the shapely object. This step was necessary as transformation can be applied 
        #only on an ogr object. Used EPSG 32643 as Bangalore is in 43N UTM grid zone.
        geom = ogr.CreateGeometryFromWkt(route.wkt)
       
        geom.Transform(coordTransform)
        length = geom.Length()
        
        #Total length to be covered is length along network between the nodes plus the distance from the O,D points to their nearest nodes
        total_length = length + dist_to_network
        #in metres
        
        return(route, total_length )
    

    在数据帧上应用上述方法来获取所有 O-D 对的最短路径的几何形状和长度:

    start_time = time.time()
    
    df['osmnx_geometry'] = df.apply(lambda x: shortestpath(x['o_lat'], x['o_long'], x['d_lat'], x['d_long'])[0] , axis=1)
    df['osmnx_length'] = df.apply(lambda x: shortestpath(x['o_lat'], x['o_long'], x['d_lat'], x['d_long'])[1] , axis=1)
    
    print("Time taken: ", (time.time() - start_time), "seconds")
    

    生成的数据框将具有路线的相关几何形状和路线长度(以米为单位):

    df
        Out[]:
        o_id    o_long  o_lat   d_id    d_long  d_lat      osmnx_geometry     osmnx_length
        o1  77.548349   12.996800   d1  77.554137   12.995225   LINESTRING (77.5482836 12.9966618, 77.54976259...   827.718256
        o2  77.555820   13.009082   d2  77.570458   12.995690   LINESTRING (77.555814 13.0090627, 77.5556026 1...   2588.006507
        o3  77.576325   13.014630   d3  77.583616   13.009188   LINESTRING (77.57588320000001 13.0146859, 77.5...   1107.137060
        o4  77.564848   12.990121   d4  77.551316   12.988570   LINESTRING (77.56473080000001 12.9898858, 77.5...   1744.708360
        o5  77.590529   12.992340   d5  77.598469   12.988289   LINESTRING (77.5901456 12.9920295, 77.5905355 ...   1097.493520
    

    答案的第 2 部分。将此数据框保存为 shapefile:

    df = df.rename(columns = {'osmnx_geometry': 'geometry'})
    gpdf = gpd.GeoDataFrame(df, geometry =df['geometry'])
    gpdf.to_file('osmnx_shortestpaths.shp')
    

    我还探索了 OSRM(用于最快路线)和 Gmaps Directions API(用于基于流量的最有效路线)来计算同一组 O-D 对的路线,可以在 here 找到相同的脚本。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-12-09
      • 2020-03-04
      • 1970-01-01
      • 1970-01-01
      • 2020-12-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多