Distances on Directed Graphs in R
at main 187 lines 7.1 kB view raw
1#' Calculate lists of pair-wise shortest paths between points. 2#' 3#' @param graph `data.frame` or equivalent object representing the network 4#' graph (see Details) 5#' @param from Vector or matrix of points **from** which route paths are to 6#' be calculated (see Details) 7#' @param to Vector or matrix of points **to** which route paths are to be 8#' calculated (see Details) 9#' @param vertices If `TRUE`, return lists of lists of vertices for each 10#' path, otherwise return corresponding lists of edge numbers from `graph`. 11#' @param pairwise If `TRUE`, calculate paths only between the ordered 12#' pairs of `from` and `to`. In this case, each of these must be the 13#' same length, and the output will contain paths the i-th members of each, and 14#' thus also be of that length. 15#' @param heap Type of heap to use in priority queue. Options include 16#' Fibonacci Heap (default; `FHeap`), Binary Heap (`BHeap`), 17#' `Radix`, Trinomial Heap (`TriHeap`), Extended Trinomial Heap 18#' (`TriHeapExt`, and 2-3 Heap (`Heap23`). 19#' @param quiet If `FALSE`, display progress messages on screen. 20#' @return List of list of paths tracing all connections between nodes such that 21#' if `x <- dodgr_paths (graph, from, to)`, then the path between 22#' `from[i]` and `to[j]` is `x [[i]] [[j]]`. Each individual path is then a 23#' vector of integers indexing into the rows of `graph` if `vertices = FALSE`, 24#' or into the rows of `dodgr_vertices (graph)` if `vertices = TRUE`. 25#' 26#' @note `graph` must minimally contain four columns of `from`, 27#' `to`, `dist`. If an additional column named `weight` or 28#' `wt` is present, shortest paths are calculated according to values 29#' specified in that column; otherwise according to `dist` values. Either 30#' way, final distances between `from` and `to` points are calculated 31#' according to values of `dist`. That is, paths between any pair of points 32#' will be calculated according to the minimal total sum of `weight` 33#' values (if present), while reported distances will be total sums of 34#' `dist` values. 35#' 36#' The `from` and `to` columns of `graph` may be either single 37#' columns of numeric or character values specifying the numbers or names of 38#' graph vertices, or combinations to two columns specifying geographical 39#' (longitude and latitude) coordinates. In the latter case, almost any sensible 40#' combination of names will be accepted (for example, `fromx, fromy`, 41#' `from_x, from_y`, or `fr_lat, fr_lon`.) 42#' 43#' `from` and `to` values can be either two-column matrices of 44#' equivalent of longitude and latitude coordinates, or else single columns 45#' precisely matching node numbers or names given in `graph$from` or 46#' `graph$to`. If `to` is missing, pairwise distances are calculated 47#' between all points specified in `from`. If neither `from` nor 48#' `to` are specified, pairwise distances are calculated between all nodes 49#' in `graph`. 50#' 51#' @family distances 52#' @export 53#' @examples 54#' graph <- weight_streetnet (hampi) 55#' from <- sample (graph$from_id, size = 100) 56#' to <- sample (graph$to_id, size = 50) 57#' dp <- dodgr_paths (graph, from = from, to = to) 58#' # dp is a list with 100 items, and each of those 100 items has 30 items, each 59#' # of which is a single path listing all vertiex IDs as taken from `graph`. 60#' 61#' # it is also possible to calculate paths between pairwise start and end 62#' # points 63#' from <- sample (graph$from_id, size = 5) 64#' to <- sample (graph$to_id, size = 5) 65#' dp <- dodgr_paths (graph, from = from, to = to, pairwise = TRUE) 66#' # dp is a list of 5 items, each of which just has a single path between each 67#' # pairwise from and to point. 68dodgr_paths <- function (graph, 69 from, 70 to, 71 vertices = TRUE, 72 pairwise = FALSE, 73 heap = "BHeap", 74 quiet = TRUE) { 75 76 hps <- get_heap (heap, graph) 77 heap <- hps$heap 78 graph <- hps$graph 79 80 gr_cols <- dodgr_graph_cols (graph) 81 # cols are (edge_id, from, to, d, w, component, xfr, yfr, xto, yto) 82 vert_map <- make_vert_map (graph, gr_cols) 83 84 if (missing (from)) { 85 from <- vert_map$vert 86 } 87 from_index <- get_path_indices (graph, gr_cols, vert_map, from) 88 89 if (missing (to)) { 90 to <- vert_map$vert 91 } 92 to_index <- get_path_indices (graph, gr_cols, vert_map, to) 93 94 graph <- convert_graph (graph, gr_cols) 95 96 if (!quiet) { 97 message ("Calculating shortest paths ... ", appendLF = FALSE) 98 } 99 if (pairwise) { 100 if (length (from_index$index) != length (to_index$index)) { 101 stop ("pairwise paths require from and to to have same length") 102 } 103 paths <- rcpp_get_paths_pairwise ( 104 graph, 105 vert_map, 106 from_index$index, 107 to_index$index, 108 heap 109 ) 110 } else { 111 paths <- rcpp_get_paths ( 112 graph, 113 vert_map, 114 from_index$index, 115 to_index$index, 116 heap 117 ) 118 } 119 120 # convert 1-based indices back into vertex IDs. Note both paths that can not 121 # be traced and single-step paths are returned from the above as NULL. The 122 # former are retained as NULL, while the following converts the latter to 123 # appropriate start-end vertices. 124 paths <- lapply (paths, function (i) { 125 lapply (i, function (j) { 126 if (is.null (j)) { 127 return (j) 128 } # nocov 129 vert_map$vert [j] 130 }) 131 }) # nolint 132 133 134 # name path lists 135 if (!is.null (from_index$id) && !is.null (to_index$id)) { 136 if (!pairwise) { 137 for (i in seq_along (from_index$id)) { 138 names (paths [[i]]) <- paste0 ( 139 from_index$id [i], 140 "-", 141 to_index$id 142 ) 143 } 144 } 145 names (paths) <- from_index$id 146 } 147 148 if (!vertices) { 149 graph_verts <- paste0 ("f", graph$from, "t", graph$to) 150 151 # convert vertex IDs to corresponding sequences of edge numbers 152 paths <- lapply (paths, function (i) { 153 lapply (i, function (j) { 154 if (length (j) > 1) { 155 indx <- 2:length (j) 156 pij <- paste0 ( 157 "f", j [indx - 1], 158 "t", j [indx] 159 ) 160 res <- match (pij, graph_verts) 161 res <- res [which (!is.na (res))] 162 return (if (length (res) == 0) { 163 NULL 164 } else { 165 res 166 }) 167 } 168 }) 169 }) # nolint 170 } 171 172 return (paths) 173} 174 175get_path_indices <- function (graph, gr_cols, vert_map, to_from) { 176 177 index_id <- get_index_id_cols (graph, gr_cols, vert_map, to_from) 178 179 index <- index_id$index - 1 # 0-based 180 if (!is.null (index_id$id)) { 181 id <- index_id$id 182 } else { 183 id <- vert_map$vert # nocov 184 } 185 186 return (list (index = index, id = id)) 187}