0

我的目标是生成道路网络中空间线之间的邻域关系对象。如果我的数据是空间多边形,我可以使用它spdep::poly2nb来执行此操作,但我无法确定如何为空间线执行此操作。

在下面的表示中,我尝试使用igraph::as_adjacency_matrix创建邻接矩阵,然后使用spdep::mat2listw. 但这是正确的方法吗?

一旦我有了邻居列表,我还想用road_id属性标记。

library(sfnetworks)
library(sf)
library(spdep)
library(igraph)

net <- as_sfnetwork(roxel, directed = FALSE) %>% 
  activate("edges") %>% 
  mutate(road_id = row_number()+1000)

# Make adjacency matrix
B_net <- igraph::as_adjacency_matrix(net, edges = TRUE, attr = names)

# Make neighbour list
nb_net <- mat2listw(B_net)$neighbours
# Can't use row.names in mat2listw because how do I set row.names in igraph::as_adjacency_matrix

sfnetworks编辑:在本期https://github.com/luukvdmeer/sfnetworks/issues/10中使用methof 的新方法给出了一个邻居列表。

net_sf <- st_as_sf(net)

net_neigh <- st_touches(net_sf)

# define ad-hoc function to translate sgbp into nb (as documented in 
# https://r-spatial.github.io/spdep/articles/nb_sf.html#creating-neighbours-using-sf-objects)
as.nb.sgbp <- function(x) {
  attrs <- attributes(x)
  x <- lapply(x, function(i) { if(length(i) == 0L) 0L else i } )
  attributes(x) <- attrs
  class(x) <- "nb"
  x
}

net_nb <- as.nb.sgbp(net_neigh)
net_lw <- nb2listw(net_nb)
4

2 回答 2

2

IMO 有几种方法可以为类似于输出的空间线创建邻域列表对象spdep::poly2nbspdep::poly2nb不幸的是,我将在这里尝试解释一些假设(类似于 的皇后参数)。

首先我们加载一些包和数据

library(sf)
#> Linking to GEOS 3.8.0, GDAL 3.0.4, PROJ 6.3.1
library(sfnetworks)
library(igraph)
#> 
#> Attaching package: 'igraph'
#> The following objects are masked from 'package:stats':
#> 
#>     decompose, spectrum
#> The following object is masked from 'package:base':
#> 
#>     union

第一个解决方案是基于sf::st_touches函数:

roxel_touches <- stplanr::geo_projected(roxel, st_touches)
roxel_touches
#> Sparse geometry binary predicate list of length 851, where the predicate was `touches'
#> first 10 elements:
#>  1: 101, 146, 493, 577, 742
#>  2: 149, 285, 294, 354, 521
#>  3: 268, 269, 270, 376, 431
#>  4: 8, 9, 276, 735
#>  5: 8, 94, 95, 108, 590
#>  6: 272, 728, 732
#>  7: 274, 275, 276, 734
#>  8: 4, 5, 95, 108, 735
#>  9: 4, 276, 281, 728
#>  10: 273, 281, 729, 733

输出是根据谓词sgbp汇总空间线之间关系的对象。touches例如输出的第一行表示roxel对象的第一行接触到第 101、146、493、577 和 742 行。该stplanr::geo_projected函数用于应用几何二元谓词函数而不重新投影数据(否则我们会得到一些警告信息)。

touches谓词在相应的帮助页面 ( )?sf::st_touches此处定义。简而言之,当该点位于其边界的联合中时(即线的第一个和最后一个点) ,该sf::st_touches函数匹配共享一个公共点的几何图形。LINESTRING例如,以下线彼此相交,但它们不相交,因为公共点不在它们的边界内。

es_1 <- st_sfc(
  st_linestring(rbind(c(0, -1), c(0, 1))), 
  st_linestring(rbind(c(-1, 0), c(1, 0)))
)
par(mar = rep(1, 4))
plot(es_1, col = c("red", "blue"), lwd = 2)
legend("topright", legend = c(1, 2), col = c("red", "blue"), lty = 1, lwd = 2)

st_intersects(es_1)
#> Sparse geometry binary predicate list of length 2, where the predicate was `intersects'
#>  1: 1, 2
#>  2: 1, 2
st_touches(es_1)
#> Sparse geometry binary predicate list of length 2, where the predicate was `touches'
#>  1: (empty)
#>  2: (empty)

另一个稍有不同的解决方案是使用以下sf::st_relate函数创建的:

roxel_relate <- stplanr::geo_projected(roxel, st_relate, pattern = "FF*FT****")
roxel_relate
#> Sparse geometry binary predicate list of length 851, where the predicate was `relate_pattern'
#> first 10 elements:
#>  1: 101, 146, 493, 577, 742
#>  2: 149, 285, 294, 354, 521
#>  3: 268, 269, 270, 376, 431
#>  4: 8, 9, 276, 735
#>  5: 8, 94, 95, 108, 590
#>  6: 272, 732
#>  7: 274, 275, 276, 734
#>  8: 4, 5, 95, 108, 735
#>  9: 4, 276, 281, 728
#>  10: 273, 281, 729, 733

输出(再次)是一个sgbp对象,它根据带有 pattern 的“相关”谓词总结空间线之间的关系"FF*FT****"。这种类型的模式用于匹配LINESTRINGS当且仅当它们在其边界中共享至少一个点,它们的内部不相交并且内部和边界之间没有共享点。例如,以下两行是接触的,但根据模式它们不相关"FF*FT****"

es_2 <- st_sfc(
  st_linestring(rbind(c(0, -1), c(0, 1)), "red"), 
  st_linestring(rbind(c(0, 0), c(1, 0)), "blue"), 
  crs = 32632
)
plot(es_2, col = c("red", "blue"), lwd = 2)
legend(x = "topright", legend = c(1, 2), lty = 1, col = c("red", "blue"), lwd = 2)

st_touches(es_2)
#> Sparse geometry binary predicate list of length 2, where the predicate was `touches'
#>  1: 2
#>  2: 1
st_relate(es_2, pattern = "FF*FT****")
#> Sparse geometry binary predicate list of length 2, where the predicate was `relate_pattern'
#>  1: (empty)
#>  2: (empty)

您可以sf::st_relatesf::st_relate. 当我们比较两个谓词的输出时,我们可以理解为什么st_touches和之间的这种区别很重要。st_relate

roxel_touches
#> Sparse geometry binary predicate list of length 851, where the predicate was `touches'
#> first 10 elements:
#>  1: 101, 146, 493, 577, 742
#>  2: 149, 285, 294, 354, 521
#>  3: 268, 269, 270, 376, 431
#>  4: 8, 9, 276, 735
#>  5: 8, 94, 95, 108, 590
#>  6: 272, 728, 732
#>  7: 274, 275, 276, 734
#>  8: 4, 5, 95, 108, 735
#>  9: 4, 276, 281, 728
#>  10: 273, 281, 729, 733
roxel_relate
#> Sparse geometry binary predicate list of length 851, where the predicate was `relate_pattern'
#> first 10 elements:
#>  1: 101, 146, 493, 577, 742
#>  2: 149, 285, 294, 354, 521
#>  3: 268, 269, 270, 376, 431
#>  4: 8, 9, 276, 735
#>  5: 8, 94, 95, 108, 590
#>  6: 272, 732
#>  7: 274, 275, 276, 734
#>  8: 4, 5, 95, 108, 735
#>  9: 4, 276, 281, 728
#>  10: 273, 281, 729, 733

我们可以看到它们几乎相同,但在少数情况下(例如,参见第 6 行)。如果我们绘制相应的线,我们可以更好地理解这种情况:

plot(st_geometry(roxel[c(6, 272, 728, 732), ]), col = c("red", "orange", "blue", "darkgreen"), lwd = 2)
legend(x = "topright", legend = c(6, 272, 728, 732), lty = 1, col = c("red", "orange", "blue", "darkgreen"), lwd = 2)

我们可以看到第 6 行与所有其他行接触,但根据模式,它与第 728 行“无关” "FF*FT****"。这意味着如果您从 开始创建邻居列表st_relate,那么您(至少)缺少一个现有链接。我明确选择了这种模式,因为用于sfnetworks从对象推断图形结构的默认算法sf与那种模式非常相似(可能相同)。我们可以检查这个事实,创建line graph关联到由创建的图as_sfnetwork

roxel_sfnetworks <- as_sfnetwork(roxel, directed = FALSE)
roxel_line_graph <- make_line_graph(roxel_sfnetworks)
roxel_adj_list <- as_adj_list(roxel_line_graph)
str(roxel_adj_list[1:10], give.attr = FALSE)
#> List of 10
#>  $ : 'igraph.vs' int [1:5] 101 146 493 577 742
#>  $ : 'igraph.vs' int [1:5] 149 285 294 354 521
#>  $ : 'igraph.vs' int [1:5] 268 269 270 376 431
#>  $ : 'igraph.vs' int [1:4] 8 9 276 735
#>  $ : 'igraph.vs' int [1:5] 8 94 95 108 590
#>  $ : 'igraph.vs' int [1:2] 272 732
#>  $ : 'igraph.vs' int [1:4] 274 275 276 734
#>  $ : 'igraph.vs' int [1:5] 4 5 95 108 735
#>  $ : 'igraph.vs' int [1:4] 4 276 281 728
#>  $ : 'igraph.vs' int [1:4] 273 281 729 733

并测试它们的相等性:

all(unlist(mapply(function(x, y) unique(x) == unique(y), roxel_adj_list, roxel_relate)))
#> [1] TRUE

我不得不使用该unique函数,因为igraph当两个顶点之间有多个边时,输出可能会返回重复的索引,即

is.simple(roxel_sfnetworks)
#> [1] FALSE

无论如何,我们可以看到输出与 相同,这意味着如果不特别考虑底层街道网络对象roxel_relate,我们不能总是安全地使用st_relate或运行。make_line_graph我不想在这里添加太多细节,因为示例太复杂了,但是我们不能总是使用该st_touches函数来推断邻居列表对象(但如果你想阅读更多关于这个主题的内容,我最近写了一个有关该主题的论文,目前正在审查中)。

无论如何,那篇论文的总结是,我认为只有在使用函数转换对象之后,我们才能安全地使用st_touchesst_relate函数来生成邻居列表。(实际上这两种方法之间仍然存在一些非常小的差异,但我仍然必须正确弄清楚如何解决它们。目前我会使用 st_touches)。roxelstplanr::rnet_breakup_vertices

# 1 - Transform the input roxel object
roxel2 <- stplanr::rnet_breakup_vertices(roxel)
#> Splitting rnet object at the intersection points between nodes and internal vertexes

# 2- Apply st_touches function
roxel2_touches <- stplanr::geo_projected(roxel2, st_touches)
roxel2_touches
#> Sparse geometry binary predicate list of length 876, where the predicate was `touches'
#> first 10 elements:
#>  1: 105, 150, 504, 591, 765
#>  2: 153, 291, 300, 363, 533
#>  3: 274, 275, 276, 386, 441
#>  4: 8, 9, 282, 758
#>  5: 8, 98, 99, 112, 604
#>  6: 278, 750, 751, 755
#>  7: 280, 281, 282, 757
#>  8: 4, 5, 99, 112, 758
#>  9: 4, 282, 287, 750
#>  10: 279, 287, 752, 756

然后,如果需要,可以将结果转换为 nb 对象:

as.nb.sgbp <- function(x) {
  attrs <- attributes(x)
  x <- lapply(x, function(i) { if(length(i) == 0L) 0L else i } )
  attributes(x) <- attrs
  class(x) <- "nb"
  x
}

roxel2_nb <- as.nb.sgbp(roxel2_touches)
roxel2_lw <- spdep::nb2listw(roxel2_nb)
roxel2_lw
#> Characteristics of weights list object:
#> Neighbour list object:
#> Number of regions: 876 
#> Number of nonzero links: 3318 
#> Percentage nonzero weights: 0.4323826 
#> Average number of links: 3.787671 
#> 
#> Weights style: W 
#> Weights constants summary:
#>     n     nn  S0       S1       S2
#> W 876 767376 876 494.4606 3580.933

我们还可以使用“线图”选项:

str(as_adj_list(make_line_graph(as_sfnetwork(roxel2, directed = FALSE)))[1:10], give.attr = FALSE)
#> List of 10
#>  $ : 'igraph.vs' int [1:5] 105 150 504 591 765
#>  $ : 'igraph.vs' int [1:5] 153 291 300 363 533
#>  $ : 'igraph.vs' int [1:5] 274 275 276 386 441
#>  $ : 'igraph.vs' int [1:4] 8 9 282 758
#>  $ : 'igraph.vs' int [1:5] 8 98 99 112 604
#>  $ : 'igraph.vs' int [1:4] 278 750 751 755
#>  $ : 'igraph.vs' int [1:4] 280 281 282 757
#>  $ : 'igraph.vs' int [1:5] 4 5 99 112 758
#>  $ : 'igraph.vs' int [1:4] 4 282 287 750
#>  $ : 'igraph.vs' int [1:4] 279 287 752 756

reprex 包(v0.3.0)于 2020 年 6 月 1 日创建

我知道我写了很多(可能是不必要的)信息,但我认为它们对于正确提供有关建议解决方案的上下文很有用。如果不清楚,请添加更多问题。

于 2020-06-01T13:33:58.363 回答
0

已编辑以显示此sfnetworks问题的解决方案https://github.com/luukvdmeer/sfnetworks/issues/10

于 2020-06-01T10:49:38.153 回答