{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Origins to Closest Destinations Tutorial\n", "This tutorial demonstrates how to run an **Origins to Closest Destinations (OD) analysis** with RA2CE.\n", "RA2CE automatically finds the shortest or quickest route from each origin to its nearest destination.\n", "\n", "If you are not yet familiar with preparing origins and destinations shapefiles, see the [Origins and Destinations Data Preparation](../tutorials/accessibility.prepare_data_origin_destinations.html) tutorial." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Step 1: Import Libraries and Set Paths" ] }, { "cell_type": "code", "execution_count": null, "id": "4d5fb747", "metadata": {}, "outputs": [], "source": [ "from pathlib import Path\n", "\n", "from ra2ce.analysis.analysis_config_data.analysis_config_data import AnalysisSectionLosses, AnalysisConfigData\n", "from ra2ce.analysis.analysis_config_data.enums.analysis_losses_enum import AnalysisLossesEnum\n", "from ra2ce.analysis.analysis_config_data.enums.weighing_enum import WeighingEnum\n", "from ra2ce.network.network_config_data.enums.aggregate_wl_enum import AggregateWlEnum\n", "from ra2ce.network import RoadTypeEnum\n", "from ra2ce.network.network_config_data.enums.network_type_enum import NetworkTypeEnum\n", "from ra2ce.network.network_config_data.enums.source_enum import SourceEnum\n", "from ra2ce.network.network_config_data.network_config_data import (\n", " NetworkSection, NetworkConfigData, OriginsDestinationsSection, HazardSection\n", ")\n", "from ra2ce.ra2ce_handler import Ra2ceHandler\n", "\n", "# Specify the path to your RA2CE project folder and input data\n", "root_dir = Path('data', 'closest_origin_destinations')\n", "network_path = root_dir.joinpath('static', 'network')" ] }, { "cell_type": "markdown", "id": "0b71eb41", "metadata": {}, "source": [ "## Step 2: Define Network with Origins & Destinations\n", "Define the network configuration using OSM data clipped to a region polygon and include specific road types." ] }, { "cell_type": "code", "execution_count": null, "id": "38f9b7b3", "metadata": {}, "outputs": [], "source": [ "network_section = NetworkSection(\n", " source=SourceEnum.OSM_DOWNLOAD,\n", " polygon=network_path.joinpath(\"region_polygon.geojson\"),\n", " network_type=NetworkTypeEnum.DRIVE,\n", " road_types=[\n", " RoadTypeEnum.MOTORWAY,\n", " RoadTypeEnum.MOTORWAY_LINK,\n", " RoadTypeEnum.PRIMARY,\n", " RoadTypeEnum.PRIMARY_LINK,\n", " RoadTypeEnum.SECONDARY,\n", " RoadTypeEnum.SECONDARY_LINK,\n", " RoadTypeEnum.TERTIARY,\n", " RoadTypeEnum.TERTIARY_LINK,\n", " RoadTypeEnum.RESIDENTIAL,\n", " ],\n", " save_gpkg=True,\n", ")" ] }, { "cell_type": "code", "execution_count": null, "id": "83e19ad9", "metadata": {}, "outputs": [], "source": [ "origin_destination_section = OriginsDestinationsSection(\n", " origins=network_path.joinpath(\"origins.shp\"),\n", " destinations=network_path.joinpath(\"destinations.shp\"),\n", " origin_count=\"POPULATION\",\n", ")" ] }, { "cell_type": "code", "execution_count": null, "id": "f6df3207", "metadata": {}, "outputs": [], "source": [ "hazard_section = HazardSection(\n", " hazard_map=[root_dir.joinpath(\"static\", \"hazard\", \"max_flood_depth.tif\")],\n", " aggregate_wl=AggregateWlEnum.MEAN,\n", " hazard_crs=\"EPSG:32736\",\n", " overlay_segmented_network=False,\n", ")" ] }, { "cell_type": "code", "execution_count": null, "id": "0d5aace0", "metadata": {}, "outputs": [], "source": [ "network_config_data = NetworkConfigData(\n", " root_path=root_dir,\n", " static_path=root_dir.joinpath('static'),\n", " network=network_section,\n", " hazard=hazard_section,\n", " origins_destinations=origin_destination_section,\n", ")" ] }, { "cell_type": "markdown", "id": "1e318bbc", "metadata": {}, "source": [ "## Step 3: Define the Analysis\n", "Use [AnalysisLossesEnum.MULTI_LINK_ORIGIN_CLOSEST_DESTINATION](../api/ra2ce.analysis.analysis_config_data.enums.html#module-ra2ce.analysis.analysis_config_data.enums.analysis_losses_enum){.api-ref} to calculate routes that avoid disrupted roads." ] }, { "cell_type": "code", "execution_count": null, "id": "7ad2bda5", "metadata": {}, "outputs": [], "source": [ "analyse_section = AnalysisSectionLosses(\n", " name=\"OD_accessibility_analysis\",\n", " analysis=AnalysisLossesEnum.MULTI_LINK_ORIGIN_CLOSEST_DESTINATION,\n", " weighing=WeighingEnum.LENGTH,\n", " calculate_route_without_disruption=True,\n", " save_csv=True,\n", " save_gpkg=True,\n", ")\n", "\n", "analysis_config_data = AnalysisConfigData(\n", " output_path=root_dir.joinpath(\"output\"),\n", " static_path=root_dir.joinpath('static'),\n", " analyses=[analyse_section],\n", ")" ] }, { "cell_type": "markdown", "id": "d77bead2", "metadata": {}, "source": [ "## Step 4: Run the Analysis" ] }, { "cell_type": "code", "execution_count": null, "id": "ccf0b858", "metadata": {}, "outputs": [], "source": [ "handler = Ra2ceHandler.from_config(\n", " network=network_config_data,\n", " analysis=analysis_config_data\n", ")\n", "handler.configure()\n", "handler.run_analysis()" ] }, { "cell_type": "markdown", "id": "d7e4717d", "metadata": {}, "source": [ "## Step 5: Interpret Results\n", "Results are stored in the `output` folder and include both CSV and GeoPackage files." ] }, { "cell_type": "code", "execution_count": null, "id": "c73d6093", "metadata": {}, "outputs": [], "source": [ "import geopandas as gpd\n", "\n", "analysis_output_path = root_dir / \"output\" / \"multi_link_origin_closest_destination\"\n", "results_gpkg = analysis_output_path / \"OD_accessibility_analysis_optimal_routes_without_hazard.gpkg\"\n", "gdf = gpd.read_file(results_gpkg)\n", "gdf.head()" ] }, { "cell_type": "markdown", "id": "d1eceebc", "metadata": {}, "source": [ "### Identifying Isolated Populations\n", "Origins that cannot reach any destination due to hazard disruption are flagged in `OD_accessibility_analysis_origins.gpkg`." ] }, { "cell_type": "code", "execution_count": null, "id": "7823ed02", "metadata": {}, "outputs": [], "source": [ "origin_gdf = gpd.read_file(analysis_output_path / 'OD_accessibility_analysis_origins.gpkg')\n", "map = origin_gdf.explore(column='EV1_me_A', cmap=['green', 'red'],\n", " marker_kwds={'radius':5}, tiles=\"CartoDB dark_matter\")\n", "map" ] }, { "cell_type": "code", "execution_count": null, "id": "e23b6dc3", "metadata": {}, "outputs": [], "source": [ "no_access_gdf = origin_gdf[origin_gdf['EV1_me_A'] == 'no access']\n", "no_access_gdf.explore(column='POPULATION', cmap='cool',\n", " marker_kwds={'radius':5}, tiles=\"CartoDB dark_matter\")" ] }, { "cell_type": "markdown", "id": "217da7bd", "metadata": {}, "source": [ "### Inspecting Optimal Routes\n", "Routes are computed from each origin to its closest destination. Routes can be filtered by specific destinations or categories." ] }, { "cell_type": "code", "execution_count": null, "id": "37d04f6e", "metadata": {}, "outputs": [], "source": [ "destinations_gdf = gpd.read_file(analysis_output_path / 'OD_accessibility_analysis_destinations.gpkg')\n", "optimal_routes_with_hazard_gdf = gpd.read_file(analysis_output_path / 'OD_accessibility_analysis_optimal_routes_with_hazard.gpkg')\n", "\n", "d_1_gdf = destinations_gdf[destinations_gdf['d_id'] == 'D_1']\n", "optimal_routes_d_1_with_hazard_gdf = optimal_routes_with_hazard_gdf[optimal_routes_with_hazard_gdf['destination'] == 'D_1']\n", "origins_with_optimal_route_d_1 = origin_gdf[origin_gdf['o_id'].isin(optimal_routes_d_1_with_hazard_gdf['origin'])]\n", "\n", "optimal_routes_d_1_with_hazard_gdf.explore(column='difference',\n", " cmap='RdYlGn_r', legend=True,\n", " tiles=\"CartoDB dark_matter\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Key Notes\n", "- Some routes may no longer exist if disrupted roads block all access\n", "- Remaining routes may be longer or slower, showing detours\n", "- Some origins may completely lose access to the destination\n", "\n", "This analysis helps quantify **loss of accessibility** due to hazards." ] } ], "metadata": { "kernelspec": { "display_name": "ra2ce_env", "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.11.13" } }, "nbformat": 4, "nbformat_minor": 5 }