В итоге я создал несколько пользовательских функций, основанных на пакете osmdat
. Я обнаружил, что osmdat
позволяет пользователю передавать пользовательские вызовы API в Overpass. После большого количества проб и ошибок в Overpass Turbo я выяснил синтаксис Overpass "достаточно хорошо", чтобы извлечь необходимую мне информацию. Я думаю, что эти три отдельные функции могут быть объединены в один вызов API Overpass, но это не для меня.
Поэтому я сначала создал функцию, чтобы получить список всех способов, которые были связаны с моим "фокусным" способом (1 из 2061 сегмента в моем фрейме данных):
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)
}
Затем я создал функцию для извлечения только идентификаторов узлов для моего основного пути.
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)
}
И третья функция для получения идентификаторов всех путей, пересекающих мой фокусный путь. Эта функция требует в качестве входных данных идентификаторы узлов фокусного пути.
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, в надежде ускорить запросы. Ха. Надеюсь ...
В любом случае. Я создал вложенное для l oop (не судите меня, я знаю, что purrr существует, я просто нахожу их интуитивным!), Используя эти три функции. Я также попытался объединить мой путь через распараллеливание этого, используя 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)
Общая логика c этого процесса:
- Выберите все WayID из фрагмента данных (1-100)
- Выберите идентификатор пути («Фокусный путь») из моего списка из 26 путей в блоке
- Найдите Идентификаторы пути «родных братьев».
- Извлечение идентификаторов узлов, составляющих целевой путь (вместе с информацией о том, где находятся сигналы - TY, osmdata
- Для каждого идентификатора узла целевого пути найдите идентификаторы путей которые пересекают его. Также возьмите классификацию этих путей.
- Избавьтесь от «пересекающихся путей», которые на самом деле являются родными братьями Фокального Пути - эти сегменты являются продолжением Фокального Пути. (Например, Я бы удалил таким образом из списка пересекающихся путей, если бы мой Focal Way был таким образом
- rbindlist навсегда
Это займет около 2-3 часов для запуска всех 2061 сегментов. Это долго; но даже прямые запросы в Overpass Turbo медленные, так что ... может быть, это правильно.