Pamhyr2/src/Model/Geometry/Profile.py

359 lines
8.5 KiB
Python

# Profile.py -- Pamhyr
# Copyright (C) 2023-2025 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 shapely import geometry
from Model.Geometry.Point import Point
from Model.Except import NotImplementedMethodeError
logger = logging.getLogger()
class Profile(object):
def __init__(self, id: int = -1, num: int = 0,
rk: float = 0.0, name: str = "",
code1: int = 0, code2: int = 0,
_type: str = "", reach=None,
status=None, owner_scenario=-1):
super(Profile, self).__init__(
id=id, status=status,
owner_scenario=owner_scenario
)
self._num = int(num)
self._code1 = int(code1)
self._code2 = int(code2)
self._rk = float(rk)
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):
return list(self._points)
@property
def points(self):
if not isinstance(self._points, list):
self._points = self._get_points_list()
return list(
filter(
lambda p: not p.is_deleted(),
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.modified()
@property
def code1(self):
"""
Returns:
Interpolation code 1.
"""
return self._code1
@code1.setter
def code1(self, value: int):
self._code1 = int(value)
self.modified()
@property
def code2(self):
"""
Returns:
Interpolation code 2.
"""
return self._code2
@code2.setter
def code2(self, value: int):
self._code2 = int(value)
self.modified()
@property
def nb_points(self):
return len(self.points)
@property
def rk(self):
"""
Returns:
Kilometer point.
"""
return self._rk
@rk.setter
def rk(self, value: float):
self._rk = float(value)
self.modified()
@property
def name(self):
"""
Returns:
Profile name.
"""
return self._name
@name.setter
def name(self, value: str):
self._name = value.strip()
self.modified()
@property
def sl(self):
"""
Returns:
Profile sediment layers.
"""
return self._sl
@sl.setter
def sl(self, value):
self._sl = value
self.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.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.
"""
if point in self._points:
point.set_as_not_deleted()
else:
self.points.insert(index, point)
self.modified()
def delete_i(self, indexes: list):
list(
map(
lambda e: e[1].set_as_deleted(),
filter(
lambda e: e[0] in indexes,
enumerate(self.points)
)
)
)
self.modified()
def delete_points(self, points):
"""Delete some elements in profile list
Args:
points: The list of profile to delete
Returns:
Nothing.
"""
list(
map(
lambda p: p.set_as_deleted(),
points
)
)
self.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.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.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.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.modified()
@timer
def reverse(self):
self._points.reverse()
self.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)
def wet_points(self, z):
raise NotImplementedMethodeError(self, self.wet_point)
def wet_width(self, z):
raise NotImplementedMethodeError(self, self.wet_width)
def wet_perimeter(self, z):
raise NotImplementedMethodeError(self, self.wet_perimeter)
def wet_area(self, z):
raise NotImplementedMethodeError(self, self.wet_area)
def wet_radius(self, z):
raise NotImplementedMethodeError(self, self.wet_radius)
def get_nb_wet_areas(self, z):
raise NotImplementedMethodeError(self, self.get_nb_wet_areas)