R:如何将osmdata的运行时减少为igraph转换

R:如何将osmdata的运行时减少为igraph转换,r,openstreetmap,igraph,sf,overpass-api,R,Openstreetmap,Igraph,Sf,Overpass Api,是否可以减少以下代码的运行时间 我的目标是从由框边界指定的开放街道数据区域中获取加权igraph对象 目前我正在尝试使用OverpassAPI来卸载内存负载,这样我就不必在内存中保存大的osm文件 首先,我得到一个由bbox(仅街道)指定为xml结构的osm数据 library(osmdata) library(osmar) install.packages("remotes") remotes::install_github("hypertidy/scgraph&

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

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

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

首先,我得到一个由bbox(仅街道)指定为xml结构的osm数据

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 ()
到一个igraph

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

原因是:如果我使用函数
osmar::as_igraph()
将对象从osmar转换为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)
  })
edges-sf->igraph
速度更快,但使用这种方法时,我需要根据边的距离将权重合并到图形中,我目前还不能这样做,非常感谢您的帮助


此外,在使用sf和生成的igraph对象时,openstreetmap gps点与其ID之间的连接不应丢失。这意味着我应该能够从生成的Igraph中找到ID的gps位置。一个查找表就足够了。如果我走
overpass->silicate->igraph
overpass->xml->osmar->igraph
路线,这是可能的。我不确定是否仍然可以使用
overpass->sf->igraph
route。将xml数据转换为另一种格式似乎需要很长时间。与其使用xml,不如让overpass返回一个
sf
对象,并使用它可能会更快。然后,
sf
对象可以被
sfnetworks
包操作和使用以获得网络,同时保留网络的空间方面。可以通过来自
sfnetworks
(或
tidygraph
)包的函数添加权重

我认为下面的代码重点关注速度和边缘权重问题。其他问题,如查找最近的节点或边,可以使用
sf
包的函数解决,但没有解决。否则,这将不仅仅是一个一次性的SO问题

通过对边缘使用
st_simplify
,可以以空间精度为代价提高速度。这种方法的一个问题是stnetworks将一个节点放置在每个线串与另一个线串相交的位置。返回的数据通常将一条道路分割为多个线串。作为示例,请参见下图边缘上两条较长的黄色道路。可能是一个可以解决的问题,但在这种情况下可能不值得

library(osmdata)
#library(osmar)
library(tidyverse)
library(sf)
library(ggplot2)
library(sfnetworks)
library(tidygraph)


# get data as an sf object rather than xml
## This is the slowest part of the code.
dat_sf <- 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_sf()



# Only keep lines & polygons. Points take up too much memory & 
## all seem to be on lines anyway. Change polygons to LINESTRING,
## as most seem to be roundabouts or a few odd cases.
lines_sf <- dat_sf$osm_lines %>% select(osm_id) %>% st_sf()
polys_sf <- dat_sf$osm_polygons %>% select(osm_id) %>% st_sf() %>% 
  st_cast('LINESTRING')

# Combine the two above sf objects into one 
dat_sf_bound <- rbind(lines_sf, polys_sf)

# get an sfnetwork
dat_sf_net <- as_sfnetwork(dat_sf_bound)

# add edge weight as distance
dat_sf_net <- dat_sf_net %>%
  activate(edges) %>%
  mutate(weight = edge_length())
仅边和带节点边的打印:

有几条长路会使颜色倾斜,但说明了将一条路一分为二的效果

编辑: 寻址注释以按纬度/经度坐标选择最近的边(道路)。节点(上面的交点/红点)没有我知道的osm id号。节点由
sfnetworks
创建

从lat/lon点的
sf
对象开始,作为我们制作的gps坐标

# random point
gps <- sfheaders::sf_point(data.frame(x = 11.81854, y = 48.04514)) %>% st_set_crs(4326)

# nearest edge(road) to the point. dat_sf_net must have edges activated.
near_edge <- st_nearest_feature(gps, dat_sf_net %>% st_as_sf())

>near_edge
[1] 4359

> st_as_sf(dat_sf_net)[near_edge,]
Simple feature collection with 1 feature and 4 fields
Geometry type: LINESTRING
Dimension:     XY
Bounding box:  xmin: 11.81119 ymin: 48.02841 xmax: 11.82061 ymax: 48.06845
Geodetic CRS:  WGS 84
# A tibble: 1 x 5
   from    to osm_id                                                           geometry   weight
  <int> <int> <chr>                                                    <LINESTRING [°]>      [m]
1  7590  7591 24232418 (11.81289 48.02841, 11.81213 48.03014, 11.81202 48.03062, 11.81… 4576.273

p3 <- gplot() +
      geom_sf(data = st_as_sf(dat_sf_net), color = 'black') +       
      geom_sf(data = gps, color = 'red') + 
      geom_sf(data = st_as_sf(dat_sf_net)[near_edge,], color = 'orange') + 
      coord_sf(xlim = c(11.7, 11.9), ylim = c(48, 48.1))

#随机点
全球定位系统%st\U set\U crs(4326)
#距离该点最近的边缘(道路)。dat_sf_网络必须激活边缘。
靠近边缘%st\u作为\u sf())
>近边缘
[1] 4359
>st_as_sf(dat_sf_net)[近边缘,]
具有1个要素和4个字段的简单要素集合
几何图形类型:线条字符串
尺寸:XY
边界框:xmin:11.81119 ymin:48.02841 xmax:11.82061 ymax:48.06845
大地测量CRS:WGS 84
#一个tibble:1 x 5
从到osm_id几何体权重
[m]
1  7590  7591 24232418 (11.81289 48.02841, 11.81213 48.03014, 11.81202 48.03062, 11.81… 4576.273

p3如果要从R中的道路网络开始创建图形对象,则我将使用以下步骤

首先,我需要从githubrepo安装
sfnetworks
(因为我们最近修复了一些bug,而且最新版本不在CRAN上)

remotes::install_github(“luukvdmeer/sfnetworks”,quiet=TRUE)
然后加载包

库(sf)
#>链接到GEOS 3.9.0、GDAL 3.2.1、项目7.2.1
图书馆(潮汐图)
#> 
#>附加包:“tidygraph”
#>以下对象已从“package:stats”屏蔽:
#> 
#>滤器
图书馆(SF网络)
图书馆(osmdata)
#>数据(c)OpenStreetMap贡献者,ODbL 1.0。https://www.openstreetmap.org/copyright
从立交桥API下载数据

my_osm_数据%
添加osm功能(
键=‘高速公路’,
值=c(“中继”、“中继链路”、“主”、“主链路”、“次”、“次链路”、“次链路”、“三级”、“三级链路”、“住宅”、“未分类”)
) %>% 
osmdata_sf(安静=假)
#>正在向立交桥API发出查询。。。
#>利率上限:2
#>查询完成!
#>将OSM数据转换为sf格式
现在,我提取道路并构建sfnetwork对象:

system.time({
#拔除道路
my_roads 3.03 0.16 3.28
如您所见,在下载OSM数据后,运行该过程只需几秒钟


目前,我忽略了
my_osm_data$osm_line
中的所有字段,但是如果您需要将
my_osm_data$osm_line
中的一些列添加到
my_roads
,那么您可以修改前面的代码如下:
my roads嗨!如果您对
osmar
的替代方法感兴趣,我可以尝试提供一个基于名为.
sfnetworks
的R包的解决方案基于
tidygraph
,这意味着
sfnetworks
返回的对象也是
igraph
对象。您好,绝对是。我需要能够做两件事:1.基于bbox区域从立交桥获取数据,2.从该区域获取igraph 3.be能耐
    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)
  })
library(osmdata)
#library(osmar)
library(tidyverse)
library(sf)
library(ggplot2)
library(sfnetworks)
library(tidygraph)


# get data as an sf object rather than xml
## This is the slowest part of the code.
dat_sf <- 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_sf()



# Only keep lines & polygons. Points take up too much memory & 
## all seem to be on lines anyway. Change polygons to LINESTRING,
## as most seem to be roundabouts or a few odd cases.
lines_sf <- dat_sf$osm_lines %>% select(osm_id) %>% st_sf()
polys_sf <- dat_sf$osm_polygons %>% select(osm_id) %>% st_sf() %>% 
  st_cast('LINESTRING')

# Combine the two above sf objects into one 
dat_sf_bound <- rbind(lines_sf, polys_sf)

# get an sfnetwork
dat_sf_net <- as_sfnetwork(dat_sf_bound)

# add edge weight as distance
dat_sf_net <- dat_sf_net %>%
  activate(edges) %>%
  mutate(weight = edge_length())
> dat_sf_net
# An sfnetwork with 33255 nodes and 28608 edges
#
# CRS:  EPSG:4326 
#
# A directed multigraph with 6391 components with spatially explicit edges
#
# Edge Data:     28,608 x 4 (active)
# Geometry type: LINESTRING
# Dimension:     XY
# Bounding box:  xmin: 11.6757 ymin: 47.74745 xmax: 12.39161 ymax: 48.22025
   from    to   weight                                                                  geometry
  <int> <int>      [m]                                                          <LINESTRING [°]>
1     1     2 306.3998 (11.68861 47.90971, 11.6878 47.90965, 11.68653 47.90954, 11.68597 47.909…
2     3     4 245.9225 (11.75216 48.17638, 11.75224 48.17626, 11.75272 48.17556, 11.7528 48.175…
3     5     6 382.2423 (11.7528 48.17351, 11.75264 48.17344, 11.75227 48.17329, 11.752 48.1732,…
4     7     8 131.1373 (11.70029 47.87861, 11.70046 47.87869, 11.70069 47.87879, 11.70138 47.87…
5     9    10 252.9170 (11.75733 48.17339, 11.75732 48.17343, 11.75726 48.17357, 11.75718 48.17…
6    11    12 131.6942 (11.75582 48.17036, 11.75551 48.1707, 11.75521 48.17106, 11.75507 48.171…
# … with 28,602 more rows
#
# Node Data:     33,255 x 1
# Geometry type: POINT
# Dimension:     XY
# Bounding box:  xmin: 11.6757 ymin: 47.74745 xmax: 12.39161 ymax: 48.22025
             geometry
          <POINT [°]>
1 (11.68861 47.90971)
2 (11.68454 47.90937)
3 (11.75216 48.17638)
# … with 33,252 more rows
# random point
gps <- sfheaders::sf_point(data.frame(x = 11.81854, y = 48.04514)) %>% st_set_crs(4326)

# nearest edge(road) to the point. dat_sf_net must have edges activated.
near_edge <- st_nearest_feature(gps, dat_sf_net %>% st_as_sf())

>near_edge
[1] 4359

> st_as_sf(dat_sf_net)[near_edge,]
Simple feature collection with 1 feature and 4 fields
Geometry type: LINESTRING
Dimension:     XY
Bounding box:  xmin: 11.81119 ymin: 48.02841 xmax: 11.82061 ymax: 48.06845
Geodetic CRS:  WGS 84
# A tibble: 1 x 5
   from    to osm_id                                                           geometry   weight
  <int> <int> <chr>                                                    <LINESTRING [°]>      [m]
1  7590  7591 24232418 (11.81289 48.02841, 11.81213 48.03014, 11.81202 48.03062, 11.81… 4576.273

p3 <- gplot() +
      geom_sf(data = st_as_sf(dat_sf_net), color = 'black') +       
      geom_sf(data = gps, color = 'red') + 
      geom_sf(data = st_as_sf(dat_sf_net)[near_edge,], color = 'orange') + 
      coord_sf(xlim = c(11.7, 11.9), ylim = c(48, 48.1))

my_sfn
#> # A sfnetwork with 33179 nodes and 28439 edges
#> #
#> # CRS:  EPSG:4326 
#> #
#> # An undirected multigraph with 6312 components with spatially explicit edges
#> #
#> Registered S3 method overwritten by 'cli':
#>   method     from         
#>   print.boxx spatstat.geom
#> # Node Data:     33,179 x 1 (active)
#> # Geometry type: POINT
#> # Dimension:     XY
#> # Bounding box:  xmin: 11.6757 ymin: 47.74745 xmax: 12.39161 ymax: 48.22025
#>                     x
#>           <POINT [°]>
#> 1 (11.68861 47.90971)
#> 2 (11.68454 47.90937)
#> 3 (11.75216 48.17638)
#> 4 (11.75358 48.17438)
#> 5  (11.7528 48.17351)
#> 6 (11.74822 48.17286)
#> # ... with 33,173 more rows
#> #
#> # Edge Data:     28,439 x 4
#> # Geometry type: LINESTRING
#> # Dimension:     XY
#> # Bounding box:  xmin: 11.6757 ymin: 47.74745 xmax: 12.39161 ymax: 48.22025
#>    from    to                                                           x weight
#>   <int> <int>                                            <LINESTRING [°]>  <dbl>
#> 1     1     2 (11.68861 47.90971, 11.6878 47.90965, 11.68653 47.90954, 1~   306.
#> 2     3     4 (11.75216 48.17638, 11.75224 48.17626, 11.75272 48.17556, ~   246.
#> 3     5     6 (11.7528 48.17351, 11.75264 48.17344, 11.75227 48.17329, 1~   382.
#> # ... with 28,436 more rows
class(my_sfn)
#> [1] "sfnetwork" "tbl_graph" "igraph"
as.igraph(my_sfn)
#> IGRAPH 101dcdf U-W- 33179 28439 -- 
#> + attr: x (v/x), x (e/x), weight (e/n)
#> + edges from 101dcdf:
#>  [1]   1--  2   3--  4   5--  6   7--  8   9-- 10  11-- 12  13-- 14  15-- 16
#>  [9]  17-- 18  16-- 19  20-- 21  21-- 22  23-- 24  25-- 26  27-- 28  29-- 30
#> [17]  31-- 32  33-- 34  35-- 36  37-- 38  39-- 40  41-- 42  43-- 44  45-- 46
#> [25]  14-- 47  48-- 49  50-- 51  52-- 53  54-- 55  56-- 57  36-- 58  58-- 59
#> [33]  60-- 61  62-- 63  64-- 65  66-- 67  68-- 69  70-- 71  72-- 73  74-- 75
#> [41]  76-- 77  78-- 79  80-- 81  82-- 83  84-- 85  86-- 87  88-- 89  90-- 91
#> [49]  92-- 93  94-- 95  96-- 97  98-- 99 100--101 102--103 104--105 106--107
#> [57] 108--109 110--111 112--113  80--114 115--116 117--118 119--120 121--122
#> + ... omitted several edges
all.equal(
  target = igraph::edge_attr(as.igraph(my_sfn), "weight"), 
  current = as.numeric(st_length(my_roads))
)
#> [1] TRUE