{ "cells": [ { "cell_type": "markdown", "id": "9ca78071b77f245e", "metadata": {}, "source": [ "# Building-to-service travel-time matrix\n", "\n", "This notebook computes the matrix of travel times from the example buildings to the example services on an\n", "intermodal graph (walk + public transport) with IduEdu's `od_matrix` and stores it in\n", "`examples_data/matrix_time.parquet` for the [provision example](provision.ipynb).\n", "\n", "The last section, *Your own area*, explains how to compute the matrix for another territory." ] }, { "cell_type": "code", "execution_count": 1, "id": "a3fd404cc3b83edd", "metadata": { "ExecuteTime": { "end_time": "2026-07-10T10:05:29.962538Z", "start_time": "2026-07-10T10:05:29.959041Z" } }, "outputs": [], "source": [ "# !pip install \"iduedu[pbf]\" pyarrow" ] }, { "cell_type": "code", "execution_count": 2, "id": "9a3e15f423bedc31", "metadata": { "ExecuteTime": { "end_time": "2026-07-10T10:05:38.142935Z", "start_time": "2026-07-10T10:05:30.282064Z" } }, "outputs": [], "source": [ "import geopandas as gpd\n", "import iduedu\n", "import pandas as pd\n", "from iduedu import get_intermodal_graph, od_matrix\n", "from shapely.ops import unary_union" ] }, { "cell_type": "markdown", "id": "68af599b21a7895d", "metadata": {}, "source": [ "## 1. Buildings and services" ] }, { "cell_type": "code", "execution_count": 3, "id": "ecaca9093632eb44", "metadata": { "ExecuteTime": { "end_time": "2026-07-10T10:05:38.217274Z", "start_time": "2026-07-10T10:05:38.158957Z" } }, "outputs": [], "source": [ "# Read building and service datasets\n", "buildings = gpd.read_parquet('examples_data/buildings.parquet')\n", "services = gpd.read_parquet('examples_data/services.parquet')" ] }, { "cell_type": "markdown", "id": "5146507282cd8082", "metadata": {}, "source": [ "## 2. Territory\n", "\n", "The convex hull of all buildings and services, buffered by 0.001° (about 60–110 m), defines the graph extent." ] }, { "cell_type": "code", "execution_count": 4, "id": "74e684470ea483a1", "metadata": { "ExecuteTime": { "end_time": "2026-07-10T10:05:39.037844Z", "start_time": "2026-07-10T10:05:38.219279Z" } }, "outputs": [], "source": [ "polygon = unary_union(\n", " buildings.to_crs(4326).geometry.to_list() + services.to_crs(4326).geometry.to_list()\n", ").convex_hull.buffer(0.001)" ] }, { "cell_type": "markdown", "id": "b6b58fffdd714d38", "metadata": {}, "source": [ "## 3. Intermodal graph\n", "\n", "The territory is cut out of the Geofabrik extract of the Northwestern Federal District (`iduedu.use_pbf`); the\n", "same call without the `with` block downloads the data from the Overpass API." ] }, { "cell_type": "code", "execution_count": 5, "id": "1e643d3fdc052876", "metadata": { "ExecuteTime": { "end_time": "2026-07-10T10:05:42.788008Z", "start_time": "2026-07-10T10:05:39.050329Z" } }, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "2026-11-04 22:07:48.449 | WARNING | Removing 407 nodes outside the largest strongly connected component. Retaining 21326 of 21733 nodes.\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "2026-11-04 22:07:48.470 | WARNING | Public transport nodes dropped with the smaller components: {'trolleybus': 2}\n" ] } ], "source": [ "with iduedu.use_pbf(territory=polygon, region=\"northwestern-fed-district\"):\n", " G_intermodal = get_intermodal_graph(territory=polygon, clip_by_territory=True)" ] }, { "cell_type": "markdown", "id": "812757b2e10fe745", "metadata": {}, "source": [ "## 4. Travel-time matrix\n", "\n", "`od_matrix` snaps origins and destinations to graph nodes and runs Dijkstra searches with the edge weight `weight`\n", "(`\"time_min\"`, minutes) up to `threshold` (45 minutes). Pairs beyond the threshold are unreachable." ] }, { "cell_type": "code", "execution_count": 6, "id": "f763ed1656707714", "metadata": { "ExecuteTime": { "end_time": "2026-07-10T10:05:43.782672Z", "start_time": "2026-07-10T10:05:42.843924Z" } }, "outputs": [], "source": [ "# Compute travel time matrix (in minutes)\n", "matrix: pd.DataFrame = od_matrix(\n", " G_intermodal,\n", " gdf_origins=buildings,\n", " gdf_destinations=services,\n", " weight=\"time_min\",\n", " threshold=45,\n", ")\n" ] }, { "cell_type": "markdown", "id": "8c56b37f6c2f508f", "metadata": {}, "source": [ "## 5. Storage\n", "\n", "The matrix is stored for the provision example." ] }, { "cell_type": "code", "execution_count": 7, "id": "371f4607ed8ec9c9", "metadata": { "ExecuteTime": { "end_time": "2026-07-10T10:05:43.818050Z", "start_time": "2026-07-10T10:05:43.798691Z" } }, "outputs": [], "source": [ "# Save matrix to Parquet format\n", "# IduEdu returns a sparse DataFrame to keep large OD matrices compact in memory.\n", "# Parquet writers do not support pandas SparseDtype columns, so convert to a dense\n", "# DataFrame before saving. Unreachable pairs stay encoded as np.inf.\n", "matrix_dense = matrix.sparse.to_dense()\n", "matrix_dense.to_parquet('examples_data/matrix_time.parquet')\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 6. Your own area\n", "\n", "The notebook is the recipe for another area: put your buildings and services (GeoDataFrames in any CRS) into\n", "`buildings` and `services` in section 1 and run it. The territory around them, the graph and the matrix are then computed from them;\n", "`iduedu.use_pbf` chooses the Geofabrik extract itself when `region` is left out. Save the matrix under your own\n", "name and pass it to `get_service_provision` as in the [provision example](provision.ipynb)." ], "id": "028d08d6" } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.12.13" } }, "nbformat": 4, "nbformat_minor": 5 }