notebook + environment + stack
This commit is contained in:
parent
b2868a66ad
commit
c018512d4d
3 changed files with 354 additions and 0 deletions
10
Dockerfile
Normal file
10
Dockerfile
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
FROM continuumio/miniconda3 AS base-deps
|
||||||
|
WORKDIR /root/home/
|
||||||
|
ADD . /root/home/remplis-ta-ville
|
||||||
|
RUN apt update
|
||||||
|
RUN cd remplis-ta-ville && conda env create && conda init
|
||||||
|
|
||||||
|
FROM base-deps
|
||||||
|
|
||||||
|
EXPOSE 8866
|
||||||
|
ENTRYPOINT cd remplis-ta-ville && conda run -n gis voila remplis-ta-ville.ipynb --no-browser --Voila.ip=0.0.0.0
|
14
environment.yml
Normal file
14
environment.yml
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
name: gis
|
||||||
|
channels:
|
||||||
|
- conda-forge
|
||||||
|
- defaults
|
||||||
|
dependencies:
|
||||||
|
- osmnx
|
||||||
|
- geopandas
|
||||||
|
- networkx
|
||||||
|
- pandas
|
||||||
|
- numpy
|
||||||
|
- jupyter
|
||||||
|
- ipython
|
||||||
|
- ipywidgets
|
||||||
|
- voila
|
330
remplis-ta-ville.ipynb
Normal file
330
remplis-ta-ville.ipynb
Normal file
|
@ -0,0 +1,330 @@
|
||||||
|
{
|
||||||
|
"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
|
||||||
|
}
|
Loading…
Reference in a new issue