Isochrone Analysis from Points of Interest¶
This notebook demonstrates how to generate accessibility isochrones from single or multiple points using different methods:
Simple
radiusandwaysisochronesStepped isochrones with customizable intervals
# Install required packages (uncomment if needed)
# !pip install objectnat iduedu
# Import necessary libraries
from iduedu import get_intermodal_graph, get_4326_boundary
import geopandas as gpd
from shapely import Point
from objectnat import get_accessibility_isochrones, get_accessibility_isochrone_stepped
1. Load Intermodal Graph¶
Load a multimodal transportation graph (roads, public transport, etc.) for a specific region using its OSM ID.
# Load boundary and graph for a specific region using OSM ID 1114252.
poly = get_4326_boundary(osm_id=1114252)
G_intermodal = get_intermodal_graph(territory=poly, clip_by_territory=True)
2. Create Points of Interest¶
Define one or more source points from which isochrones will be generated.
# Define a single point of interest
point = gpd.GeoDataFrame(geometry=[Point(30.27060176, 59.93546846)], crs=4326)
3. Generate Radius Isochrones¶
Create circular isochrones using a travel time threshold (e.g. 10 minutes).
isochrones_radius, stops_r, routes_r = get_accessibility_isochrones(
isochrone_type='radius',
points=point,
weight_type="time_min",
weight_value=10,
nx_graph=G_intermodal
)
# Visualize
m = isochrones_radius.explore(tiles='CartoDB Positron')
stops_r.explore(m=m)
routes_r.explore(m=m, column='type')
4. Generate Ways Isochrones¶
Create road network-based polygons representing reachable areas within a time or distance threshold.
isochrones_ways, stops_w, routes_w = get_accessibility_isochrones(
isochrone_type='ways',
points=point,
weight_type="time_min",
weight_value=10,
nx_graph=G_intermodal
)
# Visualize
m = isochrones_ways.explore(tiles='CartoDB Positron')
stops_w.explore(m=m)
routes_w.explore(m=m, column='type')
5. Compare Isochrone Types¶
Overlay both types of isochrones to compare coverage.
m = isochrones_radius.explore(tiles='CartoDB Positron', color='blue', name='Radius')
isochrones_ways.explore(m=m, color='red', name='Ways')
6. Generate Stepped Isochrones (Radius)¶
Create concentric buffer zones with stepped intervals (e.g. every 3 minutes).
stepped_radius, stops_s1, routes_s1 = get_accessibility_isochrone_stepped(
isochrone_type='radius',
point=point,
weight_type="time_min",
weight_value=15,
nx_graph=G_intermodal,
step=3
)
stepped_radius.explore(tiles='CartoDB Positron', column='dist')
7. Generate Stepped Isochrones (Ways)¶
Create layered polygons in the road network with custom intervals (e.g. every 3 minutes).
stepped_ways, stops_s2, routes_s2 = get_accessibility_isochrone_stepped(
isochrone_type='ways',
point=point,
weight_type="time_min",
weight_value=15,
nx_graph=G_intermodal,
step=3
)
stepped_ways.explore(tiles='CartoDB Positron', column='dist')
8. Generate Stepped Isochrones (Separate)¶
Create distinct buffer rings for each interval.
stepped_separate, stops_s3, routes_s3 = get_accessibility_isochrone_stepped(
isochrone_type='separate',
point=point,
weight_type="time_min",
weight_value=10,
nx_graph=G_intermodal,
step=2
)
stepped_separate.explore(tiles='CartoDB Positron', column='dist')
Key Parameter Summary:¶
isochrone_type:'radius','ways', or'separate'weight_type:'time_min'(minutes) or'length_meter'(meters)weight_value: total cutoff (e.g. 10 minutes)step: interval size for stepped isochrones (optional)Additional:
buffer_factor,road_buffer_size
Animation for stepped isochrones¶
from objectnat.methods.utils.graph_utils import graph_to_gdf
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
from shapely import Point
import geopandas as gpd
from objectnat import get_accessibility_isochrone_stepped
edges = graph_to_gdf(G_intermodal, nodes=False)
point = gpd.GeoDataFrame(geometry=[Point(30.27060176, 59.93546846)], crs=4326).to_crs(edges.crs)
bbox = gpd.GeoDataFrame(geometry=[poly], crs=4326).to_crs(edges.crs)
type_colors = {
'walk': '#a3a3a3',
'bus': '#1f77b4',
'trolleybus': '#2ca02c',
'tram': '#ff7f0e',
'subway': '#9467bd',
'boarding': '#8c564b'
}
edges['color'] = edges['type'].map(type_colors)
steps = [0.1, 0.5, 1, 2, 3, 4, 5]
fig, ax = plt.subplots(figsize=(10, 8), dpi=150)
plt.subplots_adjust(left=0.05, right=0.95, top=0.95, bottom=0.05)
edges_plot = edges.plot(ax=ax, color=edges['color'], alpha=0.5, linewidth=0.1, legend=True)
bbox.boundary.plot(ax=ax, color='black', linestyle='--', linewidth=1)
point.plot(ax=ax, color='red', markersize=50)
ax.set_axis_off()
def update(step):
for coll in ax.collections:
if coll.get_label() == 'isochrone':
coll.remove()
result = get_accessibility_isochrone_stepped(
isochrone_type='separate',
point=point,
weight_type="time_min",
weight_value=15,
nx_graph=G_intermodal,
step=step
)
result.plot(ax=ax, alpha=1, label='isochrone', column='dist', legend=False)
ax.set_title(f'Isochrone with step = {step} minutes')
ani = FuncAnimation(
fig,
update,
frames=steps,
repeat=True,
interval=2000
)
ani.save('isochrone_animation.gif', writer='pillow', fps=1)