├── .github ├── .gitignore ├── dependabot.yml └── workflows │ ├── pkgdown.yaml │ ├── R-CMD-check.yaml │ └── test-coverage.yaml ├── data ├── hampi.rda ├── os_roads_bristol.rda └── weighting_profiles.rda ├── tests ├── testthat.R ├── valgrind-script.R ├── testthat │ ├── test-deduplicate.R │ ├── test-geodist-measure.R │ ├── test-dists-nearest.R │ ├── test-save-load.R │ ├── test-graph-conversion.R │ ├── test-paths.R │ ├── test-cycles.R │ ├── helper-sc-conversion-fns.R │ └── test-centrality.R └── valgrind-test.R ├── vignettes ├── fig1.png ├── fig2.png ├── fig3.png ├── hampi.png ├── times-fig1.png ├── hampi-flowmap.png ├── hampi-flowmap2.png ├── hampi-flowmap3.png ├── times-fig1.tex ├── parallel.Rmd ├── fig1.tex ├── makefile ├── fig2.tex └── fig3.tex ├── R ├── zzz.R ├── times.R ├── flowmap.R ├── graph-merge.R ├── weighting_profiles.R ├── compare-heaps.R ├── deduplicate.R ├── from-to-params.R ├── utils.R └── graph-functions-spatial.R ├── src ├── sc-as-network.h ├── heaps │ ├── heap_lib.h │ ├── heap.h │ ├── bheap.h │ ├── fheap.h │ └── heap23.h ├── Makevars ├── match-points.h ├── Makevars.win ├── sf-as-network.h ├── sc-as-network.cpp ├── deduplicate.h ├── concaveman.cpp ├── fund-cycles.cpp ├── flows.h ├── dodgr-to-sf.h ├── turn_penalty.h ├── dgraph.h └── deduplicate.cpp ├── .hooks ├── no-commit-to-main └── description ├── man ├── pipe.Rd ├── dodgr_cache_on.Rd ├── dodgr_to_tidygraph.Rd ├── dodgr_components.Rd ├── dodgr_cache_off.Rd ├── igraph_to_dodgr.Rd ├── hampi.Rd ├── dodgr_to_igraph.Rd ├── dodgr_load_streetnet.Rd ├── weighting_profiles.Rd ├── dodgr_to_sf.Rd ├── clear_dodgr_cache.Rd ├── write_dodgr_wt_profile.Rd ├── dodgr_vertices.Rd ├── dodgr_deduplicate_graph.Rd ├── dodgr_streetnet_geodesic.Rd ├── dodgr_sample.Rd ├── dodgr_contract_graph.Rd ├── summary.dodgr_dists_categorical.Rd ├── dodgr_sflines_to_poly.Rd ├── compare_heaps.Rd ├── estimate_centrality_threshold.Rd ├── match_points_to_verts.Rd ├── match_pts_to_verts.Rd ├── dodgr_save_streetnet.Rd ├── dodgr_flowmap.Rd ├── dodgr_to_sfc.Rd ├── dodgr_uncontract_graph.Rd ├── dodgr_insert_vertex.Rd ├── weight_railway.Rd ├── dodgr_full_cycles.Rd ├── os_roads_bristol.Rd ├── merge_directed_graph.Rd ├── add_nodes_to_graph.Rd ├── estimate_centrality_time.Rd ├── dodgr_fundamental_cycles.Rd ├── dodgr.Rd ├── match_points_to_graph.Rd ├── match_pts_to_graph.Rd ├── dodgr_streetnet.Rd ├── dodgr_streetnet_sc.Rd ├── dodgr_isodists.Rd ├── dodgr_isoverts.Rd ├── dodgr_isochrones.Rd └── dodgr_paths.Rd ├── dodgr.Rproj ├── .gitignore ├── .Rbuildignore ├── inst └── CITATION ├── cran-comments.md ├── _pkgdown.yml ├── CONDUCT.md ├── makefile ├── CONTRIBUTING.md ├── NAMESPACE ├── DESCRIPTION └── .pre-commit-config.yaml /.github/.gitignore: -------------------------------------------------------------------------------- 1 | *.html 2 | -------------------------------------------------------------------------------- /data/hampi.rda: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UrbanAnalyst/dodgr/HEAD/data/hampi.rda -------------------------------------------------------------------------------- /tests/testthat.R: -------------------------------------------------------------------------------- 1 | library(testthat) 2 | library(dodgr) 3 | 4 | test_check("dodgr") 5 | -------------------------------------------------------------------------------- /vignettes/fig1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UrbanAnalyst/dodgr/HEAD/vignettes/fig1.png -------------------------------------------------------------------------------- /vignettes/fig2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UrbanAnalyst/dodgr/HEAD/vignettes/fig2.png -------------------------------------------------------------------------------- /vignettes/fig3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UrbanAnalyst/dodgr/HEAD/vignettes/fig3.png -------------------------------------------------------------------------------- /vignettes/hampi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UrbanAnalyst/dodgr/HEAD/vignettes/hampi.png -------------------------------------------------------------------------------- /vignettes/times-fig1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UrbanAnalyst/dodgr/HEAD/vignettes/times-fig1.png -------------------------------------------------------------------------------- /data/os_roads_bristol.rda: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UrbanAnalyst/dodgr/HEAD/data/os_roads_bristol.rda -------------------------------------------------------------------------------- /data/weighting_profiles.rda: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UrbanAnalyst/dodgr/HEAD/data/weighting_profiles.rda -------------------------------------------------------------------------------- /vignettes/hampi-flowmap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UrbanAnalyst/dodgr/HEAD/vignettes/hampi-flowmap.png -------------------------------------------------------------------------------- /vignettes/hampi-flowmap2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UrbanAnalyst/dodgr/HEAD/vignettes/hampi-flowmap2.png -------------------------------------------------------------------------------- /vignettes/hampi-flowmap3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UrbanAnalyst/dodgr/HEAD/vignettes/hampi-flowmap3.png -------------------------------------------------------------------------------- /tests/valgrind-script.R: -------------------------------------------------------------------------------- 1 | library (dodgr) 2 | graph <- weight_streetnet (hampi) 3 | graph <- dodgr_sample (graph, nverts = 100) 4 | d <- dodgr_dists (graph) 5 | -------------------------------------------------------------------------------- /R/zzz.R: -------------------------------------------------------------------------------- 1 | # nocov start 2 | .onLoad <- function (libname, pkgname) { # nolint 3 | 4 | Sys.setenv ("RCPP_PARALLEL_BACKEND" = "tinythread") 5 | invisible () 6 | } 7 | # nocov end 8 | -------------------------------------------------------------------------------- /src/sc-as-network.h: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | 4 | namespace sc { 5 | std::string random_id (size_t len); 6 | } 7 | 8 | Rcpp::CharacterVector rcpp_gen_hash (const int n, const size_t hash_len); 9 | -------------------------------------------------------------------------------- /.hooks/no-commit-to-main: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env Rscript 2 | 3 | # do not commit to main branch 4 | on_main <- identical (gert::git_branch (), "main") 5 | if (on_main) 6 | stop ("main branch is protected on GitHub; commits must be made via PR from other branch") 7 | -------------------------------------------------------------------------------- /man/pipe.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/dodgr-package.R 3 | \name{\%>\%} 4 | \alias{\%>\%} 5 | \title{Pipe operator} 6 | \usage{ 7 | lhs \%>\% rhs 8 | } 9 | \description{ 10 | Pipe operator 11 | } 12 | \keyword{internal} 13 | -------------------------------------------------------------------------------- /dodgr.Rproj: -------------------------------------------------------------------------------- 1 | Version: 1.0 2 | 3 | RestoreWorkspace: Default 4 | SaveWorkspace: Default 5 | AlwaysSaveHistory: Default 6 | 7 | EnableCodeIndexing: Yes 8 | UseSpacesForTab: Yes 9 | NumSpacesForTab: 2 10 | Encoding: UTF-8 11 | 12 | RnwWeave: Sweave 13 | LaTeX: pdfLaTeX 14 | 15 | BuildType: Package 16 | PackageUseDevtools: Yes 17 | PackageInstallArgs: --no-multiarch --with-keep.source 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .ccls-cache/ 2 | .Rproj.user 3 | .Rhistory 4 | .RData 5 | inst/doc 6 | inst/WORDLIST 7 | data-raw/ 8 | literature/ 9 | aaa\.Rmd* 10 | # History files 11 | .Rhistory 12 | .Rapp.history 13 | # Session Data files 14 | .RData 15 | # Output files from R CMD build 16 | /*.tar.gz 17 | # vim files 18 | .*.un~ 19 | .*.swp 20 | # compiled object files 21 | *.o 22 | *.so 23 | /doc/ 24 | /Meta/ 25 | -------------------------------------------------------------------------------- /src/heaps/heap_lib.h: -------------------------------------------------------------------------------- 1 | #ifndef HEAP_LIB_H 2 | #define HEAP_LIB_H 3 | /* Heap Library - Provides various heap implementations. 4 | * ---------------------------------------------------------------------------- 5 | * Author: Mark Padgham, adapted from code by Shane Saunders 6 | */ 7 | #include "bheap.h" 8 | #include "fheap.h" 9 | #include "heap23.h" 10 | #include "triheap.h" 11 | #include "triheap_ext.h" 12 | #endif 13 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "" # See documentation for possible values 9 | directory: "/" # Location of package manifests 10 | schedule: 11 | interval: "monthly" 12 | -------------------------------------------------------------------------------- /.Rbuildignore: -------------------------------------------------------------------------------- 1 | .ccls-cache/ 2 | ^.*\.Rproj$ 3 | ^CONDUCT\.md$ 4 | ^CONTRIBUTING\.md$ 5 | ^Meta$ 6 | ^README-.*\.png$ 7 | ^README\.Rmd$ 8 | ^README\.html$ 9 | ^\.Rproj\.user$ 10 | ^\.github$ 11 | ^\.hooks$ 12 | ^\.lintr$ 13 | ^\.pre-commit-config\.yaml$ 14 | ^\.travis\.yml$ 15 | ^_pkgdown\.yml$ 16 | ^aaa* 17 | ^appveyor\.yml$ 18 | ^codemeta\.json$ 19 | ^cran-comments.md$ 20 | ^doc$ 21 | ^inst/WORDLIST$ 22 | ^makefile$ 23 | ^tests/valgrind-script.R$ 24 | ^tests/valgrind-test.R$ 25 | ^vignettes/iso\.* 26 | ^vignettes/makefile$ 27 | data-raw/ 28 | docs/ 29 | literature/ 30 | -------------------------------------------------------------------------------- /.hooks/description: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env Rscript 2 | 3 | s <- gert::git_status() 4 | chk <- ("DESCRIPTION" %in% s$file && 5 | (s$status [s$file == "DESCRIPTION"] == "modified" | 6 | s$status [s$file == "DESCRIPTION"] == "new")) 7 | if (!chk) 8 | stop ("DESCRIPTION has not been updated") 9 | 10 | f <- file.path (rprojroot::find_root("DESCRIPTION"), "DESCRIPTION") 11 | x <- system2 ("git", args = c ("diff", "--cached", "-U0", f), stdout = TRUE) 12 | if (!any (grepl ("^\\+Version", x))) 13 | stop ("Version number in DESCRIPTION has not been incremented") 14 | -------------------------------------------------------------------------------- /inst/CITATION: -------------------------------------------------------------------------------- 1 | citHeader("To cite dodgr in publications use:") 2 | 3 | bibentry(bibtype = "Article", 4 | journal = "Transport Findings", 5 | doi = "10.32866/6945", 6 | publisher = "Network Design Lab", 7 | title = "dodgr: An R package for network flow aggregation", 8 | author = person("Mark Padgham"), 9 | year = "2019", 10 | month = "2", 11 | paste("Mark Padgham (2019)", 12 | "dodgr: An R package for network flow aggregation", 13 | "Transport Findings, 2(14).", 14 | "URL https://doi.org/10.32866/6945") 15 | ) 16 | -------------------------------------------------------------------------------- /src/Makevars: -------------------------------------------------------------------------------- 1 | PKG_CPPFLAGS=-I. 2 | 3 | PKG_LIBS += $(LAPACK_LIBS) $(BLAS_LIBS) $(FLIBS) 4 | PKG_LIBS += $(shell ${R_HOME}/bin/Rscript -e "RcppParallel::RcppParallelLibs()") 5 | 6 | OBJ_HEAPS = heaps/bheap.o heaps/fheap.o heaps/heap23.o \ 7 | heaps/triheap_ext.o heaps/triheap.o 8 | OBJ_SRC = centrality.o concaveman.o deduplicate.o dgraph.o pathfinders.o \ 9 | dodgr-to-sf.o flows.o fund-cycles.o graph-contract.o graph.o graph-sample.o \ 10 | match-points.o RcppExports.o run_sp.o run_sp_categorical.o \ 11 | sc-as-network.o sf-as-network.o turn_penalty.o 12 | OBJECTS = $(OBJ_HEAPS) $(OBJ_SRC) 13 | 14 | .PHONY: all clean 15 | 16 | all: $(SHLIB) $(clean) 17 | 18 | clean: $(SHLIB) 19 | rm -rf $(OBJECTS) 20 | -------------------------------------------------------------------------------- /src/match-points.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | // [[Rcpp::depends(RcppParallel)]] 6 | #include 7 | 8 | constexpr float INFINITE_FLOAT = std::numeric_limits::max (); 9 | constexpr double INFINITE_DOUBLE = std::numeric_limits::max (); 10 | constexpr int INFINITE_INT = std::numeric_limits::max (); 11 | 12 | int which_side_of_line (const double ax, const double ay, 13 | const double bx, const double by, const double x, const double y); 14 | 15 | Rcpp::IntegerVector rcpp_points_index (const Rcpp::DataFrame &xy, 16 | Rcpp::DataFrame &pts); 17 | Rcpp::NumericVector rcpp_points_to_edges_par (const Rcpp::DataFrame &graph, 18 | Rcpp::DataFrame &pts); 19 | -------------------------------------------------------------------------------- /tests/testthat/test-deduplicate.R: -------------------------------------------------------------------------------- 1 | test_all <- (identical (Sys.getenv ("MPADGE_LOCAL"), "true") || 2 | identical (Sys.getenv ("GITHUB_JOB"), "test-coverage")) 3 | 4 | # testthat::skip_if (!test_all) 5 | 6 | test_that ("deduplicate", { 7 | 8 | graph <- weight_streetnet (hampi) 9 | n0 <- nrow (graph) 10 | # Duplicate some edges: 11 | set.seed (1L) 12 | index <- sample (nrow (graph), size = 10) 13 | graph <- graph [c (seq (nrow (graph)), index), ] 14 | n1 <- nrow (graph) 15 | 16 | # expect_message ( 17 | # duplicated_edge_check (graph, proceed = TRUE), 18 | # "Graph has duplicated edges" 19 | # ) 20 | 21 | graph_dedup <- dodgr_deduplicate_graph (graph) 22 | expect_equal (nrow (graph_dedup), n0) 23 | }) 24 | -------------------------------------------------------------------------------- /src/Makevars.win: -------------------------------------------------------------------------------- 1 | PKG_CXXFLAGS += -DRCPP_PARALLEL_USE_TBB=1 2 | 3 | PKG_LIBS += $(LAPACK_LIBS) $(BLAS_LIBS) $(FLIBS) 4 | PKG_LIBS += $(shell "${R_HOME}/bin${R_ARCH_BIN}/Rscript.exe" \ 5 | -e "RcppParallel::RcppParallelLibs()") 6 | 7 | OBJ_HEAPS = heaps/bheap.o heaps/fheap.o heaps/heap23.o \ 8 | heaps/triheap_ext.o heaps/triheap.o 9 | OBJ_SRC = centrality.o concaveman.o deduplicate.o dgraph.o pathfinders.o \ 10 | dodgr-to-sf.o flows.o fund-cycles.o graph-contract.o graph.o graph-sample.o \ 11 | match-points.o RcppExports.o run_sp.o run_sp_categorical.o \ 12 | sc-as-network.o sf-as-network.o turn_penalty.o 13 | OBJECTS = $(OBJ_HEAPS) $(OBJ_SRC) 14 | 15 | .PHONY: all clean 16 | 17 | all: $(SHLIB) $(clean) 18 | 19 | clean: $(SHLIB) 20 | rm -rf $(OBJECTS) 21 | -------------------------------------------------------------------------------- /cran-comments.md: -------------------------------------------------------------------------------- 1 | # CRAN notes for dodgr_0.4.2 submission 2 | 3 | This submission fixes the test failures seen on current CRAN version. 4 | 5 | Beyond that, this submission generates the following NOTES on some systems: 6 | 7 | - Possibly invalid URLs, all of which are GitHub because of "Too Many Requests," and not because of the URLs themselves. 8 | * "GNU make is a SystemRequirements", which is unavoidable because of the need to remove compiled object files in src sub-directories. 9 | - Size Notes, which are also unavoidable as they are largely due to size of compiled "libs". 10 | 11 | Other than these, this submission generates no additional notes, and no warnings on: 12 | 13 | * Ubuntu 24.04: R-release, R-devel 14 | * win-builder (R-release, R-devel, R-oldrelease) 15 | - clang UBSAN on R-devel 16 | -------------------------------------------------------------------------------- /man/dodgr_cache_on.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/cache.R 3 | \name{dodgr_cache_on} 4 | \alias{dodgr_cache_on} 5 | \title{Turn on all dodgr caching in current session.} 6 | \usage{ 7 | dodgr_cache_on() 8 | } 9 | \value{ 10 | Nothing; the function invisibly returns \code{TRUE} if successful. 11 | } 12 | \description{ 13 | This will only have an effect after caching has been turned off with 14 | \link{dodgr_cache_off}. 15 | } 16 | \examples{ 17 | dodgr_cache_on () 18 | # Then call dodgr functions as usual: 19 | graph <- weight_streetnet (hampi, wt_profile = "foot") 20 | } 21 | \seealso{ 22 | Other cache: 23 | \code{\link{clear_dodgr_cache}()}, 24 | \code{\link{dodgr_cache_off}()}, 25 | \code{\link{dodgr_load_streetnet}()}, 26 | \code{\link{dodgr_save_streetnet}()} 27 | } 28 | \concept{cache} 29 | -------------------------------------------------------------------------------- /tests/testthat/test-geodist-measure.R: -------------------------------------------------------------------------------- 1 | test_all <- (identical (Sys.getenv ("MPADGE_LOCAL"), "true") || 2 | identical (Sys.getenv ("GITHUB_JOB"), "test-coverage")) 3 | 4 | test_that ("geodist measure", { 5 | 6 | # Reset any measures stored in options: 7 | options ("dodgr_dist_measure" = NULL) 8 | 9 | graph <- weight_streetnet (hampi) 10 | st0 <- system.time ( 11 | m <- get_geodist_measure (graph) 12 | ) 13 | st1 <- system.time ( # should use option, not calculate 14 | m <- get_geodist_measure (graph) 15 | ) 16 | expect_equal (m, "cheap") 17 | if (test_all) { 18 | expect_true (st1 [3] < st0 [3]) 19 | } 20 | 21 | op <- getOption ("dodgr_dist_measure") 22 | expect_true (length (op) > 0L) 23 | hash <- attr (graph, "hash") 24 | expect_true (hash %in% names (op)) 25 | }) 26 | -------------------------------------------------------------------------------- /man/dodgr_to_tidygraph.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/graph-conversion.R 3 | \name{dodgr_to_tidygraph} 4 | \alias{dodgr_to_tidygraph} 5 | \title{Convert a \code{dodgr} graph to an \pkg{tidygraph}.} 6 | \usage{ 7 | dodgr_to_tidygraph(graph) 8 | } 9 | \arguments{ 10 | \item{graph}{A \code{dodgr} graph} 11 | } 12 | \value{ 13 | The \code{tidygraph} equivalent of the input 14 | } 15 | \description{ 16 | Convert a \code{dodgr} graph to an \pkg{tidygraph}. 17 | } 18 | \examples{ 19 | graph <- weight_streetnet (hampi) 20 | grapht <- dodgr_to_tidygraph (graph) 21 | } 22 | \seealso{ 23 | Other conversion: 24 | \code{\link{dodgr_deduplicate_graph}()}, 25 | \code{\link{dodgr_to_igraph}()}, 26 | \code{\link{dodgr_to_sf}()}, 27 | \code{\link{dodgr_to_sfc}()}, 28 | \code{\link{igraph_to_dodgr}()} 29 | } 30 | \concept{conversion} 31 | -------------------------------------------------------------------------------- /tests/valgrind-test.R: -------------------------------------------------------------------------------- 1 | vg_check <- function () { 2 | 3 | vg <- system2 ( 4 | command = "R", 5 | args = c ( 6 | '-d "valgrind --tool=memcheck --leak-check=full"', 7 | "-f valgrind-script.R" 8 | ), 9 | stdout = TRUE, stderr = TRUE 10 | ) 11 | 12 | lost <- NULL 13 | types <- c ("definitely lost", "indirectly lost", "possibly lost") 14 | for (ty in types) { 15 | lost_type <- which (grepl (ty, vg)) 16 | n <- regmatches ( 17 | vg [lost_type], 18 | gregexpr ("[[:digit:]]+", vg [lost_type]) 19 | ) 20 | lost <- c (lost, as.numeric (n [[1]] [2:3])) 21 | } 22 | if (any (lost > 0)) { 23 | stop ("valgrind memory leaks detected!") 24 | } 25 | 26 | if (attr (vg, "status") != 0) { 27 | stop ("valgrind error") 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /man/dodgr_components.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/graph-functions.R 3 | \name{dodgr_components} 4 | \alias{dodgr_components} 5 | \title{Identify connected components of graph.} 6 | \usage{ 7 | dodgr_components(graph) 8 | } 9 | \arguments{ 10 | \item{graph}{A \code{data.frame} of edges} 11 | } 12 | \value{ 13 | Equivalent graph with additional \code{component} column, 14 | sequentially numbered from 1 = largest component. 15 | } 16 | \description{ 17 | Identify connected components of graph and add corresponding \code{component} 18 | column to \code{data.frame}. 19 | } 20 | \examples{ 21 | graph <- weight_streetnet (hampi) 22 | graph <- dodgr_components (graph) 23 | } 24 | \seealso{ 25 | Other modification: 26 | \code{\link{dodgr_contract_graph}()}, 27 | \code{\link{dodgr_uncontract_graph}()} 28 | } 29 | \concept{modification} 30 | -------------------------------------------------------------------------------- /man/dodgr_cache_off.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/cache.R 3 | \name{dodgr_cache_off} 4 | \alias{dodgr_cache_off} 5 | \title{Turn off all dodgr caching in current session.} 6 | \usage{ 7 | dodgr_cache_off() 8 | } 9 | \value{ 10 | Nothing; the function invisibly returns \code{TRUE} if successful. 11 | } 12 | \description{ 13 | This function is useful is speed is paramount, and if graph contraction is 14 | not needed. Caching can be switched back on with \link{dodgr_cache_on}. 15 | } 16 | \examples{ 17 | dodgr_cache_off () 18 | # Then call dodgr functions as usual: 19 | graph <- weight_streetnet (hampi, wt_profile = "foot") 20 | } 21 | \seealso{ 22 | Other cache: 23 | \code{\link{clear_dodgr_cache}()}, 24 | \code{\link{dodgr_cache_on}()}, 25 | \code{\link{dodgr_load_streetnet}()}, 26 | \code{\link{dodgr_save_streetnet}()} 27 | } 28 | \concept{cache} 29 | -------------------------------------------------------------------------------- /src/sf-as-network.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | // [[Rcpp::depends(RcppParallel)]] 6 | #include 7 | 8 | const double earth = 6378137.0; // WSG84 definition 9 | 10 | constexpr float INFINITE_FLOAT = std::numeric_limits::max (); 11 | constexpr double INFINITE_DOUBLE = std::numeric_limits::max (); 12 | constexpr int INFINITE_INT = std::numeric_limits::max (); 13 | 14 | namespace sf{ 15 | 16 | void fill_one_row (const R_xlen_t ngeoms, const Rcpp::NumericMatrix &gi, 17 | const Rcpp::CharacterVector &rnms, const double &hw_factor, 18 | const std::string &hway, const bool &has_names, 19 | const std::vector &way_names, 20 | const size_t &grownum, const size_t &rownum, const bool &reverse, 21 | Rcpp::NumericMatrix &nmat, Rcpp::CharacterMatrix &idmat); 22 | 23 | } 24 | 25 | Rcpp::List rcpp_sf_as_network (const Rcpp::List &sf_lines, 26 | const Rcpp::DataFrame &pr); 27 | -------------------------------------------------------------------------------- /man/igraph_to_dodgr.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/graph-conversion.R 3 | \name{igraph_to_dodgr} 4 | \alias{igraph_to_dodgr} 5 | \title{Convert a \pkg{igraph} network to an equivalent \code{dodgr} representation.} 6 | \usage{ 7 | igraph_to_dodgr(graph) 8 | } 9 | \arguments{ 10 | \item{graph}{An \pkg{igraph} network} 11 | } 12 | \value{ 13 | The \code{dodgr} equivalent of the input. 14 | } 15 | \description{ 16 | Convert a \pkg{igraph} network to an equivalent \code{dodgr} representation. 17 | } 18 | \examples{ 19 | graph <- weight_streetnet (hampi) 20 | graphi <- dodgr_to_igraph (graph) 21 | graph2 <- igraph_to_dodgr (graphi) 22 | identical (graph2, graph) # FALSE 23 | } 24 | \seealso{ 25 | \link{dodgr_to_igraph} 26 | 27 | Other conversion: 28 | \code{\link{dodgr_deduplicate_graph}()}, 29 | \code{\link{dodgr_to_igraph}()}, 30 | \code{\link{dodgr_to_sf}()}, 31 | \code{\link{dodgr_to_sfc}()}, 32 | \code{\link{dodgr_to_tidygraph}()} 33 | } 34 | \concept{conversion} 35 | -------------------------------------------------------------------------------- /man/hampi.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/dodgr-package.R 3 | \docType{data} 4 | \name{hampi} 5 | \alias{hampi} 6 | \title{Sample street network from Hampi, India.} 7 | \format{ 8 | A Simple Features \code{sf} \code{data.frame} containing the street 9 | network of Hampi. 10 | } 11 | \description{ 12 | A sample street network from the township of Hampi, Karnataka, India. 13 | } 14 | \note{ 15 | Can be re-created with the following command, which also removes 16 | extraneous columns to reduce size: 17 | } 18 | \examples{ 19 | \dontrun{ 20 | hampi <- dodgr_streetnet ("hampi india") 21 | cols <- c ("osm_id", "highway", "oneway", "geometry") 22 | hampi <- hampi [, which (names (hampi) \%in\% cols)] 23 | } 24 | # this 'sf data.frame' can be converted to a 'dodgr' network with 25 | net <- weight_streetnet (hampi, wt_profile = "foot") 26 | } 27 | \seealso{ 28 | Other data: 29 | \code{\link{os_roads_bristol}}, 30 | \code{\link{weighting_profiles}} 31 | } 32 | \concept{data} 33 | \keyword{datasets} 34 | -------------------------------------------------------------------------------- /src/sc-as-network.cpp: -------------------------------------------------------------------------------- 1 | #include "sc-as-network.h" 2 | 3 | // Function to generate IDs for the edges in each way 4 | std::string sc::random_id (size_t len) { 5 | auto randchar = []() -> char 6 | { 7 | const char charset[] = \ 8 | "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; 9 | const size_t max_index = (sizeof(charset) - 1); 10 | //return charset [ rand() % max_index ]; 11 | size_t i = static_cast (floor (Rcpp::runif (1) [0] * max_index)); 12 | return charset [i]; 13 | }; 14 | std::string str (len, 0); 15 | std::generate_n (str.begin(), len, randchar); 16 | return str; 17 | } 18 | 19 | //' rcpp_gen_hash 20 | //' 21 | //' Efficient generation of long sequences of hash keys 22 | //' 23 | //' @noRd 24 | // [[Rcpp::export]] 25 | Rcpp::CharacterVector rcpp_gen_hash (const int n, const size_t hash_len) 26 | { 27 | Rcpp::CharacterVector res (n); 28 | for (int i = 0; i < n; i++) 29 | res [i] = sc::random_id (hash_len); 30 | return res; 31 | } 32 | -------------------------------------------------------------------------------- /man/dodgr_to_igraph.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/graph-conversion.R 3 | \name{dodgr_to_igraph} 4 | \alias{dodgr_to_igraph} 5 | \title{Convert a \code{dodgr} graph to an \pkg{igraph}.} 6 | \usage{ 7 | dodgr_to_igraph(graph, weight_column = "d") 8 | } 9 | \arguments{ 10 | \item{graph}{A \code{dodgr} graph} 11 | 12 | \item{weight_column}{The column of the \code{dodgr} network to use as the edge 13 | weights in the \code{igraph} representation.} 14 | } 15 | \value{ 16 | The \code{igraph} equivalent of the input. Note that this will \emph{not} 17 | be a dual-weighted graph. 18 | } 19 | \description{ 20 | Convert a \code{dodgr} graph to an \pkg{igraph}. 21 | } 22 | \examples{ 23 | graph <- weight_streetnet (hampi) 24 | graphi <- dodgr_to_igraph (graph) 25 | } 26 | \seealso{ 27 | \link{igraph_to_dodgr} 28 | 29 | Other conversion: 30 | \code{\link{dodgr_deduplicate_graph}()}, 31 | \code{\link{dodgr_to_sf}()}, 32 | \code{\link{dodgr_to_sfc}()}, 33 | \code{\link{dodgr_to_tidygraph}()}, 34 | \code{\link{igraph_to_dodgr}()} 35 | } 36 | \concept{conversion} 37 | -------------------------------------------------------------------------------- /src/deduplicate.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | // [[Rcpp::plugins(cpp11)]] 9 | // [[Rcpp::depends(RcppParallel,RcppThread)]] 10 | #include 11 | #include 12 | 13 | namespace deduplicate { 14 | 15 | typedef std::pair str_pair; 16 | 17 | struct str_pair_hash 18 | { 19 | std::size_t operator() (const std::pair &pair) const { 20 | return std::hash () (pair.first) ^ std::hash () (pair.second); 21 | } 22 | }; 23 | 24 | typedef std::unordered_map EdgeMapType; 25 | 26 | void update_dupl_edge_map (deduplicate::EdgeMapType &edge_map, 27 | const str_pair &this_pair, const double &val); 28 | 29 | } // end namespace deduplicate 30 | 31 | Rcpp::DataFrame rcpp_deduplicate (const Rcpp::DataFrame &graph, const std::string fr_col, const std::string to_col, 32 | const std::string d_col, const std::string t_col); 33 | -------------------------------------------------------------------------------- /tests/testthat/test-dists-nearest.R: -------------------------------------------------------------------------------- 1 | test_all <- (identical (Sys.getenv ("MPADGE_LOCAL"), "true") || 2 | identical (Sys.getenv ("GITHUB_JOB"), "test-coverage")) 3 | 4 | if (!test_all) { 5 | RcppParallel::setThreadOptions (numThreads = 2) 6 | } 7 | 8 | test_that ("categorical nearest dists", { 9 | 10 | graph <- weight_streetnet (hampi) 11 | 12 | nf <- 50 13 | nt <- 100 14 | set.seed (1) 15 | from <- sample (graph$from_id, size = nf) 16 | to <- sample (graph$to_id, size = nt) 17 | 18 | expect_silent ( 19 | d <- dodgr_dists_nearest (graph, from, to) 20 | ) 21 | 22 | expect_s3_class (d, "data.frame") 23 | expect_equal (ncol (d), 3L) 24 | expect_equal (nrow (d), length (from)) 25 | expect_identical (names (d), c ("from", "to", "d")) 26 | expect_type (d$from, "character") 27 | expect_type (d$to, "character") 28 | expect_type (d$d, "double") 29 | 30 | expect_identical (d$from, from) 31 | expect_false (all (to %in% d$to)) 32 | expect_true (all (d$to [which (!is.na (d$to))] %in% to)) 33 | expect_true (min (d$d, na.rm = TRUE) >= 0.0) 34 | }) 35 | -------------------------------------------------------------------------------- /R/times.R: -------------------------------------------------------------------------------- 1 | #' Calculate matrix of pair-wise travel times between points. 2 | #' 3 | #' @inherit dodgr_dists 4 | #' @inheritParams dodgr_dists 5 | #' 6 | #' @family distances 7 | #' @export 8 | dodgr_times <- function (graph, 9 | from = NULL, 10 | to = NULL, 11 | shortest = FALSE, 12 | pairwise = FALSE, 13 | heap = "BHeap") { 14 | 15 | graph <- tbl_to_df (graph) 16 | 17 | gr_cols <- dodgr_graph_cols (graph) 18 | if (is.na (gr_cols$time)) { 19 | stop ("graph has no time column") 20 | } 21 | 22 | graph [[gr_cols$d]] <- graph [[gr_cols$time]] 23 | 24 | if (!shortest) { 25 | if (is.na (gr_cols$time_weighted)) { 26 | stop ( 27 | "Graph does not contain a weighted time column from ", 28 | "which to calculate fastest paths." 29 | ) 30 | } 31 | graph [[gr_cols$d_weighted]] <- graph [[gr_cols$time_weighted]] 32 | } 33 | 34 | dodgr_dists (graph = graph, from = from, to = to, heap = heap, pairwise= pairwise) 35 | } -------------------------------------------------------------------------------- /man/dodgr_load_streetnet.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/save_load_streetnet.R 3 | \name{dodgr_load_streetnet} 4 | \alias{dodgr_load_streetnet} 5 | \title{Load a street network previously saved with \link{dodgr_save_streetnet}.} 6 | \usage{ 7 | dodgr_load_streetnet(filename) 8 | } 9 | \arguments{ 10 | \item{filename}{Name (with optional full path) of file to be loaded.} 11 | } 12 | \value{ 13 | The loaded street network. 14 | } 15 | \description{ 16 | This always returns the full, non-contracted graph. The contracted graph can 17 | be generated by passing the result to \link{dodgr_contract_graph}. 18 | } 19 | \examples{ 20 | net <- weight_streetnet (hampi) 21 | f <- file.path (tempdir (), "streetnet.Rds") 22 | dodgr_save_streetnet (net, f) 23 | clear_dodgr_cache () # rm cached objects from tempdir 24 | # at some later time, or in a new R session: 25 | net <- dodgr_load_streetnet (f) 26 | } 27 | \seealso{ 28 | Other cache: 29 | \code{\link{clear_dodgr_cache}()}, 30 | \code{\link{dodgr_cache_off}()}, 31 | \code{\link{dodgr_cache_on}()}, 32 | \code{\link{dodgr_save_streetnet}()} 33 | } 34 | \concept{cache} 35 | -------------------------------------------------------------------------------- /man/weighting_profiles.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/dodgr-package.R 3 | \docType{data} 4 | \name{weighting_profiles} 5 | \alias{weighting_profiles} 6 | \title{Weighting profiles used to route different modes of transport.} 7 | \format{ 8 | List of \code{data.frame} objects with profile names, means of transport 9 | and weights. 10 | } 11 | \description{ 12 | Collection of weighting profiles used to adjust the routing process to 13 | different means of transport. Modified from data taken from the Routino 14 | project, with additional tables for average speeds, dependence of speed on 15 | type of surface, and waiting times in seconds at traffic lights. The latter 16 | table (called "penalties") includes waiting times at traffic lights (in 17 | seconds), additional time penalties for turning across oncoming traffic 18 | ("turn"), and a binary flag indicating whether turn restrictions should be 19 | obeyed or not. 20 | } 21 | \references{ 22 | \url{https://www.routino.org/xml/routino-profiles.xml} 23 | } 24 | \seealso{ 25 | Other data: 26 | \code{\link{hampi}}, 27 | \code{\link{os_roads_bristol}} 28 | } 29 | \concept{data} 30 | \keyword{datasets} 31 | -------------------------------------------------------------------------------- /man/dodgr_to_sf.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/graph-conversion.R 3 | \name{dodgr_to_sf} 4 | \alias{dodgr_to_sf} 5 | \title{Convert a \code{dodgr} graph into an equivalent \pkg{sf} object.} 6 | \usage{ 7 | dodgr_to_sf(graph) 8 | } 9 | \arguments{ 10 | \item{graph}{A \code{dodgr} graph} 11 | } 12 | \value{ 13 | Equivalent object of class \pkg{sf}. 14 | } 15 | \description{ 16 | Works by aggregating edges into \code{LINESTRING} objects representing longest 17 | sequences between all junction nodes. The resultant objects will generally 18 | contain more \code{LINESTRING} objects than the original \pkg{sf} object, because 19 | the former will be bisected at every junction point. 20 | } 21 | \note{ 22 | Requires the \pkg{sf} package to be installed. 23 | } 24 | \examples{ 25 | hw <- weight_streetnet (hampi) 26 | nrow (hw) # 5,729 edges 27 | xy <- dodgr_to_sf (hw) 28 | dim (xy) # 764 edges; 14 attributes 29 | } 30 | \seealso{ 31 | Other conversion: 32 | \code{\link{dodgr_deduplicate_graph}()}, 33 | \code{\link{dodgr_to_igraph}()}, 34 | \code{\link{dodgr_to_sfc}()}, 35 | \code{\link{dodgr_to_tidygraph}()}, 36 | \code{\link{igraph_to_dodgr}()} 37 | } 38 | \concept{conversion} 39 | -------------------------------------------------------------------------------- /tests/testthat/test-save-load.R: -------------------------------------------------------------------------------- 1 | testthat::skip_on_cran () 2 | testthat::skip_on_os ("mac") 3 | 4 | test_all <- (identical (Sys.getenv ("MPADGE_LOCAL"), "true") || 5 | identical (Sys.getenv ("GITHUB_JOB"), "test-coverage")) 6 | 7 | if (!test_all) { 8 | RcppParallel::setThreadOptions (numThreads = 2) 9 | } 10 | 11 | test_that ("save and load", { 12 | clear_dodgr_cache () 13 | net0 <- weight_streetnet (hampi) 14 | f <- file.path (tempdir (), "junk") 15 | expect_silent ( 16 | dodgr_save_streetnet (net0, f) 17 | ) 18 | expect_false (file.exists (f)) 19 | f <- paste0 (f, ".Rds") 20 | expect_true (file.exists (f)) 21 | 22 | x <- readRDS (f) 23 | expect_is (x, "list") 24 | 25 | expect_identical ( 26 | names (x), 27 | c ( 28 | "graph", "verts", "graph_c", 29 | "verts_c", "edge_map", "junctions" 30 | ) 31 | ) 32 | 33 | flist0 <- list.files (tempdir (), pattern = "^dodgr\\_") 34 | clear_dodgr_cache () 35 | 36 | net1 <- dodgr_load_streetnet (f) 37 | expect_equal (net0, net1) 38 | flist1 <- list.files (tempdir (), pattern = "^dodgr\\_") 39 | 40 | # This now fails in GHA test environments for some reason 41 | # expect_true (all (flist1 %in% flist0)) 42 | }) 43 | -------------------------------------------------------------------------------- /src/concaveman.cpp: -------------------------------------------------------------------------------- 1 | #include "concaveman.h" 2 | 3 | //' rcpp_concaveman 4 | //' @noRd 5 | // [[Rcpp::export]] 6 | Rcpp::DataFrame rcpp_concaveman (Rcpp::DataFrame xy, Rcpp::NumericVector hull_in, 7 | const double concavity, const double length_threshold) 8 | { 9 | std::vector x = xy ["x"], y = xy ["y"]; 10 | const size_t num_points = static_cast (xy.nrow ()); 11 | 12 | typedef double T; 13 | typedef std::array point_type; 14 | 15 | std::vector points (num_points); 16 | for (auto i = 0; i < num_points; i++) { 17 | points[i] = { x [i], y [i] }; 18 | } 19 | 20 | std::vector hull = Rcpp::as > (hull_in); 21 | 22 | auto concave_points = concaveman (points, hull, 23 | concavity, length_threshold); 24 | 25 | Rcpp::NumericVector xout (concave_points.size ()), 26 | yout (concave_points.size ()); 27 | for (int i = 0; i < concave_points.size (); i++) 28 | { 29 | xout (i) = concave_points [i] [0]; 30 | yout (i) = concave_points [i] [1]; 31 | } 32 | 33 | Rcpp::DataFrame res = Rcpp::DataFrame::create ( 34 | Rcpp::Named ("x") = xout, 35 | Rcpp::Named ("y") = yout); 36 | 37 | return res; 38 | } 39 | -------------------------------------------------------------------------------- /man/clear_dodgr_cache.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/cache.R 3 | \name{clear_dodgr_cache} 4 | \alias{clear_dodgr_cache} 5 | \title{Remove cached versions of \code{dodgr} graphs.} 6 | \usage{ 7 | clear_dodgr_cache() 8 | } 9 | \value{ 10 | Nothing; the function silently clears any cached objects 11 | } 12 | \description{ 13 | This function should generally \emph{not} be needed, except if graph 14 | structure has been directly modified other than through \code{dodgr} functions; 15 | for example by modifying edge weights or distances. Graphs are cached based 16 | on the vector of edge IDs, so manual changes to any other attributes will not 17 | necessarily be translated into changes in \code{dodgr} output unless the cached 18 | versions are cleared using this function. See 19 | \url{https://github.com/UrbanAnalyst/dodgr/wiki/Caching-of-streetnets-and-contracted-graphs} 20 | for details of caching process. 21 | } 22 | \examples{ 23 | clear_dodgr_cache () 24 | # Then call dodgr functions as usual: 25 | graph <- weight_streetnet (hampi, wt_profile = "foot") 26 | } 27 | \seealso{ 28 | Other cache: 29 | \code{\link{dodgr_cache_off}()}, 30 | \code{\link{dodgr_cache_on}()}, 31 | \code{\link{dodgr_load_streetnet}()}, 32 | \code{\link{dodgr_save_streetnet}()} 33 | } 34 | \concept{cache} 35 | -------------------------------------------------------------------------------- /vignettes/times-fig1.tex: -------------------------------------------------------------------------------- 1 | \documentclass[tikz,border=5pt]{standalone} 2 | \usepackage[utf8x]{inputenc} 3 | 4 | \begin{document} 5 | \begin{tikzpicture}[font=\tiny] 6 | \tikzstyle{node_style} = [draw=white, very thick, circle, fill=orange] 7 | \tikzstyle{arrow_style1} = [->, black, line width=1, >=latex] 8 | \tikzstyle{arrow_style2} = [->, gray, line width=1, >=latex] 9 | \tikzstyle{arrow_style3} = [->, black, line width=3, >=latex] 10 | \tikzstyle{edge_style2} = [lightgray, line width=1] 11 | \tikzstyle{line_style1} = [-, gray, line width=1] 12 | 13 | 14 | \node[node_style] (n0) at (2,2) {O}; 15 | \node[node_style] (na) at (2,0) {A}; 16 | \node[node_style] (nb) at (0,2) {B}; 17 | \node[node_style] (nc) at (4,2) {C}; 18 | \node[node_style] (nd) at (2,4) {D}; 19 | 20 | \draw[arrow_style1] (na) edge (n0); 21 | \draw[arrow_style2] (n0) edge (nb); 22 | \draw[arrow_style2] (n0) edge (nc); 23 | \draw[arrow_style2] (n0) edge (nd); 24 | 25 | \draw[arrow_style1, dashed, rounded corners] (1.8,0.2) -- (1.8, 1.8) -- (0.2, 1.8); 26 | \draw[arrow_style1, dashed, rounded corners] (2.2,0.2) -- (2.2, 1.8) -- (3.8, 1.8); 27 | \draw[arrow_style1, dashed] (2.1, 0.3) -- (2.1, 3.7); 28 | \end{tikzpicture} 29 | \end{document} 30 | -------------------------------------------------------------------------------- /man/write_dodgr_wt_profile.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/weighting_profiles.R 3 | \name{write_dodgr_wt_profile} 4 | \alias{write_dodgr_wt_profile} 5 | \title{Write \code{dodgr} weighting profiles to local file.} 6 | \usage{ 7 | write_dodgr_wt_profile(file = NULL) 8 | } 9 | \arguments{ 10 | \item{file}{Full name (including path) of file to which to write. The \code{.json} 11 | suffix will be automatically appended.} 12 | } 13 | \value{ 14 | TRUE if writing successful. 15 | } 16 | \description{ 17 | Write the \code{dodgr} street network weighting profiles to a local 18 | \code{.json}-formatted file for manual editing and subsequent re-reading. 19 | } 20 | \examples{ 21 | f <- tempfile (fileext = ".json") 22 | write_dodgr_wt_profile (file = f) 23 | wt_profiles <- jsonlite::read_json (f, simplify = TRUE) 24 | } 25 | \seealso{ 26 | \link{weight_streetnet} 27 | 28 | Other misc: 29 | \code{\link{compare_heaps}()}, 30 | \code{\link{dodgr_flowmap}()}, 31 | \code{\link{dodgr_full_cycles}()}, 32 | \code{\link{dodgr_fundamental_cycles}()}, 33 | \code{\link{dodgr_insert_vertex}()}, 34 | \code{\link{dodgr_sample}()}, 35 | \code{\link{dodgr_sflines_to_poly}()}, 36 | \code{\link{dodgr_vertices}()}, 37 | \code{\link{merge_directed_graph}()}, 38 | \code{\link{summary.dodgr_dists_categorical}()} 39 | } 40 | \concept{misc} 41 | -------------------------------------------------------------------------------- /vignettes/parallel.Rmd: -------------------------------------------------------------------------------- 1 | --- 2 | title: "Parallel computation" 3 | author: "Mark Padgham" 4 | date: "`r Sys.Date()`" 5 | output: 6 | html_document: 7 | toc: true 8 | toc_float: true 9 | number_sections: false 10 | theme: flatly 11 | vignette: > 12 | %\VignetteIndexEntry{5 parallel} 13 | %\VignetteEngine{knitr::rmarkdown} 14 | %\VignetteEncoding{UTF-8} 15 | --- 16 | 17 | ```{r pkg-load, echo = FALSE, message = FALSE} 18 | library (dodgr) 19 | ``` 20 | 21 | 22 | The `dodgr` package implements most calculations by default in parallel, using 23 | the maximum number of available threads or cores. Numbers of available threads 24 | can be determined with either of the following two functions: 25 | 26 | ```{r numcores} 27 | parallel::detectCores () 28 | RcppParallel::defaultNumThreads () 29 | ``` 30 | 31 | Numbers of cores used in calculations can be controlled by specifying the 32 | `numThread` parameter passed to [the `RcppParallel` function 33 | `setThreadOptions`](https://rcppcore.github.io/RcppParallel/#threads_used). 34 | For control over numbers of threads used, this function must be called prior to 35 | calling any `dodgr` functions. For example, single-threaded processing can be 36 | ensured by first making the following call: 37 | 38 | ```{r single-core, eval = FALSE} 39 | RcppParallel::setThreadOptions (numThreads = 1L) 40 | ``` 41 | -------------------------------------------------------------------------------- /man/dodgr_vertices.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/graph-functions.R 3 | \name{dodgr_vertices} 4 | \alias{dodgr_vertices} 5 | \title{Extract vertices of graph, including spatial coordinates if included.} 6 | \usage{ 7 | dodgr_vertices(graph) 8 | } 9 | \arguments{ 10 | \item{graph}{A flat table of graph edges. Must contain columns labelled 11 | \code{from} and \code{to}, or \code{start} and \code{stop}. May also contain 12 | similarly labelled columns of spatial coordinates (for example 13 | \code{from_x}) or \code{stop_lon}).} 14 | } 15 | \value{ 16 | A \code{data.frame} of vertices with unique numbers (\code{n}). 17 | } 18 | \description{ 19 | Extract vertices of graph, including spatial coordinates if included. 20 | } 21 | \note{ 22 | Values of \code{n} are 0-indexed 23 | } 24 | \examples{ 25 | graph <- weight_streetnet (hampi) 26 | v <- dodgr_vertices (graph) 27 | } 28 | \seealso{ 29 | Other misc: 30 | \code{\link{compare_heaps}()}, 31 | \code{\link{dodgr_flowmap}()}, 32 | \code{\link{dodgr_full_cycles}()}, 33 | \code{\link{dodgr_fundamental_cycles}()}, 34 | \code{\link{dodgr_insert_vertex}()}, 35 | \code{\link{dodgr_sample}()}, 36 | \code{\link{dodgr_sflines_to_poly}()}, 37 | \code{\link{merge_directed_graph}()}, 38 | \code{\link{summary.dodgr_dists_categorical}()}, 39 | \code{\link{write_dodgr_wt_profile}()} 40 | } 41 | \concept{misc} 42 | -------------------------------------------------------------------------------- /man/dodgr_deduplicate_graph.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/deduplicate.R 3 | \name{dodgr_deduplicate_graph} 4 | \alias{dodgr_deduplicate_graph} 5 | \title{Deduplicate edges in a graph} 6 | \usage{ 7 | dodgr_deduplicate_graph(graph) 8 | } 9 | \arguments{ 10 | \item{graph}{Any 'dodgr' graph or network.} 11 | } 12 | \value{ 13 | A potentially modified version of graph, with any formerly duplicated 14 | edges reduces to single rows containing minimal weighted distances and times. 15 | } 16 | \description{ 17 | Graph may have duplicated edges, particularly when extracted as 18 | \link{dodgr_streetnet} objects. This function de-duplicates any repeated 19 | edges, reducing weighted distances and times to the minimal values from all 20 | duplicates. 21 | } 22 | \examples{ 23 | net0 <- weight_streetnet (hampi, wt_profile = "foot") 24 | nrow (net0) 25 | # Duplicate part of input data: 26 | h2 <- rbind (hampi, hampi [1, ]) 27 | net1 <- weight_streetnet (h2, wt_profile = "foot") 28 | nrow (net1) # network then has more edges 29 | net2 <- dodgr_deduplicate_graph (net1) 30 | nrow (net2) 31 | stopifnot (identical (nrow (net0), nrow (net2))) 32 | } 33 | \seealso{ 34 | Other conversion: 35 | \code{\link{dodgr_to_igraph}()}, 36 | \code{\link{dodgr_to_sf}()}, 37 | \code{\link{dodgr_to_sfc}()}, 38 | \code{\link{dodgr_to_tidygraph}()}, 39 | \code{\link{igraph_to_dodgr}()} 40 | } 41 | \concept{conversion} 42 | -------------------------------------------------------------------------------- /vignettes/fig1.tex: -------------------------------------------------------------------------------- 1 | %\documentclass[tikz,convert={outfile=\jobname.svg},border=5pt]{standalone} 2 | \documentclass[tikz,border=5pt]{standalone} 3 | \usepackage[utf8x]{inputenc} 4 | % To create the .png: 5 | % > pdflatex fig1.tex 6 | % > convert -density 300 fig1.pdf -quality 100 fig1.png 7 | 8 | \begin{document} 9 | \begin{tikzpicture}[font=\tiny] 10 | \tikzstyle{node_style} = [draw=white, very thick, circle, fill=orange] 11 | \tikzstyle{arrow_style1} = [->, black, line width=1, >=latex] 12 | \tikzstyle{arrow_style2} = [->, black, line width=2, >=latex] 13 | \tikzstyle{arrow_style3} = [->, black, line width=3, >=latex] 14 | 15 | 16 | \node[node_style] (n1) at (0,0) {A}; 17 | \node[node_style] (n2) at (4,0) {B}; 18 | \node[node_style] (n3) at (4,2) {C}; 19 | \node[node_style] (n4) at (0,2) {D}; 20 | 21 | \draw[arrow_style1] (n1) edge [bend left=20] (n2); 22 | \draw[arrow_style2] (n2) edge [bend left=20] (n1); 23 | \draw[arrow_style1] (n2) edge [bend left=20] (n3); 24 | \draw[arrow_style2] (n3) edge [bend left=20] (n2); 25 | \draw[arrow_style1] (n3) edge [bend left=20] (n4); 26 | \draw[arrow_style2] (n4) edge [bend left=20] (n3); 27 | \draw[arrow_style1] (n4) edge (n1); 28 | \draw[arrow_style3, shorten <=2pt, shorten >=2pt] (n2) edge [out=140, in=-60] (n4); 29 | \end{tikzpicture} 30 | 31 | \end{document} 32 | -------------------------------------------------------------------------------- /vignettes/makefile: -------------------------------------------------------------------------------- 1 | LFILE = dists-categorical 2 | 3 | all: knith 4 | #all: knith open 5 | 6 | knith: $(LFILE).Rmd 7 | echo "rmarkdown::render('$(LFILE).Rmd',output_file='$(LFILE).html')" | R --no-save -q 8 | 9 | knitr: $(LFILE).Rmd 10 | echo "rmarkdown::render('$(LFILE).Rmd',rmarkdown::md_document(variant='gfm'))" | R --no-save -q 11 | 12 | open: $(LFILE).html 13 | xdg-open $(LFILE).html & 14 | 15 | clean: 16 | rm -rf *.html *.png 17 | 18 | 19 | 20 | fig1: pdflone convone rmone 21 | 22 | pdflone: fig1.tex 23 | pdflatex fig1.tex 24 | 25 | convone: fig1.pdf 26 | convert -density 300 fig1.pdf -quality 100 fig1.png 27 | 28 | rmone: 29 | rm fig1.aux fig1.log fig1.pdf 30 | 31 | fig2: pdfltwo convtwo rmtwo 32 | 33 | pdfltwo: fig2.tex 34 | pdflatex fig2.tex 35 | 36 | convtwo: fig2.pdf 37 | convert -density 300 fig2.pdf -quality 100 fig2.png 38 | 39 | rmtwo: 40 | rm fig2.aux fig2.log fig2.pdf 41 | 42 | fig3: pdflthree convthree rmthree 43 | 44 | pdflthree: fig3.tex 45 | pdflatex fig3.tex 46 | 47 | convthree: fig3.pdf 48 | convert -density 300 fig3.pdf -quality 100 fig3.png 49 | 50 | rmthree: 51 | rm fig3.aux fig3.log fig3.pdf 52 | 53 | figt1: pdflone_t convone_t rmone_t 54 | 55 | pdflone_t: times-fig1.tex 56 | pdflatex times-fig1.tex 57 | 58 | convone_t: times-fig1.pdf 59 | convert -density 300 times-fig1.pdf -quality 100 times-fig1.png 60 | 61 | rmone_t: 62 | rm times-fig1.aux times-fig1.log times-fig1.pdf 63 | -------------------------------------------------------------------------------- /_pkgdown.yml: -------------------------------------------------------------------------------- 1 | url: https://UrbanAnalyst.github.io/dodgr 2 | title: dodgr 3 | 4 | template: 5 | bootstrap: 5 6 | 7 | reference: 8 | - title: Main distance and time functions 9 | contents: 10 | - has_concept("distances") 11 | - title: Main flow functions 12 | contents: 13 | - has_concept("flows") 14 | - title: Main iso functions 15 | contents: 16 | - has_concept("iso") 17 | - title: Functions to Obtain Graphs 18 | contents: 19 | - has_concept("extraction") 20 | - title: Functions to Modify Graphs 21 | contents: 22 | - has_concept("modification") 23 | - title: Functions to Convert Graphs 24 | contents: 25 | - has_concept("conversion") 26 | - title: Graph Centrality 27 | contents: 28 | - has_concept("centrality") 29 | - title: Matching Points to Graphs 30 | contents: 31 | - has_concept("match") 32 | - title: Miscellaneous Functions 33 | contents: 34 | - has_concept("misc") 35 | - title: Save, Load, and Caching 36 | contents: 37 | - has_concept("cache") 38 | - title: Package Data 39 | contents: 40 | - has_concept("data") 41 | - title: The 'dodgr' package 42 | contents: 43 | - has_concept("package") 44 | 45 | navbar: 46 | title: dodgr 47 | type: default 48 | left: 49 | - text: Home 50 | href: index.html 51 | - text: Vignettes 52 | href: articles/index.html 53 | - text: Functions 54 | href: reference/index.html 55 | right: 56 | - icon: fa-github fa-lg 57 | href: https://github.com/UrbanAnalyst/dodgr 58 | -------------------------------------------------------------------------------- /CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Code of Conduct 2 | 3 | As contributors and maintainers of this project, we pledge to respect all people who 4 | contribute through reporting issues, posting feature requests, updating documentation, 5 | submitting pull requests or patches, and other activities. 6 | 7 | We are committed to making participation in this project a harassment-free experience for 8 | everyone, regardless of level of experience, gender, gender identity and expression, 9 | sexual orientation, disability, personal appearance, body size, race, ethnicity, age, or religion. 10 | 11 | Examples of unacceptable behavior by participants include the use of sexual language or 12 | imagery, derogatory comments or personal attacks, trolling, public or private harassment, 13 | insults, or other unprofessional conduct. 14 | 15 | Project maintainers have the right and responsibility to remove, edit, or reject comments, 16 | commits, code, wiki edits, issues, and other contributions that are not aligned to this 17 | Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed 18 | from the project team. 19 | 20 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by 21 | opening an issue or contacting one or more of the project maintainers. 22 | 23 | This Code of Conduct is adapted from the Contributor Covenant 24 | (https:contributor-covenant.org), version 1.0.0, available at 25 | https://contributor-covenant.org/version/1/0/0/ 26 | -------------------------------------------------------------------------------- /man/dodgr_streetnet_geodesic.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/utils.R 3 | \name{dodgr_streetnet_geodesic} 4 | \alias{dodgr_streetnet_geodesic} 5 | \title{Force \link{weight_streetnet} to use geodesic distances.} 6 | \usage{ 7 | dodgr_streetnet_geodesic(unset = FALSE) 8 | } 9 | \arguments{ 10 | \item{unset}{Calling this function with \code{unset = TRUE} reverts distance 11 | calculations to those described above, rather than geodesic.} 12 | } 13 | \value{ 14 | Nothing; the function is called for its side-effect only of setting 15 | distance calculations to geodesic. 16 | } 17 | \description{ 18 | Distances by default are Mapbox "cheap" distances if maximal network 19 | distances are < 100km, otherwise Haversine distances. Calling this function 20 | forces all calls to \link{weight_streetnet} from that point on to use 21 | geodesic distances. These are more computationally expensive to calculate, 22 | and weighting networks will likely take more time. 23 | } 24 | \examples{ 25 | net0 <- weight_streetnet (hampi) # Default "cheap" method 26 | dodgr_streetnet_geodesic () 27 | net1 <- weight_streetnet (hampi) 28 | cor (net0$d, net1$d) # Strongly correlated, but not perfect 29 | max (abs (net0$d - net1$d)) # in metres 30 | } 31 | \seealso{ 32 | Other extraction: 33 | \code{\link{dodgr_streetnet}()}, 34 | \code{\link{dodgr_streetnet_sc}()}, 35 | \code{\link{weight_railway}()}, 36 | \code{\link{weight_streetnet}()} 37 | } 38 | \concept{extraction} 39 | -------------------------------------------------------------------------------- /.github/workflows/pkgdown.yaml: -------------------------------------------------------------------------------- 1 | # Workflow derived from https://github.com/r-lib/actions/tree/v2/examples 2 | # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help 3 | on: 4 | push: 5 | branches: [main, master] 6 | pull_request: 7 | release: 8 | types: [published] 9 | workflow_dispatch: 10 | 11 | name: pkgdown.yaml 12 | 13 | permissions: read-all 14 | 15 | jobs: 16 | pkgdown: 17 | runs-on: ubuntu-latest 18 | # Only restrict concurrency for non-PR jobs 19 | concurrency: 20 | group: pkgdown-${{ github.event_name != 'pull_request' || github.run_id }} 21 | env: 22 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 23 | permissions: 24 | contents: write 25 | steps: 26 | - uses: actions/checkout@v4 27 | 28 | - uses: r-lib/actions/setup-pandoc@v2 29 | 30 | - uses: r-lib/actions/setup-r@v2 31 | with: 32 | use-public-rspm: true 33 | 34 | - uses: r-lib/actions/setup-r-dependencies@v2 35 | with: 36 | extra-packages: any::pkgdown, local::. 37 | needs: website 38 | 39 | - name: Build site 40 | run: pkgdown::build_site_github_pages(new_process = FALSE, install = FALSE) 41 | shell: Rscript {0} 42 | 43 | - name: Deploy to GitHub pages 🚀 44 | if: github.event_name != 'pull_request' 45 | uses: JamesIves/github-pages-deploy-action@v4.5.0 46 | with: 47 | clean: false 48 | branch: gh-pages 49 | folder: docs 50 | -------------------------------------------------------------------------------- /man/dodgr_sample.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/graph-functions.R 3 | \name{dodgr_sample} 4 | \alias{dodgr_sample} 5 | \title{Sample a random but connected sub-component of a graph} 6 | \usage{ 7 | dodgr_sample(graph, nverts = 1000) 8 | } 9 | \arguments{ 10 | \item{graph}{A flat table of graph edges. Must contain columns labelled 11 | \code{from} and \code{to}, or \code{start} and \code{stop}. May also contain 12 | similarly labelled columns of spatial coordinates (for example 13 | \code{from_x}) or \code{stop_lon}).} 14 | 15 | \item{nverts}{Number of vertices to sample} 16 | } 17 | \value{ 18 | A connected sub-component of \code{graph} 19 | } 20 | \description{ 21 | Sample a random but connected sub-component of a graph 22 | } 23 | \note{ 24 | Graphs may occasionally have \code{nverts + 1} vertices, rather than 25 | the requested \code{nverts}. 26 | } 27 | \examples{ 28 | graph <- weight_streetnet (hampi) 29 | nrow (graph) # 5,742 30 | graph <- dodgr_sample (graph, nverts = 200) 31 | nrow (graph) # generally around 400 edges 32 | nrow (dodgr_vertices (graph)) # 200 33 | } 34 | \seealso{ 35 | Other misc: 36 | \code{\link{compare_heaps}()}, 37 | \code{\link{dodgr_flowmap}()}, 38 | \code{\link{dodgr_full_cycles}()}, 39 | \code{\link{dodgr_fundamental_cycles}()}, 40 | \code{\link{dodgr_insert_vertex}()}, 41 | \code{\link{dodgr_sflines_to_poly}()}, 42 | \code{\link{dodgr_vertices}()}, 43 | \code{\link{merge_directed_graph}()}, 44 | \code{\link{summary.dodgr_dists_categorical}()}, 45 | \code{\link{write_dodgr_wt_profile}()} 46 | } 47 | \concept{misc} 48 | -------------------------------------------------------------------------------- /.github/workflows/R-CMD-check.yaml: -------------------------------------------------------------------------------- 1 | # Workflow derived from https://github.com/r-lib/actions/tree/v2/examples 2 | # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help 3 | on: 4 | push: 5 | branches: [main, master] 6 | pull_request: 7 | 8 | name: R-CMD-check.yaml 9 | 10 | permissions: read-all 11 | 12 | jobs: 13 | R-CMD-check: 14 | runs-on: ${{ matrix.config.os }} 15 | 16 | name: ${{ matrix.config.os }} (${{ matrix.config.r }}) 17 | 18 | strategy: 19 | fail-fast: false 20 | matrix: 21 | config: 22 | - {os: macos-latest, r: 'release'} 23 | - {os: windows-latest, r: 'release'} 24 | - {os: ubuntu-latest, r: 'devel', http-user-agent: 'release'} 25 | - {os: ubuntu-latest, r: 'release'} 26 | - {os: ubuntu-latest, r: 'oldrel-1'} 27 | 28 | env: 29 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 30 | R_KEEP_PKG_SOURCE: yes 31 | 32 | steps: 33 | - uses: actions/checkout@v4 34 | 35 | - uses: r-lib/actions/setup-pandoc@v2 36 | 37 | - uses: r-lib/actions/setup-r@v2 38 | with: 39 | r-version: ${{ matrix.config.r }} 40 | http-user-agent: ${{ matrix.config.http-user-agent }} 41 | use-public-rspm: true 42 | 43 | - uses: r-lib/actions/setup-r-dependencies@v2 44 | with: 45 | extra-packages: any::rcmdcheck 46 | needs: check 47 | 48 | - uses: r-lib/actions/check-r-package@v2 49 | with: 50 | upload-snapshots: true 51 | build_args: 'c("--no-manual","--compact-vignettes=gs+qpdf")' 52 | -------------------------------------------------------------------------------- /src/fund-cycles.cpp: -------------------------------------------------------------------------------- 1 | #include "fund-cycles.h" 2 | 3 | //' @noRd 4 | // [[Rcpp::export]] 5 | Rcpp::List rcpp_fundamental_cycles (Rcpp::DataFrame graph, 6 | Rcpp::DataFrame verts) 7 | { 8 | std::vector vert_id = verts ["id"]; 9 | std::vector from = graph ["from"]; 10 | std::vector to = graph ["to"]; 11 | 12 | std::unordered_map vert_index; 13 | for (size_t i = 0; i < vert_id.size (); i++) 14 | vert_index.emplace (vert_id [i], i); 15 | 16 | std::vector edge_array (from.size() * 2); 17 | for (size_t i = 0; i < from.size (); i++) 18 | { 19 | edge_array [i * 2] = vert_index.at (from [i]); 20 | edge_array [i * 2 + 1] = vert_index.at (to [i]); 21 | } 22 | graph::Graph gr (vert_id, vert_id.size (), 23 | edge_array, from.size ()); 24 | gr.computeFundamentalCycles(); 25 | 26 | Rcpp::List ret (gr.m_fundamentalCycles.size ()); 27 | std::vector::iterator cycle_iter; 28 | for (cycle_iter = gr.m_fundamentalCycles.begin(); 29 | cycle_iter != gr.m_fundamentalCycles.end (); cycle_iter++) 30 | { 31 | graph::Graph::NodePath path = 32 | gr.cycleMatrix2nodePath (*cycle_iter); 33 | std::vector pathi (path.size ()); 34 | size_t i = 0; 35 | for (const std::string* obj: path) 36 | { 37 | pathi [i++] = *obj; 38 | } 39 | ret [std::distance (gr.m_fundamentalCycles.begin(), cycle_iter)] = pathi; 40 | } 41 | 42 | return ret; 43 | } 44 | -------------------------------------------------------------------------------- /man/dodgr_contract_graph.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/graph-contraction.R 3 | \name{dodgr_contract_graph} 4 | \alias{dodgr_contract_graph} 5 | \title{Contract graph to junction vertices only.} 6 | \usage{ 7 | dodgr_contract_graph(graph, verts = NULL, nocache = FALSE) 8 | } 9 | \arguments{ 10 | \item{graph}{A flat table of graph edges. Must contain columns labelled 11 | \code{from} and \code{to}, or \code{start} and \code{stop}. May also contain 12 | similarly labelled columns of spatial coordinates (for example 13 | \code{from_x}) or \code{stop_lon}).} 14 | 15 | \item{verts}{Optional list of vertices to be retained as routing points. 16 | These must match the \code{from} and \code{to} columns of \code{graph}.} 17 | 18 | \item{nocache}{If \code{FALSE} (default), load cached version of contracted graph 19 | if previously calculated and cached. If \code{TRUE}, then re-contract graph even 20 | if previously calculated version has been stored in cache.} 21 | } 22 | \value{ 23 | A contracted version of the original \code{graph}, containing the same 24 | number of columns, but with each row representing an edge between two 25 | junction vertices (or between the submitted \code{verts}, which may or may not be 26 | junctions). 27 | } 28 | \description{ 29 | Removes redundant (straight-line) vertices from graph, leaving only junction 30 | vertices. 31 | } 32 | \examples{ 33 | graph <- weight_streetnet (hampi) 34 | nrow (graph) # 5,973 35 | graph <- dodgr_contract_graph (graph) 36 | nrow (graph) # 662 37 | } 38 | \seealso{ 39 | Other modification: 40 | \code{\link{dodgr_components}()}, 41 | \code{\link{dodgr_uncontract_graph}()} 42 | } 43 | \concept{modification} 44 | -------------------------------------------------------------------------------- /man/summary.dodgr_dists_categorical.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/dists-categorical.R 3 | \name{summary.dodgr_dists_categorical} 4 | \alias{summary.dodgr_dists_categorical} 5 | \title{Transform a result from \link{dodgr_dists_categorical} to summary statistics} 6 | \usage{ 7 | \method{summary}{dodgr_dists_categorical}(object, ...) 8 | } 9 | \arguments{ 10 | \item{object}{A 'dodgr_dists_categorical' object} 11 | 12 | \item{...}{Extra parameters currently not used} 13 | } 14 | \value{ 15 | The summary statistics (invisibly) 16 | } 17 | \description{ 18 | Transform a result from \link{dodgr_dists_categorical} to summary statistics 19 | } 20 | \examples{ 21 | # Prepare a graph for categorical routing by including an "edge_type" column 22 | graph <- weight_streetnet (hampi, wt_profile = "foot") 23 | graph <- graph [graph$component == 1, ] 24 | graph$edge_type <- graph$highway 25 | # Define start and end points for categorical distances; using all vertices 26 | # here. 27 | length (unique (graph$edge_type)) # Number of categories 28 | v <- dodgr_vertices (graph) 29 | from <- to <- v$id [1:100] 30 | d <- dodgr_dists_categorical (graph, from, to) 31 | # Internal 'summary' method to summarise results: 32 | summary (d) 33 | 34 | } 35 | \seealso{ 36 | Other misc: 37 | \code{\link{compare_heaps}()}, 38 | \code{\link{dodgr_flowmap}()}, 39 | \code{\link{dodgr_full_cycles}()}, 40 | \code{\link{dodgr_fundamental_cycles}()}, 41 | \code{\link{dodgr_insert_vertex}()}, 42 | \code{\link{dodgr_sample}()}, 43 | \code{\link{dodgr_sflines_to_poly}()}, 44 | \code{\link{dodgr_vertices}()}, 45 | \code{\link{merge_directed_graph}()}, 46 | \code{\link{write_dodgr_wt_profile}()} 47 | } 48 | \concept{misc} 49 | -------------------------------------------------------------------------------- /man/dodgr_sflines_to_poly.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/fund-cycles.R 3 | \name{dodgr_sflines_to_poly} 4 | \alias{dodgr_sflines_to_poly} 5 | \title{Convert \pkg{sf} \code{LINESTRING} objects to \code{POLYGON} objects representing all 6 | fundamental cycles within the \code{LINESTRING} objects.} 7 | \usage{ 8 | dodgr_sflines_to_poly(sflines, graph_max_size = 10000, expand = 0.05) 9 | } 10 | \arguments{ 11 | \item{sflines}{An \pkg{sf} \code{LINESTRING} object representing a network.} 12 | 13 | \item{graph_max_size}{Maximum size submitted to the internal C++ routines as 14 | a single chunk. Warning: Increasing this may lead to computer meltdown!} 15 | 16 | \item{expand}{For large graphs which must be broken into chunks, this factor 17 | determines the relative overlap between chunks to ensure all cycles are 18 | captured. (This value should only need to be modified in special cases.)} 19 | } 20 | \value{ 21 | An \code{sf::sfc} collection of \code{POLYGON} objects. 22 | } 23 | \description{ 24 | Convert \pkg{sf} \code{LINESTRING} objects to \code{POLYGON} objects representing all 25 | fundamental cycles within the \code{LINESTRING} objects. 26 | } 27 | \examples{ 28 | \dontrun{ 29 | p <- dodgr_sflines_to_poly (hampi) 30 | } 31 | } 32 | \seealso{ 33 | Other misc: 34 | \code{\link{compare_heaps}()}, 35 | \code{\link{dodgr_flowmap}()}, 36 | \code{\link{dodgr_full_cycles}()}, 37 | \code{\link{dodgr_fundamental_cycles}()}, 38 | \code{\link{dodgr_insert_vertex}()}, 39 | \code{\link{dodgr_sample}()}, 40 | \code{\link{dodgr_vertices}()}, 41 | \code{\link{merge_directed_graph}()}, 42 | \code{\link{summary.dodgr_dists_categorical}()}, 43 | \code{\link{write_dodgr_wt_profile}()} 44 | } 45 | \concept{misc} 46 | -------------------------------------------------------------------------------- /man/compare_heaps.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/compare-heaps.R 3 | \name{compare_heaps} 4 | \alias{compare_heaps} 5 | \title{Compare timings of different sort heaps for a given input graph.} 6 | \usage{ 7 | compare_heaps(graph, nverts = 100, replications = 2) 8 | } 9 | \arguments{ 10 | \item{graph}{\code{data.frame} object representing the network graph (or a 11 | sub-sample selected with \code{dodgr_sample})} 12 | 13 | \item{nverts}{Number of vertices used to generate random sub-graph. If a 14 | non-numeric value is given, the whole graph will be used.} 15 | 16 | \item{replications}{Number of replications to be used in comparison} 17 | } 18 | \value{ 19 | Result of \code{bench::mark} comparison. 20 | } 21 | \description{ 22 | Perform timing comparison between different kinds of heaps as well as with 23 | equivalent routines from the \pkg{igraph} package. To do this, a random 24 | sub-graph containing a defined number of vertices is first selected. 25 | Alternatively, this random sub-graph can be pre-generated with the 26 | \code{dodgr_sample} function and passed directly. 27 | } 28 | \examples{ 29 | graph <- weight_streetnet (hampi) 30 | \dontrun{ 31 | compare_heaps (graph, nverts = 1000, replications = 1) 32 | } 33 | } 34 | \seealso{ 35 | Other misc: 36 | \code{\link{dodgr_flowmap}()}, 37 | \code{\link{dodgr_full_cycles}()}, 38 | \code{\link{dodgr_fundamental_cycles}()}, 39 | \code{\link{dodgr_insert_vertex}()}, 40 | \code{\link{dodgr_sample}()}, 41 | \code{\link{dodgr_sflines_to_poly}()}, 42 | \code{\link{dodgr_vertices}()}, 43 | \code{\link{merge_directed_graph}()}, 44 | \code{\link{summary.dodgr_dists_categorical}()}, 45 | \code{\link{write_dodgr_wt_profile}()} 46 | } 47 | \concept{misc} 48 | -------------------------------------------------------------------------------- /makefile: -------------------------------------------------------------------------------- 1 | RFILE = README 2 | VIGNETTE = iso 3 | 4 | all: help 5 | 6 | doc: ## Update package documentation with `roxygen2` 7 | Rscript -e 'roxygen2::roxygenise()' 8 | 9 | init: ## Initialize pkgdown site 10 | echo "pkgdown::init_site()" | R --no-save -q 11 | 12 | pkgdown: ## Build entire pkgdown site 13 | echo "pkgdown::build_site()" | R --no-save -q 14 | 15 | vignette: ## Build pkgdown article 16 | echo "pkgdown::build_article('$(VIGNETTE)',quiet=FALSE)" | R --no-save -q 17 | 18 | knith: $(LFILE).Rmd ## Render README as HTML 19 | echo "rmarkdown::render('$(LFILE).Rmd',output_file='$(LFILE).html')" | R --no-save -q 20 | 21 | knitr: $(LFILE).Rmd ## Render README as markdown 22 | echo "rmarkdown::render('$(LFILE).Rmd',output_file='$(LFILE).md')" | R --no-save -q 23 | 24 | open: ## Open main HTML vignette in browser 25 | xdg-open docs/articles/$(VIGNETTE).html & 26 | 27 | allcon: ## Run 'allcontributors::add_contributors' 28 | Rscript -e 'allcontributors::add_contributors(ncols = 6L, check_urls = FALSE)' 29 | 30 | check: ## Run `rcmdcheck` 31 | Rscript -e 'rcmdcheck::rcmdcheck()' 32 | 33 | test: ## Run test suite 34 | Rscript -e 'testthat::test_local()' 35 | 36 | pkgcheck: ## Run `pkgcheck` and print results to screen. 37 | Rscript -e 'library(pkgcheck); checks <- pkgcheck(); print(checks); summary (checks)' 38 | 39 | data: ## Run 'data-raw/release-data-script' to (re-)generate release data 40 | date; time Rscript "data-raw/release-data-script.R" 41 | 42 | clean: ## Clean all junk files, including all pkgdown docs 43 | rm -rf *.html *.png README_cache docs/ 44 | 45 | help: ## Show this help 46 | @printf "Usage:\033[36m make [target]\033[0m\n" 47 | @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}' 48 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to dodgr 2 | 3 | ## Opening issues 4 | 5 | The easiest way to note any behavioural curiosities or to request any new 6 | features is by opening a [github issue](https://github.com/UrbanAnalyst/dodgr/issues). 7 | 8 | 9 | ## Development guidelines 10 | 11 | If you'd like to contribute changes to `dodgr`, we use [the GitHub 12 | flow](https://guides.github.com/introduction/flow/index.html) for proposing, 13 | submitting, reviewing, and accepting changes. If you haven't done this before, 14 | there's a nice overview of git [here](https://r-pkgs.org/git.html), as well 15 | as best practices for submitting pull requests 16 | [here](https://r-pkgs.org/git.html#pr-make). 17 | 18 | The `dodgr` coding style diverges somewhat from [this commonly used R style 19 | guide](http://adv-r.had.co.nz/Style.html), primarily in the following two ways, 20 | both of which improve code readability: (1) All curly braces are vertically aligned: 21 | ```r 22 | this <- function () 23 | { 24 | x <- 1 25 | } 26 | ``` 27 | and **not** 28 | ```r 29 | this <- function(){ 30 | x <- 1 31 | } 32 | ``` 33 | and (2) Also highlighted in that code is the additional whitespace which 34 | permeates `dodgr` code. Words of text are separated by whitespace, and so 35 | code words should be too: 36 | ```r 37 | this <- function1 (function2 (x)) 38 | ``` 39 | and **not** 40 | ```r 41 | this <- function1(function2(x)) 42 | ``` 43 | with the natural result that one ends up writing 44 | ```r 45 | this <- function () 46 | ``` 47 | with a space between `function` and `()`. That's it. 48 | 49 | 50 | ## Code of Conduct 51 | 52 | We want to encourage a warm, welcoming, and safe environment for contributing to 53 | this project. See the 54 | [code of conduct](https://github.com/UrbanAnalyst/dodgr/blob/master/CONDUCT.md) for 55 | more information. 56 | -------------------------------------------------------------------------------- /man/estimate_centrality_threshold.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/centrality.R 3 | \name{estimate_centrality_threshold} 4 | \alias{estimate_centrality_threshold} 5 | \title{Estimate a value for the 'dist_threshold' parameter of the 6 | \link{dodgr_centrality} function.} 7 | \usage{ 8 | estimate_centrality_threshold(graph, tolerance = 0.001) 9 | } 10 | \arguments{ 11 | \item{graph}{'data.frame' or equivalent object representing the network 12 | graph (see Details)} 13 | 14 | \item{tolerance}{Desired maximal degree of inaccuracy in centrality estimates 15 | \itemize{ 16 | \item values will be accurate to within this amount, subject to a constant 17 | scaling factor. Note that threshold values increase non-linearly with 18 | decreasing values of 'tolerance' 19 | }} 20 | } 21 | \value{ 22 | A single value for 'dist_threshold' giving the required tolerance. 23 | } 24 | \description{ 25 | Providing distance thresholds to this 26 | function generally provides considerably speed gains, and results in 27 | approximations of centrality. This function enables the determination of 28 | values of 'dist_threshold' corresponding to specific degrees of accuracy. 29 | } 30 | \note{ 31 | This function may take some time to execute. While running, it displays 32 | ongoing information on screen of estimated values of 'dist_threshold' and 33 | associated errors. Thresholds are progressively increased until the error is 34 | reduced below the specified tolerance. 35 | } 36 | \examples{ 37 | # No threshold estimation possible on this small example graph: 38 | graph <- weight_streetnet (hampi, wt_profile = "foot") 39 | estimate_centrality_threshold (graph) 40 | } 41 | \seealso{ 42 | Other centrality: 43 | \code{\link{dodgr_centrality}()}, 44 | \code{\link{estimate_centrality_time}()} 45 | } 46 | \concept{centrality} 47 | -------------------------------------------------------------------------------- /tests/testthat/test-graph-conversion.R: -------------------------------------------------------------------------------- 1 | context ("dodgr graph conversion") 2 | 3 | test_that ("igraph", { 4 | graph <- weight_streetnet (hampi) 5 | graph_i <- dodgr_to_igraph (graph) 6 | expect_equal (nrow (graph), igraph::ecount (graph_i)) 7 | 8 | nms <- names (igraph::edge.attributes (graph_i)) 9 | expect_lt (length (nms), ncol (graph)) 10 | expect_true ("highway" %in% nms) 11 | 12 | graph1 <- graph [, -match ("highway", names (graph))] 13 | graph_i1 <- dodgr_to_igraph (graph1) 14 | nms1 <- names (igraph::edge.attributes (graph_i1)) 15 | expect_lt (length (nms1), length (nms)) 16 | expect_false ("highway" %in% nms1) 17 | 18 | graph2 <- igraph_to_dodgr (graph_i) 19 | expect_true (!identical (graph, graph2)) 20 | expect_equal (nrow (graph), nrow (graph2)) 21 | 22 | expect_error ( 23 | graph_i <- dodgr_to_igraph (graph, 24 | weight_column = "does_not_exist" 25 | ), 26 | "graph contains no column named 'does_not_exist'" 27 | ) 28 | 29 | graph$from_id <- graph$to_id <- NULL 30 | expect_silent (graph_i <- dodgr_to_igraph (graph)) 31 | }) 32 | 33 | test_that ("tidyraph", { 34 | graph <- weight_streetnet (hampi) 35 | graph_t <- dodgr_to_tidygraph (graph) 36 | expect_equal (nrow (graph), igraph::ecount (graph_t)) 37 | }) 38 | 39 | test_that ("sf", { 40 | graph <- weight_streetnet (hampi) 41 | gsfc <- dodgr_to_sfc (graph) 42 | expect_is (gsfc, "list") 43 | expect_equal (length (gsfc), 2) 44 | 45 | gsf1 <- dodgr_to_sf (graph) 46 | gsf2 <- sf::st_sf (gsfc$dat, geometry = gsfc$geometry, crs = 4326) 47 | expect_identical (gsf1, gsf2) 48 | 49 | gc <- dodgr_contract_graph (graph) 50 | expect_error ( 51 | suppressWarnings (gsf <- dodgr_to_sf (gc)), 52 | "Graph has already been contracted" 53 | ) 54 | }) 55 | -------------------------------------------------------------------------------- /tests/testthat/test-paths.R: -------------------------------------------------------------------------------- 1 | context ("dodgr_paths") 2 | 3 | test_that ("paths", { 4 | graph <- weight_streetnet (hampi) 5 | from <- graph$from_id [1:100] 6 | to <- graph$to_id [100:150] 7 | to <- to [!to %in% from] 8 | expect_message ( 9 | dp <- dodgr_paths (graph, 10 | from = from, 11 | to = to, 12 | quiet = FALSE 13 | ), 14 | "Calculating shortest paths ..." 15 | ) 16 | expect_is (dp, "list") 17 | expect_equal (length (dp), 100) 18 | expect_equal (unique (sapply (dp, length)), length (to)) 19 | expect_is (dp [[1]] [[1]], "character") 20 | lens <- unlist (lapply (dp, function (i) lapply (i, length))) 21 | dp <- dodgr_paths (graph, from = from, to = to, vertices = FALSE) 22 | expect_is (dp, "list") 23 | expect_equal (length (dp), 100) 24 | expect_equal (unique (sapply (dp, length)), length (to)) 25 | lens2 <- unlist (lapply (dp, function (i) lapply (i, length))) 26 | # edge lists should all have one less item than vertex lists 27 | lens2 <- lens2 [which (lens > 0)] 28 | lens <- lens [which (lens > 0)] 29 | expect_true (all (abs (lens - lens2) == 1)) 30 | }) 31 | 32 | test_that ("pairwise paths", { 33 | graph <- weight_streetnet (hampi) 34 | from <- graph$from_id [1:10] 35 | to <- graph$to_id [100:105] 36 | indx <- which (!to %in% from) 37 | to <- to [indx] 38 | from <- from [indx] 39 | n <- length (indx) 40 | dp <- dodgr_paths (graph, from = from, to = to, pairwise = TRUE) 41 | expect_is (dp, "list") 42 | expect_equal (length (dp), n) 43 | expect_true (all (lapply (dp, length) == 1)) 44 | 45 | expect_error ( 46 | dp <- dodgr_paths (graph, 47 | from = from, to = to [-1], 48 | pairwise = TRUE 49 | ), 50 | "pairwise paths require from and to to have same length" 51 | ) 52 | }) 53 | -------------------------------------------------------------------------------- /src/flows.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include // std::fill, std::reverse 6 | #include 7 | #include 8 | 9 | #include 10 | // [[Rcpp::plugins(cpp11)]] 11 | // [[Rcpp::depends(RcppParallel,RcppThread)]] 12 | #include 13 | #include 14 | 15 | #include "pathfinders.h" 16 | 17 | class DGraph; 18 | class PathFinder; 19 | 20 | //---------------------------- 21 | //----- functions in flows.cpp 22 | //---------------------------- 23 | 24 | Rcpp::NumericVector rcpp_flows_aggregate_par (const Rcpp::DataFrame graph, 25 | const Rcpp::DataFrame vert_map_in, 26 | Rcpp::IntegerVector fromi, 27 | Rcpp::IntegerVector toi_in, 28 | Rcpp::NumericMatrix flows, 29 | const bool norm_sums, 30 | const double tol, 31 | const std::string heap_type); 32 | 33 | Rcpp::NumericVector rcpp_flows_aggregate_pairwise (const Rcpp::DataFrame graph, 34 | const Rcpp::DataFrame vert_map_in, 35 | Rcpp::IntegerVector fromi, 36 | Rcpp::IntegerVector toi, 37 | Rcpp::NumericVector flows, 38 | const bool norm_sums, 39 | const double tol, 40 | const std::string heap_type); 41 | 42 | Rcpp::NumericVector rcpp_flows_disperse_par (const Rcpp::DataFrame graph, 43 | const Rcpp::DataFrame vert_map_in, 44 | Rcpp::IntegerVector fromi, 45 | Rcpp::NumericVector k, 46 | Rcpp::NumericVector dens, 47 | const double &tol, 48 | std::string heap_type); 49 | 50 | Rcpp::NumericVector rcpp_flows_si (const Rcpp::DataFrame graph, 51 | const Rcpp::DataFrame vert_map_in, 52 | Rcpp::IntegerVector fromi, 53 | Rcpp::IntegerVector toi_in, 54 | Rcpp::NumericVector kvec, 55 | Rcpp::NumericVector dens_from, 56 | Rcpp::NumericVector dens_to, 57 | const bool norm_sums, 58 | const double tol, 59 | const std::string heap_type); 60 | -------------------------------------------------------------------------------- /man/match_points_to_verts.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/match-points.R 3 | \name{match_points_to_verts} 4 | \alias{match_points_to_verts} 5 | \title{Alias for \link{match_pts_to_verts}} 6 | \usage{ 7 | match_points_to_verts(verts, xy, connected = FALSE) 8 | } 9 | \arguments{ 10 | \item{verts}{A \code{data.frame} of vertices obtained from 11 | \code{dodgr_vertices(graph)}.} 12 | 13 | \item{xy}{coordinates of points to be matched to the vertices, either as 14 | matrix or \pkg{sf}-formatted \code{data.frame}.} 15 | 16 | \item{connected}{Should points be matched to the same (largest) connected 17 | component of graph? If \code{FALSE} and these points are to be used for a 18 | \code{dodgr} routing routine (\link{dodgr_dists}, \link{dodgr_paths}, or 19 | \link{dodgr_flows_aggregate}), then results may not be returned if points are 20 | not part of the same connected component. On the other hand, forcing them to 21 | be part of the same connected component may decrease the spatial accuracy of 22 | matching.} 23 | } 24 | \value{ 25 | A vector index into verts 26 | } 27 | \description{ 28 | The \link{match_pts_to_graph} function matches points to the nearest edge 29 | based on geometric intersections; this function only matches to the nearest 30 | vertex based on point-to-point distances. 31 | } 32 | \examples{ 33 | net <- weight_streetnet (hampi, wt_profile = "foot") 34 | verts <- dodgr_vertices (net) 35 | # Then generate some random points to match to graph 36 | npts <- 10 37 | xy <- data.frame ( 38 | x = min (verts$x) + runif (npts) * diff (range (verts$x)), 39 | y = min (verts$y) + runif (npts) * diff (range (verts$y)) 40 | ) 41 | pts <- match_pts_to_verts (verts, xy) 42 | pts # an index into verts 43 | pts <- verts$id [pts] 44 | pts # names of those vertices 45 | } 46 | \seealso{ 47 | Other match: 48 | \code{\link{add_nodes_to_graph}()}, 49 | \code{\link{match_points_to_graph}()}, 50 | \code{\link{match_pts_to_graph}()}, 51 | \code{\link{match_pts_to_verts}()} 52 | } 53 | \concept{match} 54 | -------------------------------------------------------------------------------- /man/match_pts_to_verts.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/match-points.R 3 | \name{match_pts_to_verts} 4 | \alias{match_pts_to_verts} 5 | \title{Match spatial points to the vertices of a spatial graph.} 6 | \usage{ 7 | match_pts_to_verts(verts, xy, connected = FALSE) 8 | } 9 | \arguments{ 10 | \item{verts}{A \code{data.frame} of vertices obtained from 11 | \code{dodgr_vertices(graph)}.} 12 | 13 | \item{xy}{coordinates of points to be matched to the vertices, either as 14 | matrix or \pkg{sf}-formatted \code{data.frame}.} 15 | 16 | \item{connected}{Should points be matched to the same (largest) connected 17 | component of graph? If \code{FALSE} and these points are to be used for a 18 | \code{dodgr} routing routine (\link{dodgr_dists}, \link{dodgr_paths}, or 19 | \link{dodgr_flows_aggregate}), then results may not be returned if points are 20 | not part of the same connected component. On the other hand, forcing them to 21 | be part of the same connected component may decrease the spatial accuracy of 22 | matching.} 23 | } 24 | \value{ 25 | A vector index into verts 26 | } 27 | \description{ 28 | The \link{match_pts_to_graph} function matches points to the nearest edge 29 | based on geometric intersections; this function only matches to the nearest 30 | vertex based on point-to-point distances. 31 | } 32 | \examples{ 33 | net <- weight_streetnet (hampi, wt_profile = "foot") 34 | verts <- dodgr_vertices (net) 35 | # Then generate some random points to match to graph 36 | npts <- 10 37 | xy <- data.frame ( 38 | x = min (verts$x) + runif (npts) * diff (range (verts$x)), 39 | y = min (verts$y) + runif (npts) * diff (range (verts$y)) 40 | ) 41 | pts <- match_pts_to_verts (verts, xy) 42 | pts # an index into verts 43 | pts <- verts$id [pts] 44 | pts # names of those vertices 45 | } 46 | \seealso{ 47 | Other match: 48 | \code{\link{add_nodes_to_graph}()}, 49 | \code{\link{match_points_to_graph}()}, 50 | \code{\link{match_points_to_verts}()}, 51 | \code{\link{match_pts_to_graph}()} 52 | } 53 | \concept{match} 54 | -------------------------------------------------------------------------------- /man/dodgr_save_streetnet.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/save_load_streetnet.R 3 | \name{dodgr_save_streetnet} 4 | \alias{dodgr_save_streetnet} 5 | \title{Save a weighted streetnet to a local file} 6 | \usage{ 7 | dodgr_save_streetnet(net, filename = NULL) 8 | } 9 | \arguments{ 10 | \item{net}{\code{data.frame} or equivalent object representing the weighted 11 | network graph.} 12 | 13 | \item{filename}{Name with optional full path of file in which to save the 14 | input \code{net}. The extension \code{.Rds} will be automatically appended, unless 15 | specified otherwise.} 16 | } 17 | \value{ 18 | Nothing; function called for side-effect of saving network. 19 | } 20 | \description{ 21 | The \link{weight_streetnet} function returns a single \code{data.frame} object, 22 | the processing of which also relies on a couple of cached lookup-tables to 23 | match edges in the \code{data.frame} to objects in the original input data. It 24 | automatically calculates and caches a contracted version of the same graph, 25 | to enable rapid conversion between contracted and uncontracted forms. This 26 | function saves all of these items in a single \code{.Rds} file, so that a the 27 | result of a \link{weight_streetnet} call can be rapidly loaded into a 28 | workspace in subsequent sessions, rather than re-calculating the entire 29 | weighted network. 30 | } 31 | \note{ 32 | This may take some time if \link{dodgr_cache_off} has been called. 33 | The contracted version of the graph is also saved, and so must be calculated 34 | if it has not previously been automatically cached. 35 | } 36 | \examples{ 37 | net <- weight_streetnet (hampi) 38 | f <- file.path (tempdir (), "streetnet.Rds") 39 | dodgr_save_streetnet (net, f) 40 | clear_dodgr_cache () # rm cached objects from tempdir 41 | # at some later time, or in a new R session: 42 | net <- dodgr_load_streetnet (f) 43 | } 44 | \seealso{ 45 | Other cache: 46 | \code{\link{clear_dodgr_cache}()}, 47 | \code{\link{dodgr_cache_off}()}, 48 | \code{\link{dodgr_cache_on}()}, 49 | \code{\link{dodgr_load_streetnet}()} 50 | } 51 | \concept{cache} 52 | -------------------------------------------------------------------------------- /man/dodgr_flowmap.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/flowmap.R 3 | \name{dodgr_flowmap} 4 | \alias{dodgr_flowmap} 5 | \title{Create a map of \code{dodgr} flows.} 6 | \usage{ 7 | dodgr_flowmap(net, bbox = NULL, linescale = 1) 8 | } 9 | \arguments{ 10 | \item{net}{A street network with a \code{flow} column obtained from 11 | \link{dodgr_flows_aggregate} or \link{dodgr_flows_disperse}} 12 | 13 | \item{bbox}{If given, scale the map to this bbox, otherwise use entire extend 14 | of \code{net}} 15 | 16 | \item{linescale}{Maximal thickness of plotted lines} 17 | } 18 | \value{ 19 | Nothing; called for side-effect of producing plot. 20 | } 21 | \description{ 22 | Create a map of the output of \link{dodgr_flows_aggregate} or 23 | \link{dodgr_flows_disperse} 24 | } 25 | \note{ 26 | \code{net} should be first passed through \code{merge_directed_graph} 27 | prior to plotting, otherwise lines for different directions will be overlaid. 28 | } 29 | \examples{ 30 | graph <- weight_streetnet (hampi) 31 | from <- sample (graph$from_id, size = 10) 32 | to <- sample (graph$to_id, size = 5) 33 | to <- to [!to \%in\% from] 34 | flows <- matrix ( 35 | 10 * runif (length (from) * length (to)), 36 | nrow = length (from) 37 | ) 38 | graph <- dodgr_flows_aggregate (graph, from = from, to = to, flows = flows) 39 | # graph then has an additonal 'flows` column of aggregate flows along all 40 | # edges. These flows are directed, and can be aggregated to equivalent 41 | # undirected flows on an equivalent undirected graph with: 42 | graph_undir <- merge_directed_graph (graph) 43 | \dontrun{ 44 | dodgr_flowmap (graph_undir) 45 | } 46 | } 47 | \seealso{ 48 | Other misc: 49 | \code{\link{compare_heaps}()}, 50 | \code{\link{dodgr_full_cycles}()}, 51 | \code{\link{dodgr_fundamental_cycles}()}, 52 | \code{\link{dodgr_insert_vertex}()}, 53 | \code{\link{dodgr_sample}()}, 54 | \code{\link{dodgr_sflines_to_poly}()}, 55 | \code{\link{dodgr_vertices}()}, 56 | \code{\link{merge_directed_graph}()}, 57 | \code{\link{summary.dodgr_dists_categorical}()}, 58 | \code{\link{write_dodgr_wt_profile}()} 59 | } 60 | \concept{misc} 61 | -------------------------------------------------------------------------------- /man/dodgr_to_sfc.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/graph-conversion.R 3 | \name{dodgr_to_sfc} 4 | \alias{dodgr_to_sfc} 5 | \title{Convert a \code{dodgr} graph into an equivalent \code{sf::sfc} object.} 6 | \usage{ 7 | dodgr_to_sfc(graph) 8 | } 9 | \arguments{ 10 | \item{graph}{A \code{dodgr} graph} 11 | } 12 | \value{ 13 | A list containing (1) A \code{data.frame} of data associated with the 14 | \code{sf} geometries; and (ii) A Simple Features Collection (\code{sfc}) list of 15 | \code{LINESTRING} objects. 16 | } 17 | \description{ 18 | Convert a \code{dodgr} graph into a \code{list} composed of 19 | two objects: \code{dat}, a \code{data.frame}; and 20 | \code{geometry}, an \code{sfc} object from the (\pkg{sf}) package. 21 | Works by aggregating edges into \code{LINESTRING} 22 | objects representing longest sequences between all junction nodes. The 23 | resultant objects will generally contain more \code{LINESTRING} objects than 24 | the original \pkg{sf} object, because the former will be bisected at every 25 | junction point. 26 | } 27 | \note{ 28 | The output of this function corresponds to the edges obtained from 29 | \code{dodgr_contract_graph}. This function does not require the \pkg{sf} package 30 | to be installed; the corresponding function that creates a full \pkg{sf} 31 | object - \link{dodgr_to_sf} does requires \pkg{sf} to be installed. 32 | } 33 | \examples{ 34 | hw <- weight_streetnet (hampi) 35 | nrow (hw) 36 | xy <- dodgr_to_sfc (hw) 37 | dim (hw) # 5.845 edges 38 | length (xy$geometry) # more linestrings aggregated from those edges 39 | nrow (hampi) # than the 191 linestrings in original sf object 40 | dim (xy$dat) # same number of rows as there are geometries 41 | # The dodgr_to_sf function then just implements this final conversion: 42 | # sf::st_sf (xy$dat, geometry = xy$geometry, crs = 4326) 43 | } 44 | \seealso{ 45 | Other conversion: 46 | \code{\link{dodgr_deduplicate_graph}()}, 47 | \code{\link{dodgr_to_igraph}()}, 48 | \code{\link{dodgr_to_sf}()}, 49 | \code{\link{dodgr_to_tidygraph}()}, 50 | \code{\link{igraph_to_dodgr}()} 51 | } 52 | \concept{conversion} 53 | -------------------------------------------------------------------------------- /man/dodgr_uncontract_graph.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/graph-contraction.R 3 | \name{dodgr_uncontract_graph} 4 | \alias{dodgr_uncontract_graph} 5 | \title{Re-expand a contracted graph.} 6 | \usage{ 7 | dodgr_uncontract_graph(graph) 8 | } 9 | \arguments{ 10 | \item{graph}{A contracted graph created from \link{dodgr_contract_graph}.} 11 | } 12 | \value{ 13 | A single \code{data.frame} representing the equivalent original, 14 | uncontracted graph. 15 | } 16 | \description{ 17 | Revert a contracted graph created with \link{dodgr_contract_graph} back to 18 | a full, uncontracted version. This function is mostly used for the side 19 | effect of mapping any new columns inserted on to the contracted graph back 20 | on to the original graph, as demonstrated in the example. 21 | } 22 | \details{ 23 | Note that this function will generally \emph{not} recover original graphs 24 | submitted to \link{dodgr_contract_graph}. Specifically, the sequence 25 | \code{dodgr_contract_graph(graph) |> dodgr_uncontract_graph()} will generally 26 | produce a graph with fewer edges than the original. This is because graphs 27 | may have multiple paths between a given pair of points. Contraction will 28 | reduce these to the single path with the shortest weighted distance (or 29 | time), and uncontraction will restore only that single edge with shortest 30 | weighted distance, and not any original edges which may have had longer 31 | weighted distances. 32 | } 33 | \examples{ 34 | graph0 <- weight_streetnet (hampi) 35 | nrow (graph0) # 6,813 36 | graph1 <- dodgr_contract_graph (graph0) 37 | nrow (graph1) # 760 38 | graph2 <- dodgr_uncontract_graph (graph1) 39 | nrow (graph2) # 6,813 40 | 41 | # Insert new data on to the contracted graph and uncontract it: 42 | graph1$new_col <- runif (nrow (graph1)) 43 | graph3 <- dodgr_uncontract_graph (graph1) 44 | # graph3 is then the uncontracted graph which includes "new_col" as well 45 | dim (graph0) 46 | dim (graph3) 47 | } 48 | \seealso{ 49 | Other modification: 50 | \code{\link{dodgr_components}()}, 51 | \code{\link{dodgr_contract_graph}()} 52 | } 53 | \concept{modification} 54 | -------------------------------------------------------------------------------- /man/dodgr_insert_vertex.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/graph-functions.R 3 | \name{dodgr_insert_vertex} 4 | \alias{dodgr_insert_vertex} 5 | \title{Insert a new node or vertex into a network} 6 | \usage{ 7 | dodgr_insert_vertex(graph, v1, v2, x = NULL, y = NULL) 8 | } 9 | \arguments{ 10 | \item{graph}{A flat table of graph edges. Must contain columns labelled 11 | \code{from} and \code{to}, or \code{start} and \code{stop}. May also contain 12 | similarly labelled columns of spatial coordinates (for example 13 | \code{from_x}) or \code{stop_lon}).} 14 | 15 | \item{v1}{Vertex defining start of graph edge along which new vertex is to be 16 | inserted} 17 | 18 | \item{v2}{Vertex defining end of graph edge along which new vertex is to be 19 | inserted (order of \code{v1} and \code{v2} is not important).} 20 | 21 | \item{x}{The \code{x}-coordinate of new vertex. If not specified, vertex is 22 | created half-way between \code{v1} and \code{v2}.} 23 | 24 | \item{y}{The \code{y}-coordinate of new vertex. If not specified, vertex is 25 | created half-way between \code{v1} and \code{v2}.} 26 | } 27 | \value{ 28 | A modified graph with specified edge between defined start and end 29 | vertices split into two edges either side of new vertex. 30 | } 31 | \description{ 32 | Insert a new node or vertex into a network 33 | } 34 | \examples{ 35 | graph <- weight_streetnet (hampi) 36 | e1 <- sample (nrow (graph), 1) 37 | v1 <- graph$from_id [e1] 38 | v2 <- graph$to_id [e1] 39 | # insert new vertex in the middle of that randomly-selected edge: 40 | graph2 <- dodgr_insert_vertex (graph, v1, v2) 41 | nrow (graph) 42 | nrow (graph2) # new edges added to graph2 43 | } 44 | \seealso{ 45 | Other misc: 46 | \code{\link{compare_heaps}()}, 47 | \code{\link{dodgr_flowmap}()}, 48 | \code{\link{dodgr_full_cycles}()}, 49 | \code{\link{dodgr_fundamental_cycles}()}, 50 | \code{\link{dodgr_sample}()}, 51 | \code{\link{dodgr_sflines_to_poly}()}, 52 | \code{\link{dodgr_vertices}()}, 53 | \code{\link{merge_directed_graph}()}, 54 | \code{\link{summary.dodgr_dists_categorical}()}, 55 | \code{\link{write_dodgr_wt_profile}()} 56 | } 57 | \concept{misc} 58 | -------------------------------------------------------------------------------- /man/weight_railway.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/weight-railway.R 3 | \name{weight_railway} 4 | \alias{weight_railway} 5 | \title{Weight a network for routing along railways.} 6 | \usage{ 7 | weight_railway( 8 | x, 9 | type_col = "railway", 10 | id_col = "osm_id", 11 | keep_cols = c("maxspeed"), 12 | excluded = c("abandoned", "disused", "proposed", "razed") 13 | ) 14 | } 15 | \arguments{ 16 | \item{x}{A street network represented either as \code{sf} \code{LINESTRING} 17 | objects, typically extracted with \link{dodgr_streetnet}.} 18 | 19 | \item{type_col}{Specify column of the \code{sf} \code{data.frame} object 20 | which designates different types of railways to be used for weighting 21 | (default works with \code{osmdata} objects).} 22 | 23 | \item{id_col}{Specify column of the \pkg{sf} \code{data.frame} object which 24 | provides unique identifiers for each railway (default works with 25 | \code{osmdata} objects).} 26 | 27 | \item{keep_cols}{Vectors of columns from \code{sf_lines} to be kept in the 28 | resultant \code{dodgr} network; vector can be either names or indices of 29 | desired columns.} 30 | 31 | \item{excluded}{Types of railways to exclude from routing.} 32 | } 33 | \value{ 34 | A \code{data.frame} of edges representing the rail network, along 35 | with a column of graph component numbers. 36 | } 37 | \description{ 38 | Weight (or re-weight) an \code{sf}-formatted OSM street network for routing 39 | along railways. 40 | } 41 | \note{ 42 | Default railway weighting is by distance. Other weighting schemes, such 43 | as by maximum speed, can be implemented simply by modifying the 44 | \code{d_weighted} column returned by this function accordingly. 45 | } 46 | \examples{ 47 | \dontrun{ 48 | # sample railway extraction with the 'osmdata' package 49 | library (osmdata) 50 | dat <- opq ("shinjuku") \%>\% 51 | add_osm_feature (key = "railway") \%>\% 52 | osmdata_sf (quiet = FALSE) 53 | graph <- weight_railway (dat$osm_lines) 54 | } 55 | } 56 | \seealso{ 57 | Other extraction: 58 | \code{\link{dodgr_streetnet}()}, 59 | \code{\link{dodgr_streetnet_geodesic}()}, 60 | \code{\link{dodgr_streetnet_sc}()}, 61 | \code{\link{weight_streetnet}()} 62 | } 63 | \concept{extraction} 64 | -------------------------------------------------------------------------------- /NAMESPACE: -------------------------------------------------------------------------------- 1 | # Generated by roxygen2: do not edit by hand 2 | 3 | S3method(summary,dodgr_dists_categorical) 4 | S3method(weight_streetnet,SC) 5 | S3method(weight_streetnet,default) 6 | S3method(weight_streetnet,sc) 7 | S3method(weight_streetnet,sf) 8 | export("%>%") 9 | export(add_nodes_to_graph) 10 | export(clear_dodgr_cache) 11 | export(compare_heaps) 12 | export(dodgr_cache_off) 13 | export(dodgr_cache_on) 14 | export(dodgr_centrality) 15 | export(dodgr_components) 16 | export(dodgr_contract_graph) 17 | export(dodgr_deduplicate_graph) 18 | export(dodgr_distances) 19 | export(dodgr_dists) 20 | export(dodgr_dists_categorical) 21 | export(dodgr_dists_nearest) 22 | export(dodgr_flowmap) 23 | export(dodgr_flows_aggregate) 24 | export(dodgr_flows_disperse) 25 | export(dodgr_flows_si) 26 | export(dodgr_full_cycles) 27 | export(dodgr_fundamental_cycles) 28 | export(dodgr_insert_vertex) 29 | export(dodgr_isochrones) 30 | export(dodgr_isodists) 31 | export(dodgr_isoverts) 32 | export(dodgr_load_streetnet) 33 | export(dodgr_paths) 34 | export(dodgr_sample) 35 | export(dodgr_save_streetnet) 36 | export(dodgr_sflines_to_poly) 37 | export(dodgr_streetnet) 38 | export(dodgr_streetnet_geodesic) 39 | export(dodgr_streetnet_sc) 40 | export(dodgr_times) 41 | export(dodgr_to_igraph) 42 | export(dodgr_to_sf) 43 | export(dodgr_to_sfc) 44 | export(dodgr_to_tidygraph) 45 | export(dodgr_uncontract_graph) 46 | export(dodgr_vertices) 47 | export(estimate_centrality_threshold) 48 | export(estimate_centrality_time) 49 | export(igraph_to_dodgr) 50 | export(match_points_to_graph) 51 | export(match_points_to_verts) 52 | export(match_pts_to_graph) 53 | export(match_pts_to_verts) 54 | export(merge_directed_graph) 55 | export(weight_railway) 56 | export(weight_streetnet) 57 | export(write_dodgr_wt_profile) 58 | importFrom(Rcpp,evalCpp) 59 | importFrom(RcppParallel,RcppParallelLibs) 60 | importFrom(grDevices,colorRampPalette) 61 | importFrom(graphics,plot) 62 | importFrom(magrittr,"%>%") 63 | importFrom(memoise,memoise) 64 | importFrom(methods,is) 65 | importFrom(osmdata,add_osm_feature) 66 | importFrom(osmdata,getbb) 67 | importFrom(osmdata,opq) 68 | importFrom(osmdata,osm_poly2line) 69 | importFrom(osmdata,osmdata_sf) 70 | importFrom(osmdata,trim_osmdata) 71 | useDynLib(dodgr, .registration = TRUE) 72 | -------------------------------------------------------------------------------- /vignettes/fig2.tex: -------------------------------------------------------------------------------- 1 | \documentclass[tikz,border=5pt]{standalone} 2 | \usepackage[utf8x]{inputenc} 3 | 4 | \begin{document} 5 | 6 | \begin{tikzpicture}[font=\tiny, >=latex, shorten >=5pt, shorten <=5pt] 7 | \tikzstyle{nonode} = [] 8 | \tikzstyle{node_style} = [draw=white, very thick, circle, fill=orange] 9 | \tikzstyle{black_arrow1} = [->, black, line width=1] 10 | \tikzstyle{black_arrow2} = [->, black, line width=2] 11 | \tikzstyle{black_arrow3} = [->, black, line width=3] 12 | \tikzstyle{grey_arrow} = [->, black!50, dashed, line width=1] 13 | 14 | \node[nonode] (nAd) at (0,-0.1) {}; 15 | \node[nonode] (nAl) at (-0.1,0) {}; 16 | \node[nonode] (nBd) at (4,-0.1) {}; 17 | \node[nonode] (nBr) at (4.1,0) {}; 18 | \node[nonode] (nCd) at (4,1.9) {}; 19 | \node[nonode] (nCr) at (4.1,2) {}; 20 | \node[nonode] (nDd) at (0,1.9) {}; 21 | \node[nonode] (nDl) at (-0.1,2) {}; 22 | 23 | \draw[grey_arrow] (nAd) edge [bend left=10] (nBd); 24 | \draw[grey_arrow] (nBd) edge [bend left=10] (nAd); 25 | \draw[grey_arrow] (nBr) edge [bend left=10] (nCr); 26 | \draw[grey_arrow] (nCr) edge [bend left=10] (nBr); 27 | \draw[grey_arrow] (nCd) edge [bend left=10] (nDd); 28 | \draw[grey_arrow] (nDd) edge [bend left=10] (nCd); 29 | \draw[grey_arrow] (nDl) edge [bend right=10] (nAl); 30 | \draw[grey_arrow] (nBd) edge [out=120, in=-40] (nDl); 31 | 32 | \node[node_style] (nA) at (0,0) {A}; 33 | \node[node_style] (nB) at (4,0) {B}; 34 | \node[node_style] (nC) at (4,2) {C}; 35 | \node[node_style] (nD) at (0,2) {D}; 36 | 37 | \draw[black_arrow1] (nA) edge [bend left=10] (nB); 38 | \draw[black_arrow2] (nB) edge [bend left=10] (nA); 39 | \draw[black_arrow1] (nB) edge [bend left=10] (nC); 40 | \draw[black_arrow2] (nC) edge [bend left=10] (nB); 41 | \draw[black_arrow1] (nC) edge [bend left=10] (nD); 42 | \draw[black_arrow2] (nD) edge [bend left=10] (nC); 43 | \draw[black_arrow1] (nD) edge (nA); 44 | 45 | \draw[black_arrow3, shorten <=2pt, shorten >=2pt] (nB) edge [out=140, in=-60] (nD); 46 | \end{tikzpicture} 47 | 48 | \end{document} 49 | -------------------------------------------------------------------------------- /man/dodgr_full_cycles.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/fund-cycles.R 3 | \name{dodgr_full_cycles} 4 | \alias{dodgr_full_cycles} 5 | \title{Calculate fundamental cycles on a FULL (that is, non-contracted) graph.} 6 | \usage{ 7 | dodgr_full_cycles(graph, graph_max_size = 10000, expand = 0.05) 8 | } 9 | \arguments{ 10 | \item{graph}{\code{data.frame} or equivalent object representing the contracted 11 | network graph (see Details).} 12 | 13 | \item{graph_max_size}{Maximum size submitted to the internal C++ routines as 14 | a single chunk. Warning: Increasing this may lead to computer meltdown!} 15 | 16 | \item{expand}{For large graphs which must be broken into chunks, this factor 17 | determines the relative overlap between chunks to ensure all cycles are 18 | captured. (This value should only need to be modified in special cases.)} 19 | } 20 | \value{ 21 | List of cycle paths, in terms of vertex IDs in \code{graph} and, for 22 | spatial graphs, the corresponding coordinates. 23 | } 24 | \description{ 25 | Calculate fundamental cycles on a FULL (that is, non-contracted) graph. 26 | } 27 | \note{ 28 | This function converts the \code{graph} to its contracted form, calculates 29 | the fundamental cycles on that version, and then expands these cycles back 30 | onto the original graph. This is far more computationally efficient than 31 | calculating fundamental cycles on a full (non-contracted) graph. 32 | } 33 | \examples{ 34 | \dontrun{ 35 | net <- weight_streetnet (hampi) 36 | graph <- dodgr_contract_graph (net) 37 | cyc1 <- dodgr_fundamental_cycles (graph) 38 | cyc2 <- dodgr_full_cycles (net) 39 | } 40 | # cyc2 has same number of cycles, but each one is generally longer, through 41 | # including all points intermediate to junctions; cyc1 has cycles composed of 42 | # junction points only. 43 | } 44 | \seealso{ 45 | Other misc: 46 | \code{\link{compare_heaps}()}, 47 | \code{\link{dodgr_flowmap}()}, 48 | \code{\link{dodgr_fundamental_cycles}()}, 49 | \code{\link{dodgr_insert_vertex}()}, 50 | \code{\link{dodgr_sample}()}, 51 | \code{\link{dodgr_sflines_to_poly}()}, 52 | \code{\link{dodgr_vertices}()}, 53 | \code{\link{merge_directed_graph}()}, 54 | \code{\link{summary.dodgr_dists_categorical}()}, 55 | \code{\link{write_dodgr_wt_profile}()} 56 | } 57 | \concept{misc} 58 | -------------------------------------------------------------------------------- /man/os_roads_bristol.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/dodgr-package.R 3 | \docType{data} 4 | \name{os_roads_bristol} 5 | \alias{os_roads_bristol} 6 | \title{Sample street network from Bristol, U.K.} 7 | \format{ 8 | A Simple Features \code{sf} \code{data.frame} representing 9 | motorways in Bristol, UK. 10 | } 11 | \description{ 12 | A sample street network for Bristol, U.K., from the Ordnance Survey. 13 | } 14 | \note{ 15 | Input data downloaded from 16 | \url{https://osdatahub.os.uk/downloads/open}, 17 | To download the data from that page click on the tick box next to 18 | 'OS Open Roads', scroll to the bottom, click 'Continue' and complete 19 | the form on the subsequent page. 20 | This dataset is open access and can be used under 21 | \href{https://www.ordnancesurvey.co.uk/licensing}{these licensing 22 | conditions}, 23 | and must be cited as follows: 24 | Contains OS data © Crown copyright and database right (2017) 25 | } 26 | \examples{ 27 | \dontrun{ 28 | library (sf) 29 | library (dplyr) 30 | # data must be unzipped here 31 | # os_roads <- sf::read_sf("~/data/ST_RoadLink.shp") 32 | # u <- paste0 ( 33 | # "https://opendata.arcgis.com/datasets/", 34 | # "686603e943f948acaa13fb5d2b0f1275_4.kml" 35 | # ) 36 | # lads <- sf::read_sf(u) 37 | # mapview::mapview(lads) 38 | # bristol_pol <- dplyr::filter(lads, grepl("Bristol", lad16nm)) 39 | # os_roads <- st_transform(os_roads, st_crs(lads) 40 | # os_roads_bristol <- os_roads[bristol_pol, ] \%>\% 41 | # dplyr::filter(class == "Motorway" & 42 | # roadNumber != "M32") \%>\% 43 | # st_zm(drop = TRUE) 44 | # mapview::mapview(os_roads_bristol) 45 | } 46 | # Converting this 'sf data.frame' to a 'dodgr' network requires manual 47 | # specification of weighting profile: 48 | colnm <- "formOfWay" # name of column used to determine weights 49 | wts <- data.frame ( 50 | name = "custom", 51 | way = unique (os_roads_bristol [[colnm]]), 52 | value = c (0.1, 0.2, 0.8, 1) 53 | ) 54 | net <- weight_streetnet ( 55 | os_roads_bristol, 56 | wt_profile = wts, 57 | type_col = colnm, id_col = "identifier" 58 | ) 59 | # 'id_col' tells the function which column to use to attribute IDs of ways 60 | } 61 | \seealso{ 62 | Other data: 63 | \code{\link{hampi}}, 64 | \code{\link{weighting_profiles}} 65 | } 66 | \concept{data} 67 | \keyword{datasets} 68 | -------------------------------------------------------------------------------- /tests/testthat/test-cycles.R: -------------------------------------------------------------------------------- 1 | context ("fundamental cycles") 2 | 3 | testthat::skip_on_cran () 4 | 5 | test_that ("dodgr_fundamental_cycles", { 6 | net <- weight_streetnet (hampi) 7 | graph <- dodgr_contract_graph (net) 8 | expect_error ( 9 | x <- dodgr_fundamental_cycles (), 10 | "graph must be provided" 11 | ) 12 | expect_error ( 13 | x <- dodgr_fundamental_cycles (graph = "a"), 14 | "graph must be a data.frame object" 15 | ) 16 | expect_silent (x <- dodgr_fundamental_cycles (graph)) 17 | expect_is (x, "list") 18 | expect_true (length (x) > 1) 19 | }) 20 | 21 | test_that ("cycles_with_max_graph_size", { 22 | net <- weight_streetnet (hampi) 23 | expect_message ( 24 | x <- dodgr_fundamental_cycles ( 25 | graph = net, 26 | graph_max_size = 1000 27 | ), 28 | "Now computing fundamental cycles" 29 | ) 30 | expect_is (x, "list") 31 | expect_length (x, 62) # more cycles than before! 32 | 33 | expect_silent ( 34 | xf <- dodgr_full_cycles ( 35 | graph = net, 36 | graph_max_size = 1000 37 | ) 38 | ) 39 | # full_cycles creates the contracted graph, which is < 1000! 40 | expect_true (length (x) > 1) 41 | }) 42 | 43 | test_that ("sflines_to_poly", { 44 | expect_error ( 45 | p <- dodgr_sflines_to_poly (list (hampi)), 46 | "lines must be an object of class 'sf' or 'sfc'" 47 | ) 48 | h <- hampi 49 | class (h$geometry) <- "list" 50 | expect_error ( 51 | p <- dodgr_sflines_to_poly (h), 52 | "lines must be an 'sfc_LINESTRING' object" 53 | ) 54 | 55 | expect_silent (p <- dodgr_sflines_to_poly (hampi)) 56 | expect_is (hampi$geometry, "sfc_LINESTRING") 57 | expect_is (p, "sfc_POLYGON") 58 | expect_true (length (p) > 50) 59 | 60 | net <- weight_streetnet (hampi, wt_profile = 1) 61 | 62 | net1 <- net [net$component == 1, ] 63 | net1$edge_id <- seq (nrow (net1)) 64 | p1 <- dodgr_full_cycles (net1) 65 | 66 | net2 <- net [net$component == 2, ] 67 | net2$edge_id <- seq (nrow (net2)) 68 | p2 <- dodgr_full_cycles (net2) 69 | 70 | # `dodgr_sflines_to_poly` analyses components seperately, so p1 + 71 | # p2 should give same result: 72 | expect_equal (length (p1) + length (p2), length (p)) 73 | }) 74 | -------------------------------------------------------------------------------- /DESCRIPTION: -------------------------------------------------------------------------------- 1 | Package: dodgr 2 | Title: Distances on Directed Graphs 3 | Version: 0.4.3.016 4 | Authors@R: c( 5 | person("Mark", "Padgham", , "mark.padgham@email.com", role = c("aut", "cre")), 6 | person("Andreas", "Petutschnig", role = "aut"), 7 | person("David", "Cooley", role = "aut"), 8 | person("Robin", "Lovelace", role = "ctb"), 9 | person("Andrew", "Smith", role = "ctb"), 10 | person("Malcolm", "Morgan", role = "ctb"), 11 | person("Andrea", "Gilardi", role="ctb", comment = c(ORCID = "0000-0002-9424-7439")), 12 | person("Eduardo", "Leoni", role="ctb", comment = c(ORCID = "0000-0003-0955-5232")), 13 | person("Shane", "Saunders", role = "cph", 14 | comment = "Original author of included code for priority heaps"), 15 | person("Stanislaw", "Adaszewski", role = "cph", 16 | comment = "author of include concaveman-cpp code") 17 | ) 18 | Description: Distances on dual-weighted directed graphs using 19 | priority-queue shortest paths (Padgham (2019) ). 20 | Weighted directed graphs have weights from A to B which may differ 21 | from those from B to A. Dual-weighted directed graphs have two sets 22 | of such weights. A canonical example is a street network to be used 23 | for routing in which routes are calculated by weighting distances 24 | according to the type of way and mode of transport, yet lengths of 25 | routes must be calculated from direct distances. 26 | License: GPL-3 27 | URL: https://UrbanAnalyst.github.io/dodgr/, 28 | https://github.com/UrbanAnalyst/dodgr 29 | BugReports: https://github.com/UrbanAnalyst/dodgr/issues 30 | Depends: 31 | R (>= 3.5.0) 32 | Imports: 33 | callr, 34 | digest, 35 | fs, 36 | geodist (>= 0.1.0), 37 | magrittr, 38 | memoise, 39 | methods, 40 | osmdata, 41 | Rcpp (>= 0.12.6), 42 | RcppParallel 43 | Suggests: 44 | bench, 45 | dplyr, 46 | ggplot2, 47 | igraph, 48 | igraphdata, 49 | jsonlite, 50 | knitr, 51 | markdown, 52 | rmarkdown, 53 | sf, 54 | testthat (>= 3.1.6), 55 | tidygraph 56 | LinkingTo: 57 | Rcpp, 58 | RcppParallel, 59 | RcppThread 60 | VignetteBuilder: 61 | knitr 62 | Encoding: UTF-8 63 | LazyData: true 64 | NeedsCompilation: yes 65 | Roxygen: list(markdown = TRUE) 66 | RoxygenNote: 7.3.2 67 | SystemRequirements: GNU make 68 | -------------------------------------------------------------------------------- /.github/workflows/test-coverage.yaml: -------------------------------------------------------------------------------- 1 | # Workflow derived from https://github.com/r-lib/actions/tree/v2/examples 2 | # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help 3 | on: 4 | push: 5 | branches: [main, master] 6 | pull_request: 7 | 8 | name: test-coverage.yaml 9 | 10 | permissions: read-all 11 | 12 | jobs: 13 | test-coverage: 14 | runs-on: ubuntu-latest 15 | env: 16 | GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} 17 | 18 | steps: 19 | - uses: actions/checkout@v4 20 | 21 | - uses: r-lib/actions/setup-r@v2 22 | with: 23 | use-public-rspm: true 24 | 25 | - uses: r-lib/actions/setup-r-dependencies@v2 26 | with: 27 | extra-packages: any::covr, any::xml2 28 | needs: coverage 29 | 30 | - name: Test coverage 31 | run: | 32 | cov <- covr::package_coverage( 33 | quiet = FALSE, 34 | clean = FALSE, 35 | install_path = file.path(normalizePath(Sys.getenv("RUNNER_TEMP"), winslash = "/"), "package"), 36 | line_exclusions = list('src/heaps/bheap.cpp','src/heaps/bheap.h','src/heaps/fheap.h','src/heaps/fheap.cpp','src/heaps/heap23.h','src/heaps/heap23.cpp','src/heaps/heap23_2.h','src/heaps/heap.h','src/heaps/heap_lib.h','src/heaps/triheap.h','src/heaps/triheap.cpp','src/heaps/triheap_ext.h','src/heaps/triheap_ext.cpp','src/dgraph.cpp') 37 | ) 38 | print(cov) 39 | covr::to_cobertura(cov) 40 | shell: Rscript {0} 41 | 42 | - uses: codecov/codecov-action@v5 43 | with: 44 | # Fail if error if not on PR, or if on PR and token is given 45 | fail_ci_if_error: ${{ github.event_name != 'pull_request' || secrets.CODECOV_TOKEN }} 46 | files: ./cobertura.xml 47 | plugins: noop 48 | disable_search: true 49 | token: ${{ secrets.CODECOV_TOKEN }} 50 | 51 | - name: Show testthat output 52 | if: always() 53 | run: | 54 | ## -------------------------------------------------------------------- 55 | find '${{ runner.temp }}/package' -name 'testthat.Rout*' -exec cat '{}' \; || true 56 | shell: bash 57 | 58 | - name: Upload test results 59 | if: failure() 60 | uses: actions/upload-artifact@v4 61 | with: 62 | name: coverage-test-failures 63 | path: ${{ runner.temp }}/package 64 | -------------------------------------------------------------------------------- /tests/testthat/helper-sc-conversion-fns.R: -------------------------------------------------------------------------------- 1 | genhash <- function (len = 10) { 2 | paste0 (sample (c (letters, LETTERS, 0:9), size = len), collapse = "") 3 | } 4 | 5 | sf_to_sc <- function (x) { 6 | 7 | pts <- do.call (rbind, x$geometry) 8 | pts <- data.frame ( 9 | "x_" = pts [, 1], 10 | "y_" = pts [, 2], 11 | "vertex_" = rownames (pts), 12 | stringsAsFactors = FALSE 13 | ) 14 | pts <- pts [which (!duplicated (pts)), ] 15 | 16 | edge <- lapply (x$geometry, function (i) { 17 | cbind ( 18 | rownames (i) [1:(nrow (i) - 1)], 19 | rownames (i) [2:nrow (i)] 20 | ) 21 | }) 22 | for (e in seq (edge)) { 23 | edge [[e]] <- cbind (edge [[e]], names (x$geometry) [e]) 24 | } 25 | edge <- data.frame (do.call (rbind, edge), stringsAsFactors = FALSE) 26 | edge$edge_ <- vapply ( 27 | seq (nrow (edge)), function (i) genhash (10), 28 | character (1) 29 | ) 30 | 31 | object_link_edge <- data.frame ( 32 | edge_ = edge$edge_, 33 | object_ = edge$X3, 34 | native_ = TRUE, 35 | stringsAsFactors = FALSE 36 | ) 37 | edge <- data.frame ( 38 | ".vx0" = edge$X1, 39 | ".vx1" = edge$X2, 40 | "edge_" = edge$edge_, 41 | stringsAsFactors = FALSE 42 | ) 43 | 44 | x_no_g <- x 45 | x_no_g$geometry <- NULL 46 | osm_id <- as.character (x_no_g$osm_id) 47 | x_no_g$osm_id <- NULL 48 | for (i in names (x_no_g)) { 49 | x_no_g [[i]] <- as.character (x_no_g [[i]]) 50 | } 51 | x_no_g <- as.list (x_no_g) 52 | x_no_g <- lapply (x_no_g, function (i) { 53 | res <- cbind (osm_id, i) 54 | res [which (!is.na (res [, 2])), , drop = FALSE] 55 | }) 56 | for (i in seq (x_no_g)) { 57 | x_no_g [[i]] <- cbind (x_no_g [[i]], names (x_no_g) [i]) 58 | } 59 | x_no_g <- data.frame (do.call (rbind, x_no_g), 60 | stringsAsFactors = FALSE 61 | ) 62 | object <- data.frame ( 63 | "object_" = x_no_g$osm_id, 64 | key = x_no_g$V3, 65 | value = x_no_g$i, 66 | stringsAsFactors = FALSE 67 | ) 68 | object <- object [order (object$object_), ] 69 | 70 | res <- list ( 71 | nodes = NULL, 72 | object = object, 73 | object_link_edge = object_link_edge, 74 | edge = edge, 75 | vertex = pts 76 | ) 77 | class (res) <- c ("SC", "sc", "osmdata_sc") 78 | return (res) 79 | } 80 | -------------------------------------------------------------------------------- /man/merge_directed_graph.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/graph-merge.R 3 | \name{merge_directed_graph} 4 | \alias{merge_directed_graph} 5 | \title{Merge directed edges into equivalent undirected edges.} 6 | \usage{ 7 | merge_directed_graph(graph, col_names = c("flow")) 8 | } 9 | \arguments{ 10 | \item{graph}{A undirected graph in which directed edges of the input graph 11 | have been merged through aggregation to yield a single, undirected edge 12 | between each pair of vertices.} 13 | 14 | \item{col_names}{Names of columns to be merged through aggregation. Values 15 | for these columns in resultant undirected graph will be aggregated from 16 | directed values.} 17 | } 18 | \value{ 19 | An equivalent graph in which all directed edges have been reduced to 20 | single, undirected edges, and all values of the specified column(s) have been 21 | aggregated across directions to undirected values. 22 | } 23 | \description{ 24 | Merge directed edges into equivalent undirected values by aggregating across 25 | directions. This function is primarily intended to aid visualisation of 26 | directed graphs, particularly visualising the results of the 27 | \link{dodgr_flows_aggregate} and \link{dodgr_flows_disperse} functions, which 28 | return columns of aggregated flows directed along each edge of a graph. 29 | } 30 | \examples{ 31 | graph <- weight_streetnet (hampi) 32 | from <- sample (graph$from_id, size = 10) 33 | to <- sample (graph$to_id, size = 5) 34 | to <- to [!to \%in\% from] 35 | flows <- matrix (10 * runif (length (from) * length (to)), 36 | nrow = length (from) 37 | ) 38 | graph <- dodgr_flows_aggregate (graph, from = from, to = to, flows = flows) 39 | # graph then has an additonal 'flows` column of aggregate flows along all 40 | # edges. These flows are directed, and can be aggregated to equivalent 41 | # undirected flows on an equivalent undirected graph with: 42 | graph_undir <- merge_directed_graph (graph) 43 | # This graph will only include those edges having non-zero flows, and so: 44 | nrow (graph) 45 | nrow (graph_undir) # the latter is much smaller 46 | } 47 | \seealso{ 48 | Other misc: 49 | \code{\link{compare_heaps}()}, 50 | \code{\link{dodgr_flowmap}()}, 51 | \code{\link{dodgr_full_cycles}()}, 52 | \code{\link{dodgr_fundamental_cycles}()}, 53 | \code{\link{dodgr_insert_vertex}()}, 54 | \code{\link{dodgr_sample}()}, 55 | \code{\link{dodgr_sflines_to_poly}()}, 56 | \code{\link{dodgr_vertices}()}, 57 | \code{\link{summary.dodgr_dists_categorical}()}, 58 | \code{\link{write_dodgr_wt_profile}()} 59 | } 60 | \concept{misc} 61 | -------------------------------------------------------------------------------- /src/heaps/heap.h: -------------------------------------------------------------------------------- 1 | #ifndef HEAP_H 2 | #define HEAP_H 3 | /* File heap.h - Abstract Base Class for Heaps 4 | * ---------------------------------------------------------------------------- 5 | * Mark Padgham, adapted from code by Shane Saunders 6 | */ 7 | 8 | #include // runtime_error 9 | 10 | 11 | /* --- Heap --- 12 | * This is an abstract base class from which specific heap classes can be 13 | * derived. Different heaps derived from this abstract base class can be used 14 | * interchangeably by algorithms that were written using the universal 15 | * interface it provides. 16 | * 17 | * This heap stores integer items, and associates with each item a double 18 | * key. Any derived heap heap must provide the following methods: 19 | * 20 | * deleteMin() - removes the item with the minimum key from the heap, and 21 | * returns it. 22 | * insert() - inserts an item 'item' with key 'key' into the heap. 23 | * decreaseKey() - decreases the key of item 'item' to the new value newKey. 24 | * nItems() - returns the number of items currently in the heap. 25 | * nComps() - returns the number of key comparison operations. 26 | * dump() - prints a text representation of the heap to the standard 27 | * output. 28 | */ 29 | class Heap { 30 | public: 31 | virtual ~Heap(){} 32 | virtual size_t deleteMin() = 0; 33 | virtual void insert(size_t item, double key) = 0; 34 | virtual void decreaseKey(size_t item, double newKey) = 0; 35 | virtual size_t nItems() const = 0; 36 | virtual long int nComps() const = 0; 37 | virtual void dump() const = 0; 38 | // MP: implement getmin fn in BHeap only 39 | virtual double getmin() = 0; 40 | }; 41 | 42 | // This is easier than templating, and is only used for a single implementation 43 | // of BHeap for path aggregation and centrality 44 | class HeapInt { 45 | public: 46 | virtual ~HeapInt(){} 47 | virtual size_t deleteMin() = 0; 48 | virtual void insert(size_t item, int key) = 0; 49 | virtual void decreaseKey(size_t item, int newKey) = 0; 50 | virtual size_t nItems() const = 0; 51 | virtual long int nComps() const = 0; 52 | virtual int getmin() = 0; 53 | }; 54 | 55 | class HeapDesc { 56 | public: 57 | virtual ~HeapDesc(){} 58 | virtual Heap *newInstance(size_t n) const = 0; 59 | }; 60 | 61 | template 62 | class HeapD: public HeapDesc { 63 | public: 64 | Heap *newInstance(size_t n) const { return new T(n); } 65 | }; 66 | 67 | 68 | #endif 69 | -------------------------------------------------------------------------------- /man/add_nodes_to_graph.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/match-points.R 3 | \name{add_nodes_to_graph} 4 | \alias{add_nodes_to_graph} 5 | \title{Insert new nodes into a graph, breaking edges at point of nearest 6 | intersection.} 7 | \usage{ 8 | add_nodes_to_graph(graph, xy, dist_tol = 0.000001, intersections_only = FALSE) 9 | } 10 | \arguments{ 11 | \item{graph}{A \code{dodgr} graph with spatial coordinates, such as a 12 | \code{dodgr_streetnet} object.} 13 | 14 | \item{xy}{coordinates of points to be matched to the vertices, either as 15 | matrix or \pkg{sf}-formatted \code{data.frame}.} 16 | 17 | \item{dist_tol}{Only insert new nodes if they are further from existing nodes 18 | than this distance, expressed in units of the distance column of \code{graph}.} 19 | 20 | \item{intersections_only}{If \code{FALSE}} 21 | } 22 | \value{ 23 | A modified version of \code{graph}, with additional edges formed by 24 | breaking previous edges at nearest perpendicular intersections with the 25 | points, \code{xy}. 26 | } 27 | \description{ 28 | Note that this routine presumes graphs to be \code{dodgr_streetnet} object, with 29 | geographical coordinates. 30 | } 31 | \details{ 32 | This inserts new nodes by extending lines from each input point to the edge 33 | with the closest point of perpendicular intersection. That edge is then split 34 | at that point of intersection, creating two new edges (or four for directed 35 | edges). If \code{intersections_only = FALSE} (default), then additional edges are 36 | inserted from those intersection points to the input points. If 37 | \code{intersections_only = TRUE}, then nodes are added by splitting graph edges at 38 | points of nearest perpendicular intersection, without adding additional edges 39 | out to the actual input points. 40 | 41 | In the former case, the properties of those new edges, such as distance and 42 | time weightings, are inherited from the edges which are intersected, and may 43 | need to be manually modified after calling this function. 44 | } 45 | \examples{ 46 | graph <- weight_streetnet (hampi, wt_profile = "foot") 47 | dim (graph) 48 | 49 | verts <- dodgr_vertices (graph) 50 | set.seed (2) 51 | npts <- 10 52 | xy <- data.frame ( 53 | x = min (verts$x) + runif (npts) * diff (range (verts$x)), 54 | y = min (verts$y) + runif (npts) * diff (range (verts$y)) 55 | ) 56 | 57 | graph <- add_nodes_to_graph (graph, xy) 58 | dim (graph) # more edges than original 59 | } 60 | \seealso{ 61 | Other match: 62 | \code{\link{match_points_to_graph}()}, 63 | \code{\link{match_points_to_verts}()}, 64 | \code{\link{match_pts_to_graph}()}, 65 | \code{\link{match_pts_to_verts}()} 66 | } 67 | \concept{match} 68 | -------------------------------------------------------------------------------- /R/flowmap.R: -------------------------------------------------------------------------------- 1 | #' Create a map of `dodgr` flows. 2 | #' 3 | #' Create a map of the output of \link{dodgr_flows_aggregate} or 4 | #' \link{dodgr_flows_disperse} 5 | #' 6 | #' @param net A street network with a `flow` column obtained from 7 | #' \link{dodgr_flows_aggregate} or \link{dodgr_flows_disperse} 8 | #' @param bbox If given, scale the map to this bbox, otherwise use entire extend 9 | #' of `net` 10 | #' @param linescale Maximal thickness of plotted lines 11 | #' @return Nothing; called for side-effect of producing plot. 12 | #' 13 | #' @note `net` should be first passed through `merge_directed_graph` 14 | #' prior to plotting, otherwise lines for different directions will be overlaid. 15 | #' @family misc 16 | #' @export 17 | #' @examples 18 | #' graph <- weight_streetnet (hampi) 19 | #' from <- sample (graph$from_id, size = 10) 20 | #' to <- sample (graph$to_id, size = 5) 21 | #' to <- to [!to %in% from] 22 | #' flows <- matrix ( 23 | #' 10 * runif (length (from) * length (to)), 24 | #' nrow = length (from) 25 | #' ) 26 | #' graph <- dodgr_flows_aggregate (graph, from = from, to = to, flows = flows) 27 | #' # graph then has an additonal 'flows` column of aggregate flows along all 28 | #' # edges. These flows are directed, and can be aggregated to equivalent 29 | #' # undirected flows on an equivalent undirected graph with: 30 | #' graph_undir <- merge_directed_graph (graph) 31 | #' \dontrun{ 32 | #' dodgr_flowmap (graph_undir) 33 | #' } 34 | dodgr_flowmap <- function (net, bbox = NULL, linescale = 1) { 35 | 36 | if (!"flow" %in% names (net)) { 37 | net$flow <- 1 38 | } 39 | gr_cols <- dodgr_graph_cols (net) 40 | names (net) [gr_cols$xfr] <- "from_lon" 41 | names (net) [gr_cols$yfr] <- "from_lat" 42 | names (net) [gr_cols$xto] <- "to_lon" 43 | names (net) [gr_cols$yto] <- "to_lat" 44 | 45 | if (is.null (bbox)) { 46 | bbox <- c ( 47 | min (net$from_lon), min (net$from_lat), 48 | max (net$from_lon), max (net$from_lat) 49 | ) 50 | } 51 | 52 | xlims <- c (bbox [1], bbox [3]) 53 | ylims <- c (bbox [2], bbox [4]) 54 | cols <- colorRampPalette (c ("lawngreen", "red")) (30) 55 | plot (NULL, xlim = xlims, ylim = ylims, xlab = "lon", ylab = "lat") 56 | net <- net [which (net$flow > 0), ] 57 | net$flow <- net$flow / max (net$flow) 58 | ncols <- 30 59 | cols <- colorRampPalette (c ("lawngreen", "red")) (ncols) 60 | cols <- cols [ceiling (net$flow * ncols)] 61 | 62 | # Suppress 'no visible binding' lint messages. 63 | from_lon <- from_lat <- to_lon <- to_lat <- NULL 64 | with (net, segments (from_lon, from_lat, to_lon, to_lat, 65 | col = cols, lwd = linescale * net$flow 66 | )) 67 | } 68 | -------------------------------------------------------------------------------- /src/dodgr-to-sf.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | 6 | const float INFINITE_FLOAT = std::numeric_limits ::max (); 7 | const double INFINITE_DOUBLE = std::numeric_limits ::max (); 8 | const long int INFINITE_INT = std::numeric_limits ::max (); 9 | 10 | const std::string osm_p4s = "+proj=longlat +datum=WGS84 +no_defs"; 11 | 12 | namespace dodgr_sf { 13 | 14 | size_t make_edge_name_set (std::unordered_set &new_edge_name_set, 15 | const Rcpp::CharacterVector &new_edges); 16 | void make_edge_name_vec (const size_t n, 17 | const Rcpp::CharacterVector &new_edges, 18 | std::vector &new_edge_name_vec); 19 | size_t get_edgevec_sizes (const size_t nedges, 20 | const Rcpp::CharacterVector &new_edges, 21 | std::vector &edgevec_sizes); 22 | void get_edge_to_vert_maps (const std::vector &edgevec_sizes, 23 | const Rcpp::DataFrame &graph_full, 24 | const Rcpp::CharacterVector &old_edges, 25 | const Rcpp::CharacterVector &new_edges, 26 | const std::vector &new_edge_names, 27 | std::unordered_map > &full_from_edge_map, 29 | std::unordered_map > &full_to_edge_map); 31 | void order_vert_sequences (Rcpp::List &edge_sequences, 32 | std::vector &new_edge_names, 33 | std::unordered_map > &full_from_edge_map, 35 | std::unordered_map > &full_to_edge_map); 37 | size_t count_non_contracted_edges (const Rcpp::CharacterVector &contr_edges, 38 | std::unordered_set &new_edge_name_set); 39 | void append_nc_edges (const size_t nc_edge_count, 40 | const Rcpp::DataFrame &graph_contr, 41 | std::unordered_set &new_edge_name_set, 42 | std::vector &new_edge_name_vec, 43 | const Rcpp::List &edge_sequences_contr, 44 | std::vector &all_edge_names, 45 | Rcpp::List &edge_sequences_all); 46 | void xy_to_sf (const Rcpp::DataFrame &graph_full, 47 | const Rcpp::List &edge_sequences, 48 | const std::vector &all_edge_names, 49 | Rcpp::List &res); 50 | 51 | } // end namespace dodgr_sf 52 | 53 | Rcpp::NumericVector rcpp_get_bbox_sf (double xmin, double xmax, 54 | double ymin, double ymax); 55 | 56 | Rcpp::List rcpp_aggregate_to_sf (const Rcpp::DataFrame &graph_full, 57 | const Rcpp::DataFrame &graph_contr, const Rcpp::DataFrame &edge_map); 58 | -------------------------------------------------------------------------------- /src/turn_penalty.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "sc-as-network.h" 4 | 5 | #include 6 | 7 | #include 8 | 9 | struct OneEdge { 10 | std::string v0, v1, edge; 11 | double x, y; 12 | }; 13 | 14 | struct OneCompoundEdge { 15 | std::string v0, v1, edge0, edge1; 16 | bool penalty; 17 | }; 18 | 19 | // ordering function to sort OneEdge structs in clockwise order, for which x and 20 | // y values pre-converted by subtracting the centre values. 21 | // https://stackoverflow.com/questions/6989100/sort-points-in-clockwise-order 22 | struct clockwise_sort 23 | { 24 | bool operator () (const OneEdge &a, const OneEdge &b) const 25 | { 26 | if (a.x >= 0.0 && b.x < 0.0) 27 | return true; 28 | if (a.x < 0.0 && b.x >= 0) 29 | return false; 30 | if (a.x == 0.0 && b.x == 0.0) 31 | { 32 | // # nocov start 33 | if (a.y >= 0.0 || b.y >= 0.0) 34 | return a.y > b.y; 35 | return b.y > a.y; 36 | // # nocov end 37 | } 38 | 39 | double det = a.x * b.y - a.y * b.x; 40 | if (det < 0) 41 | return true; 42 | if (det > 0) 43 | return false; 44 | 45 | // # nocov start 46 | // no tests make it as far as these lines ... 47 | double d1 = a.x * a.x + a.y * a.y; 48 | double d2 = b.x * b.x + b.y * b.y; 49 | return d1 > d2; 50 | // # nocov end 51 | } 52 | }; 53 | 54 | typedef std::set RTEdgeSet; 55 | 56 | namespace routetimes { 57 | 58 | void fill_edges (const Rcpp::DataFrame &graph, 59 | std::unordered_map > &the_edges, 61 | std::unordered_set &junction_vertices); 62 | 63 | void replace_one_map_edge ( 64 | std::unordered_map > &the_edges, 66 | std::string key, OneEdge edge, bool incoming); 67 | 68 | void erase_non_junctions ( 69 | std::unordered_map > &the_edges, 71 | std::unordered_set &junction_vertices); 72 | 73 | void replace_junctions ( 74 | const std::unordered_map > &the_edges, 76 | std::vector &junctions, 77 | bool left_side); 78 | 79 | Rcpp::DataFrame expand_edges (const Rcpp::DataFrame &graph, 80 | std::vector &junctions, int turn_penalty); 81 | 82 | } // end namespace 83 | 84 | Rcpp::List rcpp_route_times (const Rcpp::DataFrame graph, 85 | bool left_side, int turn_penalty); 86 | -------------------------------------------------------------------------------- /vignettes/fig3.tex: -------------------------------------------------------------------------------- 1 | \documentclass[tikz,border=5pt]{standalone} 2 | \usepackage[utf8x]{inputenc} 3 | 4 | \begin{document} 5 | 6 | \begin{tikzpicture}[scale=3] 7 | \tikzstyle{node_style} = [thick, circle, fill=orange] 8 | \tikzstyle{arrow_style1} = [->, black, line width=1, >=latex] 9 | 10 | % This is (C) at the top 11 | \node[node_style] (n21) at (0,3) {1}; 12 | \node[node_style] (n22) at (1,2.75) {2}; 13 | \node[node_style] (n23) at (2,2.5) {3}; 14 | \node[node_style] (n24) at (2.5,3) {4}; 15 | \node[node_style] (n25) at (2.5,2) {5}; 16 | 17 | \draw[arrow_style1] (n21) edge [bend left=10] (n22); 18 | \draw[arrow_style1] (n22) edge [bend left=10] (n21); 19 | \draw[arrow_style1] (n22) edge [bend left=10] (n23); 20 | \draw[arrow_style1] (n23) edge [bend left=10] (n22); 21 | \draw[arrow_style1] (n23) edge [bend left=10] (n24); 22 | \draw[arrow_style1] (n24) edge [bend left=20] (n23); 23 | \draw[arrow_style1] (n23) edge [bend left=10] (n25); 24 | \draw[arrow_style1] (n25) edge [bend left=20] (n23); 25 | 26 | % Then (B) in the middle 27 | \node[node_style] (n1) at (1,2) {1}; 28 | %\node[node_style] (n2) at (2,1.75) {2}; 29 | \node[node_style] (n3) at (3,1.5) {3}; 30 | \node[node_style] (n4) at (3.5,2) {4}; 31 | \node[node_style] (n5) at (3.5,1) {5}; 32 | 33 | \draw[arrow_style1] (n1) edge [bend left=10] (n3); 34 | \draw[arrow_style1] (n3) edge [bend left=10] (n1); 35 | %\draw[arrow_style1] (n2) edge [bend left=10] (n3); 36 | %\draw[arrow_style1] (n3) edge [bend left=10] (n2); 37 | \draw[arrow_style1] (n3) edge [bend left=10] (n4); 38 | \draw[arrow_style1] (n4) edge [bend left=20] (n3); 39 | \draw[arrow_style1] (n3) edge [bend left=10] (n5); 40 | \draw[arrow_style1] (n5) edge [bend left=20] (n3); 41 | 42 | % Then (A) at the bottom 43 | \node[node_style] (n11) at (0,1) {1}; 44 | \node[node_style] (n12) at (1,0.75) {2}; 45 | \node[node_style] (n13) at (2,0.5) {3}; 46 | \node[node_style] (n14) at (2.5,1) {4}; 47 | \node[node_style] (n15) at (2.5,0) {5}; 48 | 49 | \draw[arrow_style1] (n11) edge [bend left=10] (n12); 50 | \draw[arrow_style1] (n12) edge [bend left=10] (n11); 51 | \draw[arrow_style1] (n12) edge [bend left=10] (n13); 52 | %\draw[arrow_style1] (n13) edge [bend left=10] (n12); 53 | \draw[arrow_style1] (n13) edge [bend left=10] (n14); 54 | \draw[arrow_style1] (n14) edge [bend left=20] (n13); 55 | \draw[arrow_style1] (n13) edge [bend left=10] (n15); 56 | \draw[arrow_style1] (n15) edge [bend left=20] (n13); 57 | 58 | \node at (4, 2.5) {\bf\LARGE (A)}; 59 | \node at (4, 1.5) {\bf\LARGE (B)}; 60 | \node at (4, 0.5) {\bf\LARGE (C)}; 61 | \end{tikzpicture} 62 | 63 | \end{document} 64 | -------------------------------------------------------------------------------- /tests/testthat/test-centrality.R: -------------------------------------------------------------------------------- 1 | test_all <- (identical (Sys.getenv ("MPADGE_LOCAL"), "true") || 2 | identical (Sys.getenv ("GITHUB_JOB"), "test-coverage")) 3 | 4 | # testthat::skip_on_cran () 5 | testthat::skip_if (!test_all) 6 | 7 | test_that ("centrality", { 8 | graph_full <- weight_streetnet (hampi) 9 | graph <- dodgr_contract_graph (graph_full) 10 | 11 | graphc <- dodgr_centrality (graph, check_graph = FALSE) 12 | expect_equal (nrow (graph), nrow (graphc)) 13 | expect_equal (ncol (graph) + 1, ncol (graphc)) 14 | expect_true ("centrality" %in% names (graphc)) 15 | expect_false ("centrality" %in% names (graph)) 16 | 17 | v <- dodgr_vertices (graph) 18 | vc <- dodgr_centrality (graph, edges = FALSE, check_graph = FALSE) 19 | expect_equal (nrow (v), nrow (vc)) 20 | expect_equal (ncol (v) + 1, ncol (vc)) 21 | expect_true ("centrality" %in% names (vc)) 22 | expect_false ("centrality" %in% names (v)) 23 | }) 24 | 25 | test_that ("weighted centrality", { 26 | graph_full <- weight_streetnet (hampi) 27 | graph <- dodgr_contract_graph (graph_full) 28 | graphc0 <- dodgr_centrality (graph, check_graph = FALSE) 29 | expect_equal (nrow (graphc0), nrow (graph)) 30 | 31 | v <- dodgr_vertices (graph) 32 | set.seed (1) 33 | vert_wts <- runif (nrow (v)) 34 | graphc1 <- dodgr_centrality (graph, vert_wts = vert_wts, check_graph = FALSE) 35 | expect_equal (nrow (graphc1), nrow (graph)) 36 | expect_true (!all (graphc1$centrality == graphc0$centrality)) 37 | 38 | v0 <- dodgr_centrality (graph, edges = FALSE, check_graph = FALSE) 39 | expect_equal (nrow (dodgr_vertices (graph)), nrow (v0)) 40 | 41 | vert_wts <- runif (nrow (v)) 42 | v1 <- dodgr_centrality (graph, vert_wts = vert_wts, edges = FALSE, check_graph = FALSE) 43 | expect_equal (nrow (v1), nrow (v0)) 44 | expect_true (!all (v1$centrality == v0$centrality)) 45 | 46 | vert_wts <- NULL 47 | expect_silent ( 48 | vx <- dodgr_centrality (graph, vert_wts = vert_wts, edges = FALSE, check_graph = FALSE) 49 | ) 50 | 51 | vert_wts <- runif (nrow (v)) [-1] 52 | expect_error ( 53 | vx <- dodgr_centrality (graph, vert_wts = vert_wts, edges = FALSE, check_graph = FALSE), 54 | "vert_wts must be a vector of same length" 55 | ) 56 | 57 | vert_wts <- "A" 58 | expect_error ( 59 | vx <- dodgr_centrality (graph, vert_wts = vert_wts, edges = FALSE, check_graph = FALSE), 60 | "vert_wts must be a vector of same length" 61 | ) 62 | }) 63 | 64 | test_that ("estimate time", { 65 | graph <- weight_streetnet (hampi) 66 | expect_message ( 67 | x <- estimate_centrality_time (graph), 68 | "Estimated time to calculate centrality for full graph is" 69 | ) 70 | # Convert `x` as H:M:S to integer 71 | x <- as.integer (strsplit (x, ":") [[1]]) 72 | x <- sum (x * c (3600, 60, 1)) 73 | if (test_all) { 74 | expect_true (x <= 1L) 75 | } 76 | }) 77 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | # All available hooks: https://pre-commit.com/hooks.html 2 | # R specific hooks: https://github.com/lorenzwalthert/precommit 3 | repos: 4 | - repo: https://github.com/lorenzwalthert/precommit 5 | rev: v0.4.3.9014 6 | hooks: 7 | - id: style-files 8 | args: [--style_pkg=spaceout, --style_fun=spaceout_style, --include_roxygen_examples=FALSE] 9 | additional_dependencies: 10 | - ropensci-review-tools/spaceout 11 | # - id: roxygenize 12 | # codemeta must be above use-tidy-description when both are used 13 | # - id: codemeta-description-updated 14 | - id: use-tidy-description 15 | - id: spell-check 16 | exclude: > 17 | (?x)^( 18 | .*\.[rR]| 19 | .*\.feather| 20 | .*\.jpeg| 21 | .*\.pdf| 22 | .*\.png| 23 | .*\.py| 24 | .*\.RData| 25 | .*\.rds| 26 | .*\.Rds| 27 | .*\.Rproj| 28 | .*\.sh| 29 | (.*/|)\.gitignore| 30 | (.*/|)\.gitlab-ci\.yml| 31 | (.*/|)\.lintr| 32 | (.*/|)\.pre-commit-.*| 33 | (.*/|)\.Rbuildignore| 34 | (.*/|)\.Renviron| 35 | (.*/|)\.Rprofile| 36 | (.*/|)\.travis\.yml| 37 | (.*/|)appveyor\.yml| 38 | (.*/|)NAMESPACE| 39 | (.*/|)renv/settings\.dcf| 40 | (.*/|)renv\.lock| 41 | (.*/|)WORDLIST| 42 | \.github/workflows/.*| 43 | data/.*| 44 | )$ 45 | # - id: lintr 46 | - id: readme-rmd-rendered 47 | - id: parsable-R 48 | - id: no-browser-statement 49 | - id: no-print-statement 50 | - id: no-debug-statement 51 | - id: deps-in-desc 52 | # - id: pkgdown 53 | - repo: https://github.com/pre-commit/pre-commit-hooks 54 | rev: v6.0.0 55 | hooks: 56 | - id: check-added-large-files 57 | args: ['--maxkb=200'] 58 | - id: file-contents-sorter 59 | files: '^\.Rbuildignore$' 60 | - id: end-of-file-fixer 61 | exclude: '\.Rd' 62 | - repo: https://github.com/pre-commit-ci/pre-commit-ci-config 63 | rev: v1.6.1 64 | hooks: 65 | # Only required when https://pre-commit.ci is used for config validation 66 | - id: check-pre-commit-ci-config 67 | - repo: local 68 | hooks: 69 | - id: forbid-to-commit 70 | name: Don't commit common R artifacts 71 | entry: Cannot commit .Rhistory, .RData, .Rds or .rds. 72 | language: fail 73 | files: '\.(Rhistory|RData|Rds|rds)$' 74 | # `exclude: ` to allow committing specific files 75 | - id: description version 76 | name: Version has been incremeneted in DESCRIPTION 77 | entry: .hooks/description 78 | language: script 79 | - id: Commit via PR only 80 | name: Commit made to non-main branch 81 | entry: .hooks/no-commit-to-main 82 | language: script 83 | 84 | ci: 85 | autoupdate_schedule: monthly 86 | # skip: [pkgdown] 87 | -------------------------------------------------------------------------------- /man/estimate_centrality_time.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/centrality.R 3 | \name{estimate_centrality_time} 4 | \alias{estimate_centrality_time} 5 | \title{Estimate time required for a planned centrality calculation.} 6 | \usage{ 7 | estimate_centrality_time( 8 | graph, 9 | contract = TRUE, 10 | edges = TRUE, 11 | dist_threshold = NULL, 12 | heap = "BHeap" 13 | ) 14 | } 15 | \arguments{ 16 | \item{graph}{'data.frame' or equivalent object representing the network 17 | graph (see Details)} 18 | 19 | \item{contract}{If 'TRUE', centrality is calculated on contracted graph 20 | before mapping back on to the original full graph. Note that for street 21 | networks, in particular those obtained from the \pkg{osmdata} package, vertex 22 | placement is effectively arbitrary except at junctions; centrality for such 23 | graphs should only be calculated between the latter points, and thus 24 | 'contract' should always be 'TRUE'.} 25 | 26 | \item{edges}{If 'TRUE', centrality is calculated for graph edges, returning 27 | the input 'graph' with an additional 'centrality' column; otherwise 28 | centrality is calculated for vertices, returning the equivalent of 29 | 'dodgr_vertices(graph)', with an additional vertex-based 'centrality' column.} 30 | 31 | \item{dist_threshold}{If not 'NULL', only calculate centrality for each point 32 | out to specified threshold. Setting values for this will result in 33 | approximate estimates for centrality, yet with considerable gains in 34 | computational efficiency. For sufficiently large values, approximations will 35 | be accurate to within some constant multiplier. Appropriate values can be 36 | established via the \link{estimate_centrality_threshold} function.} 37 | 38 | \item{heap}{Type of heap to use in priority queue. Options include 39 | Fibonacci Heap (default; 'FHeap'), Binary Heap ('BHeap'), 40 | Trinomial Heap ('TriHeap'), Extended Trinomial Heap 41 | ('TriHeapExt', and 2-3 Heap ('Heap23').} 42 | } 43 | \value{ 44 | An estimated calculation time for calculating centrality for the 45 | given value of 'dist_threshold' 46 | } 47 | \description{ 48 | The 'dodgr' centrality functions are designed to be applied to potentially 49 | very large graphs, and may take considerable time to execute. This helper 50 | function estimates how long a centrality function may take for a given graph 51 | and given value of 'dist_threshold' estimated via the 52 | \link{estimate_centrality_threshold} function. 53 | } 54 | \note{ 55 | This function may take some time to execute. While running, it displays 56 | ongoing information on screen of estimated values of 'dist_threshold' and 57 | associated errors. Thresholds are progressively increased until the error is 58 | reduced below the specified tolerance. 59 | } 60 | \examples{ 61 | graph <- weight_streetnet (hampi, wt_profile = "foot") 62 | estimate_centrality_time (graph) 63 | } 64 | \seealso{ 65 | Other centrality: 66 | \code{\link{dodgr_centrality}()}, 67 | \code{\link{estimate_centrality_threshold}()} 68 | } 69 | \concept{centrality} 70 | -------------------------------------------------------------------------------- /man/dodgr_fundamental_cycles.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/fund-cycles.R 3 | \name{dodgr_fundamental_cycles} 4 | \alias{dodgr_fundamental_cycles} 5 | \title{Calculate fundamental cycles in a graph.} 6 | \usage{ 7 | dodgr_fundamental_cycles( 8 | graph, 9 | vertices = NULL, 10 | graph_max_size = 10000, 11 | expand = 0.05 12 | ) 13 | } 14 | \arguments{ 15 | \item{graph}{\code{data.frame} or equivalent object representing the contracted 16 | network graph (see Details).} 17 | 18 | \item{vertices}{\code{data.frame} returned from \link{dodgr_vertices}\code{(graph)}. 19 | Will be calculated if not provided, but it's quicker to pass this if it has 20 | already been calculated.} 21 | 22 | \item{graph_max_size}{Maximum size submitted to the internal C++ routines as 23 | a single chunk. Warning: Increasing this may lead to computer meltdown!} 24 | 25 | \item{expand}{For large graphs which must be broken into chunks, this factor 26 | determines the relative overlap between chunks to ensure all cycles are 27 | captured. (This value should only need to be modified in special cases.)} 28 | } 29 | \value{ 30 | List of cycle paths, in terms of vertex IDs in \code{graph} and, for 31 | spatial graphs, the corresponding coordinates. 32 | } 33 | \description{ 34 | Calculate fundamental cycles in a graph. 35 | } 36 | \note{ 37 | Calculation of fundamental cycles is VERY computationally demanding, 38 | and this function should only be executed on CONTRACTED graphs (that is, 39 | graphs returned from \link{dodgr_contract_graph}), and even than may take a 40 | long time to execute. Results for full graphs can be obtained with the 41 | function \link{dodgr_full_cycles}. The computational complexity can also not 42 | be calculated in advance, and so the parameter \code{graph_max_size} will lead to 43 | graphs larger than that (measured in numbers of edges) being cut into smaller 44 | parts. (Note that that is only possible for spatial graphs, meaning that it 45 | is not at all possible to apply this function to large, non-spatial graphs.) 46 | Each of these smaller parts will be expanded by the specified amount 47 | (\code{expand}), and cycles found within. The final result is obtained by 48 | aggregating all of these cycles and removing any repeated ones arising due to 49 | overlap in the expanded portions. Finally, note that this procedure of 50 | cutting graphs into smaller, computationally manageable sub-graphs provides 51 | only an approximation and may not yield all fundamental cycles. 52 | } 53 | \examples{ 54 | net <- weight_streetnet (hampi) 55 | graph <- dodgr_contract_graph (net) 56 | verts <- dodgr_vertices (graph) 57 | cyc <- dodgr_fundamental_cycles (graph, verts) 58 | } 59 | \seealso{ 60 | Other misc: 61 | \code{\link{compare_heaps}()}, 62 | \code{\link{dodgr_flowmap}()}, 63 | \code{\link{dodgr_full_cycles}()}, 64 | \code{\link{dodgr_insert_vertex}()}, 65 | \code{\link{dodgr_sample}()}, 66 | \code{\link{dodgr_sflines_to_poly}()}, 67 | \code{\link{dodgr_vertices}()}, 68 | \code{\link{merge_directed_graph}()}, 69 | \code{\link{summary.dodgr_dists_categorical}()}, 70 | \code{\link{write_dodgr_wt_profile}()} 71 | } 72 | \concept{misc} 73 | -------------------------------------------------------------------------------- /R/graph-merge.R: -------------------------------------------------------------------------------- 1 | #' Merge directed edges into equivalent undirected edges. 2 | #' 3 | #' Merge directed edges into equivalent undirected values by aggregating across 4 | #' directions. This function is primarily intended to aid visualisation of 5 | #' directed graphs, particularly visualising the results of the 6 | #' \link{dodgr_flows_aggregate} and \link{dodgr_flows_disperse} functions, which 7 | #' return columns of aggregated flows directed along each edge of a graph. 8 | #' 9 | #' @param graph A undirected graph in which directed edges of the input graph 10 | #' have been merged through aggregation to yield a single, undirected edge 11 | #' between each pair of vertices. 12 | #' @param col_names Names of columns to be merged through aggregation. Values 13 | #' for these columns in resultant undirected graph will be aggregated from 14 | #' directed values. 15 | #' @return An equivalent graph in which all directed edges have been reduced to 16 | #' single, undirected edges, and all values of the specified column(s) have been 17 | #' aggregated across directions to undirected values. 18 | #' @export 19 | #' @family misc 20 | #' @examples 21 | #' graph <- weight_streetnet (hampi) 22 | #' from <- sample (graph$from_id, size = 10) 23 | #' to <- sample (graph$to_id, size = 5) 24 | #' to <- to [!to %in% from] 25 | #' flows <- matrix (10 * runif (length (from) * length (to)), 26 | #' nrow = length (from) 27 | #' ) 28 | #' graph <- dodgr_flows_aggregate (graph, from = from, to = to, flows = flows) 29 | #' # graph then has an additonal 'flows` column of aggregate flows along all 30 | #' # edges. These flows are directed, and can be aggregated to equivalent 31 | #' # undirected flows on an equivalent undirected graph with: 32 | #' graph_undir <- merge_directed_graph (graph) 33 | #' # This graph will only include those edges having non-zero flows, and so: 34 | #' nrow (graph) 35 | #' nrow (graph_undir) # the latter is much smaller 36 | merge_directed_graph <- function (graph, col_names = c ("flow")) { 37 | 38 | # auto-detect either flow or centrality as col_names: 39 | if (length (col_names) == 1) { 40 | if (col_names == "flow" && !"flow" %in% names (graph) && 41 | "centrality" %in% names (graph)) { 42 | col_names <- "centrality" 43 | } 44 | } 45 | if (!all (col_names %in% names (graph))) { 46 | stop (paste0 ( 47 | "col_names [", 48 | paste (col_names, collapse = ", "), 49 | "] do not match columns in graph" 50 | )) 51 | } 52 | 53 | gr_cols <- dodgr_graph_cols (graph) 54 | graph2 <- convert_graph (graph, gr_cols) # nolint 55 | res <- lapply (col_names, function (i) { 56 | graph2$merge <- graph [[i]] 57 | rcpp_merge_cols (graph2) 58 | }) 59 | res <- do.call (cbind, res) 60 | index <- which (rowSums (res) > 0) 61 | graph <- graph [index, , drop = FALSE] # nolint 62 | for (i in seq (col_names)) { 63 | graph [[col_names [i]]] <- res [index, i] 64 | } # nolint 65 | class (graph) <- c (class (graph), "dodgr_merged") 66 | 67 | attr (graph, "hash") <- get_hash (graph, contracted = FALSE, force = TRUE) 68 | 69 | return (graph) 70 | } 71 | -------------------------------------------------------------------------------- /man/dodgr.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/dodgr-package.R 3 | \docType{package} 4 | \name{dodgr} 5 | \alias{dodgr-package} 6 | \alias{dodgr} 7 | \title{Distances On Directed GRaphs ("dodgr")} 8 | \description{ 9 | Distances on dual-weighted directed graphs using priority-queue shortest 10 | paths. Weighted directed graphs have weights from A to B which may differ 11 | from those from B to A. Dual-weighted directed graphs have two sets of such 12 | weights. A canonical example is a street network to be used for routing in 13 | which routes are calculated by weighting distances according to the type of 14 | way and mode of transport, yet lengths of routes must be calculated from 15 | direct distances. 16 | } 17 | \section{The Main Function}{ 18 | 19 | \itemize{ 20 | \item \code{\link[=dodgr_dists]{dodgr_dists()}}: Calculate pair-wise distances between 21 | specified pairs of points in a graph. 22 | } 23 | } 24 | 25 | \section{Functions to Obtain Graphs}{ 26 | 27 | \itemize{ 28 | \item \code{\link[=dodgr_streetnet]{dodgr_streetnet()}}: Extract a street network in Simple 29 | Features (\code{sf}) form. 30 | \item \code{\link[=weight_streetnet]{weight_streetnet()}}: Convert an \code{sf}-formatted street 31 | network to a \code{dodgr} graph through applying specified weights to all 32 | edges. 33 | } 34 | } 35 | 36 | \section{Functions to Modify Graphs}{ 37 | 38 | \itemize{ 39 | \item \code{\link[=dodgr_components]{dodgr_components()}}: Number all graph edges according to 40 | their presence in distinct connected components. 41 | \item \code{\link[=dodgr_contract_graph]{dodgr_contract_graph()}}: Contract a graph by removing 42 | redundant edges. 43 | } 44 | } 45 | 46 | \section{Miscellaneous Functions}{ 47 | 48 | \itemize{ 49 | \item \code{\link[=dodgr_sample]{dodgr_sample()}}: Randomly sample a graph, returning a single 50 | connected component of a defined number of vertices. 51 | \item \code{\link[=dodgr_vertices]{dodgr_vertices()}}: Extract all vertices of a graph. 52 | \item \code{\link[=compare_heaps]{compare_heaps()}}: Compare the performance of different 53 | priority queue heap structures for a given type of graph. 54 | } 55 | } 56 | 57 | \seealso{ 58 | Useful links: 59 | \itemize{ 60 | \item \url{https://UrbanAnalyst.github.io/dodgr/} 61 | \item \url{https://github.com/UrbanAnalyst/dodgr} 62 | \item Report bugs at \url{https://github.com/UrbanAnalyst/dodgr/issues} 63 | } 64 | 65 | } 66 | \author{ 67 | \strong{Maintainer}: Mark Padgham \email{mark.padgham@email.com} 68 | 69 | Authors: 70 | \itemize{ 71 | \item Andreas Petutschnig 72 | \item David Cooley 73 | } 74 | 75 | Other contributors: 76 | \itemize{ 77 | \item Robin Lovelace [contributor] 78 | \item Andrew Smith [contributor] 79 | \item Malcolm Morgan [contributor] 80 | \item Andrea Gilardi (\href{https://orcid.org/0000-0002-9424-7439}{ORCID}) [contributor] 81 | \item Eduardo Leoni (\href{https://orcid.org/0000-0003-0955-5232}{ORCID}) [contributor] 82 | \item Shane Saunders (Original author of included code for priority heaps) [copyright holder] 83 | \item Stanislaw Adaszewski (author of include concaveman-cpp code) [copyright holder] 84 | } 85 | 86 | } 87 | \concept{package} 88 | -------------------------------------------------------------------------------- /R/weighting_profiles.R: -------------------------------------------------------------------------------- 1 | #' Write `dodgr` weighting profiles to local file. 2 | #' 3 | #' Write the `dodgr` street network weighting profiles to a local 4 | #' `.json`-formatted file for manual editing and subsequent re-reading. 5 | #' 6 | #' @param file Full name (including path) of file to which to write. The `.json` 7 | #' suffix will be automatically appended. 8 | #' @return TRUE if writing successful. 9 | #' @seealso \link{weight_streetnet} 10 | #' @family misc 11 | #' @examples 12 | #' f <- tempfile (fileext = ".json") 13 | #' write_dodgr_wt_profile (file = f) 14 | #' wt_profiles <- jsonlite::read_json (f, simplify = TRUE) 15 | #' @export 16 | write_dodgr_wt_profile <- function (file = NULL) { 17 | 18 | requireNamespace ("jsonlite") 19 | 20 | if (is.null (file)) { 21 | stop ("file name must be given") 22 | } 23 | 24 | file <- paste0 (tools::file_path_sans_ext (file), ".json") 25 | con <- file (file, open = "wt") 26 | 27 | sc <- summary (con) 28 | if (!sc [["can write"]] == "yes") { 29 | stop ("Unable to write to connection ", sc$description) 30 | } # nocov 31 | 32 | wpj <- jsonlite::toJSON (dodgr::weighting_profiles, pretty = TRUE) 33 | writeLines (wpj, con) 34 | close (con) 35 | } 36 | 37 | read_dodgr_wt_profile <- function (file = NULL) { 38 | 39 | file <- paste0 (tools::file_path_sans_ext (file), ".json") 40 | if (!fs::file_exists (file)) { 41 | stop ("file [", file, "] does not exist") 42 | } # nocov 43 | 44 | res <- jsonlite::fromJSON (file) 45 | # jsonlite interprets these as "integer": 46 | storage.mode (res$surface_speeds$max_speed) <- "numeric" 47 | storage.mode (res$penalties$traffic_lights) <- "numeric" 48 | return (res) 49 | } 50 | 51 | 52 | get_profiles <- function (file = NULL) { 53 | 54 | if (is.null (file)) { 55 | wp <- dodgr::weighting_profiles 56 | } else { 57 | wp <- read_dodgr_wt_profile (file) 58 | } 59 | return (wp) 60 | } 61 | 62 | get_profile <- function (wt_profile, file = NULL) { 63 | 64 | profiles <- get_profiles (file)$weighting_profiles 65 | prf_names <- unique (profiles$name) 66 | if (is.numeric (wt_profile)) { 67 | # nocov start 68 | # this function is actually only called for character args 69 | wp <- profiles [profiles$name == "foot", ] 70 | wp$name <- "custom" 71 | wp$value <- wt_profile 72 | wp$max_speed <- 10 73 | # nocov end 74 | } else { 75 | # foot, horse, wheelchair, bicycle, moped, 76 | # motorcycle, motorcar, goods, hgv, psv 77 | wt_profile <- match.arg (tolower (wt_profile), prf_names) 78 | wp <- profiles [profiles$name == wt_profile, ] 79 | } 80 | return (wp) 81 | } 82 | 83 | get_surface_speeds <- function (wt_profile, file = NULL) { 84 | 85 | s <- get_profiles (file)$surface_speeds 86 | s [s$name == wt_profile, ] 87 | } 88 | 89 | get_turn_penalties <- function (wt_profile, file = NULL) { 90 | 91 | tp <- get_profiles (file)$penalties 92 | tp [tp$name == wt_profile, ] 93 | } 94 | 95 | are_turns_restricted <- function (wt_profile, file = NULL) { 96 | 97 | tp <- get_profiles (file)$penalties 98 | tp$restrictions [tp$name == wt_profile] 99 | } 100 | -------------------------------------------------------------------------------- /src/dgraph.h: -------------------------------------------------------------------------------- 1 | #ifndef DGRAPH_H 2 | #define DGRAPH_H 3 | 4 | #include 5 | #include 6 | 7 | typedef std::size_t size_t; 8 | 9 | /* Directed Graphs 10 | * ---------------------------------------------------------------------------- 11 | * Author: Mark Padgham, modified from code by Shane Saunders 12 | */ 13 | 14 | 15 | /*--- Directed Graph Classes ------------------------------------------------*/ 16 | 17 | /* --- Directed graph edge class --- 18 | * Each edge object represents an outgoing edge of some vertex and is stored in 19 | * that vertexes linked list of edges. The member 'target' is the edge's 20 | * target vertex number, whereas 'source' is the edge's source vertex number. 21 | * The member 'dist' is the associated edge distance. The pointers 'nextIn' 22 | * and 'nextOut' are used to form a linked lists of a vertices incoming and 23 | * outgoing edges respectively. Such linked lists are terminated with a null 24 | * pointer. 25 | */ 26 | class DGraphEdge { 27 | public: 28 | size_t source, target, edge_id; // edge_id only used in centrality 29 | double dist, wt; 30 | DGraphEdge *nextOut, *nextIn; 31 | }; 32 | 33 | /* --- Directed graph vertex class --- 34 | * Each vertex object has an associated linked lists of edge objects 35 | * representing the outgoing and incoming edges of that vertex. The member 36 | * pointers outHead and inHead points to the first edge object in the linked 37 | * list of outgoing, and incoming edges respectively. Similarly, outTail and 38 | * inTail point to the last edge of each linked list. The number of outgoing 39 | * and incoming edges are stored in outSize and inSize respectively. 40 | */ 41 | class DGraphVertex { 42 | public: 43 | DGraphEdge *outHead, *outTail; 44 | DGraphEdge *inHead, *inTail; 45 | int outSize, inSize; 46 | }; 47 | 48 | /* --- Directed graph class --- 49 | * Vertices in the graph are stored as an array of vertex objects, pointed to 50 | * by the member variable 'vertices'. Each vertex is identified by a number 51 | * corresponding to its index in the vertices[] array. The member 52 | * nVertices is the number of vertices in the graph. 53 | * 54 | * clear() - Remove all edges from graph. 55 | * 56 | * addNewEdge() - Adds a new edge to the edge to the graph. 57 | * 58 | * print() - Prints a text representation of the graph to the standard 59 | * output. 60 | */ 61 | class DGraph { 62 | public: 63 | DGraph(size_t n); 64 | ~DGraph(); 65 | 66 | size_t nVertices() const; 67 | const std::vector& vertices() const; 68 | 69 | // disable copy/assign as will crash (double-delete) 70 | DGraph(const DGraph&) = delete; 71 | DGraph& operator=(const DGraph&) = delete; 72 | 73 | void clear(); 74 | void addNewEdge(size_t srcVertexNo, size_t destVertexNo, 75 | double dist, double wt, size_t edge_id); 76 | bool edgeExists(size_t v, size_t w) const; 77 | bool reachable(size_t s) const; 78 | void print() const; 79 | private: 80 | void initVertices(); 81 | 82 | std::vector m_vertices; 83 | 84 | }; 85 | 86 | 87 | /*---------------------------------------------------------------------------*/ 88 | #endif 89 | -------------------------------------------------------------------------------- /src/heaps/bheap.h: -------------------------------------------------------------------------------- 1 | #ifndef BHEAP_H 2 | #define BHEAP_H 3 | /* File bheap.h - Binary Heap 4 | * ---------------------------------------------------------------------------- 5 | * Mark Padgham, adapted from code by Shane Saunders 6 | */ 7 | #include "heap.h" /* Defines the base class for heaps. */ 8 | 9 | /* This implementation stores the binary heap in a 1 dimensional array. */ 10 | 11 | 12 | /*--- Structure Definitions -------------------------------------------------*/ 13 | 14 | /* --- BHeapNode --- 15 | * A binary heap node has the following members: 16 | * item - A unique integer identifying the item. In shortest path algorithms 17 | * this is the vertex number it is associated with. 18 | * key - An integer key. In shortest path algorithms this is the tentative 19 | * shortest path distance. 20 | */ 21 | class BHeapNode { 22 | public: 23 | size_t item; 24 | double key; 25 | }; 26 | 27 | class BHeapNodeInt { 28 | public: 29 | size_t item; 30 | int key; 31 | }; 32 | 33 | /* --- BHeap --- 34 | * Binary heap structure for frontier set in Dijkstra's algorithm. 35 | * a[] - an array of binary heap nodes. 36 | * p[] - stores the positions of items in the binary heap array a[]. 37 | * itemCount - the number of items currently in the binary heap. 38 | * compCount - the number of key comparison operations 39 | */ 40 | class BHeap : public Heap { 41 | public: 42 | BHeap(size_t n); 43 | ~BHeap(); 44 | 45 | void deleteItem(size_t item); 46 | size_t deleteMin() { 47 | size_t v; 48 | v = min(); 49 | deleteItem(v); 50 | return v; 51 | } 52 | void insert(size_t item, double key); 53 | void decreaseKey(size_t item, double newKey); 54 | size_t nItems() const { return itemCount; } 55 | 56 | long int nComps() const { return compCount; } 57 | void dump() const; 58 | 59 | double getmin(); 60 | 61 | /* extra functions */ 62 | size_t min(); 63 | 64 | private: 65 | BHeapNode *a; 66 | size_t *aPos; 67 | size_t itemCount; 68 | long int compCount; 69 | 70 | void siftUp(size_t p, size_t q); 71 | }; 72 | 73 | class BHeapInt : public HeapInt { 74 | public: 75 | BHeapInt(size_t n); 76 | ~BHeapInt(); 77 | 78 | void deleteItem(size_t item); 79 | size_t deleteMin() { 80 | size_t v; 81 | v = min(); 82 | deleteItem(v); 83 | return v; 84 | } 85 | void insert(size_t item, int key); 86 | void decreaseKey(size_t item, int newKey); 87 | size_t nItems() const { return itemCount; } 88 | 89 | long int nComps() const { return compCount; } 90 | void dump() const; 91 | 92 | int getmin(); 93 | 94 | /* extra functions */ 95 | size_t min(); 96 | 97 | private: 98 | BHeapNodeInt *a; 99 | size_t *aPos; 100 | size_t itemCount; 101 | long int compCount; 102 | 103 | void siftUp(size_t p, size_t q); 104 | }; 105 | 106 | /*---------------------------------------------------------------------------*/ 107 | #endif 108 | -------------------------------------------------------------------------------- /src/heaps/fheap.h: -------------------------------------------------------------------------------- 1 | #ifndef FHEAP_H 2 | #define FHEAP_H 3 | /* File fheap.h - Fibonacci Heap 4 | * ---------------------------------------------------------------------------- 5 | * Mark Padgham, adapted from code by Shane Saunders 6 | */ 7 | #include "heap.h" /* Defines the base class for heaps. */ 8 | 9 | 10 | /* Option to allow printing of debugging information. Use 1 for yes, or 0 for 11 | * no. 12 | */ 13 | #define FHEAP_DUMP 0 14 | 15 | 16 | /* --- FHeapNode --- 17 | * Fibonacci heap node class. 18 | * 19 | * A nodes has the following pointers: 20 | * parent - a pointer to the nodes parent node (if any). 21 | * child - a pointer to a child node (typically the highest rank child). 22 | * left, right - sibling pointers which provide a circular doubly linked list 23 | * containing all the parents nodes children. 24 | * 25 | * The remaining fields are: 26 | * rank - the nodes rank, that is, the number of children it has. 27 | * key - the nodes key. 28 | * item - the number of the item that the node is associated with. 29 | */ 30 | class FHeapNode { 31 | public: 32 | FHeapNode *parent; 33 | FHeapNode *left, *right; 34 | FHeapNode *child; 35 | size_t rank; 36 | size_t marked; 37 | double key; 38 | size_t item; 39 | }; 40 | 41 | /* --- FHeap --- 42 | * Fibonacci heap class. 43 | * 44 | * trees - An array of pointers to trees at root level in the heap. Entry i 45 | * in the array points to the root node of a tree that has nodes of 46 | * dimension i on the main trunk. 47 | * nodes - An array of pointers to nodes in the heap. Nodes are indexed 48 | * according to their vertex number. This array can then be used to 49 | * look up the node for corresponding to a vertex number, and is 50 | * useful when freeing space taken up by the heap. 51 | * maxNodes - The maximum number of nodes allowed in the heap. 52 | * maxTrees - The maximum number of trees allowed in the heap (calculated from 53 | * maxNodes). 54 | * itemCount - The current number of nodes in the heap. 55 | * treeSum - The binary value represented by trees in the heap. 56 | * By maintaining this it is easy to keep track of the maximum rank 57 | * tree in the heap. 58 | * compCount - can be used for experimental purposes when counting the number 59 | * of key comparisons. 60 | */ 61 | class FHeap: public Heap { 62 | public: 63 | FHeap(size_t n); 64 | ~FHeap(); 65 | 66 | size_t deleteMin(); 67 | void insert(size_t item, double k); 68 | void decreaseKey(size_t item, double newValue); 69 | size_t nItems() const { return itemCount; } 70 | 71 | long int nComps() const { return compCount; } 72 | void dump() const; 73 | 74 | double getmin() { 75 | return 0.0; // MP: dummy value not implemented yet 76 | } 77 | 78 | private: 79 | FHeapNode **trees; 80 | FHeapNode **nodes; 81 | size_t maxNodes, maxTrees, itemCount, treeSum; 82 | long int compCount; 83 | 84 | void meld(FHeapNode *treeList); 85 | static void dumpNodes(FHeapNode *node, size_t level); 86 | }; 87 | 88 | /*---------------------------------------------------------------------------*/ 89 | #endif 90 | -------------------------------------------------------------------------------- /man/match_points_to_graph.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/match-points.R 3 | \name{match_points_to_graph} 4 | \alias{match_points_to_graph} 5 | \title{Alias for \link{match_pts_to_graph}} 6 | \usage{ 7 | match_points_to_graph(graph, xy, connected = FALSE, distances = FALSE) 8 | } 9 | \arguments{ 10 | \item{graph}{A \code{dodgr} graph with spatial coordinates, such as a 11 | \code{dodgr_streetnet} object.} 12 | 13 | \item{xy}{coordinates of points to be matched to the vertices, either as 14 | matrix or \pkg{sf}-formatted \code{data.frame}.} 15 | 16 | \item{connected}{Should points be matched to the same (largest) connected 17 | component of graph? If \code{FALSE} and these points are to be used for a 18 | \code{dodgr} routing routine (\link{dodgr_dists}, \link{dodgr_paths}, or 19 | \link{dodgr_flows_aggregate}), then results may not be returned if points are 20 | not part of the same connected component. On the other hand, forcing them to 21 | be part of the same connected component may decrease the spatial accuracy of 22 | matching.} 23 | 24 | \item{distances}{If \code{TRUE}, return a 'data.frame' object with 'index' column 25 | as described in return value; and additional columns with perpendicular 26 | distance to nearest edge in graph, and coordinates of points of intersection. 27 | See description of return value for details.} 28 | } 29 | \value{ 30 | For \code{distances = FALSE} (default), a vector index matching the \code{xy} 31 | coordinates to nearest edges. For bi-directional edges, only one match is 32 | returned, and it is up to the user to identify and suitably process matching 33 | edge pairs. For 'distances = TRUE', a 'data.frame' of four columns: 34 | \itemize{ 35 | \item "index" The index of closest edges in "graph", as described above. 36 | \item "d_signed" The perpendicular distance from ech point to the nearest 37 | edge, with negative distances denoting points to the left of edges, and 38 | positive distances denoting points to the right. Distances of zero denote 39 | points lying precisely on the line of an edge (potentially including cases 40 | where nearest point of bisection lies beyond the actual edge). 41 | \item "x" The x-coordinate of the point of intersection. 42 | \item "y" The y-coordinate of the point of intersection. 43 | } 44 | } 45 | \description{ 46 | Match spatial points to the edges of a spatial graph, through finding the 47 | edge with the closest perpendicular intersection. NOTE: Intersections are 48 | calculated geometrically, and presume planar geometry. It is up to users of 49 | projected geometrical data, such as those within a \code{dodgr_streetnet} object, 50 | to ensure that either: (i) Data span an sufficiently small area that errors 51 | from presuming planar geometry may be ignored; or (ii) Data are re-projected 52 | to an equivalent planar geometry prior to calling this routine. 53 | } 54 | \examples{ 55 | graph <- weight_streetnet (hampi, wt_profile = "foot") 56 | # Then generate some random points to match to graph 57 | verts <- dodgr_vertices (graph) 58 | npts <- 10 59 | xy <- data.frame ( 60 | x = min (verts$x) + runif (npts) * diff (range (verts$x)), 61 | y = min (verts$y) + runif (npts) * diff (range (verts$y)) 62 | ) 63 | edges <- match_pts_to_graph (graph, xy) 64 | graph [edges, ] # The edges of the graph closest to `xy` 65 | } 66 | \seealso{ 67 | Other match: 68 | \code{\link{add_nodes_to_graph}()}, 69 | \code{\link{match_points_to_verts}()}, 70 | \code{\link{match_pts_to_graph}()}, 71 | \code{\link{match_pts_to_verts}()} 72 | } 73 | \concept{match} 74 | -------------------------------------------------------------------------------- /man/match_pts_to_graph.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/match-points.R 3 | \name{match_pts_to_graph} 4 | \alias{match_pts_to_graph} 5 | \title{Match spatial points to the edges of a spatial graph.} 6 | \usage{ 7 | match_pts_to_graph(graph, xy, connected = FALSE, distances = FALSE) 8 | } 9 | \arguments{ 10 | \item{graph}{A \code{dodgr} graph with spatial coordinates, such as a 11 | \code{dodgr_streetnet} object.} 12 | 13 | \item{xy}{coordinates of points to be matched to the vertices, either as 14 | matrix or \pkg{sf}-formatted \code{data.frame}.} 15 | 16 | \item{connected}{Should points be matched to the same (largest) connected 17 | component of graph? If \code{FALSE} and these points are to be used for a 18 | \code{dodgr} routing routine (\link{dodgr_dists}, \link{dodgr_paths}, or 19 | \link{dodgr_flows_aggregate}), then results may not be returned if points are 20 | not part of the same connected component. On the other hand, forcing them to 21 | be part of the same connected component may decrease the spatial accuracy of 22 | matching.} 23 | 24 | \item{distances}{If \code{TRUE}, return a 'data.frame' object with 'index' column 25 | as described in return value; and additional columns with perpendicular 26 | distance to nearest edge in graph, and coordinates of points of intersection. 27 | See description of return value for details.} 28 | } 29 | \value{ 30 | For \code{distances = FALSE} (default), a vector index matching the \code{xy} 31 | coordinates to nearest edges. For bi-directional edges, only one match is 32 | returned, and it is up to the user to identify and suitably process matching 33 | edge pairs. For 'distances = TRUE', a 'data.frame' of four columns: 34 | \itemize{ 35 | \item "index" The index of closest edges in "graph", as described above. 36 | \item "d_signed" The perpendicular distance from ech point to the nearest 37 | edge, with negative distances denoting points to the left of edges, and 38 | positive distances denoting points to the right. Distances of zero denote 39 | points lying precisely on the line of an edge (potentially including cases 40 | where nearest point of bisection lies beyond the actual edge). 41 | \item "x" The x-coordinate of the point of intersection. 42 | \item "y" The y-coordinate of the point of intersection. 43 | } 44 | } 45 | \description{ 46 | Match spatial points to the edges of a spatial graph, through finding the 47 | edge with the closest perpendicular intersection. NOTE: Intersections are 48 | calculated geometrically, and presume planar geometry. It is up to users of 49 | projected geometrical data, such as those within a \code{dodgr_streetnet} object, 50 | to ensure that either: (i) Data span an sufficiently small area that errors 51 | from presuming planar geometry may be ignored; or (ii) Data are re-projected 52 | to an equivalent planar geometry prior to calling this routine. 53 | } 54 | \examples{ 55 | graph <- weight_streetnet (hampi, wt_profile = "foot") 56 | # Then generate some random points to match to graph 57 | verts <- dodgr_vertices (graph) 58 | npts <- 10 59 | xy <- data.frame ( 60 | x = min (verts$x) + runif (npts) * diff (range (verts$x)), 61 | y = min (verts$y) + runif (npts) * diff (range (verts$y)) 62 | ) 63 | edges <- match_pts_to_graph (graph, xy) 64 | graph [edges, ] # The edges of the graph closest to `xy` 65 | } 66 | \seealso{ 67 | Other match: 68 | \code{\link{add_nodes_to_graph}()}, 69 | \code{\link{match_points_to_graph}()}, 70 | \code{\link{match_points_to_verts}()}, 71 | \code{\link{match_pts_to_verts}()} 72 | } 73 | \concept{match} 74 | -------------------------------------------------------------------------------- /man/dodgr_streetnet.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/dodgr-streetnet.R 3 | \name{dodgr_streetnet} 4 | \alias{dodgr_streetnet} 5 | \title{Extract a street network in \pkg{sf}-format for a given location.} 6 | \usage{ 7 | dodgr_streetnet(bbox, pts = NULL, expand = 0.05, quiet = TRUE) 8 | } 9 | \arguments{ 10 | \item{bbox}{Bounding box as vector or matrix of coordinates, or location 11 | name. Passed to \code{osmdata::getbb}.} 12 | 13 | \item{pts}{List of points presumably containing spatial coordinates} 14 | 15 | \item{expand}{Relative factor by which street network should extend beyond 16 | limits defined by pts (only if \code{bbox} not given).} 17 | 18 | \item{quiet}{If \code{FALSE}, display progress messages} 19 | } 20 | \value{ 21 | A Simple Features (\code{sf}) object with coordinates of all lines in 22 | the street network. 23 | } 24 | \description{ 25 | Use the \code{osmdata} package to extract the street network for a given 26 | location. For routing between a given set of points (passed as \code{pts}), 27 | the \code{bbox} argument may be omitted, in which case a bounding box will 28 | be constructed by expanding the range of \code{pts} by the relative amount of 29 | \code{expand}. 30 | } 31 | \note{ 32 | Calls to this function may return "General overpass server error" with 33 | a note that "Query timed out." The overpass served used to access the data 34 | has a sophisticated queueing system which prioritises requests that are 35 | likely to require little time. These timeout errors can thus generally \emph{not} 36 | be circumvented by changing "timeout" options on the HTTP requests, and 37 | should rather be interpreted to indicate that a request is too large, and may 38 | need to be refined, or somehow broken up into smaller queries. 39 | } 40 | \examples{ 41 | \dontrun{ 42 | streetnet <- dodgr_streetnet ("hampi india", expand = 0) 43 | # convert to form needed for `dodgr` functions: 44 | graph <- weight_streetnet (streetnet) 45 | nrow (graph) # around 5,900 edges 46 | # Alternative ways of extracting street networks by using a small selection 47 | # of graph vertices to define bounding box: 48 | verts <- dodgr_vertices (graph) 49 | verts <- verts [sample (nrow (verts), size = 200), ] 50 | streetnet <- dodgr_streetnet (pts = verts, expand = 0) 51 | graph <- weight_streetnet (streetnet) 52 | nrow (graph) 53 | # This will generally have many more rows because most street networks 54 | # include streets that extend considerably beyond the specified bounding box. 55 | 56 | # bbox can also be a polygon: 57 | bb <- osmdata::getbb ("gent belgium") # rectangular bbox 58 | nrow (dodgr_streetnet (bbox = bb)) # around 30,000 59 | bb <- osmdata::getbb ("gent belgium", format_out = "polygon") 60 | nrow (dodgr_streetnet (bbox = bb)) # around 17,000 61 | # The latter has fewer rows because only edges within polygon are returned 62 | 63 | # Example with access restrictions 64 | bbox <- c (-122.2935, 47.62663, -122.28, 47.63289) 65 | x <- dodgr_streetnet_sc (bbox) 66 | net <- weight_streetnet (x, keep_cols = "access", turn_penalty = TRUE) 67 | # has many streets with "access" = "private"; these can be removed like this: 68 | net2 <- net [which (!net$access != "private"), ] 69 | # or modified in some other way such as strongly penalizing use of those 70 | # streets: 71 | index <- which (net$access == "private") 72 | net$time_weighted [index] <- net$time_weighted [index] * 100 73 | } 74 | } 75 | \seealso{ 76 | Other extraction: 77 | \code{\link{dodgr_streetnet_geodesic}()}, 78 | \code{\link{dodgr_streetnet_sc}()}, 79 | \code{\link{weight_railway}()}, 80 | \code{\link{weight_streetnet}()} 81 | } 82 | \concept{extraction} 83 | -------------------------------------------------------------------------------- /man/dodgr_streetnet_sc.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/dodgr-streetnet.R 3 | \name{dodgr_streetnet_sc} 4 | \alias{dodgr_streetnet_sc} 5 | \title{Extract a street network in \pkg{silicate}-format for a given location.} 6 | \usage{ 7 | dodgr_streetnet_sc(bbox, pts = NULL, expand = 0.05, quiet = TRUE) 8 | } 9 | \arguments{ 10 | \item{bbox}{Bounding box as vector or matrix of coordinates, or location 11 | name. Passed to \code{osmdata::getbb}.} 12 | 13 | \item{pts}{List of points presumably containing spatial coordinates} 14 | 15 | \item{expand}{Relative factor by which street network should extend beyond 16 | limits defined by pts (only if \code{bbox} not given).} 17 | 18 | \item{quiet}{If \code{FALSE}, display progress messages} 19 | } 20 | \value{ 21 | A Simple Features (\code{sf}) object with coordinates of all lines in 22 | the street network. 23 | } 24 | \description{ 25 | Use the \code{osmdata} package to extract the street network for a given 26 | location and return it in \code{SC}-format. For routing between a given set of 27 | points (passed as \code{pts}), the \code{bbox} argument may be omitted, in which case a 28 | bounding box will be constructed by expanding the range of \code{pts} by the 29 | relative amount of \code{expand}. 30 | } 31 | \note{ 32 | Calls to this function may return "General overpass server error" with 33 | a note that "Query timed out." The overpass served used to access the data 34 | has a sophisticated queueing system which prioritises requests that are 35 | likely to require little time. These timeout errors can thus generally \emph{not} 36 | be circumvented by changing "timeout" options on the HTTP requests, and 37 | should rather be interpreted to indicate that a request is too large, and may 38 | need to be refined, or somehow broken up into smaller queries. 39 | } 40 | \examples{ 41 | \dontrun{ 42 | streetnet <- dodgr_streetnet ("hampi india", expand = 0) 43 | # convert to form needed for `dodgr` functions: 44 | graph <- weight_streetnet (streetnet) 45 | nrow (graph) # around 5,900 edges 46 | # Alternative ways of extracting street networks by using a small selection 47 | # of graph vertices to define bounding box: 48 | verts <- dodgr_vertices (graph) 49 | verts <- verts [sample (nrow (verts), size = 200), ] 50 | streetnet <- dodgr_streetnet (pts = verts, expand = 0) 51 | graph <- weight_streetnet (streetnet) 52 | nrow (graph) 53 | # This will generally have many more rows because most street networks 54 | # include streets that extend considerably beyond the specified bounding box. 55 | 56 | # bbox can also be a polygon: 57 | bb <- osmdata::getbb ("gent belgium") # rectangular bbox 58 | nrow (dodgr_streetnet (bbox = bb)) # around 30,000 59 | bb <- osmdata::getbb ("gent belgium", format_out = "polygon") 60 | nrow (dodgr_streetnet (bbox = bb)) # around 17,000 61 | # The latter has fewer rows because only edges within polygon are returned 62 | 63 | # Example with access restrictions 64 | bbox <- c (-122.2935, 47.62663, -122.28, 47.63289) 65 | x <- dodgr_streetnet_sc (bbox) 66 | net <- weight_streetnet (x, keep_cols = "access", turn_penalty = TRUE) 67 | # has many streets with "access" = "private"; these can be removed like this: 68 | net2 <- net [which (!net$access != "private"), ] 69 | # or modified in some other way such as strongly penalizing use of those 70 | # streets: 71 | index <- which (net$access == "private") 72 | net$time_weighted [index] <- net$time_weighted [index] * 100 73 | } 74 | } 75 | \seealso{ 76 | Other extraction: 77 | \code{\link{dodgr_streetnet}()}, 78 | \code{\link{dodgr_streetnet_geodesic}()}, 79 | \code{\link{weight_railway}()}, 80 | \code{\link{weight_streetnet}()} 81 | } 82 | \concept{extraction} 83 | -------------------------------------------------------------------------------- /R/compare-heaps.R: -------------------------------------------------------------------------------- 1 | #' Compare timings of different sort heaps for a given input graph. 2 | #' 3 | #' Perform timing comparison between different kinds of heaps as well as with 4 | #' equivalent routines from the \pkg{igraph} package. To do this, a random 5 | #' sub-graph containing a defined number of vertices is first selected. 6 | #' Alternatively, this random sub-graph can be pre-generated with the 7 | #' `dodgr_sample` function and passed directly. 8 | #' 9 | #' @param graph `data.frame` object representing the network graph (or a 10 | #' sub-sample selected with `dodgr_sample`) 11 | #' @param nverts Number of vertices used to generate random sub-graph. If a 12 | #' non-numeric value is given, the whole graph will be used. 13 | #' @param replications Number of replications to be used in comparison 14 | #' @return Result of `bench::mark` comparison. 15 | #' 16 | #' @family misc 17 | #' @export 18 | #' @examples 19 | #' graph <- weight_streetnet (hampi) 20 | #' \dontrun{ 21 | #' compare_heaps (graph, nverts = 1000, replications = 1) 22 | #' } 23 | compare_heaps <- function (graph, 24 | nverts = 100, 25 | replications = 2) { 26 | 27 | requireNamespace ("bench") 28 | requireNamespace ("igraph") 29 | 30 | if (is.numeric (nverts)) { 31 | graph <- dodgr_sample (graph, nverts = nverts) 32 | } 33 | graph_contracted <- dodgr_contract_graph (graph) 34 | 35 | # route only between points on the contracted graph: 36 | gr_cols <- dodgr_graph_cols (graph) 37 | from_id <- unique (graph_contracted [[gr_cols$from]]) 38 | to_id <- unique (graph_contracted [[gr_cols$to]]) 39 | 40 | igr <- dodgr_to_igraph (graph) 41 | 42 | bench::mark ( 43 | BHeap = dodgr_dists ( 44 | graph, 45 | from = from_id, 46 | to = to_id, 47 | heap = "BHeap" 48 | ), 49 | FHeap = dodgr_dists ( 50 | graph, 51 | from = from_id, 52 | to = to_id, 53 | heap = "FHeap" 54 | ), 55 | TriHeap = dodgr_dists ( 56 | graph, 57 | from = from_id, 58 | to = to_id, 59 | heap = "TriHeap" 60 | ), 61 | TriHeapExt = dodgr_dists ( 62 | graph, 63 | from = from_id, 64 | to = to_id, 65 | heap = "TriHeapExt" 66 | ), 67 | Heap23 = dodgr_dists ( 68 | graph, 69 | from = from_id, 70 | to = to_id, 71 | heap = "Heap23" 72 | ), 73 | BHeap_contracted = dodgr_dists ( 74 | graph_contracted, 75 | from = from_id, 76 | to = to_id, 77 | heap = "BHeap" 78 | ), 79 | FHeap_contracted = dodgr_dists ( 80 | graph_contracted, 81 | from = from_id, 82 | to = to_id, 83 | heap = "FHeap" 84 | ), 85 | TriHeap_contracted = dodgr_dists ( 86 | graph_contracted, 87 | from = from_id, 88 | to = to_id, 89 | heap = "TriHeap" 90 | ), 91 | TriHeapExt_contracted = dodgr_dists ( 92 | graph_contracted, 93 | from = from_id, 94 | to = to_id, 95 | heap = "TriHeapExt" 96 | ), 97 | Heap23_contracted = dodgr_dists ( 98 | graph_contracted, 99 | from = from_id, 100 | to = to_id, 101 | heap = "Heap23" 102 | ), 103 | igraph = igraph::distances ( 104 | igr, 105 | v = from_id, 106 | to = to_id, 107 | mode = "out" 108 | ), 109 | check = FALSE # contracted don't necessarily equal full dists here 110 | ) 111 | } 112 | -------------------------------------------------------------------------------- /R/deduplicate.R: -------------------------------------------------------------------------------- 1 | #' Deduplicate edges in a graph 2 | #' 3 | #' Graph may have duplicated edges, particularly when extracted as 4 | #' \link{dodgr_streetnet} objects. This function de-duplicates any repeated 5 | #' edges, reducing weighted distances and times to the minimal values from all 6 | #' duplicates. 7 | #' @param graph Any 'dodgr' graph or network. 8 | #' @return A potentially modified version of graph, with any formerly duplicated 9 | #' edges reduces to single rows containing minimal weighted distances and times. 10 | #' @family conversion 11 | #' @examples 12 | #' net0 <- weight_streetnet (hampi, wt_profile = "foot") 13 | #' nrow (net0) 14 | #' # Duplicate part of input data: 15 | #' h2 <- rbind (hampi, hampi [1, ]) 16 | #' net1 <- weight_streetnet (h2, wt_profile = "foot") 17 | #' nrow (net1) # network then has more edges 18 | #' net2 <- dodgr_deduplicate_graph (net1) 19 | #' nrow (net2) 20 | #' stopifnot (identical (nrow (net0), nrow (net2))) 21 | #' @export 22 | dodgr_deduplicate_graph <- function (graph) { 23 | 24 | gr_cols <- dodgr_graph_cols (graph) 25 | fr_col <- names (graph) [gr_cols$from] 26 | to_col <- names (graph) [gr_cols$to] 27 | d_col <- names (graph) [gr_cols$d_weighted] 28 | t_col <- names (graph) [gr_cols$time_weighted] 29 | t_col <- ifelse (is.na (t_col), "", t_col) 30 | 31 | fr_to <- paste0 (graph [[fr_col]], "-", graph [[to_col]]) 32 | index <- which (duplicated (fr_to)) 33 | 34 | if (length (index) == 0L) { 35 | return (graph) 36 | } 37 | 38 | has_times <- nzchar (t_col) 39 | 40 | res <- rcpp_deduplicate (graph, fr_col, to_col, d_col, t_col) 41 | 42 | if (has_times) { 43 | 44 | n <- as.integer (nrow (res) / 2) 45 | res_t <- res [seq (n) + n, ] 46 | res <- res [seq (n), ] 47 | ft <- paste0 (res$from, "-", res$to) 48 | ft_t <- paste0 (res_t$from, "-", res_t$to) 49 | index_t <- match (ft, ft_t) 50 | res$t <- NA # necessary to define as numeric 51 | res$t [index_t] <- res_t$d 52 | } 53 | 54 | graph <- graph [-index, ] 55 | fr_to <- fr_to [-index] 56 | 57 | fr_to_res <- paste0 (res$from, "-", res$to) 58 | 59 | index_to_gr <- match (fr_to_res, fr_to) 60 | graph [[d_col]] [index_to_gr] <- res$d 61 | if (has_times) { 62 | graph [[t_col]] [index_to_gr] <- res$t 63 | } 64 | 65 | return (graph) 66 | } 67 | 68 | #' Issue a warning if graph has duplicated edges 69 | #' 70 | #' Currently only used in centrality; see #186. 71 | #' 72 | #' @return Logical flag indicating whether or not graph has duplicated edges; 73 | #' `TRUE` denotes a graph with duplicated edges; `FALSE` denotes a graph which 74 | #' passes this check. 75 | #' @noRd 76 | duplicated_edge_check <- function (graph, proceed = FALSE) { 77 | 78 | gr_cols <- dodgr_graph_cols (graph) 79 | fr_name <- names (graph) [gr_cols$from] 80 | to_name <- names (graph) [gr_cols$to] 81 | fr <- graph [[fr_name]] 82 | to <- graph [[to_name]] 83 | 84 | fr_to <- paste0 (fr, "-", to) 85 | 86 | has_duplicates <- any (duplicated (fr_to)) 87 | 88 | if (has_duplicates) { 89 | 90 | msg <- paste0 ( 91 | "Graph has duplicated edges. Only the first will be used here,\n", 92 | "but it is better to remove them first with the ", 93 | "'dodgr_deduplicate_graph() function." 94 | ) 95 | if (interactive ()) { 96 | message (msg) 97 | if (!proceed) { 98 | x <- readline ("Do you want to proceed (y/n)? ") 99 | if (tolower (substring (x, 1, 1) != "y")) { # nocov 100 | stop ("Okay, we'll stop there", call. = FALSE) 101 | } 102 | } 103 | } else { 104 | warning (msg) 105 | } 106 | } 107 | 108 | invisible (has_duplicates) 109 | } 110 | -------------------------------------------------------------------------------- /man/dodgr_isodists.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/iso.R 3 | \name{dodgr_isodists} 4 | \alias{dodgr_isodists} 5 | \title{Calculate isodistance contours from specified points.} 6 | \usage{ 7 | dodgr_isodists( 8 | graph, 9 | from = NULL, 10 | dlim = NULL, 11 | concavity = 0, 12 | length_threshold = 0, 13 | contract = TRUE, 14 | heap = "BHeap" 15 | ) 16 | } 17 | \arguments{ 18 | \item{graph}{\code{data.frame} or equivalent object representing the network 19 | graph. For \code{dodgr} street networks, this may be a network derived from either 20 | \pkg{sf} or \pkg{silicate} ("sc") data, generated with 21 | \link{weight_streetnet}.} 22 | 23 | \item{from}{Vector or matrix of points \strong{from} which isodistances are to 24 | be calculated.} 25 | 26 | \item{dlim}{Vector of desired limits of isodistances in metres.} 27 | 28 | \item{concavity}{A value between 0 and 1, with 0 giving (generally smoother 29 | but less detailed) convex iso-contours and 1 giving highly concave (and 30 | generally more detailed) contours.} 31 | 32 | \item{length_threshold}{The minimal length of a segment of the iso-contour 33 | to be made more convex according to the 'concavity` parameter.. Low values 34 | will produce highly detailed hulls which may cause problems; if in doubt, or 35 | if odd results appear, increase this value.} 36 | 37 | \item{contract}{If \code{TRUE}, calculate isodists only to vertices in the 38 | contract graph, in other words, only to junction vertices.} 39 | 40 | \item{heap}{Type of heap to use in priority queue. Options include 41 | Fibonacci Heap (default; \code{FHeap}), Binary Heap (\code{BHeap}), 42 | Trinomial Heap (\code{TriHeap}), Extended Trinomial Heap 43 | (\code{TriHeapExt}, and 2-3 Heap (\code{Heap23}).} 44 | } 45 | \value{ 46 | A single \code{data.frame} of isodistances as points sorted anticlockwise 47 | around each origin (\code{from}) point, with columns denoting the \code{from} points 48 | and \code{dlim} value(s). The isodistance contours are given as \code{id} values and 49 | associated coordinates of the series of points from each \code{from} point at the 50 | specified isodistances. 51 | } 52 | \description{ 53 | Calculates isodistances from input \code{data.frame} objects 54 | (\code{graph}), which must minimally contain three columns of \code{from}, \code{to}, and 55 | \code{d} or \code{dist}. If an additional column named \code{weight} or \code{wt} is present, 56 | shortest paths are calculated according to values specified in that column, 57 | while resultant isodistances are calculated from the \code{d} or \code{dist} column. 58 | That is, the paths tracing isodistances from any point will be calculated 59 | according to the minimal total sum of \code{weight} values (if present), while 60 | reported isodistances will be total sums of \code{dist} values. 61 | 62 | Graphs derived from Open Street Map street networks, via the 63 | \link{weight_streetnet} function, have columns labelled \code{d}, \code{d_weighted}, 64 | \code{time}, and \code{time_weighted}. For these inputs, isodistances are always 65 | routed using \code{d_weighted} (or \code{t_weighted} for times), while final 66 | isodistances are sums of values of \code{d} (or \code{t} for times)- that is, of 67 | un-weighted distances or times - along those paths. 68 | 69 | Function is fully vectorized to calculate accept vectors of central points 70 | and vectors defining multiple isodistances. Calculations use by default 71 | parallel computation with the maximal number of available cores or threads. 72 | This number can be reduced by specifying a value via 73 | \verb{RcppParallel::setThreadOptions (numThreads = )}. 74 | } 75 | \examples{ 76 | graph <- weight_streetnet (hampi) 77 | from <- sample (graph$from_id, size = 100) 78 | dlim <- c (1, 2, 5, 10, 20) * 100 79 | d <- dodgr_isodists (graph, from = from, dlim) 80 | } 81 | \seealso{ 82 | Other iso: 83 | \code{\link{dodgr_isochrones}()}, 84 | \code{\link{dodgr_isoverts}()} 85 | } 86 | \concept{iso} 87 | -------------------------------------------------------------------------------- /man/dodgr_isoverts.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/iso.R 3 | \name{dodgr_isoverts} 4 | \alias{dodgr_isoverts} 5 | \title{Calculate isodistance or isochrone vertices from specified points.} 6 | \usage{ 7 | dodgr_isoverts(graph, from = NULL, dlim = NULL, tlim = NULL, heap = "BHeap") 8 | } 9 | \arguments{ 10 | \item{graph}{\code{data.frame} or equivalent object representing the network 11 | graph. For \code{dodgr} street networks, this must be a network derived from 12 | \pkg{silicate} ("sc") data, generated with \link{weight_streetnet}. This 13 | function does not work with networks derived from \pkg{sf} data.} 14 | 15 | \item{from}{Vector or matrix of points \strong{from} which isodistances or 16 | isochrones are to be calculated.} 17 | 18 | \item{dlim}{Vector of desired limits of isodistances in metres.} 19 | 20 | \item{tlim}{Vector of desired limits of isochrones in seconds} 21 | 22 | \item{heap}{Type of heap to use in priority queue. Options include 23 | Fibonacci Heap (default; \code{FHeap}), Binary Heap (\code{BHeap}), 24 | Trinomial Heap (\code{TriHeap}), Extended Trinomial Heap 25 | (\code{TriHeapExt}, and 2-3 Heap (\code{Heap23}).} 26 | } 27 | \value{ 28 | A single \code{data.frame} of vertex IDs, with columns denoting the \code{from} 29 | points and \code{tlim} value(s). The isochrones are given as \code{id} values and 30 | associated coordinates of the series of points from each \code{from} point at the 31 | specified isochrone times. 32 | 33 | Isoverts are calculated by default using parallel computation with the 34 | maximal number of available cores or threads. This number can be reduced by 35 | specifying a value via \verb{RcppParallel::setThreadOptions (numThreads = )}. 36 | } 37 | \description{ 38 | Returns lists of all network vertices contained within 39 | isodistance or isochrone contours. Input objects must be \code{data.frame} 40 | objects (\code{graph}), which must minimally contain three columns of \code{from}, 41 | \code{to}, and \code{d} or \code{dist}. If an additional column named \code{weight} or \code{wt} is 42 | present, iso contours are evaluate via shortest paths calculated according 43 | to values specified in that column, while resultant values of iso contours 44 | are calculated from the \code{d} or \code{dist} column. That is, the paths tracing iso 45 | contours from any point will be calculated according to the minimal total 46 | sum of \code{weight} values (if present), while reported iso contours will be 47 | total sums of \code{dist} values. 48 | 49 | Graphs derived from Open Street Map street networks, via the 50 | \link{weight_streetnet} function, have columns labelled \code{d}, \code{d_weighted}, 51 | \code{time}, and \code{time_weighted}. For these inputs, iso contours are always 52 | routed using \code{d_weighted} (or \code{t_weighted} for times), while final iso 53 | contours reflect sums of values of \code{d} (or \code{t} for times) - that is, of 54 | un-weighted distances or times - along those paths. 55 | 56 | Function is fully vectorized to accept vectors of central points and vectors 57 | defining multiple isochrone or isodistance thresholds. Provide one or more 58 | \code{dlim} values for isodistances, or one or more \code{tlim} values for isochrones. 59 | Calculations use by default parallel computation with the maximal number of 60 | available cores or threads. This number can be reduced by specifying a value 61 | via \verb{RcppParallel::setThreadOptions (numThreads = )}. 62 | } 63 | \examples{ 64 | \dontrun{ 65 | # Use osmdata package to extract 'SC'-format data: 66 | library (osmdata) 67 | dat <- opq ("hampi india") \%>\% 68 | add_osm_feature (key = "highway") \%>\% 69 | osmdata_sc () 70 | graph <- weight_streetnet (dat) 71 | from <- sample (graph$.vx0, size = 100) 72 | tlim <- c (5, 10, 20, 30, 60) * 60 # times in seconds 73 | x <- dodgr_isoverts (graph, from = from, tlim) 74 | } 75 | } 76 | \seealso{ 77 | Other iso: 78 | \code{\link{dodgr_isochrones}()}, 79 | \code{\link{dodgr_isodists}()} 80 | } 81 | \concept{iso} 82 | -------------------------------------------------------------------------------- /src/deduplicate.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "deduplicate.h" 3 | 4 | void deduplicate::update_dupl_edge_map (deduplicate::EdgeMapType &edge_map, 5 | const deduplicate::str_pair &this_pair, const double &val) 6 | { 7 | if (edge_map.find (this_pair) == edge_map.end ()) 8 | { 9 | edge_map.emplace (this_pair, val); 10 | } else 11 | { 12 | double val_min = edge_map.find (this_pair)->second; 13 | if (val < val_min) 14 | { 15 | edge_map.erase (this_pair); 16 | edge_map.emplace (this_pair, val); 17 | } 18 | } 19 | } 20 | 21 | //' De-duplicate edges by replacing with minimal weighted distances and times 22 | //' 23 | //' @param graph Full graph in any form 24 | //' @param fr_col Name of column holding from edge labels 25 | //' @param to_col Name of column holding to edge labels 26 | //' @param d_col Name of column holding weighted distances 27 | //' @param t_col Name of column holding weighted times (or "" if no weighted 28 | //' times). 29 | //' @return A `data.frame` of 3 columns: 'from', 'to', and 'd', where 'd' is the 30 | //' minimal value taken from all duplicated edges. If 't_col' is specified, the 31 | //' equivalent minimal times are in the lower half of the result. 32 | //' @noRd 33 | // [[Rcpp::export]] 34 | Rcpp::DataFrame rcpp_deduplicate (const Rcpp::DataFrame &graph, const std::string fr_col, const std::string to_col, 35 | const std::string d_col, const std::string t_col) 36 | { 37 | const bool has_time = t_col.length () > 0L; 38 | 39 | std::unordered_set < deduplicate::str_pair, deduplicate::str_pair_hash > edge_pair_set; 40 | std::unordered_set < deduplicate::str_pair, deduplicate::str_pair_hash > edge_pair_dupl; 41 | 42 | const std::vector fr = graph [fr_col]; 43 | const std::vector to = graph [to_col]; 44 | const std::vector d = graph [d_col]; 45 | std::vector t (graph.nrow ()); 46 | if (has_time) { 47 | std::vector t = graph [t_col]; 48 | } 49 | 50 | const size_t n = static_cast (graph.nrow ()); 51 | 52 | // First loop to collect duplicated edges 53 | for (size_t i = 0; i < n; i++) 54 | { 55 | deduplicate::str_pair this_pair {fr [i], to [i]}; 56 | if (edge_pair_set.find (this_pair) != edge_pair_set.end ()) 57 | { 58 | edge_pair_dupl.emplace (this_pair); 59 | } 60 | edge_pair_set.emplace (this_pair); 61 | } 62 | 63 | // Then aggregate values for each duplicate 64 | deduplicate::EdgeMapType edge_map_d, edge_map_t; 65 | 66 | for (size_t i = 0; i < n; i++) 67 | { 68 | deduplicate::str_pair this_pair {fr [i], to [i]}; 69 | 70 | if (edge_pair_dupl.find (this_pair) == edge_pair_dupl.end ()) 71 | { 72 | continue; 73 | } 74 | 75 | deduplicate::update_dupl_edge_map (edge_map_d, this_pair, d [i]); 76 | 77 | if (has_time) 78 | { 79 | deduplicate::update_dupl_edge_map (edge_map_t, this_pair, t [i]); 80 | } 81 | 82 | } 83 | 84 | const size_t ndupl = edge_map_d.size (); 85 | std::vector fr_dupl (ndupl), to_dupl (ndupl); 86 | std::vector d_dupl (ndupl); 87 | 88 | size_t i = 0; 89 | for (auto e: edge_map_d) 90 | { 91 | fr_dupl [i] = e.first.first; 92 | to_dupl [i] = e.first.second; 93 | d_dupl [i++] = e.second; 94 | } 95 | if (has_time) 96 | { 97 | fr_dupl.resize (ndupl * 2); 98 | to_dupl.resize (ndupl * 2); 99 | d_dupl.resize (ndupl * 2); 100 | for (auto e: edge_map_t) 101 | { 102 | fr_dupl [i] = e.first.first; 103 | to_dupl [i] = e.first.second; 104 | d_dupl [i++] = e.second; 105 | } 106 | } 107 | 108 | Rcpp::DataFrame res = Rcpp::DataFrame::create ( 109 | Rcpp::Named ("from") = fr_dupl, 110 | Rcpp::Named ("to") = to_dupl, 111 | Rcpp::Named ("d") = d_dupl, 112 | Rcpp::_["stringsAsFactors"] = false); 113 | 114 | return res; 115 | } 116 | -------------------------------------------------------------------------------- /man/dodgr_isochrones.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/iso.R 3 | \name{dodgr_isochrones} 4 | \alias{dodgr_isochrones} 5 | \title{Calculate isochrone contours from specified points.} 6 | \usage{ 7 | dodgr_isochrones( 8 | graph, 9 | from = NULL, 10 | tlim = NULL, 11 | concavity = 0, 12 | length_threshold = 0, 13 | heap = "BHeap" 14 | ) 15 | } 16 | \arguments{ 17 | \item{graph}{\code{data.frame} or equivalent object representing the network 18 | graph. For \code{dodgr} street networks, this must be a network derived from 19 | \pkg{silicate} ("sc") data, generated with \link{weight_streetnet}. This 20 | function does not work with networks derived from \pkg{sf} data.} 21 | 22 | \item{from}{Vector or matrix of points \strong{from} which isochrones are to 23 | be calculated.} 24 | 25 | \item{tlim}{Vector of desired limits of isochrones in seconds} 26 | 27 | \item{concavity}{A value between 0 and 1, with 0 giving (generally smoother 28 | but less detailed) convex iso-contours and 1 giving highly concave (and 29 | generally more detailed) contours.} 30 | 31 | \item{length_threshold}{The minimal length of a segment of the iso-contour 32 | to be made more convex according to the 'concavity` parameter.. Low values 33 | will produce highly detailed hulls which may cause problems; if in doubt, or 34 | if odd results appear, increase this value.} 35 | 36 | \item{heap}{Type of heap to use in priority queue. Options include 37 | Fibonacci Heap (default; \code{FHeap}), Binary Heap (\code{BHeap}), 38 | Trinomial Heap (\code{TriHeap}), Extended Trinomial Heap 39 | (\code{TriHeapExt}, and 2-3 Heap (\code{Heap23}).} 40 | } 41 | \value{ 42 | A single \code{data.frame} of isochrones as points sorted anticlockwise 43 | around each origin (\code{from}) point, with columns denoting the \code{from} points 44 | and \code{tlim} value(s). The isochrones are given as \code{id} values and associated 45 | coordinates of the series of points from each \code{from} point at the specified 46 | isochrone times. 47 | 48 | Isochrones are calculated by default using parallel computation with the 49 | maximal number of available cores or threads. This number can be reduced by 50 | specifying a value via \verb{RcppParallel::setThreadOptions (numThreads = )}. 51 | } 52 | \description{ 53 | Calculates isochrones from input \code{data.frame} objects 54 | (\code{graph}), which must minimally contain three columns of \code{from}, \code{to}, and 55 | \code{t} or \code{time}. If an additional column named \code{t_weight} or \code{t_wt} is 56 | present, fastest paths are calculated according to values specified in that 57 | column, while resultant isochrones are calculated from the \code{t} or \code{time} 58 | column. That is, the paths tracing isochrones from any point will be 59 | calculated according to the minimal total sum of \code{t_weight} values (if 60 | present), while reported isochrones will be total sums of \code{time} values. 61 | 62 | Graphs derived from Open Street Map street networks, via the 63 | \link{weight_streetnet} function, have columns labelled \code{d}, \code{d_weighted}, 64 | \code{time}, and \code{time_weighted}. For these inputs, isochrones are always routed 65 | using \code{t_weighted}, while final isochrones are sums of values of \code{t} - that 66 | is, of un-weighted distances or times - along those paths. 67 | 68 | Function is fully vectorized to calculate accept vectors of central points 69 | and vectors defining multiple isochrones. Calculations use by default 70 | parallel computation with the maximal number of available cores or threads. 71 | This number can be reduced by specifying a value via 72 | \verb{RcppParallel::setThreadOptions (numThreads = )}. 73 | } 74 | \examples{ 75 | \dontrun{ 76 | # Use osmdata package to extract 'SC'-format data: 77 | library (osmdata) 78 | dat <- opq ("hampi india") \%>\% 79 | add_osm_feature (key = "highway") \%>\% 80 | osmdata_sc () 81 | graph <- weight_streetnet (dat) 82 | from <- sample (graph$.vx0, size = 100) 83 | tlim <- c (5, 10, 20, 30, 60) * 60 # times in seconds 84 | x <- dodgr_isochrones (graph, from = from, tlim) 85 | } 86 | } 87 | \seealso{ 88 | Other iso: 89 | \code{\link{dodgr_isodists}()}, 90 | \code{\link{dodgr_isoverts}()} 91 | } 92 | \concept{iso} 93 | -------------------------------------------------------------------------------- /R/from-to-params.R: -------------------------------------------------------------------------------- 1 | # functions to process 'from' and 'to' parameters of distance, iso, and flow 2 | # functions, mostly via the `to_from_index_with_tp` function, which 3 | # pre-processes the `from` and `to` arguments, returning both indices into the 4 | # vertex map, and corresponding ID values where those exist. 5 | # 6 | # ------------------------------------------------------------------------ 7 | 8 | 9 | #' Get lists of 'from' and 'to' indices, potentially mapped on to compound 10 | #' junctions for graphs with turn penalties. 11 | #' 12 | #' This function calls the following `fill_to_from_index` to generate the actual 13 | #' values. For graphs with turn penalties, it also returns the modified version 14 | #' of the graph including compound junctions. 15 | #' @noRd 16 | to_from_index_with_tp <- function (graph, from, to) { 17 | 18 | gr_cols <- dodgr_graph_cols (graph) 19 | is_spatial <- is_graph_spatial (graph) 20 | vert_map <- make_vert_map (graph, gr_cols, is_spatial) 21 | 22 | from_index <- 23 | fill_to_from_index (graph, vert_map, gr_cols, from, from = TRUE) 24 | to_index <- fill_to_from_index (graph, vert_map, gr_cols, to, from = FALSE) 25 | 26 | compound <- (get_turn_penalty (graph) > 0.0) 27 | graph_compound <- compound_junction_map <- NULL 28 | if (compound) { 29 | if (methods::is (graph, "dodgr_contracted")) { 30 | warning ( 31 | "graphs with turn penalties should be submitted in full, ", 32 | "not contracted form;\nsubmitting contracted graphs may ", 33 | "produce unexpected behaviour." 34 | ) 35 | } 36 | res <- create_compound_junctions (graph) 37 | graph_compound <- res$graph 38 | compound_junction_map <- res$edge_map 39 | 40 | # remap any 'from' and 'to' vertices to compound junction versions: 41 | vert_map <- make_vert_map (graph_compound, gr_cols, is_spatial) 42 | 43 | from_index <- remap_tf_index_for_tp (from_index, vert_map, from = TRUE) 44 | to_index <- remap_tf_index_for_tp (to_index, vert_map, from = FALSE) 45 | } 46 | 47 | return (list ( 48 | from = from_index, to = to_index, vert_map = vert_map, 49 | compound = compound, graph_compound = graph_compound, 50 | compound_junction_map = compound_junction_map 51 | )) 52 | } 53 | 54 | #' fill_to_from_index 55 | #' 56 | #' @noRd 57 | fill_to_from_index <- function (graph, 58 | vert_map, 59 | gr_cols, 60 | pts, 61 | from = TRUE) { 62 | 63 | id <- NULL 64 | if (is.null (pts)) { 65 | index <- seq_len (nrow (vert_map)) - 1L 66 | if (!is.null (vert_map$vert)) { 67 | id <- vert_map$vert 68 | } 69 | } else { 70 | index_id <- get_index_id_cols (graph, gr_cols, vert_map, pts) 71 | if (any (is.na (index_id$id))) { 72 | stop ("Unable to match all routing points to graph vertices") 73 | } 74 | index <- index_id$index - 1L # 0-based 75 | id <- index_id$id 76 | } 77 | 78 | list (index = index, id = id) 79 | } 80 | 81 | #' Remap 'from_index' and 'to_index' values on to the compound junctions present 82 | #' in 'vert_map'. 83 | #' 84 | #' @param index Either 'from_index' or 'to_index' calculated 85 | #' @noRd 86 | remap_tf_index_for_tp <- function (index, vert_map, from = TRUE) { 87 | 88 | vert_index <- match (index$id, vert_map$vert) 89 | if (from) { 90 | no_start <- which (!grepl ("\\_start$", index$id)) 91 | vert_index_id <- index$id 92 | vert_index_id [no_start] <- paste0 (index$id [no_start], "_start") 93 | } else { 94 | no_end <- which (!grepl ("\\_end$", index$id)) 95 | vert_index_id <- index$id 96 | vert_index_id [no_end] <- paste0 (index$id [no_end], "_end") 97 | } 98 | vert_index_comp <- match (vert_index_id, vert_map$vert) 99 | na_index <- which (!is.na (vert_index_comp)) 100 | vert_index [na_index] <- vert_index_comp [na_index] 101 | 102 | index$index <- vert_index - 1L # zero-based 103 | index$id [na_index] <- vert_index_id [na_index] 104 | 105 | return (index) 106 | } 107 | -------------------------------------------------------------------------------- /src/heaps/heap23.h: -------------------------------------------------------------------------------- 1 | #ifndef HEAP23_H 2 | #define HEAP23_H 3 | /* File heap23.h - 2-3 Heap 4 | * ---------------------------------------------------------------------------- 5 | * Mark Padgham, adapted from code by Shane Saunders 6 | */ 7 | #include "heap.h" /* Defines the base class for heaps. */ 8 | 9 | /* This 2-3 heap implementation uses the same kind of pointer structure for 10 | * nodes as the Fibonacci heap does. 11 | * 12 | * This 2-3 heap implementation was designed for use with Dijkstra's single 13 | * source shortest path algorithm. Nodes have a vertex number and a key. 14 | * This source code could easily be modified to use a pointer (void *) to any 15 | * structure, rather than using vertex numbers as an index. 16 | */ 17 | 18 | 19 | /*** Option to print debugging information. Use 1 for yes, or 0 for no. ***/ 20 | #define HEAP23_DUMP 0 21 | 22 | 23 | 24 | /* --- Heap23Node --- 25 | * Class for node objects in the 2-3 heap. 26 | * 27 | * The pointer 'parent' points to a nodes parent, and 'child' points to the 28 | * highest dimension child in a circular doubly linked list of child nodes. 29 | * The circular doubly linked list of child nodes is maintained using the 30 | * sibling pointers 'left' and 'right'. The parent pointer of root nodes is 31 | * NULL. 32 | * 33 | * The remaining structure fields are: 34 | * dim - the nodes dimension. 35 | * key - the nodes key. 36 | * item - the number of the item that the node is associated with. 37 | * 38 | * In this implementation, dimensioning of nodes begins at zero, so the 39 | * dimension of a single node with no children is zero. 40 | */ 41 | class Heap23Node { 42 | public: 43 | Heap23Node *parent; 44 | Heap23Node *child; 45 | Heap23Node *left, *right; 46 | size_t dim; 47 | double key; 48 | size_t item; 49 | }; 50 | 51 | /* The structure type for a 2-3 heap. 52 | * 53 | * trees - An array of pointers to trees at root level in the heap. Entry i 54 | * in the array points to the root node of a tree that has nodes of 55 | * dimension i on the main trunk. 56 | * nodes - An array of pointers to nodes in the heap. Nodes are indexed 57 | * according to their vertex number. This array can then be used to 58 | * look up the node for corresponding to a vertex number, and is 59 | * useful when freeing space taken up by the heap. 60 | * maxNodes - The maximum number of nodes allowed in the heap. 61 | * maxTrees - The maximum number of trees allowed in the heap (calculated from 62 | * maxNodes). 63 | * itemCount - The current number of nodes in the heap. 64 | * treeSum - The binary value represented by trees in the heap. 65 | * By maintaining this it is easy to keep track of the maximum rank 66 | * tree in the heap. 67 | * compCount - can be used for experimental purposes when counting the number 68 | * of key comparisons. 69 | */ 70 | class Heap23 : public Heap { 71 | public: 72 | Heap23(size_t n); 73 | ~Heap23(); 74 | 75 | void insert(size_t item, double k); 76 | size_t deleteMin(); 77 | void decreaseKey(size_t item, double newValue); 78 | size_t nItems() const { return itemCount; } 79 | 80 | long int nComps() const { return compCount; } 81 | 82 | void dump() const; 83 | 84 | double getmin() { 85 | return 0.0; // MP: dummy value not implemented yet 86 | } 87 | 88 | private: 89 | Heap23Node **trees; 90 | Heap23Node **nodes; 91 | size_t maxNodes, maxTrees, itemCount, treeSum; 92 | long compCount; 93 | 94 | void meld(Heap23Node *tree_list); 95 | void removeNode(Heap23Node *cutNode); 96 | 97 | static size_t merge(Heap23Node **a, Heap23Node **b); 98 | static void trimExtraNode(Heap23Node *x); 99 | static void addChild(Heap23Node *p, Heap23Node *c); 100 | static void replaceNode(Heap23Node *oldNode, Heap23Node *newNode); 101 | static void swapTrunks(Heap23Node *lowNode, Heap23Node *highNode); 102 | 103 | static void dumpNodes(Heap23Node *ptr, size_t level); 104 | }; 105 | 106 | /*---------------------------------------------------------------------------*/ 107 | #endif 108 | -------------------------------------------------------------------------------- /R/utils.R: -------------------------------------------------------------------------------- 1 | null_to_na <- function (x) { 2 | 3 | if (length (x) == 0) { 4 | x <- NA 5 | } 6 | return (x) 7 | } 8 | 9 | #' Match the heap arg and convert graph is necessary 10 | #' 11 | #' @param heap Name of heap as passed to `dodgr_dists` 12 | #' @param graph `data.frame` of graph edges 13 | #' @return List of matched heap arg and potentially converted graph 14 | #' @noRd 15 | get_heap <- function (heap, 16 | graph) { 17 | 18 | heaps <- c ("FHeap", "BHeap", "TriHeap", "TriHeapExt", "Heap23", "set") 19 | heap <- match.arg (arg = heap, choices = heaps) 20 | 21 | list (heap = heap, graph = graph) 22 | } 23 | 24 | #' Get appropriate measure for geodist distances. 25 | #' 26 | #' Default measure is "cheap", but that becomes inaccurate beyond around 100km. 27 | #' This function works out the approximate maximal graph distances, and 28 | #' determines an appropriate measure based on that. Note that "geodesic" 29 | #' distances are not used, as calculation times for those are enormously longer 30 | #' than either cheap or Haversine. 31 | #' 32 | #' Measures for graphs are stored in `options("dodgr_dist_measure")`, as a list 33 | #' with each measure named after the graph hash. 34 | #' 35 | #' @return "cheap" if maximal distances are < 100km, otherwise "haversine". 36 | #' @noRd 37 | get_geodist_measure <- function (graph) { 38 | 39 | hash <- attr (graph, "hash") 40 | measure_list <- getOption ("dodgr_dist_measure", "") 41 | 42 | has_measure <- !is.null (hash) 43 | has_single_measure <- FALSE 44 | if ("all" %in% names (measure_list)) { 45 | has_single_measure <- TRUE 46 | } else if (has_measure) { 47 | has_measure <- any (nzchar (measure_list)) && hash %in% names (measure_list) 48 | } 49 | 50 | if (has_single_measure) { 51 | measure <- measure_list [["all"]] 52 | } else if (has_measure) { 53 | measure <- measure_list [[hash]] 54 | } else { 55 | dmax <- max_spatial_dist (graph) / 1000 56 | measure <- ifelse (dmax < 100, "cheap", "haversine") 57 | 58 | # This is also called at the start of SC construction, before graph has 59 | # any hash. 60 | if (!is.null (hash)) { 61 | if (!any (nzchar (measure_list))) { 62 | measure_list <- NULL 63 | } 64 | measure_list <- c (measure_list, measure) 65 | names (measure_list) [length (measure_list)] <- eval (hash) 66 | options ("dodgr_dist_measure" = measure_list) 67 | } 68 | } 69 | 70 | return (measure) 71 | } 72 | 73 | #' Force \link{weight_streetnet} to use geodesic distances. 74 | #' 75 | #' Distances by default are Mapbox "cheap" distances if maximal network 76 | #' distances are < 100km, otherwise Haversine distances. Calling this function 77 | #' forces all calls to \link{weight_streetnet} from that point on to use 78 | #' geodesic distances. These are more computationally expensive to calculate, 79 | #' and weighting networks will likely take more time. 80 | #' 81 | #' @param unset Calling this function with `unset = TRUE` reverts distance 82 | #' calculations to those described above, rather than geodesic. 83 | #' @return Nothing; the function is called for its side-effect only of setting 84 | #' distance calculations to geodesic. 85 | #' 86 | #' @family extraction 87 | #' @examples 88 | #' net0 <- weight_streetnet (hampi) # Default "cheap" method 89 | #' dodgr_streetnet_geodesic () 90 | #' net1 <- weight_streetnet (hampi) 91 | #' cor (net0$d, net1$d) # Strongly correlated, but not perfect 92 | #' max (abs (net0$d - net1$d)) # in metres 93 | #' @export 94 | dodgr_streetnet_geodesic <- function (unset = FALSE) { 95 | 96 | if (unset) { 97 | options ("dodgr_dist_measure" = NULL) 98 | msg <- "revert to default measures" 99 | } else { 100 | options ("dodgr_dist_measure" = c (all = "geodesic")) 101 | msg <- "use the geodesic measure" 102 | } 103 | 104 | objs <- ls (envir = .GlobalEnv) 105 | objs_are_graphs <- vapply (objs, function (o) { 106 | inherits (get (o), "dodgr_streetnet") 107 | }, logical (1L)) 108 | if (any (objs_are_graphs)) { 109 | message ( 110 | "Only graphs created from this point on with ", 111 | "'weight_streetnet()' will ", 112 | msg 113 | ) 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /R/graph-functions-spatial.R: -------------------------------------------------------------------------------- 1 | # Miscellaneous **non-exported** graph functions for spatial graphs 2 | 3 | #' preprocess spatial columns of graph 4 | #' 5 | #' @noRd 6 | preprocess_spatial_cols <- function (graph) { 7 | 8 | gr_cols <- dodgr_graph_cols (graph) 9 | if (is.na (gr_cols$from) || is.na (gr_cols$to)) { 10 | scols <- find_spatial_cols (graph) 11 | graph$from_id <- scols$xy_id$xy_fr_id 12 | graph$to_id <- scols$xy_id$xy_to_id 13 | } 14 | return (graph) 15 | } 16 | 17 | #' is_graph_spatial 18 | #' 19 | #' Is the graph spatial or not? 20 | #' @param graph A `data.frame` of edges 21 | #' @return `TRUE` is `graph` is spatial, otherwise `FALSE` 22 | #' @noRd 23 | is_graph_spatial <- function (graph) { 24 | 25 | ncol (graph) > 4 & 26 | (any (grepl ( 27 | "^x|x$", 28 | names (graph) [find_fr_col (graph)], 29 | ignore.case = TRUE 30 | )) | 31 | any (grepl ( 32 | "^y|y$", 33 | names (graph) [find_to_col (graph)], 34 | ignore.case = TRUE 35 | )) | 36 | any (grepl ("lon", names (graph), ignore.case = TRUE)) | 37 | any (grepl ("lat", names (graph), ignore.case = TRUE))) 38 | } 39 | 40 | #' find_spatial_cols 41 | #' 42 | #' @return `fr_col` and `to_col` as vectors of 2 values of `x` 43 | #' then `y` coordinates 44 | #' 45 | #' @noRd 46 | find_spatial_cols <- function (graph) { 47 | 48 | graph <- tbl_to_df (graph) 49 | 50 | fr_col <- find_fr_col (graph) 51 | to_col <- find_to_col (graph) 52 | 53 | if (length (fr_col) < 2 || length (to_col) < 2) { 54 | stop (paste0 ( 55 | "Graph appears to be spatial yet unable to ", 56 | "extract coordinates." 57 | )) 58 | } 59 | 60 | if (length (fr_col) == 3) { 61 | frx_col <- find_xy_col (graph, fr_col, x = TRUE) 62 | fry_col <- find_xy_col (graph, fr_col, x = FALSE) 63 | frid_col <- fr_col [which (!fr_col %in% c (frx_col, fry_col))] 64 | fr_col <- c (frx_col, fry_col) 65 | xy_fr_id <- graph [, frid_col] 66 | if (!is.character (xy_fr_id)) { 67 | xy_fr_id <- paste0 (xy_fr_id) 68 | } 69 | } else { # len == 2, so must be only x-y 70 | if (length (grep ("lon|lat|x|y", names (graph) [fr_col])) != 2) { 71 | stop ("Unable to determine coordinate columns of graph") 72 | } # nocov 73 | xy_fr_id <- paste0 ( 74 | graph [, fr_col [1]], "-", 75 | graph [, fr_col [2]] 76 | ) 77 | } 78 | 79 | if (length (to_col) == 3) { 80 | tox_col <- find_xy_col (graph, to_col, x = TRUE) 81 | toy_col <- find_xy_col (graph, to_col, x = FALSE) 82 | toid_col <- to_col [which (!to_col %in% c (tox_col, toy_col))] 83 | to_col <- c (tox_col, toy_col) 84 | xy_to_id <- graph [, toid_col] 85 | if (!is.character (xy_to_id)) { 86 | xy_to_id <- paste0 (xy_to_id) 87 | } 88 | } else { # len == 2, so must be only x-y 89 | if (length (grep ("lon|lat|x|y", names (graph) [to_col])) != 2) { 90 | stop ("Unable to determine coordinate columns of graph") 91 | } # nocov 92 | xy_to_id <- paste0 ( 93 | graph [, to_col [1]], "-", 94 | graph [, to_col [2]] 95 | ) 96 | } 97 | 98 | list ( 99 | fr_col = fr_col, 100 | to_col = to_col, 101 | xy_id = data.frame ( 102 | xy_fr_id = xy_fr_id, 103 | xy_to_id = xy_to_id, 104 | stringsAsFactors = FALSE 105 | ) 106 | ) 107 | } 108 | 109 | max_spatial_dist <- function (graph) { 110 | 111 | sp_cols <- find_spatial_cols (graph) 112 | graph_xy <- graph [, c (sp_cols$fr_col, sp_cols$to_col)] 113 | # Rename because 'sc' have different names: 114 | names (graph_xy) <- c ("from_lon", "from_lat", "to_lon", "to_lat") 115 | 116 | rx <- range (c ( 117 | range (graph_xy$from_lon), 118 | range (graph_xy$to_lon) 119 | )) 120 | ry <- range (c ( 121 | range (graph_xy$from_lat), 122 | range (graph_xy$to_lat) 123 | )) 124 | xylims <- c (rx [1], ry [1], rx [2], ry [2]) 125 | 126 | suppressMessages ( 127 | geodist::geodist ( 128 | xylims [1:2], 129 | xylims [3:4], 130 | measure = "haversine" 131 | ) [1, 1] 132 | ) 133 | } 134 | -------------------------------------------------------------------------------- /man/dodgr_paths.Rd: -------------------------------------------------------------------------------- 1 | % Generated by roxygen2: do not edit by hand 2 | % Please edit documentation in R/paths.R 3 | \name{dodgr_paths} 4 | \alias{dodgr_paths} 5 | \title{Calculate lists of pair-wise shortest paths between points.} 6 | \usage{ 7 | dodgr_paths( 8 | graph, 9 | from, 10 | to, 11 | vertices = TRUE, 12 | pairwise = FALSE, 13 | heap = "BHeap", 14 | quiet = TRUE 15 | ) 16 | } 17 | \arguments{ 18 | \item{graph}{\code{data.frame} or equivalent object representing the network 19 | graph (see Details)} 20 | 21 | \item{from}{Vector or matrix of points \strong{from} which route paths are to 22 | be calculated (see Details)} 23 | 24 | \item{to}{Vector or matrix of points \strong{to} which route paths are to be 25 | calculated (see Details)} 26 | 27 | \item{vertices}{If \code{TRUE}, return lists of lists of vertices for each 28 | path, otherwise return corresponding lists of edge numbers from \code{graph}.} 29 | 30 | \item{pairwise}{If \code{TRUE}, calculate paths only between the ordered 31 | pairs of \code{from} and \code{to}. In this case, each of these must be the 32 | same length, and the output will contain paths the i-th members of each, and 33 | thus also be of that length.} 34 | 35 | \item{heap}{Type of heap to use in priority queue. Options include 36 | Fibonacci Heap (default; \code{FHeap}), Binary Heap (\code{BHeap}), 37 | \code{Radix}, Trinomial Heap (\code{TriHeap}), Extended Trinomial Heap 38 | (\code{TriHeapExt}, and 2-3 Heap (\code{Heap23}).} 39 | 40 | \item{quiet}{If \code{FALSE}, display progress messages on screen.} 41 | } 42 | \value{ 43 | List of list of paths tracing all connections between nodes such that 44 | if \code{x <- dodgr_paths (graph, from, to)}, then the path between 45 | \code{from[i]} and \code{to[j]} is \code{x [[i]] [[j]]}. Each individual path is then a 46 | vector of integers indexing into the rows of \code{graph} if \code{vertices = FALSE}, 47 | or into the rows of \code{dodgr_vertices (graph)} if \code{vertices = TRUE}. 48 | } 49 | \description{ 50 | Calculate lists of pair-wise shortest paths between points. 51 | } 52 | \note{ 53 | \code{graph} must minimally contain four columns of \code{from}, 54 | \code{to}, \code{dist}. If an additional column named \code{weight} or 55 | \code{wt} is present, shortest paths are calculated according to values 56 | specified in that column; otherwise according to \code{dist} values. Either 57 | way, final distances between \code{from} and \code{to} points are calculated 58 | according to values of \code{dist}. That is, paths between any pair of points 59 | will be calculated according to the minimal total sum of \code{weight} 60 | values (if present), while reported distances will be total sums of 61 | \code{dist} values. 62 | 63 | The \code{from} and \code{to} columns of \code{graph} may be either single 64 | columns of numeric or character values specifying the numbers or names of 65 | graph vertices, or combinations to two columns specifying geographical 66 | (longitude and latitude) coordinates. In the latter case, almost any sensible 67 | combination of names will be accepted (for example, \verb{fromx, fromy}, 68 | \verb{from_x, from_y}, or \verb{fr_lat, fr_lon}.) 69 | 70 | \code{from} and \code{to} values can be either two-column matrices of 71 | equivalent of longitude and latitude coordinates, or else single columns 72 | precisely matching node numbers or names given in \code{graph$from} or 73 | \code{graph$to}. If \code{to} is missing, pairwise distances are calculated 74 | between all points specified in \code{from}. If neither \code{from} nor 75 | \code{to} are specified, pairwise distances are calculated between all nodes 76 | in \code{graph}. 77 | } 78 | \examples{ 79 | graph <- weight_streetnet (hampi) 80 | from <- sample (graph$from_id, size = 100) 81 | to <- sample (graph$to_id, size = 50) 82 | dp <- dodgr_paths (graph, from = from, to = to) 83 | # dp is a list with 100 items, and each of those 100 items has 30 items, each 84 | # of which is a single path listing all vertiex IDs as taken from `graph`. 85 | 86 | # it is also possible to calculate paths between pairwise start and end 87 | # points 88 | from <- sample (graph$from_id, size = 5) 89 | to <- sample (graph$to_id, size = 5) 90 | dp <- dodgr_paths (graph, from = from, to = to, pairwise = TRUE) 91 | # dp is a list of 5 items, each of which just has a single path between each 92 | # pairwise from and to point. 93 | } 94 | \seealso{ 95 | Other distances: 96 | \code{\link{dodgr_distances}()}, 97 | \code{\link{dodgr_dists}()}, 98 | \code{\link{dodgr_dists_categorical}()}, 99 | \code{\link{dodgr_dists_nearest}()}, 100 | \code{\link{dodgr_times}()} 101 | } 102 | \concept{distances} 103 | --------------------------------------------------------------------------------