Ajout suivi prospection : statut, import en masse, message type, trajet+péage
- Statut de prospection (colonne CSV) avec badge coloré et filtre - Import en masse de liens Facebook (streaming, dédoublonnage) - Modèle de message de contact configurable + copie en un clic - Estimation distance/carburant/péage via OpenStreetMap (Nominatim + OSRM) - Section Paramètres + config.json (non versionné)
This commit is contained in:
108
trajet.py
Normal file
108
trajet.py
Normal file
@@ -0,0 +1,108 @@
|
||||
"""Distance routière et coût carburant entre deux adresses (OpenStreetMap : Nominatim + OSRM).
|
||||
|
||||
Aucune dépendance ni clé d'API : on utilise les services publics gratuits d'OpenStreetMap
|
||||
via la bibliothèque standard. Usage personnel et ponctuel (un prospect à la fois).
|
||||
"""
|
||||
|
||||
import json
|
||||
import re
|
||||
import urllib.parse
|
||||
import urllib.request
|
||||
|
||||
NOMINATIM = "https://nominatim.openstreetmap.org/search"
|
||||
OSRM = "https://router.project-osrm.org/route/v1/driving"
|
||||
# Nominatim impose un User-Agent identifiant l'application
|
||||
ENTETES = {"User-Agent": "AutoMood/1.0 (outil local de prospection)"}
|
||||
|
||||
# Les coordonnées d'une adresse ne changent pas : on évite de re-géocoder (et on
|
||||
# reste poli envers Nominatim, dont l'usage est limité à ~1 requête/seconde).
|
||||
_cache_geo = {}
|
||||
|
||||
|
||||
class ErreurTrajet(Exception):
|
||||
pass
|
||||
|
||||
|
||||
def _http_json(url):
|
||||
requete = urllib.request.Request(url, headers=ENTETES)
|
||||
try:
|
||||
with urllib.request.urlopen(requete, timeout=20) as reponse:
|
||||
return json.load(reponse)
|
||||
except Exception as e:
|
||||
raise ErreurTrajet(f"Service de cartographie injoignable : {e}")
|
||||
|
||||
|
||||
def geocoder(adresse):
|
||||
"""Adresse texte -> (longitude, latitude). Lève ErreurTrajet si introuvable."""
|
||||
adresse = (adresse or "").strip()
|
||||
if not adresse:
|
||||
raise ErreurTrajet("Adresse vide.")
|
||||
if adresse in _cache_geo:
|
||||
return _cache_geo[adresse]
|
||||
params = urllib.parse.urlencode({
|
||||
"q": adresse, "format": "json", "limit": 1, "countrycodes": "fr",
|
||||
})
|
||||
donnees = _http_json(f"{NOMINATIM}?{params}")
|
||||
if not donnees:
|
||||
raise ErreurTrajet(f"Adresse introuvable : « {adresse} ».")
|
||||
coord = (float(donnees[0]["lon"]), float(donnees[0]["lat"]))
|
||||
_cache_geo[adresse] = coord
|
||||
return coord
|
||||
|
||||
|
||||
def _est_autoroute(ref):
|
||||
"""Vrai si le numéro de route est une autoroute française (« A 11 », « A8;E60 »...).
|
||||
|
||||
Heuristique : la plupart des autoroutes « A » sont à péage. Certaines sont
|
||||
gratuites (rocades, sections urbaines) : l'estimation reste donc approximative.
|
||||
"""
|
||||
return any(re.match(r"A\s?\d", t.strip(), re.I) for t in re.split(r"[;,]", ref or ""))
|
||||
|
||||
|
||||
def itineraire(depart, arrivee):
|
||||
"""(lon, lat) x2 -> (distance_km, duree_min, km_autoroute) du trajet le plus court."""
|
||||
(lon1, lat1), (lon2, lat2) = depart, arrivee
|
||||
donnees = _http_json(f"{OSRM}/{lon1},{lat1};{lon2},{lat2}?overview=false&steps=true")
|
||||
if donnees.get("code") != "Ok" or not donnees.get("routes"):
|
||||
raise ErreurTrajet("Aucun itinéraire routier trouvé entre ces deux adresses.")
|
||||
route = donnees["routes"][0]
|
||||
metres_autoroute = sum(
|
||||
etape.get("distance", 0)
|
||||
for jambe in route.get("legs", [])
|
||||
for etape in jambe.get("steps", [])
|
||||
if _est_autoroute(etape.get("ref"))
|
||||
)
|
||||
return route["distance"] / 1000.0, route["duration"] / 60.0, metres_autoroute / 1000.0
|
||||
|
||||
|
||||
def calculer(adresse_depart, adresse_arrivee, conso_l_100km, prix_carburant, cout_peage_km=0.0):
|
||||
"""Estime distance, durée, coût carburant et péage (aller simple et aller-retour).
|
||||
|
||||
Le péage est estimé : km d'autoroute du trajet × `cout_peage_km` (tarif moyen
|
||||
paramétrable, en €/km). Mettre 0 pour ne pas compter de péage.
|
||||
"""
|
||||
distance_km, duree_min, km_peage = itineraire(
|
||||
geocoder(adresse_depart), geocoder(adresse_arrivee))
|
||||
carburant = distance_km / 100.0 * conso_l_100km * prix_carburant
|
||||
peage = km_peage * cout_peage_km
|
||||
cout_aller = carburant + peage
|
||||
return {
|
||||
"distance_km": round(distance_km, 1),
|
||||
"distance_aller_retour_km": round(distance_km * 2, 1),
|
||||
"duree_min": round(duree_min),
|
||||
"km_peage": round(km_peage, 1),
|
||||
"carburant_aller": round(carburant, 2),
|
||||
"carburant_aller_retour": round(carburant * 2, 2),
|
||||
"peage_aller": round(peage, 2),
|
||||
"peage_aller_retour": round(peage * 2, 2),
|
||||
"cout_aller": round(cout_aller, 2),
|
||||
"cout_aller_retour": round(cout_aller * 2, 2),
|
||||
}
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import sys
|
||||
if len(sys.argv) != 3:
|
||||
print("Usage : python trajet.py <adresse-depart> <adresse-arrivee>")
|
||||
sys.exit(1)
|
||||
print(json.dumps(calculer(sys.argv[1], sys.argv[2], 6.5, 1.90), ensure_ascii=False, indent=2))
|
||||
Reference in New Issue
Block a user