Level of Traffic StressΒΆ
based on https://github.com/mbonsma/LTS-OSM, inspired by https://muenchen.social/@scooooooott@urbanists.social/111745338643572229
This is in progress. Results are not accurate. https://peterfurth.sites.northeastern.edu/level-of-traffic-stress/
[35]:
from pyrosm import OSM, get_data
region = "district-of-columbia"
fp = get_data(region)
osm = OSM(fp)
network = osm.get_network("all")
[78]:
import numpy as np
def biking_permitted(row):
if row["bicycle"] == "no":
return False
if row["access"] == "no":
return False
if row["highway"] == "motorway":
return False
if row["highway"] == "motorway_link":
return False
if row["highway"] == "proposed":
return False
if (
(row["footway"] == "sidewalk")
& ~(row["bicycle"] == "yes")
& ((row["highway"] == "footway") | (row["highway"] == "path"))
):
return False
return True
def is_separated_path(row):
if row["highway"] == "cycleway":
return True
if row["highway"] == "path":
return True
if (row["highway"] == "footway") & ~(row["footway"] == "crossing"):
return True
if row["cycleway"] == "track":
return True
if row["cycleway"] == "opposite_track":
return True
return False
def is_bike_lane(row):
if row["cycleway"] in [
"crossing",
"lane",
"left",
"opposite",
"opposite_lane",
"right",
"yes",
]:
return True
return False
def parking_present(
row,
): # Unfortunately, there is little parking data in OpenStreetMap. We will assume parking.
return True
def get_max_speed(
row,
motorway=55.0,
trunk=40.0,
primary=30.0,
secondary=30.0,
tertiary=20.0,
residential=20.0,
):
if row["maxspeed"] == None:
if row["highway"] in ["motorway", "motorway_link"]:
return motorway
if row["highway"] in ["trunk", "trunk_link"]:
return trunk
if row["highway"] in ["primary", "primary_link"]:
return primary
if row["highway"] in ["tertiary", "tertiary_link"]:
return tertiary
return residential
if "mph" in row["maxspeed"]:
return float(row["maxspeed"].split(" ")[0])
return float(row["maxspeed"])
def get_lanes(row):
if row["lanes"]:
return int(row["lanes"])
if row["oneway"]:
if row["maxspeed"] == None:
if row["highway"] in ["motorway", "motorway_link"]:
return 3
if row["highway"] in ["trunk", "trunk_link"]:
return 2
if row["highway"] in ["primary", "primary_link"]:
return 2
if row["highway"] in ["tertiary", "tertiary_link"]:
return 1
return 1
if row["maxspeed"] == None:
if row["highway"] in ["motorway", "motorway_link"]:
return 6
if row["highway"] in ["trunk", "trunk_link"]:
return 4
if row["highway"] in ["primary", "primary_link"]:
return 4
if row["highway"] in ["tertiary", "tertiary_link"]:
return 1
return 1
[79]:
def level_of_traffic_stress_way_mixed_traffic(row):
if row["oneway"] == "yes":
if row["lanes_assumed"] >= 3:
return 4
if row["lanes_assumed"] >= 2:
if row["maxspeed_assumed"] <= 25:
return 3
return 4
if row["maxspeed_assumed"] >= 35:
return 4
if row["maxspeed_assumed"] >= 30:
return 2
return 1
if row["lanes_assumed"] >= 6:
return 4
if row["lanes_assumed"] >= 4:
if row["maxspeed_assumed"] <= 25:
return 3
return 4
if row["maxspeed_assumed"] >= 35:
return 4
if row["maxspeed_assumed"] >= 30:
return 2
return 1
def level_of_traffic_stress_way_bike_lane_no_parking(row):
if row["maxspeed_assumed"] >= 40:
return 4
if row["maxspeed_assumed"] >= 35:
return 3
if row["oneway"] == "yes":
if row["lanes_assumed"] > 2:
return 3
if row["lanes_assumed"] == 2:
return 2
if row["lanes_assumed"] >= 4:
return 3
if row["maxspeed_assumed"] >= 30:
return 2
return 1
def level_of_traffic_stress_way_bike_lane_parking(row):
if row["maxspeed_assumed"] >= 40:
return 4
if row["maxspeed_assumed"] >= 35:
return 3
if row["oneway"] == "yes":
if row["lanes_assumed"] >= 2:
return 3
if row["lanes_assumed"] >= 4:
return 3
if row["maxspeed_assumed"] >= 30:
return 2
return 1
def level_of_traffic_stress_way(row):
if not row["biking_permitted"]:
return 0
if row["is_separated_path"]:
return 1
if row["is_bike_lane"]:
if row["parking_present"]:
return level_of_traffic_stress_way_bike_lane_parking(row)
return level_of_traffic_stress_way_bike_lane_no_parking(row)
return level_of_traffic_stress_way_mixed_traffic(row)
[80]:
network["is_separated_path"] = network.apply(is_separated_path, axis=1)
network["biking_permitted"] = network.apply(biking_permitted, axis=1)
network["is_bike_lane"] = network.apply(is_bike_lane, axis=1)
network["parking_present"] = network.apply(parking_present, axis=1)
network["lanes_assumed"] = network.apply(get_lanes, axis=1)
network["maxspeed_assumed"] = network.apply(get_max_speed, axis=1)
network["level_of_traffic_stress"] = network.apply(level_of_traffic_stress_way, axis=1)
print(network["level_of_traffic_stress"].value_counts())
level_of_traffic_stress
1 42856
0 12026
4 3169
3 1348
2 474
Name: count, dtype: int64
[83]:
from lonboard import Map, PathLayer
from lonboard.colormap import apply_categorical_cmap
layer = PathLayer.from_geopandas(
gdf=network[
[
"geometry",
"level_of_traffic_stress",
"name",
"highway",
"is_separated_path",
"biking_permitted",
"is_bike_lane",
"parking_present",
"oneway",
"lanes",
"lanes_assumed",
"maxspeed",
"maxspeed_assumed",
"length",
]
],
width_scale=10,
)
layer.get_color = apply_categorical_cmap(
values=network["level_of_traffic_stress"],
cmap={
0: [0, 0, 0], # black
1: [0, 128, 0], # green
2: [255, 255, 0], # yellow
3: [255, 165, 0], # orange
4: [255, 0, 0], # red
},
)
Map(layers=[layer])
[83]:
[82]:
network["length"] = network.to_crs("EPSG:4087").length
network.groupby(["level_of_traffic_stress"])["length"].sum().plot(kind="barh")
[82]:
<Axes: ylabel='level_of_traffic_stress'>
[ ]: