Looking for a home

Say you are moving to a new city and want to live somewhere walkable. Here you find the blocks that are within a 10-minute walk of a grocery store, a 5-minute walk of a restaurant, and a 20-minute walk of a frequent-transit stop. Then you narrow those blocks to the overlap of two commutes. The example city is Somerville, Massachusetts.

Running this tutorial uses about 2,900 tokens.

Set up

Build a client, then read the pieces you need from the free catalog.

from closecity import Client
import geopandas as gpd

close = Client("ck_live_your_key")   # use your own key here
types = close.destination_types()
ids = dict(zip(types["label"], types["dest_type_id"]))
grocery = ids["grocery_stores"]
restaurant = ids["restaurants"]
transit = ids["frequent_transit"]

city = close.places("Somerville").iloc[0]

See what is around

Each search returns points, so plot the three amenities as three layers.

groceries = close.pois_search(lat = city["lat"], lon = city["lon"],
                              radius_m = 3000, type = grocery)
restaurants = close.pois_search(lat = city["lat"], lon = city["lon"],
                                radius_m = 3000, type = restaurant)
stops = close.pois_search(lat = city["lat"], lon = city["lon"],
                          radius_m = 3000, type = transit)

ax = restaurants.plot(color = "#c6cbe0", markersize = 6)
groceries.plot(ax = ax, color = "#058040")
stops.plot(ax = ax, color = "#f36e21", marker = "^")
<Axes: >
../_images/8e49ceb49a4c84f1bb17a12726e0f0e0e95b43d6affbffaf0920b57a44491ef4.png

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. The result is a GeoDataFrame with one row per (block, category); the boundaries come from pygris, downloaded once. To search an arbitrary area instead, use blocks_query with a centre and radius or a polygon.

blocks = close.place_blocks(city["geoid"], mode = "walk",
                            type = [grocery, restaurant, transit])

Take the blocks that pass each rule, then keep the blocks that pass all three.

near_grocery = set(blocks.loc[(blocks.dest_type_id == grocery) &
                              (blocks.travel_time <= 10), "geoid"])
near_restaurant = set(blocks.loc[(blocks.dest_type_id == restaurant) &
                                 (blocks.travel_time <= 5), "geoid"])
near_transit = set(blocks.loc[(blocks.dest_type_id == transit) &
                              (blocks.travel_time <= 20), "geoid"])

candidates = near_grocery & near_restaurant & near_transit
winners = blocks[blocks.geoid.isin(candidates)]

ax = blocks.plot(color = "#eef0f7", edgecolor = "white")
winners.plot(ax = ax, color = "#f36e21")
<Axes: >
../_images/918605f9ef0ba7bf574241ea54682753cacfdb061d141e7f5f0c9134f0bfc891.png

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. Both come back as polygons.

work_a = close.isochrone(lon = -71.0865, lat = 42.3625, mode = "transit",
                         direction = "from", minutes = 20)
work_b = close.isochrone(lon = -71.0589, lat = 42.3555, mode = "transit",
                         direction = "from", minutes = 20)

both_commutes = gpd.overlay(work_a, work_b, how = "intersection")

Keep the winning blocks that also sit inside both commutes. That short list is where to look.

shortlist = gpd.sjoin(winners, both_commutes, predicate = "intersects")

ax = winners.plot(color = "#eef0f7", edgecolor = "white")
shortlist.plot(ax = ax, color = "#058040")
<Axes: >
../_images/e84cfaa17e1bfb300de93f8559316538fcc6d152cd7c05f6db8df7667a937bed.png