为何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
问题
- 为何路径长度和行驶耗时与谷歌地图/必应地图差异较大?
- 如何提升计算精度?
实现代码
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
相关产品推荐
相关产品推荐

