Lbasara's picture
Traitement geocodage en lot
ad68f89
import requests
import folium
import colorcet as cc
import plotly.express as px
import panel as pn
import pandas as pd
import geopandas as gpd
from time import sleep
from bokeh.models.widgets.tables import DateFormatter
from vrp import calcul_routes
from tableur import Tableur
from routingmat import fetch_matrices
pn.extension("tabulator", 'filedropper', 'plotly')
wgs84="EPSG:4326"
l93="EPSG:9794"
pal=cc.glasbey_dark
def mat_figs(dfadr, mat_dist, mat_dur):
labels=[ f'{row["oid"]}:{row["lieu"]}' for _, row in dfadr.iterrows()]
fig_dist = px.imshow(mat_dur, height=1500, x=labels, y=labels, title = "Distance (m) entre deux adresses")
fig_dur = px.imshow(mat_dist, height=1500, x=labels, y=labels, title = "Temps (minutes) entre deux adresses")
return fig_dist, fig_dur
duree_taxi=pn.widgets.IntInput(name='Durée max taxi', value=45, step=1, start=0, end=300)
duree_ecole=pn.widgets.IntInput(name='Durée max devant école', value=10, step=1, start=0, end=60)
capacité=pn.widgets.IntInput(name='Capacité taxi', value=3, step=1, start=0, end=10)
def mise_en_forme_dfinal(sol, dfadr):
dfsol=sol.routes
dfsol["num_vehicule"]=dfsol.groupby("vehicle_id").ngroup()
dfsol["durée (minutes)"]=dfsol["duration"]//60
dfsol["temps d'attente (minutes)"]=dfsol["waiting_time"]//60
dfsol["indice"]=dfsol.location_index.map(dfadr["oid"].to_dict())
dfsol["lieu"] = dfsol.location_index.map(dfadr["lieu"].to_dict())
dfsol["geometry"] = dfsol.location_index.map(dfadr["geometry"].to_dict())
dfsol["heure"]=dfsol.arrival.astype("datetime64[s]").dt.time
goodcols=['num_vehicule', 'type', 'distance', 'durée (minutes)',
"temps d'attente (minutes)", 'indice', 'lieu', 'geometry', 'heure']
dffinal=dfsol[goodcols].query("type in ['pickup', 'delivery']")
return dffinal
def fetch_itinéraires(dffinal):
dres={}
for igp, gp in dffinal.groupby("num_vehicule"):
coo=[f"{p.x},{p.y}" for p in gp.geometry.to_list()]
params=dict(resource= "bdtopo-osrm",
start=coo[0],
end=coo[-1],
intermediates="|".join(coo[1:-2]),
geometryFormat="geojson",
getSteps='false',
getBbox='true',
timeUnit="minute",
crs=wgs84)
r=requests.get(url="https://data.geopf.fr/navigation/itineraire",
params=params)
sleep(0.15)
if r.ok:
dres[igp]=r.json()
return dres
def carte(dffinal, dres, dfadr):
tiles='Stadia.AlidadeSmooth'
tiles='Cartodb Positron'
bbox=dfadr.total_bounds #[0.036343, 48.41117, 0.109909, 48.465001]
m = folium.Map(location=[(bbox[3]+bbox[1])/2, (bbox[2]+bbox[0])/2], tiles=tiles)#, zoom_start=10)
for igp, gp in dffinal.groupby("num_vehicule"):
dirs=dres[igp]
color=pal[igp]
folium.GeoJson(
name=f'Vehicle {igp}',
data={"type": "FeatureCollection",
"features": [{"type": "Feature",
"geometry": dirs['geometry'],
"properties": {"color": color} }]},
style_function=lambda x: {"color": x['properties']['color']}
).add_to(m)
for _, step in gp.iterrows():
icon='user' if step['type']=='pickup' else 'briefcase'
folium.Marker(
location=[step["geometry"].y, step["geometry"].x],
icon=folium.Icon(color="lightgray", icon_color=color, icon=icon),
popup=f'{step["lieu"]}\n{step["heure"]}'
).add_to(m)
return m
tabd=Tableur(idx_offset=1)
tabp=Tableur(idx_offset=1001, delivery_tab=tabd)
def tous_calculs(_):
dfp=tabp.df.dropna()
dest_col=tabp.dest_col.value
gdfadr=gpd.GeoDataFrame(pd.concat([
dfp[["suggestion", "geometry_p"]].rename(columns={"suggestion" : "lieu", "geometry_p": "geometry"}),
tabd.df[[dest_col, "geometry"]][tabd.df[dest_col].isin(tabp.df.dropna()[dest_col])].rename(columns={dest_col : "lieu"})],
keys=["p", "d"], names=['type', 'oid']).drop_duplicates().reset_index() )
ladr=gdfadr["lieu"].to_list()
mat_dist, mat_dur=fetch_matrices(gdfadr)
fig_dist, fig_dur=mat_figs(gdfadr, mat_dist, mat_dur)
tabs.append(('Matrice de distances', pn.pane.Plotly(fig_dist, width=1100, height=1000 )))
tabs.append(('Matrice de durées', pn.pane.Plotly(fig_dur, width=1100, height=1000 )))
sol=calcul_routes(dfp, ladr, mat_dist, mat_dur, duree_taxi.value, duree_ecole.value, capacité.value)
gpu=dfp.loc[list({u["id"] for u in sol.to_dict()["unassigned"]})][['suggestion', 'indice_destination', dest_col, 'arrivée_max']].copy()
gpu["Distance (km)"]=gpu.apply(lambda row: mat_dist[ladr.index(row["suggestion"])][ladr.index( row[dest_col])], axis=1)
gpu["Durée (min)"]=gpu.apply(lambda row: mat_dur[ladr.index( row["suggestion"])][ladr.index( row[dest_col])]//60, axis=1)
tabs.append(('Non assignés', pn.widgets.Tabulator(gpu, disabled=True, height=1000)))
dffinal=mise_en_forme_dfinal(sol, gdfadr)
tab_final=pn.widgets.Tabulator(dffinal.drop("geometry", axis=1).value_counts().reset_index().sort_values(by=["num_vehicule", "heure"]).reset_index(drop=True),
formatters={"heure" : DateFormatter(format="%H:%M")},
disabled=True,
height=1000)
tabs.append(('Tableau des résultats', tab_final.style.map(lambda v: f'background-color:{pal[v]};color:white', subset="num_vehicule")))
dres=fetch_itinéraires(dffinal)
m=carte(dffinal, dres, gdfadr)
tabs.append(('Carte des résultats', pn.pane.plot.Folium(m, height=1000) ))
return
bouton=pn.widgets.Button(name="Lancer les calculs", button_type="primary", on_click=tous_calculs)
tabs=pn.Tabs(
('Destinations', tabd.get_panel()),
('Usagers', tabp.get_panel()),
('Calculs des routes', pn.Column(duree_taxi, duree_ecole, capacité, bouton) ), )
template= pn.template.MaterialTemplate(title="Problème de tournée des véhicules avec capacités et fenêtre temporelle")
template.main.append(tabs)
template.header.append("v0.2.3")
template.servable()