You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何OSMnx/NetworkX路径长度与耗时和谷歌地图差异大?如何优化?

关于OSMnx/NetworkX最短路径与商业地图差异的问题与优化方案

我是OSM数据、OSMnx及NetworkX库的新手,已成功获取两点间最短路径,但路径长度和行驶耗时与谷歌地图、必应地图结果差异显著,欢迎提供更优的A到B最短路径获取方案。

具体地址

  • 起点:3115 W Bancroft St, Toledo, OH 43606
  • 终点:6721 Whiteford Center Rd, Lambertville, MI 48144

数据对比

OSMnx length = 9135 mts, travel time = 8.3 min.
Google Maps length = 9334.2 mts, travel time = 12 min.
BingMaps length = 9334.2 mts, travel time = 13 min.
Difference in distance is 199.2 mts and aprox ~ 5 min

问题

  1. 为何路径长度和行驶耗时与谷歌地图/必应地图差异较大?
  2. 如何提升计算精度?

实现代码

import sys
import osmnx as ox
import networkx as nx
from shapely.geometry import box, Point
ox.config(use_cache=True, log_console=True)

def geocode(address):
    """Geocode an address using OSMnx."""
    try:
        x, y = ox.geocode(address)
    except Exception as e:
        print(f'Error: {str(e)}')
        return None, None
    return x, y

def boundary_constructor(orig_x, orig_y, dest_x, dest_y):
    """Create a bounding box around two points."""
    boundary_box = Point(orig_y, orig_x).buffer(0.001).union(Point(dest_y, dest_x).buffer(0.001)).bounds
    minx, miny, maxx, maxy = boundary_box
    bbox = box(*[minx, miny, maxx, maxy])
    return bbox

def getting_osm(bbox, network_type, truncate_edges):
    """Retrieve OSM data (roads, edges, nodes) for a given bounding box and network type."""
    G = ox.graph_from_polygon(bbox, retain_all=False, network_type=network_type, truncate_by_edge=truncate_edges)
    G = ox.add_edge_speeds(G)
    G = ox.add_edge_travel_times(G)
    roads = ox.graph_to_gdfs(G, nodes=False, edges=True)
    return G, roads

def find_closest_node(G, lat, lng, distance):
    """Find the closest node in a graph to a given latitude and longitude."""
    node_id, dist_to_loc = ox.distance.nearest_nodes(G, X=lat, Y=lng, return_dist=distance)
    return node_id, dist_to_loc

def shortest_path(G, orig_node_id, dest_node_id, weight):
    """Find the shortest path between two nodes in a graph."""
    try:
        route = ox.shortest_path(G, orig_node_id, dest_node_id, weight=weight)
    except nx.NetworkXNoPath as e:
        print(f"No path found between {orig_node_id} and {dest_node_id}")
        print(e)
    return route

def find_length_and_time(G, travel_length, travel_time):
    try:
        route_length = int(sum(ox.utils_graph.route_to_gdf(G, travel_length, "length")["length"]))
        route_time = int(sum(ox.utils_graph.route_to_gdf(G, travel_time, "travel_time")["travel_time"]))
    except Exception as e:
        print(f'Error: {e}')
    return route_length, route_time
    

def route_plotting(G, travel_length, travel_time):
    """Plot the shortest path between two addresses."""
    if travel_length and travel_time:
        fig, ax = ox.plot_graph_routes(
            G,
            routes=[travel_length, travel_time],
            route_colors=["r", "y"],
            route_linewidth=6,
            node_size=0
        )
    elif travel_length:
        ox.plot_route_folium(G, travel_length, popup_attribute='length')
    elif travel_time:
        ox.plot_route_folium(G, travel_time, popup_attribute='travel_time')

def main():
    # User address input
    origin_address = str(input('Enter the origin address: '))
    destination_address = str(input('Enter the destination adddress: '))
    # Geocode addresses
    orig_x, orig_y = geocode(origin_address)
    dest_x, dest_y = geocode(destination_address)
    # Check if geocoding was successful
    if not all([orig_x, orig_y, dest_x, dest_y]):
        print("Unable to geocode one or both addresses. Exiting...")
        sys.exit()
    # Create bounding box
    bbox = boundary_constructor(orig_x, orig_y, dest_x, dest_y) 
    # Retrieve OSM data
    G, roads = getting_osm(bbox, network_type='drive', truncate_edges='True')
    # Find closest node
    orig_node_id, dist_to_orig = find_closest_node(G, orig_y, orig_x, True)
    dest_node_id, dist_to_dest = find_closest_node(G, dest_y, dest_x, True)
    # find shortest path
    travel_length = shortest_path(G, orig_node_id, dest_node_id, weight='length')
    travel_time = shortest_path(G, orig_node_id, dest_node_id, weight='travel_time')
    # find route length and route time
    route_length, route_time = find_length_and_time(G, travel_length, travel_time)
    if route_length and route_time:
        print(f"Shortest travel length:{route_length: .2f} meters and takes {(route_time/60)} minutes") 
    elif route_length:
        print(f"Shortest travel length: {route_length: .2f}. No travel time found")
    elif route_time:
        print(f"Shortest travel time: {(route_time/60)}. No travel length found")
    # plot routes
    route_plotting(G, travel_length, travel_time)


if __name__ == "__main__":
    main()

问题解答

1. 差异原因分析

  • 道路数据精度差异:OSM是开源众包数据,部分道路的限速、车道数、通行规则等属性可能存在遗漏或误差;商业地图有专业采集团队,数据细节更完善、更新更及时。
  • 路径权重逻辑不同:你当前仅用length或默认travel_time(仅基于限速)计算路径,但商业地图会综合实时路况、道路优先级、转弯成本、红绿灯等待时间等多维度因素。
  • 起点终点匹配偏差:OSMnx通过nearest_nodes匹配的节点,可能和商业地图的实际道路接入点不同,尤其是地址位于小路或小区内部时,会直接影响初始路径走向。
  • 地图范围限制:代码中buffer(0.001)生成的边界范围过小,可能遗漏了更优的备选道路,迫使算法选择短但不符合实际通行习惯的路线。

2. 精度提升方案

  • 扩大地图范围:将buffer(0.001)调整为更大值(如buffer(0.01)),确保获取足够的道路网络,避免因范围限制错过最优路径。
  • 优化时间计算逻辑:
    • 不依赖默认的add_edge_speeds,针对不同道路类型(residential/primary等)自定义更贴合实际的限速值;
    • 加入转弯成本:使用ox.add_edge_turn_restrictions或自定义转弯权重,模拟实际驾驶中的转弯耗时。
  • 修正起点终点匹配:
    • 先用ox.distance.nearest_edges匹配到最近道路,再从道路节点中选择更合理的接入点;
    • 计算起点到匹配节点、终点到匹配节点的距离,加入总路程和时间中。
  • 优化路径算法:
    • 用ox.k_shortest_paths获取多条备选路径,结合实际通行规则筛选最优解;
    • 若需实时路况,可通过第三方API获取数据后更新图中边的权重。
  • 修正OSM数据:若发现OSM中道路属性错误,可直接在OSM官网提交修改,从数据源层面提升精度。
  • 更换地理编码服务:OSMnx默认地理编码精度有限,可尝试更精准的编码服务获取经纬度。

内容的提问来源于stack exchange,提问作者Gustacro

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 05:45:15