mirror of https://gitlab.com/pamhyr/pamhyr2
Model: Add Geomerty model from previous work.
parent
4a954a43ba
commit
2a00593332
|
|
@ -0,0 +1,228 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
import numpy as np
|
||||
|
||||
from time import time
|
||||
from typing import List
|
||||
from copy import deepcopy
|
||||
from operator import itemgetter
|
||||
|
||||
from Model.Geometry.PointXYZ import PointXYZ
|
||||
from Model.Geometry.ProfileXYZ import ProfileXYZ
|
||||
|
||||
# TypeProfileXYZ = List[ProfileXYZ]
|
||||
|
||||
|
||||
class Geometry:
|
||||
"""
|
||||
Reach geometry
|
||||
"""
|
||||
def __init__(self, parent=None):
|
||||
self.parent = parent
|
||||
self._list_profiles: List[ProfileXYZ] = []
|
||||
self.file_st = ""
|
||||
self.__list_copied_profiles = []
|
||||
|
||||
def __repr__(self):
|
||||
return f"\n===== Début liste des profils ======> {np.array(self._list_profiles)}" \
|
||||
f"\n<====== Fin liste des profils ====="
|
||||
|
||||
@property
|
||||
def number_profiles(self):
|
||||
"""
|
||||
Returns:
|
||||
Number of profiles
|
||||
"""
|
||||
return len(self._list_profiles)
|
||||
|
||||
def get_geometry(self) -> List[ProfileXYZ]:
|
||||
"""
|
||||
Returns:
|
||||
The profiles list.
|
||||
"""
|
||||
return self._list_profiles
|
||||
|
||||
def get_profile_i(self, i: int) -> ProfileXYZ:
|
||||
"""
|
||||
Args:
|
||||
i: Index
|
||||
Returns:
|
||||
The profile at index i.
|
||||
"""
|
||||
try:
|
||||
return self._list_profiles[i]
|
||||
except IndexError:
|
||||
raise IndexError(f"Le bief a moins de {i} profil(s)")
|
||||
|
||||
def import_geometry(self, file_path_name: str):
|
||||
"""Import a geometry from file (.ST or .st)
|
||||
|
||||
Args:
|
||||
file_path_name: The absolute path of geometry file (.ST or .st) to import.
|
||||
|
||||
Returns:
|
||||
Nothing.
|
||||
"""
|
||||
self.file_st = str(file_path_name)
|
||||
list_profile, list_header = self.read_file_st()
|
||||
# print(list_profile, "\n", list_header)
|
||||
if list_profile and list_header:
|
||||
for ind, profile in enumerate(list_profile):
|
||||
prof = ProfileXYZ(*list_header[ind])
|
||||
prof.import_points(profile)
|
||||
self._list_profiles.append(prof)
|
||||
self._update_profile_numbers()
|
||||
|
||||
else:
|
||||
print("Fichier introuvable ou non conforme !")
|
||||
|
||||
def add(self):
|
||||
"""Add a new profile at the end of profiles list
|
||||
|
||||
Returns:
|
||||
Nothing.
|
||||
"""
|
||||
nb_profile = self.number_profiles
|
||||
profile = ProfileXYZ()
|
||||
profile.num = nb_profile + 1
|
||||
self._list_profiles.append(profile)
|
||||
|
||||
def _update_profile_numbers(self):
|
||||
"""Update profiles index
|
||||
|
||||
Returns:
|
||||
Nothing.
|
||||
"""
|
||||
for ind, profile in enumerate(self.get_geometry()):
|
||||
profile.num = ind + 1
|
||||
|
||||
def insert(self, index: int):
|
||||
"""Insert new profile in list
|
||||
|
||||
Args:
|
||||
index: The position of the new profile.
|
||||
|
||||
Returns:
|
||||
Nothing.
|
||||
"""
|
||||
profile = ProfileXYZ()
|
||||
self._list_profiles.insert(index, profile)
|
||||
self._update_profile_numbers()
|
||||
|
||||
def delete(self, list_index: list):
|
||||
"""Delete some elements in profile list
|
||||
|
||||
Args:
|
||||
list_index: The list of element index
|
||||
|
||||
Returns:
|
||||
Nothing.
|
||||
"""
|
||||
try:
|
||||
if list_index:
|
||||
indices = sorted(list(set(list_index)), reverse=True)
|
||||
for idx in indices:
|
||||
# if idx < len(self._list_profiles) :
|
||||
try:
|
||||
self._list_profiles.pop(idx)
|
||||
self._update_profile_numbers()
|
||||
except IndexError:
|
||||
print("Liste vide, rien à supprimer !")
|
||||
except TypeError:
|
||||
if isinstance(list_index, int):
|
||||
self._list_profiles.pop(list_index)
|
||||
self._update_profile_numbers()
|
||||
print(f"\nSuppression --> attention !!!!\nL'argument {list_index} doit être une liste!\n")
|
||||
else:
|
||||
raise TypeError(f"L'argument {list_index} fourni est de type incorrect")
|
||||
|
||||
def _sort(self, is_reversed: bool = False):
|
||||
self._list_profiles = sorted(
|
||||
self._list_profiles,
|
||||
key=lambda profile: profile.pk,
|
||||
reverse=is_reversed
|
||||
)
|
||||
|
||||
def sort_ascending(self):
|
||||
"""Sort profiles by increasing KP
|
||||
|
||||
Returns:
|
||||
Nothing.
|
||||
"""
|
||||
self._sort(is_reversed=False)
|
||||
|
||||
def sort_descending(self):
|
||||
"""Sort profiles by decreasing KP
|
||||
|
||||
Returns:
|
||||
Nothing.
|
||||
"""
|
||||
self._sort(is_reversed=True)
|
||||
|
||||
def copy(self, index_list: List[int]):
|
||||
self.__list_copied_profiles.clear()
|
||||
index_list = list(set(index_list)) # delete duplicate index
|
||||
for index in index_list:
|
||||
try:
|
||||
self.__list_copied_profiles.append(deepcopy(self.get_profile_i(index)))
|
||||
except IndexError:
|
||||
raise IndexError(f"Echec de la copie, l'indice {index} n'existe pas !")
|
||||
|
||||
def paste(self):
|
||||
if self.__list_copied_profiles:
|
||||
for profile in self.__list_copied_profiles:
|
||||
self._list_profiles.append(profile)
|
||||
print("self.__list_copied_profiles", self.__list_copied_profiles, "\n *****")
|
||||
|
||||
def read_file_st(self):
|
||||
"""Read the ST file
|
||||
|
||||
Returns:
|
||||
List of profiles and list of headers.
|
||||
"""
|
||||
t0 = time()
|
||||
line_is_header = True
|
||||
list_point_profile = []
|
||||
list_profile = []
|
||||
list_header = []
|
||||
stop_code = "999.999"
|
||||
try:
|
||||
with open(self.file_st, encoding="utf-8") as file_st:
|
||||
for line in file_st:
|
||||
if not (line.startswith("#") or line.startswith("*")):
|
||||
line = line.split()
|
||||
if line_is_header:
|
||||
if len(line) >= 6:
|
||||
list_header.append(line[:6])
|
||||
elif len(line) == 5:
|
||||
line.append("")
|
||||
list_header.append(line)
|
||||
else:
|
||||
print(f"Point {line} invalide ==> pas pris en compte")
|
||||
line_is_header = False
|
||||
else:
|
||||
# Read until "999.9990 999.9990" as found
|
||||
if len(line) == 3:
|
||||
x, y, z = line
|
||||
if stop_code in x and stop_code in y:
|
||||
line_is_header = True
|
||||
list_profile.append(list_point_profile)
|
||||
list_point_profile = []
|
||||
else:
|
||||
line.append("")
|
||||
list_point_profile.append(line)
|
||||
elif len(line) == 4:
|
||||
x, y, z, ld = line
|
||||
if stop_code in x and stop_code in y:
|
||||
list_profile.append(list_point_profile)
|
||||
list_point_profile = []
|
||||
line_is_header = True
|
||||
else:
|
||||
list_point_profile.append(line)
|
||||
else:
|
||||
pass
|
||||
|
||||
except FileNotFoundError:
|
||||
print(f"\n \n %%%%%% Fichier : {self.file_st} introuvable !! %%%%%%")
|
||||
print("****** Fichier {} lu et traité en {} secondes *******".format(self.file_st, time() - t0))
|
||||
return list_profile, list_header
|
||||
|
|
@ -0,0 +1,78 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
from math import dist
|
||||
from pandas import isna as pd_is_na
|
||||
|
||||
class PointXYZ:
|
||||
def __init__(self, x: float, y: float, z: float, point_name: str = ""):
|
||||
self.x = float(x)
|
||||
self.y = float(y)
|
||||
self.z = float(z)
|
||||
self.name = point_name
|
||||
self.points = [self.x, self.y, self.z, self.name]
|
||||
|
||||
def __repr__(self):
|
||||
point_xyz_name = f"({self.x}, {self.y},{self.z}, {self.name})"
|
||||
return point_xyz_name
|
||||
|
||||
@property
|
||||
def x(self):
|
||||
return self._x
|
||||
|
||||
@x.setter
|
||||
def x(self, value):
|
||||
self._x = float(value)
|
||||
|
||||
@property
|
||||
def y(self):
|
||||
return self._y
|
||||
|
||||
@y.setter
|
||||
def y(self, value):
|
||||
self._y = float(value)
|
||||
|
||||
# self.points[1] = self._y
|
||||
|
||||
@property
|
||||
def z(self):
|
||||
return self._z
|
||||
|
||||
@z.setter
|
||||
def z(self, value):
|
||||
self._z = float(value)
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
return self._name
|
||||
|
||||
@name.setter
|
||||
def name(self, point_name):
|
||||
self._name = point_name
|
||||
|
||||
def point_is_named(self):
|
||||
"""
|
||||
Returns:
|
||||
True if the point is named.
|
||||
"""
|
||||
return len(self.name.strip()) != 0
|
||||
|
||||
@property
|
||||
def is_nan(self):
|
||||
"""
|
||||
Returns:
|
||||
True if at least one coordinate is as np.nan
|
||||
"""
|
||||
return pd_is_na(self.x) or pd_is_na(self.y) or pd_is_na(self.z)
|
||||
|
||||
@staticmethod
|
||||
def distance(p1, p2):
|
||||
"""Euclidean distance between p1 and p2.
|
||||
|
||||
Args:
|
||||
p1: A XYZ Point
|
||||
p2: A XYZ Point
|
||||
|
||||
Returns:
|
||||
Euclidean distance between the two points
|
||||
"""
|
||||
return dist((p1.x, p1.y, p1.z), (p2.x, p2.y, p2.z))
|
||||
|
|
@ -0,0 +1,199 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
from typing import List
|
||||
|
||||
from Model.Geometry.PointXYZ import PointXYZ
|
||||
|
||||
class ProfileXYZ:
|
||||
def __init__(self, num: int = 0, code1: int = 0, code2: int = 0,
|
||||
nb_points: int = 0, kp: float = 0., name: str = ""):
|
||||
"""ProfileXYZ constructor
|
||||
|
||||
Args:
|
||||
num: The number of this profile
|
||||
code1: The interpolation code 1
|
||||
code2: The interpolation code 2
|
||||
nb_points: Number of points
|
||||
kp: Kilometer point
|
||||
name: The name of profile
|
||||
|
||||
Returns:
|
||||
Nothing.
|
||||
"""
|
||||
self._num = int(num)
|
||||
self._code1 = int(code1)
|
||||
self._code2 = int(code2)
|
||||
self._nb_points = int(nb_points)
|
||||
self._kp = float(kp)
|
||||
self._name = str(name)
|
||||
self._list_points: List[PointXYZ] = []
|
||||
|
||||
def __repr__(self):
|
||||
df = pd.DataFrame(columns=["X", "Y", "Z", "Name"],
|
||||
data=[[p.x, p.y, p.z, p.name] for p in self._list_points])
|
||||
return f"\n{self.header}\n{df}"
|
||||
|
||||
@property
|
||||
def num(self):
|
||||
"""
|
||||
Returns:
|
||||
Number of profile.
|
||||
"""
|
||||
return self._num
|
||||
|
||||
@num.setter
|
||||
def num(self, value: int):
|
||||
self._num = int(value)
|
||||
|
||||
@property
|
||||
def code1(self):
|
||||
"""
|
||||
Returns:
|
||||
Interpolation code 1.
|
||||
"""
|
||||
return self._code1
|
||||
|
||||
@code1.setter
|
||||
def code1(self, value: int):
|
||||
self._code1 = int(value)
|
||||
|
||||
@property
|
||||
def code2(self):
|
||||
"""
|
||||
Returns:
|
||||
Interpolation code 2.
|
||||
"""
|
||||
return self._code2
|
||||
|
||||
@code2.setter
|
||||
def code2(self, value: int):
|
||||
self._code2 = int(value)
|
||||
|
||||
@property
|
||||
def nb_points(self):
|
||||
return self._nb_points
|
||||
|
||||
# @nb_points.setter
|
||||
# def nb_points(self, nb: int):
|
||||
# self._nb_points = int(nb)
|
||||
|
||||
@property
|
||||
def kp(self):
|
||||
"""
|
||||
Returns:
|
||||
Kilometer point.
|
||||
"""
|
||||
return self._kp
|
||||
|
||||
@kp.setter
|
||||
def kp(self, value: float):
|
||||
self._kp = float(value)
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"""
|
||||
Returns:
|
||||
Profile name.
|
||||
"""
|
||||
return self._name
|
||||
|
||||
@name.setter
|
||||
def name(self, other: str):
|
||||
self._name = other
|
||||
|
||||
@property
|
||||
def header(self):
|
||||
"""
|
||||
Returns:
|
||||
Profile header.
|
||||
"""
|
||||
return np.array([self._num, self._code1, self._code2, self._nb_points, self.kp, self._name])
|
||||
|
||||
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._list_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._list_points[index]
|
||||
except IndexError:
|
||||
raise IndexError(f"Le profil a moins de {index} points !")
|
||||
|
||||
def add(self):
|
||||
"""Add a new PointXYZ to profile.
|
||||
|
||||
Returns:
|
||||
Nothing.
|
||||
"""
|
||||
point_xyz = PointXYZ(0., 0., 0.)
|
||||
self._list_points.append(point_xyz)
|
||||
|
||||
def delete(self, index: int):
|
||||
"""Delete the point at index
|
||||
|
||||
Args:
|
||||
index: Index of point.
|
||||
|
||||
Returns:
|
||||
Nothing.
|
||||
"""
|
||||
try:
|
||||
self._list_points.pop(index)
|
||||
except IndexError:
|
||||
raise IndexError(f"Suppression échouée, l'indice {index} n'existe pas !")
|
||||
|
||||
def insert(self, index: int):
|
||||
"""Insert a new profile at index.
|
||||
|
||||
Args:
|
||||
index: The index of new profile.
|
||||
|
||||
Returns:
|
||||
Nothing.
|
||||
"""
|
||||
profile = ProfileXYZ()
|
||||
self._list_points.insert(index, profile)
|
||||
|
||||
def delete1(self, list_index: list):
|
||||
"""Delete a list of points
|
||||
|
||||
Args:
|
||||
list_index: Indexes list.
|
||||
|
||||
Returns:
|
||||
Nothing.
|
||||
"""
|
||||
try:
|
||||
if list_index:
|
||||
indices = sorted(list(set(list_index)), reverse=True)
|
||||
for idx in indices:
|
||||
# if idx < len(self._list_profiles) :
|
||||
try:
|
||||
self._list_points.pop(idx)
|
||||
except IndexError:
|
||||
print("Liste vide, rien à supprimer !")
|
||||
except TypeError:
|
||||
if isinstance(list_index, int):
|
||||
self._list_points.pop(list_index)
|
||||
print(f"\nSuppression --> attention !!!!\nL'argument {list_index} doit être une liste!\n")
|
||||
else:
|
||||
raise TypeError(f"L'argument {list_index} fourni est de type incorrect")
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
from Model.Geometry.Geometry import Geometry
|
||||
|
||||
class Reach:
|
||||
def __init__(self, name: str = "",
|
||||
upstream_node: str = None,
|
||||
downstream_node: str = None,
|
||||
parent=None):
|
||||
self._name = name
|
||||
self._name_upstream_node = name_upstream_node
|
||||
self._name_downstream_node = name_downstream_node
|
||||
self.parent = parent
|
||||
self._geometry = Geometry(parent=self)
|
||||
|
||||
def __repr__(self):
|
||||
return f"Bief : {self._name}\n Nb de sections : {self._geometry.number_profiles}"
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
return self._name
|
||||
|
||||
@property
|
||||
def name_upstream_node(self):
|
||||
return self._name_upstream_node
|
||||
|
||||
@property
|
||||
def name_downstream_node(self):
|
||||
return self._name_downstream_node
|
||||
|
||||
@property
|
||||
def geometry(self):
|
||||
return self._geometry
|
||||
Loading…
Reference in New Issue