script pyCreateTh

This commit is contained in:
Alex38Lyon
2026-01-06 14:20:34 +01:00
parent 4ebc6e0a4f
commit b7010fc18d
67 changed files with 143450 additions and 141799 deletions
+2
View File
@@ -10,6 +10,7 @@
"Backsights",
"beijing",
"cavename",
"centerlines",
"clarke",
"clino",
"CLINO",
@@ -83,6 +84,7 @@
"UUUUDDDDSSSB",
"UUUUDDDDSSSBL",
"UUUUDDDDSSSSSBL",
"Visualtopo",
"wpage",
"XTHERION"
]
+57 -32
View File
@@ -4,7 +4,9 @@ general_fonctions.py for pyCreateTh.py
#############################################################################################
"""
import os, logging, sys, re, configparser, unicodedata, shutil
import Lib.global_data as global_data
from pathlib import Path
import Lib.global_data as global_Data
import tkinter as tk
from tkinter import filedialog
@@ -45,34 +47,56 @@ class Colors:
BOLD = '\033[1m'
UNDERLINE = '\033[4m'
#################################################################################################
def safe_relpath(path):
abs_path = os.path.abspath(path)
ref_path = os.path.abspath(os.getcwd())
try:
valeur = "~\\" + os.path.relpath(path, ref_path)
return valeur
except ValueError:
max_depth = 4 # Profondeur maximale pour tronquer le chemin
# Disques différents, afficher le chemin relatif partiel depuis la racine commune
path_parts = abs_path.split(os.sep)
ref_parts = ref_path.split(os.sep)
while path_parts and ref_parts and path_parts[0] == ref_parts[0]:
path_parts.pop(0)
ref_parts.pop(0)
result = os.path.join(*path_parts) if path_parts else os.path.basename(path)
# fonction pour réduire l'affichage des chemins long #
#################################################################################################
def safe_relpath( path, base_dir=None, max_depth=4, max_name_len=50, prefix="~" ):
"""
Retourne un chemin lisible et sûr pour affichage (logs / UI).
# Si max_depth est défini, tronque le chemin
if max_depth is not None:
parts = result.split(os.sep)
if len(parts) > max_depth:
result = os.path.join("~\\" , *parts[-max_depth:])
return result
- Compatible Windows / Linux / macOS
- Tronque la profondeur du chemin
- Tronque le nom de fichier si trop long
- Ne lève jamais d'exception
"""
try:
path = Path(path).expanduser().resolve()
except Exception:
return str(path)
try:
base = Path(base_dir).expanduser().resolve() if base_dir else Path.cwd().resolve()
except Exception:
base = None
# 1️⃣ Nom du fichier (ou dossier) — tronqué si nécessaire
name = path.name
if len(name) > max_name_len:
stem = path.stem[: max_name_len - 6]
name = f"{stem}...{path.suffix}"
# 2️⃣ Tentative de chemin relatif
try:
if base:
rel = path.relative_to(base)
parts = list(rel.parts)
else:
raise ValueError
except Exception:
parts = list(path.parts)
# 3️⃣ Limitation de profondeur
if max_depth is not None and len(parts) > max_depth:
parts = parts[-max_depth:]
parts.insert(0, prefix)
# 4️⃣ Remplacement du nom si tronqué
if parts:
parts[-1] = name
# 5️⃣ Construction finale portable
return os.path.join(*parts)
#################################################################################################
@@ -216,19 +240,20 @@ def load_config(args, configIni="config.ini"):
if key.startswith('Copyright') and all(
k in config['Survey_Data'] for k in ['Copyright1', 'Copyright2', 'Copyright3']
):
global_data.Copyright = "\n".join([
global_Data.Copyright = "\n".join([
config['Survey_Data']['Copyright1'],
config['Survey_Data']['Copyright2'],
config['Survey_Data']['Copyright3']
])
global_data.CopyrightShort = config['Survey_Data']['Copyright_Short']
global_Data.CopyrightShort = config['Survey_Data']['Copyright_Short']
elif attr:
setattr(global_data, attr, config['Survey_Data'][key])
setattr(global_Data, attr, config['Survey_Data'][key])
app_keys = {
'template_path': 'templatePath',
'station_by_scrap': ('stationByScrap', int),
'final_therion_exe': ('finalTherionExe', lambda x: x.lower() == 'true'),
'parse_tro_files_by_explo': ('parse_tro_files_by_explo', lambda x: x.lower() == 'true'),
'therion_path': 'therionPath',
'survey_prefix_name': 'SurveyPrefixName',
'shot_lines_in_th2_files': ('linesInTh2', lambda x: x.lower() == 'true'),
@@ -240,7 +265,7 @@ def load_config(args, configIni="config.ini"):
for key, value in app_keys.items():
if 'Application_Data' in config and key in config['Application_Data']:
attr, caster = (value, str) if isinstance(value, str) else value
setattr(global_data, attr, caster(config['Application_Data'][key]))
setattr(global_Data, attr, caster(config['Application_Data'][key]))
return config_file
@@ -422,6 +447,6 @@ def update_template_files(template_path, variables, output_path):
global_Data.error_count += 1
except Exception as e:
log.error(f"An error occurred (update_template_files): {Colors.ENDC}{e}")
log.error(f"An error occurred (update_template_files : {Colors.ENDC}{os.path.basename(template_path)}{Colors.ERROR}) : {Colors.ENDC}{e}")
global_Data.error_count += 1
+12
View File
@@ -31,6 +31,18 @@ stationNamesInTh2 = True
wallLinesInTh2 = True
kSmooth = 0.5
XVIScale = 100
parse_tro_files_by_explo = True
#################################################################################################
THERION_KEYWORDS = {
"centerline", "end centerline",
"date", "units", "flags", "team",
"calibrate", "data", "declination",
"fix", "station", "equate",
"cs", "extend", "infer",
"instrument", "grade", "clino", "compass", "length", "reverse", "topofil", "duplicate", "not",
}
#################################################################################################
totfile = """\t## Survey file:
@@ -5,6 +5,8 @@
# Copyright (c) 2020 Xavier Robert <xavier.robert@ird.fr>
# SPDX-License-Identifier: GPL-3.0-or-later
# Modifié Alex 2026 01 04 -> print remplacés par log.xxx
"""
Script to build Therion files
@@ -35,6 +37,12 @@ from __future__ import division
# Import modules
import sys, os, datetime
import os, logging, sys
from Lib.general_fonctions import Colors
import Lib.global_data as global_data
log = logging.getLogger("Logger")
########################
def builddictcave(thlang = u'en', icomments = True, icoupe = True, Errfiles = True,
@@ -258,7 +266,7 @@ def writethconfig(pdata, icomments, icoupe, thlang, dictcave,
f2w.closed
print(u'\tFile ' + pdata + u' written...')
log.info(u'\tFile ' + pdata + u' written...')
return
@@ -1072,7 +1080,7 @@ def writethc(pdata, cavename = None, istructure = True):
# close the config.thc file
f1w.closed
print(u'\tFile ' + pdata + u' written...')
log.info(u'\tFile ' + pdata + u' written...')
return
@@ -1102,10 +1110,10 @@ def checkfiles(pdata, Errorfiles = True):
raise NameError(u'ERROR : File {FileNa} does exist'.format(FileNa=str(pdata)))
#sys.exit('ERROR : File {FileNa} does exist'.format(FileNa=str(pdata)))
else:
print(u'WARNING: I have erased file %s' % pdata)
log.warning(f"I have erased the file {Colors.ENDC}{pdata}")
#######
#######}
if __name__ == "__main__":
# build dictionnaries
@@ -1130,12 +1138,12 @@ if __name__ == "__main__":
if not Errfiles :
checkfiles(thcpath + thcfnme)
else:
print(u'WARNING: I will erase previous ' + thcpath + thcfnme + u' files !')
log.warning(f"I will erase previous {Colors.ENDC}{thcpath} {thcfnme}{Colors.WARNING} files !")
else:
if not Errfiles :
checkfiles(thcfnme)
else:
print(u'WARNING: I will erase previous ' + thcfnme + u' files !')
log.warning(f"I will erase previous {Colors.ENDC}{thcfnme}{Colors.WARNING} files !")
if thconfigfnme[-9:] != u'.thconfig':
thconfigfnme = thconfigfnme + u'.thconfig'
@@ -1145,12 +1153,12 @@ if __name__ == "__main__":
if not Errfiles:
checkfiles(thconfigpath + thconfigfnme)
else:
print(u'WARNING: I will erase previous ' + thconfigpath + thconfigfnme + u' files !')
log.warning(f"I will erase previous {Colors.ENDC}{thconfigpath} {thconfigfnme}{Colors.WARNING} files !")
else:
if not Errfiles :
checkfiles(thconfigfnme)
else:
print(u'WARNING: I will erase previous ' + thconfigfnme + u' files !')
log.warning(f"I will erase previous {Colors.ENDC}{thconfigfnme}{Colors.WARNING} files !")
# build thc file
if thcfile :
+29 -25
View File
@@ -5,6 +5,7 @@
# Copyright (c) 2020 Xavier Robert <xavier.robert@ird.fr>
# SPDX-License-Identifier: GPL-3.0-or-later
# Modifié Alex 2025 07 01
# Modifié Alex 2026 01 04 -> print remplacés par log.xxx
"""
!---------------------------------------------------------!
@@ -54,7 +55,8 @@ from .vtopotools import *
from .datathwritetools import *
from .buildthconfig import *
from Lib.general_fonctions import Colors
import os, logging, sys
from Lib.general_fonctions import Colors, safe_relpath
import Lib.global_data as global_data
log = logging.getLogger("Logger")
@@ -159,12 +161,12 @@ def tro2th(fle_tro_fnme = None, fle_th_fnme = None,
elif thlang in [u'en',u'EN', u'En', u'eN']: thlang = u'en'
else: raise NameError(u'ERROR: Language %s not implemented\n'
u' Use "en" instead' % thlang )
print(u'____________________________________________________________\n\n\t\tTRO 2 THERION\n____________________________________________________________\n')
log.info(u'____________________________________________________________\n\n\t\tTRO 2 THERION\n____________________________________________________________\n')
if thlang == u'fr':
print(u'\nEcrit par Xavier Robert, Groupe spéléo Vulcain - Lyon, France\n')
log.info(u'\nÉcrit par Xavier Robert, Groupe spéléo Vulcain - Lyon, France\n')
elif thlang == u'en':
print(u'\nWritten by Xavier Robert, Groupe spéléo Vulcain - Lyon, France\n')
print(u'____________________________________________________________\n\n')
log.info(u'\nWritten by Xavier Robert, Groupe spéléo Vulcain - Lyon, France\n')
log.info(u'____________________________________________________________\n\n')
coordsyst = None
coordinates = None
@@ -178,20 +180,20 @@ def tro2th(fle_tro_fnme = None, fle_th_fnme = None,
if fle_th_fnme is None:
# convert tro file to th file
print('1')
cavename, coordinates, coordsyst, fle_th_fnme = convert_tro(fle_tro_fnme, fle_tro_encoding=fle_tro_encoding,
log.info('1')
entrance, cavename, coordinates, coordsyst, fle_th_fnme = convert_tro(fle_tro_fnme, fle_tro_encoding=fle_tro_encoding,
icomments = icomments, icoupe = icoupe, istructure = istructure,
thlang = thlang, Errorfiles = Errorfiles)
else:
print(2)
cavename, coordinates, coordsyst, fle_th_fnme = convert_tro(fle_tro_fnme, fle_th_fnme, cavename,
log.info(2)
entrance, cavename, coordinates, coordsyst, fle_th_fnme = convert_tro(fle_tro_fnme, fle_th_fnme, cavename,
icomments = icomments, icoupe = icoupe, istructure = istructure,
thlang = thlang, Errorfiles = Errorfiles)
if thlang == u'fr': print(u'\tFichier Therion %s construit à partir des données %s' %(fle_th_fnme, fle_tro_fnme))
elif thlang == u'en': print(u'\tFile %s built from %s' %(fle_th_fnme, fle_tro_fnme))
if thlang == u'fr': log.info(u'\tFichier Therion %s construit à partir des données %s' %(fle_th_fnme, fle_tro_fnme))
elif thlang == u'en': log.info(u'\tFile %s built from %s' %(fle_th_fnme, fle_tro_fnme))
else:
if thlang == u'fr': print(u'\tPas de fichier .tro en entrée, pas de fichier de données .th créé...')
elif thlang == u'en': print(u'\tNo .tro File input, no .th data file created...')
if thlang == u'fr': log.info(u'\tPas de fichier .tro en entrée, pas de fichier de données .th créé...')
elif thlang == u'en': log.info(u'\tNo .tro File input, no .th data file created...')
# Build here the new structure:
if istructure: build_structure(u'cave', Errorfiles = True)
@@ -255,22 +257,22 @@ def tro2th(fle_tro_fnme = None, fle_th_fnme = None,
f3w = open(cavename.replace(u' ', u'_') + '/' + cavename.replace(u' ', u'_') + '-tot.th', 'w')
write_thtot(f3w, cavename, icomments, thlang)
f3w.closed
print(u'\tFile ' + cavename.replace(u' ', u'_') + u'/' + cavename.replace(u' ', u'_') + u'-tot.th written...\n')
log.info(u'\tFile ' + cavename.replace(u' ', u'_') + u'/' + cavename.replace(u' ', u'_') + u'-tot.th written...\n')
# build cavename-maps.th file
f4w = open(cavename.replace(u' ', u'_') + u'/' + cavename.replace(u' ', u'_') + '-maps.th', 'w')
write_thmaps(f4w, cavename, icomments, thlang)
f4w.closed
print(u'\tFile ' + cavename.replace(u' ', u'_') + u'/' + cavename.replace(u' ', u'_') + u'-maps.th written...\n\n')
log.info(u'\tFile ' + cavename.replace(u' ', u'_') + u'/' + cavename.replace(u' ', u'_') + u'-maps.th written...\n\n')
# build Legends/entrances-coordinates.th file
f5w = open(cavename.replace(u' ', u'_') + '/Legends/entrances_coordinates.th', 'w')
write_thcoords(f5w, cavename, coordinates, coordsyst, icomments, thlang)
f5w.closed
print(u'\tFile ' + cavename.replace(u' ', u'_') + u'/Legends/entrances_coordinates.th written...\n\n')
log.info(u'\tFile ' + cavename.replace(u' ', u'_') + u'/Legends/entrances_coordinates.th written...\n\n')
print(u'____________________________________________________________')
print(u'')
log.info(u'____________________________________________________________')
log.info(u'')
return
@@ -311,7 +313,7 @@ def build_structure(cavename, Errorfiles = True):
# Stop
raise NameError(u'ERROR : Folder {FileNa} does exist'.format(FileNa=str(cavename.replace(u' ', u'_'))))
else:
print(u'WARNING: I have erased folder %s' % cavename.replace(u' ', u'_'))
log.warning(f"I have erased the folder {Colors.ENDC}{cavename.replace(u' ', u'_')}")
if not os.path.exists(cavename.replace(u' ', u'_') + u'/Data'): os.mkdir(cavename.replace(u' ', u'_') + u'/Data')
if not os.path.exists(cavename.replace(u' ', u'_') + u'/Legends'): os.mkdir(cavename.replace(u' ', u'_') + u'/Legends')
if not os.path.exists(cavename.replace(u' ', u'_') + u'/Outputs'):
@@ -350,7 +352,7 @@ def mkfle_output_txt(cavename):
f1w.write(u'Folder where Therion outputs are exported \n\n')
# close the cavename/Outputs/outputs.txt file
f1w.closed
print(u'\tFile ' + cavename.replace(u' ', u'_') + u'/Outputs/outputs.txt written...')
log.info(u'\tFile ' + cavename.replace(u' ', u'_') + u'/Outputs/outputs.txt written...')
return
@@ -381,6 +383,7 @@ def convert_tro(fle_tro_fnme, fle_tro_encoding=None, fle_th_fnme = None, cavenam
cavename : Name of the cave from the .tro file
coordinates : Coordinates of the entrance
coordsyst : Coordinates system used by the .tro file
entrance : Entrance station
USAGE:
cavename, coordsyst = convert_tro(fle_tro_fnme, [fle_th_fnme = fle_th_fnme, cavename = cavename, Errorfiles = Errorfiles])
@@ -399,8 +402,8 @@ def convert_tro(fle_tro_fnme, fle_tro_encoding=None, fle_th_fnme = None, cavenam
#alt=0.
# open the .tro survey
if thlang == u'fr': log.info(f"Travail sur le fichier VisualTopo: {Colors.ENDC}{fle_tro_fnme}")
elif thlang == u'en':log.info(f"Processing VisualTopo file: {Colors.ENDC}{fle_tro_fnme}")
log.info(f"Processing VisualTopo file: {Colors.ENDC}{safe_relpath(fle_tro_fnme)}")
# print(' ')
fle_tro = open(fle_tro_fnme, 'r', encoding=fle_tro_encoding)
# read the .tro file
@@ -429,13 +432,13 @@ def convert_tro(fle_tro_fnme, fle_tro_encoding=None, fle_th_fnme = None, cavenam
# read the header
coordinates = None
cavename, coordinates, coordsyst, club, entrance, versionfle = read_vtopo_header(lines)
if cavename is None or cavename == '' or cavename == ' ':
cavename = u'cave'
if fle_th_fnme is None:
fle_th_fnme = cavename.replace(u' ', u'_') + u'.th'
print (fle_th_fnme)
log.info (fle_th_fnme)
if fle_th_fnme[-3:] != u'.th':
fle_th_fnme = fle_th_fnme + u'.th'
@@ -449,6 +452,7 @@ def convert_tro(fle_tro_fnme, fle_tro_encoding=None, fle_th_fnme = None, cavenam
# open the .th file
if istructure: fle_th = open (cavename.replace(u' ', u'_') + '/Data/' + fle_th_fnme, 'w')
else: fle_th = open (fle_th_fnme, 'w')
# write the .th header
writeheader_th(fle_th, cavename, entrance)
@@ -488,7 +492,7 @@ def convert_tro(fle_tro_fnme, fle_tro_encoding=None, fle_th_fnme = None, cavenam
if thlang == u'fr': log.info(f"Fichier Therion {Colors.ENDC}{fle_th_fnme}{Colors.INFO} écrit !")
elif thlang == u'en': log.info(f"Therion file {Colors.ENDC}{fle_th_fnme}{Colors.INFO} written!")
return cavename, coordinates, coordsyst, fle_th_fnme
return entrance, cavename, coordinates, coordsyst, fle_th_fnme
#################################################################################################
+18 -9
View File
@@ -23,8 +23,6 @@ def compile_template(template, template_args, totReadMeError = "", **kwargs ):
tmpdir = tempfile.mkdtemp()
config = template.format(**template_args, tmpdir=tmpdir.replace("\\", "/"))
log.debug(f"{config}\n")
config_file = join(tmpdir, "config.thconfig")
@@ -51,13 +49,15 @@ def compile_template(template, template_args, totReadMeError = "", **kwargs ):
try:
with open(log_file, "r", encoding="cp1252", errors="replace") as f:
logfile = f.read()
except Exception as log_err:
log.warning(f"Could not read Therion log: {Colors.ENDC}{log_err}")
# Analyse du code retour
if result.returncode != 0 or "press any key" in result.stdout.lower():
log.error(f"Therion compilation failed with return code: {Colors.ENDC}{result.returncode}\n{Colors.WHITE}{result.stdout}")
totReadMeError += f"\tTherion compilation failed with return code: {result.returncode}\n"
selector_value = template_args['selector']
log.error(f"Therion compilation {Colors.ENDC}{selector_value}{Colors.ERROR}, failed with return code: {Colors.ENDC}{result.returncode}\n{Colors.WHITE}{result.stdout}")
totReadMeError += f"\tTherion compilation {selector_value}, failed with return code: {result.returncode}\n"
global_data.error_count += 1
return "Therion error", tmpdir, totReadMeError
@@ -178,16 +178,24 @@ def compile_file_th(filepath, **kwargs):
#################################################################################################
# Attention fonctionne pour la version therion en français ! à voir pour les autres langues
lengthre = re.compile(r".*Longueur totale de la topographie = \s*(\S+)m")
depthre = re.compile(r".*Longueur totale verticale =\s*(\S+)m")
fr_lengthre = re.compile(r".*Longueur totale de la topographie = \s*(\S+)m")
fr_depthre = re.compile(r".*Longueur totale verticale =\s*(\S+)m")
en_lengthre = re.compile(r".*Total length of survey legs = \s*(\S+)m")
en_depthre = re.compile(r".*Total vertical length of survey legs =\s*(\S+)m")
def get_stats_from_log(log):
lenmatch = lengthre.findall(log)
depmatch = depthre.findall(log)
lenmatch = fr_lengthre.findall(log)
depmatch = fr_depthre.findall(log)
if len(lenmatch) == 0 and len(depmatch) == 0:
lenmatch = en_lengthre.findall(log)
depmatch = en_depthre.findall(log)
if len(lenmatch) == 1 and len(depmatch) == 1:
return {"length": lenmatch[0], "depth": depmatch[0]}
return {"length": 0, "depth": 0}
return {"length": 0.0, "depth": 0.0}
#################################################################################################
@@ -198,4 +206,5 @@ def get_syscoord_from_log(log):
if len(lenmatch) == 1:
return {"syscoord": lenmatch[0]}
return {"syscoord": 0}
@@ -9,6 +9,5 @@
# Survey / file list :
{readMeList}
# Error list :
{errorList}
+2 -1
View File
@@ -3,7 +3,7 @@
# General layout information :
[Survey_Data]
Author = Alexandre Pont
Copyright1 = # Copyright (C) ARSIP 2025
Copyright1 = # Copyright (C) ARSIP 2026
Copyright2 = # This work is under the Creative Commons Attribution-NonCommercial-NoDerivatives License:
Copyright3 = # <http://creativecommons.org/licenses/by-nc-nd/4.0/>
Copyright_Short = License CC by-nc-nd : http://creativecommons.org/licenses/by-nc-nd/4.0/
@@ -19,6 +19,7 @@ cs = UTM30
[Application_Data]
template_path = ./template
station_by_scrap = 30
parse_tro_files_by_explo = True
# For a final Therion compilation True,else False
final_therion_exe = True
+589 -77
View File
@@ -30,15 +30,15 @@ Sources documentaires :
Création Alex le 2025 06 09
En cours :
- tester la avec les dernières option de la version de DAT (CORRECTION2 et suivants)
- découper les fichier tro et th comme les fichiers dat/mark...
- tester avec les dernières option de la version de DAT (CORRECTION2 et suivants)
- améliorer fonction wall shot pour faire habillage des th2 files, les jointures...
- traiter les series avec 1 ou 2 stations
- PB des cartouches et des échelles pour faire des pdf automatiquement
- tester différentes version pour les fichiers .tro
"""
Version = "2025.11.10"
Version = "2026.01.06"
#################################################################################################
#################################################################################################
@@ -64,7 +64,6 @@ import Lib.global_data as globalData
from Lib.pytro2th.tro2th import convert_tro #Version local modifiée
#################################################################################################
debug_log = False # Mode debug des messages
@@ -88,15 +87,155 @@ class StationNameAccessor:
)
#################################################################################################
def parse_therion_centerline(file_data):
"""Découpe des centerline Therion et extrait :
- DATA : lignes de tirs
- date : date du levé
- type : liste des stations
- lines : bloc complet
"""
centerline_list = []
try:
lines = file_data.splitlines()
current_block = []
current_data = []
current_date = None
current_stations = set()
in_centerline = False
for line in lines:
stripped = line.strip()
low = stripped.lower()
# Début centerline
if low.startswith("centerline"):
in_centerline = True
current_block = [line]
current_data = []
current_date = None
current_stations = set()
continue
if not in_centerline:
continue
current_block.append(line)
# Commentaire ou vide
if not stripped or stripped.startswith("#"):
continue
# Date
m = re.match(r"^[ \t]*date\s+(.+)$", line, re.IGNORECASE)
if m:
current_date = m.group(1).strip()
continue
parts = stripped.split()
# Ligne DATA (tir)
if len(parts) >= 2 and parts[0].lower() not in globalData.THERION_KEYWORDS:
current_data.append(line)
for p in parts[:2]:
if (
p.lower() not in globalData.THERION_KEYWORDS
and not re.match(r"^[0-9.+-]+$", p)
):
current_stations.add(p)
# Fin centerline
if low.startswith("endcenterline"):
centerline_list.append({
"lines": current_block,
"DATA": current_data,
"date": current_date,
"type": sorted(current_stations)
})
in_centerline = False
current_block = []
current_data = []
current_date = None
current_stations = set()
except Exception as e:
log.error(f"An error occurred (parse_therion_centerline): {Colors.ENDC}{e}")
globalData.error_count += 1
return centerline_list
#################################################################################################
def regroupe_date(centerline_list):
"""Regroupe les centerlines par date et concatène les champs.
Args:
centerline_list (list): liste de dicts contenant :
- lines (list)
- DATA (list)
- date (str|None)
- type (list)
Returns:
list: liste de dicts regroupés par date
"""
grouped = {}
try:
for idx, cl in enumerate(centerline_list):
# Sécurité : cl doit être un dict
if not isinstance(cl, dict):
log.warning(f"regroupe_date: entrée ignorée (index {idx}, type invalide)")
continue
date = cl.get("date")
if date not in grouped:
grouped[date] = {
"date": date,
"lines": [],
"DATA": [],
"type": set()
}
# Concaténations sécurisées
if isinstance(cl.get("lines"), list):
grouped[date]["lines"].extend(cl["lines"])
if isinstance(cl.get("DATA"), list):
grouped[date]["DATA"].extend(cl["DATA"])
if isinstance(cl.get("type"), (list, set)):
grouped[date]["type"].update(cl["type"])
# Finalisation (conversion set → list)
result = []
for g in grouped.values():
g["type"] = sorted(g["type"])
result.append(g)
return result
except Exception as e:
log.error(f"An error occurred (regroupe_date): {Colors.ENDC}{e}")
globalData.error_count += 1
return []
#################################################################################################
def parse_therion_surveys(file_path):
"""Parse les enquêtes Therion à partir d'un fichier.
"""Découpe des surveys à partir d'un fichier Therion.
Args:
file_path (str): Le chemin d'accès au fichier à analyser.
Returns:
list: Une liste des noms d'enquête trouvés dans le fichier.
list: Une liste des noms des surveys trouvés dans le fichier.
"""
survey_names = []
@@ -131,6 +270,7 @@ def parse_therion_surveys(file_path):
return survey_names
#################################################################################################
def convert_to_line_polaire_df(df_lines):
"""Convertit un DataFrame de lignes cartésiennes (x1, y1, x2, y2, name1, name2)
@@ -193,6 +333,7 @@ def convert_to_line_polaire_df(df_lines):
globalData.error_count += 1
return pd.DataFrame()
#################################################################################################
def parse_xvi_file(thNameXvi):
"""Parse un fichier .xvi et extrait les stations et les lignes.
@@ -257,6 +398,7 @@ def parse_xvi_file(thNameXvi):
return stations, lines, splays, x_min, x_max, y_min, y_max, x_ecart, y_ecart
#################################################################################################
def assign_groups_and_ranks(df_lines):
"""Assigne des groupes et des rangs aux lignes du DataFrame.
@@ -771,7 +913,7 @@ def wall_construction_smoothed(df_lines, df_splays, x_min, x_max, y_min, y_max):
#################################################################################################
# Création des dossiers à partir d'un th file #
# Création des fichiers et dossiers à partir d'un th file #
#################################################################################################
def create_th_folders(ENTRY_FILE,
PROJECTION = "all",
@@ -800,6 +942,7 @@ def create_th_folders(ENTRY_FILE,
"""
threads = []
totReadMe = ""
TH_NAME = sanitize_filename(os.path.splitext(os.path.basename(ENTRY_FILE))[0])
DEST_PATH = os.path.dirname(ENTRY_FILE) + "/" + TH_NAME
ABS_PATH = os.path.dirname(ENTRY_FILE)
@@ -827,7 +970,7 @@ def create_th_folders(ENTRY_FILE,
exit(1)
# Normalise name, namespace, key, file path
log.info(f"Parsing survey entry file: {Colors.ENDC}{shortCurentFile}")
log.info(f"Parsing therion survey entry file: {Colors.ENDC}{shortCurentFile}")
survey_list = parse_therion_surveys(ENTRY_FILE)
@@ -838,7 +981,7 @@ def create_th_folders(ENTRY_FILE,
TARGET = survey_list[0]
log.info(f"Parsing survey target: {Colors.ENDC}{TARGET}")
log.info(f"Parsing therion survey target: {Colors.ENDC}{TARGET}")
loader = SurveyLoader(ENTRY_FILE)
survey = loader.get_survey_by_id(survey_list[0])
@@ -893,13 +1036,21 @@ def create_th_folders(ENTRY_FILE,
shutil.rmtree(tmpdir)
if totReadMeError == "" : totReadMeError += "\tNo errors found in this file, perfect!\n"
if logfile == "Therion error":
# log.error(f"Therion error in: {Colors.ENDC}{TH_NAME}")
flagErrorCompile = True
stat = {"length": 0, "depth": 0}
log.info(f"File: {Colors.ENDC}{os.path.basename(thFile)}{Colors.INFO}, compilation error, length: {Colors.ENDC}{stat["length"]}m{Colors.INFO}, depth: {Colors.ENDC}{stat["depth"]}m")
totReadMe = f"\t{os.path.basename(thFile)} compilation error length: {stat["length"]} m, depth: {stat["depth"]} m\n"
else :
flagErrorCompile = False
stat = get_stats_from_log(logfile)
log.info(f"File: {Colors.ENDC}{os.path.basename(thFile)}{Colors.INFO}, compilation successful, length: {Colors.ENDC}{stat["length"]}m{Colors.INFO}, depth: {Colors.ENDC}{stat["depth"]}m")
totReadMe = f"\t{os.path.basename(thFile)} compilation successful length: {stat["length"]} m, depth: {stat["depth"]} m\n"
#################################################################################################
# Update files #
@@ -920,7 +1071,7 @@ def create_th_folders(ENTRY_FILE,
ERR = "# " if flagErrorCompile else "",
Plan = plan,
Extended = extended,
Maps = maps)
Maps = maps)
# Adapte templates
config_vars = {
@@ -940,9 +1091,12 @@ def create_th_folders(ENTRY_FILE,
'totData' : totdata,
'maps' : maps,
'plan': plan,
'XVIscale':globalData.XVIScale,
'extended': extended,
'XVIscale':globalData.XVIScale,
'XVIscale' : globalData.XVIScale,
'extended' : extended,
'XVIscale' : globalData.XVIScale,
'readMeList': str(totReadMe),
'errorList' : str(totReadMeError),
'fixPointList' : str(" "),
'other_scraps_plan' : "",
'file_info' : f'# File generated by pyCreateTh.py version: {Version} date: {datetime.now().strftime("%Y.%m.%d %H:%M:%S")}',
}
@@ -1364,8 +1518,8 @@ def mak_to_th_file(ENTRY_FILE) :
crs_wkt = f'EPSG:{epsg_code}'
log.info(f"Reading mak file: {Colors.ENDC}{shortCurentFile}{Colors.GREEN}, fixed station: {Colors.ENDC}{len(fixPoints)}{Colors.GREEN}, files: {Colors.ENDC}{len(datFiles)}{Colors.GREEN}, UTM Zone: {Colors.ENDC}{UTM[0]}{Colors.GREEN}, Datum: {Colors.ENDC}{next(iter(Datums))}{Colors.GREEN}, SCR: {Colors.ENDC}{crs_wkt}")
totReadMeFixPoint = f"* Source mak file: {os.path.basename(ENTRY_FILE)}, fixed station: {len(fixPoints)}, files: {len(datFiles)}, UTM Zone: {UTM[0]}, Datum: {next(iter(Datums))}, SCR: {crs_wkt}\n"
log.info(f"Reading mak file: {Colors.ENDC}{shortCurentFile}{Colors.GREEN}, fixed station: {Colors.ENDC}{len(fixPoints)}{Colors.GREEN}, files : {Colors.ENDC}{len(datFiles)}{Colors.GREEN}, UTM Zone : {Colors.ENDC}{UTM[0]}{Colors.GREEN}, Datum : {Colors.ENDC}{next(iter(Datums))}{Colors.GREEN}, SCR : {Colors.ENDC}{crs_wkt}")
totReadMeFixPoint = f"\t* Source mak file : {os.path.basename(ENTRY_FILE)}, fixed station: {len(fixPoints)}, files : {len(datFiles)}, UTM Zone : {UTM[0]}, Datum : {next(iter(Datums))}, SCR : {crs_wkt}\n"
QtySections = 0
@@ -1424,8 +1578,8 @@ def mak_to_th_file(ENTRY_FILE) :
shutil.copy(_file, folderDest + "\\Data\\")
ABS_file = folderDest + "\\Data\\" + file
totReadMeError += f"* file: {file}\n"
totReadMeList += f"file: {file}\n"
totReadMeError += f"\t* file: {file}\n"
totReadMeList += f"\tfile: {file}\n"
Station, SurveyTitle, totReadMeError, thread2 = dat_to_th_files(ABS_file, fixPoints, crs_wkt, _ConfigPath, totReadMeError, bar)
@@ -1477,7 +1631,7 @@ def mak_to_th_file(ENTRY_FILE) :
tableau_pivot = tableau_pivot.reset_index()
tableau_equate = tableau_pivot[tableau_pivot['Survey_Name_2'].notna()]
log.info(f"Total des 'equates' in mak file: {Colors.ENDC}{len(tableau_equate)}{Colors.INFO} in {Colors.ENDC}{safe_relpath(args.file)}")
log.info(f"Total des '{Colors.ENDC}equates{Colors.INFO}' in mak file: {Colors.ENDC}{len(tableau_equate)}{Colors.INFO} in {Colors.ENDC}{safe_relpath(args.file)}")
# print(tableau_equate)
# print(f"fixPoints: {Colors.ENDC}{fixPoints}{Colors.INFO} in {Colors.ENDC}{args.file}")
@@ -1531,7 +1685,6 @@ def mak_to_th_file(ENTRY_FILE) :
update_template_files(DEST_PATH + '/template.thconfig', config_vars, DEST_PATH + '/' + SurveyTitleMak + '.thconfig')
update_template_files(DEST_PATH + '/template-tot.th', config_vars, DEST_PATH + '/' + SurveyTitleMak + '-tot.th')
update_template_files(DEST_PATH + '/template-maps.th', config_vars, DEST_PATH + '/' + SurveyTitleMak + '-maps.th')
update_template_files(DEST_PATH + '/template-readme.md', config_vars, DEST_PATH + '/' + SurveyTitleMak + '-readme.md')
#################################################################################################
# Final therion compilation #
@@ -1541,13 +1694,36 @@ def mak_to_th_file(ENTRY_FILE) :
FILE = DEST_PATH + '/' + SurveyTitleMak + '.thconfig'
t = compile_file(FILE, therion_path=globalData.therionPath)
threads.append(t)
for thread in threads: # Attendre que tous les threads se terminent
thread.join()
logfile = (DEST_PATH + '/therion.log').replace("\\", "/")
with open(logfile, 'r') as f:
content = f.read()
# print(content)
stat = get_stats_from_log(content)
if stat["length"] != 0.0 and stat["depth"] != 0.0 :
totReadMeList += f"\tFinal compilation successful length: {stat["length"]} m, depth: {stat["depth"]} m\n"
log.info(f"Final compilation successful length: {Colors.ENDC}{stat["length"]}{Colors.INFO} m, depth: {Colors.ENDC}{stat["depth"]}{Colors.INFO} m")
else :
totReadMeList += f"\tFinal compilation error, check log file\n"
log.error(f"Final compilation error, check log file")
config_vars['readMeList'] = totReadMeList
update_template_files(DEST_PATH + '/template-readme.md', config_vars, DEST_PATH +'/' + SurveyTitle + '-readme.md')
return SurveyTitleMak, threads
#################################################################################################
def station_list(data, list, fixPoints, currentSurveyName) :
"""Crée une liste de stations à partir des données fournies.
def station_list_dat(data, list, fixPoints, currentSurveyName) :
"""Crée une liste de stations à partir des données fournies issues d'un fichier dat.
Args:
data (DataFrame): Les données d'entrée contenant les informations sur les stations.
@@ -1582,6 +1758,48 @@ def station_list(data, list, fixPoints, currentSurveyName) :
return list, dfDATA
#################################################################################################
def station_list_th(data, list, fixPoints, currentSurveyName) :
"""Crée une liste de stations à partir des données fournies issues d'un fichier tro.
Args:
data (DataFrame): Les données d'entrée contenant les informations sur les stations.
list (DataFrame): La liste des stations existantes.
fixPoints (list): Les points de fixation à considérer.
currentSurveyName (str): Le nom de l'enquête en cours.
Returns:
DataFrame: La liste mise à jour des stations.
"""
# Création d'un DataFrame à partir des données
rows1 = [line.split() for line in data['DATA']]
dfDATA = pd.DataFrame(rows1)
# stations = pd.concat([dfDATA.iloc[1:, 0], dfDATA.iloc[1:, 1]]).drop_duplicates().str.replace('[', '%').str.replace(']', '%%').str.replace('@', '_._')
# stations = pd.concat([dfDATA.iloc[1:, 0], dfDATA.iloc[1:, 1]]).drop_duplicates().stationName()
# stations = pd.concat([dfDATA.iloc[:, 0], dfDATA.iloc[:, 1]]).drop_duplicates().reset_index(drop=True)
stations = pd.concat([dfDATA.iloc[:, 0], dfDATA.iloc[:, 1]]).dropna().astype(str).loc[lambda s: ~s.isin(["-", "*"])].drop_duplicates().reset_index(drop=True)
# print(stations)
fixed_names = {point[0] for point in fixPoints}
stations = stations[~stations.isin(fixed_names)]
new_entries = pd.DataFrame({
'StationName': stations,
'Survey_Name_01': currentSurveyName
})
list = pd.concat([list, new_entries], ignore_index=True)
# print(new_entries)
return list, dfDATA
#################################################################################################
def formated_station_list(df, dataFormat, unit = "meter", shortCurentFile ="None") :
"""Formate une liste de stations à partir d'un DataFrame.
@@ -2309,9 +2527,9 @@ def load_text_file_utf8(filepath, short_filename):
try:
with open(filepath, 'r', encoding=enc) as f:
content = f.read()
log.info(f"Source file: {Colors.ENDC}{short_filename}{Colors.GREEN}, encoding: {Colors.ENDC}{enc}{Colors.GREEN}, conversion to {Colors.ENDC}utf-8")
message = f"* Source file: {short_filename}, encoding: {enc}, conversion to utf-8\n"
return content, message, enc
log.info(f"Open source file: {Colors.ENDC}{short_filename}{Colors.GREEN}, with encoding: {Colors.ENDC}{enc}{Colors.GREEN} and conversion to {Colors.ENDC}utf-8")
message_log = f"* Source file: {short_filename}, encoding: {enc}, conversion to utf-8\n"
return content, message_log, enc
except UnicodeDecodeError as e:
log.debug(f"Failed {Colors.ENDC}{enc}{Colors.DEBUG} for {Colors.ENDC}{short_filename}{Colors.DEBUG}: {Colors.ENDC}{e}")
@@ -2337,12 +2555,251 @@ def load_text_file_utf8(filepath, short_filename):
return None, "", None
#################################################################################################
# Création des dossiers Th à partir d'un dat #
# Convertit un fichier .tro en fichiers .th #
#################################################################################################
def tro_to_th_files(ENTRY_FILE, centerlines = [], entrance = "", fileTitle = "", coordinates = [], coordsyst = "", fle_th_fnme = "", CONFIG_PATH = "", totReadMeError = "", bar=None) :
"""
Convertit un fichier .tro en fichiers .th
Args:
ENTRY_FILE (str): Le chemin vers le fichier .dat d'entrée.
fixPoints (list, optional): Liste des points de fixation. Defaults to [].
crs_wkt (str, optional): Le système de référence spatiale en WKT. Defaults to "".
CONFIG_PATH (str, optional): Le chemin vers le fichier de configuration. Defaults to "".
Returns:
tuple: Un tuple contenant un DataFrame des stations et le nom du survey.
"""
#################################################################################################
# 1 : Initialisations #
#################################################################################################
data = []
unique_id = 1
totdata = f"\t## Input list:\n"
totMapsPlan = ""
totMapsExtended = ""
totReadMeErrorDat = ""
maps = ""
plan = ""
extended = ""
totReadMe = ""
surveyCount = 0
totReadMeFixPoint = f"\tcs {coordsyst}\n"
totReadMeFixPoint += f"\tFix point: {entrance} [{coordinates[0]} km, {coordinates[1]} km, {coordinates[2]} m]\n"
listStationSection = pd.DataFrame(columns=['StationName', 'Survey_Name'])
threads = []
fixPoints = []
fixPoints.append([entrance, " ", coordinates[0], coordinates[1], coordinates[2]])
log.debug(f"{Colors.INFO}------------------------------------------------------------------------------------------------------------------{Colors.ENDC}")
SurveyTitle = sanitize_filename(os.path.basename(ENTRY_FILE)[:-4])
folderDest = os.path.dirname(ENTRY_FILE) + "\\" + SurveyTitle
copy_template_if_not_exists(globalData.templatePath,folderDest)
#################################################################################################
# 2 : Boucle pour convertir les centerlines #
#################################################################################################
for i, cl in enumerate( sorted(centerlines, key=lambda x: (x['date'] is None, x['date'])), start=1 ):
currentSurveyName = f"{globalData.SurveyPrefixName}{i:02d}_{sanitize_filename(cl['date'])}"
fileName = folderDest + "\\Data\\" + currentSurveyName + ".th"
log.debug(f"{Colors.INFO}Centerline # {Colors.ENDC}{i}")
log.debug(f"{Colors.INFO}Date : {Colors.ENDC}{cl['date']}")
log.debug(f"{Colors.INFO}Stations: {Colors.ENDC}{cl['DATA']}")
log.debug(f"{Colors.INFO}Lignes :{Colors.ENDC}")
add_lines = "\nencoding utf-8\n"
add_lines+= f"# File generated by pyCreateTh.py version: {Version} date: {datetime.now().strftime("%Y.%m.%d %H:%M:%S")}\n"
add_lines+= f'\nsurvey {globalData.SurveyPrefixName}{i:02d}_{sanitize_filename(cl['date'])} -title "{fileTitle} Explo num {i:02d}"'
cl['lines'] = [add_lines] + cl['lines'] + ["endsurvey"]
with open(str(fileName), "w+", encoding="utf-8") as f:
for line in cl['lines']:
log.debug(line)
f.write(f"{line}\n")
f.write(f"\n\n#############################################################################################")
f.write(f"\n# Originals data file : {args.file}")
if globalData.error_count == 0 :
f.write(f"\n# Conversion with pyCreateTh version {Version}, the {datetime.now().strftime("%Y.%m.%d %H:%M:%S")}, without error")
else :
f.write(f"\n# Conversion with pyCreateTh version {Version}, the {datetime.now().strftime("%Y.%m.%d %H:%M:%S")}, with {globalData.error_count} error(s)")
f.write(f"\n#############################################################################################\n\n")
for line in source_content.splitlines():
f.write(f"# {line}\n")
log.debug(f"{Colors.INFO}------------------------------------------------------------------------------------------------------------------{Colors.ENDC}")
# Ajouter les données de la section à la liste
if len(cl['DATA']) > 0 :
listStationSection, dfDATA = station_list_th(cl, listStationSection, fixPoints, currentSurveyName)
# print(f"Explo {i}, dfDATA : {dfDATA}")
# print(listStationSection)
StatCreateFolder, stat, totReadMeErrorDat, thread2 = create_th_folders(fileName, TARGET = None,
PROJECTION= args.proj, SCALE = args.scale,
UPDATE = args.update, CONFIG_PATH = "",
totReadMeError = totReadMeErrorDat)
threads += thread2
log.info(f"File: {Colors.ENDC}{currentSurveyName}{Colors.INFO}, compilation successful, length: {Colors.ENDC}{stat["length"]}m{Colors.INFO}, depth: {Colors.ENDC}{stat["depth"]}m")
totReadMe += f"\t{currentSurveyName} compilation successful length: {stat["length"]} m, depth: {stat["depth"]} m\n"
if not StatCreateFolder :
totMapsPlan += f"\t{plan}MP-{currentSurveyName}-Plan-tot@{currentSurveyName}\n\t{plan}break\n"
totMapsExtended += f"\t{extended}MC-{currentSurveyName}-Extended-tot@{currentSurveyName}\n\t{extended}break\n"
surveyCount += 1
totdata +=f"\tinput Data/{currentSurveyName}/{currentSurveyName}-tot.th\n"
_destination = fileName[:-3] + "\\Sources"
destination_path = os.path.join(_destination, os.path.basename(fileName))
shutil.move(fileName, destination_path)
bar(1)
# pd.set_option("display.max_rows", None)
# pd.set_option("display.max_columns", None)
# pd.set_option("display.width", None)
# print(f"{Colors.DEBUG}listStationSection : {Colors.ENDC}{listStationSection}")
#################################################################################################
# Gestion des equates
#################################################################################################
totdata +=f"\n"
_stationList = listStationSection.copy()
# On numérote les doublons de Survey_Name pour chaque StationName
_stationList['Survey_Number'] = _stationList.groupby('StationName').cumcount() + 1
# print(f"{Colors.DEBUG}_stationList : {Colors.ENDC}{_stationList}")
# On pivote le tableau pour que chaque Survey_Name devienne une colonne
tableau_pivot = _stationList.pivot(index='StationName', columns='Survey_Number', values='Survey_Name_01')
tableau_pivot.columns = [f'Survey_Name_{i}' for i in tableau_pivot.columns]
# print(f"{Colors.DEBUG}tableau_pivot : {Colors.ENDC}{tableau_pivot}{Colors.DEBUG} in {Colors.ENDC}{currentSurveyName}")
totdata +=f"\n\t## equates list:\n"
if 'Survey_Name_2' in tableau_pivot.columns:
# On réinitialise l'index pour avoir StationName comme colonne normale
tableau_pivot = tableau_pivot.reset_index()
tableau_equate = tableau_pivot[tableau_pivot['Survey_Name_2'].notna()]
log.info(f"Total 'equates' founds: {Colors.ENDC}{len(tableau_equate)}{Colors.INFO} in {Colors.ENDC}{currentSurveyName}")
# print(f"{Colors.DEBUG}tableau_equate : {Colors.ENDC}{tableau_equate}")
# print(f"{Colors.DEBUG}fixePoints : {Colors.ENDC}{fixPoints}{Colors.DEBUG} in {Colors.ENDC}{currentSurveyName}")
# Pour chaque ligne du tableau
for _, row in tableau_equate.iterrows():
station = row['StationName']
# On récupère tous les Survey_Name non vides (NaN exclus)
surveys = [row[col] for col in tableau_equate.columns if col.startswith('Survey_Name') and pd.notna(row[col])]
# Pour chaque paire unique (i < j), on écrit la ligne 'equate'
for i in range(len(surveys)):
for j in range(i + 1, len(surveys)):
totdata +=f"\tequate {station}@{surveys[i]}.{surveys[i]} {station}@{surveys[j]}.{surveys[j]}\n"
else:
log.info(f"No 'equates' found in {Colors.ENDC}{currentSurveyName}")
totdata +=f"\n\t## Maps list:\n\t{maps}input {SurveyTitle}-maps.th\n"
if totReadMeErrorDat == "" : totReadMeErrorDat += "\tThis file has no errors, perfect!\n"
config_vars = {
'fileName': SurveyTitle,
'caveName': SurveyTitle.replace("_", " "),
'Author': globalData.Author,
'Copyright': globalData.Copyright,
'Scale' : args.scale,
'Target' : "TARGET",
'mapComment' : globalData.mapComment,
'club' : globalData.club,
'thanksto' : globalData.thanksto,
'datat' : globalData.datat,
'wpage' : globalData.wpage,
'cs' : coordsyst if coordsyst != "" else globalData.cs,
'totData' : totdata,
'maps' :maps,
'plan': plan,
'XVIscale': globalData.XVIScale,
'extended': extended,
'configPath' : "",
'other_scraps_plan' : totMapsPlan,
'readMeList' : totReadMe,
'errorList' : totReadMeErrorDat,
'fixPointList' : totReadMeFixPoint,
'other_scraps_extended' : totMapsExtended,
'file_info' : f"# File generated by pyCreateTh.py version: {Version} date: {datetime.now().strftime("%Y.%m.%d-%H:%M:%S")}",
}
DEST_PATH = os.path.dirname(ENTRY_FILE) + '/' + SurveyTitle
update_template_files(DEST_PATH + '/template.thconfig', config_vars, DEST_PATH + '/' + SurveyTitle + '.thconfig')
update_template_files(DEST_PATH + '/template-tot.th', config_vars, DEST_PATH + '/' + SurveyTitle + '-tot.th')
update_template_files(DEST_PATH + '/template-maps.th', config_vars, DEST_PATH + '/' + SurveyTitle + '-maps.th')
#################################################################################################
# Final therion compilation #
#################################################################################################
if globalData.finalTherionExe == True:
FILE = DEST_PATH + '/' + SurveyTitle + '.thconfig'
t = compile_file(FILE, therion_path=globalData.therionPath)
threads.append(t)
for thread in threads: # Attendre que tous les threads se terminent
thread.join()
logfile = (DEST_PATH + '/therion.log').replace("\\", "/")
with open(logfile, 'r') as f:
content = f.read()
# print(content)
stat = get_stats_from_log(content)
if stat["length"] != 0.0 and stat["depth"] != 0.0 :
totReadMe += f"\tFinal compilation successful length: {stat["length"]} m, depth: {stat["depth"]} m\n"
log.info(f"Final compilation successful length: {Colors.ENDC}{stat["length"]}{Colors.INFO} m, depth: {Colors.ENDC}{stat["depth"]}{Colors.INFO} m")
else :
totReadMe += f"\tFinal compilation error, check log file\n"
log.error(f"Final compilation error, check log file")
config_vars['readMeList'] = totReadMe
update_template_files(DEST_PATH + '/template-readme.md', config_vars, DEST_PATH +'/' + SurveyTitle + '-readme.md')
return _stationList, SurveyTitle, totReadMeError, threads
#################################################################################################
# Convertit un fichier .dat en fichiers .th #
#################################################################################################
def dat_to_th_files (ENTRY_FILE, fixPoints = [], crs_wkt = "", CONFIG_PATH = "", totReadMeError = "", bar=None) :
"""
Convertit un fichier .dat en fichiers .th.
Convertit un fichier .dat en fichiers .th
Args:
ENTRY_FILE (str): Le chemin vers le fichier .dat d'entrée.
@@ -2486,7 +2943,7 @@ def dat_to_th_files (ENTRY_FILE, fixPoints = [], crs_wkt = "", CONFIG_PATH = "",
# Ajouter les données de la section à la liste
if len(section_data['DATA']) > 0 :
listStationSection, dfDATA = station_list(section_data, listStationSection, fixPoints, section_data['SURVEY_NAME'])
listStationSection, dfDATA = station_list_dat(section_data, listStationSection, fixPoints, section_data['SURVEY_NAME'])
section_data['STATION'] = listStationSection
data.append(section_data)
unique_id += 1
@@ -2615,12 +3072,11 @@ def dat_to_th_files (ENTRY_FILE, fixPoints = [], crs_wkt = "", CONFIG_PATH = "",
}
#################################################################################################
# gestion des DATA #
#################################################################################################
stationList, dfDATA = station_list(_line, stationList, fixPoints, currentSurveyName)
stationList, dfDATA = station_list_dat(_line, stationList, fixPoints, currentSurveyName)
headerData = dfDATA.iloc[0].tolist()
@@ -2746,9 +3202,6 @@ def dat_to_th_files (ENTRY_FILE, fixPoints = [], crs_wkt = "", CONFIG_PATH = "",
totdata +=f"\n"
_stationList = stationList.copy()
# On numérote les doublons de Survey_Name pour chaque StationName
@@ -2770,7 +3223,7 @@ def dat_to_th_files (ENTRY_FILE, fixPoints = [], crs_wkt = "", CONFIG_PATH = "",
tableau_pivot = tableau_pivot.reset_index()
tableau_equate = tableau_pivot[tableau_pivot['Survey_Name_2'].notna()]
log.info(f"Total 'equates' founds: {Colors.ENDC}{len(tableau_equate)}{Colors.INFO} in {Colors.ENDC}{shortCurentFile}")
log.info(f"Total '{Colors.ENDC}equates{Colors.INFO}' founds : {Colors.ENDC}{len(tableau_equate)}{Colors.INFO} in {Colors.ENDC}{shortCurentFile}")
# print(tableau_equate)
# print(f"fixePoints : {Colors.ENDC}{fixed_names}{Colors.INFO} in {Colors.ENDC}{ENTRY_FILE}")
@@ -2786,11 +3239,11 @@ def dat_to_th_files (ENTRY_FILE, fixPoints = [], crs_wkt = "", CONFIG_PATH = "",
for j in range(i + 1, len(surveys)):
totdata +=f"\tequate {station}@{surveys[i]}.{surveys[i]} {station}@{surveys[j]}.{surveys[j]}\n"
else:
log.info(f"No 'equates' found in {Colors.ENDC}{ENTRY_FILE}")
log.info(f"No '{Colors.ENDC}equates{Colors.INFO}' found in {Colors.ENDC}{ENTRY_FILE}")
totdata +=f"\n\t## Maps list:\n\t{maps}input {SurveyTitle}-maps.th\n"
if totReadMeErrorDat == "" : totReadMeErrorDat += "\tAny error in this file, that's perfect !\n"
if totReadMeErrorDat == "" : totReadMeErrorDat += "\tNo errors in the file, that's excellent !\n"
config_vars = {
'fileName': SurveyTitle,
@@ -2824,17 +3277,38 @@ def dat_to_th_files (ENTRY_FILE, fixPoints = [], crs_wkt = "", CONFIG_PATH = "",
update_template_files(DEST_PATH + '/template.thconfig', config_vars, DEST_PATH + '/' + SurveyTitle + '.thconfig')
update_template_files(DEST_PATH + '/template-tot.th', config_vars, DEST_PATH + '/' + SurveyTitle + '-tot.th')
update_template_files(DEST_PATH + '/template-maps.th', config_vars, DEST_PATH + '/' + SurveyTitle + '-maps.th')
update_template_files(DEST_PATH + '/template-readme.md', config_vars, DEST_PATH +'/' + SurveyTitle + '-readme.md')
#################################################################################################
# Final therion compilation #
#################################################################################################
if globalData.finalTherionExe == True:
if globalData.finalTherionExe == True :
FILE = DEST_PATH + '/' + SurveyTitle + '.thconfig'
t = compile_file(FILE, therion_path=globalData.therionPath)
threads.append(t)
for thread in threads: # Attendre que tous les threads se terminent
thread.join()
logfile = (DEST_PATH + '/therion.log').replace("\\", "/")
with open(logfile, 'r') as f:
content = f.read()
# print(content)
stat = get_stats_from_log(content)
if stat["length"] != 0.0 and stat["depth"] != 0.0 :
totReadMe += f"\tFinal compilation successful length: {stat["length"]} m, depth: {stat["depth"]} m\n"
log.info(f"Final compilation successful length: {Colors.ENDC}{stat["length"]}{Colors.INFO} m, depth: {Colors.ENDC}{stat["depth"]}{Colors.INFO} m")
else :
totReadMe += f"\tFinal compilation error, check log file\n"
log.error(f"Final compilation error, check log file")
config_vars['readMeList'] = totReadMe
update_template_files(DEST_PATH + '/template-readme.md', config_vars, DEST_PATH +'/' + SurveyTitle + '-readme.md')
stationList["Survey_Name_02"] = SurveyTitle
totReadMeError += totReadMeErrorDat
@@ -2860,6 +3334,7 @@ def wait_until_file_is_released(filepath, timeout=30):
try:
with open(filepath, "rb"):
return True
except PermissionError:
if time.time() - start > timeout:
log.Error(f"Timeout: The file remains locked after {Colors.ENDC}{timeout}{Colors.ERROR} secondes: {Colors.ENDC}{filepath}")
@@ -2874,6 +3349,7 @@ if __name__ == u'__main__':
start_time = datetime.now()
threads = []
fileTitle = ""
_fileTitle = ""
#################################################################################################
# Parse arguments #
@@ -2921,7 +3397,7 @@ if __name__ == u'__main__':
#################################################################################################
# titre #
#################################################################################################
titre_largeur = 150
titre_largeur = 160
bordure = "#" * titre_largeur + Colors.ENDC
ansi_escape = re.compile(r'\x1b\[[0-9;]*m')
@@ -3012,15 +3488,11 @@ if __name__ == u'__main__':
log.critical(f"The folder {Colors.ENDC}{SurveyTitleDat}{Colors.ERROR}{Colors.BOLD}, all ready exist : update mode is not possible for mak files")
exit(0)
with alive_bar(
QtySections,
title=f"{Colors.GREEN}Surveys progress: {Colors.BLUE}",
length = 20,
enrich_print=False,
with alive_bar( QtySections, title=f"{Colors.GREEN}Dat to Th conversion progress: {Colors.BLUE}", length = 20, enrich_print=False,
stats=True, # Désactive les stats par défaut pour plus de lisibilité
elapsed=True, # Optionnel : masque le temps écoulé
monitor=True, # Optionnel : masque les métriques (ex: "eta")
bar="smooth" # Style de la barre (autres options: "smooth", "classic", "blocks")
bar="smooth" # Style de la barre (autres options: "smooth", "classic", "blocks")
) as bar:
with redirect_stdout(sys.__stdout__):
for i in range(1):
@@ -3038,47 +3510,84 @@ if __name__ == u'__main__':
elif args.file[-3:].lower() == "tro" :
SrcFile = abspath(args.file)
DestFile = SrcFile[:-4] # + "Th"
DestFile = SrcFile[:-4] + ".th"
source_content, val, encodage = load_text_file_utf8(SrcFile, os.path.basename(SrcFile))
fileTitle, coordinates, coordsyst, fle_th_fnme = convert_tro(
fle_tro_fnme = SrcFile,
fle_tro_encoding= encodage,
fle_th_fnme = DestFile,
cavename = None,
icomments = True,
icoupe = False,
istructure = False,
thlang = None,
Errorfiles = False
)
entrance, fileTitle, coordinates, coordsyst, fle_th_fnme = convert_tro( fle_tro_fnme = SrcFile, fle_tro_encoding= encodage,
fle_th_fnme = DestFile, cavename = None, icomments = True, icoupe = False, istructure = False,
thlang = None, Errorfiles = False )
if coordsyst == None :
log.critical(f"The VisualTopo file {Colors.ENDC}{SrcFile}{Colors.ERROR}{Colors.BOLD}, have no coordinate system define. Correct it and try again")
exit(0)
content, val, encodage = load_text_file_utf8(fle_th_fnme, os.path.basename(fle_th_fnme))
if encodage != "utf-8":
with open(str(fle_th_fnme), "w+", encoding="utf-8") as f:
f.write(content)
with open(fle_th_fnme, 'a', encoding='utf-8') as file:
file.write("\n\n")
for line in source_content.splitlines():
file.write(f"# {line}\n")
if globalData.parse_tro_files_by_explo :
_centerlines = parse_therion_centerline(content)
centerlines = regroupe_date(_centerlines)
with alive_bar( len(centerlines) + 1 , title=f"{Colors.GREEN}Tro to Th conversion progress: {Colors.BLUE}", length = 20, enrich_print=False,
stats=True, # Désactive les stats par défaut pour plus de lisibilité
elapsed=True, # Optionnel : masque le temps écoulé
monitor=True, # Optionnel : masque les métriques (ex: "eta")
bar="smooth" # Style de la barre (autres options: "smooth", "classic", "blocks")
) as bar:
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(SrcFile)}{Colors.ERROR}, error: {Colors.ENDC}{globalData.error_count}")
else :
bar.text(f"{Colors.INFO}file: {Colors.ENDC}{os.path.basename(SrcFile)}")
stationList, fileTitle, totReadMeError, thread2 = tro_to_th_files (ENTRY_FILE = SrcFile ,
centerlines = centerlines,
entrance = entrance,
fileTitle = fileTitle,
coordinates = coordinates,
coordsyst = coordsyst,
fle_th_fnme = fle_th_fnme,
CONFIG_PATH = "",
totReadMeError = "",
bar = bar)
threads += thread2
bar()
else :
if encodage != "utf-8":
with open(str(fle_th_fnme), "w+", encoding="utf-8") as f:
f.write(content)
with open(fle_th_fnme, 'a', encoding='utf-8') as file: # Données originales en commentaire dans le fichier th
file.write(f"\n\n#############################################################################################")
file.write(f"\n# Originals data file : {args.file}")
if globalData.error_count == 0 :
file.write(f"\n# Conversion with pyCreateTh version {Version}, the {datetime.now().strftime("%Y.%m.%d %H:%M:%S")}, without error")
else :
file.write(f"\n# Conversion with pyCreateTh version {Version}, the {datetime.now().strftime("%Y.%m.%d %H:%M:%S")}, with {globalData.error_count} error(s)")
flagErrorCompile, stat, totReadMeError, thread2 = create_th_folders(
ENTRY_FILE = fle_th_fnme,
TARGET = None,
PROJECTION= args.proj,
SCALE = args.scale,
UPDATE = args.update,
CONFIG_PATH = "")
threads += thread2
fileTitle = sanitize_filename(os.path.basename(fle_th_fnme)[:-3])
file.write(f"\n#############################################################################################\n\n")
for line in source_content.splitlines():
file.write(f"# {line}\n")
flagErrorCompile, stat, totReadMeError, thread2 = create_th_folders( ENTRY_FILE = fle_th_fnme, TARGET = None, PROJECTION= args.proj,
SCALE = args.scale, UPDATE = args.update, CONFIG_PATH = "")
threads += thread2
fileTitle = sanitize_filename(os.path.basename(fle_th_fnme)[:-3])
if os.path.isfile(fle_th_fnme):
os.remove(fle_th_fnme)
#################################################################################################
# Autres types #
#################################################################################################
else :
log.error(f"file {Colors.ENDC}{safe_relpath(args.file)}{Colors.ERROR} not yet supported")
globalData.error_count += 1
@@ -3095,13 +3604,13 @@ if __name__ == u'__main__':
duration = (datetime.now() - start_time).total_seconds()
if globalData.error_count == 0 :
log.info(f"All files processed successfully in {Colors.ENDC}{duration:.2f}{Colors.INFO} secondes, without errors")
log.info(f"All files processed successfully in {Colors.ENDC}{duration:.2f}{Colors.INFO} secondes, without error")
else :
log.error(f"There were {Colors.ENDC}{globalData.error_count}{Colors.ERROR} errors during {Colors.ENDC}{duration:.2f}{Colors.ERROR} secondes, check the log file: {Colors.ENDC}{os.path.basename(output_log)}")
wait_until_file_is_released(output_log)
release_log_file(log)
release_log_file(log)
# Supprimer le fichier cible si il existe déjà
if os.path.isfile(destination_file):
@@ -3109,4 +3618,7 @@ if __name__ == u'__main__':
if not args.update :
shutil.move(output_log, destination_path)
if os.path.exists(fileTitle):
os.remove(fileTitle)
@@ -19,6 +19,7 @@
"datathwritetools",
"ecart",
"ENDC",
"endcenterline",
"endlayout",
"endscrap",
"etrs",
-6
View File
@@ -1,6 +0,0 @@
therion 6.3.4 (2025-04-08)
- using Proj 9.5.1, compiled against 9.5.1
initialization file: C:\Program Files\Therion/therion.ini
reading ... done
C:\Program Files\Therion\therion.exe: error -- can't open file for input -- L5.thconfig
Press ENTER to exit!