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

如何用OSMNX/Momepy生成带街道连续性的Dual NetworkX图?

生成具备街道连续性的对偶图(Dual Graph)方案(适配OSMNX/Momepy)

传统对偶图会把每条独立路段转成节点,但我们需要把同一条街道的所有路段合并成单个节点,核心思路是先给同街道的路段打上统一标识,再基于这个标识合并对偶图节点。以下是分别用OSMNX和Momepy实现的具体步骤:


用OSMNX实现

步骤1:获取街道网络并标记同街道路段

先拉取目标区域的街道网络,提取路段数据后,给同属一条街道的路段分配统一的street_id(处理无名道路和重名情况):

import osmnx as ox
import networkx as nx
import geopandas as gpd
import pandas as pd

# 拉取目标区域的驾车网络(以曼哈顿为例)
G = ox.graph_from_place("Manhattan, New York, USA", network_type="drive")

# 把网络转成GeoDataFrame,只保留路段数据
edges = ox.graph_to_gdfs(G, nodes=False, edges=True)

# 处理无名道路:给每条无名路分配唯一ID
edges["street_id"] = edges["name"].fillna(f"unnamed_{edges.index}")
# 优化重名:结合道路编号(ref)生成唯一标识,避免重名街道被错误合并
edges["street_id"] = edges.apply(
    lambda x: f"{x['name']}_{x['ref']}" if pd.notna(x["ref"]) else x["street_id"],
    axis=1
)

步骤2:构建初始对偶图

先把每个路段作为对偶图的节点,再根据原始网络的交叉口(节点),给共享同一交叉口的路段节点添加连接边:

# 初始化有向对偶图(如果不需要方向,改用nx.Graph)
dual_G = nx.DiGraph()

# 给对偶图添加节点:每个路段对应一个节点,存储street_id和几何信息
for idx, row in edges.iterrows():
    dual_G.add_node(idx, street_id=row["street_id"], geometry=row["geometry"])

# 记录每个原始交叉口节点连接的所有路段
node_edges = {}
for u, v, key in G.edges(keys=True):
    node_edges.setdefault(u, []).append((u, v, key))
    node_edges.setdefault(v, []).append((u, v, key))

# 遍历每个交叉口,给共享该交叉口的路段节点添加边
for node, edge_list in node_edges.items():
    if len(edge_list) >= 2:
        # 两两连接该交叉口下的所有路段节点
        for i in range(len(edge_list)):
            for j in range(i + 1, len(edge_list)):
                edge1_key = edge_list[i][2]
                edge2_key = edge_list[j][2]
                dual_G.add_edge(edge1_key, edge2_key, intersection_node=node)

步骤3:合并同街道的节点

用NetworkX的contracted_nodes方法,把同一street_id下的所有节点合并成一个,同时更新合并后的街道几何:

# 按street_id分组,获取每组对应的路段节点列表
street_groups = edges.groupby("street_id").index.tolist()

# 遍历每组,合并同街道节点
for street_id, node_list in street_groups.items():
    if len(node_list) > 1:
        # 选第一个节点作为主节点,合并其他节点
        main_node = node_list[0]
        for node in node_list[1:]:
            dual_G = nx.contracted_nodes(dual_G, main_node, node, self_loops=False)
            # 合并主节点和被合并节点的几何,得到整条街道的连续几何
            merged_geom = dual_G.nodes[main_node]["geometry"].union(dual_G.nodes[node]["geometry"])
            dual_G.nodes[main_node]["geometry"] = merged_geom

用Momepy实现(更适配城市形态分析)

Momepy专门针对城市网络设计,处理街道合并和对偶图更高效:

步骤1:加载并合并同街道路段

先获取街道数据,基于name和ref合并同一条街道的所有路段:

import momepy
import geopandas as gpd
import networkx as nx
import pandas as pd

# 从OSM拉取目标区域的驾车网络
gdf = momepy.download_osm("Manhattan, New York, USA", network_type="driving")

# 生成唯一street_id,处理无名和重名情况
gdf["street_id"] = gdf.apply(
    lambda x: f"{x['name']}_{x['ref']}" if pd.notna(x["ref"]) else x["name"],
    axis=1
)
gdf["street_id"] = gdf["street_id"].fillna(f"unnamed_{gdf.index}")

# 按street_id合并路段,得到整条街道的连续几何
merged_streets = gdf.dissolve(
    by="street_id",
    aggfunc={"geometry": "union", "highway": "first"}
)

步骤2:构建基于合并后街道的对偶图

直接以合并后的整条街道为节点,根据街道间的交叉口添加连接边:

# 初始化无向对偶图(需要方向则用nx.DiGraph)
dual_G = nx.Graph()

# 添加节点:每个合并后的街道对应一个节点
for idx, row in merged_streets.iterrows():
    dual_G.add_node(idx, street_id=idx, geometry=row["geometry"], highway=row["highway"])

# 遍历所有街道对,检查是否有交叉口,有则添加边
for i in range(len(merged_streets)):
    street1 = merged_streets.iloc[i]
    for j in range(i + 1, len(merged_streets)):
        street2 = merged_streets.iloc[j]
        # 检查两条街道是否相交,且交点是单个点(即交叉口)
        if street1["geometry"].intersects(street2["geometry"]):
            intersection = street1["geometry"].intersection(street2["geometry"])
            if intersection.type == "Point":
                dual_G.add_edge(street1.name, street2.name, intersection=intersection)

关键注意事项

  • 重名街道处理:仅用name容易出错,结合ref(道路编号)、highway类型或原始osmid集合生成street_id更准确。
  • 无名道路:必须给每条无名道路分配唯一ID,避免不同的无名路被错误合并。
  • 方向控制:如果需要保留单向通行信息,使用有向图(nx.DiGraph);无向图适合分析街道连通性。
  • 几何准确性:合并后的街道几何用union确保是连续的线串,后续可根据需求简化或平滑。

内容的提问来源于stack exchange,提问作者Frôis

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 10:54:55