Say you are moving to a new city and want to live near the amenities that are important to you. In this tutorial, we find the blocks that are within a 10-minute walk of a supermarket, a 5-minute walk of a restaurant, and a 20-minute walk of a frequent-transit stop. Then, we narrow those blocks to the overlap of two commutes. The example city is Somerville, Massachusetts.
Running this tutorial uses about 2,600 tokens.
Set up
Build a client, then read the pieces you need from the free catalog instead of memorising codes.
library(closecity)
library(sf)
close <- closecity::close_client(api_key = "ck_live_your_key") # use your own key here
# The catalog lists every category with its numeric id. Pull the ids you need.
amenity_types <- close$destination_types()
ids <- setNames(amenity_types$dest_type_id, amenity_types$label)
supermarket_dest_id <- ids[["grocery_stores"]]
restaurant_dest_id <- ids[["restaurants"]]
freq_transit_stop_dest_id <- ids[["frequent_transit"]]
# Turn the city name into a GEOID and pull its boundary for context.
city <- close$places(q = "Somerville")[1, ]
city_boundary <- close$place_boundary(geoid = city$geoid)See what is around
Look at the raw ingredients first: every supermarket, restaurant, and
frequent-transit stop within Somerville, from
$place_pois(). The city boundary, not a guessed radius, is
the edge. Give each category a colour and map them together.
supermarkets <- close$place_pois(geoid = city$geoid, type = supermarket_dest_id)
restaurants <- close$place_pois(geoid = city$geoid, type = restaurant_dest_id)
stops <- close$place_pois(geoid = city$geoid, type = freq_transit_stop_dest_id)
supermarkets$kind <- "Supermarket"
restaurants$kind <- "Restaurant"
stops$kind <- "Transit stop"
around <- rbind(supermarkets, restaurants, stops)
palette <- c(Supermarket = "#058040", Restaurant = "#c6cbe0", `Transit stop` = "#f36e21")
closecity::close_map(
x = around,
color = palette[around$kind],
label = "kind",
boundary = city_boundary
)Find the blocks that qualify
Somerville is a census place, so one call by place GEOID pulls the
per-block walk times for every block in the city.
$place_blocks() reads every page and returns one sf row per
(block, category); block boundaries come from tigris,
downloaded once and cached. (To search an arbitrary area instead, use
$blocks_query() with a centre and radius or a polygon. We
do that with a radius in the other tutorials only to keep their token
cost low; a place GEOID pulls the whole city.)
blocks <- close$place_blocks(
geoid = city$geoid,
mode = "walk",
type = c(supermarket_dest_id, restaurant_dest_id, freq_transit_stop_dest_id)
)Reshape to one row per block, with a walk-time column for each amenity, so a block carries all three times at once (and the hover on the map shows them). Then flag the blocks that pass every rule.
city_blocks <- blocks[!duplicated(blocks$geoid), "geoid"]
time_to <- function(type_id) {
sub <- blocks[blocks$dest_type_id == type_id, ]
setNames(sub$travel_time, sub$geoid)[city_blocks$geoid]
}
city_blocks$supermarket_min <- time_to(supermarket_dest_id)
city_blocks$restaurant_min <- time_to(restaurant_dest_id)
city_blocks$transit_min <- time_to(freq_transit_stop_dest_id)
city_blocks$qualifies <- (city_blocks$supermarket_min <= 10 &
city_blocks$restaurant_min <= 5 &
city_blocks$transit_min <= 20)
city_blocks$qualifies[is.na(city_blocks$qualifies)] <- FALSEShow every block in the city, highlight the ones that qualify, and hover any block to read its walk time to each amenity.
closecity::close_map(
x = city_blocks,
highlight = "qualifies",
color = "#f36e21",
boundary = city_boundary
)Narrow to a shared commute
Suppose two of you work in different places. A transit isochrone from each workplace shows how far each commute reaches; drawn together, half-transparent, you can see both at once.
work_a <- close$isochrone(
lon = -71.0865,
lat = 42.3625,
mode = "transit",
direction = "from",
minutes = 20,
format = "geojson"
)
work_b <- close$isochrone(
lon = -71.0589,
lat = 42.3555,
mode = "transit",
direction = "from",
minutes = 20,
format = "geojson"
)
closecity::close_map(
x = work_a,
color = "#058040",
opacity = 0.5,
background = work_b,
background_color = "#f36e21",
background_opacity = 0.5
)Keep the qualifying blocks that also sit inside both commutes. The final map shows those winning blocks, with the shortlist (inside both commutes) highlighted, over the two commute walksheds.
both_commutes <- sf::st_intersection(sf::st_union(work_a), sf::st_union(work_b))
winners <- city_blocks[city_blocks$qualifies, ]
winners$shortlist <- sf::st_intersects(winners, both_commutes, sparse = FALSE)[, 1]
closecity::close_map(
x = winners,
highlight = "shortlist",
color = "#1f78b4",
boundary = city_boundary,
background = list(work_a, work_b),
background_color = c("#058040", "#f36e21"),
background_fill = FALSE
)