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')
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