Source code for wolfhece.xyz_file

"""
Author: HECE - University of Liege, Pierre Archambeau
Date: 2024

Copyright (c) 2024 University of Liege. All rights reserved.

This script and its content are protected by copyright law. Unauthorized
copying or distribution of this file, via any medium, is strictly prohibited.
"""

# Fichier xyz
import numpy as np
import matplotlib.pyplot as plt
from os.path import normpath,exists,join,basename
from os import listdir,scandir
import logging

from .PyTranslate import _

[docs] class XYZFile: """ Classe pour la gestion des fichiers xyz """
[docs] x:np.array
[docs] y:np.array
[docs] z:np.array
[docs] filename:str
def __init__(self, fname:str, toread:bool= True, folder:str= None, bounds:list= None, delimiter:str=','): """ Initialisation du nom du fichier """ self.filename = fname self.x = None self.y = None self.z = None self.xyz = None if toread: self.read_from_file(folder=folder, bounds=bounds, delimiter=delimiter) @property
[docs] def nblines(self): if self.x is None: return 0 else: return len(self.x)
[docs] def reset(self): """ Reset des données """ self.x = None self.y = None self.z = None self.xyz = None
[docs] def test_bounds(self,bounds): if bounds is None: return True x1=bounds[0][0] x2=bounds[0][1] y1=bounds[1][0] y2=bounds[1][1] mybounds = self.get_extent() test = not(x2 < mybounds[0][0] or x1 > mybounds[0][1] or y2 < mybounds[1][0] or y1 > mybounds[1][1]) return test
[docs] def read_from_file(self, folder:str=None, bounds:list=None, delimiter=','): """ Lecture d'un fichier xyz et remplissage de l'objet """ try: if folder is None: self.xyz = np.genfromtxt(self.filename, delimiter=delimiter, dtype=np.float32) else: if bounds is None: self.reset() logging.error(_('Bounds must be defined when reading a directory')) return self.xyz = xyz_scandir(folder,bounds=bounds,delimiter=delimiter) # check if self.xyz is an empty array if len(self.xyz) == 0: self.xyz = None return except: self.reset() logging.error(_('Error reading file: {self.filename}')) return self.x = self.xyz[:,0] self.y = self.xyz[:,1] self.z = self.xyz[:,2]
[docs] def fill_from_wolf_array(self, myarray,nullvalue=0.): """ Création d'un fichier xyz depuis les données d'un WOLF array """ nbmaxlines = myarray.nbx * myarray.nby self.x = np.zeros(nbmaxlines) self.y = np.zeros(nbmaxlines) self.z = np.zeros(nbmaxlines) k=0 for cury in range(myarray.nby): y = cury * myarray.dy + 0.5 * myarray.dy + myarray.origy + myarray.transly for curx in range(myarray.nbx): z = myarray.array[curx, cury] if z != nullvalue: x = curx * myarray.dx + 0.5 * myarray.dx + myarray.origx + myarray.translx self.x[k] = x self.y[k] = y self.z[k] = z k+=1 # crop the arrays self.x = self.x[:k] self.y = self.y[:k] self.z = self.z[:k]
[docs] def write_to_file(self): """ Ecriture des informations dans un fichier """ with open(self.filename, 'w') as f: for i in range(self.nblines): f.write('{:.3f},{:.3f},{:.3f}\n'.format(self.x[i], self.y[i], self.z[i]))
[docs] def get_extent(self): """ Retourne les limites du rectangle qui encadre le nuage de points """ xlim = [np.min(self.x), np.max(self.x)] ylim = [np.min(self.y), np.max(self.y)] return (xlim, ylim)
[docs] def merge(self, xyz_list:list["XYZFile"]): """ Merge des fichiers xyz en 1 seul """ newxyz = np.concatenate([cur.xyz for cur in xyz_list]) if self.xyz is not None: self.xyz = np.concatenate([self.xyz,newxyz]) else: self.xyz = newxyz self.x = self.xyz[:,0] self.y = self.xyz[:,1] self.z = self.xyz[:,2]
[docs] def plot(self): """ Représentation graphique des points """ plt.scatter(self.x, self.y, c=self.z, marker='.', cmap='viridis', edgecolors='none') plt.xlabel('x [m]') plt.ylabel('y [m]') plt.colorbar() plt.axis('equal') plt.savefig('figure.png',dpi=300) plt.ion() plt.show() plt.pause(0.1)
[docs] def find_points(self,bounds): if bounds is None: return self.xyz xb=bounds[0] yb=bounds[1] # Get arrays which indicate invalid X, Y, or Z values. X_valid = (xb[0] <= self.x) & (xb[1] > self.x) Y_valid = (yb[0] <= self.y) & (yb[1] > self.y) good_indices = np.where(X_valid & Y_valid)[0] return self.xyz[good_indices]
[docs] def is_float(s): try: float(s) return True except ValueError: return False
[docs] def xyz_scandir(mydir:str, bounds:list, delimiter:str=',') -> np.ndarray: """ Function that reads all the xyz files in a directory and its subdirectories :param mydir: directory to scan :dtype mydir: str :param bounds: bounds of the area to consider [[x1,x2],[y1,y2]] :dtype bounds: list :return: list of points """ first=[] for curfile in listdir(mydir): if curfile.endswith('.xyz'): mydata = XYZFile(join(mydir,curfile), delimiter=delimiter) if mydata.test_bounds(bounds): if isinstance(mydir,str): logging.info(mydir) else: logging.info(mydir.path) logging.info(curfile) first.append(mydata.find_points(bounds)) # if there is a subdirectory, we go deeper for entry in scandir(mydir): if entry.is_dir(): locf=xyz_scandir(entry, bounds, delimiter) if len(locf)>0: first.append(locf) retfirst=[] if len(first)>0 : retfirst=np.concatenate(first) return retfirst