Computes American Community Survey (ACS) estimates for the area within a given drive time of each of a set of points, such as the locations of pre-kindergarten classrooms. Such an area is called an isochrone. The drive-time areas come from a routing service, either the 'Open Source Routing Machine' ('OSRM', < https://project-osrm.org/>) or 'openrouteservice' (< https://openrouteservice.org/>), and the ACS 5-year estimates of census tracts come from the Census Bureau (< https://www.census.gov/data/developers.html>) through the 'tidycensus' package, one state at a time. The tracts that overlap an area are combined by area weighting, which uses two weights: the share of each tract's area that lies inside, for counts and rates, and each tract's share of the overlapping area, for the medians and per-person values of three ACS tables (median household income, median home value, and per capita income). A median or per-person value from any other table is added up like a count. Counts, medians, per-person values, and five rates are returned with a margin of error at a chosen confidence level, 90 percent by default, and with a record of the settings, inputs, and package versions behind the run. The five rates are the poverty rate, the shares of households receiving Supplemental Nutrition Assistance Program (SNAP) benefits and Supplemental Security Income, the unemployment rate, and the labor force participation rate. The margins of error of counts and rates use the approximation formulas in chapter 8 of U.S. Census Bureau (2020) "Understanding and Using American Community Survey Data: What All Data Users Need to Know" < https://www.census.gov/programs-surveys/acs/library/handbooks/general.html>. For a count the formula for a sum is applied to the weighted tract estimates, with the weights treated as fixed. For a rate the default is the formula for a ratio. Its margin of error is at least as wide as that of the formula for a proportion, which the handbook gives for ratios whose numerator is part of the denominator, as it is in all five rates. The margins of error of medians and per-person values are an approximation made by the package.

catchmentACS describes the neighborhoods around a set of sites, such as pre-kindergarten (Pre-K) classrooms, with data from the American Community Survey (ACS). For each site and each drive time, such as 5, 10, and 15 minutes, it finds the area that can be reached by car within that time, the site's catchment. It then combines the ACS estimates of the census tracts that overlap the catchment into estimates for it: how many people live there, how many are below the poverty level, what share of households receive benefits from the Supplemental Nutrition Assistance Program (SNAP), and so on. Every estimate comes with a margin of error, the half-width of its 90 percent confidence interval, and with a record of the settings and inputs behind it.
For a count, each tract contributes the share of its own area that lies inside the drive-time area: a tract half inside contributes half of its count. This assumes that whatever a variable counts is spread evenly over a tract: the people below the poverty level, and not only the population as a whole. Each rate is the ratio of two counts computed this way. Medians and per-person values from three ACS tables (median household income, median home value, and per capita income) are averaged over the overlapping tracts instead, with weights proportional to the area each tract shares with the area. Those weights ignore how many people live in each tract, so such an average can be far from the median of the area itself. A median or per-person value from any other ACS table is added up like a count. The articles listed below describe the calculations and what they assume.
When catchmentACS is available on CRAN, install.packages("catchmentACS")
installs the released version. The development version is on GitHub:
# install.packages("pak")
pak::pak("joonho112/catchmentACS")
pak::pak() does not build the articles; they are on the package website,
https://joonho112.github.io/catchmentACS/articles/. Building drive-time areas
with the default routing service needs the osrm package, and the package's maps
need the leaflet package. Neither is installed with catchmentACS:
install.packages(c("osrm", "leaflet"))
Downloading ACS data needs a free Census API key, which can be requested at https://api.census.gov/data/key_signup.html and saved with:
tidycensus::census_api_key("YOUR_KEY_HERE", install = TRUE)
readRenviron("~/.Renviron")
The first line saves the key in ~/.Renviron for later sessions; the second
makes it usable in the session that is running. A key already saved there needs
overwrite = TRUE.
The key is needed only when catchmentACS downloads estimates: when no saved result
of them matches the call, or when force_refresh = TRUE. A run that supplies estimates through
acs =, like the example below, needs no key.
This product uses the Census Bureau Data API but is not endorsed or certified by the Census Bureau.
Downloaded estimates and drive-time areas are saved for the rest of the R
session, so a repeated call neither downloads nor routes again. To keep them
between sessions, add this line to the R startup file, for example with
usethis::edit_r_profile():
options(catchmentACS.cache_dir = tools::R_user_dir("catchmentACS", "cache"))
Saved results in that folder that go unused for 30 days are deleted.
?cacs_cache_dir gives the details.
Everything the example needs comes from three files that ship with the package.
They hold made-up data: 20 sites on a grid over a rectangle around Alabama,
drive-time areas drawn as circles around the sites, and random ACS estimates
for 911 small squares that stand in for census tracts. Given the areas and the
estimates, cacs_run() skips the steps that build the areas and download the
estimates, so the example contacts no service and needs no key.
library(catchmentACS)
library(dplyr)
library(sf) # needed to subset the bundled sf objects with [
sites <- readRDS(system.file(
"extdata", "legacy_2025_sites.rds", package = "catchmentACS"
))
iso <- readRDS(system.file(
"extdata", "legacy_2025_isochrones.rds", package = "catchmentACS"
))
acs <- readRDS(system.file(
"extdata", "sample_alabama_subset.rds", package = "catchmentACS"
))
one_site <- "AL_SITE_07"
result <- cacs_run(
sites = sites[sites$site_id == one_site, , drop = FALSE],
state = "AL",
precomputed_isochrones = iso[iso$site_id == one_site, , drop = FALSE],
acs = acs,
verbose = FALSE
)
tibble::as_tibble(result) |>
filter(variable %in% names(cacs_acs_default_rates)) |>
select(site_id, drive_time_min, variable, estimate, moe)
The call returns one row for each drive time and rate, with an estimate and its
margin of error, and the same for each ACS variable in acs. Down the column
for this site the poverty rate is 0.62 for the 5-minute area and 0.36 for the
15-minute area, and the share of households receiving SNAP benefits is 0.04 and
0.11. These numbers describe no real place. The bundled ACS values are random,
and the made-up squares are spaced apart rather than tiling the state, so the
5-minute area overlaps one square and the two wider areas overlap three: the
change down the column is the change from one square's values to an average of
three. The file ships with the package, so every reader gets the same numbers.
For some other sites a rate has no value, because one made-up estimate in
twenty is missing; the run then warns and leaves NA in estimate and moe.
AL_SITE_07 has no such row.
For a run on your own sites, the call is the same without
precomputed_isochrones = and acs =. The sites go in as a data frame with
the columns site_id, lon, and lat, or as an sf object of points with a
site_id column. The run then needs a routing service, a Census API key, and
an internet connection.
Getting started with catchmentACS
goes through the result column by column, and
Routing services, API keys, and offline use
covers the routing services and their keys.
ORS_API_KEY environment
variable, ors_api_key = in cacs_isochrone(), or
iso_args = list(ors_api_key = ...) in cacs_run(). The openrouteservice
route has been checked only with simulated responses from openrouteservice.Not implemented yet: Mapbox and r5r as routing services, weighting by
population, rates other than the five above, and ACS pulls of 1-year estimates,
block groups, counties, or several states at once. Asking for any of these
stops the run with an error naming what is missing. Weighting by population, a
different rates list, and several states in state = give the error no
matter what else is supplied. The other five give it only when the step that
would use them runs. With precomputed_isochrones = supplied the routing step
does not run, so provider = "mapbox" is accepted and left unused; with
acs = supplied the download step does not run, so
acs_args = list(survey = "acs1") is too.
The articles below are on the package website. vignette("name", package = "catchmentACS") opens one locally when the installed package includes the
articles. A version installed from CRAN includes them. A version installed from
GitHub includes them only when they are built during installation, as with
remotes::install_github("joonho112/catchmentACS", build_vignettes = TRUE), or
with devtools::install(build_vignettes = TRUE) from a local copy.
| Article | What it covers |
|---|---|
| Getting started with catchmentACS | A first run on the example data, and how to read the result. |
| A worked example with Alabama Pre-K sites | The five steps one at a time, then tables for a report. |
| Routing services, API keys, and offline use | Which services are implemented, what keys they need, and how to run without a network. |
| Mapping each step for one Birmingham site | Leaflet maps of the drive-time areas, the tracts, the weighted estimates, and the rates. |
| Article | What it covers |
|---|---|
| How the estimates are computed | The calculation from tracts to catchment estimates, and its assumptions. |
| Area weighting of tract estimates | The two weights, and when an area-weighted average stands in for a median. |
| Margins of error for combined estimates | How the margins of error of the tracts combine, and what the formulas leave out. |
| Rates and their margins of error | The five rate definitions and the two formulas for the margin of error of a rate. |
| Article | What it covers |
|---|---|
| Updating code written for versions 0.1 and 0.2 | The ten changes that reach code written for 0.1 or 0.2. |
| Updating code written for version 0.3 | What changed in 0.4. |
| Updating code written for version 0.4 | What changed in 0.5.0, including the corrected medians and per-person values. |
Help pages for the functions are on the website as well. ?cacs_run describes
every argument of the one call that runs the whole calculation.
catchmentACS is written by JoonHo Lee
(ORCID: 0009-0006-4019-8703).
citation("catchmentACS") gives the citation for the installed version, and
toBibtex(citation("catchmentACS")) gives it as a BibTeX entry.
MIT © JoonHo Lee
Bug reports and feature requests are welcome at https://github.com/joonho112/catchmentACS/issues.