Comment trouver le bureau de poste le plus proche de chez vous à l’aide de Voronoï et Python ?
Bien entendu, cette question peut se formuler avec autre chose qu’un bureau de poste…
Si vous ne connaissez pas le pavage de Voronoï, je vous encourage à regarder ce post.
Vous pouvez aussi regarder cet article sur Wikipedia.
Comment trouver le bureau de poste le plus proche de chez vous? La méthode.
La méthode est “simple” : on répertorie toutes les coordonnées de ce que l’on cherche (ici, les bureaux de poste) et on fait notre pavage…
Facile à dire, mais concrètement ?
Récupération des coordonnées
On va s’aider de l’API OpenStreetMap.
import requests
def get_post_offices_overpass(city_name):
query = f"""
[out:json];
area["name"="{city_name}"]->.searchArea;
node["amenity"="post_office"](area.searchArea);
out body;
"""
url = "http://overpass-api.de/api/interpreter"
response = requests.post(url, data={'data': query})
data = response.json()
post_offices = [(element['tags'].get('name', 'Post Office'), element['lat'], element['lon'])
for element in data['elements']]
return post_offices
Placement sur une carte et tracé du pavage de Voronoï
On va s’aider de folium.
import folium
import numpy as np
from shapely.geometry import Polygon
from shapely.ops import unary_union
from geovoronoi import voronoi_regions_from_coords
def create_map(city_name="Bordeaux"):
post_offices = get_post_offices_overpass(city_name)
# Créer une carte centrée sur Bordeaux
map_bordeaux = folium.Map(location=[44.8378, -0.5792], zoom_start=13)
# Ajouter les bureaux de poste à la carte
for name, lat, lng in post_offices:
folium.Marker(location=[lat, lng], popup=name).add_to(map_bordeaux)
# Convertir les coordonnées des bureaux de poste en objets Point
coords = np.array([(lng, lat) for name, lat, lng in post_offices])
# Définir une zone de délimitation (par exemple, les limites de Bordeaux)
# Utiliser un polygone explicite pour Bordeaux
boundary_coords = [(-0.8, 45.1), (0.02, 45.1), (0.02, 44.6), (-0.8, 44.6), (-0.8, 45.1)]
boundary_polygon = Polygon(boundary_coords)
# Calculer les régions de Voronoi à partir des coordonnées
try:
region_polys, region_pts = voronoi_regions_from_coords(coords, boundary_polygon)
except RuntimeError as e:
print(f"Erreur lors de la génération des régions de Voronoi: {e}")
return
# Ajouter les régions de Voronoi à la carte folium
for region in region_polys.values():
folium.Polygon(locations=[(lat, lng) for lng, lat in region.exterior.coords]).add_to(map_bordeaux)
# Sauvegarder la carte dans un fichier HTML
map_bordeaux.save('map_bordeaux.html')
return None
Cela va générer une page HTML d’une carte zoomable:

Bon, là, ce n’est qu’une image car je ne peux pas insérer la page directement, mais vous voyez le genre ?
Et pour une autre ville ?
La seule chose qui diffère dans le code est le rectangle dans lequel se trouve la ville: il faudra le construire vous même (le boundary_polygon).
Pour se faire, vous pouvez ajouter l’instruction:
boundary_polygon = Polygon(boundary_coords)
print("Coords:", coords) # ligne à ajouter
tout de suite après avoir défini la variable boundary_polygon. Vous verrez ainsi où se situent les points et vous n’aurez plus qu’à modifier les coordonnées des coins du rectangle :
boundary_coords = [(xmin, ymax), (xmax, ymax), (xmax, ymin), (xmin, ymin), (xmin, ymax)]
Et pour autre chose qu’un bureau de poste?
Il faudra changer les informations de la fonction get_post_offices_overpass. Concrètement, il faudra changer “Post Office” par ce que vous voulez. Pour connaître les termes, consultez la page:
https://wiki.openstreetmap.org/wiki/FR:Key:shop
Et c’est ainsi que je m’aperçus que je faisais 1 km pour rien à chaque fois…
cool