Изохроны

Этот ноутбук строит изохроны на интермодальном графе UrbanGraph из IduEdu (пешком + общественный транспорт):

  • get_graph_isochrones с geometry_type="radius" и "ways" для одной точки отправления;

  • get_stepped_graph_isochrones с поясами шириной step для "radius", "ways" и "separate".

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

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

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

from objectnat import get_graph_isochrones, get_stepped_graph_isochrones
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. Интермодальный граф

Граф покрывает район и обрезан по его границе. Веса рёбер — time_min (минуты) и length_meter (метры).

# 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:15:27.509 | 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:15:27.542 | WARNING  | iduedu.graph_builders.intermodal_builders:join_pt_walk_graph:248 - Public transport nodes dropped with the smaller components: {'trolleybus': 2}

2. Точка отправления

Одна точка отправления в WGS 84; она привязывается к ближайшему узлу графа.

# Define a single point of interest
point = gpd.GeoDataFrame(geometry=[Point(30.27060176, 59.93546846)], crs=4326)

3. Изохрона radius

Объединение буферов вокруг узлов, достижимых за 10 минут; остаток бюджета каждого узла переводится в расстояние по медианной скорости на рёбрах графа.

isochrones_radius = get_graph_isochrones(
    G_intermodal,
    geometry_type='radius',
    gdf_origins=point,
    weight_type="time_min",
    weight_value_cutoff=10,
)

# Visualize
for_map(isochrones_radius).explore(tiles=grey_tiles())
Make this Notebook Trusted to load map: File -> Trust Notebook

4. Изохрона ways

Пешеходные рёбра, достижимые за 10 минут, с буфером road_buffer_size (по умолчанию 5 м).

isochrones_ways = get_graph_isochrones(
    G_intermodal,
    geometry_type='ways',
    gdf_origins=point,
    weight_type="time_min",
    weight_value_cutoff=10,
)

# Visualize
for_map(isochrones_ways).explore(tiles=grey_tiles())
Make this Notebook Trusted to load map: File -> Trust Notebook

5. Сравнение

Изохрона radius (синяя) покрывает и места между улицами; изохрона ways (красная) идёт по улицам.

m = for_map(isochrones_radius).explore(tiles=grey_tiles(), color='blue', name='Radius')
for_map(isochrones_ways).explore(m=m, color='red', name='Ways')
Make this Notebook Trusted to load map: File -> Trust Notebook

6. Ступенчатые изохроны, radius

Пояса по 3 минуты до 15 минут; dist — верхняя граница пояса.

stepped_radius = get_stepped_graph_isochrones(
    G_intermodal,
    geometry_type='radius',
    gdf_origins=point,
    weight_type="time_min",
    weight_value_cutoff=15,
    step=3,
)

for_map(stepped_radius).explore(tiles=grey_tiles(), column='dist')
Make this Notebook Trusted to load map: File -> Trust Notebook

7. Ступенчатые изохроны, ways

Ячейки Вороного достижимых узлов, обрезанные по буферам улиц, поясами по 3 минуты.

stepped_ways = get_stepped_graph_isochrones(
    G_intermodal,
    geometry_type='ways',
    gdf_origins=point,
    weight_type="time_min",
    weight_value_cutoff=15,
    step=3,
)
for_map(stepped_ways).explore(tiles=grey_tiles(), column='dist')
Make this Notebook Trusted to load map: File -> Trust Notebook

8. Ступенчатые изохроны, separate

Независимые буферы на каждый пояс по 2 минуты до 10 минут.

stepped_separate = get_stepped_graph_isochrones(
    G_intermodal,
    geometry_type='separate',
    gdf_origins=point,
    weight_type="time_min",
    weight_value_cutoff=10,
    step=2,
)

for_map(stepped_separate).explore(tiles=grey_tiles(), column='dist')
Make this Notebook Trusted to load map: File -> Trust Notebook

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

  • geometry_type: "radius", "ways" или "separate" (ступенчатые изохроны принимают также None — ячейки Вороного);

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

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

  • step: ширина пояса для ступенчатых изохрон;

  • buffer_factor ("separate") и road_buffer_size ("ways", метры).

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

Территория — отношение 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_origins = gpd.GeoDataFrame(geometry=[Point(30.2706, 59.9355), Point(30.2959, 59.9441)], crs=4326)  # your points

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_isochrones(
    my_graph, gdf_origins=my_origins, weight_type="time_min", weight_value_cutoff=15, step=3,
    geometry_type="separate",
)

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=(8, 7))
local.plot(ax=ax, column="dist", cmap="YlGnBu_r", alpha=0.85, legend=True, legend_kwds={"label": "travel time, min"})
gpd.GeoSeries([my_boundary], crs=4326).to_crs(local.crs).boundary.plot(ax=ax, color="#3d3d3d", linewidth=1)
my_origins.to_crs(local.crs).plot(ax=ax, color="#d7301f", markersize=30, edgecolor="white", zorder=5)
ax.set_axis_off()
plt.show()
# my_bands.to_file("my_isochrones.gpkg")  # for GIS
2026-11-06 19:15:43.083 | 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:15:43.117 | WARNING  | iduedu.graph_builders.intermodal_builders:join_pt_walk_graph:248 - Public transport nodes dropped with the smaller components: {'trolleybus': 2}
../../_images/98866d214fadde177cbd5b8f7670788520b71eeb914409002743a661b4081989.png
for_map(my_bands).explore(column="dist", cmap="YlGnBu_r", tiles=grey_tiles())
Make this Notebook Trusted to load map: File -> Trust Notebook