Зоны покрытия

Этот ноутбук строит зоны покрытия сервисов из примера на интермодальном графе UrbanGraph: каждое место относится к сервису, до которого сетевая стоимость в пределах бюджета минимальна, поэтому зоны не перекрываются. Он показывает

  • get_graph_coverage по расстоянию (800 м) и по времени (10 мин);

  • get_stepped_graph_coverage поясами по 2 минуты в виде ячеек Вороного и в виде независимых буферов;

  • get_radius_coverage без графа.

Метод описан на странице зон покрытия. Интерактивные карты показывают облегчённую копию каждого результата (for_map: контуры упрощены на 2 м), чтобы ноутбук оставался небольшим.

Последний раздел, Своя территория, показывает, что заменить, чтобы получить такие же картинки для другой территории.

# Install required packages (uncomment if needed)
# !pip install iduedu pyarrow objectnat
import geopandas as gpd
import iduedu
from iduedu import get_4326_boundary, get_intermodal_graph

from objectnat import get_graph_coverage, get_radius_coverage, get_stepped_graph_coverage
import folium


def for_map(gdf, tolerance=2.0):
    """A light copy for a web map: outlines simplified by `tolerance` metres, coordinates rounded to about 10 cm."""
    local = gdf.to_crs(gdf.estimate_utm_crs())
    local["geometry"] = local.simplify(tolerance)
    light = local.to_crs(4326)
    light["geometry"] = light.geometry.set_precision(1e-6)
    return light[~light.is_empty]

def grey_tiles():
    """Light grey base map (Esri World Light Gray); its tiles end at zoom 16 and are stretched beyond it."""
    return folium.TileLayer("Esri.WorldGrayCanvas", max_zoom=19, max_native_zoom=16)

1. Интермодальный граф

# Vasilyevsky Island district, Saint Petersburg (OSM relation 1114252).
# IduEdu cuts the territory out of the Geofabrik extract (pip install "iduedu[pbf]");
# without the `with` block the same calls query the Overpass API.
with iduedu.use_pbf(osm_id=1114252, region="northwestern-fed-district"):
    poly = get_4326_boundary(osm_id=1114252)
    G_intermodal = get_intermodal_graph(territory=poly, clip_by_territory=True)
2026-11-06 19:16:01.201 | WARNING  | iduedu.graph.transformers:keep_largest_connected_component:64 - Removing 338 nodes outside the largest strongly connected component. Retaining 21181 of 21519 nodes.
2026-11-06 19:16:01.224 | WARNING  | iduedu.graph_builders.intermodal_builders:join_pt_walk_graph:248 - Public transport nodes dropped with the smaller components: {'trolleybus': 2}

2. Сервисы

36 сервисов со столбцами name и capacity (EPSG:32636).

services = gpd.read_parquet('examples_data/services.parquet')

3. Покрытие по расстоянию

Зоны в пределах 800 м сетевого расстояния (weight_type="length_meter").

# 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
for_map(result_length).explore(column='name', legend=False, tiles=grey_tiles())
Make this Notebook Trusted to load map: File -> Trust Notebook

4. Покрытие по времени

Зоны в пределах 10 минут (weight_type="time_min"), обрезанные по границе района через zone.

# 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
for_map(result_time).explore(column='name', legend=False, tiles=grey_tiles())
Make this Notebook Trusted to load map: File -> Trust Notebook

Использованные параметры

  • weight_type: "length_meter" (метры) или "time_min" (минуты);

  • weight_value_cutoff: бюджет в единицах weight_type;

  • zone (необязательно): полигон, по которому обрезаются зоны; без него они обрезаются по вогнутой оболочке достижимых узлов;

  • geometry_type (необязательно): "radius" или "ways" дополнительно обрезает зоны по буферам остаточного расстояния или по буферам улиц.

5. Ступенчатое покрытие по времени

Обслуживаемая территория, разбитая на пояса по 2 минуты до 15 минут, с двумя значениями geometry_type:

  • None: ячейки Вороного узлов графа;

  • "separate": независимые буферы для каждого пояса, без ячеек Вороного.

"radius" и "ways" обрезают те же пояса Вороного по буферам остаточного расстояния или по буферам улиц, как в ноутбуке изохрон.

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
for_map(stepped_cov_voronoi).explore(column='dist', tiles=grey_tiles())
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
for_map(stepped_cov_separate).explore(column='dist', tiles=grey_tiles(), vmin=0)
Make this Notebook Trusted to load map: File -> Trust Notebook

6. Покрытие по радиусу

Без графа: полигоны Вороного сервисов, обрезанные по буферам 500 м.

radius_cov = get_radius_coverage(gdf_from=services[['name','geometry']], radius=500)
# Visualize radius coverage
for_map(radius_cov).explore(column='name', legend=False, tiles=grey_tiles())
Make this Notebook Trusted to load map: File -> Trust Notebook

7. Своя территория

Территория — отношение OSM (relation района или города на openstreetmap.org); REGION задаёт выгрузку Geofabrik, из которой она вырезается (iduedu.find_geofabrik_region находит её по полигону; без неё IduEdu один раз обращается к Overpass, чтобы выбрать выгрузку). Результат — GeoDataFrame: нарисуйте его в matplotlib (здесь поверх улиц графа, с границей территории), посмотрите на веб-карте или сохраните для ГИС через to_file.

import matplotlib.pyplot as plt

OSM_ID, REGION = 1114252, "northwestern-fed-district"  # your territory
my_services = services  # your services: points in any CRS

with iduedu.use_pbf(osm_id=OSM_ID, region=REGION):  # downloaded once, then cached
    my_boundary = get_4326_boundary(osm_id=OSM_ID)
    my_graph = get_intermodal_graph(territory=my_boundary, clip_by_territory=True)
my_bands = get_stepped_graph_coverage(
    my_graph, gdf_destinations=my_services, weight_type="time_min", weight_value_cutoff=15, step=2,
    zone=gpd.GeoDataFrame(geometry=[my_boundary], crs=4326),
)

local = my_bands.to_crs(my_graph.nodes_gdf.crs)
streets = my_graph.edges_gdf[my_graph.edges_gdf["type"] == "walk"]  # the street network as the background
ax = streets.plot(color="#c2c2c2", linewidth=0.3, figsize=(9, 6))
local.plot(ax=ax, column="dist", cmap="YlGnBu_r", alpha=0.85, legend=True,
           legend_kwds={"label": "time to the nearest service, min"})
gpd.GeoSeries([my_boundary], crs=4326).to_crs(local.crs).boundary.plot(ax=ax, color="#3d3d3d", linewidth=1)
my_services.to_crs(local.crs).plot(ax=ax, color="#1f2d4d", markersize=14, edgecolor="white", zorder=5)
ax.set_axis_off()
plt.show()
# my_bands.to_file("my_coverage.gpkg")  # for GIS
2026-11-06 19:16:16.427 | WARNING  | iduedu.graph.transformers:keep_largest_connected_component:64 - Removing 338 nodes outside the largest strongly connected component. Retaining 21181 of 21519 nodes.
2026-11-06 19:16:16.458 | WARNING  | iduedu.graph_builders.intermodal_builders:join_pt_walk_graph:248 - Public transport nodes dropped with the smaller components: {'trolleybus': 2}
../../_images/b853d44b3e98e2c373529b73c462c671d459d76461ed2bbbda02580cc4f86ea9.png
for_map(my_bands).explore(column="dist", cmap="YlGnBu_r", tiles=grey_tiles())
Make this Notebook Trusted to load map: File -> Trust Notebook