import os import requests import json import math from shapely.geometry import Polygon import argparse import matplotlib.pyplot as plt # Configuration OVERPASS_API = "https://overpass-api.de/api/interpreter" # Configuration des arguments de ligne de commande parser = argparse.ArgumentParser(description="Calculer les parkings et la surface d'une ville.") parser.add_argument("--city", type=str, default="Limours", help="Nom de la ville (défaut: Limours)") parser.add_argument("--osm-id", type=str, default="3601176343", help="Identifiant OpenStreetMap de la ville (défaut: 3601176343 pour Limours)") args = parser.parse_args() CITY_NAME = args.city # limours: 3601176343 # briis-sous-forges: 3601209165 city_overpass_id = args.osm_id COUNTRY_NAME = "France" # estimations des tailles de parking en m carré selon le placement PARKING_SIZE_ESTIMATE = { "bike":2, "surface": 20, "multilevel": 25, "underground": 30, "pmr": 40 } DEGREES_TO_METERS = 111312 def estimate_road_length_and_surface(city_name): # Vérifier si le fichier JSON existe if not os.path.exists('overpass_data.json'): print("Le fichier overpass_data.json n'existe pas. Veuillez d'abord exécuter fetch.py.") return None # Charger les données depuis le fichier JSON with open('overpass_data.json', 'r') as file: data = json.load(file) parking_area = 0 bicycle_parking_area = 0 bicycle_parking_capacity_provided = 0 car_parking_capacity_provided = 0 charging_stations = 0 charging_points = 0 parking_with_capacity_count = 0 # Compteur pour les parkings avec capacité renseignée road_length = 0 roundabout_count = 0 mini_roundabout_count = 0 road_cycleway_length = 0 road_surface = 0 cycleway_count = 0 building_count = 0 building_area = 0 building_sizes = [0, 10, 50, 100, 200, 500, float('inf')] building_size_counts = [0, 0, 0, 0, 0, 0, 0] charging_stations_with_capacity_count = 0 charging_stations_capacity_provided = 0 for element in data["elements"]: if "tags" in element and "building" in element["tags"]: if "geometry" in element: polygon = Polygon([(point['lon'], point['lat']) for point in element["geometry"]]) area = polygon.area for i, size in enumerate(building_sizes): if area < size: building_size_counts[i] += 1 break print("Bâtiments de moins de 10 m²:", building_size_counts[0]) print("Bâtiments de 10 à 50 m²:", building_size_counts[1]) print("Bâtiments de 50 à 100 m²:", building_size_counts[2]) print("Bâtiments de 100 à 200 m²:", building_size_counts[3]) print("Bâtiments de 200 à 500 m²:", building_size_counts[4]) print("Bâtiments de plus de 500 m²:", building_size_counts[5]) print('éléments:', len(data["elements"])) for element in data["elements"]: two_points_distance = 0 element_road_length = 0 if "geometry" in element: geometry = element["geometry"] coordinates = geometry if len(coordinates) > 1: for i in range(len(coordinates) - 1): # print(coordinates) lon1 = coordinates[i]['lon'] lat1 = coordinates[i]['lat'] lon2 = coordinates[i + 1]['lon'] lat2 = coordinates[i + 1]['lat'] # print(lon1, lat1, lon2, lat2) # Convertir les degrés de lat/lon en mètres lon1_m, lat1_m = int(lon1 * DEGREES_TO_METERS), int(lat1 * DEGREES_TO_METERS) lon2_m, lat2_m = int(lon2 * DEGREES_TO_METERS), int(lat2 * DEGREES_TO_METERS) two_points_distance = math.sqrt((lon2_m - lon1_m)**2 + (lat2_m - lat1_m)**2) # print(lon1_m, lat1_m, lon2_m, lat2_m) # print('two_points_distance ', two_points_distance) # break element_road_length += two_points_distance # Estimation de la surface des bâtiments if "tags" in element and "building" in element["tags"]: building_count += 1 if "geometry" in element: polygon = Polygon([(point['lon'], point['lat']) for point in element["geometry"]]) building_area += polygon.area if "tags" in element and "highway" in element["tags"] and element["tags"]["highway"] == "cycleway": cycleway_count += 1 road_cycleway_length += two_points_distance if "tags" in element and "highway" in element["tags"]: if "lanes" in element["tags"]: lane_width = 3.5 # Assuming 3.5m width per lane road_width = int(element["tags"]["lanes"]) * lane_width * 2 # Double voie else: road_width = 7 # Default width for single lane roads if element["tags"]["highway"] in ["motorway", "trunk", "primary", "secondary", "tertiary", "unclassified", "residential", "service"] and "length" in element: road_length += element["length"] road_surface += element["length"] * road_width elif element["tags"]["highway"] == "roundabout": roundabout_count += 1 road_length += math.pi * element["tags"]["circumference"] road_surface += math.pi * element["tags"]["circumference"] * road_width elif element["tags"]["highway"] == "mini_roundabout": mini_roundabout_count += 1 if "tags" in element and "amenity" in element["tags"]: if element["tags"]["amenity"] == "parking": if "capacity" in element["tags"]: parking_with_capacity_count += 1 # print('car parking capacity:', element["tags"]["capacity"]) car_parking_capacity_provided += int(element["tags"]["capacity"]) # Estimer la surface du parking à partir de la capacité parking_area += PARKING_SIZE_ESTIMATE["surface"] * int(element["tags"]["capacity"]) elif "geometry" in element: # Calculer la surface selon la forme du polygone polygon = Polygon([(point['lon'], point['lat']) for point in element["geometry"]]) parking_area += polygon.area / 1_000_000 # Conversion en km² elif element["tags"]["amenity"] == "bicycle_parking": if "capacity" in element["tags"]: parking_with_capacity_count += 1 bicycle_parking_capacity_provided += int(element["tags"]["capacity"]) bicycle_parking_area += PARKING_SIZE_ESTIMATE["bike"] # stations de recharge elif element["tags"]["amenity"] == "charging_station": charging_stations += 1 if "capacity" in element["tags"]: charging_stations_with_capacity_count += 1 charging_stations_capacity_provided += int(element["tags"]["capacity"]) elif "tags" in element and "man_made" in element["tags"] and element["tags"]["man_made"] == "charge_point": charging_points += 1 # points de charge road_length += element_road_length road_length_km = road_length / 1000 road_surface_km2 = 3.5*2*road_length / 1000000 # Conversion de mètres carrés en kilomètres carrés # road_surface = 3.5*2*road_length / 1000 # compter la surface des buildings # comparer la surface des buildings par rapport à la surface totale de la ville # ----------------- # estimer la surface prise par les parkings voiture, compter selon les capacity renseignées, et selon les surfaces des polygones geojson # compter les places de parking par habitant # ----------------- # infos vélo: # estimer les km de piste cyclable # estimer la surface prise par les parkings vélo # compter les places de parking vélo, amenity=bicycle_parking # compter les parking vélo par habitant return { "longueur_route_km": road_length_km, "road_cycleway_km": road_cycleway_length/1000, "compte_highways": len(data["elements"]), "surface_route_km2": road_surface_km2, "building_count" : building_count, "building_area" : building_area/1000, "building_sizes" : building_sizes, "building_size_counts" : building_size_counts, "compte_piste_cyclable": cycleway_count, "car_parking_capacity_provided" : car_parking_capacity_provided, "roundabout_count":roundabout_count, "mini_roundabout_count":mini_roundabout_count, "surface_parking_km2": parking_area, # Surface totale des parkings en km² "surface_bicycle_parking_km2": bicycle_parking_area / 1_000_000, # Conversion en km² "parking_with_capacity_count": parking_with_capacity_count, # Nombre de parkings avec capacité renseignée "capacity_bicycle_parking_provided": bicycle_parking_capacity_provided, "bicycle_parking_surface_from_capacity_provided_in_m2" : bicycle_parking_capacity_provided * 2, "charging_stations" : charging_stations, "charging_stations_with_capacity_count" : charging_stations_with_capacity_count, "charging_stations_capacity_provided" : charging_stations_capacity_provided, "charging_points" : charging_points, } def get_osm_relation_id(city_name, country_name): # Configuration pour Nominatim NOMINATIM_API = "https://nominatim.openstreetmap.org/search" NOMINATIM_QUERY = f"{city_name}, {country_name}" # Requête HTTP pour trouver l'identifiant OSM de la ville response = requests.get(NOMINATIM_API, params={ "format": "json", "addressdetails": 1, "q": NOMINATIM_QUERY }, headers={"User-Agent": "MonApplication/1.0"}) # Ajout du User-Agent # Vérification de la réponse if response.status_code == 200: data = response.json() print('résultat de recherche nominatim', len(data)) # Trouver l'identifiant OSM de la relation correspondant à la ville for result in data: if "osm_id" in result: return result["osm_id"] else: print(f"Échec de la recherche sur Nominatim. Code d'erreur: {response.status_code}") return None def get_city_boundary(city_name, country_name): # Requête Overpass pour obtenir les limites de la ville overpass_query__city_bounds = f"""[out:json][timeout:25];relation["name"="{city_name}"]["admin_level"="8"];(._;>;);out geom; """ response = requests.post(OVERPASS_API, data={"data": overpass_query__city_bounds}, headers={"X-Requested-With": "overpass-turbo"}) print(response.status_code) if response.status_code == 200: data = response.json() # print("data response: ", data) if data["elements"]: for element in data["elements"]: if "bounds" in element: # print('bounds: ', element['bounds']) return element['bounds'] else: return None else: return None def get_parking_areas(city_bounds): overpass_query__parkings = '[out:json][timeout:25];area(id:3601176343)->.searchArea;nwr["amenity"="parking"](area.searchArea);out center;' print('reqête pour les parkings: ', overpass_query__parkings) response = requests.post(OVERPASS_API, data={"data": overpass_query__parkings}) if response.status_code != 200: print(f"Échec de la requête Overpass. Code d'erreur: {response.status_code}") return [] data = response.json() with open(f'parkings_results_{CITY_NAME}.json', 'w') as file: json.dump(data, file, ensure_ascii=False, indent=4) return data["elements"] def estimate_parking_surface(parking_areas): total_surface = 0 parking_count = 0 # print('parkings: ', len(parking_areas)) for area in parking_areas: if "tags" in area and "amenity" in area["tags"] and area["tags"]["amenity"] == "parking": if "capacity" in area["tags"]: # print('capacité du parking: ', area["tags"]["capacity"]) parking_count += int(area["tags"]["capacity"]) else: parking_count += 1 if "parking" in area["tags"] and area["tags"]["parking"] in PARKING_SIZE_ESTIMATE: total_surface += PARKING_SIZE_ESTIMATE[area["tags"]["parking"]] else: # Utilisation d'une valeur par défaut si le type de parking n'est pas spécifié total_surface += PARKING_SIZE_ESTIMATE["surface"] return total_surface, parking_count def calculate_city_surface(city_bounds): # Conversion des coordonnées en mètres carrés sans bibliothèque lat_diff = city_bounds["maxlat"] - city_bounds["minlat"] lon_diff = city_bounds["maxlon"] - city_bounds["minlon"] # Approximation de la distance en mètres meters_per_latitude = 111_000 # 1 degré de latitude ≈ 111 km meters_per_longitude = 111_000 * math.cos(math.radians((city_bounds["maxlat"] + city_bounds["minlat"]) / 2)) # Ajustement pour la longitude # Calcul de la largeur et de la hauteur en mètres width = lon_diff * meters_per_longitude height = lat_diff * meters_per_latitude # Calcul de la surface en mètres carrés city_surface_m2 = width * height print("city_surface_m2", city_surface_m2) return city_surface_m2 # Retourne l'aire en mètres carrés def get_population(): wiki_api_url = f"https://fr.wikipedia.org/w/api.php?action=parse&page={CITY_NAME}&format=json" response = requests.get(wiki_api_url) data = response.json() nombre_habitants = None for line in data["parse"]["text"]["*"].splitlines(): if "Habitants" in line or "Population" in line: nombre_habitants = int(line.split(":")[1].strip().replace(" ", "")) break return nombre_habitants def draw_city_map(city_name, road_surface_km2, city_surface_km2): # Dimensions de la carte fig, ax = plt.subplots(figsize=(8, 8)) # Calcul de la longueur du côté du carré représentant la surface des routes road_side_length = (road_surface_km2 * 1_000_000) ** 0.5 # Conversion de km² à m² city_side_length = (city_surface_km2) ** 0.5 # La surface de la ville est déjà en m² # Création d'un carré représentant la surface des routes road_square = plt.Rectangle((0, 0), road_side_length, road_side_length, color='blue', alpha=0.5, label='Surface des routes') # Création d'un carré représentant la surface de la ville city_square = plt.Rectangle((road_side_length, 0), city_side_length, city_side_length, color='green', alpha=0.5, label='Surface de la ville') # Ajout des carrés à la carte ax.add_patch(road_square) ax.add_patch(city_square) # Configuration de la carte ax.set_xlim(0, max(road_side_length, city_side_length) * 1.1) # Laisser un peu d'espace ax.set_ylim(0, max(road_side_length, city_side_length) * 1.1) ax.set_title(f"Comparaison de la surface de {city_name} - Routes vs Ville") ax.set_xlabel("Longitude (m)") ax.set_ylabel("Latitude (m)") ax.legend() # Sauvegarde de la carte en JPG plt.savefig(f"{city_name}_city_map_comparison.jpg", format='jpg') # plt.show() def draw_bar_chart(city_name, road_surface_km2, city_surface_km2): # Données pour le diagramme labels = ['Surface des routes (km²)', 'Surface de la ville (km²)'] values = [road_surface_km2, city_surface_km2] # Création du diagramme en barres fig, ax = plt.subplots() ax.bar(labels, values, color=['blue', 'green'], alpha=0.7) # Configuration du diagramme ax.set_title(f"Comparaison des surfaces à {city_name}") ax.set_ylabel("Surface (km²)") ax.set_ylim(0, max(values) * 1.1) # Ajuster l'axe y pour laisser un peu d'espace # Sauvegarde du diagramme en JPG plt.savefig(f"{city_name}_surface_comparison_bar_chart.jpg", format='jpg') # plt.show() def main(): # city_bounds = get_city_boundary(CITY_NAME, COUNTRY_NAME) # city_surface_km2 = calculate_city_surface(city_bounds) / 1_000_000 # Conversion de m² à km² city_surface_km2 = 10.86 resultats = estimate_road_length_and_surface(CITY_NAME) print('résultats:', resultats) # Sauvegarder les résultats dans summary_results.json with open('summary_results.json', 'w') as summary_file: json.dump(resultats, summary_file, ensure_ascii=False, indent=2) road_surface_km2 = resultats["surface_route_km2"] # draw_city_map(CITY_NAME, road_surface_km2, city_surface_km2) draw_bar_chart(CITY_NAME, road_surface_km2, city_surface_km2) # Appel de la fonction pour le diagramme en barres print(f"**Résultats pour {CITY_NAME}, {COUNTRY_NAME}**") if __name__ == "__main__": main()