The hardware and bandwidth for this mirror is donated by METANET, the Webhosting and Full Service-Cloud Provider.
If you wish to report a bug, or if you are interested in having us mirror your free-software or open-source project, please feel free to contact us at mirror[@]metanet.ch.

Routing services, API keys, and offline use

Two steps of cacs_run() contact services outside R. cacs_isochrone() asks a routing service, by default the Open Source Routing Machine (OSRM), for the area reachable from each site within each drive time. cacs_acs_prefetch() downloads American Community Survey (ACS) 5-year estimates for the census tracts of one state from the Census Bureau.

library(catchmentACS)
library(dplyr)
library(sf)  # needed to subset the bundled sf objects with [

What each service needs

Service Selected with What it needs
Public OSRM demo server provider = "osrm" and osrm_mode = "demo", the defaults The osrm package
An OSRM server that you run provider = "osrm" and osrm_mode = "docker" The osrm package and the server
openrouteservice provider = "ors" The openrouteservice package and an openrouteservice API key
Census Bureau API cacs_acs_prefetch(), which cacs_run() calls unless acs is supplied A Census API key
No service precomputed_isochrones and acs, both supplied to cacs_run() Drive-time areas and ACS estimates that you already have

cacs_run() passes osrm_mode, res, and some other settings of cacs_isochrone() through its argument iso_args, as in iso_args = list(osrm_mode = "docker").

Census API key

cacs_acs_prefetch() downloads the estimates through the tidycensus package, which is installed with catchmentACS. A download needs a Census API key in the environment variable CENSUS_API_KEY. The call below writes the key to your .Renviron file, from which later R sessions read it:

tidycensus::census_api_key("YOUR_KEY_HERE", install = TRUE)

Sys.setenv(CENSUS_API_KEY = "YOUR_KEY_HERE") sets the key for the current session only. The key is needed only to download: cacs_acs_prefetch() reads a result saved in the cache by an earlier download, without a key or an internet connection. By default, the cache keeps results only until the R session ends; the section “The cache” below describes a cache folder that keeps them for later sessions. If a download is needed and the key is not set, cacs_acs_prefetch() stops with an error before any request is sent. Estimates can be downloaded for the year values 2009 to 2024; ?cacs_acs_prefetch describes the download.

OSRM

With provider = "osrm", cacs_isochrone() builds the areas with the osrm package, which is not installed with catchmentACS. By default, the requests go to the public OSRM demo server, which needs no key:

# Needs the osrm package and an internet connection.
iso <- cacs_isochrone(
  sites = cacs_alabama_sites[1:3, ],
  drive_times = c(5, 10, 15),
  provider = "osrm",
  osrm_mode = "demo",
  res = 30L,
  verbose = TRUE
)

The osrm package pauses between requests to the demo server, which adds about 10 seconds for each site with the default settings. When the server reports that its limit on requests has been reached (HTTP status 429), cacs_isochrone() stops with an error; it returns no areas and saves none in the cache, not even those built before the limit was reached. An OSRM server that you run yourself, for example in a Docker container, has neither the pauses nor a limit shared with other users. osrm_mode = "docker" sends the requests to such a server, at http://0.0.0.0:5000/ unless another address is given in osrm.server, as below, or in the option catchmentACS.osrm_docker_server. cacs_run() drops osrm.server from iso_args with a warning, so with cacs_run() the option sets the address.

# Needs the osrm package and an OSRM server at this address.
iso <- cacs_isochrone(
  sites = cacs_alabama_sites[1:3, ],
  drive_times = c(5, 10, 15),
  provider = "osrm",
  osrm_mode = "docker",
  `osrm.server` = "http://localhost:5000/",
  verbose = TRUE
)

The osrm package draws the areas of a site from a grid of res by res points around it. The grid is sized for the longest drive time, so the areas for shorter drive times rest on fewer points. Without res, the grid has 30 by 30 points with osrm_mode = "demo" and 70 by 70 with osrm_mode = "docker"; a finer grid gives more detailed areas but sends more requests. Areas built with different values of res can differ, so giving res in the call keeps the grid the same when osrm_mode or the options change. ?cacs_isochrone describes the servers and the grid in more detail.

cacs_validate_osrm_endpoint() sends one small request to an OSRM server and returns a table with one row, in which quota_ok is TRUE when the server answered with HTTP status 200. ?cacs_validate_osrm_endpoint describes the other columns and the warning given when the server’s limit on requests has been reached. A server that cannot be reached, or does not answer within timeout seconds, gives quota_ok = FALSE and http_status = NA without an error.

Without server, the request goes to the public demo server, or to the address in the osrm package’s option osrm.server when that is set. A server that you run is checked only when its address is given in server:

# Needs an internet connection for the first call and a server that you
# run for the second.
cacs_validate_osrm_endpoint()
cacs_validate_osrm_endpoint(server = "http://localhost:5000", timeout = 2)

openrouteservice

With provider = "ors", cacs_isochrone() builds the areas with openrouteservice through the openrouteservice package, which is not installed with catchmentACS. It needs an openrouteservice API key. cacs_isochrone() takes the key from its argument ors_api_key, by default the value of the environment variable ORS_API_KEY; with cacs_run(), the key comes from the same variable or from iso_args = list(ors_api_key = ...). An empty key gives an error before any request is sent. A key set in .Renviron, like the Census key, stays out of scripts and reports.

# Needs the openrouteservice package, an API key, and an internet connection.
nzchar(Sys.getenv("ORS_API_KEY"))  # TRUE when the key is set

iso <- cacs_isochrone(
  sites = cacs_alabama_sites[1:3, ],
  drive_times = c(5, 10, 15),
  provider = "ors",
  ors_api_key = Sys.getenv("ORS_API_KEY"),
  verbose = TRUE
)

The areas from openrouteservice and from OSRM are computed in different ways, so they can differ for the same site and drive time. The package’s tests exercise this path against simulated openrouteservice responses only; the live-service tests it has cover OSRM (see NEWS.md).

Supplying drive-time areas and ACS estimates

cacs_run() does not build drive-time areas when it is given them in precomputed_isochrones, and does not download ACS estimates when it is given them in acs. precomputed_isochrones takes a table like the one that cacs_isochrone() returns, and acs one like the result of cacs_acs_prefetch(). cacs_validate_iso() and cacs_acs_validate() check that tables made with other tools have the columns and the coordinate reference systems that cacs_run() needs, and cacs_acs_validate() also checks that no tract has two rows for the same variable; they do not check the values of the estimates and margins of error. The negative codes that the Census Bureau’s data API puts in place of some estimates and margins of error, such as -555555555, pass these checks. cacs_run() treats these codes as missing values but uses any other negative value as it is (see ?cacs_intersect_weight, which also says when a warning is given).

The result carries a record of the call, the attribute cacs_run_provenance, whose element execution_path names the combination of inputs:

execution_path precomputed_isochrones acs Services it may contact
"5-call" not supplied not supplied the routing service and the Census Bureau API
"4-call-A" supplied not supplied the Census Bureau API
"4-call-B" not supplied supplied the routing service
"3-call" supplied supplied none

The number in each value counts the steps, out of five, that cacs_run() runs itself. The elements bypass_iso and bypass_acs of the same record are TRUE when the areas and the estimates, respectively, were supplied.

The example below draws on files that ship with the package. Their drive-time areas were drawn without a routing service: each is a circle around its site, with a radius of 5 km for the 5-minute area, 10 km for 10 minutes, and 15 km for 15 minutes. The columns that name the routing service were filled in when the file was made, with values like those of an OSRM result:

example_iso <- readRDS(system.file(
  "extdata", "legacy_2025_isochrones.rds", package = "catchmentACS"
))

example_iso |>
  sf::st_drop_geometry() |>
  count(provider, provider_requested, profile,
        osm_snapshot_date, ring_topology)
#> # A tibble: 1 × 6
#>   provider provider_requested profile osm_snapshot_date ring_topology     n
#>   <chr>    <chr>              <chr>   <chr>             <chr>         <int>
#> 1 osrm     osrm               car     2025-04-01        cumulative       60

cacs_run() copies these values into the columns provider, profile, and osm_snapshot_date of its result, so with areas supplied, its argument provider is only recorded in cacs_run_provenance and shown when the result is printed. The ACS file holds made-up estimates for small squares, spaced apart, that are used in place of census tracts. With both files, cacs_run() contacts no service:

example_sites <- readRDS(system.file(
  "extdata", "legacy_2025_sites.rds", package = "catchmentACS"
))
acs <- readRDS(system.file(
  "extdata", "sample_alabama_subset.rds", package = "catchmentACS"
))

one_site <- "AL_SITE_07"
iso_one <- example_iso |>
  filter(site_id == one_site, drive_time_min == 5L)
site_one <- example_sites |>
  filter(site_id == one_site)

out <- cacs_run(
  sites = site_one,
  state = "AL",
  year = 2023,
  drive_times = 5L,
  variables = unname(cacs_acs_default_vars),
  provider = "osrm",
  precomputed_isochrones = iso_one,
  acs = acs,
  weight_method = "area",
  output = "long",
  verbose = FALSE
)

attr(out, "cacs_run_provenance") |>
  as.data.frame() |>
  select(execution_path, provider, bypass_iso, bypass_acs,
         weight_method, output_format)
#>   execution_path provider bypass_iso bypass_acs weight_method output_format
#> 1         3-call     osrm       TRUE       TRUE          area          long

?cacs_run describes which other arguments are then only recorded.

What is not implemented yet

weight_method = "population", like the providers Mapbox and r5r, is accepted but not implemented yet. cacs_isochrone() gives its error for the two providers after checking its other arguments and before sending any request. In cacs_run(), weight_method = "population" gives an error before any step runs, while the two providers give theirs at the routing step, after the ACS estimates are downloaded unless acs is supplied. The three errors have the class catchmentACS_error_credential, which is also given for a missing or refused API key (see ?catchmentACS-conditions).

The cache

Unless the cache is turned off, cacs_isochrone() and cacs_acs_prefetch() save their results in the folder that cacs_cache_dir() returns. A later call that matches an earlier one reads the saved result instead of contacting the service; the help page of each function says what must match. For the drive-time areas, the sites themselves must match, among other things: their coordinates and their site_id values, along with the set of drive times, the provider, profile, osrm_mode, and res, and the installed versions of sf, PROJ, and GEOS. Adding one site to a run therefore builds the areas for all of the sites again.

By default, the folder is inside the temporary folder of the R session, which R deletes when the session ends. Saved results are kept for later sessions when the option catchmentACS.cache_dir or the environment variable CACS_CACHE_DIR names a folder that lasts, for example with options(catchmentACS.cache_dir = tools::R_user_dir("catchmentACS", "cache")) in the R startup file. Once per session, the results in such a folder that have not been used for 30 days are deleted; the option catchmentACS.cache_max_age_days sets another number of days (see ?cacs_cache_dir). cacs_set_cache(FALSE) turns the cache off for the rest of the session, and cacs_get_cache_state() reports the setting.

force_refresh = TRUE makes cacs_acs_prefetch() download the estimates again and replace the saved result, which needs the Census key; with cacs_run(), it is given as acs_args = list(force_refresh = TRUE). As long as they are kept, saved drive-time areas are used again, even if the map data of the routing service have changed since; a call with another osm_snapshot_date builds new ones. cacs_clear_cache() deletes saved results:

# Lists what the cache folder holds, then deletes the saved drive-time
# areas and ACS estimates.
cacs_cache_status()
cacs_clear_cache("isochrone", confirm = FALSE)
cacs_clear_cache("acs", confirm = FALSE)

cacs_acs_prefetch(write_gpkg = TRUE) also saves a downloaded result as a GeoPackage file, in the acs folder of the cache, for use in other GIS software. It is written even when the cache is off, but not when a saved result is read. The file is deleted when the session ends if the cache folder is the default one. In a folder that lasts between sessions, it is deleted together with its saved result or, if it has none, as when the cache was off, once it is as old as the saved results that are deleted; cacs_clear_cache() deletes it too. To keep a copy, write the result with sf::st_write().

cacs_intersect_weight(), the step of cacs_run() that combines the areas with the tracts, saves its results too, and its help page says what must match. The argument cache_dir of cacs_run() sets the folder for the saved results of all three functions.

A run that contacts both services

Without precomputed_isochrones and acs, cacs_run() builds the areas and downloads the estimates itself. The routing service is chosen with provider, and for OSRM the server with osrm_mode in iso_args. The call below uses the example sites cacs_alabama_sites, made-up points near Alabama city centers:

# Needs the osrm package, a Census API key, and an internet connection.
result <- cacs_run(
  sites = cacs_alabama_sites,  # or your own sites
  state = "AL",
  year = 2023,
  drive_times = c(5, 10, 15),
  variables = unname(cacs_acs_default_vars),
  provider = "osrm",
  iso_args = list(osrm_mode = "demo"),  # "docker" for a server that you run
  # With "docker", the server's address is set before the call with
  # options(catchmentACS.osrm_docker_server = "http://localhost:5000/")
  weight_method = "area",
  output = "long",
  verbose = TRUE
)

summary(result)$rates_per_site_moe
attr(result, "cacs_run_provenance")

These binaries (installable software) and packages are in development.
They may not be fully stable and should be used with caution. We make no claims about them.