pyCreateTh

This commit is contained in:
Alex38Lyon
2025-06-16 07:37:02 +02:00
parent 231a5a22f3
commit 6882d52675
77 changed files with 43343 additions and 35074 deletions
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because it is too large Load Diff
+10
View File
@@ -0,0 +1,10 @@
set xth(ctrl,cp,datlist) {}
xth_cp_data_tree_insert 1 {} 0 "" "" "length: 0.00m (surface 0.00m, duplicated 0.00m)"
xth_cp_data_tree_create
set xth(ctrl,cp,maplist) {}
xth_cp_map_tree_insert projection 0 p1 {} 0 plan {} {} {}
xth_cp_map_tree_create
xth_cp_comp_stat 0 0
set xth(th_exit_state) 2
set xth(th_exit_number) 1749133569
File diff suppressed because one or more lines are too long
Binary file not shown.

After

Width:  |  Height:  |  Size: 8.4 MiB

+76
View File
@@ -0,0 +1,76 @@
encoding utf-8
# Fichier thCongig poour créer un fichier lox de la en UTM30ED50
# Copyright (C) ARSIP 2025 https://www.arsip.fr/
# Auteur : Alexandre Pont <alexandre.pont***@***yahoo.fr>
# Fichiers / Organisation dossiers, d'après Xavier Robert
# This work is under the Creative Commons Attribution-NonCommercial-NoDerivatives License:
# <http://creativecommons.org/licenses/by-nc-nd/4.0/>
# INTRO
# le signe "#" en début de ligne signifie que la ligne est commentée. Elle ne
# sera donc pas lue lors de la compilation.
# Dans ce fichier on met les specifications generales, à savoir
# dans quel fichier sont les donnees topo, l'aspect que l'on veut
# donner aux topos imprimées (layout) et ce que l'on
# veut comme résultat : map, ou atlas ou 3D ou donnees en format SQL
# Alors, on peut fractionner ce fichier en trois parts:
# - source, pour specifier les fichiers ou sont les données topo/dessin
# - layout, pour specifier la composition du document à imprimer
# - export: map, atlas, etc
# 1-SOURCES
# La ligne source spécifie le fichier ou sont les donnees topo
# jb.th". (Au fichier "jb.th" il faudra avoir une ligne
# "input "nomducavite.th2" pour specifier le fichier ou se trouvent
# les donnees du dessin, comme ça, ce fichier thconfig appellera
# "jb.th" et a leur tour, "jb.th" appellera
# "jb-dessin.th2")
# source Synthese-PSM_LARRA-tot.th
# pour le MNT avec une résolution spaciale de 1" STRM10
source DEM_SRTM30_UTM30_PSM.th
# Add Coordinates
#input Legendes/entrances_coordinates.th
# Add config file
# 2-LAYOUT
# Ici, on peut specifier des choses comme les symboles à utiliser (UIS, etc)
# ou imprimer des explications des symboles
# 3-EXPORT
# Export des xvi pour le dessin si besoin
#export map -fmt xvi -layout xviexport -o Data/Complexe_Lonne_Peyret-Bourrugues-map.xvi
#export map -proj extended -layout xviexport -fmt xvi -o Data/Complexe_Lonne_Peyret-Bourrugues-coupe.xvi
# Export des fichiers pdf, plan et coupe.
# ATTENTION, la topo étant énorme, il faut mettre l'option ne traçant pas la centerline !
# export map -projection plan -fmt pdf -layout my_layout -o Outputs/Synthese-PSM_LARRA-Plan.pdf
# export map -projection extended -fmt pdf -layout layout-coupe -o Outputs/GL102_Puits-Coupe.pdf
# Export du fichier 3d pour Loch
cs EPSG:23030 # UTTM30 ED50
export model -enable all -o Surface_PSM_LARRA_UTM30ED50.lox
# Export de la database sql
# export des statistiques de l'ensemble de la cavité
# Attention, les points de départ et d'arrivée de chaque centreline correspondent
# au second point et à l'avant dernier point de la série.
# Export des fichiers ESRI
# Export des fichiers kml
Binary file not shown.
+31
View File
@@ -0,0 +1,31 @@
therion 6.3.3 (2025-01-06)
- using Proj 9.4.1, compiled against 9.4.1
initialization file: C:\Program Files\Therion/therion.ini
reading ... done
configuration file: Surface_PSM_LARRA.thconfig
reading ... done
reading source files ... done
preprocessing database ... done
output coordinate system: EPSG:23030
meridian convergence (deg): -1.5117
scanning centreline tree ... done
searching for centerline loops ... done
calculating station coordinates ... done
processing survey data ... no data.
calculating basic statistics ... done
processing extended elevation ... done
processing references ... done
selecting export objects ... done
writing Surface_PSM_LARRA_UTM30ED50.lox ...
processing projection plan ... done
done
writing xtherion file ... done
compilation time: 1 sec
############# CRS transformations chosen by PROJ ###############
Area of Use (AoU): (-0.902, 42.840) (-0.601, 43.050)
[UTM30 → LAT-LONG] AoU: [no] transformation: [Inverse of UTM zone 30N + axis order change (2D)] definition: [proj=pipeline step inv proj=utm zone=30 ellps=WGS84 step proj=unitconvert xy_in=rad xy_out=deg] accuracy: [0.000 m]
[UTM30 → EPSG:23030] AoU: [yes] transformation: [Inverse of UTM zone 30N + Inverse of ED50 to WGS 84 (41) + UTM zone 30N] definition: [proj=pipeline step inv proj=utm zone=30 ellps=WGS84 step inv proj=hgridshift grids=es_ign_SPED2ETV2.tif step proj=utm zone=30 ellps=intl] accuracy: [1.000 m]
[EPSG:4326 → EPSG:23030] AoU: [yes] transformation: [axis order change (2D) + Inverse of ED50 to WGS 84 (41) + UTM zone 30N] definition: [proj=pipeline step proj=unitconvert xy_in=deg xy_out=rad step inv proj=hgridshift grids=es_ign_SPED2ETV2.tif step proj=utm zone=30 ellps=intl] accuracy: [1.000 m]
[EPSG:23030 → EPSG:4326] AoU: [yes] transformation: [Inverse of UTM zone 30N + ED50 to WGS 84 (41) + axis order change (2D)] definition: [proj=pipeline step inv proj=utm zone=30 ellps=intl step proj=hgridshift grids=es_ign_SPED2ETV2.tif step proj=unitconvert xy_in=rad xy_out=deg] accuracy: [1.000 m]
########## end of CRS transformations chosen by PROJ ###########
+62
View File
@@ -0,0 +1,62 @@
{
"cSpell.words": [
"annee",
"ansi",
"australiangeodetic",
"australiangeodeticdatum",
"backclino",
"backcompass",
"beijing",
"cavename",
"clarke",
"clino",
"CLINO",
"cumcount",
"datat",
"depmatch",
"depthre",
"downcasting",
"drawnexemptextendedre",
"drawnexemptplanre",
"drawnexemptre",
"drawnre",
"ecart",
"ENDC",
"endlayout",
"endscrap",
"endsurvey",
"equats",
"etrs",
"ETRS",
"european",
"explo",
"formated",
"geocentricdatumofaustralia",
"geocentricofaustralia",
"Geophysicaya",
"isin",
"Koef",
"lengthre",
"lenmatch",
"levelname",
"levelno",
"migovec",
"NODRAW",
"northamerican",
"northamericandatum",
"nouvelletriangulationfrançaise",
"pulkovo",
"sinfo",
"sirgas",
"southamerican",
"southamericandatum",
"syscoord",
"tendcenterline",
"thanksto",
"thconfig",
"therion",
"totdata",
"wpage",
"XTHERION"
]
}
+67
View File
@@ -0,0 +1,67 @@
import logging
import sys
import re
# Couleurs ANSI par niveau de log
COLOR_CODES = {
logging.DEBUG: "\033[94m", # Bleu
logging.INFO: "\033[92m", # Vert
logging.WARNING: "\033[95m",
logging.ERROR: "\033[91m", # Rouge
logging.CRITICAL: "\033[1;91m", # Rouge vif
}
RESET = "\033[0m"
# Supprime les codes ANSI (pour l'écriture dans les fichiers)
def strip_ansi_codes(text):
ansi_escape = re.compile(r'\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])')
return ansi_escape.sub('', text)
# Formatter pour la console avec couleurs
class ConsoleFormatter(logging.Formatter):
def format(self, record):
color = COLOR_CODES.get(record.levelno, "")
message = super().format(record)
return f"{color}{message}{RESET}"
# Formatter pour le fichier avec "!!!" sur les erreurs
class FileFormatter(logging.Formatter):
def format(self, record):
clean_msg = strip_ansi_codes(record.getMessage())
prefix = "!!! " if record.levelno >= logging.ERROR else ""
record_copy = logging.LogRecord(
name=record.name,
level=record.levelno,
pathname=record.pathname,
lineno=record.lineno,
msg=f"{prefix}{clean_msg}",
args=(),
exc_info=record.exc_info,
func=record.funcName,
sinfo=record.stack_info
)
return super().format(record_copy)
# Fonction de configuration du logger
def setup_logger(logfile="app.log", debug_log=False):
logger = logging.getLogger("Logger")
logger.setLevel(logging.DEBUG)
logger.handlers.clear()
min_level = logging.DEBUG if debug_log else logging.INFO
# Console handler
console_handler = logging.StreamHandler(sys.stdout)
console_handler.setLevel(min_level)
console_formatter = ConsoleFormatter("%(levelname)s: %(message)s")
console_handler.setFormatter(console_formatter)
logger.addHandler(console_handler)
# File handler
file_handler = logging.FileHandler(logfile, encoding="utf-8")
file_handler.setLevel(min_level)
file_formatter = FileFormatter("%(asctime)s - %(levelname)s - %(message)s")
file_handler.setFormatter(file_formatter)
logger.addHandler(file_handler)
return logger
+243
View File
@@ -0,0 +1,243 @@
import re
from os.path import dirname, abspath, join
import argparse
file_path_reg = r"(?:\n|^)\s*###filepath:(.*)"
input_reg = r"(?:\n|^)\s*(?:input|source)\s+\"?([^\s\"]+)?"
survey_reg = r"(?:\n|^)\s*survey\s+(\S+)"
end_survey_reg = r"(?:\n|^)\s*endsurvey"
scrap_reg = r"(?:\n|^)\s*scrap\s+(\S+)"
end_scrap_reg = r"(?:\n|^)\s*endscrap"
projection_reg = r"(?:\n|^).*-projection\s+(\S+)"
drawnre = re.compile(r".*line wall")
drawnexemptre = re.compile(r".*NODRAW")
drawnexemptplanre = re.compile(r".*NODRAW PLAN")
drawnexemptextendedre = re.compile(r".*NODRAW EE")
class NoSurveysFoundException(Exception):
pass
class MultipleSurveyFoundException(Exception):
pass
class Scrap:
id = None
projection = None
data = None
parent = None
def __init__(self, id, parent, projection):
self.id = id
self.projection = projection
self.parent = parent
def is_drawn(self):
if not self.data:
return False
for line in self.data:
match = drawnre.match(line)
if match:
return True
return False
class Survey:
parent = None
file_path = None
id = None
children = []
data = None
scraps = []
plan_drawn_override = False
extended_drawn_override = False
def __init__(self, id, parent, file_path):
self.id = id
self.parent = parent
self.file_path = file_path
@property
def therion_id(self):
if len(self.id) == 1:
return self.id[0]
return "{}@{}".format(self.name, self.namespace)
@property
def name(self):
return self.id[-1]
@property
def namespace(self):
return ".".join(list(reversed(self.id[0:-1])))
def data(self, data):
self._data = data
self.scraps = Survey.parse(self)
def parse(self):
scraps = []
scrap = None
data = []
for index, line in enumerate(self.data):
match = re.match(scrap_reg, line)
if match:
id = self.id + [match.group(1)]
projection = "plan"
match = re.match(projection_reg, line)
if match:
projection = match.group(1)
scrap = Scrap(id[:], self, projection)
scraps = scraps + [scrap]
data = [line]
continue
match = re.match(end_scrap_reg, line)
if match:
id = self.id
data = data + [line]
scrap.data = data[:]
data = []
continue
# Exempt drawing
match = drawnexemptplanre.match(line)
if match:
self.plan_drawn_override = True
match = drawnexemptextendedre.match(line)
if match:
self.extended_drawn_override = True
match = drawnexemptre.match(line)
if match:
self.plan_drawn_override = True
self.extended_drawn_override = True
data = data + [line]
self.scraps = scraps
class SurveyLoader:
_data = None
survey = None
surveys = {}
@property
def surveys_list(self):
return list(self.surveys.values())
@property
def base_surveys(self):
return [s for s in self.surveys_list if len(s.children) == 0]
@staticmethod
def load(file_path):
with open(file_path, "r", encoding="utf-8") as f:
data = f.read()
lines = []
for line in data.splitlines():
if not line.strip():
continue
# if line.lstrip().startswith("#"):
# continue
match = re.match(input_reg, line)
if match:
new_file_path = abspath(join(dirname(file_path), match.group(1)))
lines = lines + ["###filepath:{}".format(new_file_path)]
lines = lines + ["\t{}".format(l) for l in SurveyLoader.load(new_file_path)]
lines = lines + ["###filepath:{}".format(file_path)]
else:
lines.append(line.strip())
return lines
@staticmethod
def parse(lines, orig_file_path=None):
surveys = {}
id = []
file_path = orig_file_path
parent = None
survey = None
data = []
for index, line in enumerate(lines):
match = re.match(file_path_reg, line)
if match:
file_path = match.group(1)
continue
match = re.match(survey_reg, line)
if match:
id = id + [match.group(1)]
parent = survey
survey = Survey(id[:], parent, file_path)
surveys[".".join(id[:])] = survey
if parent:
parent.data = data[:]
parent.children = parent.children + [survey]
data = [line]
continue
match = re.match(end_survey_reg, line)
if match:
popped = id.pop()
data = data + [line]
survey.data = data[:]
if len(survey.children) == 0:
survey.parse()
if not survey.parent:
return survey, surveys
parent.data = parent.data + data
data = parent.data[:]
parent = survey.parent
survey = parent
continue
data = data + [line]
return parent, surveys
def __init__(self, file_path):
# print(f"\033[32mDebug SurveyLoader.load : \033[0m {file_path}")
self._data = SurveyLoader.load(file_path)
survey, surveys = SurveyLoader.parse(self._data, file_path)
self.survey = survey
self.surveys = surveys
def get_survey_by_id(self, therion_id):
id = []
if "@" in therion_id:
parts = therion_id.split("@")
id = list(reversed(parts[1].split("."))) + [parts[0]]
else:
id = list(reversed(therion_id.split(".")))
key = ".".join(id)
if key in self.surveys:
return self.surveys[key]
else:
potential_key = [k for k in self.surveys.keys() if k.endswith(".{}".format(key))]
if len(potential_key) == 1:
return self.surveys[potential_key[0]]
potential_keys = [k for k in self.surveys.keys() if key in k]
if len(potential_keys) == 1:
return self.surveys[potential_keys[0]]
elif len(potential_keys) > 1:
raise MultipleSurveyFoundException("Multiple surveys were found with that key:\n\t{}".format("\n\t".join(potential_keys)))
return None
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Parse a survey")
parser.add_argument(
"survey_file",
help='The survey file (*.th) to work from. e.g. "data/system_migovec.th"',
)
parser.add_argument(
"survey_selector",
help='The selector for the survey to produce a scrap for. e.g. "roundpond@vrtnarija.vrtnarija_vilinska.system_migovec"',
)
args = parser.parse_args()
entrypoint = abspath(args.survey_file)
loader = SurveyLoader(entrypoint)
print(loader.get_survey_by_id(args.survey_selector))
+279
View File
@@ -0,0 +1,279 @@
import tempfile
import shutil
import os
from os.path import join
import subprocess
import re
import logging
import threading
log = logging.getLogger("Logger")
#################################################################################################
# Codes de couleur ANSI
class Colors:
BLACK = '\033[90m'
RED = '\033[91m'
GREEN = '\033[92m'
YELLOW = '\033[93m'
BLUE = '\033[94m'
MAGENTA = '\033[95m'
CYAN = '\033[96m'
WHITE = '\033[97m'
ERROR = '\033[91m'
WARNING = '\033[95m'
HEADER = '\033[96m'
DEBUG = '\033[94m' # Bleu
INFO = '\033[92m' # Vert
CRITICAL = '\033[1;91m', # Rouge vif
ENDC = '\033[0m'
BOLD = '\033[1m'
UNDERLINE = '\033[4m'
#################################################################################################
def compile_template(template, template_args, **kwargs):
global error_count
try :
logfile = ""
tmpdir = tempfile.mkdtemp()
config = template.format(**template_args, tmpdir=tmpdir.replace("\\", "/"))
log.debug(f"{config}\n")
config_file = join(tmpdir, "config.thconfig")
log_file = join(tmpdir, "log.log")
therion_path = kwargs["therion_path"] if "therion_path" in kwargs else "therion"
with open(config_file, mode="w+", encoding="utf-8") as tmp:
with open(log_file, mode="w+") as tmp2:
tmp.write(config)
tmp.flush()
subprocess.check_output('''"{}" "{}" -l "{}"'''.format(therion_path, config_file, log_file), shell=True, )
tmp2.flush()
logfile = tmp2.read()
if kwargs["cleanup"]:
shutil.rmtree(tmpdir)
log.debug("\n" )
return logfile, tmpdir
except Exception as e:
log.error(f"Therion template compilation error: {Colors.ENDC}{e}")
error_count += 1
def compile_template2(template, template_args, **kwargs):
global error_count
logfile = ""
tmpdir = None
try:
tmpdir = tempfile.mkdtemp()
config = template.format(**template_args, tmpdir=tmpdir.replace("\\", "/"))
log.debug(f"{config}\n")
config_file = join(tmpdir, "config.thconfig")
log_file = join(tmpdir, "log.log")
therion_path = kwargs.get("therion_path", "therion")
# Écriture des fichiers config + log
with open(config_file, "w", encoding="utf-8") as tmp:
tmp.write(config)
tmp.flush()
# Exécution de Therion
result = subprocess.run(
[therion_path, config_file, "-l", log_file],
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
text=True, # Décode automatiquement en UTF-8 (avec fallback ci-dessous)
timeout=kwargs.get("timeout", 60),
errors="replace" # Remplace caractères invalides (évite UnicodeDecodeError)
)
# Lecture du log (en mode tolérant)
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:
log.error(f"Therion compilation failed with return code {Colors.ENDC}{result.returncode} {Colors.ERROR}{result.stdout}")
error_count += 1
else:
log.info(f"Therion compilation successful")
return logfile, tmpdir
except subprocess.TimeoutExpired:
log.error(f"Therion process timed out and was terminated")
error_count += 1
return "Therion timeout", tmpdir
except Exception as e:
log.error(f"Therion template compilation error: {Colors.ENDC}{e}")
error_count += 1
return str(e), tmpdir
finally:
if kwargs.get("cleanup", True) and tmpdir:
try:
shutil.rmtree(tmpdir)
except Exception as cleanup_err:
log.warning(f"Could not delete temp directory: {Colors.ENDC}{cleanup_err}")
#################################################################################################
def compile_file(filename, **kwargs):
global error_count
try:
tmpdir = os.path.dirname(filename)
log_file = join(tmpdir, "therion.log").replace("\\", "/")
therion_path = kwargs["therion_path"] if "therion_path" in kwargs else "therion"
process = subprocess.Popen(
[therion_path, filename, "-l", log_file],
cwd=tmpdir,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT, # fusion stdout + stderr
universal_newlines=True, # décodage automatique en texte
bufsize=1 # ligne par ligne
)
log.info(f"Start therion compilation file : {Colors.ENDC}~\\{os.path.relpath(filename)}")
# Lecture en temps réel
for line in process.stdout:
line = line.rstrip()
lower_line = line.lower()
if "error" in lower_line:
log.error(f" [Therion_Compile] {Colors.ENDC}{line}")
elif "warning" in lower_line:
log.warning(f" [Therion_Compile] {Colors.ENDC}{line}")
else:
log.debug(f" [Therion_Compile] {Colors.ENDC}{line}")
process.wait()
# Si la commande échoue, result.returncode sera non nul
if process.returncode != 0:
# Affichage des erreurs et de la sortie standard
log.error(f"Error during Therion compilation, stderr : \n{Colors.ENDC}{process.stderr.decode()}")
error_count += 1
log.info(f"Therion file : {Colors.ENDC}~\\{os.path.relpath(filename)}{Colors.GREEN} succeeded")
except Exception as e:
log.error(f"Therion file {Colors.ENDC}~\\{os.path.relpath(filename, os.path.expanduser('~'))}{Colors.ERROR} compilation error: {Colors.ENDC}{e}")
error_count += 1
def compile_file2(filename, **kwargs):
global error_count
tmpdir = os.path.dirname(filename)
log_file = join(tmpdir, "therion.log").replace("\\", "/")
therion_path = kwargs.get("therion_path", "therion")
timeout = kwargs.get("timeout", 60) # seconds
log.info(f"Start therion compilation file : {Colors.WHITE}{filename}")
try:
# Lancement du processus Therion
process = subprocess.Popen(
[therion_path, filename, "-l", log_file],
cwd=tmpdir,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
universal_newlines=True,
bufsize=1
)
# Fonction de lecture en temps réel (dans un thread séparé)
def read_output(proc):
try:
for line in proc.stdout:
line = line.rstrip()
lower_line = line.lower()
if "error" in lower_line:
log.error(f"[Therion_Compile] {Colors.ENDC}{line}")
elif "warning" in lower_line:
log.warning(f" [Therion_Compile] {Colors.ENDC}{line}")
else:
log.debug(f" [Therion_Compile] {Colors.ENDC}{line}")
except Exception as e:
log.warning(f"Reading Therion output: {Colors.ENDC}{e}")
# Démarrage du thread de lecture
output_thread = threading.Thread(target=read_output, args=(process,))
output_thread.start()
# Attente avec timeout
output_thread.join(timeout)
if output_thread.is_alive():
log.error(f"Therion compilation timed out after {Colors.ENDC}{timeout}{Colors.ERROR} seconds. Killing process...")
error_count += 1
process.kill()
output_thread.join()
process.wait()
# Vérification du code de retour
if process.returncode != 0:
log.error(f"Therion returned error code {Colors.ENDC}{process.returncode}")
error_count += 1
else:
log.info(f"Therion file : {Colors.ENDC}~\\{os.path.relpath(filename)}{Colors.GREEN} compilation succeeded")
except Exception as e:
log.error(f"Therion file {Colors.ENDC}~\\{os.path.relpath(filename)}{Colors.ERROR} compilation error: {Colors.ENDC}{e}")
error_count += 1
#################################################################################################
def compile_file_th(filepath, **kwargs):
template = """source {filepath}
layout test
scale 1 500
endlayout
"""
template_args = {"filepath": filepath}
logs, _ = compile_template(template, template_args, cleanup=True, **kwargs)
return logs
#################################################################################################
# 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")
#################################################################################################
def get_stats_from_log(log):
lenmatch = lengthre.findall(log)
depmatch = depthre.findall(log)
if len(lenmatch) == 1 and len(depmatch) == 1:
return {"length": lenmatch[0], "depth": depmatch[0]}
return {"length": 0, "depth": 0}
#################################################################################################
syscoord = re.compile(r".*output coordinate system: \s*(\S+)")
#################################################################################################
def get_syscoord_from_log(log):
lenmatch = syscoord.findall(log)
if len(lenmatch) == 1:
return {"syscoord": lenmatch[0]}
return {"syscoord": 0}
+9
View File
@@ -0,0 +1,9 @@
Scripts pour Therion
====================
pyMaktoTh
---------
En Java : https://github.com/rogerschuster/compass2therion
Format des fichiers compass : https://fountainware.com/compass/Documents/FileFormats/FileFormats.htm
@@ -0,0 +1,2 @@
Folder where Therion outputs are exported
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,14 @@
encoding utf-8
{file_info}
{Copyright}
map MP-{fileName}-Plan-tot -title "{fileName}"
{other_scraps_plan}
endmap
map MC-{fileName}-Extended-tot -title "{fileName}"
{other_scraps_extended}
endmap
@@ -0,0 +1,9 @@
encoding utf-8
{file_info}
{Copyright}
to add this survey in a main survey add in your -tot.th file:
input Data/{fileName}/{fileName}-tot.th
equate
@@ -0,0 +1,8 @@
encoding utf-8
{file_info}
{Copyright}
survey {fileName} -title "{fileName}"
{totData}
endsurvey
@@ -0,0 +1,389 @@
encoding utf-8
{file_info}
###############################################################################################
{Copyright}
###############################################################################################
## INTRO
## Le signe "#" en début de ligne signifie que la ligne est commentée. Elle ne
## sera donc pas lue lors de la compilation.
## Dans ce fichier on met les specifications generales, à savoir
## dans quel fichier sont les donnees topo, l'aspect que l'on veut
## donner aux topos imprimées (layout) et ce que l'on
## veut comme résultat : map, ou atlas ou 3D ou donnees en format SQL
## Alors, on peut fractionner ce fichier en trois parts:
## - source, pour specifier les fichiers ou sont les données topo/dessin
## - layout, pour specifier la composition du document à imprimer
## - export: map, atlas, etc
###############################################################################################
## 1-SOURCES
###############################################################################################
## La ligne source spécifie le fichier ou sont les donnees topo
## jb.th". (Au fichier "jb.th" il faudra avoir une ligne
## "input "nomducavite.th2" pour specifier le fichier ou se trouvent
## les donnees du dessin, comme ça, ce fichier thconfig appellera
## "jb.th" et a leur tour, "jb.th" appellera
## "jb-dessin.th2")
source {fileName}-tot.th
## Add config file
input config.thc
###############################################################################################
## 2-LAYOUT
###############################################################################################
## Ici, on peut specifier des choses comme les symboles à utiliser (UIS, etc)
## ou imprimer des explications des symboles
## Début de la définition du Layout "xviexport"
layout xviexport
#cs UTM32
## echelle à laquelle on veut dessiner la topo
scale 1 {Scale}
#scale 1 1000
## taille de la grille
grid-size 2 2 2 m
## mettre la grille en arrière plan
grid bottom
endlayout
## fin de la définition du layout "xviexport"
## Début de la définition du layout "Layout-Plan"
layout layout-Plan
## Call the config settings (Layout config inside the config.thc file)
copy fonts_1000
copy drawingconfig
#copy layoutcontinuation # Pour afficher le label des continuations
copy headerl
copy langue-fr
## Définition du système de projection du plan
cs {cs}
## La ligne base-scale spécifie l'échelle auquel nous avons dessiné nos croquis.
## Par défaut, Therion pense que c'est une échelle 1:200. Si on a utilisé une autre échelle,
## il faut enlever le "#" et spécifier l'échelle vraiment employée, comme c'est le cas
## après avoir dessiné la topo sur un cheminement exporté avec le layout "xviexport".
## Jouer avec le ration base-scale/scale permet de jouer globalement sur les tailles
## des caractères et des traits.
base-scale 1 {Scale}
## Maintenant on va mettre une ligne "scale" pour specifier l'échelle pour imprimer la topo.
## La combination entre scale et base-scale contrôle l'épaisseur des lignes, rotation, etc, convenable
## pour faire l'ampliation-réduction entre dessin et le résultat de l'imprimante
## C'est tres important s'assurer que la configuration de l'imprimante ne spécifie pas l'option "Fit in page"
## ou similaire, sinon, l'échelle sera changée pendant l'impression!
scale 1 {Scale}
## Echelle graphique 100 m ampleur (Généralement, le choix scale/10 est plutôt pas mal)
scale-bar 100 m
## Voici une ligne pour specifier qu'il faut imprimer une grille au dessous de la topo
grid bottom
## Défini la rotation de la topographie
#rotate -65
## Une ligne pour specifier que la grille est 1000x1000x1000 m
## (Trois dimensions, oui, ça sert pour la coupe aussi)
grid-size 50 50 50 m
## la topo est transparente (on peut voir les galeries en dessous)
## C'est on par défaut, donc, pas vraiment besoin de specifier
transparency on
## Couleurs de la topographie
#colour map-bg [70 90 70]
#colour map-fg [100 100 80]
#colour map-fg altitude
#colour map-fg explo-date
#colour map-fg topo-date
#colour map-fg map
#colour map-fg scrap
#colour-legend off
colour map-fg 90
## ça marche seulement si transparency est "on" 90% blanc= 10% noir
opacity 75
#surface bottom
#surface-opacity 100
## Auteur
doc-author "{Author}"
## Titre
doc-title "{cavename} Plan - 1:{Scale}"
doc-subject "{cavename}, topographie en plan"
doc-keywords "Cave, Survey, {cavename}, Pierre saint Martin - Larra, {map_comment}"
## Maintenant on spécifie la position de la manchette, dont l'intérieur est occupé par le titre, auteurs, etc.
## Nous pouvons indiquer les cordonnées du point de la topo ou l'on veut la manchette :
## 0 0, c'est en bas, à gauche, 100 100, c'est en haut, à droite
## La manchette a des "points cardinaux" autour : n, s, ne, sw, etc.
## Il faut specifier un de ces points comme ce que sera placé sur les cordonnées.
## Alors nous pouvons specifier que le sud-ouest de la manchette soit placé en bas, a gauche,
## ou une autre combination...
map-header 2 98 nw
## arrière plan de la manchette
map-header-bg on
## Légende pour expliciter les symboles. "on" imprimera seulement la légende des symboles dessinés
## sur la topo. Si l'on veut pour tous les symboles, utilisés ou pas, il faut indiquer "all".
## "legend off" = pas de légende
legend on
## Par défaut, la légende est de 14 cm de largeur
legend-width 15 cm
legend-columns 2
## Un commentaire à ajouter au titre, on pourrait indiquer ici la mairie où est placée la cavité
## dont le nom est probablement le titre de la topo.
map-comment "{map_comment}"
#map-comment "{map_comment}<br>Coordonnées : ({cs}/WGS84) xxx.xxx xxxx.xxx, Alt.: xxxx m"
## Afficher les statistiques d'explo/topo par équipe/nom. C'est lourd
## si la cavité est importante et qu'il y a beaucoup d'explorateurs/topographes.
statistics explo-length off
statistics topo-length off
## Afficher un copyright
statistics copyright 2
## Dessin ou pas du cheminement topo
# symbol-hide point station
# symbol-hide line survey
# symbol-hide group
# symbol-show line wall
# symbol-hide point station-name
# symbol-hide point u:symbol_plan
# symbol-hide point u:symbol_extend
# debug scrap-names
# debug station-names
layers on
overlap 2 cm
code tex-map
\legendwidth=15cm
\legendtextsize={\size[12]}
\legendtextheadersize={\size[28]} %%% Taille du titre
\legendtextsectionsize={\size[14]} %%% Taille du titre
%\legendtextcolor={\color[0 0 110]} %# RGB values 0--100
% Output map title as determined by Therion is stored in cavename, défini par la une Map.
% It will be empty if there are multiple maps selected for any one projection
% AND there are multiple source surveys identified in the thconfig file
% ie Therion can not infer a unique title from the input data given.
% This code allows you to define an output map title {cavename} if it happens to be empty
\edef\temp{\the\cavename} % cavename from Therion
\edef\nostring{} % empty string
\ifx\temp\nostring % test if cavename is empty
% if empty
reassign cavename to describe selected maps as a group
\else % if not empty keep the value set by therion, or assign an override cavename here
\fi
\cavename={{cavename}, Plan 1:{Scale}} % Note Alex : Bug avec certains fichiers ?
\newtoks\club \club={{club}}
%\newtoks\thanksto \thanksto={{thanksto}}
\newtoks\wpage \wpage={{wpage}}
\newtoks\datat \datat={{datat}}
\newtoks\synth \synth={{Author}}
\framethickness=0.5mm
endcode
endlayout
##debut de la definition du layout "layout-Extended"
layout layout-Extended
## Call the config settings (Layout config inside the config.thc file)
copy drawingconfig
#copy layoutcontinuation # Pour afficher le label des continuations
copy header_coupe
#copy headerl
#copy header_coupe_vert-auto
#copy header_coupe_vert-to-place
copy langue-fr
## Définition du système de projection du plan
cs {cs}
## La ligne base-scale spécifie l'échelle auquel nous avons dessiné nos croquis.
## Par défaut, Therion pense que c'est une échelle 1:200. Si on a utilisé une autre échelle,
## il faut enlever le "#" et spécifier l'échelle vraiment employée, comme c'est le cas
## après avoir dessiné la topo sur un cheminement exporté avec le layout "xviexport".
## Jouer avec le ration base-scale/scale permet de jouer globalement sur les tailles
## des caractères et des traits.
base-scale 1 {Scale}
## Maintenant on va mettre une ligne "scale" pour specifier l'échelle pour imprimer la topo.
## La combination entre scale et base-scale contrôle l'épaisseur des lignes, rotation, etc, convenable
## pour faire l'ampliation-réduction entre dessin et le résultat de l'imprimante
## C'est tres important s'assurer que la configuration de l'imprimante ne spécifie pas l'option "Fit in page"
## ou similaire, sinon, l'échelle sera changée pendant l'impression!
scale 1 {Scale}
## Echelle graphique 100 m ampleur (Généralement, le choix scale/10 est plutôt pas mal)
scale-bar 40 m
## Voici une ligne pour specifier qu'il faut imprimer une grille au dessous de la topo
#grid bottom
grid off
## Une ligne pour specifier que la grille est 1000x1000x1000 m
## (Trois dimensions, oui, ça sert pour la coupe aussi)
#grid-size 250 250 250 m
## la topo est transparente (on peut voir les galeries inférieurs)
## C'est on par défaut, donc, pas vraiment besoin de specifier
transparency on
## Couleurs de la topographie
#colour map-bg [70 90 70]
#colour map-fg [100 100 80]
#colour map-fg altitude
#colour map-fg explo-date
#colour map-fg topo-date
#colour map-fg map
#colour map-fg scrap
#colour-legend off
colour map-fg 90
## ça marche seulement si transparency est "on" 90% blanc= 10% noir
opacity 75
#surface bottom
#surface-opacity 100
## Auteur
doc-author "{Author}"
## Titre
doc-title "{cavename} Coupe développée - 1:{Scale}"
doc-subject "{cavename}, topographie en coupe développée"
doc-keywords "Cave, Survey, {cavename}, Pierre saint Martin - Larra, Coupe développée, {map_comment}"
## Maintenant on spécifie la position de la manchette, dont l'intérieur est occupé par le titre, auteurs, etc.
## Nous pouvons indiquer les cordonnées du point de la topo ou l'on veut la manchette :
## 0 0, c'est en bas, à gauche, 100 100, c'est en haut, à droite
## La manchette a des "points cardinaux" autour : n, s, ne, sw, etc.
## Il faut specifier un de ces points comme ce que sera placé sur les cordonnées.
## Alors nous pouvons specifier que le sud-ouest de la manchette soit placé en bas, a gauche,
## ou une autre combination...
map-header 98 98 ne
## arrière plan de la manchette
map-header-bg on
## Légende pour expliciter les symboles. "on" imprimera seulement la légende des symboles dessinés
## sur la topo. Si l'on veut pour tous les symboles, utilisés ou pas, il faut indiquer "all".
## "legend off" = pas de légende
legend on
## Par défaut, la légende est de 14 cm de largeur
legend-width 15 cm
legend-columns 2
## Un commentaire à ajouter au titre, on pourrait indiquer ici la mairie où est placée la cavité
## dont le nom est probablement le titre de la topo.
map-comment "{map_comment}"
#map-comment "{map_comment}<br>Coordonnées : ({cs}/WGS84) xxx.xxx xxxx.xxx, Alt.: xxxx m"
## Afficher les statistiques d'explo/topo par équipe/nom. C'est lourd
## si la cavité est importante et qu'il y a beaucoup d'explorateurs/topographes.
statistics explo-length off
statistics topo-length off
## Afficher un copyright
statistics copyright 2
## Dessin ou pas du cheminement topo
# symbol-hide point station
# symbol-hide line survey
# symbol-hide group
# symbol-show line wall
# symbol-hide point u:symbol_plan
# symbol-hide point u:symbol_extend
# debug scrap-names
# debug station-names
layers on
overlap 2 cm
## Modification du Titre de la topo
code tex-map
\legendwidth=15cm
\legendtextsize={\size[12]}
\legendtextheadersize={\size[28]} %%% Taille du titre
\legendtextsectionsize={\size[14]} %%% Taille du titre
%\legendtextcolor={\color[0 0 110]} %# RGB values 0--100
% Output map title as determined by Therion is stored in cavename, défini par la une Map.
% It will be empty if there are multiple maps selected for any one projection
% AND there are multiple source surveys identified in the thconfig file
% ie Therion can not infer a unique title from the input data given.
% This code allows you to define an output map title {cavename} if it happens to be empty
\edef\temp{\the\cavename} % cavename from Therion
\edef\nostring{} % empty string
\ifx\temp\nostring % test if cavename is empty
% if empty
reassign cavename to describe selected maps as a group
\else % if not empty keep the value set by therion, or assign an override cavename here
\fi
\cavename={{cavename}, Coupe développée 1:{Scale}} % Note Alex : Bug avec certains fichiers ?
\newtoks\club \club={{club}}
%\newtoks\thanksto \thanksto={{thanksto}}
\newtoks\wpage \wpage={{wpage}}
\newtoks\datat \datat={{datat}}
\newtoks\synth \synth={{Author}}
\framethickness=0.5mm
endcode
endlayout
## Fin de la definition du Layout "normal"
layout layout-kml
## Définition du système de projection du plan
cs EPSG:2154
## Couleur de la topographie
## Rouge-Orange = 255,69,0 -->
## Orange = 255,165,0 -->
## Orange Sombre = 255,140,0 -->
## Bleu --> 0, 0 255
color map-fg [0 0 100]
endlayout
###############################################################################################
# 3-EXPORT
###############################################################################################
## Export des xvi pour le dessin si besoin
# export map -proj plan -layout xviexport -fmt xvi -o Data/{fileName}-Plan.xvi
# export map -proj extended -layout xviexport -fmt xvi -o Data/{fileName}-Extended.xvi
## Selection des Maps à exporter
# select MP-{fileName}-Plan-tot@{fileName}
# select MC-{fileName}-Extended-tot@{fileName}
## Export des fichiers pdf, plan et coupe.
## ATTENTION, la topo étant énorme, il faut mettre l'option ne traçant pas la centerline !
# export map -projection plan -fmt pdf -layout layout-Plan -o Outputs/{fileName}-Plan.pdf
# export map -projection extended -fmt pdf -layout layout-Extended -o Outputs/{fileName}-Extended.pdf
## Export du fichier 3d pour Loch
export model -enable all -o Outputs/{fileName}.lox
export model -enable all -o Outputs/{fileName}.kml
## Export des fichiers ESRI
# export map -proj plan -fmt esri -o Outputs/{fileName}
## Export des fichiers kml
# export map -proj plan -fmt kml -o Outputs/{fileName}.kml -layout layout-kml
# export model -fmt kml -o Outputs/{fileName}-model.kml -enable all
# export model -enable all -o Outputs/{fileName}-3D.kml
# export cave-list -location on -o Outputs/{fileName}-Cave-list.html
# export survey-list -location on -o Outputs/{fileName}-Surveys.html
###############################################################################################
## END
###############################################################################################
+22
View File
@@ -0,0 +1,22 @@
# Configuration values for pyCreate_th2.ph
[Survey_Data]
Author = Alexandre Pont
Copyright1 = # Copyright (C) ARSIP 2025
Copyright2 = # This work is under the Creative Commons Attribution-NonCommercial-NoDerivatives License:
Copyright3 = # <http://creativecommons.org/licenses/by-nc-nd/4.0/>
Copyright_Short = Licence CC by-nc-nd : http://creativecommons.org/licenses/by-nc-nd/4.0/
map_comment = Massif de la Pierre Saint Martin - Larra
club = ARSIP
thanksto = Merçi à tout le monde
datat = https://github.com/Alex38Lyon/Synthese-PSM_LARRA
wpage = https://www.arsip.fr/
cs = UTM30
[Application_Data]
template_path = ./template
station_by_scrap = 30
final_therion_exe = True
therion_path = C:\Program Files\Therion\therion.exe
shot_lines_in_th2_files = False
station_name_in_th2_files = False
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,35 @@
{
"folders": [
{
"path": "."
}
],
"settings": {
"cSpell.words": [
"australiangeodetic",
"australiangeodeticdatum",
"backclino",
"cavename",
"clarke",
"clino",
"datat",
"ecart",
"ENDC",
"endlayout",
"endscrap",
"etrs",
"european",
"geocentricdatumofaustralia",
"geocentricofaustralia",
"Makto",
"northamerican",
"northamericandatum",
"nouvelletriangulationfrançaise",
"thanksto",
"thconfig",
"therion",
"wpage",
"XTHERION"
]
}
}
+993
View File
@@ -0,0 +1,993 @@
"""
#############################################################################################
# #
# Script pour automatiser la création des dossiers et fichiers pour un fichier .th #
# #
# By Alexandre PONT (alexandre_pont@yahoo.fr) #
# #
# Définir les différentes variables dans fichier config.ini #
# Création des dossiers nécessaires d'après dossier 'template' #
# Création des fichiers nécessaires : th, th2, -tot.th #
# Création des scrap avec stations topo #
# #
# usage : python pyCreate_th2.py #
# #
#############################################################################################
Création Alex the 2024 12 16 :
Thank's too
- Tanguy Racine for the script https://github.com/tr1813
- Xavier Robert for the main principes https://github.com/robertxa
- Benoit Urruty https://github.com/BenoitURRUTY
Version 2025 03 21 : Création mode --update th2
"""
Version ="2025.03.21"
#################################################################################################
#################################################################################################
import os
from os.path import isfile, join, abspath
import sys
import re
import unicodedata
import argparse
import shutil
from datetime import datetime
import configparser
import tkinter as tk
from tkinter import filedialog
from helpers.survey import SurveyLoader, NoSurveysFoundException
from helpers.therion import compile_template, Colors, compile_file
#################################################################################################
## [Survey_Data] default values
Author = "Created by pyCreate_th2.py"
Copyright = "# Copyright (C) pyCreate_th2.py"
Copyright_Short = "Licence (C) pyCreate_th2.py"
map_comment = "Created by pyCreate_th2.py"
cs = "UTM30"
club = "Therion"
thanksto = "Therion"
datat = "https://therion.speleo.sk/"
wpage = "https://therion.speleo.sk/"
## [Application_data] default values
template_path = "./template"
station_by_scrap = 20
final_therion_exe = True
therion_path = "C:/Therion/therion.exe"
LINES = -1
NAMES = -1
#################################################################################################
# # Codes de couleur ANSI
# class Colors:
# BLACK = '\033[90m'
# RED = '\033[91m'
# GREEN = '\033[92m'
# YELLOW = '\033[93m'
# BLUE = '\033[94m'
# MAGENTA = '\033[95m'
# CYAN = '\033[96m'
# WHITE = '\033[97m'
# ERROR = '\033[91m'
# WARNING = '\033[95m'
# HEADER = '\033[96m'
# ENDC = '\033[0m'
# BOLD = '\033[1m'
# UNDERLINE = '\033[4m'
#################################################################################################
def sanitize_filename(th_name):
"""
Cleans a string to make it compatible with filenames on Windows, Linux, and macOS.
Replaces special and accented characters with compatible characters.
Args:
th_name (str): The filename to clean.
Returns:
str: The cleaned and compatible string.
"""
# Unicode normalization to replace accented characters with their non-accented equivalents
th_name = unicodedata.normalize('NFKD', th_name).encode('ASCII', 'ignore').decode('ASCII')
# Replace illegal characters with an underscore (_)
th_name = re.sub(r'[<>:"/\\|?*\']', '_', th_name) # Characters not allowed on Windows
th_name = re.sub(r'[\s]', '_', th_name) # Replace spaces with underscores
th_name = re.sub(r'[^a-zA-Z0-9._-]', '_', th_name) # Keep letters, digits, . _ -
# Ensure the name is not empty or just underscores
return th_name.strip('_') or "default_filename"
#################################################################################################
def colored_help(parser):
# Captures the help output
help_text = parser.format_help()
# Coloration des différentes parties
colored_help_text = help_text.replace(
'usage:', f'{Colors.ERROR}usage:{Colors.ENDC}'
).replace(
'options:', f'{Colors.GREEN}options:{Colors.ENDC}'
).replace('positional arguments:', f'{Colors.BLUE}positional arguments:{Colors.ENDC}')
# Surligner les arguments
for action in parser._actions:
if action.option_strings:
# Colorer les options (--xyz)
for opt in action.option_strings:
colored_help_text = colored_help_text.replace(opt, f'{Colors.BLUE}{opt}{Colors.ENDC}')
# Imprimer le texte coloré
print(colored_help_text)
sys.exit(0)
#################################################################################################
def read_config(config_file):
global Author
global Copyright
global Copyright_Short
global map_comment
global club
global thanksto
global datat
global wpage
global cs
global template_path
global station_by_scrap
global final_therion_exe
global therion_path
global LINES
global NAMES
# Initialize the configparser to read .ini files
config = configparser.ConfigParser()
config.read(config_file, encoding="utf-8")
if 'Survey_Data' in config and 'Author' in config['Survey_Data']:
Author = config['Survey_Data']['Author']
if 'Survey_Data' in config and 'Copyright1' in config['Survey_Data']:
Copyright = config['Survey_Data']['Copyright1'] + "\n" + config['Survey_Data']['Copyright2'] + "\n" + config['Survey_Data']['Copyright3'] + "\n"
if 'Survey_Data' in config and 'Copyright_Short' in config['Survey_Data']:
Copyright_Short = config['Survey_Data']['Copyright_Short']
if 'Survey_Data' in config and 'map_comment' in config['Survey_Data']:
map_comment = config['Survey_Data']['map_comment']
if 'Survey_Data' in config and 'club' in config['Survey_Data']:
club = config['Survey_Data']['club']
if 'Survey_Data' in config and 'thanksto' in config['Survey_Data']:
thanksto = config['Survey_Data']['thanksto']
if 'Survey_Data' in config and 'datat' in config['Survey_Data']:
datat = config['Survey_Data']['datat']
if 'Survey_Data' in config and 'wpage' in config['Survey_Data']:
wpage = config['Survey_Data']['wpage']
if 'Survey_Data' in config and 'cs' in config['Survey_Data']:
cs = config['Survey_Data']['cs']
if 'Application_Data' in config and 'template_path' in config['Application_Data']:
template_path = config['Application_Data']['template_path']
if 'Application_Data' in config and 'station_by_scrap' in config['Application_Data']:
station_by_scrap = int(config['Application_Data']['station_by_scrap'])
if 'Application_Data' in config and 'final_therion_exe' in config['Application_Data']:
final_therion_exe = bool(config['Application_Data']['final_therion_exe'])
if 'Application_Data' in config and 'therion_path' in config['Application_Data']:
therion_path = config['Application_Data']['therion_path']
if LINES == -1 :
if 'Application_Data' in config and 'shot_lines_in_th2_files' in config['Application_Data']:
LINES = 0 if config['Application_Data']['shot_lines_in_th2_files'] == "False" else 1
if NAMES == -1 :
if 'Application_Data' in config and 'station_name_in_th2_files' in config['Application_Data']:
NAMES = 0 if config['Application_Data']['station_name_in_th2_files'] == "False" else 1
#################################################################################################
def copy_template_if_not_exists(template_path, destination_path):
# Check if the destination folder exists
try:
if not os.path.exists(destination_path):
# If the destination folder does not exist, copy the template
shutil.copytree(template_path, destination_path)
print(f"{Colors.GREEN}The folder '{Colors.GREEN}{template_path}{Colors.ENDC}' has been copied to '{Colors.ENDC}{destination_path}{Colors.GREEN}'{Colors.ENDC}")
else:
print(f"{Colors.WARNING}Warning: The folder '{Colors.ENDC}{destination_path}{Colors.WARNING}' already exists. No files were copied.{Colors.ENDC}")
except Exception as e:
print(f"{Colors.ERROR}Copy template error: {Colors.ENDC}{e}")
exit(1)
#################################################################################################
def add_copyright_header(file_path, copyright_text):
# Lire le contenu du fichier
with open(file_path, 'r') as file:
content = file.readlines()
# Vérifier si le copyright est déjà présent
if not any("copyright" in line.lower() for line in content):
# Ajouter le copyright en en-tête
content.insert(0, f"{copyright_text}\n")
# Réécrire le fichier avec le copyright ajouté
with open(file_path, 'w') as file:
file.writelines(content)
#################################################################################################
def copy_file_with_copyright(th_file, destination_path, copyright_text):
# Vérifier si le fichier existe
if os.path.exists(th_file):
# Créer le dossier de destination s'il n'existe pas
os.makedirs(destination_path, exist_ok=True)
# Copier le fichier vers le dossier de destination
dest_file = os.path.join(destination_path, os.path.basename(th_file))
shutil.copy(th_file, dest_file)
# Ajouter le copyright dans l'en-tête si nécessaire
add_copyright_header(dest_file, copyright_text)
# print(f"{Colors.GREEN}File '{Colors.ENDC}{th_file}{Colors.GREEN}' has been copied to '{Colors.ENDC}{destination_path}{Colors.GREEN}' with the copyright header added.{Colors.ENDC}")
else:
print(f"{Colors.ERROR}Error: The file .th does not exist {Colors.ENDC}{th_file}")
#################################################################################################
def process_template(template_path, variables, output_path):
"""
Process a Therion template file by replacing variables.
Args:
template_path (str): Path to the original template file
variables (dict): Dictionary of variables to replace
output_path (str): Path for the new configuration file
"""
try:
# Read the content of the template file
with open(template_path, 'r', encoding='utf-8') as file:
content = file.read()
# Replace variables
for var, value in variables.items():
# Use regex to replace {variable} with its value
pattern = r'\{' + re.escape(var) + r'\}'
content = re.sub(pattern, str(value), content)
# Write the new file
with open(output_path, 'w', encoding='utf-8') as file:
file.write(content)
print(f"{Colors.GREEN}Update template successfully: {Colors.ENDC}{output_path}")
# Delete the original template file
os.remove(template_path)
except FileNotFoundError:
print(f"{Colors.WARNING}Warning: Template file {Colors.ENDC}{template_path}{Colors.WARNING} not found.{Colors.ENDC}")
except PermissionError:
print(f"{Colors.ERROR}Error: Insufficient permissions to write the file.{Colors.ENDC}")
except Exception as e:
print(f"{Colors.ERROR}An error occurred: {Colors.ENDC}{e}")
#################################################################################################
def parse_therion_surveys(file_path):
"""
Reads a Therion file and extracts survey names.
Args:
file_path (str): Path to the Therion file to parse
Returns:
list: List of survey names
"""
survey_names = []
try:
with open(file_path, 'r', encoding='utf-8') as file:
# Read all lines from the file
lines = file.readlines()
for line in lines:
# Look for lines starting with survey
line = line.strip()
if line.startswith('survey ') and ' -title ' in line:
# Split the line and extract the survey name
start_index = line.find('survey ') + len('survey ')
end_index = line.find(' -title ')
survey_name = line[start_index:end_index].strip()
survey_names.append(survey_name)
except FileNotFoundError:
print(f"{Colors.WARNING}Warning: File {Colors.ENDC}{file_path}{Colors.WARNING} not found.{Colors.ENDC}")
except PermissionError:
print(f"{Colors.ERROR}Error: Insufficient permissions to read {Colors.ENDC}{file_path}")
except Exception as e:
print(f"{Colors.ERROR}An error occurred: {Colors.ENDC}{e}")
return survey_names
#################################################################################################
def str_to_bool(value):
"""
Function to convert string to boolean
"""
if isinstance(value, bool):
return value
if value.lower() in ('true', '1', 'yes', 'y'):
return True
elif value.lower() in ('false', '0', 'no', 'n'):
return False
else:
raise argparse.ArgumentTypeError(f"{Colors.ERROR}Error: Invalid boolean value: {Colors.ENDC}{value}")
#################################################################################################
def select_file():
# Créer une instance de la fenêtre tkinter
root = tk.Tk()
# Cacher la fenêtre principale
root.withdraw()
# Afficher la boîte de dialogue de sélection de fichier
file_path = filedialog.askopenfilename(title="Sélectionnez un fichier")
# Retourner le chemin complet du fichier sélectionné
return file_path
#################################################################################################
# main function #
#################################################################################################
if __name__ == u'__main__':
#################################################################################################
# Parse arguments #
#################################################################################################
parser = argparse.ArgumentParser(
description=f"{Colors.HEADER}Create a skeleton folder and th2 files with scraps from a .th Therion file\nVersion: {Colors.ENDC}{Version}\n",
formatter_class=argparse.RawDescriptionHelpFormatter)
parser.print_help = colored_help.__get__(parser)
parser.add_argument("--survey_file", help="The survey file (*.th) to perform e.g. './Therion_file.th'", default="")
parser.add_argument("--survey_name", help="Scrap name (if different from 'survey_file' name)", default="None")
#parser.add_argument("--proj", choices=['plan', 'elevation', 'extended', 'none'], help="The scrap projection to produce", default="plan")
#parser.add_argument("--format", choices=['th2', 'plt'], help="Output format. Either th2 for producing skeleton for drawing or plt for visualizing in aven/loch", default="th2")
parser.add_argument("--output", default="./", help="Output folder path")
# parser.add_argument("--therion-path", help="Path to therion binary", default="therion")
parser.add_argument("--scale", help="Scale for the exports", default="500")
parser.add_argument("--lines", type=str_to_bool, help="Shot lines in th2 files", default=-1)
parser.add_argument("--names", type=str_to_bool, help="Stations names in th2 files", default=-1)
parser.add_argument("--update", help="Mode update, option th2", default="")
parser.epilog = (
f"{Colors.GREEN}Please, complete {Colors.RED}config.ini{Colors.GREEN} file for personal configuration{Colors.ENDC}\n"
f"{Colors.GREEN}If no argument :{Colors.RED} files selection windows\n{Colors.ENDC}\n"
f"{Colors.BLUE}Examples:{Colors.ENDC}\n"
f"\t> python pyCreate_th2.py ./test/Entree.th --survey_name Geophysicaya_01_entree --output ./test/ --scale 1000\n"
f"\t> python pyCreate_th2.py Entree.th\n"
f"\t> python pyCreate_th2.py\n\n")
args = parser.parse_args()
# print("args.survey_file : " + args.survey_file )
# print("args.update : " + args.update )
if args.survey_file == "":
args.survey_file = select_file()
print(f"Selected file : {args.survey_file}")
ENTRY_FILE = abspath(args.survey_file)
# PROJECTION = args.proj.capitalize()
PROJECTION = "Plan"
TARGET = args.survey_name
OUTPUT = args.output
#FORMAT = args.format
FORMAT = "th2"
SCALE = args.scale
LINES = args.lines
NAMES = args.names
# TH_NAME = args.survey_file.split("/")[-1].strip(".th")
TH_NAME = sanitize_filename(os.path.splitext(os.path.basename(args.survey_file))[0])
DEST_PATH = os.path.dirname(args.survey_file) + "/" + TH_NAME
#DEST_PATH = args.output + TH_NAME.split("/")[-1].strip(".th")
#ABS_PATH = ENTRY_FILE.strip(args.survey_file)
ABS_PATH = os.path.dirname(ENTRY_FILE)
# print("args.survey_file : " + args.survey_file )
# print("ENTRY_FILE: " + ENTRY_FILE )
# print("PROJECTION: " + PROJECTION )
# print("TARGET: " + TARGET )
# print("OUTPUT: " + OUTPUT )
# print("FORMAT: " + FORMAT )
# print("SCALE: " + SCALE )
# print("TH_NAME: " + TH_NAME )
# print("DEST_PATH: " + DEST_PATH )
# print("ABS_PATH: " + ABS_PATH )
#################################################################################################
# Reading config.ini #
#################################################################################################
try:
# Load the 'database' section from the configuration file
read_config("config.ini")
# print("Auteur: " + Author)
# print(f"Copyright: \n{Copyright}")
except ValueError as e:
# Handle errors if the section is missing
print(f"{Colors.ERROR}Error: read_config:{Colors.ERROR}", e)
if PROJECTION.lower() != "plan" :
print(f"{Colors.ERROR}Error: Sorry, projection '{Colors.ENDC}{PROJECTION}{Colors.ERROR}' not yet implemented{Colors.ENDC}")
exit(1)
if not os.path.isfile(ENTRY_FILE):
print(f"{Colors.ERROR}Error: The Therion file didn't exist: {Colors.ENDC} {ENTRY_FILE}")
exit(1)
if FORMAT not in ["th2", "plt"]:
print(f"{Colors.ERROR}Error: Please choose a supported format: th2, plt{Colors.ENDC}")
exit(1)
# Normalise name, namespace, key, file path
print(f"{Colors.GREEN}Parsing survey entry file:\t{Colors.ENDC} {args.survey_file}")
survey_list = parse_therion_surveys(ENTRY_FILE)
# print(survey_list)
if TARGET == "None" :
if len(survey_list) > 1 :
print(f"{Colors.ERROR}Error: Multiple surveys were found, not yet implemented{Colors.ENDC}")
exit(1)
TARGET = sanitize_filename(survey_list[0])
print(f"{Colors.GREEN}Parsing survey target: \t{Colors.ENDC} {TARGET}")
loader = SurveyLoader(ENTRY_FILE)
survey = loader.get_survey_by_id(survey_list[0])
# print(survey.name)
if not survey:
raise NoSurveysFoundException(f"{Colors.ERROR}Error: No survey found with that selector{Colors.ENDC}")
if args.update == "th2":
print(f"{Colors.GREEN} Update th2 files {Colors.ENDC}")
print(f"\t{Colors.BLUE}survey_file : {Colors.ENDC} {args.survey_file}")
print(f"\t{Colors.BLUE}ENTRY_FILE: {Colors.ENDC} {ENTRY_FILE}")
print(f"\t{Colors.BLUE}PROJECTION: {Colors.ENDC} {PROJECTION}")
print(f"\t{Colors.BLUE}TARGET: {Colors.ENDC} {TARGET}")
print(f"\t{Colors.BLUE}OUTPUT: {Colors.ENDC} {OUTPUT}")
print(f"\t{Colors.BLUE}FORMAT: {Colors.ENDC} {FORMAT}")
print(f"\t{Colors.BLUE}SCALE: {Colors.ENDC} {SCALE}")
print(f"\t{Colors.BLUE}TH_NAME: {Colors.ENDC} {TH_NAME}")
DEST_PATH = os.path.dirname(args.survey_file)
print(f"\t{Colors.BLUE}DEST_PATH: {Colors.ENDC} {DEST_PATH}")
print(f"\t{Colors.BLUE}ABS_PATH: {Colors.ENDC} {ABS_PATH}")
#################################################################################################
# Copy template folders #
#################################################################################################
if args.update == "":
# print(f"{Colors.GREEN}Copy template folder and adapte it{Colors.ENDC}")
copy_template_if_not_exists(template_path, DEST_PATH)
copy_file_with_copyright(ENTRY_FILE, DEST_PATH + "/Data", Copyright)
# Adapte templates
config_vars = {
'fileName': TH_NAME,
'cavename': TH_NAME.replace("_", " "),
'Author': Author,
'Copyright': Copyright,
'Scale' : SCALE,
'Target' : TARGET,
'map_comment' : map_comment,
'club' : club,
'thanksto' : thanksto.replace("_", r"\_"),
'datat' : datat.replace("_", r"\_"),
'wpage' : wpage.replace("_", r"\_"),
'cs' : cs,
'other_scraps_plan' : "",
'file_info' : f'# File generated by pyCreate_th2.py (version {Version}) date: {datetime.now().strftime("%Y.%m.%d %H:%M:%S")}',
}
process_template(DEST_PATH + '/template.thconfig', config_vars, DEST_PATH + '/' + TH_NAME + '.thconfig')
process_template(DEST_PATH + '/template-tot.th', config_vars, DEST_PATH + '/' + TH_NAME + '-tot.th')
process_template(DEST_PATH + '/template-readme.md', config_vars, DEST_PATH + '/readme.md')
#################################################################################################
# Produce the parsable XVI file #
#################################################################################################
print(f"{Colors.GREEN}Compiling 2D XVI file: \t{Colors.ENDC} {TH_NAME}")
template = """source "{th_file}"
layout minimal
scale 1 {scale}
endlayout
select {selector}
export model -o "{th_name}.lox"
export map -projection plan -o "{th_name}-Plan.xvi" -layout minimal -layout-debug station-names
export map -projection extended -o "{th_name}-Extended.xvi" -layout minimal -layout-debug station-names
"""
if args.update == "th2":
template_args = {
"th_file": DEST_PATH + "/" + TH_NAME + ".th",
"selector": survey.therion_id,
"th_name": DEST_PATH + "/" + TH_NAME,
"scale": SCALE,
}
else :
template_args = {
"th_file": DEST_PATH + "/Data/" + TH_NAME + ".th",
"selector": survey.therion_id,
"th_name": DEST_PATH + "/Data/" + TH_NAME,
"scale": SCALE,
}
log, tmpdir = compile_template(template, template_args, cleanup=False, therion_path=therion_path)
#################################################################################################
# Parse the Plan XVI file #
#################################################################################################
if args.update == "th2":
th_name_xvi = DEST_PATH + "/" + TH_NAME + "-Plan.xvi"
else :
th_name_xvi = DEST_PATH + "/Data/" + TH_NAME + "-Plan.xvi"
print(f"{Colors.GREEN}Parsing plan XVI file:\t{Colors.ENDC}{th_name_xvi}")
stations = {}
lines = []
with open(join(th_name_xvi), "r", encoding="utf-8") as f:
xvi_content = f.read()
xvi_stations, xvi_shots = xvi_content.split("XVIshots")
# Extract all the stations
for line in xvi_stations.split("\n"):
match = re.search(r"{\s*(-?\d+\.\d+)\s*(-?\d+\.\d+)\s([^@]+)(?:@([^\s}]*))?\s*}", line)
if match:
x = match.groups()[0]
y = match.groups()[1]
station_number = match.groups()[2]
namespace = match.groups()[3]
namespace_array = namespace.split(".") if namespace else []
station = station_number
if len(namespace_array) > 1:
station = "{}@{}".format(station_number, ".".join(namespace_array[0:-1]))
stations["{}.{}".format(x, y)] = [x, y, station]
# Extraire les valeurs x et y à partir des listes dans stations
x_values = [float(value[0]) for value in stations.values()]
y_values = [float(value[1]) for value in stations.values()]
# Trouver les min et max de x
x_min = float(min(x_values))
x_max = float(max(x_values))
# Trouver les min et max de y
y_min = float(min(y_values))
y_max = float(max(y_values))
x_ecart = x_max - x_min
y_ecart = y_max - y_min
# Afficher les résultats
# print("x_min:", x_min, "x_max:", x_max)
# print("y_min:", y_min, "y_max:", y_max)
# print("Écart max-min pour x:", x_ecart)
# print("Écart max-min pour y:", y_ecart)
# Extract all the lines
for line in xvi_shots.split("\n"):
match = re.search(r"^\s*{\s*(-?\d+\.\d+)\s*(-?\d+\.\d+)\s*(-?\d+\.\d+)\s*(-?\d+\.\d+)\s*.*}", line )
if match:
x1 = match.groups()[0]
y1 = match.groups()[1]
x2 = match.groups()[2]
y2 = match.groups()[3]
key1 = "{}.{}".format(x1, y1)
key2 = "{}.{}".format(x2, y2)
# Splays won't have stations
station1 = stations[key1][2] if key1 in stations else None
station2 = stations[key2][2] if key2 in stations else None
lines.append([x1, y1, x2, y2, station1, station2])
# shutil.rmtree(tmpdir)
if args.update == "th2":
th2_name = DEST_PATH + "/" + TH_NAME
else :
th2_name = DEST_PATH + "/Data/" + TH_NAME
output_path = f'{th2_name}-{PROJECTION}.{FORMAT}'
scrap_to_add = int(len(stations)/station_by_scrap)-1
# print(stations)
print(f"{Colors.GREEN}Writing output to:\t{Colors.ENDC}{output_path}")
# Write TH2
if FORMAT == "th2":
th2_file_header = """encoding utf-8"""
th2_file = """
##XTHERION## xth_me_area_adjust {X_Min} {Y_Min} {X_Max} {Y_Max}
##XTHERION## xth_me_area_zoom_to 100
##XTHERION## xth_me_image_insert {insert_XVI}
{Copyright}
# File generated by pyCreate_th2.py version {version} date: {date}
# x_min: {X_Min}, x_max: {X_Max} ecart : {X_Max_X_Min}
# y_min: {Y_Min}, y_max: {Y_Max} ecart : {Y_Max_Y_Min}
scrap S{projection_short}-{name}_01 -station-names "" "@{name}" -projection {projection} -author {year} "{author}" -copyright {year} "{Copyright_Short}"
{points}
{names}
{lines}
endscrap"""
th2_point = """ point {x} {y} station -name {station}"""
th2_name = """ point {x} {y} station-name -align tr -scale xs -text {station}"""
th2_line = """ line u:Shot_Survey
{x1} {y1}
{x2} {y2}
endline"""
seen = set()
th2_lines = []
th2_points = []
th2_names = []
other_scraps_plan = ""
for line in lines:
th2_lines.append(th2_line.format(x1=line[0], y1=line[1], x2=line[2], y2=line[3]))
coords1 = "{}.{}".format(line[0], line[1])
if coords1 not in seen:
seen.add(coords1)
th2_points.append(th2_point.format(x=line[0], y=line[1], station=line[4]))
th2_names.append(th2_name.format(x=line[0], y=line[1], station=line[4]))
coords2 = "{}.{}".format(line[2], line[3])
if "{}.{}".format(line[2], line[3]) not in seen:
seen.add(coords2)
if line[5] != None:
th2_points.append(th2_point.format(x=line[2], y=line[3], station=line[5]))
th2_names.append(th2_name.format(x=line[2], y=line[3], station=line[5]))
if isfile(output_path):
print(f"{Colors.WARNING}Warning: {Colors.ENDC}{os.path.basename(output_path)}{Colors.WARNING} file already exists - nothing done{Colors.ENDC}")
else :
name = TARGET,
# print(f"{Colors.GREEN}Therion output path :\t{Colors.ENDC}{output_path}")
with open(str(output_path), "w+") as f:
f.write(th2_file_header)
f.write(th2_file.format(
name = name[0],
Copyright = Copyright,
Copyright_Short = Copyright_Short,
points="\n".join(th2_points),
lines="\n".join(th2_lines) if LINES else "",
names="\n".join(th2_names) if NAMES else "",
projection=PROJECTION.lower(),
projection_short=PROJECTION[0].upper(),
author=Author,
year=datetime.now().year,
version = Version,
date=datetime.now().strftime("%Y.%m.%d-%H:%M:%S"),
X_Min=x_min*1.2,
X_Max=x_max*1.2,
Y_Min=y_min*1.2,
Y_Max=y_max*1.2,
X_Max_X_Min =x_ecart,
Y_Max_Y_Min =y_ecart,
insert_XVI = "{" + stations[next(iter(stations))][0] + "1 1.0} {"
+ stations[next(iter(stations))][1] + " "
+ stations[next(iter(stations))][2] +"} "
+ os.path.basename(th_name_xvi) + " 0 {}",
)
)
if scrap_to_add >= 1 :
for i in range(scrap_to_add):
other_scraps_plan = other_scraps_plan + f"\tbreak\n\tS{PROJECTION[0].upper()}-{name[0]}_{i+2:02}\n"
th2_scrap = """
scrap S{projection_short}-{name}_{num:02} -station-names "" "@{name}" -projection {projection} -author {year} "{author}" -copyright {year} "{Copyright_Short}"
endscrap
"""
f.write(th2_scrap.format(
name=name[0],
projection=PROJECTION.lower(),
projection_short=PROJECTION[0].upper(),
author=Author,
year=datetime.now().year,
Copyright_Short = Copyright_Short,
num=f"{i+2:02}",
)
)
#################################################################################################
# Parse the Extended XVI file #
#################################################################################################
if args.update == "th2":
th_name_xvi = DEST_PATH + "/" + TH_NAME + "-Extended.xvi"
else :
th_name_xvi = DEST_PATH + "/Data/" + TH_NAME + "-Extended.xvi"
print(f"{Colors.GREEN}Parsing extended XVI file:\t{Colors.ENDC}{th_name_xvi}")
# Parse the Extended XVI file
stations = {}
lines = []
with open(join(th_name_xvi), "r", encoding="utf-8") as f:
xvi_content = f.read()
xvi_stations, xvi_shots = xvi_content.split("XVIshots")
# Extract all the stations
for line in xvi_stations.split("\n"):
match = re.search(r"{\s*(-?\d+\.\d+)\s*(-?\d+\.\d+)\s([^@]+)(?:@([^\s}]*))?\s*}", line)
if match:
x = match.groups()[0]
y = match.groups()[1]
station_number = match.groups()[2]
namespace = match.groups()[3]
namespace_array = namespace.split(".") if namespace else []
station = station_number
if len(namespace_array) > 1:
station = "{}@{}".format(station_number, ".".join(namespace_array[0:-1]))
stations["{}.{}".format(x, y)] = [x, y, station]
# Extraire les valeurs x et y à partir des listes dans stations
x_values = [float(value[0]) for value in stations.values()]
y_values = [float(value[1]) for value in stations.values()]
# Trouver les min et max de x
x_min = float(min(x_values))
x_max = float(max(x_values))
# Trouver les min et max de y
y_min = float(min(y_values))
y_max = float(max(y_values))
x_ecart = x_max - x_min
y_ecart = y_max - y_min
# Afficher les résultats
# print("x_min:", x_min, "x_max:", x_max)
# print("y_min:", y_min, "y_max:", y_max)
# print("Écart max-min pour x:", x_ecart)
# print("Écart max-min pour y:", y_ecart)
# Extract all the lines
for line in xvi_shots.split("\n"):
match = re.search(r"^\s*{\s*(-?\d+\.\d+)\s*(-?\d+\.\d+)\s*(-?\d+\.\d+)\s*(-?\d+\.\d+)\s*.*}", line )
if match:
x1 = match.groups()[0]
y1 = match.groups()[1]
x2 = match.groups()[2]
y2 = match.groups()[3]
key1 = "{}.{}".format(x1, y1)
key2 = "{}.{}".format(x2, y2)
# Splays won't have stations
station1 = stations[key1][2] if key1 in stations else None
station2 = stations[key2][2] if key2 in stations else None
lines.append([x1, y1, x2, y2, station1, station2])
shutil.rmtree(tmpdir)
if args.update == "th2":
th2_name = DEST_PATH + "/" + TH_NAME
else :
th2_name = DEST_PATH + "/Data/" + TH_NAME
output_path = f'{th2_name}-Extended.{FORMAT}'
print(f"{Colors.GREEN}Writing output to:\t\t{Colors.ENDC}{output_path}")
# Write TH2
if FORMAT == "th2":
th2_file_header = """encoding utf-8"""
th2_file = """
##XTHERION## xth_me_area_adjust {X_Min} {Y_Min} {X_Max} {Y_Max}
##XTHERION## xth_me_area_zoom_to 100
##XTHERION## xth_me_image_insert {insert_XVI}
{Copyright}
# File generated by pyCreate_th2.py version {version} date: {date}
# x_min: {X_Min}, x_max: {X_Max} ecart : {X_Max_X_Min}
# y_min: {Y_Min}, y_max: {Y_Max} ecart : {Y_Max_Y_Min}
scrap SC-{name}_01 -station-names "" "@{name}" -projection extended -author {year} "{author}" -copyright {year} "{Copyright_Short}"
{points}
{names}
{lines}
endscrap"""
th2_point = """ point {x} {y} station -name {station}"""
th2_name = """ point {x} {y} station-name -align tr -scale xs -text {station}"""
th2_line = """ line u:Shot_Survey
{x1} {y1}
{x2} {y2}
endline
"""
seen = set()
th2_lines = []
th2_points = []
th2_names = []
other_scraps_extended = ""
for line in lines:
th2_lines.append(th2_line.format(x1=line[0], y1=line[1], x2=line[2], y2=line[3]))
coords1 = "{}.{}".format(line[0], line[1])
if coords1 not in seen:
seen.add(coords1)
th2_points.append(th2_point.format(x=line[0], y=line[1], station=line[4]))
th2_names.append(th2_name.format(x=line[0], y=line[1], station=line[4]))
coords2 = "{}.{}".format(line[2], line[3])
if "{}.{}".format(line[2], line[3]) not in seen:
seen.add(coords2)
if line[5] != None:
th2_points.append(th2_point.format(x=line[2], y=line[3], station=line[5]))
th2_names.append(th2_name.format(x=line[2], y=line[3], station=line[5]))
if isfile(output_path):
print(f"{Colors.WARNING}Warning: {Colors.ENDC}{os.path.basename(output_path)}{Colors.WARNING} file already exists - nothing done{Colors.ENDC}")
else :
name = TARGET,
# print(f"{Colors.GREEN}Therion output path :\t{Colors.ENDC}{output_path}")
with open(str(output_path), "w+") as f:
f.write(th2_file_header)
f.write(th2_file.format(
name = name[0],
Copyright = Copyright,
Copyright_Short = Copyright_Short,
points="\n".join(th2_points),
lines="\n".join(th2_lines) if LINES else "",
names="\n".join(th2_names) if NAMES else "",
projection="extended",
projection_short="C",
author=Author,
year=datetime.now().year,
version = Version,
date=datetime.now().strftime("%Y.%m.%d-%H:%M:%S"),
X_Min=x_min*1.2,
X_Max=x_max*1.2,
Y_Min=y_min*1.2,
Y_Max=y_max*1.2,
X_Max_X_Min =x_ecart,
Y_Max_Y_Min =y_ecart,
insert_XVI = "{" + stations[next(iter(stations))][0] + "1 1.0} {"
+ stations[next(iter(stations))][1] + " "
+ stations[next(iter(stations))][2] +"} "
+ os.path.basename(th_name_xvi) + " 0 {}",
)
)
if scrap_to_add >= 1 :
for i in range(scrap_to_add):
other_scraps_extended = other_scraps_extended + f"\tbreak\n\tSC-{name[0]}_{i+2:02}\n"
th2_scrap = """
scrap SC-{name}_{num:02} -station-names "" "@{name}" -projection extended -author {year} "{author}" -copyright {year} "{Copyright_Short}"
endscrap
"""
f.write(th2_scrap.format(
name=name[0],
author=Author,
Copyright_Short = Copyright_Short,
year=datetime.now().year,
num=f"{i+2:02}",
)
)
#################################################################################################
# Update -maps files #
#################################################################################################
if args.update == "":
config_vars = {
'fileName': TH_NAME,
'Author': Author,
'Copyright': Copyright,
'Scale' : SCALE,
'Target' : TARGET,
'map_comment' : map_comment,
'club' : club,
'thanksto' : thanksto,
'datat' : datat,
'wpage' : wpage,
'cs' : cs,
'other_scraps_plan' : other_scraps_plan,
'other_scraps_extended' : other_scraps_extended,
'file_info' : f"# File generated by pyCreate_th2.py version {Version} date: {datetime.now().strftime("%Y.%m.%d-%H:%M:%S")}",
}
process_template(DEST_PATH + '/template-maps.th', config_vars, DEST_PATH + '/' + TH_NAME + '-maps.th')
#################################################################################################
# Final therion compilation #
#################################################################################################
if args.update == "":
if final_therion_exe == True:
print(f"{Colors.GREEN}Final therion compilation{Colors.ENDC}")
PATH = os.path.dirname(args.survey_file) + "/" + TH_NAME + "/" + TH_NAME + ".thconfig"
compile_file(PATH, therion_path=therion_path)
+15
View File
@@ -0,0 +1,15 @@
numpy
ttkthemes
matplotlib
pandas
Shapely
Fiona
pyproj
scipy
netCDF4
xarray
joblib
geopandas
motionless
salem
configparser
+1 -1
View File
@@ -63,7 +63,7 @@ def hash_file(filepath, algo='sha256', chunk_size=8192):
for chunk in iter(lambda: f.read(chunk_size), b''):
hasher.update(chunk)
return hasher.hexdigest()
#####################################################################################################################################
# Fonction pour importer un fichier SQL dans une base de données SQLite #
+3
View File
@@ -31,6 +31,9 @@ encoding utf-8
# "jb-dessin.th2")
source Synthese-PSM_LARRA-tot.th
# pour le MNT avec une résolution spaciale de 1" STRM10
source SIG/DEM_SRTM30_UTM30_PSM.th
# Add Coordinates
#input Legendes/entrances_coordinates.th
+480 -480
View File
File diff suppressed because it is too large Load Diff