#!/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()