Aperta minimal example¶
Walkable supermarkets in Cambridge, MA — end-to-end in ~50 lines using only OpenStreetMap. Runs in ~10–15 s on a laptop.
Every aperta primitive is exercised exactly once, with no commentary. For the guided tour see `walkthrough/accessibility.ipynb <../walkthrough/accessibility.ipynb>`__; for a production-scale demo see `extended/ <../extended/>`__.
[1]:
import matplotlib.pyplot as plt
import osmnx as ox
import geopandas as gpd
import aperta.accessibility as accessibility
import aperta.geo_mapping as geo_mapping
import aperta.geo_processing as geo_processing
import aperta.network_snap as network_snap
import aperta.od_pairs as od_pairs
import aperta.routing as routing
import aperta.routing_prep as routing_prep
import aperta.visualization as viz
PLACE = 'Cambridge, Massachusetts, USA'
1. AOI + walking network¶
[2]:
boundary = ox.geocode_to_gdf(PLACE)
crs = boundary.estimate_utm_crs()
# Note that we choose network_type='all'. Network_type='walk' can exclude
# important parts of the walking network that are connected to the rest of the
# network through an unwalkable highway / main road segment. Non-walkable roads
# get cleaned up in prepare_network below.
graph = ox.project_graph(
ox.graph_from_place(PLACE, network_type='all', simplify=True),
to_crs=crs,
)
prepared = routing_prep.prepare_network(graph, 'walk')
2. H3 cells (origins) + supermarkets (destinations)¶
[3]:
cells = geo_processing.build_h3_grid(
boundary.geometry.iloc[0], 10,
polygon_crs='EPSG:4326', target_crs=crs,
)
cell_centroids = gpd.GeoDataFrame(
geometry=cells.geometry.centroid, index=cells.index, crs=cells.crs,
)
cells['node_id'], _ = network_snap.snap_to_network_nodes(
cell_centroids, prepared.graph,
eligible_node_ids=prepared.snap_eligible_nodes,
)
_sm = ox.features_from_place(PLACE, tags={'shop': 'supermarket'}).to_crs(crs)
supermarkets = gpd.GeoDataFrame(geometry=_sm.geometry.centroid.values, crs=crs)
supermarkets['cell_id'], _ = geo_mapping.map_points_to_polygons(
supermarkets, cells, allow_nearest=True,
)
cells['supermarkets'] = (supermarkets.groupby('cell_id').size()
.reindex(cells.index, fill_value=0).astype(float))
3. Route + count accessibility¶
[4]:
walk_weight = routing.mask_excluded_edges(
lambda d: d['length'] / 1.4, prepared.cost_excluded_flag,
)
routing.apply_edge_weights(prepared.graph, walk_weight, 'walk_time_s')
pairs = od_pairs.get_pairs(cells, r_cells=2000.0, node_column='node_id')
times = routing.tiered_path_costs(pairs, prepared.graph, weight='walk_time_s')
sm_weights = od_pairs.dest_values('supermarkets', pairs, cells, node_column='node_id')
acc = accessibility.cumulative_opportunities(
times, {'supermarkets': sm_weights}, {},
[accessibility.Bin('15min', 0, 15 * 60)],
)
4. Map¶
[5]:
per_cell = cells['node_id'].map(acc[('15min', 'supermarkets')])
viz.plot_cell_values(
cells, per_cell,
title=f'Walkable supermarkets within 15 min — {PLACE}',
overlays=[(supermarkets, {'color': 'red', 'markersize': 8})],
)
plt.tight_layout()
plt.show()
[ ]: