Extract count of intersections for Open Street Map way IDs, by route type
编辑添加了额外的细节
我有一个包含 2,061 个开放街道地图 (OSM) 路段的 shapefile。我的 shapefile 中的每个段都由其 OSM Way ID 标识。
这是我数据中五个片段的示例:
1 2 3 4 5 6 7 | data = structure(list(osm_id = structure(1:5, .Label = c("17990110", "17993246","17994983","17994985","17995338"), class ="factor"), name = structure(c(1L, 3L, 4L, 5L, 2L), .Label = c("109th Avenue Northeast", "85th Avenue Northeast","Bunker Lake Boulevard Northeast", "Foley Boulevard","Northdale Boulevard Northwest"), class ="factor")), row.names = c(NA, 5L), class = c("sf","data.frame")) |
对于这 2061 个路段中的每一个,我想分别计算每种道路类型(住宅、主要、第三...)与其他道路的交叉口数量。
例如,这条 OSM 路与其他 27 条路相交,其中 11 条是"住宅",其中 3 条是"二级"高速公路。
这是次要的分析,但最终,对于多种类型道路连接的交叉路口,我将选择"最大"类型的道路。例如,该节点连接了一条服务道路和一条住宅道路;我想选择住宅道路道路。我想我可以为此创建自己的层次结构列表并在以后处理它。
我正在尝试使用 R Open Sci 包 osmdata
到目前为止,我可以使用 osmdata:
到达 #2(信号交叉口)
1 2 3 4 5 6 7 | node_dat <- opq_osm_id(type ="node", id = '17990110')%>% opq_string()%>% osmdata_sf node_dat_pts <- node_dat$osm_points nrow(node_dat_pts[node_dat_pts$traffic_signals %in%"signal",]) |
这表明该 OSM 路段存在三个交通信号灯。 (尽管实际上只有两个信号交叉口——两个信号与分开的高速公路相关联……但这可能是另一个问题)。
我是 R 本地人,这就是 osmdata 包对我如此有吸引力的原因,但我愿意探索 Overpass API 中的查询。 TBH 我发现关于如何在网站上获取交叉节点的示例不太适用 - 我不清楚如何将此过程扩展到我拥有的 2000 个方式 ID 的长列表(但如果文档或示例存在,请指点我)。
我也愿意探索 Python 中的其他工具库; Python osmnx 包似乎具有用于计算"交叉点密度"的出色工具,但对于指定的多边形,如城市边界,并且似乎没有通过方式或节点 ID 在 Python 中创建调用的功能。
我也知道我可以在 ArcGIS 或 QGIS 中执行此操作,但因为我的数据库中已经有这些 OSM ID,所以为大都市地区加载整个交叉路口的形状文件似乎是一种浪费做某种缓冲过程来获取我需要的信息。另外,如果我有一个方便的脚本来通过方式或节点 ID 提取一些信息,我可以更轻松地扩大我提取的数据种类,以获取为 OSM 元素记录的其他重要信息。
感谢空间数据社区!
交通信号应始终标记为
1 2 3 4 5 6 7 8 9 10 | library(osmdata) hw <- opq("minneapolis") %>% add_osm_feature(key ="highway") %>% osmdata_sf() signal_nodes <- opq("minneapolis") %>% add_osm_feature(key ="traffic_signals") %>% osmdata_sf() index <- which (!is.na (hw$osm_points$traffic_signals) | grepl ("traffic_signals", hw$osm_points$highway)) # grepl because can be"traffic_signals:<value>" signal_node_ids <- unique (c (signal_nodes$osm_points$osm_id, hw$osm_points$osm_id [index])) |
然后保存描述交通信号的节点的所有 OSM ID 值。
获得路口密度的一种直接方法是将高速公路的
1 2 3 4 | library(dodgr) hw <- osm_poly2line(hw)$osm_lines %>% weight_streetnet(keep_cols ="highway", wt_profile = 1) %>% # wt_profile irrelevant here dodgr_contract_graph() |
1 2 | net_footway <- net[net$highway =="footway", ] table(table(c(net_footway$from_id, net_footway$to_id))) |
值为1表示单向终端节点;值为 2 表示双向终端节点;值 4 表示两条边之间的交叉点;等等。那张桌子增加到 14 条,因为人行道可能相当复杂,而且明尼阿波利斯某处显然有 7 条人行道的交汇处。这些 ID 是 OSM id,因此您可以轻松检查它们中的哪些在
需要解决的剩余问题:
我最终创建了一些依赖于
所以我首先创建了一个函数来获取与我的"焦点"方式相关的所有方式的列表(我的数据框中 2,061 个段中的 1 个):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | get_related_ways <- function(wayid, bboxstring){ related_ways_query <- paste0("[bbox:", bboxstring,"];\ ", "way(id:", wayid,");\ ", "rel(bw);\ ", # get all sibling "way(r);\ ", "out;") related_ways <- osmdata_sf(related_ways_query) related_ways <- related_ways$osm_lines related_ways <- related_ways$osm_id return(related_ways) } |
然后我创建了一个函数来仅提取我的焦点路径的节点 ID。
1 2 3 4 5 6 7 | 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。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | get_intersecting_ways <- function(nodeid, bboxstring){ node_to_intways_query <- paste0("[bbox:", bboxstring,"];\ ", "node(id:", nodeid,");\ ", "way(bn)[highway];\ ", "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 存在,我只是觉得它们很直观!)。我还尝试通过使用 foreach 和 doParallel 并行化这个方法,并将我的数据集分成 100 个块,每个块有 26 种方式。它仍然很慢。 Overpass API 可能很慢?更有可能我在设置时做错了,这只是我第三次或第四次使用 doParallel。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 | 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) |
这个过程的一般逻辑是:
运行所有 2061 段大约需要 2-3 小时。那是很长的时间;但是,即使是 Overpass Turbo 中的直接查询也很慢,所以...也许这是正确的。