Python中如何将GeoDataFrame转换为NetworkX并应用于Dijkstra最短路径算法
Python环境下路网Shp导入、GeoDataFrame转NetworkX及Dijkstra最短路径实现
前置依赖
先安装需要的第三方库:
pip install geopandas networkx shapely
1. 读取Shapefile格式路网数据
路网类Shapefile一般为线要素格式,直接用geopandas读取即可,读取后先过滤掉几何为空的无效要素:
import geopandas as gpd import networkx as nx from shapely.geometry import LineString # 替换为本地shapefile文件路径 road_gdf = gpd.read_file("./city_road.shp") # 过滤空几何无效数据 road_gdf = road_gdf[~road_gdf.geometry.isna()].reset_index(drop=True)
注意:如果数据为WGS84经纬度坐标系(EPSG:4326),请先调用road_gdf = road_gdf.to_crs(epsg=对应区域投影EPSG编码)转换为平面投影坐标系,否则后续计算的长度单位为度,和实际距离偏差极大。
2. GeoDataFrame转NetworkX图结构
常规城市路网不考虑单行线时用无向图即可,需要考虑通行方向时初始化有向图。直接取线段首尾坐标作为节点唯一标识,不需要额外生成节点ID,避免ID重复问题;边权重优先使用Shapefile自带的长度/通行时间字段,无对应字段时用几何长度作为权重:
# 初始化无向图,考虑单行线替换为 nx.DiGraph() G = nx.Graph() for _, row in road_gdf.iterrows(): geom = row.geometry # 拆分多段线为单段线处理 if geom.geom_type == "MultiLineString": line_list = list(geom.geoms) else: line_list = [geom] for line in line_list: # 取线段首尾坐标作为节点ID start_node = (line.coords[0][0], line.coords[0][1]) end_node = (line.coords[-1][0], line.coords[-1][1]) # 计算边权重 if "length" in road_gdf.columns: edge_weight = row["length"] else: edge_weight = line.length # 保存边的所有属性,包含几何、道路名称、等级等信息,后续可视化/分析要用 edge_attr = row.drop("geometry").to_dict() edge_attr["weight"] = edge_weight edge_attr["geometry"] = line G.add_edge(start_node, end_node, **edge_attr)
3. 基于Dijkstra算法计算最短路径
NetworkX内置的Dijkstra算法可直接调用,注意输入的起终点坐标需要和路网坐标系一致,先匹配距离最近的路网节点再计算路径:
# 替换为实际起终点坐标,需和路网坐标系保持一致 origin = (116.3970, 39.9080) destination = (116.4080, 39.9150) # 查找距离输入坐标最近的路网节点 def get_nearest_node(graph, point): nodes = list(graph.nodes) return min(nodes, key=lambda n: (n[0]-point[0])**2 + (n[1]-point[1])**2) start_node = get_nearest_node(G, origin) end_node = get_nearest_node(G, destination) # 计算最短路径,返回路径经过的节点序列 path_nodes = nx.dijkstra_path(G, source=start_node, target=end_node, weight="weight") # 计算最短路径总权重(总长度/总通行时间) path_total_cost = nx.dijkstra_path_length(G, source=start_node, target=end_node, weight="weight") # 提取路径所有边的几何,可直接用于导出或可视化 path_edge_pairs = list(zip(path_nodes[:-1], path_nodes[1:])) path_geometries = [G.edges[u, v]["geometry"] for u, v in path_edge_pairs]
实际使用的几个注意点:
- 若运行时报错节点不存在,优先检查起终点坐标与路网坐标系是否匹配,禁止跨坐标系传参
- 若需考虑限行、限速、转向限制等规则,只需在添加边时设置对应方向的权重即可,有向图场景下仅添加允许通行方向的边
- 提取出的路径几何可直接构造为GeoDataFrame,导出为Shapefile或对接可视化库出图
内容的提问来源于stack exchange,提问作者K.N.M.Hirushan
相关产品推荐
相关产品推荐

