# Profile.py -- Pamhyr # Copyright (C) 2023 INRAE # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see <https://www.gnu.org/licenses/>. # -*- coding: utf-8 -*- import logging from tools import timer from Model.Geometry.Point import Point from Model.Except import NotImplementedMethodeError logger = logging.getLogger() class Profile(object): _id_cnt = 0 def __init__(self, id: int = -1, num: int = 0, kp: float = 0.0, name: str = "", code1: int = 0, code2: int = 0, _type: str = "", reach=None, status=None): super(Profile, self).__init__() self._status = status if id == -1: self.id = Profile._id_cnt else: self.id = id Profile._id_cnt = max(self.id, Profile._id_cnt+1) self._num = int(num) self._code1 = int(code1) self._code2 = int(code2) self._kp = float(kp) self._name = str(name) self._reach = reach self._sl = None self._points: List[Point] = [] self._profile_type = _type def __len__(self): return len(self.points) @property def number_points(self): return len(self.points) def _get_points_list(self): # Points list generator is type (int, Point) with the first # element the index of the Point in list return list( map( lambda p: p[1], sorted( self._points, key=lambda p: p[0] ) ) ) @property def points(self): if not isinstance(self._points, list): self._points = self._get_points_list() return self._points def point(self, index): return self.points[index] @property def reach(self): return self._reach @property def num(self): """ Returns: Number of profile. """ return self._num @num.setter def num(self, value: int): self._num = int(value) self._status.modified() @property def code1(self): """ Returns: Interpolation code 1. """ return self._code1 @code1.setter def code1(self, value: int): self._code1 = int(value) self._status.modified() @property def code2(self): """ Returns: Interpolation code 2. """ return self._code2 @code2.setter def code2(self, value: int): self._code2 = int(value) self._status.modified() @property def nb_points(self): return len(self.points) @property def kp(self): """ Returns: Kilometer point. """ return self._kp @kp.setter def kp(self, value: float): self._kp = float(value) self._status.modified() @property def name(self): """ Returns: Profile name. """ return self._name @name.setter def name(self, value: str): self._name = value.strip() self._status.modified() @property def sl(self): """ Returns: Profile sediment layers. """ return self._sl @sl.setter def sl(self, value): self._sl = value self._status.modified() @property def profile_type(self): """ Returns: Profile type. """ return self._profile_type @profile_type.setter def profile_type(self, value: str): self._profile_type = value self._status.modified() def point(self, i: int): if i < len(self.points): return self.points[i] return None def named_points(self): """List of named point Returns: The list of named point """ return [point for point in self.points if point.point_is_named()] def insert_point(self, index: int, point: Point): """Insert point at index. Args: index: The index of new profile. point: The point. Returns: Nothing. """ self.points.insert(index, point) self._status.modified() def delete(self, indexes: int): """Delete points at index Args: indexes: List of index of points. Returns: Nothing. """ points = set( map( lambda e: e[1], filter( lambda e: e[0] in indexes, enumerate(self.points) ) ) ) self.points = list( filter( lambda p: p not in points, self.points ) ) self._status.modified() def delete_points(self, points): """Delete some elements in profile list Args: points: The list of profile to delete Returns: Nothing. """ self.points = list( filter( lambda p: p not in points, self.points ) ) self._status.modified() # Move def move_up_point(self, index: int): if index < len(self.points): next = index - 1 p = self.points p[index], p[next] = p[next], p[index] self._status.modified() def move_down_point(self, index: int): if index >= 0: prev = index + 1 p = self.points p[index], p[prev] = p[prev], p[index] self._status.modified() # Sort @timer def sort(self, column, is_reversed: bool = False): def predicate(p): return p.x if column == 'y': def predicate(p): return p.y elif column == 'z': def predicate(p): return p.z self.points = sorted( self.points, key=predicate, reverse=is_reversed ) self._status.modified() @timer def sort_with_indexes(self, indexes: list): if len(self.points) != len(indexes): logger.critical("Indexes list do not correspond to point list") self.points = list( map( lambda x: x[1], sorted( enumerate(self.points), key=lambda x: indexes[x[0]] ) ) ) self._status.modified() # Sediment Layers def get_sl(self): """Get sediment layer height of points Get sediment layer of points (without spesific point sl) Returns: List of sediment layers height """ res = [] psl = [point.sl for point in self.points] # Compute max number of layers sl_max = 0 for sl in psl: n = 0 if sl is None else len(sl) sl_max = max(n, sl_max) # Create list of height for each sl and each layer for i in range(0, sl_max): cur = [] # Compute new layer line for each sl for sl in psl: if sl is not None and i < len(sl): cur.append(sl.get(i).height) else: cur.append(0) # Add layer line to result res.append(cur) return res # Abstract method, must be implemented for in non abstract class def get_station(self): raise NotImplementedMethodeError(self, self.get_station) # Computation method # Abstract method for width approximation def width_approximation(self): raise NotImplementedMethodeError(self, self.width_approximation) # Abstract method for width approximation def get_water_limits(self, z): raise NotImplementedMethodeError(self, self.get_water_limits)