Skip to content

Commit

Permalink
Merge pull request #206 from ATFutures/dev
Browse files Browse the repository at this point in the history
dev for #201 #203
  • Loading branch information
mpadge authored Apr 26, 2023
2 parents 8360ecc + cdc4273 commit d741c92
Show file tree
Hide file tree
Showing 27 changed files with 857 additions and 47 deletions.
2 changes: 1 addition & 1 deletion DESCRIPTION
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Package: dodgr
Title: Distances on Directed Graphs
Version: 0.2.20
Version: 0.2.20.001
Authors@R: c(
person("Mark", "Padgham", , "mark.padgham@email.com", role = c("aut", "cre")),
person("Andreas", "Petutschnig", role = "aut"),
Expand Down
1 change: 1 addition & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export(dodgr_deduplicate_graph)
export(dodgr_distances)
export(dodgr_dists)
export(dodgr_dists_categorical)
export(dodgr_dists_nearest)
export(dodgr_flowmap)
export(dodgr_flows_aggregate)
export(dodgr_flows_disperse)
Expand Down
22 changes: 21 additions & 1 deletion R/RcppExports.R
Original file line number Diff line number Diff line change
Expand Up @@ -320,7 +320,14 @@ rcpp_get_sp_dists_par <- function(graph, vert_map_in, fromi, toi_in, heap_type,
.Call(`_dodgr_rcpp_get_sp_dists_par`, graph, vert_map_in, fromi, toi_in, heap_type, is_spatial)
}

#' rcpp_get_sp_dists_par
#' rcpp_get_sp_dists_nearest
#'
#' @noRd
rcpp_get_sp_dists_nearest <- function(graph, vert_map_in, fromi, toi_in, heap_type) {
.Call(`_dodgr_rcpp_get_sp_dists_nearest`, graph, vert_map_in, fromi, toi_in, heap_type)
}

#' rcpp_get_sp_dists_paired_par
#'
#' @noRd
rcpp_get_sp_dists_paired_par <- function(graph, vert_map_in, fromi, toi, heap_type, is_spatial) {
Expand Down Expand Up @@ -380,6 +387,19 @@ rcpp_get_sp_dists_categorical <- function(graph, vert_map_in, fromi, toi_in, hea
.Call(`_dodgr_rcpp_get_sp_dists_categorical`, graph, vert_map_in, fromi, toi_in, heap_type, proportions_only)
}

#' rcpp_get_sp_dists_categ_paired
#'
#' Pairwise version of 'get_sp_dists_categorical'. The `graph` must have an
#'`edge_type` column of non-negative integers, with 0 denoting edges which are
#' not aggregated, and all other values defining aggregation categories.
#'
#' Implemented in parallal form only; no single-threaded version, and
#' only for AStar (so graphs must be spatial).
#' @noRd
rcpp_get_sp_dists_categ_paired <- function(graph, vert_map_in, fromi, toi_in, heap_type) {
.Call(`_dodgr_rcpp_get_sp_dists_categ_paired`, graph, vert_map_in, fromi, toi_in, heap_type)
}

#' rcpp_get_sp_dists_cat_threshold
#'
#' The `graph` must have an `edge_type` column of non-negative integers,
Expand Down
72 changes: 50 additions & 22 deletions R/dists-categorical.R
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@
#' distances and for each edge category; if `TRUE`, return single vector of
#' proportional distances, like the `summary` function applied to full
#' results. See Note.
#' @param pairwise If `TRUE`, calculate distances only between the ordered
#' pairs of `from` and `to`. In this case, neither the `proportions_only` nor
#' `dlimit` parameters have any effect, and the result is a single matrix with
#' one row for each pair of `from`-`to` points, and one column for each
#' category.
#' @param dlimit If no value to `to` is given, distances are aggregated from
#' each `from` point out to the specified distance limit (in the same units as
#' the edge distances of the input graph). `dlimit` only has any effect if `to`
Expand Down Expand Up @@ -54,14 +59,21 @@
#' sapply (d, dim)
#' # 9 distance matrices, all of same dimensions, first of which is standard
#' # distance matrix
#' # s <- summary (d) # print summary as proportions along each "edge_type"
#' s <- summary (d) # print summary as proportions along each "edge_type"
#' # or directly calculate proportions only
#' dodgr_dists_categorical (graph, from, to,
#' proportions_only = TRUE
#' )
#'
#' # Pairwise distances return single matrix with number of rows equal to 'from'
#' # / 'to', and number of columns equal to number of edge types plus one for
#' # total distances.
#' d <- dodgr_dists_categorical (graph, from, to, pairwise = TRUE)
#' class (d)
#' dim (d)
#'
#' # The 'dlimit' parameter can be used to calculate total distances along each
#' # category of edges from a set of points:
#' # category of edges from a set of points out to specified threshold:
#' dlimit <- 2000 # in metres
#' d <- dodgr_dists_categorical (graph, from, dlimit = dlimit)
#' dim (d) # length(from), length(unique(edge_type)) + 1
Expand All @@ -71,6 +83,7 @@ dodgr_dists_categorical <- function (graph,
from = NULL,
to = NULL,
proportions_only = FALSE,
pairwise = FALSE,
dlimit = NULL,
heap = "BHeap",
quiet = TRUE) {
Expand Down Expand Up @@ -144,33 +157,48 @@ dodgr_dists_categorical <- function (graph,

if (!is.null (to)) {

d <- rcpp_get_sp_dists_categorical (
graph,
vert_map,
from_index$index,
to_index$index,
heap,
proportions_only
)
if (pairwise) {

n <- length (to)
res <- rcpp_get_sp_dists_categ_paired (
graph,
vert_map,
from_index$index,
to_index$index,
heap
)
colnames (res) <- c ("total", names (edge_type_table))
rownames (res) <- paste0 (from_index$id, "-", to_index$id)

if (!proportions_only) {
} else {

res <- process_categorical_dmat (
d,
from_index,
to_index,
d <- rcpp_get_sp_dists_categorical (
graph,
vert_map,
edge_type_table
from_index$index,
to_index$index,
heap,
proportions_only
)

} else {
n <- length (to)

if (!proportions_only) {

res <- process_categorical_dmat (
d,
from_index,
to_index,
vert_map,
edge_type_table
)

} else {

res <- apply (d, 2, sum)
res [2:length (res)] <- res [2:length (res)] / res [1]
res <- res [-1]
names (res) <- names (edge_type_table)
res <- apply (d, 2, sum)
res [2:length (res)] <- res [2:length (res)] / res [1]
res <- res [-1]
names (res) <- names (edge_type_table)
}
}
} else {

Expand Down
180 changes: 180 additions & 0 deletions R/dists-nearest.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
#' Calculate vector of shortest distances from a series of 'from' points to
#' nearest one of series of 'to' points.
#'
#' @param graph `data.frame` or equivalent object representing the network
#' graph (see Notes)
#' @param from Vector or matrix of points **from** which route distances are to
#' be calculated (see Notes)
#' @param to Vector or matrix of points **to** which shortest route distances
#' are to be calculated to nearest 'to' point only.
#' @param shortest If `FALSE`, calculate distances along the \emph{fastest}
#' rather than shortest routes (see Notes).
#' @param heap Type of heap to use in priority queue. Options include
#' Fibonacci Heap (default; `FHeap`), Binary Heap (`BHeap`),
#' `Trinomial Heap (`TriHeap`), Extended Trinomial Heap
#' (`TriHeapExt`, and 2-3 Heap (`Heap23`).
#' @param parallel If `TRUE`, perform routing calculation in parallel (see
#' details)
#' @param quiet If `FALSE`, display progress messages on screen.
#' @return Vector of distances, one element for each 'from' point giving the
#' distance to the nearest 'to' point.
#'
#' @note `graph` must minimally contain three columns of `from`,
#' `to`, `dist`. If an additional column named `weight` or
#' `wt` is present, shortest paths are calculated according to values
#' specified in that column; otherwise according to `dist` values. Either
#' way, final distances between `from` and `to` points are calculated
#' by default according to values of `dist`. That is, paths between any pair of
#' points will be calculated according to the minimal total sum of `weight`
#' values (if present), while reported distances will be total sums of `dist`
#' values.
#'
#' For street networks produced with \link{weight_streetnet}, distances may also
#' be calculated along the \emph{fastest} routes with the `shortest = FALSE`
#' option. Graphs must in this case have columns of `time` and `time_weighted`.
#' Note that the fastest routes will only be approximate when derived from
#' \pkg{sf}-format data generated with the \pkg{osmdata} function
#' `osmdata_sf()`, and will be much more accurate when derived from `sc`-format
#' data generated with `osmdata_sc()`. See \link{weight_streetnet} for details.
#'
#' The `from` and `to` columns of `graph` may be either single
#' columns of numeric or character values specifying the numbers or names of
#' graph vertices, or combinations to two columns specifying geographical
#' (longitude and latitude) coordinates. In the latter case, almost any sensible
#' combination of names will be accepted (for example, `fromx, fromy`,
#' `from_x, from_y`, or `fr_lat, fr_lon`.)
#'
#' `from` and `to` values can be either two-column matrices or
#' equivalent of longitude and latitude coordinates, or else single columns
#' precisely matching node numbers or names given in `graph$from` or
#' `graph$to`. If `to` is `NULL`, pairwise distances are calculated from all
#' `from` points to all other nodes in `graph`. If both `from` and `to` are
#' `NULL`, pairwise distances are calculated between all nodes in `graph`.
#'
#' Calculations in parallel (`parallel = TRUE`) ought very generally be
#' advantageous. For small graphs, calculating distances in parallel is likely
#' to offer relatively little gain in speed, but increases from parallel
#' computation will generally markedly increase with increasing graph sizes.
#' By default, parallel computation uses the maximal number of available cores
#' or threads. This number can be reduced by specifying a value via
#' `RcppParallel::setThreadOptions (numThreads = <desired_number>)`. Parallel
#' calculations are, however, not able to be interrupted (for example, by
#' `Ctrl-C`), and can only be stopped by killing the R process.
#'
#' @family distances
#' @export
#' @examples
#' # A simple graph
#' graph <- data.frame (
#' from = c ("A", "B", "B", "B", "C", "C", "D", "D"),
#' to = c ("B", "A", "C", "D", "B", "D", "C", "A"),
#' d = c (1, 2, 1, 3, 2, 1, 2, 1)
#' )
#' dodgr_dists (graph)
#'
#' # A larger example from the included [hampi()] data.
#' graph <- weight_streetnet (hampi)
#' from <- sample (graph$from_id, size = 100)
#' to <- sample (graph$to_id, size = 50)
#' d <- dodgr_dists (graph, from = from, to = to)
#' # d is a 100-by-50 matrix of distances between `from` and `to`
#'
#' \dontrun{
#' # a more complex street network example, thanks to @chrijo; see
#' # https://github.com/ATFutures/dodgr/issues/47
#'
#' xy <- rbind (
#' c (7.005994, 51.45774), # limbeckerplatz 1 essen germany
#' c (7.012874, 51.45041)
#' ) # hauptbahnhof essen germany
#' xy <- data.frame (lon = xy [, 1], lat = xy [, 2])
#' essen <- dodgr_streetnet (pts = xy, expand = 0.2, quiet = FALSE)
#' graph <- weight_streetnet (essen, wt_profile = "foot")
#' d <- dodgr_dists (graph, from = xy, to = xy)
#' # First reason why this does not work is because the graph has multiple,
#' # disconnected components.
#' table (graph$component)
#' # reduce to largest connected component, which is always number 1
#' graph <- graph [which (graph$component == 1), ]
#' d <- dodgr_dists (graph, from = xy, to = xy)
#' # should work, but even then note that
#' table (essen$level)
#' # There are parts of the network on different building levels (because of
#' # shopping malls and the like). These may or may not be connected, so it may
#' # be necessary to filter out particular levels
#' index <- which (!(essen$level == "-1" | essen$level == "1")) # for example
#' library (sf) # needed for following sub-select operation
#' essen <- essen [index, ]
#' graph <- weight_streetnet (essen, wt_profile = "foot")
#' graph <- graph [which (graph$component == 1), ]
#' d <- dodgr_dists (graph, from = xy, to = xy)
#' }
dodgr_dists_nearest <- function (graph,
from = NULL,
to = NULL,
shortest = TRUE,
heap = "BHeap",
parallel = TRUE,
quiet = TRUE) {

graph <- tbl_to_df (graph)

hps <- get_heap (heap, graph)
heap <- hps$heap
graph <- hps$graph

graph <- preprocess_spatial_cols (graph)
gr_cols <- dodgr_graph_cols (graph)
is_spatial <- is_graph_spatial (graph)
to_from_indices <- to_from_index_with_tp (graph, from, to)
if (to_from_indices$compound) {
graph <- to_from_indices$graph_compound
}

if (!shortest) {
if (is.na (gr_cols$time_weighted)) {
stop (
"Graph does not contain a weighted time column from ",
"which to calculate fastest paths."
)
}
graph [[gr_cols$d_weighted]] <- graph [[gr_cols$time_weighted]]
}

graph <- convert_graph (graph, gr_cols)

if (!quiet) {
message ("Calculating shortest paths ... ", appendLF = FALSE)
}

if (parallel && heap == "TriHeapExt") {
if (!quiet) {
message (
"Extended TriHeaps can not be calculated in parallel; ",
"reverting to serial calculation"
)
}
parallel <- FALSE
}

d <- rcpp_get_sp_dists_nearest (
graph,
to_from_indices$vert_map,
to_from_indices$from$index,
to_from_indices$to$index,
heap
)
index <- seq_along (to_from_indices$from$index)
nearest_index <- as.integer (d [index + length (index)])
d <- d [index]
nearest_index <- match (nearest_index, to_from_indices$to$index)
nearest_ids <- to_from_indices$to$id [nearest_index]
nearest_ids <- gsub ("\\_(start|end)$", "", nearest_ids)

return (data.frame (
from = to_from_indices$from$id,
to = nearest_ids,
d = d,
stringsAsFactors = FALSE
))
}
6 changes: 5 additions & 1 deletion codemeta.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"codeRepository": "https://github.com/ATFutures/dodgr",
"issueTracker": "https://github.com/ATFutures/dodgr/issues",
"license": "https://spdx.org/licenses/GPL-3.0",
"version": "0.2.20",
"version": "0.2.20.1",
"programmingLanguage": {
"@type": "ComputerLanguage",
"name": "R",
Expand Down Expand Up @@ -340,7 +340,11 @@
},
"SystemRequirements": "C++11, GNU make"
},
<<<<<<< HEAD
"fileSize": "28156.314KB",
=======
"fileSize": "28124.324KB",
>>>>>>> 0b4bd47e1729f244d53494eaa752de8d15ccee65
"citation": [
{
"@type": "ScholarlyArticle",
Expand Down
1 change: 1 addition & 0 deletions man/dodgr_distances.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions man/dodgr_dists.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit d741c92

Please sign in to comment.