Python 指南-最短路径(Dijkstra 算法):

        Dijkstra 算法可在 Python 库 OSMNX 中实现,可用于查找两个位置之间按距离或时间加权的最短路径。该算法使用 OpenStreetMap (OSM) 网络来驾驶、步行或骑自行车,并在后台使用 Python 库 NETWORKX 查找路线。

编码练习

正如我提到的,我将做一个分步指南,所以让我们开始吧。首先让我们导入所需的库

import osmnx as ox
import geopandas as gpd
from shapely.geometry import Point, LineString
import pandas as pd
import matplotlib.pyplot as plt

1. 定义出发地和目的地

简单地说,我们将创建几何对象作为点:

# origin and destination geom

origin_geom = Point(-5.6613932957355715, 32.93210288339607)

destination_geom = Point(-3.3500597061072726, 34.23038027794419)

2. 获取OSM Graph对象

然后,我们将提取将用于生成最短路径的图。让我们一步一步来看看。

  • 从起点和终点创建 GeoDataFrame
# create origin dataframe
origin =  gpd.GeoDataFrame(columns = ['name', 'geometry'], crs = 4326, geometry = 'geometry')
origin.at[0, 'name'] = 'origin'
origin.at[0, 'geometry'] =origin_geom

# create destination dataframe
destination =  gpd.GeoDataFrame(columns = ['name', 'geometry'], crs = 4326, geometry = 'geometry')
destination.at[0, 'name'] = 'destination'
destination.at[0, 'geometry'] = destination_geom

  • 获取包含出发地和目的地的图表

我们将使用 Geopandas 中的函数Envelope来使用多边形作为掩码来获取图形。

首先是一个简单的功能。

def get_graph_from_locations(origin, destination, network='drive'):
    '''
    network_type as drive, walk, bike
    origin gdf 4326
    destination gdf 4326
    '''
    # combine and area buffer
    combined = pd.concat([origin, destination])

    convex = combined.unary_union.envelope # using envelope instead of convex, otherwise it breaks the unary_union
    
    graph_extent = convex.buffer(0.02)

    graph = ox.graph_from_polygon(graph_extent, network_type= network)

    return graph

然后,使用它并绘制结果。

graph = get_graph_from_locations(origin, destination)
fig, ax = ox.plot_graph(graph, node_size=0, edge_linewidth=0.2)

图片由作者提供。图表包含出发地和目的地

3. 查找最近的出发地和目的地节点

使用起始位置和目标位置获取属于网络一部分的最近节点。可以使用 osmnx 函数获取节点的代码。

# ------------- get closest nodes

# origin
closest_origin_node = ox.nearest_nodes(G=graph, 
                                       X=origin_geom.x, 
                                       Y=origin_geom.y)

# destination
closest_destination_node = ox.nearest_nodes(G=graph, 
                                           X=destination_geom.x, 
                                           Y=destination_geom.y)

您可以检查并注意到我们目前只有代码。

4. 寻找最短路径

然后,利用最短路径函数来获取路径。

# run
route = ox.shortest_path(graph, 
                         orig = closest_origin_node, 
                         dest = closest_destination_node, 
                         weight = 'length')

这将返回一堆作为路由一部分的节点代码。

图片来自AuthorNode的代码

5. 从节点创建线几何图形

我们将从图中提取节点的几何图形并创建表示最短路径的 LineString 几何图形

首先是一个用于此的函数。

def nodes_to_route(graph_nodes, path_nodes):

    # Extract the route nodes of the graph
    route_nodes = graph_nodes.loc[path_nodes]

    # ---> note! If you have more routes, check for each one, to be removed in length is 1.  A path can not be built with only 1 node.

    # Create a LineString out of the route
    list_geom = route_nodes.geometry.to_list()
    path = LineString(list_geom)

    # Append the result into the GeoDataFrame
    route_df = gpd.GeoDataFrame( [[path]] )

    # Add a column name
    route_df.columns = ['geometry'] 

    # Set geometry
    route_df = route_df.set_geometry('geometry')

    # Set coordinate reference system
    route_df.crs = graph_nodes.crs
    
    # remove nans
    route_df = route_df.dropna(subset=['geometry'])

    return route_df

获取节点,并在函数中使用它们。

# get all network nodes
graph_nodes = ox.graph_to_gdfs(graph, edges=False)

# get the line geometries from osm nodes
route_gdf = nodes_to_route(graph_nodes, route)

6. 计算距离

我们将使用墨卡托投影以米为单位测量路线。如果您想要更准确的信息,可以使用位置投影。

首先,为此提供一个函数。

def compute_distance(shortest_path_gdf):
    '''
    Compute distance in EPSG:3387
    
    '''
    
    # project WGS84 to EPSG3387
    distances = shortest_path_gdf.to_crs("EPSG:3387").geometry.length
    
    # add
    shortest_path_gdf['distance'] = distances
    
    return shortest_path_gdf

然后,使用它:

# calculate distance m
route_distance_gdf = compute_distance(route_gdf)

它将测量大约 351.243 米的路线。

7. 保存网络和路径

将地图的网络和路径保存在本地磁盘中。

获取网络并定义 GeoDataFrame:

# fetch network
network = ox.graph_to_gdfs(graph, nodes=False)

# get only needed columns
network_gdf = network.reset_index(drop=True)[['geometry']]

然后存储:

network_gdf.to_file( r'osm_network.gpkg' )
route_distance_gdf.to_file( r'osm_shortest_path.gpkg' )

您可以使用此数据来创建您自己的地图。例如QGIS中的这个:

图片由作者提供。 QGIS中的最短路径和网络

8. 绘制结果

我们将通过绘制所有元素来检查我们的工作是否正确。

# plot network
ax = network_gdf.plot(figsize=(12, 10), linewidth = 0.2, color='grey', zorder=0);

# origin and destination
origin.plot(ax=ax, markersize=46, alpha=0.8, color='blue', zorder=1)
destination.plot(ax=ax, markersize=46, alpha=0.8, color='green', zorder=2)

# route
route_distance_gdf.plot(ax=ax, linewidth = 3, color='red', alpha=0.4, zorder=3)

plt.axis(False);

结果就这么简单。

图片由作者提供。 Matplotlib 中的最短路径、网络、起点和终点

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

gis收藏家

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值