remplis-ta-ville/remplis-ta-ville.ipynb

331 lines
14 KiB
Text
Raw Normal View History

2024-09-07 18:39:47 +02:00
{
"cells": [
{
"cell_type": "markdown",
"id": "4af40662-b782-4904-ba32-224960d16d38",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": ""
},
"tags": []
},
"source": [
"# Remplis ta ville"
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "e2abc871-bf82-4cf6-993e-cd904c7e7468",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": ""
},
"tags": []
},
"outputs": [],
"source": [
"import ipywidgets as widgets\n",
"from IPython import display\n",
"import os"
]
},
{
"cell_type": "markdown",
"id": "caf2f2f9-05de-4ea7-8efb-55e5eeaffc59",
"metadata": {},
"source": [
"Trouves-tu que ta région n'est pas assez encombrée par le trafic de transit, qu'elle n'est pas assez bruyante, que l'air et les eaux n'y sont pas assez pollués ? Eh bien, tu te trouves sur la bonne page ! Choisis ta région parmi la liste et laisse-moi te montrer ce que représente la quantité de trafic automobile supplémentaire estimée par l'Office Fédéral des Routes (OFROU) engendrée par l'extension de la largeur des autoroutes."
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "529de80c-dd7f-45eb-b3c1-753b33d2bdb7",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": ""
},
"tags": []
},
"outputs": [
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "a65109866b884b0c8bde3165b2fb39fb",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"HBox(children=(VBox(children=(Dropdown(description='Région', options=(('Ville de Genève', 'geneva'), ('Région …"
]
},
"execution_count": 2,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"places = {\n",
" \"geneva\": {\"shorthand\": \"geneva\", \"friendly\": \"Ville de Genève\", \"label\": \"Geneva, Switzerland\", \"cars\": 44_000, \"node\": 2809410},\n",
" \"coppet\": {\"shorthand\": \"coppet\", \"friendly\": \"Région de Coppet\", \"label\": [\"Commugny, Switzerland\",\"Coppet, Switzerland\",\"Founex, Switzerland\"], \"cars\": 8_800, \"node\": 6514705484},\n",
" \"nyon\": {\"shorthand\": \"nyon\", \"friendly\": \"Ville de Nyon\", \"label\": \"Nyon, Switzerland\", \"cars\": 7_000, \"node\": 311240719},\n",
"}\n",
"\n",
"mots = {\n",
" \"car\": {\"shorthand\": \"car\", \"friendly\": \"Voiture\", \"ppv\": 1.5, \"length\": 7},\n",
" \"bus\": {\"shorthand\": \"bus\", \"friendly\": \"Bus\", \"ppv\": 132, \"length\": 21},\n",
" \"bike\": {\"shorthand\": \"bike\", \"friendly\": \"Vélo\",\"ppv\": 1, \"length\": 0.5},\n",
"}\n",
"\n",
"w_place = widgets.Dropdown(\n",
" options=[(places[place][\"friendly\"],places[place][\"shorthand\"]) for place in places.keys()],\n",
" description='Région',\n",
")\n",
"\n",
"w_mot = widgets.Dropdown(\n",
" options=[(mots[mot][\"friendly\"],mots[mot][\"shorthand\"]) for mot in mots.keys()],\n",
" description='Véhicule',\n",
")\n",
"\n",
"w_submit = widgets.Button(\n",
" description='Remplir !',\n",
" disabled=False\n",
")\n",
"\n",
"w_progress = widgets.IntProgress(\n",
" min=0,\n",
" description=' ',\n",
" orientation='horizontal'\n",
")\n",
"\n",
"def simulate(*ignore):\n",
" \n",
" import geopandas as gpd\n",
" import pandas as pd\n",
" import networkx as nx\n",
" import osmnx as ox\n",
" import numpy as np\n",
" \n",
" place = places[w_place.value]\n",
" mot = mots[w_mot.value]\n",
" \n",
" people = place[\"cars\"] * mots[\"car\"][\"ppv\"]\n",
" \n",
" vehicles = np.ceil(people / mot[\"ppv\"])\n",
"\n",
" # Download regional roads graph and convert it to GeoDataFrame\n",
" \n",
" utw = [\"name\",\"highway\",\"access\",\"length\",\"lanes\",\"lanes:forward\",\"lanes:backward\",\"lanes:psv\",\"lanes:psv:forward\",\"lanes:psv:backward\",\"lanes:bus\",\"lanes:bus:forward\",\"lanes:bus:backward\",\"motor_vehicle\"]\n",
" ox.settings.useful_tags_way=utw\n",
"\n",
" w_progress.description = \"Chargement du graphe\"\n",
" w_progress.value = 0\n",
" G = ox.graph_from_place(place[\"label\"], network_type=\"drive\")\n",
" \n",
" # Estimate and clean:\n",
" # \n",
" # * Lanes dedicated to motorized individual transport\n",
" # * Lanes length\n",
" # * Lanes cars capacity\n",
"\n",
" w_progress.description = \"Annotation du graphe\"\n",
" edges = ox.graph_to_gdfs(G, nodes=False, edges=True)\n",
"\n",
" w_progress.description = \"Estimation des capacités\"\n",
" edges[\"lanes:forward\"] = edges[\"lanes:forward\"].fillna(0) if \"lanes:forward\" in edges.columns else 0\n",
" edges[\"lanes:forward\"] = edges.apply(lambda row: np.min(np.array(row[\"lanes:forward\"]).astype(int)), axis=1)\n",
" edges[\"lanes:backward\"] = edges[\"lanes:backward\"].fillna(0) if \"lanes:backward\" in edges.columns else 0\n",
" edges[\"lanes:backward\"] = edges.apply(lambda row: np.min(np.array(row[\"lanes:backward\"]).astype(int)), axis=1)\n",
" edges[\"lanes\"] = edges[\"lanes\"].fillna(0) if \"lanes\" in edges.columns else 0\n",
" edges[\"lanes\"] = edges.apply(lambda row: np.min(np.array(row[\"lanes\"]).astype(int)), axis=1)\n",
"\n",
" edges[\"lanes:psv\"] = edges[\"lanes:psv\"].fillna(0) if \"lanes:psv\" in edges.columns else 0\n",
" edges[\"lanes:psv\"] = edges.apply(lambda row: np.min(np.array(row[\"lanes:psv\"]).astype(int)), axis=1)\n",
" edges[\"lanes:psv:forward\"] = edges[\"lanes:psv:forward\"].fillna(0) if \"lanes:psv:forward\" in edges.columns else 0\n",
" edges[\"lanes:psv:forward\"] = edges.apply(lambda row: np.min(np.array(row[\"lanes:psv:forward\"]).astype(int)), axis=1)\n",
" edges[\"lanes:psv:backward\"] = edges[\"lanes:psv:backward\"].fillna(0) if \"lanes:psv:backward\" in edges.columns else 0\n",
" edges[\"lanes:psv:backward\"] = edges.apply(lambda row: np.min(np.array(row[\"lanes:psv:backward\"]).astype(int)), axis=1)\n",
"\n",
" edges[\"lanes:bus\"] = edges[\"lanes:bus\"].fillna(0) if \"lanes:bus\" in edges.columns else 0\n",
" edges[\"lanes:bus\"] = edges.apply(lambda row: np.min(np.array(row[\"lanes:bus\"]).astype(int)), axis=1)\n",
" edges[\"lanes:bus:forward\"] = edges[\"lanes:bus:forward\"].fillna(0) if \"lanes:bus:forward\" in edges.columns else 0\n",
" edges[\"lanes:bus:forward\"] = edges.apply(lambda row: np.min(np.array(row[\"lanes:bus:forward\"]).astype(int)), axis=1)\n",
" edges[\"lanes:bus:backward\"] = edges[\"lanes:bus:backward\"].fillna(0) if \"lanes:bus:backward\" in edges.columns else 0\n",
" edges[\"lanes:bus:backward\"] = edges.apply(lambda row: np.min(np.array(row[\"lanes:bus:backward\"]).astype(int)), axis=1)\n",
"\n",
" edges[\"lanes:mit\"] = edges.apply(lambda row: np.max([row[\"lanes\"],row[\"lanes:forward\"]+row[\"lanes:backward\"],2]) - np.max([row[\"lanes:psv\"],row[\"lanes:psv:forward\"]+row[\"lanes:psv:backward\"],row[\"lanes:bus:backward\"]]), axis=1)\n",
"\n",
" edges[\"highway\"] = edges[\"highway\"] if \"highway\" in edges.columns else \"\"\n",
" edges[\"motor_vehicle\"] = edges[\"motor_vehicle\"] if \"motor_vehicle\" in edges.columns else \"\"\n",
" edges[\"access\"] = edges[\"access\"] if \"access\" in edges.columns else \"\"\n",
" \n",
" edges[\"lanes:mit\"] = edges.apply(lambda row: (0 if np.isin(row[\"highway\"],[\"residential\",\"living_street\"]).any() else row[\"lanes:mit\"]), axis=1)\n",
" edges[\"lanes:mit\"] = edges.apply(lambda row: (0 if np.isin(row[\"motor_vehicle\"],[\"no\",\"private\",\"destination\",\"agricultural\",\"forestry\",\"delivery\"]).any() else row[\"lanes:mit\"]), axis=1)\n",
" edges[\"lanes:mit\"] = edges.apply(lambda row: (0 if np.isin(row[\"access\"], [\"no\",\"destination\"]).any() else row[\"lanes:mit\"]), axis=1)\n",
"\n",
" edges[\"length\"] = edges[\"length\"].fillna(0)\n",
" \n",
" edges[\"capacity\"] = edges.apply(lambda row: np.floor(row[\"length\"] * row[\"lanes:mit\"] / mot[\"length\"]), axis=1)\n",
" \n",
" edges_reset = edges.copy()\n",
" \n",
" # print(f\"Occupancy : {vehicles / sum(edges[\"capacity\"])}\")\n",
"\n",
" # Colorize the plotted roads graph:\n",
"\n",
" w_progress.description = \"Remplissage des rues\"\n",
" \n",
" counter = 0\n",
" nodes = [place[\"node\"]];\n",
" shoteach = vehicles / 10;\n",
" \n",
" edges = edges_reset.copy()\n",
" \n",
" edges_copy = pd.DataFrame(index=edges.index)\n",
" edges_copy[\"occupancy\"] = 0\n",
" edges_copy[\"capacity\"] = edges[\"capacity\"]\n",
" lastshot = 0\n",
"\n",
" w_progress.max = vehicles\n",
" \n",
" while counter < vehicles and nodes:\n",
" \n",
" idx = nodes.pop(0)\n",
" if idx in edges.index.get_level_values(0).values:\n",
" nodedges = edges.loc[idx]\n",
" nodedges_copy = edges_copy.loc[idx]\n",
" for i,r in nodedges.iterrows():\n",
" #print(nodes,counter,r[\"capacity\"],nodedges_copy.loc[i,\"occupancy\"],edges.shape[0])\n",
" if counter >= vehicles:\n",
" break\n",
" \n",
" if not nodedges_copy.loc[i,\"occupancy\"]:\n",
" nodedges_copy.loc[i,\"occupancy\"] = r[\"capacity\"]\n",
" counter += r[\"capacity\"]\n",
" w_progress.value = counter\n",
" nodes.append(i[0])\n",
" edges.drop(idx, inplace=True)\n",
" if counter // shoteach > lastshot:\n",
" lastshot += 1\n",
" \n",
" edges_copy[\"color\"] = edges_copy.apply(lambda row: 'darkred' if row[\"occupancy\"] else 'darkseagreen', axis=1)\n",
" edges_copy[\"color\"] = edges_copy.apply(lambda row: 'darkgray' if not row[\"capacity\"] else row[\"color\"], axis=1)\n",
" \n",
" colors = edges_copy.loc[:,\"color\"].to_numpy()\n",
" \n",
" fig, ax = ox.plot_graph(G, edge_color=colors, node_size=15, edge_linewidth=5, filepath=f\"./outputs/{place[\"shorthand\"]}/plot_{mot[\"shorthand\"]}_{lastshot}.svg\", save=True, show=False, close=True)\n",
" \n",
" w_progress.description = \"Terminé\"\n",
"\n",
"w_submit.on_click(simulate)\n",
"\n",
"if os.environ.get('SERVER_SOFTWARE','jupyter').startswith('voila'):\n",
" toolbar = widgets.HBox([widgets.VBox([w_place, w_mot])],layout=widgets.Layout(justify_content=\"center\"))\n",
"else:\n",
" toolbar = widgets.HBox([widgets.VBox([w_place, w_mot, w_submit, w_progress])],layout=widgets.Layout(justify_content=\"center\"))\n",
"\n",
"toolbar"
]
},
{
"cell_type": "markdown",
"id": "c16c39c7-c442-4460-97c6-318b01c357db",
"metadata": {},
"source": [
"Pour te donner une idée concrète de la quantité que cela représente, je vais les placer dans ta région. Imagine-toi ta région vide de voitures. Maintenant, remplis les routes de ta région une par une jusqu'à atteindre l'augmentation de la quantité de voitures prédite par l'OFROU en cas d'extension des autoroutes. Les routes complètement remplies sont représentées en rouge, celles encore vides en vert. Les routes représentées en gris sont des routes essentiellement résidentielles, elles ne sont donc pas prises en compte dans le calcul."
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "f4440649-731e-421e-b43d-17576bb8ad3f",
"metadata": {
"editable": true,
"slideshow": {
"slide_type": ""
},
"tags": []
},
"outputs": [
{
"data": {
"application/vnd.jupyter.widget-view+json": {
"model_id": "d7f46e8330ab475481525e195b871224",
"version_major": 2,
"version_minor": 0
},
"text/plain": [
"HBox(children=(VBox(children=(HBox(children=(IntSlider(value=1, max=10, min=1),), layout=Layout(justify_conten…"
]
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"slider = widgets.IntSlider(min=1, max=10, step=1)\n",
"\n",
"image = widgets.Image(format='svg+xml')\n",
"\n",
"def compute(*ignore):\n",
" file = open(f\"./outputs/{places[w_place.value][\"shorthand\"]}/plot_{w_mot.value}_{slider.value}.svg\", \"rb\")\n",
" read = file.read()\n",
" image.value = read\n",
"\n",
"slider.observe(compute, 'value')\n",
"\n",
"widgets.HBox([widgets.VBox([widgets.HBox([slider], layout=widgets.Layout(justify_content=\"center\")), image], layout=widgets.Layout(max_width=\"700px\"))], layout=widgets.Layout(justify_content=\"center\"))"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "adf24613-a4ab-433f-8a1d-6e1d168163aa",
"metadata": {},
"outputs": [],
"source": [
"from IPython.core.display import HTML\n",
"HTML(\"\"\"\n",
"<style>\n",
"h1, p {\n",
" display: block !important;\n",
" max-width: 700px !important;\n",
" margin: 0 auto !important;\n",
"}\n",
"</style>\n",
"\"\"\")"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"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.5"
},
"voila": {
"template": "lab",
"theme": "dark"
}
},
"nbformat": 4,
"nbformat_minor": 5
}