parking-land/find_city_osm_id.py

341 lines
No EOL
13 KiB
Python
Executable file

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Script pour trouver l'identifiant OpenStreetMap d'une ville à partir de son nom.
Utilise l'API Nominatim pour effectuer la recherche.
"""
import sys
import argparse
import requests
import json
import time
import os
import subprocess
from urllib.parse import quote
def search_city(city_name, country=None, limit=5):
"""
Recherche une ville par son nom en utilisant l'API Nominatim d'OpenStreetMap.
Args:
city_name (str): Nom de la ville à rechercher
country (str, optional): Code pays pour filtrer les résultats (ex: 'fr', 'be', 'ch')
limit (int, optional): Nombre maximum de résultats à retourner
Returns:
list: Liste des correspondances trouvées
"""
# Construire l'URL de recherche
base_url = "https://nominatim.openstreetmap.org/search"
# Paramètres de recherche
params = {
'q': city_name,
'format': 'json',
'addressdetails': 1,
'limit': limit,
'polygon_geojson': 0,
}
# Ajouter le filtre de pays si spécifié
if country:
params['countrycodes'] = country
# Ajouter des paramètres pour filtrer les résultats par type
params['featuretype'] = 'city'
# En-têtes pour respecter les conditions d'utilisation de Nominatim
headers = {
'User-Agent': 'CityOSMIDFinder/1.0',
'Accept-Language': 'fr-FR,fr;q=0.9,en-US;q=0.8,en;q=0.7',
}
try:
# Effectuer la requête
response = requests.get(base_url, params=params, headers=headers)
# Vérifier si la requête a réussi
if response.status_code != 200:
print(f"Erreur lors de la requête: {response.status_code}")
return []
# Analyser les résultats
results = response.json()
# Filtrer pour ne garder que les villes/villages
filtered_results = []
for result in results:
# Vérifier si c'est une ville ou un village
if 'address' in result:
place_type = None
for key in ['city', 'town', 'village', 'municipality', 'hamlet']:
if key in result['address']:
place_type = key
break
if place_type:
# Ajouter des informations supplémentaires
result['place_type'] = place_type
# Rechercher l'ID de relation
osm_id = result.get('osm_id')
osm_type = result.get('osm_type')
if osm_id and osm_type:
# Convertir le type en majuscule pour la première lettre
osm_type = osm_type.capitalize()
# Ajouter à la liste filtrée
filtered_results.append(result)
return filtered_results
except Exception as e:
print(f"Erreur lors de la recherche: {str(e)}")
return []
def get_relation_id(osm_type, osm_id):
"""
Récupère l'ID de relation pour un élément OSM.
Si l'élément est déjà une relation, retourne simplement son ID.
Sinon, essaie de trouver une relation associée.
Args:
osm_type (str): Type d'élément OSM ('Node', 'Way', 'Relation')
osm_id (int): ID de l'élément OSM
Returns:
int: ID de la relation ou None si aucune relation n'est trouvée
"""
if osm_type == 'Relation':
return osm_id
# Pour les nodes et ways, on doit chercher la relation associée
# Construire la requête Overpass
if osm_type == 'Node':
query = f"""
[out:json];
node({osm_id});
is_in->.a;
relation.a["admin_level"="8"];
out ids;
"""
elif osm_type == 'Way':
query = f"""
[out:json];
way({osm_id});
is_in->.a;
relation.a["admin_level"="8"];
out ids;
"""
else:
return None
# Envoyer la requête à l'API Overpass
try:
overpass_url = "https://overpass-api.de/api/interpreter"
response = requests.post(overpass_url, data={"data": query})
if response.status_code != 200:
print(f"Erreur lors de la requête Overpass: {response.status_code}")
return None
data = response.json()
# Extraire l'ID de relation
if 'elements' in data and data['elements']:
for element in data['elements']:
if element.get('type') == 'relation':
return element.get('id')
return None
except Exception as e:
print(f"Erreur lors de la récupération de l'ID de relation: {str(e)}")
return None
def display_results(results):
"""
Affiche les résultats de recherche de manière formatée.
Args:
results (list): Liste des résultats de recherche
"""
if not results:
print("Aucun résultat trouvé.")
return
print(f"\n{len(results)} résultat(s) trouvé(s):\n")
for i, result in enumerate(results, 1):
# Extraire les informations
name = result.get('display_name', 'Nom inconnu')
osm_type = result.get('osm_type', '').capitalize()
osm_id = result.get('osm_id', 'ID inconnu')
place_type = result.get('place_type', 'Type inconnu')
# Extraire les informations d'adresse
address = result.get('address', {})
city = address.get('city', address.get('town', address.get('village', address.get('municipality', address.get('hamlet', '')))))
county = address.get('county', '')
state = address.get('state', '')
country = address.get('country', '')
# Afficher les informations
print(f"{i}. {city} ({place_type})")
print(f" Localisation: {county}, {state}, {country}")
print(f" OSM ID: {osm_id} (Type: {osm_type})")
print(f" URL: https://www.openstreetmap.org/{osm_type.lower()}/{osm_id}")
# Rechercher l'ID de relation si ce n'est pas déjà une relation
if osm_type != 'Relation':
print(" Recherche de l'ID de relation associé...")
relation_id = get_relation_id(osm_type, osm_id)
if relation_id:
print(f" ID de relation: {relation_id}")
print(f" URL de relation: https://www.openstreetmap.org/relation/{relation_id}")
else:
print(" Aucune relation trouvée.")
print()
def generate_map(osm_id, output_path=None, use_folium=True, verbose=False):
"""
Génère une carte pour la ville avec l'ID OpenStreetMap spécifié.
Args:
osm_id (int): ID OpenStreetMap de la ville
output_path (str, optional): Chemin où sauvegarder la carte
use_folium (bool, optional): Utiliser Folium au lieu d'OSMnx
verbose (bool, optional): Afficher les messages de débogage
Returns:
str: Chemin vers la carte générée ou None en cas d'erreur
"""
try:
# Vérifier si le script generate_city_map.py existe
if not os.path.exists("generate_city_map.py"):
print("Erreur: Le script generate_city_map.py n'a pas été trouvé.")
return None
# Construire la commande
cmd = ["python", "generate_city_map.py", str(osm_id)]
# Ajouter les options
if output_path:
cmd.extend(["-o", output_path])
if use_folium:
cmd.append("--folium")
if verbose:
cmd.append("-v")
# Exécuter la commande
print(f"Génération de la carte pour l'ID OSM {osm_id}...")
print(f"Commande: {' '.join(cmd)}")
result = subprocess.run(cmd, capture_output=True, text=True)
# Vérifier si la commande a réussi
if result.returncode == 0:
output_file = output_path if output_path else "city_map.html" if use_folium else "city_map.jpg"
print(f"Carte générée avec succès: {output_file}")
# Afficher les messages de sortie si verbose
if verbose:
print("\nSortie de la commande:")
print(result.stdout)
return output_file
else:
print(f"Erreur lors de la génération de la carte (code {result.returncode}):")
print(result.stderr)
return None
except Exception as e:
print(f"Erreur lors de la génération de la carte: {str(e)}")
return None
def main():
"""
Fonction principale qui traite les arguments de ligne de commande
et effectue la recherche.
"""
parser = argparse.ArgumentParser(description='Recherche l\'identifiant OpenStreetMap d\'une ville à partir de son nom.')
parser.add_argument('city_name', type=str, help='Nom de la ville à rechercher')
parser.add_argument('-c', '--country', type=str, help='Code pays pour filtrer les résultats (ex: fr, be, ch)', default='fr')
parser.add_argument('-l', '--limit', type=int, default=5, help='Nombre maximum de résultats à afficher (défaut: 5)')
parser.add_argument('-j', '--json', action='store_true', help='Afficher les résultats au format JSON', default=False)
parser.add_argument('-r', '--relation-only', action='store_true', help='Afficher uniquement les IDs de relation', default=False)
# Nouvelles options pour la génération de carte
parser.add_argument('-g', '--generate-map', action='store_true', default=True,
help='Générer automatiquement une carte pour le premier résultat (actif par défaut)')
parser.add_argument('-ng', '--no-generate-map', action='store_false', dest='generate_map',
help='Ne pas générer automatiquement de carte')
parser.add_argument('-o', '--output', type=str, default='carte_ville.html',
help='Chemin où sauvegarder la carte générée (défaut: carte_ville.html)')
parser.add_argument('--no-folium', action='store_false', dest='use_folium',
help='Utiliser OSMnx au lieu de Folium pour la génération de carte', default=True)
parser.add_argument('-v', '--verbose', action='store_true',
help='Afficher les messages de débogage')
args = parser.parse_args()
# Effectuer la recherche
print(f"Recherche de la ville: {args.city_name}")
if args.country:
print(f"Filtrage par pays: {args.country}")
results = search_city(args.city_name, args.country, args.limit)
# Afficher les résultats
if args.json:
print(json.dumps(results, indent=2))
elif args.relation_only:
for result in results:
osm_type = result.get('osm_type', '').capitalize()
osm_id = result.get('osm_id', '')
if osm_type == 'Relation':
print(f"{osm_id}")
else:
relation_id = get_relation_id(osm_type, osm_id)
if relation_id:
print(f"{relation_id}")
else:
display_results(results)
# Générer une carte si demandé et si des résultats ont été trouvés
if args.generate_map and results:
# Prendre le premier résultat
first_result = results[0]
osm_type = first_result.get('osm_type', '').capitalize()
osm_id = first_result.get('osm_id', '')
# Obtenir l'ID de relation si nécessaire
if osm_type != 'Relation':
relation_id = get_relation_id(osm_type, osm_id)
if relation_id:
osm_id = relation_id
else:
print("Aucune relation trouvée pour ce résultat. Utilisation de l'ID original.")
# Générer la carte
if osm_id:
print("\n--- Génération automatique de la carte ---")
map_path = generate_map(osm_id, args.output, args.use_folium, args.verbose)
if map_path:
print(f"\nCarte générée avec succès: {map_path}")
# Si c'est un fichier HTML, suggérer de le convertir en JPG
if map_path.endswith('.html'):
print("\nPour convertir cette carte HTML en image JPG, utilisez la commande:")
print(f"python html2jpg.py {map_path} -o {os.path.splitext(map_path)[0]}.jpg")
else:
print("\nÉchec de la génération de la carte.")
if __name__ == "__main__":
main()