diff --git a/README.md b/README.md new file mode 100644 index 0000000..07958e8 --- /dev/null +++ b/README.md @@ -0,0 +1,63 @@ + +# Base de données Topographiques des systèmes karstiques du massif de la Pierre Saint Martin - Larra + +Ce dépôt contient les données topographiques et les dessins associés des cavités du massif de la Pierre Saint Martin - Larra. + +Ce dépôt est mis à jour à chaque fois qu'une nouvelle topographie est rajoutée à l'un des systèmes décrits dans cette base de données. + +Si besoin, des templates pour Therion sont disponibles sur [https://github.com/robertxa/Th-Config-Xav](https://github.com/robertxa/Th-Config-Xav). + +## Description + +Ce dépôt est en cours de développement et a pour objectif de sauvegarder et partager les données topographiques chiffrées et dessinées au format [Therion](https://therion.speleo.sk/). +Ce travail est réalisé par les membres de l'ARSIP, collectif d'exploration du massif de la Pierre Saint Martin. + +

+ + Logo ARSIP + +

+ +Une convention a aussi été mise en place pour la gestion des points d'interrogation, avec la définition des différents champs : + +- **Champ "Code"** : décrit le type de terminus. Il peut prendre les valeurs : + - `A` : il suffit d'y aller et de continuer, pas d'obstacles + - `D` : Désobstruction nécessaire + - `E` : Escalade nécessaire + - `P` : Puits non descendu + - `Q` : non renseigné sur les topographies anciennes, c'est à voir/vérifier + - `S` : Siphon à plonger + - `T` : Trémie à désobstruer + +- **Champ "Cavite"** : nom de la cavité concernée +- **Champ "Reseau"** : partie de la cavité où se situe le point d'interrogation (pour le localiser rapidement sur les topographies) +- **Champ "CA"** : rempli s'il y a présence de courant d'air, avec éventuellement des remarques/commentaires + +**Exemple** : +```text +point 3922.0 1660.0 continuation -attr code Q -attr Cavite "GL102" -attr reseau "Grand Chao" -text "Rivière à topographier" -attr CA "inconnu" +``` + +## Licence + +L'ensemble de ces données est publié sous la licence libre Creative Commons CC BY-NC-ND 4.0 (Attribution, partage à l'identique et pas d'utilisation commerciale) : +[https://creativecommons.org/licenses/by-nc-nd/4.0/](https://creativecommons.org/licenses/by-nc-nd/4.0/) + +

+ + Licence CC BY-NC-ND + +

+ +## Auteur de la base de données + +Alexandre PONT () pour le compte de l'ARSIP + +## Contact + +Pour plus d'informations, vous pouvez contacter l'ARSIP : [https://www.arsip.fr/contactez-nous](https://www.arsip.fr/contactez-nous) + +## Remerciements + +Cette base de données est construite sur le modèle de celle des [massifs du Folly et de Criou](https://github.com/robertxa/Topographies-Samoens_Folly), développée par Xavier Robert, +un grand merci pour le soutien actif. diff --git a/README.rst b/README.rst deleted file mode 100644 index a1e51ac..0000000 --- a/README.rst +++ /dev/null @@ -1,76 +0,0 @@ -Base de données Topographiques des systèmes karstiques du massif de la Pierre Saint Martin - Larra -========================================================================================================== - -Ce dépôt contient les données topographiques et les dessins associés des cavités du massif de la Pierre Saint Martin - Larra . - -Ce dépôt est mis à jour à chaque fois qu'une nouvelle topographie est rajoutée à l'un des systèmes décrit dans cette base de données. - -Si besoin, des templates pour Therion sont disponibles sur https://github.com/robertxa/Th-Config-Xav . - - -Description ------------ - -Ce dépôt est en cours de développement et a pour objectif de sauvegarder et partager les données topographiques chiffrées et dessinées au format `Therion `_. -Ce travail est réalisé par les menbres de l'ARSIP, collectif d'exploration du massif de la pierre Saint Martin - -.. image:: /Logos/Logo-ARSIP-Synthese-Topo.jpg - :target: http://arsip.fr/ - :align: center - :width: 200px - - - -Une convention a aussi été mise en place pour la gestion des points d'interrogation, avec la définition des différents champs : - - * le champ "Code" qui décrit le type de terminus. Il peut prendre les valeurs : - - * A : il suffit d'y aller et de continuer, pas d'obstacles - - * D : Désobstruction nécessaire, - - * E : Escalade nécessaire, - - * P : Puits non descendu, - - * Q : non renseigné sur les topographies anciennes, c'est à voir/vérifier, - - * S : Siphon à plonger, - - * T : Trémie à désobstruer - - * le champ "Cavite" qui donne le nom de la cavité en question, - - * le champ "Reseau" qui indique la partie de la cavité où se situe le point d'interrogation (pour pouvoir le retrouver plus rapidement sur les topographies), - - * le champ "CA" qui est rempli si présence de courant d'air, avec éventuellement des remarques/commentaires. - -Exemple : -point 3922.0 1660.0 continuation -attr code Q -attr Cavite "GL102" -attr reseau "Grand Chao" -text "Rivière à topographier" -attr CA "inconnu" - -Licence -------- - -L'ensemble de ces données est publié sous la licence libre Creative Commons CC BY-NC-ND 4.0 (Attribution, partage à l'identique et pas d'utilisation commerciale) : -https://creativecommons.org/licenses/by-nc-nd/4.0/ - -.. image:: https://mirrors.creativecommons.org/presskit/buttons/88x31/png/by-nc-nd.png - :align: center - :width: 100px - :target: https://creativecommons.org/licenses/by-nc-nd/4.0/ - -Auteur de la base de données ----------------------------- - -Alexandre PONT (alexandre dot pont at yahoo dot fr ) pour le compte de l'ARSIP - -Contact --------- - -Pour plus d'informations, vous pouvez contacter L'ARSIP : https://www.arsip.fr/contactez-nous - -Remerciements -------------- - -Cette base de données est construite sur le modèle de celle des `massifs du Folly et de Criou `_, développée par Xavier Robert -), un grand merci pour le soutien actif diff --git a/Scripts/pyCreateTh/Lib/__pycache__/general_fonctions.cpython-313.pyc b/Scripts/pyCreateTh/Lib/__pycache__/general_fonctions.cpython-313.pyc index 22e7d38..bf64290 100644 Binary files a/Scripts/pyCreateTh/Lib/__pycache__/general_fonctions.cpython-313.pyc and b/Scripts/pyCreateTh/Lib/__pycache__/general_fonctions.cpython-313.pyc differ diff --git a/Scripts/pyCreateTh/Lib/__pycache__/therion.cpython-313.pyc b/Scripts/pyCreateTh/Lib/__pycache__/therion.cpython-313.pyc index acda215..c4b2243 100644 Binary files a/Scripts/pyCreateTh/Lib/__pycache__/therion.cpython-313.pyc and b/Scripts/pyCreateTh/Lib/__pycache__/therion.cpython-313.pyc differ diff --git a/Scripts/pyCreateTh/Lib/general_fonctions.py b/Scripts/pyCreateTh/Lib/general_fonctions.py index c292747..d832295 100644 --- a/Scripts/pyCreateTh/Lib/general_fonctions.py +++ b/Scripts/pyCreateTh/Lib/general_fonctions.py @@ -249,7 +249,7 @@ def read_config(config_file): global_data.stationNamesInTh2 = bool(config['Application_Data']['station_name_in_th2_files']) if 'Application_Data' in config and 'kSmooth' in config['Application_Data']: - global_data.therionPath = float(config['Application_Data']['kSmooth']) + global_data.kSmooth = float(config['Application_Data']['kSmooth']) diff --git a/Scripts/pyCreateTh/Lib/therion.py b/Scripts/pyCreateTh/Lib/therion.py index ba4a50b..6c79a6d 100644 --- a/Scripts/pyCreateTh/Lib/therion.py +++ b/Scripts/pyCreateTh/Lib/therion.py @@ -22,6 +22,8 @@ def compile_template(template, template_args, totReadMeError = "", **kwargs ): try: tmpdir = tempfile.mkdtemp() config = template.format(**template_args, tmpdir=tmpdir.replace("\\", "/")) + + log.debug(f"{config}\n") diff --git a/Scripts/pyCreateTh/config.ini b/Scripts/pyCreateTh/config.ini index c2c2837..a68e595 100644 --- a/Scripts/pyCreateTh/config.ini +++ b/Scripts/pyCreateTh/config.ini @@ -28,8 +28,8 @@ therion_path = C:\Program Files\Therion\therion.exe survey_prefix_name = Explo_ # Options for Th2 files -shot_lines_in_th2_files = True -station_name_in_th2_files = True +shot_lines_in_th2_files = False +station_name_in_th2_files = False wall_lines_in_th2_files = False # Koef for smoothing wall diff --git a/Scripts/pyCreateTh/pyCreateTh.py b/Scripts/pyCreateTh/pyCreateTh.py index 389a4ff..7b872de 100644 --- a/Scripts/pyCreateTh/pyCreateTh.py +++ b/Scripts/pyCreateTh/pyCreateTh.py @@ -42,12 +42,13 @@ En cours : """ -Version = "2025.07.01" +Version = "2025.07.02" ################################################################################################# ################################################################################################# import os, re, argparse, shutil, sys, time, math from os.path import isfile, join, abspath, splitext +from pathlib import Path import numpy as np import networkx as nx import pandas as pd @@ -423,7 +424,7 @@ def parse_xvi_file(th_name_xvi): return stations, lines, splays, x_min, x_max, y_min, y_max, x_ecart, y_ecart ################################################################################################# -def assign_groups_and_ranks(df_lines): +def assign_groups_and_ranks_Old(df_lines): G = nx.Graph() for _, row in df_lines.iterrows(): G.add_edge(row["name1"], row["name2"]) @@ -483,6 +484,9 @@ def assign_groups_and_ranks(df_lines): df_equates["group_id"] = df_equates["group_id"].astype(int) df_equates["start_point"] = df_equates["start_point"].astype(str) df_equates["end_point"] = df_equates["end_point"].astype(str) + + print("df_result columns:", df_result.columns) + print("df_result empty:", df_result.empty) # Ajout de la colonne max_rank max_ranks = df_result.groupby("group_id")["rank_in_group"].max().reset_index() @@ -501,6 +505,92 @@ def assign_groups_and_ranks(df_lines): return df_result, df_equates +def assign_groups_and_ranks(df_lines): + G = nx.Graph() + for _, row in df_lines.iterrows(): + G.add_edge(row["name1"], row["name2"]) + + used_edges = set() + results = [] + equates = [] # Liste des (group_id, start_point, end_point) + group_id = 0 + + def walk_path(u, prev=None): + path = [] + current = u + while True: + neighbors = [n for n in G.neighbors(current) if n != prev] + if len(neighbors) != 1: + break + next_node = neighbors[0] + edge = tuple(sorted((current, next_node))) + if edge in used_edges: + break + used_edges.add(edge) + path.append(edge) + prev = current + current = next_node + return path + + # Noeuds ayant un degré différent de 2 + start_nodes = [n for n in G.nodes if G.degree(n) != 2] + + # Si tous les nœuds ont un degré 2 : cycle fermé + if not start_nodes: + start_nodes = [list(G.nodes)[0]] + + for node in start_nodes: + for neighbor in G.neighbors(node): + edge = tuple(sorted((node, neighbor))) + if edge in used_edges: + continue + used_edges.add(edge) + path = [(node, neighbor)] + walk_path(neighbor, node) + + for rank, (n1, n2) in enumerate(path): + match = df_lines[(df_lines["name1"] == n1) & (df_lines["name2"] == n2)] + if match.empty: + match = df_lines[(df_lines["name1"] == n2) & (df_lines["name2"] == n1)] + if not match.empty: + row = match.iloc[0].copy() + row["group_id"] = group_id + row["rank_in_group"] = rank + results.append(row) + if rank == 0: + start_point = n1 + end_point = path[-1][1] if path else start_point + equates.append((group_id, str(start_point), str(end_point))) + group_id += 1 + + # Création du DataFrame principal + df_result = pd.DataFrame(results) + + # Création du DataFrame equates + df_equates = pd.DataFrame(equates, columns=["group_id", "start_point", "end_point"]) + df_equates["group_id"] = df_equates["group_id"].astype(int) + df_equates["start_point"] = df_equates["start_point"].astype(str) + df_equates["end_point"] = df_equates["end_point"].astype(str) + + # Ajout de la colonne max_rank (si possible) + if not df_result.empty and "group_id" in df_result.columns: + max_ranks = df_result.groupby("group_id")["rank_in_group"].max().reset_index() + max_ranks.rename(columns={"rank_in_group": "max_rank"}, inplace=True) + max_ranks["max_rank"] = max_ranks["max_rank"].astype(int) + df_equates = df_equates.merge(max_ranks, on="group_id", how="left") + else: + df_equates["max_rank"] = 0 + + # Ajout de la colonne start_group (raccord logique avec un autre groupe) + end_to_group = df_equates[["end_point", "group_id"]].copy() + end_to_group.rename(columns={"end_point": "start_point", "group_id": "start_group"}, inplace=True) + end_to_group["start_point"] = end_to_group["start_point"].astype(str) + df_equates = df_equates.merge(end_to_group, on="start_point", how="left") + + # Remplacer les NaN dans start_group par 0 + df_equates["start_group"] = df_equates["start_group"].fillna(0).astype(int) + + return df_result, df_equates + ################################################################################################# def add_start_end_splays(df_splays_complet, df_equates): @@ -560,7 +650,7 @@ def add_start_end_splays(df_splays_complet, df_equates): return df_splays_new - +################################################################################################# def align_points(smoothX1, smoothY1, X, Y, smoothX2, smoothY2): # Vecteurs d'origine vers smooth1 et smooth2 dx1, dy1 = smoothX1 - X, smoothY1 - Y @@ -591,7 +681,6 @@ def align_points(smoothX1, smoothY1, X, Y, smoothX2, smoothY2): return (_smoothX1, _smoothY1), (_smoothX2, _smoothY2) - ################################################################################################# def wall_construction_smoothed(df_lines, df_splays, x_min, x_max, y_min, y_max): @@ -610,7 +699,6 @@ def wall_construction_smoothed(df_lines, df_splays, x_min, x_max, y_min, y_max): if len(df_lines) <= 2 or len(df_splays) <= 2: return th2_walls, 0, 0, 0, 0 - df_lines, df_equates = assign_groups_and_ranks(df_lines) # Conversion en polaire @@ -969,7 +1057,6 @@ def create_th_folders(ENTRY_FILE, if not survey: raise NoSurveysFoundException(f"No survey found with that selector") - if UPDATE : DEST_PATH = os.path.dirname(args.file) log.info(f"Update th2 files: {Colors.ENDC}{DEST_PATH}") @@ -984,7 +1071,6 @@ def create_th_folders(ENTRY_FILE, log.debug(f"\t{Colors.BLUE}DEST_PATH: {Colors.ENDC} {DEST_PATH}") log.debug(f"\t{Colors.BLUE}ABS_PATH: {Colors.ENDC} {ABS_PATH}") - ################################################################################################# # Copy template folders # ################################################################################################# @@ -1000,20 +1086,19 @@ def create_th_folders(ENTRY_FILE, log.info(f"Compiling 2D XVI file: {Colors.ENDC}{TH_NAME}") if UPDATE: - template_args = { - "th_file": DEST_PATH + "/" + TH_NAME + ".th", - "selector": survey.therion_id, - "th_name": DEST_PATH + "/" + TH_NAME, - "scale": int(int(SCALE)/10), - } + thFile = Path(DEST_PATH + "\\" + TH_NAME + ".th") + thName = Path(DEST_PATH + "\\" + TH_NAME) else : - template_args = { - "th_file": DEST_PATH + "/Data/" + TH_NAME + ".th", + thFile = Path(DEST_PATH + "\\Data\\" + TH_NAME + ".th") + thName = Path(DEST_PATH + "\\Data\\" + TH_NAME) + + template_args = { + "th_file": thFile, "selector": survey.therion_id, - "th_name": DEST_PATH + "/Data/" + TH_NAME, + "th_name": thName, "XVIscale": globalData.XVIScale, - } + } logfile, tmpdir, totReadMeError = compile_template(globalData.thconfigTemplate, template_args, totReadMeError, cleanup=False, therion_path=globalData.therionPath) @@ -1026,8 +1111,7 @@ def create_th_folders(ENTRY_FILE, else : flagErrorCompile = False stat = get_stats_from_log(logfile) - - + ################################################################################################# # Update files # ################################################################################################# @@ -1508,7 +1592,7 @@ def mak_to_th_file(ENTRY_FILE) : for file in datFiles : ABS_file = os.path.dirname(abspath(args.file)) + "\\"+ file - content, val = load_text_file_utf8(ABS_file, os.path.basename(ABS_file)) + content, val, encodage = load_text_file_utf8(ABS_file, os.path.basename(ABS_file)) section = content.split('\x0c') QtySections += len(section) @@ -1553,9 +1637,9 @@ def mak_to_th_file(ENTRY_FILE) : for file in datFiles: if globalData.error_count > 0: - bar.text(f"{Colors.INFO}, file: {Colors.ENDC}{file[:-4]}{Colors.ERROR}, error: {Colors.ENDC}{globalData.error_count}") + bar.text(f"{Colors.INFO}file: {Colors.ENDC}{file[:-4]}{Colors.ERROR}, error: {Colors.ENDC}{globalData.error_count}") else : - bar.text(f"{Colors.INFO}, file: {Colors.ENDC}{file[:-4]}") + bar.text(f"{Colors.INFO}file: {Colors.ENDC}{file[:-4]}") _file = os.path.dirname(abspath(args.file)) + "\\" + file shutil.copy(_file, folderDest + "\\Data\\") @@ -2798,9 +2882,9 @@ def dat_to_th_files (ENTRY_FILE, fixPoints = [], crs_wkt = "", CONFIG_PATH = "", surveyCount += 1 if globalData.error_count > 0: - bar.text(f"{Colors.INFO}, file: {Colors.ENDC}{os.path.basename(ENTRY_FILE)[:-4]}{Colors.INFO}, survey: {Colors.ENDC}{currentSurveyName}{Colors.ERROR}, error: {Colors.ENDC}{globalData.error_count}") + bar.text(f"{Colors.INFO}file: {Colors.ENDC}{os.path.basename(ENTRY_FILE)[:-4]}{Colors.INFO}, survey: {Colors.ENDC}{currentSurveyName}{Colors.ERROR}, error: {Colors.ENDC}{globalData.error_count}") else : - bar.text(f"{Colors.INFO}, file: {Colors.ENDC}{os.path.basename(ENTRY_FILE)[:-4]}{Colors.INFO}, survey: {Colors.ENDC}{currentSurveyName}") + bar.text(f"{Colors.INFO}file: {Colors.ENDC}{os.path.basename(ENTRY_FILE)[:-4]}{Colors.INFO}, survey: {Colors.ENDC}{currentSurveyName}") bar() ################################################################################################# @@ -3081,9 +3165,9 @@ if __name__ == u'__main__': with redirect_stdout(sys.__stdout__): for i in range(1): if globalData.error_count > 0: - bar.text(f"{Colors.INFO}, file: {Colors.ENDC}{os.path.basename(ABS_file)[:-4]}{Colors.ERROR}, error: {Colors.ENDC}{globalData.error_count}") + bar.text(f"{Colors.INFO}file: {Colors.ENDC}{os.path.basename(ABS_file)[:-4]}{Colors.ERROR}, error: {Colors.ENDC}{globalData.error_count}") else : - bar.text(f"{Colors.INFO}, file: {Colors.ENDC}{os.path.basename(ABS_file)[:-4]}") + bar.text(f"{Colors.INFO}file: {Colors.ENDC}{os.path.basename(ABS_file)[:-4]}") stationList, fileTitle, totReadMeError, thread2 = dat_to_th_files (ABS_file , fixPoints = [], crs_wkt = "", CONFIG_PATH = _ConfigPath, totReadMeError = "", bar = bar) threads += thread2 bar()