mirror of
https://forge.chapril.org/tykayn/osm-commerces
synced 2025-10-04 17:04:53 +02:00
add computing from osm history
This commit is contained in:
parent
da60f964ab
commit
66bbce5e85
13 changed files with 3921 additions and 0 deletions
287
counting_osm_objects/get_poly.py
Normal file
287
counting_osm_objects/get_poly.py
Normal file
|
@ -0,0 +1,287 @@
|
|||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
Script pour récupérer le polygone d'une commune française à partir de son code INSEE.
|
||||
|
||||
Ce script:
|
||||
1. Demande un code INSEE
|
||||
2. Interroge l'API Overpass Turbo pour obtenir les limites administratives
|
||||
3. Extrait le polygone de la commune
|
||||
4. Sauvegarde le polygone dans un fichier
|
||||
|
||||
Usage:
|
||||
python get_poly.py [code_insee]
|
||||
|
||||
Si le code INSEE n'est pas fourni en argument, le script le demandera interactivement.
|
||||
"""
|
||||
|
||||
import sys
|
||||
import os
|
||||
import json
|
||||
import urllib.parse
|
||||
import urllib.request
|
||||
import argparse
|
||||
|
||||
|
||||
def get_insee_code():
|
||||
"""
|
||||
Récupère le code INSEE soit depuis les arguments de ligne de commande,
|
||||
soit en demandant à l'utilisateur.
|
||||
|
||||
Returns:
|
||||
str: Le code INSEE de la commune
|
||||
"""
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Récupère le polygone d'une commune à partir de son code INSEE"
|
||||
)
|
||||
parser.add_argument("insee", nargs="?", help="Code INSEE de la commune")
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.insee:
|
||||
return args.insee
|
||||
|
||||
# Si le code INSEE n'est pas fourni en argument, le demander
|
||||
return input("Entrez le code INSEE de la commune: ")
|
||||
|
||||
|
||||
def query_overpass_api(insee_code):
|
||||
"""
|
||||
Interroge l'API Overpass pour obtenir les limites administratives d'une commune.
|
||||
|
||||
Args:
|
||||
insee_code (str): Le code INSEE de la commune
|
||||
|
||||
Returns:
|
||||
dict: Les données GeoJSON de la commune
|
||||
"""
|
||||
print(f"Récupération des limites administratives pour la commune {insee_code}...")
|
||||
|
||||
# Construire la requête Overpass QL pour obtenir la relation administrative
|
||||
query = f"""
|
||||
[out:json][timeout:60];
|
||||
(
|
||||
relation["boundary"="administrative"]["admin_level"="8"]["ref:INSEE"="{insee_code}"];
|
||||
way(r);
|
||||
node(w);
|
||||
);
|
||||
out geom;
|
||||
"""
|
||||
|
||||
# Encoder la requête pour l'URL
|
||||
encoded_query = urllib.parse.quote(query)
|
||||
|
||||
# Construire l'URL de l'API Overpass
|
||||
url = f"https://overpass-api.de/api/interpreter?data={encoded_query}"
|
||||
|
||||
try:
|
||||
# Envoyer la requête à l'API
|
||||
print("Envoi de la requête à Overpass API...")
|
||||
with urllib.request.urlopen(url) as response:
|
||||
data = json.loads(response.read().decode("utf-8"))
|
||||
|
||||
# Afficher des informations sur la réponse (version réduite pour production)
|
||||
print(
|
||||
f"Réponse reçue de l'API Overpass. Nombre d'éléments: {len(data.get('elements', []))}"
|
||||
)
|
||||
|
||||
return data
|
||||
except Exception as e:
|
||||
print(f"Erreur lors de la requête à l'API Overpass: {e}")
|
||||
raise RuntimeError(f"Erreur lors de la requête à l'API Overpass: {e}")
|
||||
|
||||
|
||||
def extract_polygon(data):
|
||||
"""
|
||||
Extrait le polygone des données GeoJSON.
|
||||
|
||||
Args:
|
||||
data (dict): Les données GeoJSON de la commune
|
||||
|
||||
Returns:
|
||||
list: Liste des coordonnées du polygone
|
||||
"""
|
||||
print("Extraction du polygone des données...")
|
||||
|
||||
# Vérifier si des éléments ont été trouvés
|
||||
if not data.get("elements"):
|
||||
print("Aucune limite administrative trouvée pour ce code INSEE.")
|
||||
raise ValueError("Aucune limite administrative trouvée pour ce code INSEE.")
|
||||
|
||||
try:
|
||||
# Collecter tous les nœuds (points) avec leurs coordonnées
|
||||
nodes = {}
|
||||
for element in data["elements"]:
|
||||
if element["type"] == "node":
|
||||
nodes[element["id"]] = (element["lon"], element["lat"])
|
||||
|
||||
# Trouver les ways qui forment le contour de la commune
|
||||
ways = []
|
||||
for element in data["elements"]:
|
||||
if element["type"] == "way":
|
||||
ways.append(element)
|
||||
|
||||
# Si aucun way n'est trouvé, essayer d'extraire directement les coordonnées des nœuds
|
||||
if not ways and nodes:
|
||||
print("Aucun way trouvé. Utilisation directe des nœuds...")
|
||||
polygon = list(nodes.values())
|
||||
return polygon
|
||||
|
||||
# Trouver la relation administrative
|
||||
relation = None
|
||||
for element in data["elements"]:
|
||||
if (
|
||||
element["type"] == "relation"
|
||||
and element.get("tags", {}).get("boundary") == "administrative"
|
||||
):
|
||||
relation = element
|
||||
break
|
||||
|
||||
if not relation:
|
||||
print("Aucune relation administrative trouvée.")
|
||||
# Si nous avons des ways, nous pouvons essayer de les utiliser directement
|
||||
if ways:
|
||||
print("Tentative d'utilisation directe des ways...")
|
||||
# Prendre le premier way comme contour
|
||||
way = ways[0]
|
||||
polygon = []
|
||||
for node_id in way.get("nodes", []):
|
||||
if node_id in nodes:
|
||||
polygon.append(nodes[node_id])
|
||||
return polygon
|
||||
raise ValueError(
|
||||
"Impossible de trouver une relation administrative ou des ways"
|
||||
)
|
||||
|
||||
# Extraire les ways qui forment le contour extérieur de la relation
|
||||
outer_ways = []
|
||||
for member in relation.get("members", []):
|
||||
if member.get("role") == "outer" and member.get("type") == "way":
|
||||
# Trouver le way correspondant
|
||||
for way in ways:
|
||||
if way["id"] == member["ref"]:
|
||||
outer_ways.append(way)
|
||||
break
|
||||
|
||||
# Si aucun way extérieur n'est trouvé, utiliser tous les ways
|
||||
if not outer_ways:
|
||||
print("Aucun way extérieur trouvé. Utilisation de tous les ways...")
|
||||
outer_ways = ways
|
||||
|
||||
# Construire le polygone à partir des ways extérieurs
|
||||
polygon = []
|
||||
for way in outer_ways:
|
||||
for node_id in way.get("nodes", []):
|
||||
if node_id in nodes:
|
||||
polygon.append(nodes[node_id])
|
||||
|
||||
if not polygon:
|
||||
raise ValueError("Impossible d'extraire le polygone de la relation")
|
||||
|
||||
print(f"Polygone extrait avec {len(polygon)} points.")
|
||||
return polygon
|
||||
except Exception as e:
|
||||
print(f"Erreur lors de l'extraction du polygone: {e}")
|
||||
raise RuntimeError(f"Erreur lors de l'extraction du polygone: {e}")
|
||||
|
||||
|
||||
def save_polygon_to_file(polygon, insee_code):
|
||||
"""
|
||||
Sauvegarde le polygone dans un fichier.
|
||||
|
||||
Args:
|
||||
polygon (list): Liste des coordonnées du polygone
|
||||
insee_code (str): Le code INSEE de la commune
|
||||
|
||||
Returns:
|
||||
str: Le chemin du fichier créé
|
||||
|
||||
Raises:
|
||||
ValueError: Si le polygone est vide ou invalide
|
||||
IOError: Si une erreur survient lors de l'écriture du fichier
|
||||
"""
|
||||
if not polygon:
|
||||
raise ValueError("Le polygone est vide")
|
||||
|
||||
try:
|
||||
# Créer le répertoire de sortie s'il n'existe pas
|
||||
output_dir = os.path.join(
|
||||
os.path.dirname(os.path.abspath(__file__)), "polygons"
|
||||
)
|
||||
os.makedirs(output_dir, exist_ok=True)
|
||||
|
||||
# Définir le nom du fichier de sortie
|
||||
output_file = os.path.join(output_dir, f"commune_{insee_code}.poly")
|
||||
|
||||
print(f"Sauvegarde du polygone dans le fichier {output_file}...")
|
||||
|
||||
# Écrire le polygone dans le fichier au format .poly (format utilisé par Osmosis)
|
||||
with open(output_file, "w") as f:
|
||||
f.write(f"commune_{insee_code}\n")
|
||||
f.write("1\n") # Numéro de section
|
||||
|
||||
# Écrire les coordonnées
|
||||
for i, (lon, lat) in enumerate(polygon):
|
||||
f.write(f" {lon:.7f} {lat:.7f}\n")
|
||||
|
||||
# Fermer le polygone en répétant le premier point
|
||||
if len(polygon) > 1 and polygon[0] != polygon[-1]:
|
||||
lon, lat = polygon[0]
|
||||
f.write(f" {lon:.7f} {lat:.7f}\n")
|
||||
|
||||
f.write("END\n")
|
||||
f.write("END\n")
|
||||
|
||||
print(f"Polygone sauvegardé avec succès dans {output_file}")
|
||||
return output_file
|
||||
except IOError as e:
|
||||
print(f"Erreur lors de l'écriture du fichier: {e}")
|
||||
raise # Re-raise the IOError
|
||||
except Exception as e:
|
||||
print(f"Erreur inattendue lors de la sauvegarde du polygone: {e}")
|
||||
raise RuntimeError(f"Erreur inattendue lors de la sauvegarde du polygone: {e}")
|
||||
|
||||
|
||||
def main():
|
||||
"""
|
||||
Fonction principale du script.
|
||||
"""
|
||||
try:
|
||||
# Récupérer le code INSEE
|
||||
insee_code = get_insee_code()
|
||||
|
||||
# Vérifier que le code INSEE est valide (format numérique ou alphanumérique pour les DOM-TOM)
|
||||
if not insee_code:
|
||||
raise ValueError("Le code INSEE ne peut pas être vide")
|
||||
|
||||
if not insee_code.isalnum() or len(insee_code) not in [5, 3]:
|
||||
raise ValueError(
|
||||
"Code INSEE invalide. Il doit être composé de 5 chiffres (ou 3 pour certains territoires)."
|
||||
)
|
||||
|
||||
# Interroger l'API Overpass
|
||||
data = query_overpass_api(insee_code)
|
||||
|
||||
# Extraire le polygone
|
||||
polygon = extract_polygon(data)
|
||||
|
||||
# Sauvegarder le polygone dans un fichier
|
||||
output_file = save_polygon_to_file(polygon, insee_code)
|
||||
|
||||
print(
|
||||
f"Terminé. Le polygone de la commune {insee_code} a été sauvegardé dans {output_file}"
|
||||
)
|
||||
return 0 # Succès
|
||||
except ValueError as e:
|
||||
print(f"Erreur de validation: {e}")
|
||||
return 1 # Erreur
|
||||
except KeyboardInterrupt:
|
||||
print("\nOpération annulée par l'utilisateur.")
|
||||
return 1 # Erreur
|
||||
except Exception as e:
|
||||
print(f"Erreur inattendue: {e}")
|
||||
return 1 # Erreur
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
Loading…
Add table
Add a link
Reference in a new issue