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-09-16 12:49:49.917 | WARNING  | iduedu.graph.transformers:keep_largest_connected_component:64 - Removing 334 nodes outside the largest strongly connected component. Retaining 21184 of 21518 nodes.
2026-09-16 12:49:49.939 | WARNING  | iduedu.graph_builders.intermodal_builders:join_pt_walk_graph:248 - Public transport nodes dropped with the smaller components: {'trolleybus': 2}

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')
Make this Notebook Trusted to load map: File -> Trust Notebook

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')
Make this Notebook Trusted to load map: File -> Trust Notebook

Key Parameters Explained:

  • weight_type:

    • "length_meter" for distance-based coverage

    • "time_min" for time-based coverage

  • weight_value_cutoff: Maximum travel distance/time threshold

  • zone (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')
Make this Notebook Trusted to load map: File -> Trust Notebook
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)
Make this Notebook Trusted to load map: File -> Trust Notebook

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')
Make this Notebook Trusted to load map: File -> Trust Notebook