From 422790fd08b15bb2a4cc81b63f8b003746b90b6c Mon Sep 17 00:00:00 2001 From: mpadge Date: Mon, 9 Sep 2019 11:55:27 +0200 Subject: [PATCH] add isoverts fn --- NAMESPACE | 1 + R/iso.R | 84 +++++++++++++++++++++++++++++++++++++-- man/match_pts_to_graph.Rd | 2 +- src/run_sp.cpp | 24 +++++++++-- tests/testthat/test-iso.R | 4 +- 5 files changed, 105 insertions(+), 10 deletions(-) diff --git a/NAMESPACE b/NAMESPACE index 53e66c8b8..c55557f69 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -18,6 +18,7 @@ export(dodgr_full_cycles) export(dodgr_fundamental_cycles) export(dodgr_isochrones) export(dodgr_isodists) +export(dodgr_isoverts) export(dodgr_paths) export(dodgr_sample) export(dodgr_sflines_to_poly) diff --git a/R/iso.R b/R/iso.R index 3ff71058c..3aa7f7ee3 100644 --- a/R/iso.R +++ b/R/iso.R @@ -35,9 +35,7 @@ dodgr_isodists <- function (graph, from = NULL, dlim = NULL, heap = 'BHeap') dat <- iso_pre (graph, from, heap) d <- rcpp_get_iso (dat$graph, dat$vert_map, dat$from_index$index, - dlim, dat$heap) - # verts inside the isohull are flagged with -1, and are removed here: - d [d < 0] <- NA + sort (dlim), dat$heap) if (!is.null (dat$from_index$id)) rownames (d) <- dat$from_index$id @@ -45,6 +43,12 @@ dodgr_isodists <- function (graph, from = NULL, dlim = NULL, heap = 'BHeap') rownames (d) <- dat$vert_map$vert colnames (d) <- dat$vert_map$vert + # verts ON isohulls are flagged with negative values at isodistance; + # terminal verts are also negative at their specified distance; all other + # points inside any hulls are positive. The latter are removed here: + d [d >= 0] <- NA + d <- -d # convert negative-flagged iso-values to positive + return (dmat_to_pts (d, dat$from_index$id, dat$v, dlim)) } @@ -132,6 +136,76 @@ dodgr_isochrones <- function (graph, from = NULL, tlim = NULL, heap = 'BHeap') return (res) } +#' dodgr_isoverts +#' +#' Calculate isodistance or isochrone contours from specified points, and return +#' lists of all network vertices contained within the contours. Function is +#' fully vectorized to calculate accept vectors of central points and vectors +#' defining multiple isochrone thresholds. Provide one or more `dlim` values for +#' isodistances, or one or more `tlim` values for isochrones. +#' +#' @inheritParams dodgr_isodists +#' +#' @param from Vector or matrix of points **from** which isodistances or +#' isochrones are to be calculated. +#' @param tlim Vector of desired limits of isochrones in seconds +#' @return A single `data.frame` of vertex IDs, with columns denoting the `from` +#' points and `tlim` value(s). The isochrones are given as `id` values and +#' associated coordinates of the series of points from each `from` point at the +#' specified isochrone times. +#' +#' @export +#' @examples +#' \dontrun{ +#' # Use osmdata package to extract 'SC'-format data: +#' library (osmdata) +#' dat <- opq ("hampi india") %>% +#' add_osm_feature (key = "highway") %>% +#' osmdata_sc () +#' graph <- weight_streetnet (dat) +#' from <- sample (graph$from_id, size = 100) +#' tlim <- c (5, 10, 20, 30, 60) * 60 # times in seconds +#' x <- dodgr_isoverts (graph, from = from, tlim) +#' } +dodgr_isoverts <- function (graph, from = NULL, dlim = NULL, tlim = NULL, + heap = 'BHeap') +{ + if (!methods::is (graph, "dodgr_streetnet_sc")) + stop ("isochrones can only be calculated from SC-class networks ", + "generated by osmdata_sc.") + if (!is.null (dlim) & !is.null (tlim)) + stop ("Only one of dlim or tlim can be provided") + + if (!is.null (tlim)) + { + graph$d_weighted <- graph$time_weighted + graph$d <- graph$time + dlim <- tlim + } + + dat <- iso_pre (graph, from, heap) + + d <- rcpp_get_iso (dat$graph, dat$vert_map, dat$from_index$index, + sort (dlim), dat$heap) + index <- which (!is.na (d)) + + if (!is.null (dat$from_index$id)) + rownames (d) <- dat$from_index$id + else + rownames (d) <- dat$vert_map$vert + colnames (d) <- dat$vert_map$vert + + # convert d-values to the *next highest* specified dlim value. breaks need + # to start < 0 to include 0 in lowest class + breaks <- c (-0.001, dlim) + na_index <- which (!is.na (d)) + f <- cut (d [na_index], breaks = breaks) + index <- match (f, attr (f, "levels")) + d [na_index] <- breaks [-1] [index] + + return (dmat_to_pts (d, dat$from_index$id, dat$v, dlim)) +} + # convert distance matrix with values equal to various isodistances into list of # lists of points ordered around the central points dmat_to_pts <- function (d, from, v, dlim) @@ -143,6 +217,10 @@ dmat_to_pts <- function (d, from, v, dlim) o <- v [match (from [i], v$id), ] pts [[i]] <- lapply (dlim, function (j) { res <- pt_names [which (d [i, ] == j)] + # Then any additional terminal vertices + index <- which (!d [i, ] %in% dlim & + d [i, ] < j) + res <- c (res, pt_names [index]) res <- v [match (res, v$id), ] if (nrow (res) > 0) { diff --git a/man/match_pts_to_graph.Rd b/man/match_pts_to_graph.Rd index a1852b56d..8f2071e72 100644 --- a/man/match_pts_to_graph.Rd +++ b/man/match_pts_to_graph.Rd @@ -15,7 +15,7 @@ matrix or \pkg{sf}-formatted \code{data.frame}.} \item{connected}{Should points be matched to the same (largest) connected component of graph? If \code{FALSE} and these points are to be used for a -\code{dodgr} routine routine (\link{dodgr_dists}, \link{dodgr_paths}, or +\code{dodgr} routing routine (\link{dodgr_dists}, \link{dodgr_paths}, or \link{dodgr_flows_aggregate}), then results may not be returned if points are not part of the same connected component. On the other hand, forcing them to be part of the same connected component may decrease the spatial accuracy of diff --git a/src/run_sp.cpp b/src/run_sp.cpp index c8ca541cc..c2e4065df 100644 --- a/src/run_sp.cpp +++ b/src/run_sp.cpp @@ -165,16 +165,32 @@ struct OneIso : public RcppParallel::Worker pathfinder->DijkstraLimit (d, w, prev, from_i, dlimit_max); + // Get the set of terminal vertices. + std::set terminal_verts; + for (auto j: prev) + if (w [j] > INFINITE_INT && w [j] < dlimit_max) + { + if (prev [j] < INFINITE_INT) + { + terminal_verts.erase (j); + terminal_verts.emplace (prev [j]); + } + } + + for (size_t j = 0; j < nverts; j++) { - if (prev [j] < INFINITE_INT && w [j] < INFINITE_DOUBLE) + if (terminal_verts.find (j) != terminal_verts.end ()) + { + dout (i, j) = -w [j]; // Flag terminal verts with -d + } else if (prev [j] < INFINITE_INT && w [j] < dlimit_max) { for (auto k: dlimit) { if (w [j] > k && w [prev [j]] < k) - dout (i, prev [j]) = k; - else if (w [j] < k) - dout (i, j) = -1.0; // flag verts within isohull + dout (i, prev [j]) = -k; // flag isohull verts with -k + else + dout (i, j) = w [j]; // distance of other internal verts } } } diff --git a/tests/testthat/test-iso.R b/tests/testthat/test-iso.R index 47bd79da2..5880ae7ca 100644 --- a/tests/testthat/test-iso.R +++ b/tests/testthat/test-iso.R @@ -23,7 +23,7 @@ test_that("isodists", { # some points may give no iso countours, so the following 2 may # not always be equal: expect_true ((npts - length (unique (d$from))) >= 0) - expect_true (length (unique (d$dlim)) == length (dlim)) + expect_true (length (unique (d$dlim)) <= length (dlim)) }) test_that ("errors", { @@ -57,5 +57,5 @@ test_that("isochrones", { # some points may give no iso countours, so the following 2 may # not always be equal: expect_true ((npts - length (unique (x$from))) >= 0) - expect_true (length (unique (x$tlim)) == length (tlim)) + expect_true (length (unique (x$tlim)) <= length (tlim)) })