Graph Coverage Analysis for Service Points¶
This notebook demonstrates how to calculate coverage zones from service points through a multimodal transportation network using Dijkstra’s algorithm and Voronoi diagrams.
# Install required packages (uncomment if needed)
# !pip install iduedu pyarrow objectnat
# Import dependencies
from iduedu import get_intermodal_graph, get_4326_boundary
import geopandas as gpd
from objectnat import get_graph_coverage,get_stepped_graph_coverage,get_radius_coverage
1. Load Transportation Network¶
First, we retrieve the multimodal graph (roads, public transport, etc.) for a specified region using its OSM ID.
# Get city boundary and transportation network
poly = get_4326_boundary(osm_id=1114252) # Example OSM ID for a city
G_intermodal = get_intermodal_graph(territory=poly, clip_by_territory=True)
2026-07-10 13:06:04.730 | WARNING | iduedu.graph.transformers:keep_largest_connected_component:64 - Removing 343 nodes outside the largest strongly connected component. Retaining 21016 of 21359 nodes.
2. Load Service Points¶
These represent locations (e.g., healthcare facilities, schools) for which we want to calculate coverage zones.
# Load service points (replace with your actual data path)
services = gpd.read_parquet('examples_data/services.parquet')
3. Calculate Coverage by Distance¶
Creates service areas based on maximum travel distance (800 meters in this example).
# Calculate coverage zones by distance (800m cutoff)
result_length = get_graph_coverage(
G_intermodal,
gdf_destinations=services,
weight_type="length_meter",
weight_value_cutoff=800,
)
# Coverage results are indexed like the input services. Join service attributes
# back by index before coloring or filtering by service columns such as "name".
result_length = result_length.join(services[['name']], how="left")
# Visualize results
result_length.explore(column='name', tiles='CartoDB Positron')
4. Calculate Coverage by Travel Time¶
Creates service areas based on maximum travel time (10 minutes in this example), clipped to the city boundary.
# Prepare zone boundary
zone = gpd.GeoDataFrame(geometry=[poly], crs=4326)
# Calculate coverage zones by time (10min cutoff)
result_time = get_graph_coverage(
G_intermodal,
gdf_destinations=services,
weight_type="time_min",
weight_value_cutoff=10,
zone=zone,
)
# Coverage results preserve the input service index, so service attributes can
# be joined back by index for visualization.
result_time = result_time.join(services[['name']], how="left")
# Visualize results
result_time.explore(column='name', tiles='CartoDB Positron')
Key Parameters Explained:¶
weight_type:"length_meter"for distance-based coverage"time_min"for time-based coverage
weight_value_cutoff: Maximum travel distance/time thresholdzone(optional): Boundary polygon to clip results
5. Calculate stepped Coverage by Travel Time¶
This method divides the total travel time threshold into steps (e.g. every 2 minutes), creating incremental zones. Useful for visualizing service accessibility gradients.
You can choose the visualization method:
"voronoi": polygons based on proximity to reachable network nodes"separate": independent buffer zones for each interval
stepped_cov_voronoi = get_stepped_graph_coverage(
G_intermodal,
gdf_destinations=services,
weight_type="time_min",
geometry_type=None,
weight_value_cutoff=15,
step=2,
zone=zone,
)
# Visualize stepped coverage
stepped_cov_voronoi.explore(column='dist', tiles='CartoDB Positron')
stepped_cov_separate = get_stepped_graph_coverage(
G_intermodal,
gdf_destinations=services,
weight_type="time_min",
geometry_type='separate',
weight_value_cutoff=15,
step=2,
)
# Visualize stepped coverage
stepped_cov_separate.explore(column='dist', tiles='CartoDB Positron', vmin=0)
6. Calculate Radius-Based Coverage¶
If a transport network is unavailable or unnecessary, use simple circular buffers around service points.
This method creates geometric buffers with specified radius (e.g., 500 meters).
radius_cov = get_radius_coverage(gdf_from=services[['name','geometry']], radius=500)
# Visualize radius coverage
radius_cov.explore(column='name', tiles='CartoDB Positron')