我刚接触 OSM 数据、OSMnx 和 networkx 库,因此如果有更好的方法来实现从 A 到 B 位置的最短路径,我将不胜感激。
我正在尝试获取从 A 到 B 的最短路径。我成功获取了路径,但与 Google 地图返回的结果相比,长度和时间旅行有很大不同。
1. 为什么路径长度和旅行时间与Google Maps / Bing Maps有很大不同?
2. 如何提高准确率?
使用的地址: 3115 W 班克罗夫特街,托莱多,俄亥俄州 43606 6721 怀特福德中心路,兰伯特维尔,密歇根州 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()
为什么 Google 地图和 Bing 地图的路径长度和旅行时间如此不同?
从广义上讲,它是那些拥有更好数据和更好算法的地图提供商的某种组合。
从数据开始,开放街道地图中的大多数街道都没有标注最大法定速度。对于这些街道中的任何一条,OSMnx 都会尝试通过查看“高速公路类型”以及具有最大速度注释的相同高速公路类型的道路来猜测最大速度。 (来源。)假设是,如果一个城市的平均住宅街道的最高速度为 20 英里/小时,那么没有已知速度限制的住宅街道的最高速度可能为 20 英里/小时。当然,这个假设并不总是正确的,如果知道实际速度,您的路线计划将会更好。 为了改进这一点,如果您知道您管辖范围内未张贴街道的速度限制,您可以向
hwy_speeds
提供
add_edge_speeds()
参数。这就涉及到下一个问题,那就是人们并不总是按照限速行驶。有时他们开得更快,或者有时交通堵塞,他们必须放慢速度。如果没有一些带有交通信息的数据源,这个问题很难解决。
另一个问题是 OSMnx 假设所有交叉点都需要零秒才能穿过。 OSM 用于查找最快路线的算法不允许交叉路口(或节点)有任何行驶时间。即使这不是问题,人们也需要计算出穿过十字路口需要多长时间。 (要解决这个问题,您需要考虑一些问题:这个十字路口有红绿灯吗?这是右转还是左转?您有优先通行权吗?)
总结来说,有四个因素会导致行程时间不同:
用该类别的平均速度来估算速度未知的街道