parking-land/compute.py

389 lines
17 KiB
Python
Raw Permalink Normal View History

2025-03-16 12:20:05 +01:00
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)")
2025-03-16 16:26:58 +01:00
parser.add_argument("--osm-id", type=str, default="3601176343", help="Identifiant OpenStreetMap de la ville (défaut: 3601176343 pour Limours)")
2025-03-16 12:20:05 +01:00
args = parser.parse_args()
CITY_NAME = args.city
# limours: 3601176343
# briis-sous-forges: 3601209165
city_overpass_id = args.osm_id
2025-03-16 12:20:05 +01:00
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"])
2025-03-16 16:26:58 +01:00
elif "tags" in element and "man_made" in element["tags"] and element["tags"]["man_made"] == "charge_point":
2025-03-16 12:20:05 +01:00
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()