Files
Synthese-PSM_LARRA/Scripts/pyCreateTh/pyCreate_th2_Old.py
T
Alex38Lyon 6882d52675 pyCreateTh
2025-06-16 07:37:02 +02:00

993 lines
41 KiB
Python

"""
#############################################################################################
# #
# 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)