Michelg29's picture
Update app.py
f5ed798 verified
#from os import write
import joblib
import numpy as np
import streamlit as st
import requests
import pandas as pd
#from dotenv import load_dotenv
#import os
#load_dotenv(".api_env")
API_KEY = 'AIzaSyAWfT7LegzDMdwebnghQu3vjqYBZfJUdo0'
API_METEO = 'bd715056584f453cc48cb17af2e5e0bd'
# -------------------------------------------------------------------------
# PARAMÈTRES ET CHARGEMENT DU MODÈLE
# -------------------------------------------------------------------------
modele_linear = joblib.load("mon_modele.joblib")
modele_rf = joblib.load("mon_modele_rf1.joblib")
# Tarif fixe (0.70 $/km) pour le modèle
TARIF_PAR_KM = 0.70
st.title("Prédiction du prix d'une course")
# -------------------------------------------------------------------------
# FONCTIONS D'APPEL D'API
# -------------------------------------------------------------------------
def get_address_suggestions(input_text, api_key):
"""Autocomplete des adresses (Google Places)."""
if not input_text:
return []
url = (
"https://maps.googleapis.com/maps/api/place/autocomplete/json"
f"?input={input_text}"
f"&key={api_key}"
"&types=geocode"
"&language=fr"
)
resp = requests.get(url).json()
#print(resp)
suggestions = []
if resp["status"] == "OK":
for pred in resp["predictions"]:
suggestions.append({
"description": pred["description"],
"place_id": pred["place_id"]
})
for i in suggestions:
print(f"Adresse : {i['description']}\n"
f"ID : {i['place_id']} ")
print(suggestions)
return suggestions
def get_place_details(place_id, api_key):
"""Récupère (lat, lng) d’un lieu via Place Details API."""
url = (
"https://maps.googleapis.com/maps/api/place/details/json"
f"?place_id={place_id}"
f"&key={api_key}"
)
resp = requests.get(url).json()
if resp["status"] == "OK":
loc = resp["result"]["geometry"]["location"]
#print(loc)
return loc["lat"], loc["lng"]
else:
return None, None
def get_distance_and_duration(lat1, lng1, lat2, lng2, api_key):
"""
Retourne (distance_km, duration_min) entre deux points
via l’API Distance Matrix.
"""
origin = f"{lat1},{lng1}"
destination = f"{lat2},{lng2}"
url = (
"https://maps.googleapis.com/maps/api/distancematrix/json"
f"?origins={origin}"
f"&destinations={destination}"
f"&key={api_key}"
"&mode=driving"
)
resp = requests.get(url).json()
if resp["status"] == "OK":
elem = resp["rows"][0]["elements"][0]
if elem["status"] == "OK":
dist_m = elem["distance"]["value"] # mètres
dur_s = elem["duration"]["value"] # secondes
dist_km = round(dist_m / 1000, 2)
dur_min = round(dur_s / 60, 1)
return dist_km, dur_min
return None, None
def interpret_weather(description):
"""
:param description: recupere la description qui est de base une chaine de caractere qui
nous est retournée par la fonction get_weather.
:return: nous retourne un des mots clés (rain, snow, clear)
"""
if description is not None:
# convertissons toute la description en minuscule
description_lower = description.lower()
if "rain" in description_lower :
return "rain"
elif "snow" in description_lower :
return "snow"
else:
return "clear"
def get_weather(lat, lng, api_key):
"""
Récupère la météo actuelle à partir des coordonnées GPS.
Retourne (température, description) ou (None, None) en cas d'erreur.
"""
url = (
f"https://api.openweathermap.org/data/2.5/weather"
f"?lat={lat}"
f"&lon={lng}"
f"&appid={api_key}"
f"&units=metric"
f"&lang=en"
)
response = requests.get(url)
if response.status_code == 200:
data = response.json()
temp = data["main"]["temp"]
description = data["weather"][0]["description"]
desc = interpret_weather(description)
return temp, desc
else:
return None, None
def encoding_weather(description):
"""
:param description: recupere la description du retour de la fonction get_weather.
:return: nous retourne 0 ou 1 (pour que le modele comprenne)
"""
if description is not None:
description_lower = description.lower()
if description_lower == "rain":
return 1,0
elif description_lower == "snow":
return 0,1
else:
return 0,0
else:
print("Description non recuperée")
return
def interpret_traffic_level(elem):
"""
Compare duration_in_traffic vs. duration
pour en déduire un label (Low, Medium, High).
"""
dur_no_traffic = elem["duration"]["value"] # en secondes
dur_with_traffic = elem["duration_in_traffic"]["value"]
ratio = dur_with_traffic / dur_no_traffic
# Ex. si la durée augmente de moins de 10% => Low
# entre 10% et 30% => Medium
# plus de 30% => High
if ratio < 1.10:
return "Low"
elif ratio < 1.30:
return "Medium"
else:
return "High"
def get_traffic_conditions(lat1, lng1, lat2, lng2, api_key):
"""
Récupère les conditions de trafic entre deux points via l'API Distance Matrix.
Retourne le niveau de trafic (Low, Medium, High).
"""
origin = f"{lat1},{lng1}"
destination = f"{lat2},{lng2}"
url = (
"https://maps.googleapis.com/maps/api/distancematrix/json"
f"?origins={origin}"
f"&destinations={destination}"
f"&key={api_key}"
"&mode=driving"
"&departure_time=now"
"&traffic_model=best_guess"
)
resp = requests.get(url).json()
if resp["status"] == "OK":
elem = resp["rows"][0]["elements"][0]
if elem["status"] == "OK":
# Extraire duration & duration_in_traffic et créer un label
return interpret_traffic_level(elem)
return "Unknown"
def encodage_traffic_conditions(level):
"""
:param level: c'est le niveau du trafic qui sera (Low,Medium ou High)
:return: retourner un tuple avec deux valeurs 0 ou 1 pour pouvoir etre considerer par notre modele
"""
if level is not None:
if level == "Low":
return 1,0
elif level == "Medium":
return 0,1
elif level == "High":
return 0,0
else:
print("Le niveau du traffic est introuvable")
return
# -------------------------------------------------------------------------
# INTERFACE UTILISATEUR : ADRESSES
# -------------------------------------------------------------------------
st.header("Adresse de départ")
pickup_input = st.text_input(
"Tapez votre adresse de départ (puis Entrée)",
placeholder="Ex: 84 Rue de Sauternes"
)
pickup_suggestions = []
if pickup_input:
pickup_suggestions = get_address_suggestions(pickup_input, API_KEY)
pickup_options = [s["description"] for s in pickup_suggestions]
selected_pickup = st.selectbox("Suggestions pour l'adresse de départ :", pickup_options,
placeholder= "Pas d'options proposés")
st.header("Adresse d'arrivée")
destination_input = st.text_input(
"Tapez votre adresse d'arrivée (puis Entrée)",
placeholder="Ex: 4949 Métropolitain Est"
)
dest_suggestions = []
if destination_input:
dest_suggestions = get_address_suggestions(destination_input, API_KEY)
dest_options = [s["description"] for s in dest_suggestions]
selected_destination = st.selectbox("Suggestions pour l'adresse d'arrivée :", dest_options)
# -------------------------------------------------------------------------
# BOUTON POUR TOUT CALCULER
# -------------------------------------------------------------------------
if st.button("Prédire le prix"):
# Vérifier que l'utilisateur a sélectionné une suggestion
if not selected_pickup or not selected_destination:
st.error("Veuillez sélectionner une adresse de départ et d'arrivée.")
else:
# Récupérer le place_id du départ
pickup_place_id = None
for s in pickup_suggestions:
if s["description"] == selected_pickup:
pickup_place_id = s["place_id"]
break
# Récupérer le place_id de l'arrivée
dest_place_id = None
for s in dest_suggestions:
if s["description"] == selected_destination:
dest_place_id = s["place_id"]
break
if pickup_place_id and dest_place_id:
# Coords de départ
pickup_coords = get_place_details(pickup_place_id, API_KEY)
# Coords d'arrivée
dest_coords = get_place_details(dest_place_id, API_KEY)
if pickup_coords and dest_coords:
dist_km, dur_min = get_distance_and_duration(
pickup_coords[0], pickup_coords[1],
dest_coords[0], dest_coords[1],
API_KEY
)
print(pickup_coords)
#
traffic_level = get_traffic_conditions(pickup_coords[0],pickup_coords[1],
dest_coords[0],dest_coords[1],API_KEY)
temp, desc = get_weather(pickup_coords[0],pickup_coords[1],API_METEO)
if dist_km is not None and dur_min is not None:
# Affichage
if dur_min > 60:
heures = dur_min // 60
minutes = dur_min % 60
#print(heures, minutes)
st.write(f"**Durée estimée** : {int(heures)} heures {int(minutes)} minutes")
else:
st.write(f"**Durée estimée** : {int(dur_min)} minutes")
st.write(f"**Distance estimée** : {dist_km} km")
#
traffic_low, traffic_medium = encodage_traffic_conditions(traffic_level)
#
weather_rain, weather_snow = encoding_weather(desc)
# Appel du modèle ML :
# => Le modèle attend [distance, durée, prix_km=0.70]
# => On lui passe donc ces 3 features
colonnes = ['distance', 'minutes', 'prix_kilometre',
'Traffic_Conditions_Low', 'Traffic_Conditions_Medium',
'Weather_Rain', 'Weather_Snow']
donnee_entree = pd.DataFrame([[dist_km, dur_min, TARIF_PAR_KM,
traffic_low, traffic_medium,
weather_rain, weather_snow]], columns=colonnes)
if dist_km > 50:
prediction_modele1 = modele_linear.predict(donnee_entree)
print("prediction_modele1")
st.write(f"**Prédiction du modèle** : {prediction_modele1[0]:.2f} $")
else:
prediction_modele2 = modele_rf.predict(donnee_entree)
print("prediction_modele2")
st.write(f"**Prédiction du modèle** : {prediction_modele2[0]:.2f} $")
else:
st.error("Impossible de calculer la distance ou la durée.")
else:
st.error("Impossible de récupérer les coordonnées GPS.")
# Affichage des conditions du traffic
if traffic_level:
st.write(f"**Conditions de trafic** : {traffic_level}")
else:
st.write('**Conditions de traffic** : Inconnues')
# Affichage de la meteo
meteo = get_weather(pickup_coords[0], pickup_coords[1], API_METEO)
if meteo:
temperature, descrip = meteo
st.write(f"**Temperature** : {round(temperature)} degré celsius, principalement : {descrip} en ce moment")
else:
st.error("Place ID introuvable pour l'adresse sélectionnée.")