我最终创建了一些依赖osmdat
包的自定义函数。我发现它osmdat
允许用户将自定义 API 调用传递给 Overpass。在Overpass Turbo中经过大量试验和错误后,我发现 Overpass 语法“足够好”以提取我需要的信息。我认为这三个独立的函数可以组合成一个 Overpass API 调用,但这超出了我的范围。
所以我首先创建了一个函数来获取与我的“焦点”方式相关的所有方式的列表(我的数据框中的 2,061 个段中的 1 个):
get_related_ways <- function(wayid, bboxstring){
related_ways_query <-
paste0("[bbox:", bboxstring,"];\n",
"way(id:", wayid, ");\n",
"rel(bw);\n", # get all sibling
"way(r);\n",
"out;")
related_ways <- osmdata_sf(related_ways_query)
related_ways <- related_ways$osm_lines
related_ways <- related_ways$osm_id
return(related_ways)
}
然后,我创建了一个函数来仅为我的焦点方式提取节点 ID。
get_nodes_from_way <- function(wayid){
nodes_from_way <- opq_osm_id(id = wayid, "way")%>%osmdata_sf()
nodes_from_way <- nodes_from_way$osm_points
nodes_from_way$geometry <- NULL
setnames(nodes_from_way, old = 'osm_id', new = 'node_from_way_id')
return(nodes_from_way)
}
第三个功能是获取与我的焦点相交的所有方式的 ID。此功能需要输入焦点路径的节点 ID。
get_intersecting_ways <- function(nodeid, bboxstring){
node_to_intways_query <-
paste0("[bbox:", bboxstring,"];\n",
"node(id:", nodeid, ");\n",
"way(bn)[highway];\n",
"out;")
intways_from_node <- osmdata_sf(node_to_intways_query)
intways_from_node <- intways_from_node$osm_lines
intways_from_node$geometry <- NULL
intways_from_node <- intways_from_node[,names(intways_from_node) %in%c("osm_id", "name", "highway", "ref")]
setnames(intways_from_node, old = 'osm_id', new = 'intersecting_way_id')
setnames(intways_from_node, old = 'highway', new = 'intersecting_way_type')
setnames(intways_from_node, old = 'ref', new = 'intersecting_way_ref')
setnames(intways_from_node, old = 'name', new = 'intersecting_way_name')
return(intways_from_node)
}
对于所有三个函数,我都有一个“bboxstring”或边界框字符串传递给 Overpass,希望加快查询速度。哈。希望...
无论如何。我使用这三个函数创建了一个嵌套的 for 循环(不要评判我,我知道 purrr 存在,我只是觉得它们很直观!)。II 还尝试通过使用 foreach 和 doParallel 并行化这个方法,并将我的数据集分成 100 个块,每个块有 26 种方式。它仍然很慢。Overpass API 可能很慢?更有可能我在设置时做错了,这只是我第 3 次或第 4 次使用 doParallel。
for(this_part in unique(cmp_osmdat$partnum)){
osm_character_ids <- as.character(cmp_osmdat$osm_id)
# test:
# osm_character_ids <- osm_character_ids[1:3]
# for each parallel process to get our intersecting ways ("all ways")
all_ways <-
foreach(w = seq_along(osm_character_ids),
# require list of packages from above:
.packages = packs,
.errorhandling = "remove", # remove data frames that throw an error
# print the stuff:
.verbose = TRUE) %dopar% {
environmentIsLocked(asNamespace("curl"))
unlockBinding(sym = "has_internet", asNamespace("curl"))
assign(x = "has_internet", value = {function() T}, envir = asNamespace("curl"))
this_way_id <- osm_character_ids[[w]]
# find ways that are related to this one (same road, different segments)
# so that we can filter these out as intersections:
these_related_ways <- get_related_ways(this_way_id, this_bbox_string)
# get nodes of this way:
these_nodes_from_way <- get_nodes_from_way(this_way_id)
# adding a column to store this way id, for easy rbind later
# (foreach doesn't store list names?)
these_nodes_from_way$way_id <- this_way_id
# create an empty list to store interesecting ways for each node:
these_intersecting_ways <- list()
# get intersecing ways from nodes:
for(n in seq_along(these_nodes_from_way$node_from_way_id)){
this_node <- these_nodes_from_way$node_from_way_id[[n]]
# put intersecting ways into our empty list (the name of the list item will be the node ID)
these_intersecting_ways[[this_node]] <- get_intersecting_ways(this_node, this_bbox_string)
} # end get intersecting ways from node
# combine intersecting ways of each node into one data table:
these_intersecting_ways_df <- rbindlist(these_intersecting_ways, idcol = 'node_from_way_id', use.names = T, fill = T)
# get rid of intersections with this way's realtives (other segments of the same road):
these_intersecting_ways_df <- these_intersecting_ways_df[!these_intersecting_ways_df$intersecting_way_id %in% these_related_ways,]
# to get node information, merge intersecting ways to our node data:
nodes_and_ways <- merge(these_intersecting_ways_df, these_nodes_from_way, by = 'node_from_way_id')
# return node and intersection data
return(nodes_and_ways)
} # end foreach
nodes_and_ways_df <- rbindlist(all_ways, use.names = T, fill = T)
# save file, one for each part (results in 10 csvs)
write.csv(nodes_and_ways_df,
file = paste0("intersection_density_results/intersection-density-data-part-", this_part, ".csv"), row.names = F)
} # end 10 parts
stopCluster(cl)
这个过程的一般逻辑是:
- 从数据框块中选择所有 WayID (1-100)
- 从我的块中 26 种方式的列表中获取一个方式 ID(“焦点方式”)
- 查找焦点路径的“兄弟”的路径 ID。
- 提取构成焦点路径的节点 ID(以及有关信号所在位置的信息 - TY、osmdata
- 对于焦点路径的每个节点 ID,找到与其相交的路径的路径 ID。还有抓分类的那些方法。
- 摆脱实际上是焦点方式的兄弟的“交叉方式”——这些部分是焦点方式的延续。(例如,如果我的焦点方式是这种方式,我将从相交方式列表中删除这种方式
- rbindlist 永远
运行所有 2061 段大约需要 2-3 小时。那是很长一段时间;但是,即使是 Overpass Turbo 中的直接查询也很慢,所以......也许这是正确的。