R:如何减少 osmdata 到 igraph 转换的运行时间

And*_*eas 5 r igraph openstreetmap overpass-api r-sf

是否可以减少以下代码的运行时间?

我的目标是从框边界指定的开放街道数据区域获取加权 igraph 对象。

目前我正在尝试使用立交桥 api 来卸载内存负载,这样我就不必在内存中保留大的 osm 文件。

首先,我获取由 bbox(仅街道)指定的 osm 数据作为 xml 结构

library(osmdata)
library(osmar)
install.packages("remotes")
remotes::install_github("hypertidy/scgraph")
library(scgraph)


dat <- opq(bbox = c(11.68771, 47.75233, 12.35058, 48.19743 )) %>%
  add_osm_feature(key = 'highway',value = c("trunk", "trunk_link", "primary","primary_link", "secondary", "secondary_link", "tertiary","tertiary_link", "residential", "unclassified" ))%>% 
  osmdata_xml ()
Run Code Online (Sandbox Code Playgroud)

然后我将生成的 xml 对象dat转换为 osmar 对象dat_osmar,最后转换为igraph对象:

dat_osmar <-as_osmar(xmlParse(dat))
dat_graoh <- as_igraph(dat_osmar)
Run Code Online (Sandbox Code Playgroud)

我如何优化这些例程?

也许可以将dat (XML) 对象分成块并并行解析它?

我经历了几个步骤才最终得到加权无向图。

目前,整个过程在我的机器上需要 89.555 秒。

如果我可以缩短这两个步骤的运行时间:

dat_osmar <-as_osmar(xmlParse(dat))
dat_graoh <- as_igraph(dat_osmar)
Run Code Online (Sandbox Code Playgroud)

那已经有帮助了。

我尝试的方法之一是使用osmdata_sc()而不是osmdata_xml()

这提供了一个硅酸盐对象,我可以将其转换为:

scgraph::sc_as_igraph(dat)
Run Code Online (Sandbox Code Playgroud)

到一个 igraph。

它的速度相当快,但遗憾的是重量正在丢失,所以这不是一个解决方案。

原因是:如果我使用函数从osmar对象到igraph对象的转换,则osmar::as_igraph()根据两条边之间的距离计算权重并将其添加到 igraph:

    edges <- lapply(dat, function(x) {
    n <- nrow(x)
    from <- 1:(n - 1)
    to <- 2:n
    weights <- distHaversine(x[from, c("lon", "lat")], x[to, 
      c("lon", "lat")])
    cbind(from_node_id = x[from, "ref"], to_node_id = x[to, 
      "ref"], way_id = x[1, "id"], weights = weights)
  })
Run Code Online (Sandbox Code Playgroud)

这是缺少的scgraph::sc_as_igraph(dat)

如果可以将其添加到硅酸盐igraph 的转换中,我可以跳过该dat_osmar <-as_osmar(xmlParse(dat))步骤并选择overpass->silicate->igraph更快的路线,而不是overpass->xml->osmar->igraph.

osmdata包还通过osmdata_sf()提供sf响应

所以也许工作流程overpass->sf->igraph更快,但在使用这种方式时,我需要根据边缘的距离将权重合并到图中,而我目前还不够好,无法做到这一点,并且非常感谢任何帮助。

此外,在使用 sf 和生成的 igraph 对象时,openstreetmap gps 点及其 ID 之间的连接不应丢失。这意味着我应该能够从生成的 Igraph 中找到 ID 的 GPS 位置。一个查找表就足够了。如果我去overpass->silicate->igraphoverpass->xml->osmar->igraph路线是可能的。我不确定路线是否仍然可行overpass->sf->igraph

agi*_*ila 3

如果您想从 R 中的道路网络开始创建图形对象,那么我将使用以下过程。

\n

首先,我需要安装sfnetworks从 github 存储库安装(因为我们最近修复了一些错误并且最新版本不在 CRAN 上)

\n
remotes::install_github("luukvdmeer/sfnetworks", quiet = TRUE)\n
Run Code Online (Sandbox Code Playgroud)\n

然后加载包

\n
library(sf)\n#> Linking to GEOS 3.9.0, GDAL 3.2.1, PROJ 7.2.1\nlibrary(tidygraph)\n#> \n#> Attaching package: \'tidygraph\'\n#> The following object is masked from \'package:stats\':\n#> \n#>     filter\nlibrary(sfnetworks)\nlibrary(osmdata)\n#> Data (c) OpenStreetMap contributors, ODbL 1.0. https://www.openstreetmap.org/copyright\n
Run Code Online (Sandbox Code Playgroud)\n

从 Overpass API 下载数据

\n
my_osm_data <- opq(bbox = c(11.68771, 47.75233, 12.35058, 48.19743 )) %>%\n  add_osm_feature(\n    key = \'highway\', \n    value = c("trunk", "trunk_link", "primary","primary_link", "secondary", "secondary_link", "tertiary","tertiary_link", "residential", "unclassified")\n  ) %>% \n  osmdata_sf(quiet = FALSE)\n#> Issuing query to Overpass API ...\n#> Rate limit: 2\n#> Query complete!\n#> converting OSM data to sf format\n
Run Code Online (Sandbox Code Playgroud)\n

现在我提取道路并构建 sfnetwork 对象:

\n
system.time({\n  # extract the roads\n  my_roads <- st_geometry(my_osm_data$osm_lines)\n  \n  # build the sfnetwork object\n  my_sfn <- as_sfnetwork(my_roads, directed = FALSE, length_as_weight = TRUE)\n})\n#>    user  system elapsed \n#>    3.03    0.16    3.28\n
Run Code Online (Sandbox Code Playgroud)\n

正如您所看到的,下载 OSM 数据后,只需几秒钟即可运行该过程。

\n

目前我忽略了 中的所有字段,但是如果您需要添加中my_osm_data$osm_lines的一些列,那么您可以修改前面的代码,如下所示:my_osm_data$osm_linesmy_roadsmy_roads <- my_osm_data$osm_lines[, "relevant columns"]

\n

有关对象构造的一些细节sfnetwork:参数指定我们要构建一个无向图(有关更多详细信息,请参阅此处此处的"directed = FALSE"文档),而参数表示边的长度将存储在列中被称为length_as_weight = TRUE"weight"和使用。

\n

这是打印的my_sfn

\n
my_sfn\n#> # A sfnetwork with 33179 nodes and 28439 edges\n#> #\n#> # CRS:  EPSG:4326 \n#> #\n#> # An undirected multigraph with 6312 components with spatially explicit edges\n#> #\n#> Registered S3 method overwritten by \'cli\':\n#>   method     from         \n#>   print.boxx spatstat.geom\n#> # Node Data:     33,179 x 1 (active)\n#> # Geometry type: POINT\n#> # Dimension:     XY\n#> # Bounding box:  xmin: 11.6757 ymin: 47.74745 xmax: 12.39161 ymax: 48.22025\n#>                     x\n#>           <POINT [\xc2\xb0]>\n#> 1 (11.68861 47.90971)\n#> 2 (11.68454 47.90937)\n#> 3 (11.75216 48.17638)\n#> 4 (11.75358 48.17438)\n#> 5  (11.7528 48.17351)\n#> 6 (11.74822 48.17286)\n#> # ... with 33,173 more rows\n#> #\n#> # Edge Data:     28,439 x 4\n#> # Geometry type: LINESTRING\n#> # Dimension:     XY\n#> # Bounding box:  xmin: 11.6757 ymin: 47.74745 xmax: 12.39161 ymax: 48.22025\n#>    from    to                                                           x weight\n#>   <int> <int>                                            <LINESTRING [\xc2\xb0]>  <dbl>\n#> 1     1     2 (11.68861 47.90971, 11.6878 47.90965, 11.68653 47.90954, 1~   306.\n#> 2     3     4 (11.75216 48.17638, 11.75224 48.17626, 11.75272 48.17556, ~   246.\n#> 3     5     6 (11.7528 48.17351, 11.75264 48.17344, 11.75227 48.17329, 1~   382.\n#> # ... with 28,436 more rows\n
Run Code Online (Sandbox Code Playgroud)\n

my_sfn根据定义是一个 igraph 对象:

\n
class(my_sfn)\n#> [1] "sfnetwork" "tbl_graph" "igraph"\n
Run Code Online (Sandbox Code Playgroud)\n

但是,如果你想更明确的话,那么

\n
as.igraph(my_sfn)\n#> IGRAPH 101dcdf U-W- 33179 28439 -- \n#> + attr: x (v/x), x (e/x), weight (e/n)\n#> + edges from 101dcdf:\n#>  [1]   1--  2   3--  4   5--  6   7--  8   9-- 10  11-- 12  13-- 14  15-- 16\n#>  [9]  17-- 18  16-- 19  20-- 21  21-- 22  23-- 24  25-- 26  27-- 28  29-- 30\n#> [17]  31-- 32  33-- 34  35-- 36  37-- 38  39-- 40  41-- 42  43-- 44  45-- 46\n#> [25]  14-- 47  48-- 49  50-- 51  52-- 53  54-- 55  56-- 57  36-- 58  58-- 59\n#> [33]  60-- 61  62-- 63  64-- 65  66-- 67  68-- 69  70-- 71  72-- 73  74-- 75\n#> [41]  76-- 77  78-- 79  80-- 81  82-- 83  84-- 85  86-- 87  88-- 89  90-- 91\n#> [49]  92-- 93  94-- 95  96-- 97  98-- 99 100--101 102--103 104--105 106--107\n#> [57] 108--109 110--111 112--113  80--114 115--116 117--118 119--120 121--122\n#> + ... omitted several edges\n
Run Code Online (Sandbox Code Playgroud)\n

您可以看到边的权重属性等于每个 LINESTRING 几何体的长度:

\n
all.equal(\n  target = igraph::edge_attr(as.igraph(my_sfn), "weight"), \n  current = as.numeric(st_length(my_roads))\n)\n#> [1] TRUE\n
Run Code Online (Sandbox Code Playgroud)\n

reprex 包(v1.0.0)创建于 2021-03-26

\n

如果您想了解更多详细信息sfnetworks,可以查看网站介绍性插图。话虽如此,我不明白你的意思

\n
\n

openstreetmap GPS 点及其 ID 之间的连接不应丢失

\n
\n

您能否添加更多详细信息以及对原始问题的评论或编辑?为什么需要 OSM id?OSM id 是什么意思?我认为我需要更多细节来扩展这个答案。

\n

编辑

\n

我刚刚重新阅读了 @mrhellmann 的答案,我注意到我忘记将 POLYGON 数据转换为线。无论如何,我建议osmdata::osm_poly2line()在运行代码后立即申请通过 Overpass API 下载 OSM 数据。

\n