OSMnx中使用consolidate_intersections时如何排除含特定属性的边以保留桥梁
这个问题我之前做城市路网简化项目的时候刚好碰到过,简直感同身受——既要用足够大的阈值把那些零散的普通路口合并干净,结果短桥梁愣是被误删,调低阈值又完全达不到简化效果。给你分享两个亲测有效的方案:
方案一:拆分路网分别处理(最稳妥,无需修改源码)
核心思路就是把桥梁边和普通路网彻底分开,只对普通路网执行路口合并,最后再把桥梁边加回去,这样桥梁边完全不受合并逻辑的影响:
从原始路网中拆分出纯桥梁子图和纯非桥梁子图
先复制原图,然后分别移除不需要的边,得到两个独立的子图:import osmnx as ox import pandas as pd # 假设你已经加载好原始路网G,比如: # G = ox.graph_from_place("上海市黄浦区", network_type="drive") # 提取仅包含桥梁边的子图 G_bridges = G.copy() non_bridge_edges = [(u, v, k) for u, v, k, d in G.edges(keys=True, data=True) if d.get("bridge") != "yes"] G_bridges.remove_edges_from(non_bridge_edges) # 提取仅包含非桥梁边的子图 G_non_bridges = G.copy() bridge_edges = [(u, v, k) for u, v, k, d in G.edges(keys=True, data=True) if d.get("bridge") == "yes"] G_non_bridges.remove_edges_from(bridge_edges)对非桥梁子图执行路口合并
这里就可以放心用你需要的大阈值,完全不用担心桥梁被合并:# 用你原来的阈值,比如tolerance=15米(根据你的需求调整) G_non_bridges_consolidated = ox.consolidate_intersections( G_non_bridges, tolerance=15, rebuild_graph=True, dead_ends=True # 保留死胡同节点,避免桥梁端点被误删 )合并处理后的非桥梁路网和原始桥梁路网
把两个子图转成GeoDataFrame合并,再转回图结构即可:# 转换为GeoDataFrame方便合并 nodes_non_bridge, edges_non_bridge = ox.graph_to_gdfs(G_non_bridges_consolidated) nodes_bridge, edges_bridge = ox.graph_to_gdfs(G_bridges) # 合并节点(优先保留处理后的非桥梁节点,避免重复) nodes_combined = pd.concat([nodes_non_bridge, nodes_bridge]).drop_duplicates(subset="osmid", keep="first") # 合并所有边 edges_combined = pd.concat([edges_non_bridge, edges_bridge]) # 转回路网图 G_final = ox.gdfs_to_graph(nodes_combined, edges_combined)
这个方法的好处是完全不修改OSMnx的原生逻辑,兼容性拉满,而且桥梁边100%保留原始状态。
方案二:自定义合并逻辑(进阶玩法)
如果你不想拆分路网,也可以自己封装一个带节点过滤的合并函数——核心是在OSMnx的合并逻辑中,跳过所有属于桥梁边的节点:
先给所有桥梁边的端点标记一个“保护”属性:
# 遍历所有桥梁边,给两端节点加上protected标记 for u, v, k, d in G.edges(keys=True, data=True): if d.get("bridge") == "yes": G.nodes[u]["protected"] = True G.nodes[v]["protected"] = True参考OSMnx原生的
consolidate_intersections函数,修改节点聚类的逻辑,在生成合并簇的时候,排除所有带有protected=True属性的节点。这样合并操作就不会碰桥梁的端点,自然也就不会合并桥梁边了。
不过这个方案需要你对OSMnx的源码有一定了解,适合需要高度自定义的场景,一般来说方案一完全够用。
小提醒
合并后可以用ox.plot_graph(G_final)检查一下,如果发现桥梁边的端点和合并后的普通路网节点不匹配,用空间匹配把桥梁端点替换成最近的合并节点就行,比如用geopandas.sjoin_nearest。
内容来源于stack exchange

