######!/usr/bin/env python # -*- coding: utf8 -*- # coding: utf8 # Copyright (c) 2020 Xavier Robert # SPDX-License-Identifier: GPL-3.0-or-later # Modifié Alex 2025 07 01 # Modifié Alex 2026 01 04 -> print remplacés par log.xxx """ !---------------------------------------------------------! ! ! ! Tro to Therion ! ! ! ! Code to transform the .tro files ! ! Visual Topo into files that can ! ! be used by Therion ! ! ! ! Written by Xavier Robert ! ! ! !---------------------------------------------------------! ENGLISH : This code is to transform the .tro file from Visualtopo (http://vtopo.free.fr/) into files that can be read by Therion (http://therion.speleo.sk/). It reads .tro file and produce one .th file (file with survey data), and one thconfig file (file that is used to compile and build the survey with Therion). TODOS : - Correct the errors in encodings... This is the most important.... - Check all the situations possibles... - Add title to the centerline ! """ # Do divisions with Reals, not with integers # Must be at the beginning of the file from __future__ import division #from __future__ import unicode_literals import sys, os, wget, logging from urllib.error import HTTPError # Import Python modules #modulesNames = ['sys', 'warnings'] #for module in modulesNames: # try: # # because we want to import using a variable, do it this way # module_obj = __import__(module) # # create a global object containging our module # globals()[module] = module_obj # except ImportError: # sys.exit("ERROR : Module " + module + " not present. \n\n Please, install it \ # \n\n Edit the source code for more information") from .buildparam import * from .vtopotools import * from .datathwritetools import * from .buildthconfig import * import os, logging, sys from Lib.general_fonctions import Colors, safe_relpath import Lib.global_data as global_data log = logging.getLogger("Logger") ################################################################################################# def tro2th(fle_tro_fnme = None, fle_th_fnme = None, fle_tro_encoding=None, thlang = u'fr', cavename = None, icomments = True, icoupe = True, ithconfig = True, istructure = True, thconfigfnme = None, ithc = True, thcpath = None, thcfnme = u'config.thc', sourcefile = None, xviscale = 1000, xvigrid = 10, scale = 500, Errorfiles = True): """ Main function to convert tro to th files. This is this function that should be called from python. INPUTS: fle_tro_fnme : (string) Path and name of the .tro file to convert. if None (value by default), the function does not convert anything but build .thconfig and config.thc files If the path is not given, the function will look in the folder from where it is launched fle_th_fnme : (string) Path and name of the .th file to create from the .tro file. If None (value by default), this file is created from the .tro file name and in the same folder than that .tro file thlang : (string) String that set the language. 'fr' by default. If you need english, change 'fr' to 'en' in the function definition set 'fr' for french set 'en' for english ... other languages are not implemented cavename : (string) Name of the cave. If set to None (default value), it is get from the .tro file. icomments : (Boolean) To add (True, by default) or not (False) comments in the produced files icoupe : (Boolean) To set (True, by default) or not (False) an extended-elevation layout in the .thconfig file ithconfig : (Boolean) To set if the thconfig file is created (True, by default) or not istructure : (Boolean) To set if the structure and the addditional files are created (True, by default) or not thconfigfnme : (string) Path and name of the thconfig file. If None (by default), path and name build from the .tro file ithc : (Boolean) To build (True, by default) or not (False) a config file config.thc thcpath : (string) Path to the directry that contains the config file called in the cave.thconfig file. If used with ithc = False, this path is only used for the declaration in the cave.thconfig If used with ithc = True, the config file will be written in that directory. Set to None by default thcfnme : (string) Name of the config.thc (value by default if set to None or if ommitted) sourcefile : (list of strings) Define the source files declared in the cave.thconfig ex :['example.th', 'example.th2', 'example-coupe.th2'] If None or ommitted, it is build from the .tro file or the cavename xviscale : (Real) Scale of the xvi file. Set to 1000 by default that corresponds to 1/1000 xvigrid : (Real) Spacing of the grid for the xvi, in meters. Set 10 by default scale : (Real) scale of the map Set to 500 by default that corresponds to 1/500 Errorfiles : (Boolean) If True (by default), an error will be raised if output files exists in the folder If False, only a warning is raised, and the previous files are erased by the new ones. Use with caution OUTPUTS: Depending of the parameters inputed, several files can be produced cavename.th : survey data for Therion cavename.thconfig : file to build the pdf's maps and others confgi.thc : config file for the .thconfig file. USAGE: To build everything tro2th(fle_tro_fnme = 'Test', fle_th_fnme = 'Test', thlang = 'fr', cavename = 'Test', icomments = True, icoupe = True, ithconfig = True, thconfigfnme = None, ithc = True, thcpath = None, thcfnme = 'config.thc', sourcefiles = None, xviscale = 1000, xvigrid = 10, scale = 500, Errorfiles = True) To build only a .th file tro2th(fle_tro_fnme = 'Test', fle_th_fnme = 'Test', thlang = 'fr', cavename = 'Test', icomments = True, icoupe = True, ithconfig = False ithc = False Errorfiles = True) To build only a thonfig file, in english, without any comments and without extended elevation layout tro2th(thlang = 'en', cavename = 'Test', icomments = False, icoupe = False, ithconfig = False, thconfigfnme = None, ithc = False, thcpath = my/path/to/my/confg/file, thcfnme = 'config.thc', sourcefiles = ['Test.th', 'Test.th2'], xviscale = 1000, xvigrid = 10, scale = 500, Errorfiles = True) Author: Xavier Robert, Lima 2016/06/27 License: CCby-nc """ if thlang in [u'fr', u'FR', u'Fr', u'fR']: thlang = u'fr' elif thlang in [u'en',u'EN', u'En', u'eN']: thlang = u'en' else: raise NameError(u'ERROR: Language %s not implemented\n' u' Use "en" instead' % thlang ) log.info(u'____________________________________________________________\n\n\t\tTRO 2 THERION\n____________________________________________________________\n') if thlang == u'fr': log.info(u'\nÉcrit par Xavier Robert, Groupe spéléo Vulcain - Lyon, France\n') elif thlang == u'en': log.info(u'\nWritten by Xavier Robert, Groupe spéléo Vulcain - Lyon, France\n') log.info(u'____________________________________________________________\n\n') coordsyst = None coordinates = None if fle_tro_fnme is not None: if fle_tro_fnme[-4:] != u'.tro': fle_tro_fnme = fle_tro_fnme + u'.tro' # check if file exists if os.path.isfile(fle_tro_fnme) == False : if thlang == u'fr': raise NameError(u'ERROR : Le fichier {FileNa} n\'existe pas'.format(FileNa=str(fle_tro_fnme))) elif thlang == u'en': raise NameError(u'ERROR : File {FileNa} does not exist'.format(FileNa=str(fle_tro_fnme))) if fle_th_fnme is None: # convert tro file to th file log.info('1') entrance, cavename, coordinates, coordsyst, fle_th_fnme = convert_tro(fle_tro_fnme, fle_tro_encoding=fle_tro_encoding, icomments = icomments, icoupe = icoupe, istructure = istructure, thlang = thlang, Errorfiles = Errorfiles) else: log.info(2) entrance, cavename, coordinates, coordsyst, fle_th_fnme = convert_tro(fle_tro_fnme, fle_th_fnme, cavename, icomments = icomments, icoupe = icoupe, istructure = istructure, thlang = thlang, Errorfiles = Errorfiles) if thlang == u'fr': log.info(u'\tFichier Therion %s construit à partir des données %s' %(fle_th_fnme, fle_tro_fnme)) elif thlang == u'en': log.info(u'\tFile %s built from %s' %(fle_th_fnme, fle_tro_fnme)) else: if thlang == u'fr': log.info(u'\tPas de fichier .tro en entrée, pas de fichier de données .th créé...') elif thlang == u'en': log.info(u'\tNo .tro File input, no .th data file created...') # Build here the new structure: if istructure: build_structure(u'cave', Errorfiles = True) if sourcefile is None: if fle_th_fnme is None: if cavename is None: cavename = u'cave' sourcefile = [cavename.replace(u' ', u'_') + u'.th', u'#' + cavename.replace(u' ', u'_') + u'.th2', u'#' + cavename.replace(u' ', u'_') + u'-coupe.th2'] else: sourcefile = [fle_th_fnme, u'#' + fle_th_fnme[0:-4] + u'th2', u'#' + fle_th_fnme[0:-4] + u'-coupe.th2' ] # Build the dictionnary for the thconfig file dictcave = [sourcefile, xviscale, xvigrid, cavename, coordsyst, scale] # build thc file if ithc : if thcpath is not None : # Download the config file from my github page try: wget.download('https://raw.githubusercontent.com/robertxa/Th-Config-Xav/master/config.thc', thcpath) #thcpath + thcfnme) except HTTPError: if thcpath[-1] not in ['/', '\\']: thcpath = thcpath + '/' writethc(thcpath + thcfnme, istructure) except FileNotFoundError: if thcpath[-1] not in ['/', '\\']: thcpath = thcpath + '/' writethc(thcpath + thcfnme, istructure) else: # Download the config file from my github page try: if istructure: wget.download('https://raw.githubusercontent.com/robertxa/Th-Config-Xav/master/config.thc', cavename.replace(u' ', u'_') + '/') else: wget.download('https://raw.githubusercontent.com/robertxa/Th-Config-Xav/master/config.thc') except HTTPError: writethc(thcfnme, cavename, istructure) except FileNotFoundError: writethc(thcfnme, cavename, istructure) # build thconfig file # Needs to be change to take in account istructure if ithconfig : # write the file if thconfigfnme is None or thconfigfnme == u'' or thconfigfnme == u' ': if fle_th_fnme is None: thconfigfnme = cavename.replace(u' ', u'_') + u'.thconfig' else: thconfigfnme = fle_th_fnme[0:-3] + u'.thconfig' if thcpath is not None: thcfnme = thcpath + thcfnme writethconfig(cavename.replace(u' ', u'_') + thconfigfnme, icomments, icoupe, thlang, dictcave, ithc, thcfnme) else: thcfnme = thcfnme writethconfig(cavename.replace(u' ', u'_') + thconfigfnme, icomments, icoupe, thlang, dictcave, ithc, cavename.replace(u' ', u'_') + u'/config.thc') if istructure: # build cavename-tot.th file f3w = open(cavename.replace(u' ', u'_') + '/' + cavename.replace(u' ', u'_') + '-tot.th', 'w') write_thtot(f3w, cavename, icomments, thlang) f3w.closed log.info(u'\tFile ' + cavename.replace(u' ', u'_') + u'/' + cavename.replace(u' ', u'_') + u'-tot.th written...\n') # build cavename-maps.th file f4w = open(cavename.replace(u' ', u'_') + u'/' + cavename.replace(u' ', u'_') + '-maps.th', 'w') write_thmaps(f4w, cavename, icomments, thlang) f4w.closed log.info(u'\tFile ' + cavename.replace(u' ', u'_') + u'/' + cavename.replace(u' ', u'_') + u'-maps.th written...\n\n') # build Legends/entrances-coordinates.th file f5w = open(cavename.replace(u' ', u'_') + '/Legends/entrances_coordinates.th', 'w') write_thcoords(f5w, cavename, coordinates, coordsyst, icomments, thlang) f5w.closed log.info(u'\tFile ' + cavename.replace(u' ', u'_') + u'/Legends/entrances_coordinates.th written...\n\n') log.info(u'____________________________________________________________') log.info(u'') return ################################################################################################# def build_structure(cavename, Errorfiles = True): """ Check and build if needed the new structure: -Cave/ -Data/ -cavename.th (-cavename.th2) -Legends/ -entrances_coordinates.th -Outputs/ -outputs.txt -Cavename.thconfig -cavename-tot.th -cavename-maps.th -config.thc INPUTS: cavename = name of the cave that is used to build all the folders and file structure Errorfiles = Boolean; If True (Default), the program stops if the structure already exists If False or none, if the structure exists, it is erased OUTPUTS: None, except a new structure USAGE: build_structure(cavename, Errorfiles) """ # check if the folder cavename/ exists if os.path.exists(cavename.replace(u' ', u'_')): if Errorfiles: # Stop raise NameError(u'ERROR : Folder {FileNa} does exist'.format(FileNa=str(cavename.replace(u' ', u'_')))) else: log.warning(f"I have erased the folder {Colors.ENDC}{cavename.replace(u' ', u'_')}") if not os.path.exists(cavename.replace(u' ', u'_') + u'/Data'): os.mkdir(cavename.replace(u' ', u'_') + u'/Data') if not os.path.exists(cavename.replace(u' ', u'_') + u'/Legends'): os.mkdir(cavename.replace(u' ', u'_') + u'/Legends') if not os.path.exists(cavename.replace(u' ', u'_') + u'/Outputs'): os.mkdir(cavename.replace(u' ', u'_') + u'/Outputs') # Add outputs.txt file mkfle_output_txt(cavename.replace(u' ', u'_')) # - if no, create it and create the other files else: # Create the subfolders os.mkdir(cavename.replace(u' ', u'_')) os.mkdir(cavename.replace(u' ', u'_') + u'/Data') os.mkdir(cavename.replace(u' ', u'_') + u'/Outputs') # Add outputs.txt file mkfle_output_txt(cavename.replace(u' ', u'_')) os.mkdir(cavename.replace(u' ', u'_') + u'/Legends') return ################################################################################################# def mkfle_output_txt(cavename): """ Build the file Output.txt in the folder cavename/Outputs/ INPUTS: cavename = name of the cave OUTPUTS: None USAGE: create_output_txt(cavename) """ # Open the cavename/Outputs/outputs.txt file f1w = open(cavename.replace(u' ', u'_') + u'/Outputs/outputs.txt','w') f1w.write(u'Folder where Therion outputs are exported \n\n') # close the cavename/Outputs/outputs.txt file f1w.closed log.info(u'\tFile ' + cavename.replace(u' ', u'_') + u'/Outputs/outputs.txt written...') return ################################################################################################# def convert_tro(fle_tro_fnme, fle_tro_encoding=None, fle_th_fnme = None, cavename = None, icomments = True, icoupe = True, istructure = True, thlang = u'fr', Errorfiles = True): """ Function that manages the tro 2 th conversion INPUTS: fle_tro_fnme : path and file name of the .tro file to convert fle_th_fnme : path and file name of the .th file to create. If ommitted, set to None, and this varaible will be set in function of the fle_tro_fnme or cavename cavename : Name of the cave. If ommitted, it is set to None, and it is get from the .tro file icomments : (Boolean) To add (True, by default) or not (False) comments in the produced files icoupe : (Boolean) To set (True, by default) or not (False) an extended-elevation layout in the .thconfig file istructure : (Boolean) To set if the structure and the addditional files are created (True, by default) or not thlang : (string) String that set the language. 'fr' by default. If you need english, change 'fr' to 'en' in the function definition set 'fr' for french set 'en' for english Errorfiles : True (by default if ommitted) to get an error if the .th file already exists. False if only a warning... OUTPUTS: new .th file with surveyed data for Therion cavename : Name of the cave from the .tro file coordinates : Coordinates of the entrance coordsyst : Coordinates system used by the .tro file entrance : Entrance station USAGE: cavename, coordsyst = convert_tro(fle_tro_fnme, [fle_th_fnme = fle_th_fnme, cavename = cavename, Errorfiles = Errorfiles]) fle_th_fnme, cavename and Errorfiles can be ommitted. Author: Xavier Robert, Lima 2016/06/27 License: CCby-nc """ #from codecs import open # Initialization of some variables... #xcoord=0. #ycoord=0. #alt=0. # open the .tro survey log.info(f"Processing VisualTopo file: {Colors.ENDC}{safe_relpath(fle_tro_fnme)}") # print(' ') fle_tro = open(fle_tro_fnme, 'r', encoding=fle_tro_encoding) # read the .tro file try: lines = fle_tro.readlines() # change the encoding lines = convert_text(lines) except UnicodeDecodeError: # find the line where there is the error # initiate the line number lineNumber = 1 try: # read the first line line = fle_tro.redline() while line: # update the line number lineNumber += 1 # read the next line line = fle_tro.readline() except UnicodeDecodeError: log.error(f"Special or accentuated character not supporter line {Colors.ENDC}{lineNumber}{Colors.ERROR}, correct the input file") global_data.error_count += 1 # read the header coordinates = None cavename, coordinates, coordsyst, club, entrance, versionfle = read_vtopo_header(lines) if cavename is None or cavename == '' or cavename == ' ': cavename = u'cave' if fle_th_fnme is None: fle_th_fnme = cavename.replace(u' ', u'_') + u'.th' log.info (fle_th_fnme) if fle_th_fnme[-3:] != u'.th': fle_th_fnme = fle_th_fnme + u'.th' # Build here the new structure: if istructure: build_structure(cavename, Errorfiles) # check if file exists... checkfiles(fle_th_fnme, Errorfiles) # open the .th file if istructure: fle_th = open (cavename.replace(u' ', u'_') + '/Data/' + fle_th_fnme, 'w') else: fle_th = open (fle_th_fnme, 'w') # write the .th header writeheader_th(fle_th, cavename, entrance) # initiate variables i = 0 iline = [] dataold = [] # get line numbers of the lines beginning with 'Param' for line in lines: if u'Param' in line: iline.append(i) i+=1 for j in iline: # read the settings of the survey settings, comments = read_settings(lines[j].replace(u'\n', u'')) # read the data from the tro file data = read_data(lines, settings, j, iline) # write centerline header writecenterlineheader(fle_th, entrance, settings, comments, data, coordsyst, coordinates, club, icomments, thlang) # write the data to the .th file writedata(fle_th, settings, data, dataold) # write the end of the centerline in the .th file fle_th.write(u'\n\tendcenterline\n\n') dataold = data # write the end of the survey in the .th file fle_th.write(u'\nendsurvey\n') fle_th.close #print (fle_th_fnme) if thlang == u'fr': log.info(f"Fichier Therion {Colors.ENDC}{fle_th_fnme}{Colors.INFO} écrit !") elif thlang == u'en': log.info(f"Therion file {Colors.ENDC}{fle_th_fnme}{Colors.INFO} written!") return entrance, cavename, coordinates, coordsyst, fle_th_fnme ################################################################################################# if __name__ == u'__main__': # initiate variables # run the transformation tro2th()