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

如何高效利用sfnetwork与igraph构建全连通道路网络?

高效构建全连通道路网络(sfnetwork + igraph)

问题背景

我正在处理存储道路网络的sf文件(每行代表一段道路),目标是构建全连通网络。使用sfnetwork和igraph创建网络后,发现生成的网络被分割为3749个不连通组件,尝试用空间网格法识别组件间最近点来连接,但效率极低且未完成边的创建。

原创建网络代码:

strade_net <- strade_snapped %>%
  as_sfnetwork() %>%
  activate("edges") %>%
  arrange(edge_length()) %>%
  filter(!edge_is_multiple()) %>%
  filter(!edge_is_loop()) %>%
  activate("edges") %>%
  mutate(weight = edge_length()) %>%
  activate("nodes") %>%
  mutate(bc = centrality_betweenness(weights = weight, directed = FALSE)) %>%
  mutate(id = as.character(seq_along(.)))

nodes_strade <- sf::st_as_sf(strade_net, "nodes") 
edges_strade <- sf::st_as_sf(strade_net, "edges")

nodes <- st_transform(nodes_strade, crs = 32632)
edges <- st_transform(edges_strade, crs = 32632)

# Create the graph
graph <- graph_from_data_frame(d = edges, vertices = nodes$id, directed = FALSE)

组件情况:

print(components(graph)$no)
[1] 3749
print(components(graph)$csize[1:50])
 [1]   2   5   5   4  41   7  20  92   2  18 245  11   4   6 308   4  19   2   2   2   4   2   2   2   2
[26]   2   2   2   2   2   2   2   2   2   2   2   2   2   2   2   2   2   2   2   2 101   5   2   2   2
print(components(graph)$membership[1:50])
 1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 
 1  1  2  2  3  3  4  4  5  5  6  6  7  7  8  8  8  8  9  9 10 10 11 11 12 12 13 13 14 14 15 15 16 16 17 
36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 
17 18 18 19 19 20 20 21 21 22 22 23 23 24 24 

高效解决方案

方案一:利用空间索引快速匹配跨组件最近节点

直接在sfnetwork生态内操作,借助nngeo::st_nn的空间索引功能,快速找到跨组件的最近节点对,避免手动循环网格的低效问题。

library(sfnetwork)
library(nngeo)
library(dplyr)

# 1. 在sfnetwork中标记每个节点所属组件
strade_net <- strade_net %>%
  activate("nodes") %>%
  mutate(component = group_components()) # 内置函数直接生成组件分组

# 2. 提取带组件信息的节点sf对象
nodes_with_comp <- st_as_sf(strade_net, "nodes")

# 3. 用空间索引找每个节点的跨组件最近邻(最大距离10米)
nn_pairs <- st_nn(
  nodes_with_comp, nodes_with_comp,
  k = 1,
  maxdist = 10,
  progress = FALSE,
  returnDist = TRUE
)

# 4. 过滤出有效跨组件配对,去重避免双向重复边
valid_pairs <- lapply(seq_along(nn_pairs$nn), function(i) {
  neighbor <- nn_pairs$nn[[i]]
  if (length(neighbor) == 0) return(NULL)
  if (nodes_with_comp$component[i] != nodes_with_comp$component[neighbor]) {
    data.frame(
      from = nodes_with_comp$id[i],
      to = nodes_with_comp$id[neighbor],
      weight = nn_pairs$dist[[i]]
    )
  } else {
    NULL
  }
}) %>% bind_rows() %>%
  mutate(pair_id = paste(pmin(from, to), pmax(from, to), sep = "-")) %>%
  distinct(pair_id, .keep_all = TRUE) %>%
  select(-pair_id)

# 5. 生成连接边的LINESTRING几何并加入sfnetwork
edges_to_add <- st_sf(
  from = valid_pairs$from,
  to = valid_pairs$to,
  weight = valid_pairs$weight,
  geometry = st_nearest_points(
    nodes_with_comp[nodes_with_comp$id %in% valid_pairs$from, ],
    nodes_with_comp[nodes_with_comp$id %in% valid_pairs$to, ]
  )
)

# 6. 合并到原网络得到全连通网络
strade_net_connected <- strade_net %>%
  activate("edges") %>%
  bind_edges(edges_to_add)

# 验证连通性:组件数应为1
strade_net_connected %>%
  activate("nodes") %>%
  mutate(component = group_components()) %>%
  pull(component) %>%
  unique() %>%
  length()

方案二:优先合并小组件(优化性能)

如果存在大量极小组件(如size<=2),可以只处理小组件与大组件的连接,进一步减少计算量:

# 1. 统计组件大小,筛选极小组件
component_sizes <- strade_net %>%
  activate("nodes") %>%
  as_tibble() %>%
  count(component, name = "size")

small_components <- component_sizes %>% filter(size <= 2) %>% pull(component)

# 2. 分离小组件节点和其他节点
small_nodes <- nodes_with_comp %>% filter(component %in% small_components)
large_nodes <- nodes_with_comp %>% filter(!component %in% small_components)

# 3. 找每个小节点的最近大节点
nn_small_large <- st_nn(
  small_nodes, large_nodes,
  k = 1,
  maxdist = 10,
  returnDist = TRUE,
  progress = FALSE
)

# 4. 生成有效配对并创建连接边
valid_small_pairs <- lapply(seq_along(nn_small_large$nn), function(i) {
  neighbor <- nn_small_large$nn[[i]]
  if (length(neighbor) == 0) return(NULL)
  data.frame(
    from = small_nodes$id[i],
    to = large_nodes$id[neighbor],
    weight = nn_small_large$dist[[i]]
  )
}) %>% bind_rows()

# 后续生成边、合并网络步骤同方案一

关键优化点

  • 空间索引加速:st_nn默认使用R-tree空间索引,比手动网格循环效率提升几个数量级
  • sfnetwork原生操作:避免在sfnetwork和igraph之间来回转换,保留空间属性的同时简化流程
  • 去重逻辑:通过配对ID去重,避免重复添加双向边
  • 优先处理小组件:减少不必要的组件间配对计算,适合组件数量极多的场景

内容的提问来源于stack exchange,提问作者sawu

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 14:49:50