From a695a8ef03226bca25b42ae4f2830cafdb705c5f Mon Sep 17 00:00:00 2001 From: Tykayn Date: Mon, 28 Jul 2025 16:37:50 +0200 Subject: [PATCH] up --- counting_osm_objects/.gitignore | 4 +- counting_osm_objects/README_update.md | 71 +++++++ counting_osm_objects/generate_graph.py | 65 +++++- counting_osm_objects/historize_zone.py | 2 +- counting_osm_objects/latest.sh | 9 + ...oop_thematics_history_in_zone_to_counts.py | 13 +- .../osm-commerces-villes-export.csv | 3 +- counting_osm_objects/up.sh | 4 + counting_osm_objects/update.py | 194 ++++++++++++++++++ 9 files changed, 353 insertions(+), 12 deletions(-) create mode 100644 counting_osm_objects/README_update.md create mode 100644 counting_osm_objects/latest.sh create mode 100644 counting_osm_objects/up.sh create mode 100755 counting_osm_objects/update.py diff --git a/counting_osm_objects/.gitignore b/counting_osm_objects/.gitignore index 7c30004..0a0fae4 100644 --- a/counting_osm_objects/.gitignore +++ b/counting_osm_objects/.gitignore @@ -5,4 +5,6 @@ test_data/* test_temp/* test_results/* osm_config.txt -__pycache__ \ No newline at end of file +__pycache__ +secrets.sh +cookie.txt \ No newline at end of file diff --git a/counting_osm_objects/README_update.md b/counting_osm_objects/README_update.md new file mode 100644 index 0000000..0680df2 --- /dev/null +++ b/counting_osm_objects/README_update.md @@ -0,0 +1,71 @@ +# Mise à jour du fichier historisé france internal + +Ce document explique comment utiliser le script `update.py` pour mettre à jour le fichier historisé france internal (`france-internal.osh.pbf`) en utilisant l'outil `osmupdate`. + +## Prérequis + +- Python 3.6 ou supérieur +- L'outil `osmupdate` installé sur le système +- Un fichier `france-internal.osh.pbf` existant dans le répertoire `osm_data` + +## Utilisation + +Le script `update.py` peut être exécuté de deux façons : + +```bash +# Méthode 1 : Exécution directe (le script est exécutable) +./update.py + +# Méthode 2 : Exécution via Python +python3 update.py +``` + +### Options + +- `--verbose` ou `-v` : Affiche la sortie des commandes en temps réel, ce qui permet de suivre la progression de la mise à jour. + +Exemple : +```bash +./update.py --verbose +``` + +## Fonctionnement + +Le script effectue les opérations suivantes : + +1. Vérifie si le fichier `france-internal.osh.pbf` existe dans le répertoire `osm_data`. +2. Crée un répertoire temporaire `update_temp` s'il n'existe pas déjà. +3. Exécute la commande `osmupdate` pour mettre à jour le fichier avec les dernières modifications d'OpenStreetMap. +4. Crée une sauvegarde de l'ancien fichier avec l'extension `.bak`. +5. Remplace l'ancien fichier par le nouveau fichier mis à jour. +6. Affiche des informations sur la durée de la mise à jour. + +## Logs + +Le script utilise le module `logging` de Python pour enregistrer les informations importantes : + +- Heure de début et de fin de la mise à jour +- Durée totale de la mise à jour +- Erreurs éventuelles lors de la mise à jour + +## Intégration avec d'autres scripts + +Le fichier `france-internal.osh.pbf` est utilisé comme fichier d'entrée par défaut dans le script `historize_zone.py`. Après avoir mis à jour ce fichier avec `update.py`, les analyses historiques effectuées par `historize_zone.py` utiliseront automatiquement les données les plus récentes. + +## Automatisation + +Pour automatiser la mise à jour régulière du fichier, vous pouvez ajouter une tâche cron : + +```bash +# Exemple : mise à jour quotidienne à 3h du matin +0 3 * * * cd /chemin/vers/osm-commerce-sf/counting_osm_objects && ./update.py >> update.log 2>&1 +``` + +## Dépannage + +Si vous rencontrez des erreurs lors de l'exécution du script : + +1. Vérifiez que `osmupdate` est correctement installé et accessible dans le PATH. +2. Assurez-vous que le fichier `france-internal.osh.pbf` existe dans le répertoire `osm_data`. +3. Vérifiez les permissions des répertoires `osm_data` et `update_temp`. +4. Consultez les logs pour plus d'informations sur l'erreur. \ No newline at end of file diff --git a/counting_osm_objects/generate_graph.py b/counting_osm_objects/generate_graph.py index 6e85b38..122e964 100755 --- a/counting_osm_objects/generate_graph.py +++ b/counting_osm_objects/generate_graph.py @@ -13,6 +13,31 @@ import matplotlib.pyplot as plt import matplotlib.dates as mdates from datetime import datetime import argparse +import csv + + +def get_city_name(insee_code): + """ + Récupère le nom de la ville à partir du code INSEE. + + Args: + insee_code: Code INSEE de la commune + + Returns: + Nom de la ville ou None si non trouvé + """ + try: + csv_path = os.path.join(os.path.dirname(__file__), "osm-commerces-villes-export.csv") + if os.path.exists(csv_path): + with open(csv_path, 'r') as f: + reader = csv.DictReader(f) + for row in reader: + if row.get('zone') == insee_code: + return row.get('name') + return None + except Exception as e: + print(f"Erreur lors de la récupération du nom de la ville: {e}") + return None def parse_args(): @@ -217,15 +242,26 @@ def generate_graph(df, output_path=None): plt.legend() zone_label = "" + city_name = None # Ajouter des informations sur la commune if "code_insee" in df.columns and len(df["code_insee"].unique()) == 1: insee_code = df["code_insee"].iloc[0] - plt.figtext(0.02, 0.02, f"Commune: {insee_code}", fontsize=10) - zone_label = insee_code + city_name = get_city_name(insee_code) + if city_name: + plt.figtext(0.02, 0.02, f"Commune: {insee_code} - {city_name}", fontsize=10) + zone_label = f"{insee_code} - {city_name}" + else: + plt.figtext(0.02, 0.02, f"Commune: {insee_code}", fontsize=10) + zone_label = insee_code elif "zone" in df.columns and len(df["zone"].unique()) == 1: zone = df["zone"].iloc[0] - plt.figtext(0.02, 0.02, f"Zone: {zone}", fontsize=10) - zone_label = zone + city_name = get_city_name(zone) + if city_name: + plt.figtext(0.02, 0.02, f"Zone: {zone} - {city_name}", fontsize=10) + zone_label = f"{zone} - {city_name}" + else: + plt.figtext(0.02, 0.02, f"Zone: {zone}", fontsize=10) + zone_label = zone plt.title(f"{zone_label} : {label_objet} dans le temps") @@ -333,12 +369,29 @@ def generate_completion_graph(df, output_path=None): plt.legend() # Ajouter des informations sur la commune + zone_label = "" if "code_insee" in df.columns and len(df["code_insee"].unique()) == 1: insee_code = df["code_insee"].iloc[0] - plt.figtext(0.02, 0.02, f"Commune: {insee_code}", fontsize=10) + city_name = get_city_name(insee_code) + if city_name: + plt.figtext(0.02, 0.02, f"Commune: {insee_code} - {city_name}", fontsize=10) + zone_label = f"{insee_code} - {city_name}" + else: + plt.figtext(0.02, 0.02, f"Commune: {insee_code}", fontsize=10) + zone_label = insee_code elif "zone" in df.columns and len(df["zone"].unique()) == 1: zone = df["zone"].iloc[0] - plt.figtext(0.02, 0.02, f"Zone: {zone}", fontsize=10) + city_name = get_city_name(zone) + if city_name: + plt.figtext(0.02, 0.02, f"Zone: {zone} - {city_name}", fontsize=10) + zone_label = f"{zone} - {city_name}" + else: + plt.figtext(0.02, 0.02, f"Zone: {zone}", fontsize=10) + zone_label = zone + + # Mettre à jour le titre avec le nom de la zone + if zone_label: + plt.title(f"{zone_label} : Évolution du taux de complétion dans le temps") # Ajouter la date de génération now = datetime.now().strftime("%Y-%m-%d %H:%M") diff --git a/counting_osm_objects/historize_zone.py b/counting_osm_objects/historize_zone.py index ae07dd5..3afb9f3 100755 --- a/counting_osm_objects/historize_zone.py +++ b/counting_osm_objects/historize_zone.py @@ -186,7 +186,7 @@ def process_city_history(input_file, poly_file, cleanup=False, benchmark=False): os.makedirs(temp_dir, exist_ok=True) # Construire la commande avec les options supplémentaires - command = f"python3 {loop_script} --input {input_file} --poly {poly_file} --output-dir {output_dir} --temp-dir {temp_dir}" + command = f"python3 {loop_script} --input {input_file} --poly {poly_file} --output-dir {output_dir} --temp-dir {temp_dir} --max-dates 100" # Ajouter les options de nettoyage et de benchmark si activées if cleanup: diff --git a/counting_osm_objects/latest.sh b/counting_osm_objects/latest.sh new file mode 100644 index 0000000..547659f --- /dev/null +++ b/counting_osm_objects/latest.sh @@ -0,0 +1,9 @@ +# get latest france with cli +source ./secrets.sh +python3 /home/poule/encrypted/stockage-syncable/www/development/html/sendfile_osm_oauth_protector/oauth_cookie_client.py \ + -u "$OSM_USER" -p "$OSM_PASS" \ + -c https://osm-internal.download.geofabrik.de/get_cookie \ + -o cookie.txt + + +wget -N --no-cookies --header "Cookie: $(cat cookie.txt | cut -d ';' -f 1)" https://osm-internal.download.geofabrik.de/europe/france-latest-internal.osm.pbf -O osm_data/france-internal.osh.pbf \ No newline at end of file diff --git a/counting_osm_objects/loop_thematics_history_in_zone_to_counts.py b/counting_osm_objects/loop_thematics_history_in_zone_to_counts.py index d68ce62..1e85e6d 100644 --- a/counting_osm_objects/loop_thematics_history_in_zone_to_counts.py +++ b/counting_osm_objects/loop_thematics_history_in_zone_to_counts.py @@ -426,13 +426,20 @@ def generate_time_slices(max_dates=None): current_date = datetime(current_date.year, current_date.month - 1, 1) # 3. Dates annuelles de l'année précédente jusqu'à 2004 + # Amélioration: deux mesures par an (1er janvier et 1er juillet) pour les périodes éloignées start_year = min( now.year - 1, 2023 ) # Commencer à l'année précédente (ou 2023 si nous sommes en 2024) for year in range(start_year, 2003, -1): - date_str = f"{year}-01-01T00:00:00Z" - if date_str not in dates: # Éviter les doublons - dates.append(date_str) + # Ajouter le 1er janvier + date_str_jan = f"{year}-01-01T00:00:00Z" + if date_str_jan not in dates: # Éviter les doublons + dates.append(date_str_jan) + + # Ajouter le 1er juillet + date_str_jul = f"{year}-07-01T00:00:00Z" + if date_str_jul not in dates: # Éviter les doublons + dates.append(date_str_jul) # Limiter le nombre de dates si spécifié if max_dates is not None and max_dates > 0: diff --git a/counting_osm_objects/osm-commerces-villes-export.csv b/counting_osm_objects/osm-commerces-villes-export.csv index 0e1316e..6d82720 100644 --- a/counting_osm_objects/osm-commerces-villes-export.csv +++ b/counting_osm_objects/osm-commerces-villes-export.csv @@ -44,7 +44,6 @@ zone,name,lat,lon,population,budgetAnnuel,completionPercent,placesCount,avecHora 79298,Saint-Symphorien,46.2688000,-0.4842000,1986,2909490.00,,,,,,,,,, 51454,Reims,49.2535000,4.0551000,178478,310908676.00,,,,,,,,,, 64102,Bayonne,43.4844000,-1.4611000,53312,82740224.00,,,,,,,,,, -55153,Dieppe-sous-Douaumont,49.2217000,5.5201000,193,302817.00,,,,,,,,,, 39300,Lons-le-Saunier,46.6758000,5.5574000,16942,25379116.00,,,,,,,,,, 85288,Talmont-Saint-Hilaire,46.4775000,-1.6299000,8327,10908370.00,,,,,,,,,, 13203,,43.3113000,5.3806000,55653,81642951.00,,,,,,,,,, @@ -191,3 +190,5 @@ zone,name,lat,lon,population,budgetAnnuel,completionPercent,placesCount,avecHora 66136,Perpignan,42.6990000,2.9045000,120996,193593600.00,,,,,,,,,, 11041,Bize-Minervois,43.3364000,2.8719000,1292,1758412.00,,,,,,,,,, 75016,,,,,,,,,,,,,,, +76540,Rouen, + diff --git a/counting_osm_objects/up.sh b/counting_osm_objects/up.sh new file mode 100644 index 0000000..c5e05a9 --- /dev/null +++ b/counting_osm_objects/up.sh @@ -0,0 +1,4 @@ + +osmupdate --verbose --keep-tempfiles --day -t=test_temp/ -v osm_data/france-internal.osh.pbf test_temp/changes.osc.gz +#osmium extract -p polyons/commune_91111.poly -s simple changes.osc.gz -O -o test_temp/changes.local.osc.gz +osmium apply-changes -H osm_data/france-internal.osh.pbf test_temp/changes.osc.gz -O -o osm_data/france-internal_updated.osh.pbf diff --git a/counting_osm_objects/update.py b/counting_osm_objects/update.py new file mode 100755 index 0000000..bb34e52 --- /dev/null +++ b/counting_osm_objects/update.py @@ -0,0 +1,194 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +""" +Script pour mettre à jour le fichier historisé france internal. + +Ce script utilise osmupdate pour mettre à jour le fichier france-internal.osh.pbf +avec les dernières modifications d'OpenStreetMap. + +Usage: + python update.py [--verbose] +""" + +import os +import sys +import argparse +import subprocess +import logging +from datetime import datetime + +# Chemin vers le répertoire du script +SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__)) + +# Chemin vers le fichier historisé france internal +FRANCE_INTERNAL_FILE = os.path.join(SCRIPT_DIR, "osm_data", "france-internal.osh.pbf") + +# Chemin vers le répertoire temporaire pour osmupdate +TEMP_DIR = os.path.join(SCRIPT_DIR, "update_temp") + +# Configurer le logging +logging.basicConfig( + level=logging.INFO, + format="%(asctime)s - %(levelname)s - %(message)s", + datefmt="%Y-%m-%d %H:%M:%S", +) +logger = logging.getLogger(__name__) + + +def run_command(command, verbose=False): + """ + Exécute une commande shell et retourne la sortie. + + Args: + command (str): Commande à exécuter + verbose (bool): Si True, affiche la sortie de la commande en temps réel + + Returns: + tuple: (code de retour, sortie standard, sortie d'erreur) + """ + logger.info(f"Exécution: {command}") + + if verbose: + # Exécuter la commande avec sortie en temps réel + process = subprocess.Popen( + command, + shell=True, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + universal_newlines=True, + bufsize=1 + ) + + stdout_lines = [] + stderr_lines = [] + + # Lire la sortie standard en temps réel + for line in process.stdout: + line = line.strip() + stdout_lines.append(line) + print(line) + + # Lire la sortie d'erreur en temps réel + for line in process.stderr: + line = line.strip() + stderr_lines.append(line) + print(f"ERREUR: {line}", file=sys.stderr) + + # Attendre la fin du processus + return_code = process.wait() + return return_code, "\n".join(stdout_lines), "\n".join(stderr_lines) + else: + # Exécuter la commande sans sortie en temps réel + try: + result = subprocess.run( + command, + shell=True, + check=False, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + universal_newlines=True, + ) + return result.returncode, result.stdout, result.stderr + except Exception as e: + logger.error(f"Erreur lors de l'exécution de la commande: {e}") + return 1, "", str(e) + + +def update_france_internal(verbose=False): + """ + Met à jour le fichier historisé france internal avec osmupdate. + + Args: + verbose (bool): Si True, affiche la sortie de la commande en temps réel + + Returns: + bool: True si la mise à jour a réussi, False sinon + """ + # Vérifier si le fichier existe + if not os.path.isfile(FRANCE_INTERNAL_FILE): + logger.error(f"Le fichier {FRANCE_INTERNAL_FILE} n'existe pas.") + logger.error("Veuillez télécharger le fichier initial depuis Geofabrik ou une autre source.") + return False + + # Créer le répertoire temporaire s'il n'existe pas + os.makedirs(TEMP_DIR, exist_ok=True) + + # Chemin vers le fichier mis à jour + updated_file = os.path.join(TEMP_DIR, "france-internal-updated.osh.pbf") + + # Construire la commande osmupdate + command = f"osmupdate --verbose --keep-tempfiles -t={TEMP_DIR}/temp {FRANCE_INTERNAL_FILE} {updated_file}" + + # Exécuter la commande + logger.info("Mise à jour du fichier france-internal.osh.pbf en cours...") + return_code, stdout, stderr = run_command(command, verbose) + + if return_code != 0: + logger.error(f"Erreur lors de la mise à jour: {stderr}") + return False + + # Remplacer l'ancien fichier par le nouveau + if os.path.isfile(updated_file): + # Créer une sauvegarde de l'ancien fichier + backup_file = f"{FRANCE_INTERNAL_FILE}.bak" + try: + os.rename(FRANCE_INTERNAL_FILE, backup_file) + logger.info(f"Sauvegarde de l'ancien fichier créée: {backup_file}") + except Exception as e: + logger.error(f"Erreur lors de la création de la sauvegarde: {e}") + return False + + # Déplacer le nouveau fichier + try: + os.rename(updated_file, FRANCE_INTERNAL_FILE) + logger.info(f"Fichier mis à jour avec succès: {FRANCE_INTERNAL_FILE}") + return True + except Exception as e: + logger.error(f"Erreur lors du déplacement du fichier mis à jour: {e}") + # Restaurer l'ancien fichier en cas d'erreur + try: + os.rename(backup_file, FRANCE_INTERNAL_FILE) + logger.info("Restauration de l'ancien fichier réussie.") + except Exception as e2: + logger.error(f"Erreur lors de la restauration de l'ancien fichier: {e2}") + return False + else: + logger.error(f"Le fichier mis à jour {updated_file} n'a pas été créé.") + return False + + +def main(): + """Fonction principale""" + parser = argparse.ArgumentParser( + description="Met à jour le fichier historisé france internal avec osmupdate." + ) + parser.add_argument( + "--verbose", "-v", action="store_true", help="Affiche la sortie des commandes en temps réel" + ) + + args = parser.parse_args() + + # Afficher l'heure de début + start_time = datetime.now() + logger.info(f"Début de la mise à jour: {start_time.strftime('%Y-%m-%d %H:%M:%S')}") + + # Mettre à jour le fichier + success = update_france_internal(args.verbose) + + # Afficher l'heure de fin et la durée + end_time = datetime.now() + duration = end_time - start_time + logger.info(f"Fin de la mise à jour: {end_time.strftime('%Y-%m-%d %H:%M:%S')}") + logger.info(f"Durée totale: {duration}") + + if success: + logger.info("Mise à jour terminée avec succès.") + return 0 + else: + logger.error("Échec de la mise à jour.") + return 1 + + +if __name__ == "__main__": + sys.exit(main()) \ No newline at end of file