Accessibility Modelling
Using the R5Py library to calculate the travel time matrix and analyze the accessibility of urban amenities The R5Py library, is a Python wrapper for the R5 routing engine. R5 is a multimodal routing engine that can be used to calculate travel times between locations in a city using different modes of transport, such as walking, cycling, and public transport. It is open-source, efficient and use real-time data and OpenStreetMap to provide accurate travel time estimates.
R5Py calculates the travel time matrix between origins and destinations.
# Or instead, you can use explore for an interactive plot.NOTE: this cell has been placed at the top for quick reference of the final output.
join.explore(column="access_schools", scheme='natural_breaks',cmap="plasma")
Step 1 install and import packages¶
# Install the packages used in the analysis
!pip install networkx
!pip install osmnx
!pip install r5py
!pip install mapclassify
!pip install folium
#Java 21: Update Java version to execute updated RFpy
!java -version
!apt-get update
!apt-get install -y openjdk-21-jdk
import os
os.environ['JAVA_HOME'] = '/usr/lib/jvm/java-21-openjdk-amd64'
os.environ['PATH'] = f"{os.environ['JAVA_HOME']}/bin:{os.environ['PATH']}"
#### Import the necessary libraries ####
# R5Py for travel time matrix computation
from r5py import TransportNetwork, TravelTimeMatrixComputer
# GeoPandas for spatial data manipulation
import geopandas as gpd
import pandas as pd
# General library
from matplotlib import pyplot as plt
from datetime import datetime, timedelta
import networkx as nx
import osmnx as ox
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
import geopandas as gpd
# from osgeo import gdal
import warnings
warnings.filterwarnings("ignore", category=DeprecationWarning, module="pandas.core.frame")
Step 2 Acquire Spatial Data¶
# Download Schools from OSM
# Define the location of interest
place_name = "Nairobi County"
# Define the tags for schools
#school_tags = {'amenity': 'school', 'geometry':'point'}
# Use the new `features_from_place` function instead of `geometries_from_place`
#schools = ox.features_from_place(place_name, tags=school_tags)
schools = ox.features_from_place(place_name, tags={'amenity':'school', 'geometry':'point'})
# Ensure the geometry column is properly set
if 'geometry' not in schools.columns or schools.geometry.is_empty.all():
raise ValueError("The 'geometry' column does not contain valid geometries.")
# Function to clean the GeoDataFrame
def clean_geodataframe(gdf):
# Drop columns with unsupported data types
for column in gdf.columns:
if gdf[column].apply(lambda x: isinstance(x, (list, dict, tuple))).any():
gdf = gdf.drop(columns=[column])
# Drop rows with invalid geometries
gdf = gdf.dropna(subset=['geometry'])
gdf = gdf[gdf.geometry.notna()]
return gdf
schools_cleaned = clean_geodataframe(schools)
# Save the cleaned schools data to a GeoJSON file
schools_cleaned.to_file("schoolsN.geojson", driver="GeoJSON")
<frozen importlib._bootstrap>:914: ImportWarning: _PyDrive2ImportHook.find_spec() not found; falling back to find_module() <frozen importlib._bootstrap>:914: ImportWarning: _PyDriveImportHook.find_spec() not found; falling back to find_module() <frozen importlib._bootstrap>:914: ImportWarning: _GenerativeAIImportHook.find_spec() not found; falling back to find_module() <frozen importlib._bootstrap>:914: ImportWarning: _OpenCVImportHook.find_spec() not found; falling back to find_module() <frozen importlib._bootstrap>:914: ImportWarning: APICoreClientInfoImportHook.find_spec() not found; falling back to find_module() <frozen importlib._bootstrap>:914: ImportWarning: _BokehImportHook.find_spec() not found; falling back to find_module() <frozen importlib._bootstrap>:914: ImportWarning: _AltairImportHook.find_spec() not found; falling back to find_module() <frozen importlib._bootstrap>:914: ImportWarning: _PyDrive2ImportHook.find_spec() not found; falling back to find_module() <frozen importlib._bootstrap>:914: ImportWarning: _PyDriveImportHook.find_spec() not found; falling back to find_module() <frozen importlib._bootstrap>:914: ImportWarning: _GenerativeAIImportHook.find_spec() not found; falling back to find_module() <frozen importlib._bootstrap>:914: ImportWarning: _OpenCVImportHook.find_spec() not found; falling back to find_module() <frozen importlib._bootstrap>:914: ImportWarning: APICoreClientInfoImportHook.find_spec() not found; falling back to find_module() <frozen importlib._bootstrap>:914: ImportWarning: _BokehImportHook.find_spec() not found; falling back to find_module() <frozen importlib._bootstrap>:914: ImportWarning: _AltairImportHook.find_spec() not found; falling back to find_module()
##Covert polygons to points
import json
# Load GeoJSON data from a file (replace with your actual GeoJSON file path)
geojson_file_path = "schoolsN.geojson"
with open(geojson_file_path, "r") as infile:
geojson_data = json.load(infile)
# Create an empty list to store the points
points = []
# Iterate through each feature in the GeoJSON data
for feature in geojson_data["features"]:
if feature["geometry"]["type"] == "Polygon":
# Calculate the centroid of the polygon
coordinates = feature["geometry"]["coordinates"][0] # Assuming the first set of coordinates is the outer ring
lon = sum(coord[0] for coord in coordinates) / len(coordinates)
lat = sum(coord[1] for coord in coordinates) / len(coordinates)
# Create a new point feature with the same attributes as the original polygon
point_feature = {
"type": "Feature",
"geometry": {"type": "Point", "coordinates": [lon, lat]},
"properties": feature.get("properties", {}),
}
points.append(point_feature)
elif feature["geometry"]["type"] == "Point":
# Retain existing point features
points.append(feature)
# Create a new GeoJSON object with the points
points_geojson = {"type": "FeatureCollection", "features": points}
# Save the points GeoJSON to a file (optional)
with open("points2.geojson", "w") as outfile:
json.dump(points_geojson, outfile)
##Check for duplication of features when polygons were converted to points.
#In the case of schools in Nairobi, some facilities had double represntation e.g Calvary Education Centre school was represented as two points
# You can verify through visual inspection in QGIS
#Remove duplicates
import json
# Load the GeoJSON file
with open('points2.geojson', 'r') as f:
geojson_data = json.load(f)
# Define the list of IDs to remove
ids_to_remove = [4457763959, 5926218250, 1201173154, 244391943, 5926387116, 4838514224, 260967392, 260967410, 261016370, 17101235, 17101234, 1244387410, 583157837, 17131693, 11902999269, 878232422, 878232423, 878232421, 720900823, 879524501, 973017504, 364993385, 36499663, 364982859, 6445081210, 5933622804, 5933721703, 5933721704, 5933721713, 6445081205, 1110227334, 5926355908, 949598078, 9137254518, 2951976091, 6445111405, 9167078118, 9164228727, 9169505385, 949597326, 949598174, 2935256787, 4711263743, 878232421, 5926090741, 5924835189, 1437347777, 890550597, 5924832980, 4461494343, 11801760327, 8128866092, 5926218136, 8157780961, 5933721708, 7174839950, 1432507879, 9108401529, 9167057817, 1483304295, 612007447, 2923513484, 6249035802, 5925863197, 9167037538, 551901275, 9164063621, 2932284095, 3082332223, 9119356135, 5926218141, 4464546559, 2405836319, 6445081208, 8129001741, 655592513, 5924835188, 6249035798, 6445081197, 9157462997, 6445081206, 6445081191, 6445132391, 1999910117, 5924832982, 5933721709, 5926218143, 5933622801, 3073260240, 592207512, 5948282117, 5933573020, 9163232288, 2952187220, 9114039451, 4472329843, 9167220117, 5933721712, 4464506028, 9167097883, 5948170933, 9163860973, 9169179517, 5926355912, 2405854914, 2405798440, 5926355913, 6249035795, 9164093563, 9165314982, 5953268876, 261016416, 260967410, 5948376939, 2968577925, 5926387120, 5948282126, 5948282127, 2405829025, 4711308866, 254653583, 7937755856, 1419004987, 5933714619, 5924832973, 9163408296, 9134741817, 9167044817, 293228409, 5933652696, 5948282118, 5933721705, 3070186504, 9166926919, 8764466562, 93668640, 3082332228, 9166914517, 6445077911, 364996664, 364996663, 364993385, 5948282121, 5933714621, 9075883318, 5134869728, 748826131, 9216102018, 9058309550, 9125919261, 11801760315, 9160876505, 2923513490, 9169178418, 1235786842, 973017505, 6830686439, 9162410712, 261016729, 9139621563, 2923089741, 9166932882, 3070274195, 9122789938, 5926218139, 5933714626, 160913634, 9137263417, 659301385, 5926090747, 144188716, 2953922777, 10780953107, 11801760314, 4464601890, 9165513244, 1764153756, 6445077901, 8374583635, 6249035799, 9173935115, 4464655293, 2105394325, 9163228068, 4464622787, 2923513494, 6249035791, 5926229107, 5926387128, 5926387129, 2968578135, 6445111413, 5915900141, 561265479, 6583228985, 409162258, 9164251364, 9167259317, 5933721655, 5933721656, 1493484848, 6445132385, 569284196, 5948282123, 973017504, 879524501, 97076364, 9160478755, 11801760328, 5926195779, 5170840100, 9122984596, 162826365, 6249035794, 6021550872, 6445111407, 5926387131, 6445129687, 4464546560, 6817508245, 752317805, 171869601, 171869598, 5948282114, 5933721653, 612007554, 9165286734, 5948220719, 3174474065, 5926195782, 5926195783, 6445111392, 5232810721, 1580152996, 2405823296, 9164495804, 9166883307, 5933721660, 7321575796, 5925863204, 890550666, 4711263746, 9161134707, 5933721658, 5112486230, 11681732671, 1764155302, 5926355907, 7098406185, 97066384, 9164129614, 9170788222, 712907767, 10780953108, 5948220720, 2935256797, 2923436819, 2923815702, 5400815313, 5924832976, 1493484865, 9722373317, 2959968892, 9122892721, 6445111419, 11801760330, 3533006870, 2923815708, 5119191997, 6445077890, 3082332231, 9162822140, 3070240345, 2968578147, 11801760316, 9164147011, 9158114917, 9116097926, 5119239745, 828155762, 17131693, 5926218264, 5926229115, 5925863206, 5948144504, 6249035793, 1419005003, 6445111422, 11801760311, 949597387, 5134869730, 775535480, 7089472243, 9134893317, 5933622807, 9163361080, 5948376938, 883145970, 5953317911, 2964954568, 5926387124, 9165411582, 8374564304, 5925863211, 5134869731, 5170815428, 7325103880, 5926218261, 11801760334, 2405836316, 5933652807, 3004383269, 9151330717, 9157787749, 3454366438, 9169137017, 9137438617, 5933721715, 9157599989, 5926218253, 5926218262, 5948376942, 106088779, 9116299929, 9157610203, 5933573023, 5953218907, 9129136345, 5933652693, 10788139060, 5933721719, 6249035800]
# Get the features from the GeoJSON data
features = geojson_data['features']
# Remove the features with the matching IDs
features = [feature for feature in features if feature['properties']['osmid'] not in ids_to_remove]
# Update the GeoJSON data with the modified features
geojson_data['features'] = features
# Write the updated GeoJSON data to a new file
with open('points_cleaned.geojson', 'w') as f:
json.dump(geojson_data, f)
Schools = gpd.read_file('points_cleaned.geojson')
Schools.head()
element_type | osmid | addr:city | addr:postcode | addr:street | amenity | name | phone | religion | website | ... | wikipedia | noaddress | roof:colour | roof:levels | roof:shape | addr:housename | description | layer | service_times | geometry | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | node | 30092225 | Nairobi | 00100 | Ole Shapara Avenue | school | Kinderworld International School | +254798139954 | muslim | https://kis.sc.ke/ | ... | None | None | None | None | None | None | None | None | None | POINT (36.82355 -1.31379) |
1 | node | 30092294 | None | None | None | school | Nairobi Academy Secondary | None | None | None | ... | None | None | None | None | None | None | None | None | None | POINT (36.75327 -1.34018) |
2 | node | 30312225 | None | None | None | school | Consolata School | None | None | None | ... | None | None | None | None | None | None | None | None | None | POINT (36.80406 -1.26749) |
3 | node | 30402763 | Nairobi | None | Kipande Road | school | Aga Khan Nursery | None | None | None | ... | None | None | None | None | None | None | None | None | None | POINT (36.81492 -1.27217) |
4 | node | 295848487 | Nairobi | None | Juja Road | school | Pangani Girls Secondary School | None | None | None | ... | None | None | None | None | None | None | None | None | None | POINT (36.83739 -1.26894) |
5 rows × 260 columns
##Change osmid to id
Schools = Schools.rename(columns={'osmid': 'id'})
#Retain name, geometry and ID columns
columns_to_retain = ['id', 'geometry','name']
Schools = Schools[columns_to_retain]
Schools.head()
id | geometry | name | |
---|---|---|---|
0 | 30092225 | POINT (36.82355 -1.31379) | Kinderworld International School |
1 | 30092294 | POINT (36.75327 -1.34018) | Nairobi Academy Secondary |
2 | 30312225 | POINT (36.80406 -1.26749) | Consolata School |
3 | 30402763 | POINT (36.81492 -1.27217) | Aga Khan Nursery |
4 | 295848487 | POINT (36.83739 -1.26894) | Pangani Girls Secondary School |
##Get grid or other geography to represent origins
##Hexagon grid of the study area in this examople created in QGIS at 500meters aggregating the WorldPop grid centroids from 100meters
origins = gpd.read_file('NbiCentroids_500mHex_2020.geojson')
origins.head()
id | subcounty | ward | Population | geometry | |
---|---|---|---|---|---|
0 | 1382.0 | Langata Sub County | Karen Ward | 213.695 | POINT (36.74868 -1.33218) |
1 | 5271.0 | Kasarani Sub County | Ruai Ward | 339.260 | POINT (36.98600 -1.26232) |
2 | 3794.0 | Ruaraka Sub County | Korogocho Ward | 13673.037 | POINT (36.89655 -1.24190) |
3 | 4018.0 | Langata Sub County | Mugumo-ini Ward | 0.000 | POINT (36.90810 -1.38431) |
4 | 2158.0 | Langata Sub County | Mugumo-ini Ward | 0.000 | POINT (36.79532 -1.36839) |
# Import spatial boundaries of for context of the study area; you can get boundaries from GADM: https://gadm.org/ or your database
ox_bound = gpd.read_file('NairobiBoundary.geojson').to_crs(epsg=4326)
# Plot the features for better understanding
origins.explore(column='Population', scheme='natural_breaks',cmap="plasma")
<frozen importlib._bootstrap>:914: ImportWarning: _PyDrive2ImportHook.find_spec() not found; falling back to find_module() <frozen importlib._bootstrap>:914: ImportWarning: _PyDriveImportHook.find_spec() not found; falling back to find_module() <frozen importlib._bootstrap>:914: ImportWarning: _GenerativeAIImportHook.find_spec() not found; falling back to find_module() <frozen importlib._bootstrap>:914: ImportWarning: _OpenCVImportHook.find_spec() not found; falling back to find_module() <frozen importlib._bootstrap>:914: ImportWarning: APICoreClientInfoImportHook.find_spec() not found; falling back to find_module() <frozen importlib._bootstrap>:914: ImportWarning: _BokehImportHook.find_spec() not found; falling back to find_module() <frozen importlib._bootstrap>:914: ImportWarning: _AltairImportHook.find_spec() not found; falling back to find_module() <frozen importlib._bootstrap>:914: ImportWarning: _PyDrive2ImportHook.find_spec() not found; falling back to find_module() <frozen importlib._bootstrap>:914: ImportWarning: _PyDriveImportHook.find_spec() not found; falling back to find_module() <frozen importlib._bootstrap>:914: ImportWarning: _GenerativeAIImportHook.find_spec() not found; falling back to find_module() <frozen importlib._bootstrap>:914: ImportWarning: _OpenCVImportHook.find_spec() not found; falling back to find_module() <frozen importlib._bootstrap>:914: ImportWarning: APICoreClientInfoImportHook.find_spec() not found; falling back to find_module() <frozen importlib._bootstrap>:914: ImportWarning: _BokehImportHook.find_spec() not found; falling back to find_module() <frozen importlib._bootstrap>:914: ImportWarning: _AltairImportHook.find_spec() not found; falling back to find_module() <frozen importlib._bootstrap>:914: ImportWarning: _PyDrive2ImportHook.find_spec() not found; falling back to find_module() <frozen importlib._bootstrap>:914: ImportWarning: _PyDriveImportHook.find_spec() not found; falling back to find_module() <frozen importlib._bootstrap>:914: ImportWarning: _GenerativeAIImportHook.find_spec() not found; falling back to find_module() <frozen importlib._bootstrap>:914: ImportWarning: _OpenCVImportHook.find_spec() not found; falling back to find_module() <frozen importlib._bootstrap>:914: ImportWarning: APICoreClientInfoImportHook.find_spec() not found; falling back to find_module() <frozen importlib._bootstrap>:914: ImportWarning: _BokehImportHook.find_spec() not found; falling back to find_module() <frozen importlib._bootstrap>:914: ImportWarning: _AltairImportHook.find_spec() not found; falling back to find_module()
<frozen importlib._bootstrap>:914: ImportWarning: _PyDrive2ImportHook.find_spec() not found; falling back to find_module() <frozen importlib._bootstrap>:914: ImportWarning: _PyDriveImportHook.find_spec() not found; falling back to find_module() <frozen importlib._bootstrap>:914: ImportWarning: _GenerativeAIImportHook.find_spec() not found; falling back to find_module() <frozen importlib._bootstrap>:914: ImportWarning: _OpenCVImportHook.find_spec() not found; falling back to find_module() <frozen importlib._bootstrap>:914: ImportWarning: APICoreClientInfoImportHook.find_spec() not found; falling back to find_module() <frozen importlib._bootstrap>:914: ImportWarning: _BokehImportHook.find_spec() not found; falling back to find_module() <frozen importlib._bootstrap>:914: ImportWarning: _AltairImportHook.find_spec() not found; falling back to find_module()
# You can use .explore() to interactively explore the data or .plot() to plot the data
# destinations_university.plot()
Schools.explore()
Time matrix and accessibility¶
To start with the accessibility analysis, we would like to make the simplist case: How long it would take from one certain place to travel to another?
###### Create a TransportNetwork object in R5Pyusing the OSM data ######
# This provides a basis for analysis in the entire third section
#Get osm_pbf from 'extract.bbbike.jpeg'
#GTFS from Digital Matatus
transport_network = TransportNetwork(
osm_pbf='NairobiOSM.osm.pbf', # Path to the OSM PBF file
gtfs=['GTFS_FEED_2019.zip'] # Path to the GTFS file
)
# Select one point as origin, you can choose any point in the origins dataset
# Some good example could include:
# You can select by ID
Banda = Schools.loc[Schools['name'] == "The Banda School"]
# Create a TravelTimeMatrixComputer object using R5Py to calculate travel times
# This is a key step as it will determine the travel times between all pairs of origins and destinations in the network
travel_time_computer_point = TravelTimeMatrixComputer(
transport_network=transport_network,
origins= Banda,
destinations=origins,
snap_to_network=True,
# Below are some of the travel options that can be tweaked as needed
transport_modes=['WALK', 'TRANSIT', 'CAR'], # Modes of transport to consider, e.g., 'WALK', 'BICYCLE', 'CAR', 'TRANSIT'
# speed_walking=3.6,
)
# For more information on the available options, please refer to the R5Py documentation: https://r5py.readthedocs.io/en/latest/
/usr/local/lib/python3.10/dist-packages/r5py/r5/regional_task.py:227: RuntimeWarning: Departure time 2024-08-02 19:25:17.157338 is outside of the time range covered by currently loaded GTFS data sets. warnings.warn(
# Compute travel times between all origins and destinations;
od_matrix_point = travel_time_computer_point.compute_travel_times()
od_matrix_point.head()
# The calculated time_matrix is vey simple: you have the starting id, and its time of travel to any other location (id).
from_id | to_id | travel_time | |
---|---|---|---|
0 | 1203369516 | 1382.0 | 10.0 |
1 | 1203369516 | 5271.0 | NaN |
2 | 1203369516 | 3794.0 | NaN |
3 | 1203369516 | 4018.0 | 33.0 |
4 | 1203369516 | 2158.0 | 7.0 |
od_matrix_point['to_id'] = od_matrix_point['to_id'].astype('int64')
join_point = origins.merge(od_matrix_point, left_on='id', right_on='to_id')
join_point.head()
# You can see how the aggregated data look like. A geometry is introduced to spatially plot the accessibility.
id | subcounty | ward | Population | geometry | from_id | to_id | travel_time | |
---|---|---|---|---|---|---|---|---|
0 | 1382.0 | Langata Sub County | Karen Ward | 213.695 | POINT (36.74868 -1.33218) | 1203369516 | 1382 | 10.0 |
1 | 5271.0 | Kasarani Sub County | Ruai Ward | 339.260 | POINT (36.98600 -1.26232) | 1203369516 | 5271 | NaN |
2 | 3794.0 | Ruaraka Sub County | Korogocho Ward | 13673.037 | POINT (36.89655 -1.24190) | 1203369516 | 3794 | NaN |
3 | 4018.0 | Langata Sub County | Mugumo-ini Ward | 0.000 | POINT (36.90810 -1.38431) | 1203369516 | 4018 | 33.0 |
4 | 2158.0 | Langata Sub County | Mugumo-ini Ward | 0.000 | POINT (36.79532 -1.36839) | 1203369516 | 2158 | 7.0 |
# Build a plot to show the travel times
fig, ax = plt.subplots(figsize=(10,8)) # Adjust size as needed
# Plot the boundary for context
ox_bound.plot(ax=ax, color='none', scheme='natural_breaks', linewidth=1)
join_point.plot(ax=ax,column="travel_time", cmap="cividis_r",scheme = 'natural_breaks', markersize=2,linewidth=1,legend=True,aspect='equal')
Banda.plot(ax=ax, color='red', markersize=10,legend=True)
ax.set_title("Travel time from selected point to all destinations combining public transport and active travel")
ax.set_axis_off()
<frozen importlib._bootstrap>:914: ImportWarning: _PyDrive2ImportHook.find_spec() not found; falling back to find_module() <frozen importlib._bootstrap>:914: ImportWarning: _PyDriveImportHook.find_spec() not found; falling back to find_module() <frozen importlib._bootstrap>:914: ImportWarning: _GenerativeAIImportHook.find_spec() not found; falling back to find_module() <frozen importlib._bootstrap>:914: ImportWarning: _OpenCVImportHook.find_spec() not found; falling back to find_module() <frozen importlib._bootstrap>:914: ImportWarning: APICoreClientInfoImportHook.find_spec() not found; falling back to find_module() <frozen importlib._bootstrap>:914: ImportWarning: _BokehImportHook.find_spec() not found; falling back to find_module() <frozen importlib._bootstrap>:914: ImportWarning: _AltairImportHook.find_spec() not found; falling back to find_module()
Walking
travel_time_computer_point_walk = TravelTimeMatrixComputer(
transport_network=transport_network,
origins=Banda,
destinations=origins,
snap_to_network=True,
# Below are some of the travel options that can be tweaked as needed
transport_modes=['WALK'], # Modes of transport to consider, e.g., 'WALK', 'BICYCLE', 'CAR', 'TRANSIT'
# speed_walking=3.6,
)
# Compute travel times between all origins and destinations
od_matrix_point_walk = travel_time_computer_point_walk.compute_travel_times()
# Create the combined matrix
join_point = origins.merge(od_matrix_point_walk, left_on="id", right_on="to_id")
join_point.head()
id | subcounty | ward | Population | geometry | from_id | to_id | travel_time | |
---|---|---|---|---|---|---|---|---|
0 | 1382.0 | Langata Sub County | Karen Ward | 213.695 | POINT (36.74868 -1.33218) | 1203369516 | 1382.0 | 103.0 |
1 | 5271.0 | Kasarani Sub County | Ruai Ward | 339.260 | POINT (36.98600 -1.26232) | 1203369516 | 5271.0 | NaN |
2 | 3794.0 | Ruaraka Sub County | Korogocho Ward | 13673.037 | POINT (36.89655 -1.24190) | 1203369516 | 3794.0 | NaN |
3 | 4018.0 | Langata Sub County | Mugumo-ini Ward | 0.000 | POINT (36.90810 -1.38431) | 1203369516 | 4018.0 | NaN |
4 | 2158.0 | Langata Sub County | Mugumo-ini Ward | 0.000 | POINT (36.79532 -1.36839) | 1203369516 | 2158.0 | 65.0 |
join_point.explore(column="travel_time", scheme='natural_breaks',cmap="cividis_r")
# Or, You can use this part to plot the result
fig, ax = plt.subplots(figsize=(10,8)) # Adjust size as needed
# Change the following line to your own variable.
join_point.plot(ax=ax,column="travel_time", cmap="cividis_r",markersize=2,linewidth=1,legend=True)
Banda.plot(ax=ax, color='red', markersize=10, scheme='natural_breaks', legend=True)
ox_bound.plot(ax=ax, color='none',linewidth=1)
ax.set_title("Travel time from selected point to all destinations with only walking")
ax.set_axis_off()
Opportunities accessible within a certain travel time¶
It is important to understand the accessibility of urban amenities, such as food outlets, schools, and healthcare facilities, to different parts of the city. This information can help urban planners and policymakers identify areas with limited access to essential services and improve the overall accessibility of the city.
Similar case can be made for the accessibility of opportunities, such as jobs and schools.
In this case, we will calculate the number of schools accessible within 25 minutes of sustainable travel (walking, cycling and public transport) from each population grid in the city.
travel_time_computer = TravelTimeMatrixComputer(
transport_network=transport_network,
origins=origins,
destinations=Schools, #
#snap_to_network=True,
transport_modes=['WALK','BICYCLE', 'TRANSIT'],
)
/usr/local/lib/python3.10/dist-packages/r5py/r5/regional_task.py:227: RuntimeWarning: Departure time 2024-08-02 19:25:17.157338 is outside of the time range covered by currently loaded GTFS data sets. warnings.warn(
#Origin Destination (O-D) Matrix
od_matrix = travel_time_computer.compute_travel_times()
#WARNING!- it takes some time to calculate the OD matrix (about 40 minutes)
# If takes too much time to calculate the OD matrix on Colab or Jupyter, you can try creating it in QGIS using QNEAT3 algorithim.
# The load it # od_matrix = pd.read_csv('your_matrix.csv')
# Set the travel time threshold : in this example 25 used based on National Policy
time_threshold = 25
# Filter the OD matrix to include only travel times less than or equal to 25 minutes
filtered_matrix = od_matrix[od_matrix['travel_time'] <= time_threshold]
# Count the number of accessible POIs for each population grid
accessible_pois = filtered_matrix.groupby('from_id').size().reset_index(name='access_schools')
# Merge the accessibility information with the origins GeoDataFrame for plotting
join = origins.merge(accessible_pois, left_on="id", right_on="from_id")
# Build a plot to show the accessible opportunities
fig, ax = plt.subplots(figsize=(10,8)) # Adjust size as needed
# Plot the Oxford County boundary for context
ox_bound.plot(ax=ax, color='none', scheme='natural_breaks', linewidth=1)
# Plot the number of accessible food outlets aggregated in the above block
join.plot(ax=ax,column="access_schools", scheme='natural_breaks',cmap="plasma",markersize=2,linewidth=1, legend=True)
ax.set_title(f"Number of School Facilities Accessible Under {time_threshold} Minutes")
ax.set_axis_off()
Sources: Code modified from Arcaute et al., 2024 : OSSEN Tutorial-Elsa Arcaute: Street Network and Accessibility