mirror of
https://forge.chapril.org/tykayn/parking-land
synced 2025-06-20 01:44:42 +02:00
389 lines
No EOL
17 KiB
Python
389 lines
No EOL
17 KiB
Python
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() |