# -*- coding: utf-8 -*- import numpy as np from typing import List from tools import timer from Model.Except import ClipboardFormatError from Model.Geometry.Profile import Profile from Model.Geometry.PointXYZ import PointXYZ from Model.Geometry.Vector_1d import Vector1d class ProfileXYZ(Profile): def __init__(self, name: str = "", kp: float = 0., reach = None, num = 0, nb_point: int = 0, code1: int = 0, code2: int = 0): """ProfileXYZ constructor Args: num: The number of this profile code1: The interpolation code 1 code2: The interpolation code 2 kp: Kilometer point name: The name of profile Returns: Nothing. """ super(ProfileXYZ, self).__init__( num = num, name = name, kp = kp, code1 = code1, code2 = code2, _type = "XYZ", reach = reach, ) @classmethod def from_data(cls, header, data): profile = None try: if len(header) == 0: profile = cls( *data ) else: valid_header = {'name', 'reach', 'kp'} d = {} for i, v in enumerate(data): h = header[i].strip().lower().split(' ')[0] if h in valid_header: d[h] = v profile = cls(**d) except Exception as e: raise ClipboardFormatError(header, data) return profile def x(self): return [point.x for point in self._points] def y(self): return [point.y for point in self._points] def z(self): return [point.z for point in self._points] def names(self): return [point.name for point in self._points] def x_max(self): return max(self.filter_isnan(self.x())) def x_min(self): return min(self.filter_isnan(self.x())) def y_max(self): return max(self.filter_isnan(self.y())) def y_min(self): return min(self.filter_isnan(self.y())) def z_max(self): return max(self.filter_isnan(self.z())) def z_min(self): return min(self.filter_isnan(self.z())) def import_points(self, list_points: list): """Import a list of points to profile Args: list_points: Liste of PointXYZ Returns: Nothing. """ for point in list_points: pt = PointXYZ(*point) self._points.append(pt) def get_point_i(self, index: int) -> PointXYZ: """Get point at index. Args: index: Index of point. Returns: The point. """ try: return self._points[index] except IndexError: raise IndexError(f"Invalid point index: {index}") def add(self): """Add a new PointXYZ to profile. Returns: Nothing. """ point_xyz = PointXYZ(0., 0., 0.) self._points.append(point_xyz) def insert(self, index: int): """Insert a new point at index. Args: index: The index of new profile. Returns: The new point. """ point = PointXYZ(0., 0., 0.) self._points.insert(index, point) return point def filter_isnan(self, lst): """Returns the input list without 'nan' element Args: lst: The list to filter Returns: The list without 'nan' """ return [x for x in lst if not np.isnan(x)] def _first_point_not_nan(self): first_point = self._points[0] for point in self._points: if not point.is_nan(): first_point = point break return first_point def _last_point_not_nan(self): last_point = self._points[-1] for point in self._points[::-1]: if not point.is_nan(): last_point = point break return last_point @timer def get_station(self) -> np.ndarray: """Projection of the points of the profile on a plane. Args: profile: The profile Returns: Projection of the points of the profile on a plane. """ if self.nb_points < 3: return None else: first_named_point = None index_first_named_point = None last_named_point = None first_point_not_nan = self._first_point_not_nan() last_point_not_nan = self._last_point_not_nan() for index, point in enumerate(self._points): if point.point_is_named(): index_first_named_point = index first_named_point = point break for point in reversed(self._points): if point.point_is_named(): last_named_point = point break station = [] constant = 0.0 if (first_named_point is not None and last_named_point is not None): if (first_named_point != last_named_point and first_named_point.x != last_named_point.x): vector = Vector1d(first_named_point, last_named_point) normalized_direction_vec = vector.normalized_direction_vector() else: vector = Vector1d(first_point_not_nan, last_point_not_nan) normalized_direction_vec = vector.normalized_direction_vector() for point in self._points: xi = point.x - first_named_point.x yi = point.y - first_named_point.y station_i = (normalized_direction_vec[0] * xi + normalized_direction_vec[1] * yi) station.append(station_i) constant = station[index_first_named_point] elif first_named_point is None: vector = Vector1d(first_point_not_nan, last_point_not_nan) normalized_direction_vec = vector.normalized_direction_vector() for point in self._points: xi = point.x - first_point_not_nan.x yi = point.y - first_point_not_nan.y station_i = (normalized_direction_vec[0] * xi + normalized_direction_vec[1] * yi) station.append(station_i) z_min = self.z_min() index_profile_z_min = list( filter( lambda z: z[1] == z_min, enumerate(self.z()) ) )[0] constant = station[index_profile_z_min[0]] return list(map(lambda s: s - constant, station))