Spaces:
Sleeping
Sleeping
| 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() |