mirror of
https://gitlab.com/pamhyr/pamhyr2.git
synced 2026-10-30 03:20:41 +01:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
28c23ee580 | ||
|
|
71c5aec876 | ||
|
|
3ce9e094e7 | ||
|
|
c4554d38c1 | ||
|
|
4deed897ff | ||
|
|
460450f524 | ||
|
|
60336c25f1 | ||
|
|
82093bcf95 | ||
|
|
7b8191e2f0 | ||
|
|
88dd9cf15a | ||
|
|
7d4091eb24 | ||
|
|
59fc80a10b | ||
|
|
ea146cbe87 | ||
|
|
c6b28ba3a0 | ||
|
|
fe57890da4 | ||
|
|
8cd9ec939b | ||
|
|
872ff0ef62 | ||
|
|
052faed48d | ||
|
|
31b37a7792 | ||
|
|
dfe12c8bcb | ||
|
|
89d5b2d576 | ||
|
|
601ef73b5d | ||
|
|
e4f8bc347d | ||
|
|
6c4b67d93a | ||
|
|
2ee8420b94 | ||
|
|
bbe127865b | ||
|
|
50686e1b65 | ||
|
|
7b3c3d891d | ||
|
|
98feca7fca | ||
|
|
e45eb3fd69 | ||
|
|
dd86b8b461 | ||
|
|
57d43cd5d3 | ||
|
|
14b5b5e3df | ||
|
|
a7e43ca6a7 | ||
|
|
8ca21c413f | ||
|
|
61fd54c496 | ||
|
|
b017127b3a | ||
|
|
52f2d15280 | ||
|
|
5c86e46c72 | ||
|
|
a5648851f2 | ||
|
|
cb41520a20 | ||
|
|
537612e5e3 | ||
|
|
4e0c671f68 | ||
|
|
507cfb9d53 | ||
|
|
2013009e8a | ||
|
|
f1a1d08458 | ||
|
|
7de6cc4468 | ||
|
|
783d83e4cc | ||
|
|
c366a1a4e7 | ||
|
|
b784cab94b | ||
|
|
78e361d486 | ||
|
|
3b1f135fdb | ||
|
|
2f93d1a83f | ||
|
|
653a26fee1 | ||
|
|
e33c9cb2f0 | ||
|
|
4be43528e3 | ||
|
|
9d343a42ce | ||
|
|
459722610a | ||
|
|
f745dce04e | ||
|
|
c1f4aaf957 | ||
|
|
f4bf8d3766 | ||
|
|
6e94d7a707 | ||
|
|
87e8e17e91 | ||
|
|
9e79178d0f | ||
|
|
9d0168a9ee | ||
|
|
9760d41561 | ||
|
|
0c039793df | ||
|
|
4cec45131e | ||
|
|
e6c08cb4e4 | ||
|
|
2d17d8cfe4 | ||
|
|
ef583fc9bb | ||
|
|
db87b1e214 | ||
|
|
5790bdfe82 | ||
|
|
731567369e | ||
|
|
317cbf904d | ||
|
|
f4561494f6 | ||
|
|
5164891928 | ||
|
|
5e79e29823 | ||
|
|
cc3649fcb8 | ||
|
|
ab11d15729 | ||
|
|
0e75d67671 | ||
|
|
ba81f923d9 | ||
|
|
a0cdb182fd | ||
|
|
ca7296ca8c | ||
|
|
fb2d0d3e71 | ||
|
|
2afd641f0f | ||
|
|
d871bbfc3b | ||
|
|
5e9ab81363 | ||
|
|
e066c6ab94 | ||
|
|
8e82772cfc | ||
|
|
6dc96b86b4 | ||
|
|
8c2874e04d | ||
|
|
c7001bf0fa | ||
|
|
f62b209ffb | ||
|
|
0fe71b2b92 | ||
|
|
e528ba5278 | ||
|
|
0c0d7f3481 | ||
|
|
9c5ea0bdd9 | ||
|
|
cd540b0146 | ||
|
|
6d9b193d25 | ||
|
|
982d346264 | ||
|
|
4c927eb7c7 | ||
|
|
fead93f0b9 | ||
|
|
ca3ba3adb6 | ||
|
|
9f87a38107 | ||
|
|
329572d191 | ||
|
|
8a0f214144 | ||
|
|
5ff3b9457a | ||
|
|
8ec25cac46 | ||
|
|
39180f8ea3 | ||
|
|
2203b2ca8b | ||
|
|
c3a5ea1b91 | ||
|
|
6349c4f752 | ||
|
|
8c5fa68f11 | ||
|
|
cd2e29a77d | ||
|
|
ce090c6437 | ||
|
|
f80be0f3da | ||
|
|
fd9a338365 | ||
|
|
776164310b | ||
|
|
52ab9f4ade | ||
|
|
88c75c0d35 | ||
|
|
cd571b6ec4 | ||
|
|
701f9ab69b | ||
|
|
6d47664767 | ||
|
|
8cb8c50998 | ||
|
|
f14a149748 | ||
|
|
beb2c2fec0 | ||
|
|
48e010f195 |
No files matched your search
+7
-2
@@ -29,7 +29,7 @@ stages:
|
||||
#############
|
||||
|
||||
variables:
|
||||
MAGE_8_VERSION: "v8.3.5"
|
||||
MAGE_8_VERSION: "v8.3.6"
|
||||
|
||||
dl-mage8-doc:
|
||||
stage: downloads
|
||||
@@ -262,6 +262,11 @@ build-linux:
|
||||
- cp -v ../mage8-linux/* pamhyr/mage8/
|
||||
# Copy Pamhyr
|
||||
- cp -r dist/pamhyr/* pamhyr/
|
||||
# Pamhyr script to force x11
|
||||
- echo '#!/usr/bin/env bash' > pamhyr/pamhyr-x11
|
||||
- echo "" >> pamhyr/pamhyr-x11
|
||||
- echo "GDK_BACKEND=x11 ./pamhyr $@" >> pamhyr/pamhyr-x11
|
||||
- chmod +x pamhyr/pamhyr-x11
|
||||
# Copy Pamhyr resources
|
||||
- mkdir -p pamhyr/_internal/View
|
||||
- mkdir -p pamhyr/_internal/View/ui
|
||||
@@ -333,7 +338,7 @@ build-windows:
|
||||
- python -m pip install -U -r ..\requirements.txt
|
||||
# Run Pyinstaller
|
||||
# - pyinstaller --noconsole -y ..\src\pamhyr.py
|
||||
- pyinstaller --hide-console hide-early -y ..\src\pamhyr.py
|
||||
- pyinstaller -i ../src/View/ui/ressources/icon.ico --hide-console hide-early -y ..\src\pamhyr.py
|
||||
# Create directory
|
||||
- mkdir pamhyr
|
||||
- dir
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -646,7 +646,7 @@ class AddNodeCommand(QUndoCommand):
|
||||
self._node = node
|
||||
|
||||
def undo(self):
|
||||
self._graph.remove_node(self._node.name)
|
||||
self._graph.remove_node(self._node)
|
||||
|
||||
def redo(self):
|
||||
self._graph.insert_node(self._node)
|
||||
|
||||
@@ -11,7 +11,8 @@
|
||||
\usepackage[absolute]{textpos}
|
||||
\usepackage{titlesec}
|
||||
\usepackage{amssymb}
|
||||
\usepackage{tcolorbox}
|
||||
\usepackage[minted,xparse,breakable]{tcolorbox}
|
||||
\usepackage{pmboxdraw}
|
||||
|
||||
\usepackage{enumitem}
|
||||
\setlist[enumerate]{itemsep=-0.5em}
|
||||
@@ -140,7 +141,7 @@
|
||||
\setminted[llvm]{breaklines,fontsize=\footnotesize,encoding=utf8}
|
||||
|
||||
%% Add box arrond of minted
|
||||
\BeforeBeginEnvironment{minted}{\begin{tcolorbox}[boxsep=0pt, left=0.1cm, right=0.1cm, arc=0pt, boxrule=0.5pt, colback=white]}%
|
||||
\BeforeBeginEnvironment{minted}{\begin{tcolorbox}[breakable, boxsep=0pt, left=0.1cm, right=0.1cm, arc=0pt, boxrule=0.5pt, colback=white]}%
|
||||
\AfterEndEnvironment{minted}{\end{tcolorbox}}%
|
||||
|
||||
\makeatletter
|
||||
|
||||
@@ -11,3 +11,4 @@ pyinstaller>=5.11.0
|
||||
shapely>=2.0.1
|
||||
lxml>=4.9.3
|
||||
platformdirs>=4.2.0
|
||||
pyshp>=2.3.1
|
||||
@@ -3,6 +3,7 @@
|
||||
!define LIC_NAME "LICENSE"
|
||||
!define APP_NAME "PAMHYR"
|
||||
|
||||
Icon "pamhyr\_internal\View\ui\ressources\icon.ico"
|
||||
Name "Pamhyr2"
|
||||
OutFile "pamhyr-windows.exe"
|
||||
LicenseData "pamhyr\_internal\LICENSE"
|
||||
|
||||
@@ -11,3 +11,4 @@ pyinstaller>=5.11.0
|
||||
shapely>=2.0.1
|
||||
lxml>=4.9.3
|
||||
platformdirs>=4.2.0
|
||||
pyshp>=2.3.1
|
||||
@@ -92,6 +92,21 @@ class AbstractModelChecker(object):
|
||||
def is_error(self):
|
||||
return self._status == STATUS.ERROR
|
||||
|
||||
# Basic check
|
||||
def basic_check(self, study):
|
||||
if study is None:
|
||||
self._status = STATUS.ERROR
|
||||
self._summary = "invalid_study"
|
||||
return False
|
||||
|
||||
river = study.river
|
||||
if river is None:
|
||||
self._status = STATUS.ERROR
|
||||
self._summary = "no_river_found"
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
# Abstract function
|
||||
|
||||
def _run(self, study):
|
||||
|
||||
+4
-4
@@ -206,11 +206,11 @@ class MageGeometryGuideLineChecker(StudyGeometryChecker):
|
||||
def __init__(self, version="mage8"):
|
||||
super(MageGeometryGuideLineChecker, self).__init__()
|
||||
|
||||
self._name = _translate("Checker", "Mage geometry guide line checker")
|
||||
self._name = _translate("Checker", "Mage geometry guideline checker")
|
||||
self._description = _translate(
|
||||
"Checker",
|
||||
"Check if exists geometry guide line is correctly defined \
|
||||
for each reaches of the study"
|
||||
"Check if exists geometry guidelines are correctly defined \
|
||||
for each reach"
|
||||
)
|
||||
self._solver = version
|
||||
self._modules = Modules.GEOMETRY
|
||||
@@ -241,7 +241,7 @@ for each reaches of the study"
|
||||
for profile in profiles:
|
||||
if not profile.has_standard_named_points():
|
||||
self._status = STATUS.WARNING
|
||||
self._summary = "no_standard_guileline_defined"
|
||||
self._summary = "no_standard_guideline_defined"
|
||||
return False
|
||||
|
||||
for gl in gls[1:]:
|
||||
|
||||
+127
-17
@@ -17,6 +17,8 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import time
|
||||
import logging
|
||||
from functools import reduce
|
||||
|
||||
from PyQt5.QtCore import QCoreApplication
|
||||
|
||||
@@ -25,28 +27,24 @@ from Checker.Checker import AbstractModelChecker, STATUS
|
||||
|
||||
_translate = QCoreApplication.translate
|
||||
|
||||
logger = logging.getLogger()
|
||||
|
||||
|
||||
class StudyNetworkReachChecker(AbstractModelChecker):
|
||||
def __init__(self):
|
||||
super(StudyNetworkReachChecker, self).__init__()
|
||||
|
||||
self._name = _translate("Checker", "Study network reach checker")
|
||||
self._name = _translate("Checker", "Study reach network checker")
|
||||
self._description = _translate(
|
||||
"Checker", "Check if exists at least one reach for study"
|
||||
"Checker", "Check if exists at least one reach exists"
|
||||
)
|
||||
self._modules = Modules.NETWORK
|
||||
|
||||
def run(self, study):
|
||||
if study is None:
|
||||
self._status = STATUS.ERROR
|
||||
self._summary = "invalid_study"
|
||||
if not self.basic_check(study):
|
||||
return False
|
||||
|
||||
river = study.river
|
||||
if river is None:
|
||||
self._status = STATUS.ERROR
|
||||
self._summary = "no_river_found"
|
||||
return False
|
||||
|
||||
edges = list(filter(lambda e: e.is_enable(), river.edges()))
|
||||
if len(edges) == 0:
|
||||
@@ -65,7 +63,7 @@ class StudyGeometryChecker(AbstractModelChecker):
|
||||
|
||||
self._name = _translate("Checker", "Study geometry checker")
|
||||
self._description = _translate(
|
||||
"Checker", "Check if exists geometry for each reach of study"
|
||||
"Checker", "Check if the geometry of each reach exists"
|
||||
)
|
||||
self._modules = Modules.GEOMETRY
|
||||
|
||||
@@ -77,16 +75,10 @@ class StudyGeometryChecker(AbstractModelChecker):
|
||||
summary = "ok"
|
||||
status = STATUS.OK
|
||||
|
||||
if study is None:
|
||||
self._status = STATUS.ERROR
|
||||
self._summary = "invalid_study"
|
||||
if not self.basic_check(study):
|
||||
return False
|
||||
|
||||
river = study.river
|
||||
if river is None:
|
||||
self._status = STATUS.ERROR
|
||||
self._summary = "no_river_found"
|
||||
return False
|
||||
|
||||
edges = river.enable_edges()
|
||||
if len(edges) == 0:
|
||||
@@ -106,6 +98,124 @@ class StudyGeometryChecker(AbstractModelChecker):
|
||||
return ok
|
||||
|
||||
|
||||
class StudyInitialConditionsChecker(AbstractModelChecker):
|
||||
def __init__(self):
|
||||
super(StudyInitialConditionsChecker, self).__init__()
|
||||
|
||||
self._name = _translate("Checker", "Study initial conditions checker")
|
||||
self._description = _translate(
|
||||
"Checker", "Check initial conditions for each node of study"
|
||||
)
|
||||
self._modules = Modules.INITIAL_CONDITION
|
||||
|
||||
def run(self, study):
|
||||
ok = True
|
||||
nerror = 0
|
||||
self._summary = "ok"
|
||||
self._status = STATUS.OK
|
||||
|
||||
if not self.basic_check(study):
|
||||
return False
|
||||
|
||||
for reach in study.river.enable_edges():
|
||||
ok &= self.check_reach(study, reach.reach)
|
||||
|
||||
return ok
|
||||
|
||||
def check_reach(self, study, reach):
|
||||
ok = True
|
||||
river = study.river
|
||||
|
||||
if reach not in river.initial_conditions:
|
||||
self._summary = "missing_initial_condition_defined"
|
||||
self._status = STATUS.WARNING
|
||||
return ok
|
||||
|
||||
ic = river.initial_conditions[reach]
|
||||
len_ic = len(ic)
|
||||
len_reach = len(reach)
|
||||
|
||||
if len_ic < len_reach:
|
||||
self._summary = "initial_condition_missing_profile"
|
||||
self._status = STATUS.WARNING
|
||||
|
||||
if len_ic > len_reach:
|
||||
self._summary = "more_initial_condition_than_profile"
|
||||
self._status = STATUS.ERROR
|
||||
ok = False
|
||||
|
||||
return ok
|
||||
|
||||
|
||||
class StudyBoundaryConditionChecker(AbstractModelChecker):
|
||||
def __init__(self):
|
||||
super(StudyBoundaryConditionChecker, self).__init__()
|
||||
|
||||
self._name = _translate("Checker", "Study boundary conditions checker")
|
||||
self._description = _translate(
|
||||
"Checker", "Check boundary conditions for each node of study"
|
||||
)
|
||||
self._modules = Modules.BOUNDARY_CONDITION
|
||||
|
||||
def run(self, study):
|
||||
ok = True
|
||||
nerror = 0
|
||||
self._summary = "ok"
|
||||
self._status = STATUS.OK
|
||||
|
||||
if not self.basic_check(study):
|
||||
return False
|
||||
|
||||
ok = self.check_liquid(study)
|
||||
|
||||
return ok
|
||||
|
||||
def check_liquid(self, study):
|
||||
river = study.river
|
||||
|
||||
bcs = river.boundary_condition.get_tab('liquid')
|
||||
if len(bcs) == 0:
|
||||
self._status = STATUS.ERROR
|
||||
self._summary = "no_boundary_condition_defined"
|
||||
return False
|
||||
|
||||
upstream, downstream = reduce(
|
||||
lambda acc, n: (
|
||||
acc[0] + [n] if river.is_upstream_node(n) else acc[0],
|
||||
acc[1] + [n] if river.is_downstream_node(n) else acc[1],
|
||||
),
|
||||
filter(
|
||||
lambda n: river.is_enable_node(n),
|
||||
river.nodes()
|
||||
),
|
||||
([], [])
|
||||
)
|
||||
|
||||
bcs_nodes = set(map(lambda bc: bc.node, bcs))
|
||||
|
||||
upstream_ok = self.check_liquid_all_node_has_bc(
|
||||
bcs_nodes, upstream
|
||||
)
|
||||
downstream_ok = self.check_liquid_all_node_has_bc(
|
||||
bcs_nodes, downstream
|
||||
)
|
||||
|
||||
ok = upstream_ok and downstream_ok
|
||||
|
||||
if not ok:
|
||||
self._status = STATUS.ERROR
|
||||
self._summary = "no_boundary_condition_at_boundary_node"
|
||||
|
||||
return ok
|
||||
|
||||
def check_liquid_all_node_has_bc(self, bcs_nodes, nodes):
|
||||
return reduce(
|
||||
lambda acc, n: (acc and (n in bcs_nodes)),
|
||||
nodes,
|
||||
True
|
||||
)
|
||||
|
||||
|
||||
class DummyOK(AbstractModelChecker):
|
||||
def __init__(self):
|
||||
super(DummyOK, self).__init__()
|
||||
|
||||
+98
-11
@@ -293,10 +293,12 @@ class MeshingWithMageMailleurTT(AMeshingTool):
|
||||
def meshing(self, reach,
|
||||
step: float = 50,
|
||||
limites=[-1, -1],
|
||||
origin=0,
|
||||
directrices=['un', 'np'],
|
||||
lplan: bool = False,
|
||||
lm: int = 3,
|
||||
linear: bool = False):
|
||||
linear: bool = False,
|
||||
origin_value=0.0):
|
||||
if reach is None or len(reach.profiles) == 0:
|
||||
return reach
|
||||
|
||||
@@ -305,32 +307,41 @@ class MeshingWithMageMailleurTT(AMeshingTool):
|
||||
st_file = self.export_reach_to_st(reach, tmp)
|
||||
m_file = st_file.rsplit(".ST", 1)[0] + ".M"
|
||||
|
||||
os.sync()
|
||||
|
||||
proc = QProcess()
|
||||
proc.setWorkingDirectory(tmp)
|
||||
|
||||
# Mage section index start at 1
|
||||
origin += 1
|
||||
limites[0] += 1
|
||||
limites[1] += 1
|
||||
|
||||
lplan = 1 if lplan else 0
|
||||
linear = 1 if linear else 0
|
||||
|
||||
logger.info(
|
||||
f"! {self._exe_path()} {st_file} {m_file} " +
|
||||
f"! {self._exe_path()} " +
|
||||
f"{st_file} {m_file} " +
|
||||
f"mesh " +
|
||||
f"{str(step)} " +
|
||||
f"{limites[0]} {limites[1]} " +
|
||||
f"{directrices[0]} {directrices[1]} " +
|
||||
f"{lplan} {lm} {linear}"
|
||||
f"{lplan} {lm} {linear} " +
|
||||
f"{origin} "
|
||||
)
|
||||
proc.start(
|
||||
self._exe_path(),
|
||||
list(map(
|
||||
list(
|
||||
map(
|
||||
str,
|
||||
[
|
||||
st_file, m_file, step,
|
||||
st_file, m_file,
|
||||
"mesh", step,
|
||||
limites[0], limites[1],
|
||||
directrices[0], directrices[1],
|
||||
lplan, lm, linear
|
||||
lplan, lm, linear, origin, origin_value
|
||||
]
|
||||
))
|
||||
)
|
||||
)
|
||||
)
|
||||
proc.waitForFinished()
|
||||
|
||||
@@ -347,9 +358,85 @@ class MeshingWithMageMailleurTT(AMeshingTool):
|
||||
logger.error(
|
||||
f"{logger_color_red()}{errors}{logger_color_reset()}"
|
||||
)
|
||||
else:
|
||||
self.import_m_file(reach, m_file)
|
||||
return reach
|
||||
|
||||
self.import_m_file(reach, m_file)
|
||||
return reach
|
||||
|
||||
def update_rk(self, reach,
|
||||
step: float = 50,
|
||||
limites=[-1, -1],
|
||||
origin=0,
|
||||
directrices=['un', 'np'],
|
||||
lplan: bool = False,
|
||||
lm: int = 3,
|
||||
linear: bool = False,
|
||||
origin_value=0.0,
|
||||
orientation=0):
|
||||
if reach is None or len(reach.profiles) == 0:
|
||||
return reach
|
||||
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
logger.debug(f"temp file: {tmp}")
|
||||
st_file = self.export_reach_to_st(reach, tmp)
|
||||
m_file = st_file.rsplit(".ST", 1)[0] + ".M"
|
||||
|
||||
os.sync()
|
||||
|
||||
proc = QProcess()
|
||||
proc.setWorkingDirectory(tmp)
|
||||
|
||||
# Mage section indices start at 1
|
||||
origin += 1
|
||||
limites[0] += 1
|
||||
limites[1] += 1
|
||||
|
||||
lplan = 1 if lplan else 0
|
||||
linear = 1 if linear else 0
|
||||
|
||||
logger.info(
|
||||
f"! {self._exe_path()} " +
|
||||
f"{st_file} {m_file} " +
|
||||
f"update_rk " +
|
||||
f"{str(step)} " +
|
||||
f"{limites[0]} {limites[1]} " +
|
||||
f"{directrices[0]} {directrices[1]} " +
|
||||
f"{orientation} {lm} {linear} " +
|
||||
f"{origin} "
|
||||
)
|
||||
proc.start(
|
||||
self._exe_path(),
|
||||
list(
|
||||
map(
|
||||
str,
|
||||
[
|
||||
st_file, m_file,
|
||||
"update_rk", step,
|
||||
limites[0], limites[1],
|
||||
directrices[0], directrices[1],
|
||||
orientation, lm, linear, origin, origin_value
|
||||
]
|
||||
)
|
||||
)
|
||||
)
|
||||
proc.waitForFinished()
|
||||
|
||||
if proc.exitCode() != 0:
|
||||
logger.error(
|
||||
f"{logger_color_red()}" +
|
||||
f"MailleurTT error: {proc.exitCode()}" +
|
||||
f"{logger_color_reset()}"
|
||||
)
|
||||
outputs = proc.readAllStandardOutput()
|
||||
logger.debug(outputs)
|
||||
|
||||
errors = proc.readAllStandardError()
|
||||
logger.error(
|
||||
f"{logger_color_red()}{errors}{logger_color_reset()}"
|
||||
)
|
||||
return reach
|
||||
|
||||
self.import_m_file(reach, m_file)
|
||||
return reach
|
||||
|
||||
def export_reach_to_st(self, reach, tmp):
|
||||
|
||||
@@ -315,6 +315,9 @@ class BoundaryCondition(SQLSubModel):
|
||||
self._data.sort(reverse=_reverse, key=key)
|
||||
self._status.modified()
|
||||
|
||||
def index(self, bc):
|
||||
self._data.index(bc)
|
||||
|
||||
def get_i(self, index):
|
||||
return self.data[index]
|
||||
|
||||
|
||||
+2
-1
@@ -21,7 +21,7 @@ from PyQt5.QtCore import (
|
||||
)
|
||||
|
||||
from PyQt5.QtWidgets import (
|
||||
QApplication, QMessageBox,
|
||||
QApplication, QMessageBox, QLabel,
|
||||
)
|
||||
|
||||
_translate = QCoreApplication.translate
|
||||
@@ -34,6 +34,7 @@ _translate = QCoreApplication.translate
|
||||
def exception_message_box(exception):
|
||||
msg = QMessageBox()
|
||||
msg.setIcon(QMessageBox.Critical)
|
||||
msg.findChild(QLabel, "qt_msgbox_label").setFixedWidth(384)
|
||||
|
||||
msg.setText("Exception :")
|
||||
msg.setInformativeText(f"{exception}")
|
||||
|
||||
@@ -43,8 +43,8 @@ class Friction(SQLSubModel):
|
||||
|
||||
self._name = name
|
||||
self._edge = None
|
||||
self._begin_kp = 0.0
|
||||
self._end_kp = 0.0
|
||||
self._begin_rk = 0.0
|
||||
self._end_rk = 0.0
|
||||
self._begin_strickler = None
|
||||
self._end_strickler = None
|
||||
|
||||
@@ -54,8 +54,8 @@ class Friction(SQLSubModel):
|
||||
CREATE TABLE friction(
|
||||
id INTEGER NOT NULL PRIMARY KEY,
|
||||
ind INTEGER NOT NULL,
|
||||
begin_kp REAL NOT NULL,
|
||||
end_kp REAL NOT NULL,
|
||||
begin_rk REAL NOT NULL,
|
||||
end_rk REAL NOT NULL,
|
||||
reach INTEGER NOT NULL,
|
||||
begin_strickler INTEGER NOT NULL,
|
||||
end_strickler INTEGER NOT NULL,
|
||||
@@ -69,6 +69,12 @@ class Friction(SQLSubModel):
|
||||
|
||||
@classmethod
|
||||
def _db_update(cls, execute, version):
|
||||
major, minor, release = version.strip().split(".")
|
||||
if major == minor == "0":
|
||||
if int(release) < 11:
|
||||
execute("ALTER TABLE friction RENAME COLUMN begin_kp TO begin_rk")
|
||||
execute("ALTER TABLE friction RENAME COLUMN end_kp TO end_rk")
|
||||
|
||||
return True
|
||||
|
||||
@classmethod
|
||||
@@ -80,7 +86,7 @@ class Friction(SQLSubModel):
|
||||
stricklers = data["stricklers"].stricklers
|
||||
|
||||
table = execute(
|
||||
"SELECT id, ind, begin_kp, end_kp, " +
|
||||
"SELECT id, ind, begin_rk, end_rk, " +
|
||||
"begin_strickler, end_strickler " +
|
||||
f"FROM friction WHERE reach = {reach.id}"
|
||||
)
|
||||
@@ -102,8 +108,8 @@ class Friction(SQLSubModel):
|
||||
# Create friction
|
||||
sec = cls(status=status)
|
||||
sec.edge = reach
|
||||
sec.begin_kp = row[2]
|
||||
sec.end_kp = row[3]
|
||||
sec.begin_rk = row[2]
|
||||
sec.end_rk = row[3]
|
||||
sec.begin_strickler = bs
|
||||
sec.end_strickler = es
|
||||
|
||||
@@ -124,11 +130,11 @@ class Friction(SQLSubModel):
|
||||
|
||||
execute(
|
||||
"INSERT INTO " +
|
||||
"friction(id, ind, begin_kp, end_kp, " +
|
||||
"friction(id, ind, begin_rk, end_rk, " +
|
||||
"reach, begin_strickler, end_strickler) " +
|
||||
"VALUES (" +
|
||||
f"{self.id}, {ind}, " +
|
||||
f"{self._begin_kp}, {self._end_kp}, " +
|
||||
f"{self._begin_rk}, {self._end_rk}, " +
|
||||
f"{self._edge.id}, " +
|
||||
f"{b_s_id}, {e_s_id}" +
|
||||
")"
|
||||
@@ -157,10 +163,10 @@ class Friction(SQLSubModel):
|
||||
def edge(self, edge):
|
||||
self._edge = edge
|
||||
if (edge is not None and
|
||||
self._begin_kp == 0.0 and
|
||||
self._end_kp == 0.0):
|
||||
self._begin_kp = self._edge.reach.get_kp_min()
|
||||
self._end_kp = self._edge.reach.get_kp_max()
|
||||
self._begin_rk == 0.0 and
|
||||
self._end_rk == 0.0):
|
||||
self._begin_rk = self._edge.reach.get_rk_min()
|
||||
self._end_rk = self._edge.reach.get_rk_max()
|
||||
self._status.modified()
|
||||
|
||||
def has_edge(self):
|
||||
@@ -176,39 +182,47 @@ class Friction(SQLSubModel):
|
||||
return self.has_edge() and self.has_coefficient()
|
||||
|
||||
@property
|
||||
def begin_kp(self):
|
||||
return self._begin_kp
|
||||
def begin_rk(self):
|
||||
return self._begin_rk
|
||||
|
||||
@begin_kp.setter
|
||||
def begin_kp(self, begin_kp):
|
||||
@begin_rk.setter
|
||||
def begin_rk(self, begin_rk):
|
||||
if self._edge is None:
|
||||
self._begin_kp = begin_kp
|
||||
self._begin_rk = begin_rk
|
||||
else:
|
||||
_min = self._edge.reach.get_kp_min()
|
||||
_max = self._edge.reach.get_kp_max()
|
||||
_min = self._edge.reach.get_rk_min()
|
||||
_max = self._edge.reach.get_rk_max()
|
||||
|
||||
if _min <= begin_kp <= _max:
|
||||
self._begin_kp = begin_kp
|
||||
if _min <= begin_rk <= _max:
|
||||
self._begin_rk = begin_rk
|
||||
|
||||
self._status.modified()
|
||||
|
||||
@property
|
||||
def end_kp(self):
|
||||
return self._end_kp
|
||||
def end_rk(self):
|
||||
return self._end_rk
|
||||
|
||||
@end_kp.setter
|
||||
def end_kp(self, end_kp):
|
||||
@end_rk.setter
|
||||
def end_rk(self, end_rk):
|
||||
if self._edge is None:
|
||||
self._end_kp = end_kp
|
||||
self._end_rk = end_rk
|
||||
else:
|
||||
_min = self._edge.reach.get_kp_min()
|
||||
_max = self._edge.reach.get_kp_max()
|
||||
_min = self._edge.reach.get_rk_min()
|
||||
_max = self._edge.reach.get_rk_max()
|
||||
|
||||
if _min <= end_kp <= _max:
|
||||
self._end_kp = end_kp
|
||||
if _min <= end_rk <= _max:
|
||||
self._end_rk = end_rk
|
||||
|
||||
self._status.modified()
|
||||
|
||||
def __contains__(self, rk):
|
||||
return self.contains_rk(rk)
|
||||
|
||||
def contains_rk(self, rk):
|
||||
return (
|
||||
self._begin_rk <= rk <= self._end_rk
|
||||
)
|
||||
|
||||
@property
|
||||
def begin_strickler(self):
|
||||
return self._begin_strickler
|
||||
|
||||
@@ -44,7 +44,7 @@ class FrictionList(PamhyrModelList):
|
||||
logger.info(f"Update friction TABLE from {version}")
|
||||
cls._db_update_0_0_1(execute, version)
|
||||
|
||||
return True
|
||||
return cls._update_submodel(execute, version)
|
||||
|
||||
@classmethod
|
||||
def _db_load(cls, execute, data=None):
|
||||
|
||||
@@ -192,6 +192,22 @@ class PointXYZ(Point, SQLSubModel):
|
||||
def dist(self, p2):
|
||||
return PointXYZ.distance(self, p2)
|
||||
|
||||
def dist_2d(self, p2):
|
||||
return PointXYZ.distance_2d(self, p2)
|
||||
|
||||
@staticmethod
|
||||
def distance_2d(p1, p2):
|
||||
"""Euclidean distance between p1 and p2.
|
||||
|
||||
Args:
|
||||
p1: A XYZ Point
|
||||
p2: A XYZ Point
|
||||
|
||||
Returns:
|
||||
Euclidean 2D distance between the two points
|
||||
"""
|
||||
return dist((p1.x, p1.y), (p2.x, p2.y))
|
||||
|
||||
@staticmethod
|
||||
def distance(p1, p2):
|
||||
"""Euclidean distance between p1 and p2.
|
||||
@@ -201,6 +217,14 @@ class PointXYZ(Point, SQLSubModel):
|
||||
p2: A XYZ Point
|
||||
|
||||
Returns:
|
||||
Euclidean distance between the two points
|
||||
Euclidean 3D distance between the two points
|
||||
"""
|
||||
return dist((p1.x, p1.y, p1.z), (p2.x, p2.y, p2.z))
|
||||
|
||||
@staticmethod
|
||||
def areatriangle3d(p1, p2, p3):
|
||||
a = PointXYZ.distance(p1, p2)
|
||||
b = PointXYZ.distance(p2, p3)
|
||||
c = PointXYZ.distance(p3, p1)
|
||||
s = (a + b + c) / 2
|
||||
return (s*(s-a) * (s-b)*(s-c)) ** 0.5
|
||||
@@ -19,6 +19,7 @@
|
||||
import logging
|
||||
|
||||
from tools import timer
|
||||
from shapely import geometry
|
||||
|
||||
from Model.Geometry.Point import Point
|
||||
from Model.Except import NotImplementedMethodeError
|
||||
@@ -30,7 +31,7 @@ class Profile(object):
|
||||
_id_cnt = 0
|
||||
|
||||
def __init__(self, id: int = -1, num: int = 0,
|
||||
kp: float = 0.0, name: str = "",
|
||||
rk: float = 0.0, name: str = "",
|
||||
code1: int = 0, code2: int = 0,
|
||||
_type: str = "", reach=None,
|
||||
status=None):
|
||||
@@ -48,7 +49,7 @@ class Profile(object):
|
||||
self._num = int(num)
|
||||
self._code1 = int(code1)
|
||||
self._code2 = int(code2)
|
||||
self._kp = float(kp)
|
||||
self._rk = float(rk)
|
||||
self._name = str(name)
|
||||
self._reach = reach
|
||||
self._sl = None
|
||||
@@ -135,16 +136,16 @@ class Profile(object):
|
||||
return len(self.points)
|
||||
|
||||
@property
|
||||
def kp(self):
|
||||
def rk(self):
|
||||
"""
|
||||
Returns:
|
||||
Kilometer point.
|
||||
"""
|
||||
return self._kp
|
||||
return self._rk
|
||||
|
||||
@kp.setter
|
||||
def kp(self, value: float):
|
||||
self._kp = float(value)
|
||||
@rk.setter
|
||||
def rk(self, value: float):
|
||||
self._rk = float(value)
|
||||
self._status.modified()
|
||||
|
||||
@property
|
||||
@@ -295,6 +296,11 @@ class Profile(object):
|
||||
)
|
||||
self._status.modified()
|
||||
|
||||
@timer
|
||||
def reverse(self):
|
||||
self._points.reverse()
|
||||
self._status.modified()
|
||||
|
||||
# Sediment Layers
|
||||
|
||||
def get_sl(self):
|
||||
@@ -341,3 +347,12 @@ class Profile(object):
|
||||
# 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_perimeter(self, z):
|
||||
raise NotImplementedMethodeError(self, self.wet_perimeter)
|
||||
|
||||
def wet_area(self, z):
|
||||
raise NotImplementedMethodeError(self, self.wet_area)
|
||||
+210
-107
@@ -22,6 +22,7 @@ from typing import List
|
||||
from functools import reduce
|
||||
|
||||
from tools import timer
|
||||
from shapely import geometry
|
||||
|
||||
from Model.Tools.PamhyrDB import SQLSubModel
|
||||
from Model.Except import ClipboardFormatError
|
||||
@@ -40,7 +41,7 @@ class ProfileXYZ(Profile, SQLSubModel):
|
||||
def __init__(self,
|
||||
id: int = -1,
|
||||
name: str = "",
|
||||
kp: float = 0.,
|
||||
rk: float = 0.,
|
||||
reach=None,
|
||||
num=0,
|
||||
nb_point: int = 0,
|
||||
@@ -52,7 +53,7 @@ class ProfileXYZ(Profile, SQLSubModel):
|
||||
num: The number of this profile
|
||||
code1: The interpolation code 1
|
||||
code2: The interpolation code 2
|
||||
kp: Kilometer point
|
||||
rk: Kilometer point
|
||||
name: The name of profile
|
||||
|
||||
Returns:
|
||||
@@ -62,7 +63,7 @@ class ProfileXYZ(Profile, SQLSubModel):
|
||||
id=id,
|
||||
num=num,
|
||||
name=name,
|
||||
kp=kp,
|
||||
rk=rk,
|
||||
code1=code1, code2=code2,
|
||||
_type="XYZ",
|
||||
reach=reach,
|
||||
@@ -77,7 +78,7 @@ class ProfileXYZ(Profile, SQLSubModel):
|
||||
ind INTEGER NOT NULL,
|
||||
name TEXT,
|
||||
reach INTEGER NOT NULL,
|
||||
kp REAL NOT NULL,
|
||||
rk REAL NOT NULL,
|
||||
num INTEGER NOT NULL,
|
||||
code1 INTEGER NOT NULL,
|
||||
code2 INTEGER NOT NULL,
|
||||
@@ -93,7 +94,8 @@ class ProfileXYZ(Profile, SQLSubModel):
|
||||
def _db_update(cls, execute, version):
|
||||
major, minor, release = version.strip().split(".")
|
||||
if major == minor == "0":
|
||||
if int(release) < 2:
|
||||
rl = int(release)
|
||||
if rl < 2:
|
||||
execute(
|
||||
"""
|
||||
ALTER TABLE geometry_profileXYZ
|
||||
@@ -102,6 +104,14 @@ class ProfileXYZ(Profile, SQLSubModel):
|
||||
"""
|
||||
)
|
||||
|
||||
if rl < 11:
|
||||
execute(
|
||||
"""
|
||||
ALTER TABLE geometry_profileXYZ
|
||||
RENAME COLUMN kp TO rk
|
||||
"""
|
||||
)
|
||||
|
||||
return cls._update_submodel(execute, version)
|
||||
|
||||
@classmethod
|
||||
@@ -111,7 +121,7 @@ class ProfileXYZ(Profile, SQLSubModel):
|
||||
reach = data["reach"]
|
||||
|
||||
table = execute(
|
||||
"SELECT id, ind, name, kp, num, code1, code2, sl " +
|
||||
"SELECT id, ind, name, rk, num, code1, code2, sl " +
|
||||
"FROM geometry_profileXYZ " +
|
||||
f"WHERE reach = {reach.id}"
|
||||
)
|
||||
@@ -120,7 +130,7 @@ class ProfileXYZ(Profile, SQLSubModel):
|
||||
id = row[0]
|
||||
ind = row[1]
|
||||
name = row[2]
|
||||
kp = row[3]
|
||||
rk = row[3]
|
||||
num = row[5]
|
||||
code1 = row[5]
|
||||
code2 = row[6]
|
||||
@@ -128,7 +138,7 @@ class ProfileXYZ(Profile, SQLSubModel):
|
||||
|
||||
new = cls(
|
||||
id=id, num=num,
|
||||
name=name, kp=kp,
|
||||
name=name, rk=rk,
|
||||
code1=code1, code2=code2,
|
||||
reach=reach,
|
||||
status=status
|
||||
@@ -158,10 +168,10 @@ class ProfileXYZ(Profile, SQLSubModel):
|
||||
sql = (
|
||||
"INSERT OR REPLACE INTO " +
|
||||
"geometry_profileXYZ(id, ind, name, reach, " +
|
||||
"kp, num, code1, code2, sl) " +
|
||||
"rk, num, code1, code2, sl) " +
|
||||
"VALUES (" +
|
||||
f"{self.id}, {ind}, '{self._db_format(self._name)}', " +
|
||||
f"{self.reach.id}, {self.kp}, {self.num}, " +
|
||||
f"{self.reach.id}, {self.rk}, {self.num}, " +
|
||||
f"{self.code1}, {self.code1}, {sl}" +
|
||||
")"
|
||||
)
|
||||
@@ -186,19 +196,19 @@ class ProfileXYZ(Profile, SQLSubModel):
|
||||
try:
|
||||
if len(header) == 0:
|
||||
name = data[0]
|
||||
kp = data[1]
|
||||
rk = data[1]
|
||||
reach = data[2]
|
||||
status = data[3]
|
||||
|
||||
profile = cls(
|
||||
id=-1,
|
||||
name=name,
|
||||
kp=kp,
|
||||
rk=rk,
|
||||
reach=reach,
|
||||
status=status
|
||||
)
|
||||
else:
|
||||
valid_header = {'name', 'reach', 'kp', 'status'}
|
||||
valid_header = {'name', 'reach', 'rk', 'status'}
|
||||
d = {}
|
||||
for i, v in enumerate(data):
|
||||
h = header[i].strip().lower().split(' ')[0]
|
||||
@@ -322,7 +332,7 @@ class ProfileXYZ(Profile, SQLSubModel):
|
||||
logger.debug(f"{e}")
|
||||
raise IndexError(
|
||||
f"Invalid point name: {name} " +
|
||||
f"for profile ({self.id}) kp = {self.kp}"
|
||||
f"for profile ({self.id}) rk = {self.rk}"
|
||||
)
|
||||
|
||||
def has_standard_named_points(self):
|
||||
@@ -373,99 +383,13 @@ class ProfileXYZ(Profile, SQLSubModel):
|
||||
"""
|
||||
return [x for x in lst if not np.isnan(x)]
|
||||
|
||||
def _first_point_not_nan(self):
|
||||
first_point = self.points[0]
|
||||
def speed(self, q, z):
|
||||
area = self.wet_area(z)
|
||||
|
||||
for point in self.points:
|
||||
if not point.is_nan():
|
||||
first_point = point
|
||||
break
|
||||
if area == 0:
|
||||
return 0
|
||||
|
||||
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)
|
||||
norm_dir_vec = vector.normalized_direction_vector()
|
||||
else:
|
||||
vector = Vector1d(first_point_not_nan, last_point_not_nan)
|
||||
norm_dir_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 = (norm_dir_vec[0] * xi +
|
||||
norm_dir_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)
|
||||
norm_dir_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 = (norm_dir_vec[0] * xi +
|
||||
norm_dir_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))
|
||||
return q / area
|
||||
|
||||
def width_approximation(self):
|
||||
if self.has_standard_named_points():
|
||||
@@ -477,6 +401,42 @@ class ProfileXYZ(Profile, SQLSubModel):
|
||||
|
||||
return abs(rg.dist(rd))
|
||||
|
||||
def wet_perimeter(self, z):
|
||||
poly = self.wet_polygon(z)
|
||||
|
||||
if poly is None:
|
||||
return 0
|
||||
|
||||
return poly.length
|
||||
|
||||
def wet_area(self, z):
|
||||
poly = self.wet_polygon(z)
|
||||
|
||||
if poly is None:
|
||||
return 0
|
||||
|
||||
return poly.area
|
||||
|
||||
def wet_polygon(self, z):
|
||||
points = self.wet_points(z)
|
||||
if len(points) < 3:
|
||||
return None
|
||||
|
||||
z = map(lambda p: p.z, points)
|
||||
station = self._get_station(points)
|
||||
|
||||
poly = geometry.Polygon(list(zip(station, z)))
|
||||
return poly
|
||||
|
||||
def wet_points(self, z):
|
||||
left, right = self.get_water_limits(z)
|
||||
|
||||
points = list(filter(lambda p: p.z < z, self._points))
|
||||
points = [left] + points + [right]
|
||||
points = sorted(points, key=lambda p: p.x)
|
||||
|
||||
return points
|
||||
|
||||
def get_water_limits(self, z):
|
||||
"""
|
||||
Determine left and right limits of water elevation.
|
||||
@@ -509,7 +469,7 @@ class ProfileXYZ(Profile, SQLSubModel):
|
||||
[self.point(i_left).z, self.point(i_left - 1).z],
|
||||
[self.point(i_left).y, self.point(i_left - 1).y]
|
||||
)
|
||||
pt_left = PointXYZ(x, y, z)
|
||||
pt_left = PointXYZ(x, y, z, name="wl_left")
|
||||
else:
|
||||
pt_left = self.point(0)
|
||||
|
||||
@@ -525,8 +485,151 @@ class ProfileXYZ(Profile, SQLSubModel):
|
||||
[self.point(i_right).z, self.point(i_right + 1).z],
|
||||
[self.point(i_right).y, self.point(i_right + 1).y]
|
||||
)
|
||||
pt_right = PointXYZ(x, y, z)
|
||||
pt_right = PointXYZ(x, y, z, name="wl_right")
|
||||
else:
|
||||
pt_right = self.point(self.number_points - 1)
|
||||
|
||||
return pt_left, pt_right
|
||||
|
||||
def get_station(self):
|
||||
"""Projection of the points of the profile on a plane.
|
||||
|
||||
Args:
|
||||
self: The profile
|
||||
|
||||
Returns:
|
||||
Projection of the points of the profile on a plane.
|
||||
"""
|
||||
if self.nb_points < 3:
|
||||
return None
|
||||
else:
|
||||
return self._get_station(self.points)
|
||||
|
||||
@timer
|
||||
def _get_station(self, points):
|
||||
first_named_point = None
|
||||
index_first_named_point = None
|
||||
last_named_point = None
|
||||
|
||||
first_point_not_nan = self._first_point_not_nan(points)
|
||||
last_point_not_nan = self._last_point_not_nan(points)
|
||||
|
||||
for index, point in enumerate(points):
|
||||
if point.point_is_named():
|
||||
index_first_named_point = index
|
||||
first_named_point = point
|
||||
break
|
||||
|
||||
for point in reversed(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)
|
||||
norm_dir_vec = vector.normalized_direction_vector()
|
||||
else:
|
||||
vector = Vector1d(first_point_not_nan, last_point_not_nan)
|
||||
norm_dir_vec = vector.normalized_direction_vector()
|
||||
|
||||
for point in points:
|
||||
xi = point.x - first_named_point.x
|
||||
yi = point.y - first_named_point.y
|
||||
station_i = (norm_dir_vec[0] * xi +
|
||||
norm_dir_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)
|
||||
norm_dir_vec = vector.normalized_direction_vector()
|
||||
|
||||
for point in points:
|
||||
xi = point.x - first_point_not_nan.x
|
||||
yi = point.y - first_point_not_nan.y
|
||||
station_i = (norm_dir_vec[0] * xi +
|
||||
norm_dir_vec[1] * yi)
|
||||
station.append(station_i)
|
||||
|
||||
z_min = self.z_min()
|
||||
index_profile_z_min = next(
|
||||
filter(
|
||||
lambda z: z[1] == z_min,
|
||||
enumerate(self.z())
|
||||
)
|
||||
)
|
||||
constant = station[index_profile_z_min[0]]
|
||||
|
||||
return list(map(lambda s: s - constant, station))
|
||||
|
||||
def _first_point_not_nan(self, points):
|
||||
first_point = None
|
||||
|
||||
for point in points:
|
||||
if not point.is_nan():
|
||||
first_point = point
|
||||
break
|
||||
|
||||
return first_point
|
||||
|
||||
def _last_point_not_nan(self, points):
|
||||
last_point = None
|
||||
|
||||
for point in reversed(points):
|
||||
if not point.is_nan():
|
||||
last_point = point
|
||||
break
|
||||
|
||||
return last_point
|
||||
|
||||
def purge(self, np_purge):
|
||||
"""
|
||||
Remove points to keep at most np_purge points.
|
||||
"""
|
||||
if (self.nb_points <= np_purge):
|
||||
return
|
||||
|
||||
nb_named = 2 # we consider the first and last point as named
|
||||
area = [0.0]
|
||||
|
||||
for i in range(1, self.nb_points-1):
|
||||
if self.point(i).point_is_named():
|
||||
area.append(9999999.999)
|
||||
nb_named += 1
|
||||
else:
|
||||
area.append(
|
||||
PointXYZ.areatriangle3d(
|
||||
self.point(i-1),
|
||||
self.point(i),
|
||||
self.point(i+1))
|
||||
)
|
||||
|
||||
area.append(0.0)
|
||||
|
||||
while self.nb_points > max(np_purge, nb_named):
|
||||
to_rm = np.argmin(area[1:self.nb_points - 1]) + 1
|
||||
|
||||
self.delete_i([to_rm])
|
||||
area.pop(to_rm)
|
||||
|
||||
for i in [to_rm-1, to_rm]:
|
||||
if (i == 0):
|
||||
continue
|
||||
|
||||
if (i == self.nb_points - 1):
|
||||
continue
|
||||
|
||||
if self.point(i).point_is_named():
|
||||
area[i] = 9999999.999
|
||||
else:
|
||||
area[i] = PointXYZ.areatriangle3d(
|
||||
self.point(i-1),
|
||||
self.point(i),
|
||||
self.point(i+1)
|
||||
)
|
||||
+136
-31
@@ -16,7 +16,9 @@
|
||||
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import os
|
||||
import logging
|
||||
import shapefile
|
||||
import numpy as np
|
||||
|
||||
from time import time
|
||||
@@ -139,10 +141,10 @@ class Reach(SQLSubModel):
|
||||
def profiles(self, profiles):
|
||||
self._profiles = profiles
|
||||
|
||||
def get_profiles_from_kp(self, kp):
|
||||
def get_profiles_from_rk(self, rk):
|
||||
return list(
|
||||
filter(
|
||||
lambda p: p.kp == kp, self.profiles
|
||||
lambda p: p.rk == rk, self.profiles
|
||||
)
|
||||
)
|
||||
|
||||
@@ -333,18 +335,18 @@ class Reach(SQLSubModel):
|
||||
)
|
||||
)
|
||||
|
||||
def get_kp(self):
|
||||
"""List of profiles kp
|
||||
def get_rk(self):
|
||||
"""List of profiles rk
|
||||
|
||||
Returns:
|
||||
List of profiles kp
|
||||
List of profiles rk
|
||||
"""
|
||||
return [profile.kp for profile in self.profiles]
|
||||
return [profile.rk for profile in self.profiles]
|
||||
|
||||
def get_kp_complete_profiles(self):
|
||||
def get_rk_complete_profiles(self):
|
||||
return list(
|
||||
map(
|
||||
lambda profile: profile.kp,
|
||||
lambda profile: profile.rk,
|
||||
filter(
|
||||
lambda profile: len(profile) > 0,
|
||||
self.profiles
|
||||
@@ -352,18 +354,42 @@ class Reach(SQLSubModel):
|
||||
)
|
||||
)
|
||||
|
||||
def get_kp_min(self):
|
||||
if len(self.get_kp()) > 0:
|
||||
return min(self.get_kp())
|
||||
def get_rk_min(self):
|
||||
if len(self.get_rk()) > 0:
|
||||
return min(self.get_rk())
|
||||
else:
|
||||
return 0.0
|
||||
|
||||
def get_kp_max(self):
|
||||
if len(self.get_kp()) > 0:
|
||||
return max(self.get_kp())
|
||||
def get_rk_max(self):
|
||||
if len(self.get_rk()) > 0:
|
||||
return max(self.get_rk())
|
||||
else:
|
||||
return 0.0
|
||||
|
||||
def inter_profiles_rk(self):
|
||||
profiles = sorted(self.profiles, key=lambda p: p.rk)
|
||||
first = profiles[0]
|
||||
last = profiles[-1]
|
||||
|
||||
res_rk = [first.rk]
|
||||
|
||||
profiles = iter(profiles)
|
||||
previous = next(profiles)
|
||||
|
||||
for profile in profiles:
|
||||
prev = previous.rk
|
||||
curr = profile.rk
|
||||
|
||||
diff = abs(previous.rk - profile.rk)
|
||||
|
||||
new = previous.rk + (diff / 2.0)
|
||||
res_rk.append(new)
|
||||
|
||||
previous = profile
|
||||
|
||||
res_rk.append(last.rk)
|
||||
return res_rk
|
||||
|
||||
# Sediment Layers
|
||||
|
||||
def get_sl(self):
|
||||
@@ -510,7 +536,7 @@ class Reach(SQLSubModel):
|
||||
def sort(self, is_reversed: bool = False):
|
||||
self.profiles = sorted(
|
||||
self.profiles,
|
||||
key=lambda profile: profile.kp,
|
||||
key=lambda profile: profile.rk,
|
||||
reverse=is_reversed
|
||||
)
|
||||
self._status.modified()
|
||||
@@ -533,8 +559,66 @@ class Reach(SQLSubModel):
|
||||
|
||||
# Import/Export
|
||||
|
||||
@timer
|
||||
def import_geometry(self, file_path_name: str):
|
||||
ext = file_path_name.split(".")
|
||||
if len(ext) > 0:
|
||||
ext = ext[-1].lower()
|
||||
|
||||
if ext in ['st', 'm']:
|
||||
return self.import_geometry_st(file_path_name)
|
||||
|
||||
if ext in ['shp']:
|
||||
return self.import_geometry_shp(file_path_name)
|
||||
|
||||
raise FileFormatError(
|
||||
file_path_name,
|
||||
f"Geometry file extention ('.{ext}') unkown"
|
||||
)
|
||||
|
||||
@timer
|
||||
def import_geometry_shp(self, file_path_name: str):
|
||||
sf = shapefile.Reader(
|
||||
os.path.abspath(file_path_name)
|
||||
)
|
||||
|
||||
if sf.shapeType != shapefile.POLYLINEZ:
|
||||
raise FileFormatError(
|
||||
file_path_name,
|
||||
"Shapefile is not a POLYLINEZ"
|
||||
)
|
||||
|
||||
profiles = []
|
||||
|
||||
for i in range(len(sf)):
|
||||
s_profile = sf.shape(i)
|
||||
z = s_profile.z
|
||||
|
||||
ind = 0
|
||||
points = []
|
||||
for point in s_profile.points:
|
||||
points.append([
|
||||
point[0],
|
||||
point[1],
|
||||
z[ind]
|
||||
])
|
||||
|
||||
ind += 1
|
||||
|
||||
prof = ProfileXYZ(
|
||||
reach=self, status=self._status
|
||||
)
|
||||
prof.import_points(points)
|
||||
|
||||
profiles.append(prof)
|
||||
|
||||
self.profiles = profiles + self.profiles
|
||||
self._update_profile_numbers()
|
||||
self._recompute_rk()
|
||||
|
||||
return profiles
|
||||
|
||||
@timer
|
||||
def import_geometry_st(self, file_path_name: str):
|
||||
"""Import a geometry from file (.ST or .st)
|
||||
|
||||
Args:
|
||||
@@ -549,7 +633,7 @@ class Reach(SQLSubModel):
|
||||
try:
|
||||
list_profile, list_header = self.read_file_st(str(file_path_name))
|
||||
profile_header = ["num", "code1",
|
||||
"code2", "nb_point", "kp", "name"]
|
||||
"code2", "nb_point", "rk", "name"]
|
||||
|
||||
if list_profile and list_header:
|
||||
for ind, profile in enumerate(list_profile):
|
||||
@@ -637,12 +721,12 @@ class Reach(SQLSubModel):
|
||||
c1 = f"{profile.code1:>6}"
|
||||
c2 = f"{profile.code2:>6}"
|
||||
t = f"{len(profile.points):>6}"
|
||||
kp = f"{profile.kp:>12f}"[0:12]
|
||||
rk = f"{profile.rk:>12f}"[0:12]
|
||||
pname = profile.name
|
||||
if profile.name == "":
|
||||
pname = f"p{profile.id:>3}".replace(" ", "0")
|
||||
|
||||
st.write(f"{num}{c1}{c2}{t} {kp} {pname}\n")
|
||||
st.write(f"{num}{c1}{c2}{t} {rk} {pname}\n")
|
||||
|
||||
for point in profile.points:
|
||||
x = f"{point.x:<12.4f}"[0:12]
|
||||
@@ -661,15 +745,15 @@ class Reach(SQLSubModel):
|
||||
last = self.profile(len(self) - 1)
|
||||
|
||||
z_first = first.z_min()
|
||||
kp_first = first.kp
|
||||
rk_first = first.rk
|
||||
|
||||
z_last = last.z_min()
|
||||
kp_last = last.kp
|
||||
rk_last = last.rk
|
||||
|
||||
return (
|
||||
(z_last - z_first)
|
||||
/
|
||||
(kp_last - kp_first)
|
||||
(rk_last - rk_first)
|
||||
)
|
||||
|
||||
def get_incline_mean(self):
|
||||
@@ -680,15 +764,15 @@ class Reach(SQLSubModel):
|
||||
|
||||
for profile in profiles[1:]:
|
||||
z_first = previous.z_min()
|
||||
kp_first = previous.kp
|
||||
rk_first = previous.rk
|
||||
|
||||
z_last = profile.z_min()
|
||||
kp_last = profile.kp
|
||||
rk_last = profile.rk
|
||||
|
||||
incline_acc += (
|
||||
(z_last - z_first)
|
||||
/
|
||||
(kp_last - kp_first)
|
||||
(rk_last - rk_first)
|
||||
)
|
||||
|
||||
previous = profile
|
||||
@@ -703,15 +787,15 @@ class Reach(SQLSubModel):
|
||||
|
||||
for profile in profiles[1:]:
|
||||
z_first = previous.z_min()
|
||||
kp_first = previous.kp
|
||||
rk_first = previous.rk
|
||||
|
||||
z_last = profile.z_min()
|
||||
kp_last = profile.kp
|
||||
rk_last = profile.rk
|
||||
|
||||
incline_acc += [
|
||||
(z_last - z_first)
|
||||
/
|
||||
(kp_last - kp_first)
|
||||
(rk_last - rk_first)
|
||||
]
|
||||
|
||||
previous = profile
|
||||
@@ -727,15 +811,15 @@ class Reach(SQLSubModel):
|
||||
|
||||
for profile in profiles[1:]:
|
||||
z_first = previous.z_min()
|
||||
kp_first = previous.kp
|
||||
rk_first = previous.rk
|
||||
|
||||
z_last = profile.z_min()
|
||||
kp_last = profile.kp
|
||||
rk_last = profile.rk
|
||||
|
||||
incline_acc += [
|
||||
(z_last - z_first)
|
||||
/
|
||||
(kp_last - kp_first)
|
||||
(rk_last - rk_first)
|
||||
]
|
||||
|
||||
previous = profile
|
||||
@@ -762,3 +846,24 @@ class Reach(SQLSubModel):
|
||||
except Exception as e:
|
||||
logger_exception(e)
|
||||
return 0
|
||||
|
||||
def _recompute_rk(self, offset=0.0):
|
||||
self._recompute_rk_no_gl(offset=offset)
|
||||
|
||||
def _recompute_rk_no_gl(self, offset=0.0):
|
||||
profiles = iter(self.profiles)
|
||||
|
||||
previous = next(profiles)
|
||||
previous.rk = offset
|
||||
|
||||
for profile in profiles:
|
||||
prev_points = previous.points
|
||||
curr_points = profile.points
|
||||
|
||||
dist = (
|
||||
prev_points[0].dist_2d(curr_points[0]) +
|
||||
prev_points[-1].dist_2d(curr_points[-1])
|
||||
) / 2.0
|
||||
|
||||
profile.rk = previous.rk + dist
|
||||
previous = profile
|
||||
@@ -50,8 +50,8 @@ class HydraulicStructure(SQLSubModel):
|
||||
self.id = id
|
||||
|
||||
self._name = name
|
||||
self._input_kp = None
|
||||
self._output_kp = None
|
||||
self._input_rk = None
|
||||
self._output_rk = None
|
||||
self._input_reach = None
|
||||
self._output_reach = None
|
||||
self._enabled = True
|
||||
@@ -69,8 +69,8 @@ class HydraulicStructure(SQLSubModel):
|
||||
id INTEGER NOT NULL PRIMARY KEY,
|
||||
name TEXT NOT NULL,
|
||||
enabled BOOLEAN NOT NULL,
|
||||
input_kp REAL NOT NULL,
|
||||
output_kp REAL NOT NULL,
|
||||
input_rk REAL NOT NULL,
|
||||
output_rk REAL NOT NULL,
|
||||
input_reach INTEGER,
|
||||
output_reach INTEGER,
|
||||
FOREIGN KEY(input_reach) REFERENCES river_reach(id),
|
||||
@@ -84,9 +84,25 @@ class HydraulicStructure(SQLSubModel):
|
||||
def _db_update(cls, execute, version):
|
||||
major, minor, release = version.strip().split(".")
|
||||
if major == minor == "0":
|
||||
if int(release) < 6:
|
||||
rl = int(release)
|
||||
|
||||
if rl < 6:
|
||||
cls._db_create(execute)
|
||||
|
||||
if rl < 11:
|
||||
execute(
|
||||
"""
|
||||
ALTER TABLE hydraulic_structures
|
||||
RENAME COLUMN input_kp TO input_rk
|
||||
"""
|
||||
)
|
||||
execute(
|
||||
"""
|
||||
ALTER TABLE hydraulic_structures
|
||||
RENAME COLUMN output_kp TO output_rk
|
||||
"""
|
||||
)
|
||||
|
||||
return True
|
||||
|
||||
@classmethod
|
||||
@@ -95,7 +111,7 @@ class HydraulicStructure(SQLSubModel):
|
||||
|
||||
table = execute(
|
||||
"SELECT id, name, enabled, " +
|
||||
"input_kp, output_kp, " +
|
||||
"input_rk, output_rk, " +
|
||||
"input_reach, output_reach " +
|
||||
"FROM hydraulic_structures "
|
||||
)
|
||||
@@ -106,8 +122,8 @@ class HydraulicStructure(SQLSubModel):
|
||||
hs_id = next(it)
|
||||
name = next(it)
|
||||
enabled = (next(it) == 1)
|
||||
input_kp = next(it)
|
||||
output_kp = next(it)
|
||||
input_rk = next(it)
|
||||
output_rk = next(it)
|
||||
input_reach_id = next(it)
|
||||
output_reach_id = next(it)
|
||||
|
||||
@@ -118,8 +134,8 @@ class HydraulicStructure(SQLSubModel):
|
||||
)
|
||||
|
||||
hs.enabled = enabled
|
||||
hs.input_kp = input_kp if input_kp != -1 else None
|
||||
hs.output_kp = output_kp if output_kp != -1 else None
|
||||
hs.input_rk = input_rk if input_rk != -1 else None
|
||||
hs.output_rk = output_rk if output_rk != -1 else None
|
||||
|
||||
hs.input_reach, hs.output_reach = reduce(
|
||||
lambda acc, n: (
|
||||
@@ -148,24 +164,24 @@ class HydraulicStructure(SQLSubModel):
|
||||
if self._output_reach is not None:
|
||||
output_reach_id = self._output_reach.id
|
||||
|
||||
input_kp = -1
|
||||
if self.input_kp is not None:
|
||||
input_kp = self.input_kp
|
||||
input_rk = -1
|
||||
if self.input_rk is not None:
|
||||
input_rk = self.input_rk
|
||||
|
||||
output_kp = -1
|
||||
if self.output_kp is not None:
|
||||
output_kp = self.output_kp
|
||||
output_rk = -1
|
||||
if self.output_rk is not None:
|
||||
output_rk = self.output_rk
|
||||
|
||||
sql = (
|
||||
"INSERT INTO " +
|
||||
"hydraulic_structures(" +
|
||||
" id, name, enabled, input_kp, output_kp, " +
|
||||
" id, name, enabled, input_rk, output_rk, " +
|
||||
" input_reach, output_reach" +
|
||||
") " +
|
||||
"VALUES (" +
|
||||
f"{self.id}, '{self._db_format(self._name)}', " +
|
||||
f"{self._db_format(self.enabled)}, " +
|
||||
f"{input_kp}, {output_kp}, " +
|
||||
f"{input_rk}, {output_rk}, " +
|
||||
f"{input_reach_id}, {output_reach_id}" +
|
||||
")"
|
||||
)
|
||||
@@ -198,24 +214,24 @@ class HydraulicStructure(SQLSubModel):
|
||||
self._status.modified()
|
||||
|
||||
@property
|
||||
def input_kp(self):
|
||||
return self._input_kp
|
||||
def input_rk(self):
|
||||
return self._input_rk
|
||||
|
||||
@input_kp.setter
|
||||
def input_kp(self, input_kp):
|
||||
if input_kp is not None:
|
||||
input_kp = float(input_kp)
|
||||
@input_rk.setter
|
||||
def input_rk(self, input_rk):
|
||||
if input_rk is not None:
|
||||
input_rk = float(input_rk)
|
||||
|
||||
self._input_kp = input_kp
|
||||
self._input_rk = input_rk
|
||||
self._status.modified()
|
||||
|
||||
@property
|
||||
def output_kp(self):
|
||||
return self._output_kp
|
||||
def output_rk(self):
|
||||
return self._output_rk
|
||||
|
||||
@output_kp.setter
|
||||
def output_kp(self, output_kp):
|
||||
self._output_kp = output_kp
|
||||
@output_rk.setter
|
||||
def output_rk(self, output_rk):
|
||||
self._output_rk = output_rk
|
||||
self._status.modified()
|
||||
|
||||
@property
|
||||
|
||||
@@ -30,7 +30,7 @@ logger = logging.getLogger()
|
||||
class Data(SQLSubModel):
|
||||
def __init__(self, name: str = "",
|
||||
comment: str = "", reach=None,
|
||||
kp: float = 0.0, discharge: float = 0.0,
|
||||
rk: float = 0.0, discharge: float = 0.0,
|
||||
height: float = 0.0,
|
||||
status=None):
|
||||
super(Data, self).__init__()
|
||||
@@ -42,14 +42,14 @@ class Data(SQLSubModel):
|
||||
self._name = name
|
||||
self._comment = comment
|
||||
|
||||
self._kp = kp
|
||||
self._rk = rk
|
||||
self._discharge = discharge
|
||||
self._speed = 0.0
|
||||
self._elevation = 0.0
|
||||
self._height = height
|
||||
|
||||
if self._kp != 0.0:
|
||||
self._update_from_kp()
|
||||
if self._rk != 0.0:
|
||||
self._update_from_rk()
|
||||
if self._height != 0.0:
|
||||
self._update_from_height()
|
||||
if self._discharge != 0.0:
|
||||
@@ -64,7 +64,7 @@ class Data(SQLSubModel):
|
||||
name TEXT NOT NULL,
|
||||
comment TEXT NOT NULL,
|
||||
reach INTEGER,
|
||||
kp REAL NOT NULL,
|
||||
rk REAL NOT NULL,
|
||||
discharge REAL NOT NULL,
|
||||
height REAL NOT NULL,
|
||||
FOREIGN KEY(reach) REFERENCES river_reach(id)
|
||||
@@ -75,13 +75,20 @@ class Data(SQLSubModel):
|
||||
|
||||
@classmethod
|
||||
def _db_update(cls, execute, version):
|
||||
major, minor, release = version.strip().split(".")
|
||||
if major == minor == "0":
|
||||
if int(release) < 11:
|
||||
execute(
|
||||
"ALTER TABLE initial_conditions RENAME COLUMN kp TO rk"
|
||||
)
|
||||
|
||||
return cls._update_submodel(execute, version)
|
||||
|
||||
@classmethod
|
||||
def _db_load(cls, execute, data=None):
|
||||
id = data["reach"].id
|
||||
table = execute(
|
||||
"SELECT ind, name, comment, kp, discharge, height " +
|
||||
"SELECT ind, name, comment, rk, discharge, height " +
|
||||
"FROM initial_conditions " +
|
||||
f"WHERE reach = {id}"
|
||||
)
|
||||
@@ -95,7 +102,7 @@ class Data(SQLSubModel):
|
||||
ind = row[0]
|
||||
name = row[1]
|
||||
comment = row[2]
|
||||
kp = row[3]
|
||||
rk = row[3]
|
||||
discharge = row[4]
|
||||
height = row[5]
|
||||
|
||||
@@ -104,7 +111,7 @@ class Data(SQLSubModel):
|
||||
status=data["status"],
|
||||
name=name,
|
||||
comment=comment,
|
||||
kp=kp,
|
||||
rk=rk,
|
||||
discharge=discharge,
|
||||
height=height,
|
||||
)
|
||||
@@ -118,12 +125,12 @@ class Data(SQLSubModel):
|
||||
|
||||
execute(
|
||||
"INSERT INTO " +
|
||||
"initial_conditions(ind, name, comment, kp, " +
|
||||
"initial_conditions(ind, name, comment, rk, " +
|
||||
"discharge, height, reach) " +
|
||||
"VALUES (" +
|
||||
f"{ind}, '{self._db_format(self.name)}', " +
|
||||
f"'{self._db_format(self._comment)}', " +
|
||||
f"{self._kp}, {self._discharge}, {self._height}, " +
|
||||
f"{self._rk}, {self._discharge}, {self._height}, " +
|
||||
f"{self._reach.id}" +
|
||||
")"
|
||||
)
|
||||
@@ -134,7 +141,7 @@ class Data(SQLSubModel):
|
||||
new = Data(
|
||||
name=self.name,
|
||||
comment=self._comment,
|
||||
kp=self._kp,
|
||||
rk=self._rk,
|
||||
discharge=self._discharge,
|
||||
height=self._height,
|
||||
reach=self._reach,
|
||||
@@ -154,8 +161,8 @@ class Data(SQLSubModel):
|
||||
val = self._name
|
||||
elif key == "comment":
|
||||
val = self._comment
|
||||
elif key == "kp":
|
||||
val = self._kp
|
||||
elif key == "rk":
|
||||
val = self._rk
|
||||
elif key == "speed":
|
||||
val = self._speed
|
||||
elif key == "discharge":
|
||||
@@ -168,7 +175,7 @@ class Data(SQLSubModel):
|
||||
return val
|
||||
|
||||
def _update_get_min(self):
|
||||
profile = self._reach.reach.get_profiles_from_kp(self._kp)
|
||||
profile = self._reach.reach.get_profiles_from_rk(self._rk)
|
||||
if len(profile) > 0:
|
||||
min = profile[0].z_min()
|
||||
else:
|
||||
@@ -176,7 +183,7 @@ class Data(SQLSubModel):
|
||||
|
||||
return min
|
||||
|
||||
def _update_from_kp(self):
|
||||
def _update_from_rk(self):
|
||||
min = self._update_get_min()
|
||||
self._elevation = min + self._height
|
||||
|
||||
@@ -197,9 +204,9 @@ class Data(SQLSubModel):
|
||||
self._name = str(value)
|
||||
elif key == "comment":
|
||||
self._comment = str(value)
|
||||
elif key == "kp":
|
||||
self._kp = float(value)
|
||||
self._update_from_kp()
|
||||
elif key == "rk":
|
||||
self._rk = float(value)
|
||||
self._update_from_rk()
|
||||
elif key == "speed":
|
||||
# Not supposed to be modified
|
||||
self._speed = float(value)
|
||||
@@ -266,6 +273,9 @@ class InitialConditions(SQLSubModel):
|
||||
def __len__(self):
|
||||
return len(self._data)
|
||||
|
||||
def lst(self):
|
||||
return self._data
|
||||
|
||||
@property
|
||||
def reach(self):
|
||||
return self._reach
|
||||
@@ -295,6 +305,15 @@ class InitialConditions(SQLSubModel):
|
||||
self._data.insert(index, n)
|
||||
self._status.modified()
|
||||
|
||||
def new_from_data(self, rk, discharge, elevation):
|
||||
n = Data(reach=self._reach, status=self._status)
|
||||
|
||||
n['rk'] = rk
|
||||
n['discharge'] = discharge
|
||||
n['elevation'] = elevation
|
||||
|
||||
return n
|
||||
|
||||
def insert(self, index, data):
|
||||
self._data.insert(index, data)
|
||||
self._status.modified()
|
||||
@@ -332,8 +351,8 @@ class InitialConditions(SQLSubModel):
|
||||
)
|
||||
)
|
||||
|
||||
def get_kp(self):
|
||||
return self._data_get("kp")
|
||||
def get_rk(self):
|
||||
return self._data_get("rk")
|
||||
|
||||
def get_elevation(self):
|
||||
return self._data_get("elevation")
|
||||
@@ -341,10 +360,10 @@ class InitialConditions(SQLSubModel):
|
||||
def get_discharge(self):
|
||||
return self._data_get("discharge")
|
||||
|
||||
def _sort_by_z_and_kp(self, profiles):
|
||||
def _sort_by_z_and_rk(self, profiles):
|
||||
profiles.sort(
|
||||
reverse=False,
|
||||
key=lambda p: p.kp
|
||||
key=lambda p: p.rk
|
||||
)
|
||||
|
||||
first_z = profiles[0].z()
|
||||
@@ -353,14 +372,14 @@ class InitialConditions(SQLSubModel):
|
||||
if first_z > last_z:
|
||||
profiles.sort(
|
||||
reverse=True,
|
||||
key=lambda p: p.kp
|
||||
key=lambda p: p.rk
|
||||
)
|
||||
|
||||
def generate_growing_constante_height(self, height: float):
|
||||
self._data = []
|
||||
|
||||
profiles = self._reach.reach.profiles.copy()
|
||||
self._sort_by_z_and_kp(profiles)
|
||||
self._sort_by_z_and_rk(profiles)
|
||||
|
||||
incline = self._reach.reach.get_incline_median_mean()
|
||||
logger.debug(f"incline = {incline}")
|
||||
@@ -381,13 +400,13 @@ class InitialConditions(SQLSubModel):
|
||||
previous_elevation
|
||||
)
|
||||
|
||||
logger.debug(f"({profile.kp}):")
|
||||
logger.debug(f"({profile.rk}):")
|
||||
logger.debug(f" width = {width}")
|
||||
logger.debug(f" strickler = {strickler}")
|
||||
logger.debug(f" discharge = {discharge}")
|
||||
|
||||
new = Data(reach=self._reach, status=self._status)
|
||||
new["kp"] = profile.kp
|
||||
new["rk"] = profile.rk
|
||||
new["discharge"] = discharge
|
||||
|
||||
new["elevation"] = elevation
|
||||
@@ -406,7 +425,7 @@ class InitialConditions(SQLSubModel):
|
||||
|
||||
def _generate_height_estimation_from_discharge(self, discharge: float):
|
||||
profiles = self._reach.reach.profiles.copy()
|
||||
self._sort_by_z_and_kp(profiles)
|
||||
self._sort_by_z_and_rk(profiles)
|
||||
|
||||
previous_elevation = -99999.99
|
||||
|
||||
@@ -427,13 +446,13 @@ class InitialConditions(SQLSubModel):
|
||||
previous_elevation
|
||||
)
|
||||
|
||||
logger.debug(f"({profile.kp}):")
|
||||
logger.debug(f"({profile.rk}):")
|
||||
logger.debug(f" width = {width}")
|
||||
logger.debug(f" strickler = {strickler}")
|
||||
logger.debug(f" height = {height}")
|
||||
|
||||
new = Data(reach=self._reach, status=self._status)
|
||||
new["kp"] = profile.kp
|
||||
new["rk"] = profile.rk
|
||||
new["discharge"] = discharge
|
||||
new["elevation"] = elevation
|
||||
|
||||
@@ -444,10 +463,10 @@ class InitialConditions(SQLSubModel):
|
||||
|
||||
def _generate_resort_data(self, profiles):
|
||||
is_reverse = False
|
||||
if profiles[0].kp > profiles[-1].kp:
|
||||
if profiles[0].rk > profiles[-1].rk:
|
||||
is_reverse = True
|
||||
|
||||
self._data.sort(
|
||||
reverse=not is_reverse,
|
||||
key=lambda d: d['kp']
|
||||
key=lambda d: d['rk']
|
||||
)
|
||||
@@ -48,8 +48,8 @@ class LateralContribution(SQLSubModel):
|
||||
self._name = name
|
||||
self._type = ""
|
||||
self._edge = None
|
||||
self._begin_kp = 0.0
|
||||
self._end_kp = 0.0
|
||||
self._begin_rk = 0.0
|
||||
self._end_rk = 0.0
|
||||
self._data = []
|
||||
self._header = []
|
||||
self._types = [float, float]
|
||||
@@ -66,8 +66,8 @@ class LateralContribution(SQLSubModel):
|
||||
type TEXT NOT NULL,
|
||||
tab TEXT NOT NULL,
|
||||
edge INTEGER,
|
||||
begin_kp REAL NOT NULL,
|
||||
end_kp REAL NOT NULL,
|
||||
begin_rk REAL NOT NULL,
|
||||
end_rk REAL NOT NULL,
|
||||
FOREIGN KEY(edge) REFERENCES river_reach(id)
|
||||
)
|
||||
""")
|
||||
@@ -87,6 +87,22 @@ class LateralContribution(SQLSubModel):
|
||||
|
||||
@classmethod
|
||||
def _db_update(cls, execute, version):
|
||||
major, minor, release = version.strip().split(".")
|
||||
if major == minor == "0":
|
||||
if int(release) < 11:
|
||||
execute(
|
||||
"""
|
||||
ALTER TABLE lateral_contribution
|
||||
RENAME COLUMN begin_kp TO begin_rk
|
||||
"""
|
||||
)
|
||||
execute(
|
||||
"""
|
||||
ALTER TABLE lateral_contribution
|
||||
RENAME COLUMN end_kp TO end_rk
|
||||
"""
|
||||
)
|
||||
|
||||
return True
|
||||
|
||||
@classmethod
|
||||
@@ -110,7 +126,7 @@ class LateralContribution(SQLSubModel):
|
||||
tab = data["tab"]
|
||||
|
||||
table = execute(
|
||||
"SELECT id, name, type, edge, begin_kp, end_kp " +
|
||||
"SELECT id, name, type, edge, begin_rk, end_rk " +
|
||||
f"FROM lateral_contribution WHERE tab = '{tab}'"
|
||||
)
|
||||
|
||||
@@ -125,8 +141,8 @@ class LateralContribution(SQLSubModel):
|
||||
lc.edge = None
|
||||
if row[3] != -1:
|
||||
lc.edge = next(filter(lambda e: e.id == row[3], data["edges"]))
|
||||
lc._begin_kp = row[4]
|
||||
lc._end_kp = row[5]
|
||||
lc._begin_rk = row[4]
|
||||
lc._end_rk = row[5]
|
||||
|
||||
values = execute(
|
||||
"SELECT ind, data0, data1 FROM lateral_contribution_data " +
|
||||
@@ -160,11 +176,11 @@ class LateralContribution(SQLSubModel):
|
||||
sql = (
|
||||
"INSERT INTO " +
|
||||
"lateral_contribution(id, name, type, tab, " +
|
||||
"edge, begin_kp, end_kp) " +
|
||||
"edge, begin_rk, end_rk) " +
|
||||
"VALUES (" +
|
||||
f"{self.id}, '{self._db_format(self._name)}', " +
|
||||
f"'{self._db_format(self._type)}', '{tab}', {edge}, " +
|
||||
f"{self._begin_kp}, {self._end_kp}" +
|
||||
f"{self._begin_rk}, {self._end_rk}" +
|
||||
")"
|
||||
)
|
||||
execute(sql)
|
||||
@@ -231,44 +247,44 @@ class LateralContribution(SQLSubModel):
|
||||
def edge(self, edge):
|
||||
self._edge = edge
|
||||
if edge is not None:
|
||||
self._begin_kp = self._edge.reach.get_kp_min()
|
||||
self._end_kp = self._edge.reach.get_kp_max()
|
||||
self._begin_rk = self._edge.reach.get_rk_min()
|
||||
self._end_rk = self._edge.reach.get_rk_max()
|
||||
self._status.modified()
|
||||
|
||||
def has_edge(self):
|
||||
return self._edge is not None
|
||||
|
||||
@property
|
||||
def begin_kp(self):
|
||||
return self._begin_kp
|
||||
def begin_rk(self):
|
||||
return self._begin_rk
|
||||
|
||||
@begin_kp.setter
|
||||
def begin_kp(self, begin_kp):
|
||||
@begin_rk.setter
|
||||
def begin_rk(self, begin_rk):
|
||||
if self._edge is None:
|
||||
self._begin_kp = begin_kp
|
||||
self._begin_rk = begin_rk
|
||||
else:
|
||||
_min = self._edge.reach.get_kp_min()
|
||||
_max = self._edge.reach.get_kp_max()
|
||||
_min = self._edge.reach.get_rk_min()
|
||||
_max = self._edge.reach.get_rk_max()
|
||||
|
||||
if _min <= begin_kp <= _max:
|
||||
self._begin_kp = begin_kp
|
||||
if _min <= begin_rk <= _max:
|
||||
self._begin_rk = begin_rk
|
||||
|
||||
self._status.modified()
|
||||
|
||||
@property
|
||||
def end_kp(self):
|
||||
return self._end_kp
|
||||
def end_rk(self):
|
||||
return self._end_rk
|
||||
|
||||
@end_kp.setter
|
||||
def end_kp(self, end_kp):
|
||||
@end_rk.setter
|
||||
def end_rk(self, end_rk):
|
||||
if self._edge is None:
|
||||
self._end_kp = end_kp
|
||||
self._end_rk = end_rk
|
||||
else:
|
||||
_min = self._edge.reach.get_kp_min()
|
||||
_max = self._edge.reach.get_kp_max()
|
||||
_min = self._edge.reach.get_rk_min()
|
||||
_max = self._edge.reach.get_rk_max()
|
||||
|
||||
if _min <= end_kp <= _max:
|
||||
self._end_kp = end_kp
|
||||
if _min <= end_rk <= _max:
|
||||
self._end_rk = end_rk
|
||||
|
||||
self._status.modified()
|
||||
|
||||
@@ -377,8 +393,8 @@ class LateralContribution(SQLSubModel):
|
||||
def convert(self, cls):
|
||||
new = cls(name=self.name, status=self._status)
|
||||
new.edge = self.edge
|
||||
new.begin_kp = self.begin_kp
|
||||
new.end_kp = self.end_kp
|
||||
new.begin_rk = self.begin_rk
|
||||
new.end_rk = self.end_rk
|
||||
|
||||
for i, _ in enumerate(self.data):
|
||||
new.add(i)
|
||||
|
||||
+14
-24
@@ -142,26 +142,21 @@ class Graph(object):
|
||||
def insert_node(self, node):
|
||||
return self._add_node(node)
|
||||
|
||||
def remove_node(self, node_name: str):
|
||||
self._nodes = list(
|
||||
filter(
|
||||
lambda n: n.name != node_name,
|
||||
self._nodes
|
||||
)
|
||||
)
|
||||
self._remove_associated_edge(node_name)
|
||||
def remove_node(self, node):
|
||||
self._nodes.remove(node)
|
||||
self._remove_associated_edge(node)
|
||||
self._status.modified()
|
||||
|
||||
def _remove_associated_edge(self, node_name: str):
|
||||
def _remove_associated_edge(self, node: str):
|
||||
edges = list(
|
||||
filter(
|
||||
lambda e: (e.node1.name == node_name or
|
||||
e.node2.name == node_name),
|
||||
lambda e: (e.node1 == node or
|
||||
e.node2 == node),
|
||||
self._edges,
|
||||
)
|
||||
)
|
||||
for edge in edges:
|
||||
self.remove_edge(edge.name)
|
||||
self.remove_edge(edge)
|
||||
|
||||
def create_node(self, x: float = 0.0, y: float = 0.0):
|
||||
node = self._create_node(x, y)
|
||||
@@ -176,11 +171,11 @@ class Graph(object):
|
||||
return edge
|
||||
|
||||
def _add_edge(self, edge):
|
||||
# This edge already exists ?
|
||||
if any(filter(lambda e: (e.node1 == edge.node1 and
|
||||
e.node2 == edge.node2),
|
||||
self._edges)):
|
||||
return None
|
||||
# # This edge already exists ?
|
||||
# if any(filter(lambda e: (e.node1 == edge.node1 and
|
||||
# e.node2 == edge.node2),
|
||||
# self._edges)):
|
||||
# return None
|
||||
|
||||
self._edges.append(edge)
|
||||
|
||||
@@ -197,13 +192,8 @@ class Graph(object):
|
||||
def create_edge(self, n1: Node, n2: Node):
|
||||
return self._create_edge(n1, n2)
|
||||
|
||||
def remove_edge(self, edge_name: str):
|
||||
self._edges = list(
|
||||
filter(
|
||||
lambda e: e.name != edge_name,
|
||||
self._edges
|
||||
)
|
||||
)
|
||||
def remove_edge(self, edge):
|
||||
self._edges.remove(edge)
|
||||
self._status.modified()
|
||||
|
||||
def is_upstream_node(self, node):
|
||||
|
||||
@@ -60,7 +60,7 @@ class GraphTestCase(unittest.TestCase):
|
||||
|
||||
e0 = g.add_edge(n0, n1)
|
||||
|
||||
g.remove_edge(e0.name)
|
||||
g.remove_edge(e0)
|
||||
|
||||
self.assertEqual(g.nodes_counts(), 2)
|
||||
self.assertEqual(g.edges_counts(), 0)
|
||||
@@ -73,7 +73,7 @@ class GraphTestCase(unittest.TestCase):
|
||||
|
||||
e0 = g.add_edge(n0, n1)
|
||||
|
||||
g.remove_node(n0.name)
|
||||
g.remove_node(n0)
|
||||
|
||||
self.assertEqual(g.nodes_counts(), 1)
|
||||
self.assertEqual(g.edges_counts(), 0)
|
||||
@@ -88,7 +88,7 @@ class GraphTestCase(unittest.TestCase):
|
||||
e0 = g.add_edge(n0, n1)
|
||||
e1 = g.add_edge(n1, n2)
|
||||
|
||||
g.remove_node(n1.name)
|
||||
g.remove_node(n1)
|
||||
|
||||
self.assertEqual(g.nodes_counts(), 2)
|
||||
self.assertEqual(g.edges_counts(), 0)
|
||||
|
||||
@@ -35,8 +35,8 @@ class Profile(object):
|
||||
return self._profile.name
|
||||
|
||||
@property
|
||||
def kp(self):
|
||||
return self._profile.kp
|
||||
def rk(self):
|
||||
return self._profile.rk
|
||||
|
||||
@property
|
||||
def geometry(self):
|
||||
@@ -126,3 +126,11 @@ class River(object):
|
||||
|
||||
self._reachs.append(new)
|
||||
return new
|
||||
|
||||
def get_reach_by_geometry(self, geometry_reach):
|
||||
return next(
|
||||
filter(
|
||||
lambda r: r.geometry is geometry_reach,
|
||||
self._reachs
|
||||
)
|
||||
)
|
||||
@@ -378,6 +378,7 @@ class River(Graph, SQLSubModel):
|
||||
|
||||
def init_default(self):
|
||||
self.init_default_network()
|
||||
self.init_default_sediment()
|
||||
self.init_default_additional_files()
|
||||
|
||||
def init_default_network(self):
|
||||
@@ -386,6 +387,20 @@ class River(Graph, SQLSubModel):
|
||||
|
||||
e = self.add_edge(n1, n2)
|
||||
|
||||
def init_default_sediment(self):
|
||||
sediment = self._sediment_layers
|
||||
default = sediment.new(0)
|
||||
default_0 = default.new(0)
|
||||
|
||||
default.name = "default"
|
||||
default.comment = "Default sediment layers"
|
||||
|
||||
default_0.name = "L0"
|
||||
default_0.height = 1.0
|
||||
default_0.d50 = 0.002
|
||||
default_0.sigma = 1.0
|
||||
default_0.critical_constraint = 0.047
|
||||
|
||||
def init_default_additional_files(self):
|
||||
add_file = self._additional_files.new(0)
|
||||
add_file.name = "Pamhyr2 stamp file"
|
||||
|
||||
+17
-2
@@ -22,10 +22,19 @@ logger = logging.getLogger()
|
||||
|
||||
|
||||
class SavedStatus(object):
|
||||
def __init__(self):
|
||||
def __init__(self, version=0):
|
||||
super(SavedStatus, self).__init__()
|
||||
self._version = version
|
||||
self._saved = True
|
||||
|
||||
@property
|
||||
def version(self):
|
||||
return self._version
|
||||
|
||||
@version.setter
|
||||
def version(self, version):
|
||||
self._version = version
|
||||
|
||||
def is_saved(self):
|
||||
return self._saved
|
||||
|
||||
@@ -34,5 +43,11 @@ class SavedStatus(object):
|
||||
self._saved = True
|
||||
|
||||
def modified(self):
|
||||
# logger.debug("model status set as modified")
|
||||
if self._saved:
|
||||
self._version += 1
|
||||
|
||||
logger.debug(
|
||||
"STATUS: Model status set as modified " +
|
||||
f"at version {self._version}"
|
||||
)
|
||||
self._saved = False
|
||||
+36
-9
@@ -41,7 +41,7 @@ class Study(SQLModel):
|
||||
|
||||
def __init__(self, filename=None, init_new=True):
|
||||
# Metadata
|
||||
self._version = "0.0.9"
|
||||
self._version = "0.0.11"
|
||||
self.creation_date = datetime.now()
|
||||
self.last_modification_date = datetime.now()
|
||||
self.last_save_date = datetime.now()
|
||||
@@ -71,6 +71,8 @@ class Study(SQLModel):
|
||||
lst = [
|
||||
StudyNetworkReachChecker(),
|
||||
StudyGeometryChecker(),
|
||||
StudyInitialConditionsChecker(),
|
||||
StudyBoundaryConditionChecker(),
|
||||
# DummyOK(),
|
||||
# DummyWARNING(),
|
||||
# DummyERROR(),
|
||||
@@ -115,6 +117,9 @@ class Study(SQLModel):
|
||||
if ".backup" in self.filename:
|
||||
is_new = True
|
||||
|
||||
if not os.path.exists(self.filename):
|
||||
is_new = True
|
||||
|
||||
self._init_db_file(self.filename, is_new=is_new)
|
||||
self.commit()
|
||||
|
||||
@@ -207,7 +212,12 @@ class Study(SQLModel):
|
||||
def _create(self):
|
||||
# Info (metadata)
|
||||
self.execute(
|
||||
"CREATE TABLE info(key TEXT NOT NULL UNIQUE, value TEXT NOT NULL)")
|
||||
"INSERT INTO info VALUES ('study_release', '0')"
|
||||
)
|
||||
|
||||
self.execute(
|
||||
"CREATE TABLE info(key TEXT NOT NULL UNIQUE, value TEXT NOT NULL)"
|
||||
)
|
||||
self.execute(
|
||||
"INSERT INTO info VALUES ('version', " +
|
||||
f"'{self._db_format(self._version)}')",
|
||||
@@ -216,19 +226,20 @@ class Study(SQLModel):
|
||||
self.execute("INSERT INTO info VALUES ('name', '')")
|
||||
self.execute("INSERT INTO info VALUES ('description', '')")
|
||||
self.execute(
|
||||
f"INSERT INTO info VALUES ('time_system', '{self._time_system}')")
|
||||
f"INSERT INTO info VALUES ('time_system', '{self._time_system}')"
|
||||
)
|
||||
self.execute(
|
||||
f"INSERT INTO info VALUES ('date', " +
|
||||
"'{self._date.timestamp()}')"
|
||||
"INSERT INTO info VALUES ('date', " +
|
||||
f"'{self._date.timestamp()}')"
|
||||
)
|
||||
|
||||
self.execute(
|
||||
f"INSERT INTO info VALUES ('creation_date', " +
|
||||
"'{self.creation_time.timestamp()}')"
|
||||
"INSERT INTO info VALUES ('creation_date', " +
|
||||
f"'{self.creation_date.timestamp()}')"
|
||||
)
|
||||
self.execute(
|
||||
f"INSERT INTO info VALUES ('last_save_date', " +
|
||||
"'{self.last_save_time.timestamp()}')"
|
||||
"INSERT INTO info VALUES ('last_save_date', " +
|
||||
f"'{self.last_save_date.timestamp()}')"
|
||||
)
|
||||
|
||||
self._create_submodel()
|
||||
@@ -243,6 +254,12 @@ class Study(SQLModel):
|
||||
|
||||
logger.debug("Update database")
|
||||
|
||||
major, minor, release = version[0].split('.')
|
||||
if major == "0" and minor == "0" and int(release) < 10:
|
||||
self.execute(
|
||||
"INSERT INTO info VALUES ('study_release', '0')"
|
||||
)
|
||||
|
||||
if self._update_submodel(version[0]):
|
||||
self.execute(
|
||||
f"UPDATE info SET value='{self._version}' WHERE key='version'"
|
||||
@@ -256,6 +273,11 @@ class Study(SQLModel):
|
||||
def _load(cls, filename):
|
||||
new = cls(init_new=False, filename=filename)
|
||||
|
||||
version = new.execute(
|
||||
"SELECT value FROM info WHERE key='study_release'"
|
||||
)
|
||||
new.status.version = int(version[0])
|
||||
|
||||
# TODO: Load metadata
|
||||
new.name = new.execute("SELECT value FROM info WHERE key='name'")[0]
|
||||
new.description = new.execute(
|
||||
@@ -290,6 +312,11 @@ class Study(SQLModel):
|
||||
def _save(self, progress=None):
|
||||
progress = progress if progress is not None else lambda: None
|
||||
|
||||
self.execute(
|
||||
"INSERT INTO info VALUES ('study_release', " +
|
||||
f"'{self.status.version}')"
|
||||
)
|
||||
|
||||
self.execute(
|
||||
f"UPDATE info SET " +
|
||||
f"value='{self._db_format(self.name)}' WHERE key='name'"
|
||||
|
||||
+70
-1
@@ -23,7 +23,20 @@ from enum import Flag, auto
|
||||
logger = logging.getLogger()
|
||||
|
||||
|
||||
class Modules(Flag):
|
||||
class IterableFlag(Flag):
|
||||
def __iter__(self):
|
||||
all = filter(
|
||||
lambda v: v in self,
|
||||
type(self).values()
|
||||
)
|
||||
return all
|
||||
|
||||
@classmethod
|
||||
def values(cls):
|
||||
raise Exception("Not implemented yet")
|
||||
|
||||
|
||||
class Modules(IterableFlag):
|
||||
NONE = 0
|
||||
|
||||
# General
|
||||
@@ -48,6 +61,24 @@ class Modules(Flag):
|
||||
# Display
|
||||
WINDOW_LIST = auto()
|
||||
|
||||
@classmethod
|
||||
def values(cls):
|
||||
return [
|
||||
cls.STUDY, cls.CONFIG,
|
||||
cls.NETWORK,
|
||||
cls.GEOMETRY,
|
||||
cls.BOUNDARY_CONDITION,
|
||||
cls.LATERAL_CONTRIBUTION,
|
||||
cls.FRICTION,
|
||||
cls.INITIAL_CONDITION,
|
||||
cls.HYDRAULIC_STRUCTURES,
|
||||
cls.RESERVOIR,
|
||||
cls.SEDIMENT_LAYER,
|
||||
cls.ADDITIONAL_FILES,
|
||||
cls.RESULTS,
|
||||
cls.WINDOW_LIST,
|
||||
]
|
||||
|
||||
@classmethod
|
||||
def all(cls):
|
||||
return ~cls.NONE
|
||||
@@ -93,3 +124,41 @@ class Modules(Flag):
|
||||
cls.RESERVOIR: "Reservoir",
|
||||
cls.SEDIMENT_LAYER: "Sediment layer",
|
||||
}
|
||||
|
||||
def impact(self):
|
||||
res = Modules(0)
|
||||
for mod in self:
|
||||
if mod in _impact:
|
||||
for i in _impact[mod]:
|
||||
res |= i
|
||||
return res
|
||||
|
||||
def impact_set(self):
|
||||
res = []
|
||||
for mod in self:
|
||||
if mod in _impact:
|
||||
res += _impact[mod]
|
||||
|
||||
return set(res)
|
||||
|
||||
|
||||
_impact = {
|
||||
Modules.NETWORK: [
|
||||
Modules.GEOMETRY, Modules.BOUNDARY_CONDITION,
|
||||
Modules.LATERAL_CONTRIBUTION, Modules.FRICTION,
|
||||
Modules.RESERVOIR, Modules.SEDIMENT_LAYER,
|
||||
],
|
||||
Modules.GEOMETRY: [
|
||||
Modules.LATERAL_CONTRIBUTION, Modules.FRICTION,
|
||||
Modules.INITIAL_CONDITION, Modules.SEDIMENT_LAYER,
|
||||
],
|
||||
Modules.BOUNDARY_CONDITION: [
|
||||
Modules.SEDIMENT_LAYER
|
||||
],
|
||||
Modules.LATERAL_CONTRIBUTION: [],
|
||||
Modules.FRICTION: [],
|
||||
Modules.INITIAL_CONDITION: [],
|
||||
Modules.HYDRAULIC_STRUCTURES: [],
|
||||
Modules.RESERVOIR: [],
|
||||
Modules.SEDIMENT_LAYER: [],
|
||||
}
|
||||
+18
-14
@@ -180,7 +180,7 @@ class Mage(CommandLineSolver):
|
||||
c1 = f"{profile.code1:>6}"
|
||||
c2 = f"{profile.code2:>6}"
|
||||
t = f"{len(profile.points):>6}"
|
||||
kp = f"{profile.kp:>12f}"[0:12]
|
||||
rk = f"{profile.rk:>12f}"[0:12]
|
||||
pname = profile.name
|
||||
if profile.name == "":
|
||||
# Generate name from profile id prefixed with
|
||||
@@ -207,7 +207,7 @@ class Mage(CommandLineSolver):
|
||||
)
|
||||
|
||||
# Profile header line
|
||||
wfile.write(f"{num}{c1}{c2}{t} {kp} {pname} {sediment}\n")
|
||||
wfile.write(f"{num}{c1}{c2}{t} {rk} {pname} {sediment}\n")
|
||||
|
||||
def _export_ST_point_line(self, wfile, files, point):
|
||||
x = f"{point.x:<12.4f}"[0:12]
|
||||
@@ -339,7 +339,7 @@ class Mage(CommandLineSolver):
|
||||
f.write(f"* {lateral.edge.name} ({name}) {lateral.lctype}\n")
|
||||
f.write(
|
||||
f"${name} " +
|
||||
f"{lateral.begin_kp:>10.4f} {lateral.end_kp:>10.4f}\n"
|
||||
f"{lateral.begin_rk:>10.4f} {lateral.end_rk:>10.4f}\n"
|
||||
)
|
||||
header = lateral.header
|
||||
f.write(f"*{header[0]:>9}|{header[1]:>10}\n")
|
||||
@@ -371,17 +371,17 @@ class Mage(CommandLineSolver):
|
||||
continue
|
||||
|
||||
num = f"{id:>3}"
|
||||
bkp = f"{friction.begin_kp:>10.3f}"
|
||||
ekp = f"{friction.end_kp:>10.3f}"
|
||||
brk = f"{friction.begin_rk:>10.3f}"
|
||||
erk = f"{friction.end_rk:>10.3f}"
|
||||
|
||||
# if friction.begin_kp != friction.end_kp:
|
||||
# if friction.begin_rk != friction.end_rk:
|
||||
# print("TODO")
|
||||
|
||||
strickler = friction.begin_strickler
|
||||
coef_1 = f"{strickler.minor:>10.3f}"
|
||||
coef_2 = f"{strickler.medium:>10.3f}"
|
||||
|
||||
f.write(f"K{num} {bkp}{ekp}{coef_1}{coef_2}\n")
|
||||
f.write(f"K{num} {brk}{erk}{coef_1}{coef_2}\n")
|
||||
|
||||
id += 1
|
||||
|
||||
@@ -401,7 +401,7 @@ class Mage(CommandLineSolver):
|
||||
|
||||
# TODO put real date...
|
||||
f.write(f"$ date en minutes : 0.00\n")
|
||||
f.write(f"* IB IS discharge elevation kp\n")
|
||||
f.write(f"* IB IS discharge elevation rk\n")
|
||||
|
||||
id = 1
|
||||
for reach in reachs:
|
||||
@@ -418,9 +418,9 @@ class Mage(CommandLineSolver):
|
||||
IS = f"{id_sec}"
|
||||
discharge = f"{d['discharge']:>10.5f}"
|
||||
elevation = f"{d['elevation']:>11.6f}"
|
||||
kp = f"{d['kp']:>9.2f}"
|
||||
rk = f"{d['rk']:>9.2f}"
|
||||
|
||||
f.write(f"{IR} {IS} {discharge} {elevation} {kp}\n")
|
||||
f.write(f"{IR} {IS} {discharge} {elevation} {rk}\n")
|
||||
id_sec += 1
|
||||
|
||||
id += 1
|
||||
@@ -492,12 +492,15 @@ class Mage(CommandLineSolver):
|
||||
if not hs.input_reach.is_enable():
|
||||
continue
|
||||
|
||||
if hs.input_kp is None:
|
||||
if not hs.enabled:
|
||||
continue
|
||||
|
||||
if hs.input_rk is None:
|
||||
continue
|
||||
|
||||
f.write(
|
||||
'* ouvrage au pk ' +
|
||||
f"{float(hs.input_kp):>12.1f}" + ' ' +
|
||||
f"{float(hs.input_rk):>12.1f}" + ' ' +
|
||||
hs.name + '\n'
|
||||
)
|
||||
|
||||
@@ -507,6 +510,7 @@ class Mage(CommandLineSolver):
|
||||
|
||||
def _export_SIN_bhs(self, study, sin_dict, hs, f):
|
||||
for bhs in hs.basic_structures:
|
||||
if bhs.enabled:
|
||||
reach_id = study.river.get_edge_id(hs.input_reach) + 1
|
||||
param_str = ' '.join(
|
||||
[
|
||||
@@ -523,7 +527,7 @@ class Mage(CommandLineSolver):
|
||||
|
||||
f.write(
|
||||
f"{sin_dict[bhs._type]} " +
|
||||
f"{reach_id} {float(hs.input_kp):>12.3f} " +
|
||||
f"{reach_id} {float(hs.input_rk):>12.3f} " +
|
||||
f"{param_str} {name}\n"
|
||||
)
|
||||
|
||||
@@ -799,7 +803,7 @@ class Mage8(Mage):
|
||||
value = "O" if value.lower() == "y" else "N"
|
||||
|
||||
if name == "init_internal":
|
||||
value = ("p" if value.lower() in ["y", "yes", "true"]
|
||||
value = ("p" if value.lower() in ["y", "yes", "true", "o"]
|
||||
else "")
|
||||
|
||||
logger.debug(
|
||||
|
||||
@@ -0,0 +1,441 @@
|
||||
# RubarBE.py -- Pamhyr
|
||||
# Copyright (C) 2024 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 os
|
||||
import logging
|
||||
import numpy as np
|
||||
|
||||
from tools import timer, trace, old_pamhyr_date_to_timestamp
|
||||
|
||||
from Solver.CommandLine import CommandLineSolver
|
||||
|
||||
from Model.Results.Results import Results
|
||||
from Model.Results.River.River import River, Reach, Profile
|
||||
|
||||
logger = logging.getLogger()
|
||||
|
||||
|
||||
class RubarBE(CommandLineSolver):
|
||||
_type = "rubarbe"
|
||||
|
||||
def __init__(self, name):
|
||||
super(RubarBE, self).__init__(name)
|
||||
|
||||
self._type = "rubarbe"
|
||||
|
||||
self._cmd_input = ""
|
||||
self._cmd_solver = "@path @input -o @output"
|
||||
self._cmd_output = ""
|
||||
|
||||
@classmethod
|
||||
def default_parameters(cls):
|
||||
lst = super(RubarBE, cls).default_parameters()
|
||||
|
||||
lst += [
|
||||
("rubarbe_cfl", "0.50000E+00"),
|
||||
("rubarbe_condam", "1"),
|
||||
("rubarbe_condav", "3"),
|
||||
("rubarbe_regime", "0"),
|
||||
("rubarbe_iodev", "n"),
|
||||
("rubarbe_iodebord", ""),
|
||||
("rubarbe_iostockage", ""),
|
||||
("rubarbe_iopdt", "y"),
|
||||
("rubarbe_iovis", "n"),
|
||||
("rubarbe_rep", "n"),
|
||||
("rubarbe_tinit", "000:00:00:00"),
|
||||
("rubarbe_tmax", "999:99:99:00"),
|
||||
("rubarbe_tiopdt", "000:00:00:00"),
|
||||
("rubarbe_dt", "3000.0"),
|
||||
("rubarbe_ts", "999:99:99:00"),
|
||||
("rubarbe_dtsauv", "999:99:99:00"),
|
||||
("rubarbe_psave", "999:99:99:00"),
|
||||
("rubarbe_fdeb1", "1"),
|
||||
("rubarbe_fdeb2", "10"),
|
||||
("rubarbe_fdeb3", "100"),
|
||||
("rubarbe_tf_1", "y"),
|
||||
("rubarbe_tf_2", "y"),
|
||||
("rubarbe_tf_3", "y"),
|
||||
("rubarbe_tf_4", "y"),
|
||||
("rubarbe_tf_5", "y"),
|
||||
("rubarbe_tf_6", "n"),
|
||||
("rubarbe_trased", "y"),
|
||||
("rubarbe_optfpc", "0"),
|
||||
("rubarbe_ros", "2650.0"),
|
||||
("rubarbe_dm", "0.1"),
|
||||
("rubarbe_segma", "1.0"),
|
||||
# Sediment parameters
|
||||
("rubarbe_sediment_ros", "2650.0"),
|
||||
("rubarbe_sediment_por", "0.4"),
|
||||
("rubarbe_sediment_dcharg", "0.0"),
|
||||
("rubarbe_sediment_halfa", "1.0"),
|
||||
("rubarbe_sediment_mult_1", "1.0"),
|
||||
("rubarbe_sediment_mult_2", ""),
|
||||
("rubarbe_sediment_mult_3", ""),
|
||||
("rubarbe_sediment_mult_4", ""),
|
||||
("rubarbe_sediment_mult_5", ""),
|
||||
("rubarbe_sediment_visc", "0.047"),
|
||||
("rubarbe_sediment_opts", "6"),
|
||||
("rubarbe_sediment_odchar", "0"),
|
||||
("rubarbe_sediment_unisol", "1"),
|
||||
("rubarbe_sediment_typdef", "3"),
|
||||
("rubarbe_sediment_depot", "2"),
|
||||
("rubarbe_sediment_choixc", "2"),
|
||||
("rubarbe_sediment_option", "2"),
|
||||
("rubarbe_sediment_capsol", "1"),
|
||||
("rubarbe_sediment_bmiu", "0.85"),
|
||||
("rubarbe_sediment_demix", "0"),
|
||||
("rubarbe_sediment_defond", "1"),
|
||||
("rubarbe_sediment_varcons", "1"),
|
||||
("rubarbe_sediment_dchard", "0.0"),
|
||||
("rubarbe_sediment_dchars", "0.0"),
|
||||
]
|
||||
|
||||
return lst
|
||||
|
||||
@classmethod
|
||||
def checkers(cls):
|
||||
lst = [
|
||||
]
|
||||
|
||||
return lst
|
||||
|
||||
##########
|
||||
# Export #
|
||||
##########
|
||||
|
||||
def cmd_args(self, study):
|
||||
lst = super(RubarBE, self).cmd_args(study)
|
||||
|
||||
return lst
|
||||
|
||||
def input_param(self):
|
||||
name = self._study.name
|
||||
return f"{name}.REP"
|
||||
|
||||
def output_param(self):
|
||||
name = self._study.name
|
||||
return f"{name}.BIN"
|
||||
|
||||
def log_file(self):
|
||||
name = self._study.name
|
||||
return f"{name}.TRA"
|
||||
|
||||
def export(self, study, repertory, qlog=None):
|
||||
self._study = study
|
||||
name = study.name.replace(" ", "_")
|
||||
|
||||
self._export_donnee(study, repertory, qlog, name=name)
|
||||
self._export_ts(study, repertory, qlog, name=name)
|
||||
self._export_geomac_i(study, repertory, qlog, name=name)
|
||||
self._export_mail(study, repertory, qlog, name=name)
|
||||
self._export_tps(study, repertory, qlog, name=name)
|
||||
self._export_stricklers(study, repertory, qlog, name=name)
|
||||
|
||||
def _export_donnee(self, study, repertory, qlog, name="0"):
|
||||
if qlog is not None:
|
||||
qlog.put("Export DONNEE file")
|
||||
|
||||
with open(
|
||||
os.path.join(
|
||||
repertory, f"donnee.{name}"
|
||||
), "w+"
|
||||
) as f:
|
||||
params = filter(
|
||||
lambda p: "rubarbe_sediment_" not in p.name,
|
||||
study.river.get_params(self._type).parameters
|
||||
)
|
||||
|
||||
it = iter(params)
|
||||
|
||||
line = 0
|
||||
while line < 29:
|
||||
param = next(it)
|
||||
name = param.name
|
||||
value = param.value
|
||||
|
||||
if value != "":
|
||||
# Value format
|
||||
if value.count(':') == 3:
|
||||
value = old_pamhyr_date_to_timestamp(value)
|
||||
value = f"{value:>12.5e}".upper()
|
||||
|
||||
if value.count('.') == 1:
|
||||
value = f"{float(value):>12.5e}".upper()
|
||||
|
||||
if value == "y" or value == "n":
|
||||
value = "O" if value == "y" else "N"
|
||||
|
||||
# Write value
|
||||
f.write(f"{name:<50}{value}")
|
||||
|
||||
# Add values of 'rubarbe_iodebord' and
|
||||
# 'rubarbe_iostockage'
|
||||
if name == "rubarbe_iodev":
|
||||
v2 = next(it).value
|
||||
v3 = next(it).value
|
||||
|
||||
f.write(f"{v2}{v3}")
|
||||
|
||||
# New line
|
||||
f.write(f"\n")
|
||||
|
||||
line += 1
|
||||
|
||||
def _export_ts(self, study, repertory, qlog, name="0"):
|
||||
if qlog is not None:
|
||||
qlog.put("Export TS file")
|
||||
|
||||
with open(
|
||||
os.path.join(
|
||||
repertory, f"ts.{name}"
|
||||
), "w+"
|
||||
) as f:
|
||||
def float_format(string):
|
||||
if "." in string:
|
||||
return f"{float(string):>10.0f}"
|
||||
return ""
|
||||
|
||||
params = filter(
|
||||
lambda p: "rubarbe_sediment_" in p.name,
|
||||
study.river.get_params(self.type).parameters
|
||||
)
|
||||
it = iter(params)
|
||||
|
||||
line = 0
|
||||
while line < 20:
|
||||
param = next(it)
|
||||
name = param.name
|
||||
value = param.value
|
||||
|
||||
if value != "":
|
||||
# Value format
|
||||
if value.count('.') == 1:
|
||||
value = f"{float_format(value)}"
|
||||
else:
|
||||
value = f"{value:>10}"
|
||||
|
||||
# Write value
|
||||
f.write(f"{name:<50}{value}")
|
||||
|
||||
# Add values of 'rubarbe_iodebord' and
|
||||
# 'rubarbe_iostockage'
|
||||
if name == "rubarbe_sediment_mult_1":
|
||||
m2 = f"{float_format(next(it).value)}"
|
||||
m3 = f"{float_format(next(it).value)}"
|
||||
m4 = f"{float_format(next(it).value)}"
|
||||
m5 = f"{float_format(next(it).value)}"
|
||||
|
||||
f.write(f"{m2}{m3}{m4}{m5}")
|
||||
|
||||
# New line
|
||||
f.write(f"\n")
|
||||
|
||||
line += 1
|
||||
|
||||
def _export_geomac_i(self, study, repertory, qlog, name="0"):
|
||||
if qlog is not None:
|
||||
qlog.put("Export GEOMAC-i file")
|
||||
|
||||
with open(
|
||||
os.path.join(
|
||||
repertory, f"geomac-i.{name}"
|
||||
), "w+"
|
||||
) as f:
|
||||
for edge in study.river.enable_edges():
|
||||
reach = edge.reach
|
||||
n_profiles = len(reach)
|
||||
time = 0.0
|
||||
|
||||
f.write(f"{n_profiles:>5} {time:>11.3f}\n")
|
||||
|
||||
ind = 1
|
||||
for profile in reach.profiles:
|
||||
rk = profile.rk
|
||||
n_points = len(profile)
|
||||
|
||||
f.write(f"{ind:>4} {rk:>11.3f} {n_points:>4}\n")
|
||||
|
||||
for point in profile.points:
|
||||
label = point.name.lower()
|
||||
if label != "":
|
||||
if label[0] == "r":
|
||||
label = label[1].upper()
|
||||
else:
|
||||
label = lable[0]
|
||||
|
||||
y = point.y
|
||||
z = point.z
|
||||
dcs = 0.001
|
||||
scs = 1.0
|
||||
tmcs = 0.0
|
||||
|
||||
f.write(
|
||||
f"{label} {y:>11.5f}" +
|
||||
f"{z:>13.5f}{dcs:>15.10f}" +
|
||||
f"{scs:>15.10f}{tmcs:>15.5f}" +
|
||||
"\n"
|
||||
)
|
||||
|
||||
ind += 1
|
||||
|
||||
def _export_mail(self, study, repertory, qlog, name="0"):
|
||||
if qlog is not None:
|
||||
qlog.put("Export MAIL file")
|
||||
|
||||
with open(
|
||||
os.path.join(
|
||||
repertory, f"mail.{name}"
|
||||
), "w+"
|
||||
) as f:
|
||||
for edge in study.river.enable_edges():
|
||||
reach = edge.reach
|
||||
lm = len(reach) + 1
|
||||
f.write(f"{lm:>13}\n")
|
||||
|
||||
for mails in [reach.inter_profiles_rk(),
|
||||
reach.get_rk()]:
|
||||
ind = 0
|
||||
for mail in mails:
|
||||
f.write(f"{mail:15.3f}")
|
||||
|
||||
ind += 1
|
||||
if ind % 3 == 0:
|
||||
f.write("\n")
|
||||
|
||||
if ind % 3 != 0:
|
||||
f.write("\n")
|
||||
|
||||
def _export_stricklers(self, study, repertory, qlog, name="0"):
|
||||
self._export_frot(study, repertory, qlog, name=name, version="")
|
||||
self._export_frot(study, repertory, qlog, name=name, version="2")
|
||||
|
||||
def _export_frot(self, study, repertory, qlog, name="0", version=""):
|
||||
if qlog is not None:
|
||||
qlog.put(f"Export FROT{version} file")
|
||||
|
||||
with open(
|
||||
os.path.join(
|
||||
repertory, f"frot{version}.{name}"
|
||||
), "w+"
|
||||
) as f:
|
||||
for edge in study.river.enable_edges():
|
||||
reach = edge.reach
|
||||
lm = len(reach) + 1
|
||||
f.write(f"{lm:>6}\n")
|
||||
|
||||
def get_stricklers_from_rk(rk):
|
||||
return next(
|
||||
map(
|
||||
lambda s: (
|
||||
s.begin_strickler.medium if version == "2"
|
||||
else s.begin_strickler.minor
|
||||
),
|
||||
filter(
|
||||
lambda f: rk in f,
|
||||
edge.frictions.lst
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
ind = 1
|
||||
for mail in edge.reach.inter_profiles_rk():
|
||||
coef = get_stricklers_from_rk(mail)
|
||||
|
||||
f.write(f"{ind:>6} {coef:>12.5f}")
|
||||
|
||||
ind += 1
|
||||
f.write("\n")
|
||||
|
||||
def _export_tps(self, study, repertory, qlog, name="0"):
|
||||
if qlog is not None:
|
||||
qlog.put("Export TPS file")
|
||||
|
||||
with open(
|
||||
os.path.join(
|
||||
repertory, f"tps.{name}"
|
||||
), "w+"
|
||||
) as f:
|
||||
for edge in study.river.enable_edges():
|
||||
reach = edge.reach
|
||||
|
||||
f.write(f"0.0\n")
|
||||
|
||||
ics = study.river.initial_conditions.get(edge)
|
||||
data = self._export_tps_init_data(ics)
|
||||
|
||||
profiles = reach.profiles
|
||||
first = profiles[0]
|
||||
last = profiles[-1]
|
||||
|
||||
if first.rk not in data or last.rk not in data:
|
||||
logger.error(
|
||||
"Study initial condition is not fully defined"
|
||||
)
|
||||
return
|
||||
|
||||
f_h_s = self._export_tps_profile_height_speed(first, data)
|
||||
l_h_s = self._export_tps_profile_height_speed(last, data)
|
||||
|
||||
# First mail
|
||||
f.write(f"{1:>5} {f_h_s[0]} {f_h_s[1]}")
|
||||
|
||||
ind = 2
|
||||
it = iter(profiles)
|
||||
prev = next(it)
|
||||
|
||||
prev_h, prev_s = f_h_s
|
||||
for profile in it:
|
||||
if profile.rk not in data:
|
||||
ind += 1
|
||||
continue
|
||||
|
||||
cur_h, cur_s = self._export_tps_profile_height_speed(
|
||||
profile, data
|
||||
)
|
||||
|
||||
# Mean of height and speed
|
||||
h = (prev_h + cur_h) / 2
|
||||
s = (prev_s + cur_s) / 2
|
||||
|
||||
f.write(f"{ind:>5} {h} {s}\n")
|
||||
|
||||
prev_h, prev_s = cur_h, cur_s
|
||||
ind += 1
|
||||
|
||||
# Last mail
|
||||
f.write(f"{ind:>5} {f_h_s[0]} {f_h_s[1]}")
|
||||
|
||||
def _export_tps_init_data(self, ics):
|
||||
data = {}
|
||||
|
||||
for d in ics.data:
|
||||
data[d['rk']] = (
|
||||
d['elevation'],
|
||||
d['discharge'],
|
||||
)
|
||||
|
||||
return data
|
||||
|
||||
def _export_tps_profile_height_speed(self, profile, data):
|
||||
z = data[profile.rk][0]
|
||||
q = data[profile.rk][1]
|
||||
|
||||
height = z - profile.z_min()
|
||||
speed = profile.speed(q, z)
|
||||
|
||||
return height, speed
|
||||
@@ -22,6 +22,7 @@ from Solver.GenericSolver import GenericSolver
|
||||
from Solver.Mage import (
|
||||
Mage7, Mage8, MageFake7,
|
||||
)
|
||||
from Solver.RubarBE import RubarBE
|
||||
|
||||
_translate = QCoreApplication.translate
|
||||
|
||||
@@ -30,6 +31,7 @@ solver_long_name = {
|
||||
# "mage7": "Mage v7",
|
||||
"mage8": "Mage v8",
|
||||
# "mage_fake7": "Mage fake v7",
|
||||
# "rubarbe": "RubarBE",
|
||||
}
|
||||
|
||||
solver_type_list = {
|
||||
@@ -37,4 +39,5 @@ solver_type_list = {
|
||||
# "mage7": Mage7,
|
||||
"mage8": Mage8,
|
||||
# "mage_fake7": MageFake7,
|
||||
# "rubarbe": RubarBE,
|
||||
}
|
||||
@@ -67,5 +67,5 @@ class AboutWindow(PamhyrDialog):
|
||||
label = f"\n - {author}" + label
|
||||
except StopIteration:
|
||||
label = _translate("About", "Contributors: ") + label
|
||||
label = "Copyright © 2023-2024 INRAE\n" + label
|
||||
label = "Copyright © 2022-2024 INRAE\n" + label
|
||||
self.set_label_text("label_copyright", label)
|
||||
@@ -99,7 +99,7 @@ class TableModel(PamhyrTableModel):
|
||||
logger.info(e)
|
||||
logger.debug(traceback.format_exc())
|
||||
|
||||
self.dataChanged.emit(index, index)
|
||||
self.update()
|
||||
return True
|
||||
|
||||
def add(self, row, parent=QModelIndex()):
|
||||
@@ -112,7 +112,7 @@ class TableModel(PamhyrTableModel):
|
||||
)
|
||||
|
||||
self.endInsertRows()
|
||||
self.layoutChanged.emit()
|
||||
self.update()
|
||||
|
||||
def delete(self, rows, parent=QModelIndex()):
|
||||
self.beginRemoveRows(parent, rows[0], rows[-1])
|
||||
@@ -135,7 +135,7 @@ class TableModel(PamhyrTableModel):
|
||||
)
|
||||
|
||||
self.layoutAboutToBeChanged.emit()
|
||||
self.layoutChanged.emit()
|
||||
self.update()
|
||||
|
||||
def move_up(self, row, parent=QModelIndex()):
|
||||
if row <= 0:
|
||||
@@ -152,7 +152,7 @@ class TableModel(PamhyrTableModel):
|
||||
)
|
||||
|
||||
self.endMoveRows()
|
||||
self.layoutChanged.emit()
|
||||
self.update()
|
||||
|
||||
def move_down(self, index, parent=QModelIndex()):
|
||||
if row > len(self._data):
|
||||
@@ -169,7 +169,7 @@ class TableModel(PamhyrTableModel):
|
||||
)
|
||||
|
||||
self.endMoveRows()
|
||||
self.layoutChanged.emit()
|
||||
self.update()
|
||||
|
||||
def paste(self, row, header, data):
|
||||
if len(data) == 0:
|
||||
@@ -190,4 +190,13 @@ class TableModel(PamhyrTableModel):
|
||||
)
|
||||
|
||||
self.layoutAboutToBeChanged.emit()
|
||||
self.update()
|
||||
|
||||
def auto_sort(self):
|
||||
self.layoutAboutToBeChanged.emit()
|
||||
self._data.sort(key=lambda x: x[0])
|
||||
self.layoutAboutToBeChanged.emit()
|
||||
|
||||
def update(self):
|
||||
# self.auto_sort()
|
||||
self.layoutChanged.emit()
|
||||
@@ -195,6 +195,7 @@ class EditBoundaryConditionWindow(PamhyrWindow):
|
||||
self.find(QAction, "action_sort").triggered.connect(self.sort)
|
||||
|
||||
self._table.dataChanged.connect(self.update)
|
||||
self._table.layoutChanged.connect(self.update)
|
||||
|
||||
if self._is_solid:
|
||||
self._d50sigma.d50Changed.connect(self.d50_changed)
|
||||
|
||||
@@ -48,7 +48,7 @@ class BCTranslate(MainTranslate):
|
||||
|
||||
self._sub_dict["long_types"] = {
|
||||
"ND": self._dict["not_defined"],
|
||||
"PC": _translate("BoundaryCondition", "Ponctual contribution"),
|
||||
"PC": _translate("BoundaryCondition", "Point sources"),
|
||||
"TZ": _translate("BoundaryCondition", "Z(t)"),
|
||||
"TD": _translate("BoundaryCondition", "Q(t)"),
|
||||
"ZD": _translate("BoundaryCondition", "Q(Z)"),
|
||||
|
||||
@@ -46,7 +46,7 @@ class Worker(QObject):
|
||||
self.signalStatus.emit(checker.name)
|
||||
|
||||
# Run checker
|
||||
checker._run(self._study)
|
||||
checker.run(self._study)
|
||||
|
||||
self.signalStatus.emit("progress")
|
||||
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
# PlotRKC.py -- Pamhyr
|
||||
# Copyright (C) 2023-2024 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 -*-
|
||||
|
||||
from PyQt5.QtCore import QCoreApplication
|
||||
from View.HydraulicStructures.PlotRKC import PlotRKC
|
||||
|
||||
_translate = QCoreApplication.translate
|
||||
|
||||
|
||||
class PlotRKZ(PlotRKC):
|
||||
def __init__(self, canvas=None, trad=None, toolbar=None,
|
||||
river=None, reach=None, profile=None,
|
||||
parent=None):
|
||||
super(PlotRKC, self).__init__(
|
||||
canvas=canvas,
|
||||
trad=trad,
|
||||
data=river,
|
||||
toolbar=toolbar,
|
||||
parent=parent
|
||||
)
|
||||
|
||||
self._current_reach = reach
|
||||
self._current_profile = profile
|
||||
|
||||
self.label_x = self._trad["unit_rk"]
|
||||
self.label_y = self._trad["unit_elevation"]
|
||||
|
||||
self._isometric_axis = False
|
||||
|
||||
self._auto_relim_update = True
|
||||
self._autoscale_update = True
|
||||
self.parent = parent
|
||||
|
||||
def onpick(self, event):
|
||||
return
|
||||
@@ -41,10 +41,10 @@ class PlotStricklers(PamhyrPlot):
|
||||
parent=parent
|
||||
)
|
||||
|
||||
self.label_x = self._trad["kp"]
|
||||
self.label_y = self._trad["stricklers"]
|
||||
self.label_x = self._trad["rk"]
|
||||
self.label_y = self._trad["strickler_plot"]
|
||||
|
||||
self.line_kp_elevation = [None, None]
|
||||
self.line_rk_elevation = [None, None]
|
||||
|
||||
self._isometric_axis = False
|
||||
|
||||
@@ -65,9 +65,9 @@ class PlotStricklers(PamhyrPlot):
|
||||
self._init = True
|
||||
|
||||
def draw_data(self):
|
||||
kp = self.data.reach.get_kp()
|
||||
rk = self.data.reach.get_rk()
|
||||
self.canvas.axes.set_xlim(
|
||||
left=min(kp), right=max(kp)
|
||||
left=min(rk), right=max(rk)
|
||||
)
|
||||
|
||||
frictions = self.data.frictions
|
||||
@@ -76,14 +76,14 @@ class PlotStricklers(PamhyrPlot):
|
||||
self.draw_frictions(lst, self.color_plot)
|
||||
|
||||
# HightLight
|
||||
kp_min, kp_max = (-1, -1)
|
||||
rk_min, rk_max = (-1, -1)
|
||||
if self._highlight_data is not None:
|
||||
kp_min, kp_max = self._highlight_data
|
||||
rk_min, rk_max = self._highlight_data
|
||||
|
||||
lst = list(
|
||||
filter(
|
||||
lambda s: (s.begin_kp == kp_min and
|
||||
s.end_kp == kp_max),
|
||||
lambda s: (s.begin_rk == rk_min and
|
||||
s.end_rk == rk_max),
|
||||
frictions.frictions
|
||||
)
|
||||
)
|
||||
@@ -96,7 +96,7 @@ class PlotStricklers(PamhyrPlot):
|
||||
lambda f: f.is_full_defined(),
|
||||
frictions
|
||||
),
|
||||
key=lambda s: s.begin_kp
|
||||
key=lambda s: s.begin_rk
|
||||
)
|
||||
|
||||
coef = flatten(
|
||||
@@ -106,9 +106,9 @@ class PlotStricklers(PamhyrPlot):
|
||||
)
|
||||
)
|
||||
|
||||
kp = flatten(
|
||||
rk = flatten(
|
||||
map(
|
||||
lambda s: [s.begin_kp, s.end_kp],
|
||||
lambda s: [s.begin_rk, s.end_rk],
|
||||
lst
|
||||
)
|
||||
)
|
||||
@@ -116,13 +116,13 @@ class PlotStricklers(PamhyrPlot):
|
||||
coef_minor = list(map(lambda s: s.minor, coef))
|
||||
coef_medium = list(map(lambda s: s.medium, coef))
|
||||
|
||||
self.line_kp_elevation[0] = self.canvas.axes.plot(
|
||||
kp, coef_minor,
|
||||
self.line_rk_elevation[0] = self.canvas.axes.plot(
|
||||
rk, coef_minor,
|
||||
color=color, lw=1.
|
||||
)
|
||||
|
||||
self.line_kp_elevation[1] = self.canvas.axes.plot(
|
||||
kp, coef_medium,
|
||||
self.line_rk_elevation[1] = self.canvas.axes.plot(
|
||||
rk, coef_medium,
|
||||
color=color, lw=1.
|
||||
)
|
||||
|
||||
|
||||
@@ -113,10 +113,10 @@ class TableModel(PamhyrTableModel):
|
||||
|
||||
if self._headers[column] == "name":
|
||||
return self._lst.get(row).name
|
||||
elif self._headers[column] == "begin_kp":
|
||||
return self._lst.get(row).begin_kp
|
||||
elif self._headers[column] == "end_kp":
|
||||
return self._lst.get(row).end_kp
|
||||
elif self._headers[column] == "begin_rk":
|
||||
return self._lst.get(row).begin_rk
|
||||
elif self._headers[column] == "end_rk":
|
||||
return self._lst.get(row).end_rk
|
||||
elif self._headers[column] == "begin_strickler":
|
||||
value = self._lst.get(row).begin_strickler
|
||||
if value is None:
|
||||
@@ -144,13 +144,13 @@ class TableModel(PamhyrTableModel):
|
||||
self._lst, row, value
|
||||
)
|
||||
)
|
||||
elif self._headers[column] == "begin_kp":
|
||||
elif self._headers[column] == "begin_rk":
|
||||
self._undo.push(
|
||||
SetBeginCommand(
|
||||
self._lst, row, value
|
||||
)
|
||||
)
|
||||
elif self._headers[column] == "end_kp":
|
||||
elif self._headers[column] == "end_rk":
|
||||
self._undo.push(
|
||||
SetEndCommand(
|
||||
self._lst, row, value
|
||||
|
||||
@@ -49,14 +49,14 @@ class SetBeginCommand(QUndoCommand):
|
||||
|
||||
self._frictions = frictions
|
||||
self._index = index
|
||||
self._old = self._frictions.get(self._index).begin_kp
|
||||
self._old = self._frictions.get(self._index).begin_rk
|
||||
self._new = float(new_value)
|
||||
|
||||
def undo(self):
|
||||
self._frictions.get(self._index).begin_kp = float(self._old)
|
||||
self._frictions.get(self._index).begin_rk = float(self._old)
|
||||
|
||||
def redo(self):
|
||||
self._frictions.get(self._index).begin_kp = float(self._new)
|
||||
self._frictions.get(self._index).begin_rk = float(self._new)
|
||||
|
||||
|
||||
class SetEndCommand(QUndoCommand):
|
||||
@@ -65,14 +65,14 @@ class SetEndCommand(QUndoCommand):
|
||||
|
||||
self._frictions = frictions
|
||||
self._index = index
|
||||
self._old = self._frictions.get(self._index).end_kp
|
||||
self._old = self._frictions.get(self._index).end_rk
|
||||
self._new = float(new_value)
|
||||
|
||||
def undo(self):
|
||||
self._frictions.get(self._index).end_kp = float(self._old)
|
||||
self._frictions.get(self._index).end_rk = float(self._old)
|
||||
|
||||
def redo(self):
|
||||
self._frictions.get(self._index).end_kp = float(self._new)
|
||||
self._frictions.get(self._index).end_rk = float(self._new)
|
||||
|
||||
|
||||
class SetBeginStricklerCommand(QUndoCommand):
|
||||
|
||||
@@ -50,7 +50,7 @@ from View.Frictions.Table import (
|
||||
)
|
||||
|
||||
from View.Tools.Plot.PamhyrCanvas import MplCanvas
|
||||
from View.Geometry.PlotKPZ import PlotKPZ
|
||||
from View.Frictions.PlotRKZ import PlotRKZ
|
||||
from View.Frictions.PlotStricklers import PlotStricklers
|
||||
from View.Frictions.translate import FrictionsTranslate
|
||||
|
||||
@@ -110,7 +110,7 @@ class FrictionsWindow(PamhyrWindow):
|
||||
table_view=table,
|
||||
table_headers=self._trad.get_dict("table_headers"),
|
||||
editable_headers=[
|
||||
"name", "begin_kp", "end_kp",
|
||||
"name", "begin_rk", "end_rk",
|
||||
"begin_strickler", "end_strickler"
|
||||
],
|
||||
delegates={
|
||||
@@ -133,11 +133,13 @@ class FrictionsWindow(PamhyrWindow):
|
||||
self.plot_layout = self.find(QVBoxLayout, "verticalLayout")
|
||||
self.plot_layout.addWidget(self.canvas)
|
||||
|
||||
self.plot = PlotKPZ(
|
||||
self.plot = PlotRKZ(
|
||||
canvas=self.canvas,
|
||||
data=self._reach.reach,
|
||||
reach=self._reach,
|
||||
river=self._study.river,
|
||||
trad=self._trad,
|
||||
toolbar=None,
|
||||
parent=self
|
||||
)
|
||||
self.plot.draw()
|
||||
|
||||
@@ -202,7 +204,7 @@ class FrictionsWindow(PamhyrWindow):
|
||||
data = self._reach
|
||||
reach = self._reach.reach
|
||||
sec = self._frictions.get(rows[0])
|
||||
highlight = (sec.begin_kp, sec.end_kp)
|
||||
highlight = (sec.begin_rk, sec.end_rk)
|
||||
|
||||
self.update_plot(highlight)
|
||||
|
||||
|
||||
@@ -27,7 +27,10 @@ class FrictionsTranslate(MainTranslate):
|
||||
def __init__(self):
|
||||
super(FrictionsTranslate, self).__init__()
|
||||
|
||||
self._dict["kp"] = self._dict["unit_kp"]
|
||||
self._dict["rk"] = self._dict["unit_rk"]
|
||||
self._dict["strickler_plot"] = _translate(
|
||||
"Frictions", "Strickler ($m^{1/3}/s$)"
|
||||
)
|
||||
self._dict["stricklers"] = _translate(
|
||||
"Frictions", "Stricklers"
|
||||
)
|
||||
@@ -39,8 +42,8 @@ class FrictionsTranslate(MainTranslate):
|
||||
self._sub_dict["table_headers"] = {
|
||||
# "name": self._dict["name"],
|
||||
# "edge": self._dict["reach"],
|
||||
"begin_kp": _translate("Frictions", "Begin kp (m)"),
|
||||
"end_kp": _translate("Frictions", "End kp (m)"),
|
||||
"begin_strickler": _translate("Frictions", "Begin strickler"),
|
||||
"end_strickler": _translate("Frictions", "End strickler"),
|
||||
"begin_rk": _translate("Frictions", "Start (m)"),
|
||||
"end_rk": _translate("Frictions", "End (m)"),
|
||||
"begin_strickler": _translate("Frictions", "Start coefficient"),
|
||||
"end_strickler": _translate("Frictions", "End coefficient"),
|
||||
}
|
||||
@@ -58,6 +58,7 @@ class MeshingDialog(PamhyrDialog):
|
||||
self._end_cs = -1
|
||||
self._begin_dir = "un"
|
||||
self._end_dir = "np"
|
||||
self._origin = self._reach.profile(0)
|
||||
|
||||
self._init_default_values_profiles()
|
||||
self._init_default_values_guidelines()
|
||||
@@ -67,17 +68,6 @@ class MeshingDialog(PamhyrDialog):
|
||||
self._space_step
|
||||
)
|
||||
|
||||
lm_dict = self._trad.get_dict("lm_dict")
|
||||
self.combobox_add_items(
|
||||
"comboBox_lm", list(
|
||||
map(
|
||||
lambda x: lm_dict[x],
|
||||
["1", "2", "3"]
|
||||
)
|
||||
)
|
||||
)
|
||||
self.set_combobox_text("comboBox_lm", self._lm)
|
||||
|
||||
if self._linear:
|
||||
self.set_radio_button("radioButton_linear", True)
|
||||
else:
|
||||
@@ -86,11 +76,11 @@ class MeshingDialog(PamhyrDialog):
|
||||
def _init_default_values_profiles(self):
|
||||
profiles = self.profiles
|
||||
|
||||
self.combobox_add_items("comboBox_begin_kp", profiles)
|
||||
self.combobox_add_items("comboBox_end_kp", profiles)
|
||||
self.combobox_add_items("comboBox_begin_rk", profiles)
|
||||
self.combobox_add_items("comboBox_end_rk", profiles)
|
||||
|
||||
self.set_combobox_text("comboBox_begin_kp", profiles[0])
|
||||
self.set_combobox_text("comboBox_end_kp", profiles[-1])
|
||||
self.set_combobox_text("comboBox_begin_rk", profiles[0])
|
||||
self.set_combobox_text("comboBox_end_rk", profiles[-1])
|
||||
|
||||
@property
|
||||
def profiles(self):
|
||||
@@ -108,9 +98,9 @@ class MeshingDialog(PamhyrDialog):
|
||||
name = profile.name
|
||||
|
||||
if name == "":
|
||||
name = f"{profile.kp}"
|
||||
name = f"{profile.rk}"
|
||||
else:
|
||||
name += f" ({profile.kp})"
|
||||
name += f" ({profile.rk})"
|
||||
|
||||
return name
|
||||
|
||||
@@ -118,8 +108,8 @@ class MeshingDialog(PamhyrDialog):
|
||||
gl, _ = self._reach.compute_guidelines()
|
||||
gl = list(gl)
|
||||
|
||||
bgl = ['un'] + gl
|
||||
egl = gl + ['np']
|
||||
bgl = ['un'] + gl + ['np']
|
||||
egl = ['un'] + gl + ['np']
|
||||
|
||||
self.combobox_add_items("comboBox_begin_gl", bgl)
|
||||
self.combobox_add_items("comboBox_end_gl", egl)
|
||||
@@ -135,21 +125,17 @@ class MeshingDialog(PamhyrDialog):
|
||||
def lplan(self):
|
||||
return self._lplan
|
||||
|
||||
@property
|
||||
def lm(self):
|
||||
return int(self._lm)
|
||||
|
||||
@property
|
||||
def linear(self):
|
||||
return self._linear
|
||||
|
||||
@property
|
||||
def begin_cs(self):
|
||||
return self._begin_cs + 1
|
||||
return self._begin_cs
|
||||
|
||||
@property
|
||||
def end_cs(self):
|
||||
return self._end_cs + 1
|
||||
return self._end_cs
|
||||
|
||||
@property
|
||||
def begin_dir(self):
|
||||
@@ -165,8 +151,8 @@ class MeshingDialog(PamhyrDialog):
|
||||
)
|
||||
self._linear = self.get_radio_button("radioButton_linear")
|
||||
|
||||
p1 = self.get_combobox_text("comboBox_begin_kp")
|
||||
p2 = self.get_combobox_text("comboBox_end_kp")
|
||||
p1 = self.get_combobox_text("comboBox_begin_rk")
|
||||
p2 = self.get_combobox_text("comboBox_end_rk")
|
||||
|
||||
self._begin_cs = self.profiles.index(p1)
|
||||
self._end_cs = self.profiles.index(p2)
|
||||
@@ -174,9 +160,6 @@ class MeshingDialog(PamhyrDialog):
|
||||
self._begin_dir = self.get_combobox_text("comboBox_begin_gl")
|
||||
self._end_dir = self.get_combobox_text("comboBox_end_gl")
|
||||
|
||||
r_lm_dict = self._trad.get_dict("r_lm_dict")
|
||||
self._lm = r_lm_dict[self.get_combobox_text("comboBox_lm")]
|
||||
|
||||
super().accept()
|
||||
|
||||
def reject(self):
|
||||
|
||||
@@ -98,6 +98,7 @@ class PlotAC(PamhyrPlot):
|
||||
label=self.label_next_plot_selected,
|
||||
color=self.color_plot_next, lw=1.6, linestyle='--'
|
||||
)
|
||||
self.canvas.axes.set_visible(False)
|
||||
|
||||
def draw_gl(self):
|
||||
if self._current_data_update:
|
||||
@@ -106,12 +107,16 @@ class PlotAC(PamhyrPlot):
|
||||
profile_id = 0
|
||||
|
||||
profile = self.data.profile(profile_id)
|
||||
if profile is None:
|
||||
return
|
||||
|
||||
station = profile.get_station()
|
||||
elevation = profile.z()
|
||||
gl = profile.names()
|
||||
|
||||
self.annotation = []
|
||||
self.complete_gl, self.incomplete_gl = self.data.compute_guidelines()
|
||||
|
||||
lcomplete = list(self.complete_gl)
|
||||
lincomplete = list(self.incomplete_gl)
|
||||
|
||||
@@ -129,7 +134,7 @@ class PlotAC(PamhyrPlot):
|
||||
|
||||
if txt.strip() in self.complete_gl:
|
||||
color = self.color_complete_gl[
|
||||
lcomplete.index(txt)
|
||||
lcomplete.index(txt) % len(self.color_complete_gl)
|
||||
]
|
||||
else:
|
||||
color = self.color_incomplete_gl[
|
||||
@@ -160,6 +165,9 @@ class PlotAC(PamhyrPlot):
|
||||
self.draw()
|
||||
return
|
||||
|
||||
if self.data is None:
|
||||
return
|
||||
|
||||
self.update_current()
|
||||
self.update_gl()
|
||||
|
||||
@@ -171,6 +179,10 @@ class PlotAC(PamhyrPlot):
|
||||
previous_id = profile_id - 1
|
||||
next_id = profile_id + 1
|
||||
|
||||
self.previous_plot_selected.set_visible(False)
|
||||
self.plot_selected.set_visible(False)
|
||||
self.next_plot_selected.set_visible(False)
|
||||
|
||||
self.previous_plot_selected.set_data([], [])
|
||||
self.plot_selected.set_data([], [])
|
||||
self.next_plot_selected.set_data([], [])
|
||||
@@ -180,18 +192,23 @@ class PlotAC(PamhyrPlot):
|
||||
self.data.profile(previous_id).get_station(),
|
||||
self.data.profile(previous_id).z()
|
||||
)
|
||||
self.previous_plot_selected.set_visible(True)
|
||||
|
||||
if 0 <= profile_id < self.data.number_profiles:
|
||||
self.plot_selected.set_data(
|
||||
self.data.profile(profile_id).get_station(),
|
||||
self.data.profile(profile_id).z()
|
||||
)
|
||||
self.plot_selected.set_visible(True)
|
||||
|
||||
if 0 <= next_id < self.data.number_profiles:
|
||||
self.next_plot_selected.set_data(
|
||||
self.data.profile(next_id).get_station(),
|
||||
self.data.profile(next_id).z()
|
||||
)
|
||||
self.next_plot_selected.set_visible(True)
|
||||
|
||||
self.canvas.axes.set_visible(True)
|
||||
|
||||
def update_full(self):
|
||||
self.draw()
|
||||
|
||||
@@ -1,269 +0,0 @@
|
||||
# PlotKPC.py -- Pamhyr
|
||||
# Copyright (C) 2023-2024 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 View.Tools.PamhyrPlot import PamhyrPlot
|
||||
|
||||
logger = logging.getLogger()
|
||||
|
||||
|
||||
class PlotKPZ(PamhyrPlot):
|
||||
def __init__(self, canvas=None, trad=None, data=None, toolbar=None,
|
||||
parent=None):
|
||||
super(PlotKPZ, self).__init__(
|
||||
canvas=canvas,
|
||||
trad=trad,
|
||||
data=data,
|
||||
toolbar=toolbar,
|
||||
parent=parent
|
||||
)
|
||||
|
||||
self._isometric_axis = False
|
||||
|
||||
self.line_kp_zgl = []
|
||||
self.line_kp_zmin = None
|
||||
self.line_kp_zmin_zmax = None
|
||||
self.line_kp_zmin_zmax_highlight = None
|
||||
|
||||
self.label_x = self._trad["unit_kp"]
|
||||
self.label_y = self._trad["unit_height"]
|
||||
|
||||
self.before_plot_selected = None
|
||||
self.plot_selected = None
|
||||
self.after_plot_selected = None
|
||||
|
||||
@timer
|
||||
def draw(self):
|
||||
self.init_axes()
|
||||
|
||||
if self.data is None:
|
||||
return
|
||||
|
||||
if len(self.data.profiles) == 0:
|
||||
return
|
||||
|
||||
profiles_defined = any(
|
||||
filter(
|
||||
lambda profile: len(profile) > 0,
|
||||
self.data.profiles
|
||||
)
|
||||
)
|
||||
|
||||
if not profiles_defined:
|
||||
self._init = False
|
||||
return
|
||||
|
||||
self.draw_z_line()
|
||||
self.draw_z_line_highlight()
|
||||
self.draw_current()
|
||||
self.draw_gl()
|
||||
self.draw_bottom()
|
||||
|
||||
self.idle()
|
||||
self._init = True
|
||||
|
||||
def draw_z_line(self):
|
||||
kp = self.data.get_kp_complete_profiles()
|
||||
z_min = self.data.get_z_min()
|
||||
z_max = self.data.get_z_max()
|
||||
|
||||
self.line_kp_zmin_zmax = self.canvas.axes.vlines(
|
||||
x=kp, ymin=z_min, ymax=z_max,
|
||||
color=self.color_plot,
|
||||
lw=1.
|
||||
)
|
||||
|
||||
def draw_z_line_highlight(self):
|
||||
if self._highlight_data is not None:
|
||||
kp = self.data.get_kp_complete_profiles()
|
||||
z_min = self.data.get_z_min()
|
||||
z_max = self.data.get_z_max()
|
||||
|
||||
kp_min, kp_max = self._highlight_data
|
||||
|
||||
indexes = list(
|
||||
map(
|
||||
lambda x: x[0],
|
||||
filter(
|
||||
lambda x: kp_min <= x[1] <= kp_max,
|
||||
enumerate(kp)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
def indexes_filter(data): return list(
|
||||
map(
|
||||
lambda x: x[1],
|
||||
filter(
|
||||
lambda x: x[0] in indexes,
|
||||
enumerate(data)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
ikp = indexes_filter(kp)
|
||||
imin = indexes_filter(z_min)
|
||||
imax = indexes_filter(z_max)
|
||||
|
||||
self.line_kp_zmin_zmax_highlight = self.canvas.axes.vlines(
|
||||
x=ikp,
|
||||
ymin=imin, ymax=imax,
|
||||
color=self.color_plot_highlight,
|
||||
lw=1.
|
||||
)
|
||||
|
||||
def draw_current(self):
|
||||
kp = self.data.get_kp_complete_profiles()
|
||||
z_min = self.data.get_z_min()
|
||||
z_max = self.data.get_z_max()
|
||||
|
||||
self.plot_selected, = self.canvas.axes.plot(
|
||||
(kp[0], kp[0]),
|
||||
(z_min[0], z_max[0]),
|
||||
color=self.color_plot_current, lw=1.5
|
||||
)
|
||||
self.plot_selected.set_visible(False)
|
||||
|
||||
self.before_plot_selected, = self.canvas.axes.plot(
|
||||
(kp[0], kp[0]),
|
||||
(z_min[0], z_max[0]),
|
||||
color=self.color_plot_previous, lw=1.5, linestyle='--'
|
||||
)
|
||||
self.before_plot_selected.set_visible(False)
|
||||
|
||||
self.after_plot_selected, = self.canvas.axes.plot(
|
||||
(kp[0], kp[0]),
|
||||
(z_min[0], z_max[0]),
|
||||
color=self.color_plot_next, lw=1.5, linestyle='--'
|
||||
)
|
||||
self.after_plot_selected.set_visible(False)
|
||||
|
||||
def draw_gl(self):
|
||||
kp = self.data.get_kp_complete_profiles()
|
||||
|
||||
ind = 0
|
||||
self.line_kp_zgl = []
|
||||
for z in self.data.get_guidelines_z():
|
||||
# Is incomplete guideline?
|
||||
if len(z) != len(kp):
|
||||
continue
|
||||
|
||||
self.line_kp_zgl.append(
|
||||
self.canvas.axes.plot(
|
||||
kp, z, lw=1., color=self.colors[ind]
|
||||
)
|
||||
)
|
||||
ind += 1
|
||||
|
||||
def draw_bottom(self):
|
||||
kp = self.data.get_kp_complete_profiles()
|
||||
z_min = self.data.get_z_min()
|
||||
|
||||
self.line_kp_zmin, = self.canvas.axes.plot(
|
||||
kp, z_min,
|
||||
linestyle=":", lw=1.5,
|
||||
color='lightgrey'
|
||||
)
|
||||
|
||||
@timer
|
||||
def update(self):
|
||||
if not self._init:
|
||||
self.draw()
|
||||
return
|
||||
|
||||
self.update_gl()
|
||||
self.update_current()
|
||||
|
||||
self.update_idle()
|
||||
|
||||
def update_current(self):
|
||||
if self._current_data_update:
|
||||
ind = self._current_data
|
||||
before = ind - 1
|
||||
after = ind + 1
|
||||
|
||||
self.before_plot_selected.set_visible(False)
|
||||
self.plot_selected.set_visible(False)
|
||||
self.after_plot_selected.set_visible(False)
|
||||
|
||||
if 0 <= before < self.data.number_profiles:
|
||||
profile = self.data.profile(before)
|
||||
if len(profile) > 0:
|
||||
kp_i = profile.kp
|
||||
z_min_i = profile.z_min()
|
||||
z_max_i = profile.z_max()
|
||||
|
||||
self.before_plot_selected.set_data(
|
||||
(kp_i, kp_i),
|
||||
(z_min_i, z_max_i)
|
||||
)
|
||||
self.before_plot_selected.set_visible(True)
|
||||
|
||||
if 0 <= ind < self.data.number_profiles:
|
||||
profile = self.data.profile(ind)
|
||||
if len(profile) > 0:
|
||||
kp_i = profile.kp
|
||||
z_min_i = profile.z_min()
|
||||
z_max_i = profile.z_max()
|
||||
|
||||
self.plot_selected.set_data(
|
||||
(kp_i, kp_i),
|
||||
(z_min_i, z_max_i)
|
||||
)
|
||||
self.plot_selected.set_visible(True)
|
||||
|
||||
if 0 <= after < self.data.number_profiles:
|
||||
profile = self.data.profile(after)
|
||||
if len(profile) > 0:
|
||||
kp_i = profile.kp
|
||||
z_min_i = profile.z_min()
|
||||
z_max_i = profile.z_max()
|
||||
|
||||
self.after_plot_selected.set_data(
|
||||
(kp_i, kp_i),
|
||||
(z_min_i, z_max_i)
|
||||
)
|
||||
self.after_plot_selected.set_visible(True)
|
||||
|
||||
def update_gl(self):
|
||||
if self._current_data_update:
|
||||
return
|
||||
|
||||
kp = self.data.get_kp_complete_profiles()
|
||||
z_min = self.data.get_z_min()
|
||||
z_max = self.data.get_z_max()
|
||||
|
||||
self.line_kp_zmin.set_data(kp, z_min)
|
||||
|
||||
self.line_kp_zmin_zmax.remove()
|
||||
self.line_kp_zmin_zmax = self.canvas.axes.vlines(
|
||||
x=kp,
|
||||
ymin=z_min, ymax=z_max,
|
||||
color='r', lw=1.
|
||||
)
|
||||
|
||||
z_complete = self.data.get_guidelines_z()
|
||||
try:
|
||||
for i in range(len(self.line_kp_zgl)):
|
||||
self.line_kp_zgl[i][0].set_data(
|
||||
kp, z_complete[i]
|
||||
)
|
||||
except Exception as e:
|
||||
logger.warning(f"Failed to update graphic KPZ: {e}")
|
||||
@@ -0,0 +1,372 @@
|
||||
# PlotRKC.py -- Pamhyr
|
||||
# Copyright (C) 2023-2024 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 View.Tools.PamhyrPlot import PamhyrPlot
|
||||
from PyQt5.QtWidgets import QApplication
|
||||
from PyQt5.QtCore import (
|
||||
Qt, QItemSelectionModel,
|
||||
QItemSelection, QItemSelectionRange,
|
||||
)
|
||||
|
||||
logger = logging.getLogger()
|
||||
|
||||
|
||||
class PlotRKZ(PamhyrPlot):
|
||||
def __init__(self, canvas=None, trad=None,
|
||||
study=None, data=None, toolbar=None,
|
||||
table=None, parent=None):
|
||||
self._study = study
|
||||
|
||||
super(PlotRKZ, self).__init__(
|
||||
canvas=canvas,
|
||||
trad=trad,
|
||||
data=data,
|
||||
toolbar=toolbar,
|
||||
table=table,
|
||||
parent=parent
|
||||
)
|
||||
|
||||
self._isometric_axis = False
|
||||
|
||||
self.line_rk_zgl = []
|
||||
self.line_rk_zmin = None
|
||||
self.line_rk_zmin_zmax = None
|
||||
self.line_rk_zmin_zmax_highlight = None
|
||||
|
||||
self.label_x = self._trad["unit_rk"]
|
||||
self.label_y = self._trad["unit_height"]
|
||||
|
||||
self.before_plot_selected = None
|
||||
self.plot_selected = None
|
||||
self.after_plot_selected = None
|
||||
self.parent = parent
|
||||
self._table = table
|
||||
self._colors = []
|
||||
|
||||
def onpick(self, event):
|
||||
if event.mouseevent.inaxes != self.canvas.axes:
|
||||
return
|
||||
if event.mouseevent.button.value != 1:
|
||||
return
|
||||
|
||||
modifiers = QApplication.keyboardModifiers()
|
||||
if modifiers not in [Qt.ControlModifier,
|
||||
Qt.NoModifier,
|
||||
Qt.ShiftModifier]:
|
||||
return
|
||||
|
||||
ind, point = self._closest_rk(event)
|
||||
if self.parent._table is None:
|
||||
return
|
||||
self.parent._table.blockSignals(True)
|
||||
if modifiers == Qt.ControlModifier:
|
||||
rows = list(
|
||||
set(
|
||||
(i.row() for i in self.parent.tableView.selectedIndexes())
|
||||
)
|
||||
)
|
||||
if ind in rows:
|
||||
rows.remove(ind)
|
||||
else:
|
||||
rows.append(ind)
|
||||
self._select_in_table(rows)
|
||||
elif modifiers == Qt.ShiftModifier:
|
||||
rows = sorted(list(
|
||||
set(
|
||||
(i.row() for i in self.parent.tableView.selectedIndexes())
|
||||
)
|
||||
))
|
||||
if len(rows) > 0:
|
||||
i1 = min(rows[0], rows[-1], ind)
|
||||
i2 = max(rows[0], rows[-1], ind)
|
||||
else:
|
||||
i1 = ind
|
||||
i2 = ind
|
||||
self._select_range_in_table(i1, i2)
|
||||
else:
|
||||
self._select_in_table([ind])
|
||||
self.parent._table.blockSignals(False)
|
||||
|
||||
return
|
||||
|
||||
def _closest_rk(self, event):
|
||||
|
||||
s = event.artist.get_segments()
|
||||
x = [i[0, 0] for i in s]
|
||||
mx = event.mouseevent.xdata
|
||||
points = enumerate(x)
|
||||
|
||||
def dist_mouse(point):
|
||||
x = point[1]
|
||||
d = abs(mx - x)
|
||||
return d
|
||||
|
||||
closest = min(
|
||||
points, key=dist_mouse
|
||||
)
|
||||
|
||||
return closest
|
||||
|
||||
def _select_in_table(self, ind):
|
||||
if self._table is None:
|
||||
return
|
||||
|
||||
self._table.setFocus()
|
||||
selection = self._table.selectionModel()
|
||||
index = QItemSelection()
|
||||
|
||||
if len(ind) == 0:
|
||||
return
|
||||
|
||||
for i in ind:
|
||||
index.append(QItemSelectionRange(self._table.model().index(i, 0)))
|
||||
|
||||
selection.select(
|
||||
index,
|
||||
QItemSelectionModel.Rows |
|
||||
QItemSelectionModel.ClearAndSelect |
|
||||
QItemSelectionModel.Select
|
||||
)
|
||||
|
||||
if len(ind) > 0:
|
||||
self._table.scrollTo(self._table.model().index(ind[-1], 0))
|
||||
|
||||
def _select_range_in_table(self, ind1, ind2):
|
||||
if self._table is not None:
|
||||
self._table.setFocus()
|
||||
selection = self._table.selectionModel()
|
||||
index = QItemSelection(self._table.model().index(ind1, 0),
|
||||
self._table.model().index(ind2, 0))
|
||||
selection.select(
|
||||
index,
|
||||
QItemSelectionModel.Rows |
|
||||
QItemSelectionModel.ClearAndSelect |
|
||||
QItemSelectionModel.Select
|
||||
)
|
||||
self._table.scrollTo(self._table.model().index(ind2, 0))
|
||||
|
||||
@timer
|
||||
def draw(self):
|
||||
self.init_axes()
|
||||
|
||||
if self.data is None:
|
||||
return
|
||||
|
||||
if len(self.data.profiles) == 0:
|
||||
return
|
||||
|
||||
profiles_defined = any(
|
||||
filter(
|
||||
lambda profile: len(profile) > 0,
|
||||
self.data.profiles
|
||||
)
|
||||
)
|
||||
|
||||
if not profiles_defined:
|
||||
self._init = False
|
||||
return
|
||||
|
||||
self.draw_z_line()
|
||||
self.draw_lr()
|
||||
self.draw_gl()
|
||||
self.draw_bottom()
|
||||
self.draw_profiles_hs(self._data)
|
||||
|
||||
self.idle()
|
||||
self._init = True
|
||||
|
||||
def draw_z_line(self):
|
||||
rk = self.data.get_rk_complete_profiles()
|
||||
z_min = self.data.get_z_min()
|
||||
z_max = self.data.get_z_max()
|
||||
|
||||
self._colors, self._style = self.color_hightlight()
|
||||
|
||||
self.line_rk_zmin_zmax = self.canvas.axes.vlines(
|
||||
x=rk, ymin=z_min, ymax=z_max,
|
||||
color=self._colors,
|
||||
linestyle=self._style,
|
||||
lw=1.,
|
||||
picker=10,
|
||||
)
|
||||
|
||||
def color_hightlight(self):
|
||||
rows = sorted(list(
|
||||
set(
|
||||
(i.row() for i in self.parent.tableView.selectedIndexes())
|
||||
)
|
||||
))
|
||||
colors = [self.color_plot for row in range(len(self._data))]
|
||||
style = ["-" for row in range(len(self._data))]
|
||||
if len(rows) > 0:
|
||||
for row in rows:
|
||||
colors[row] = self.color_plot_current
|
||||
if rows[0] > 0:
|
||||
colors[rows[0]-1] = self.color_plot_previous
|
||||
style[rows[0]-1] = "--"
|
||||
if rows[-1] < len(self._data)-1:
|
||||
colors[rows[-1]+1] = self.color_plot_next
|
||||
style[rows[-1]+1] = "--"
|
||||
return colors, style
|
||||
|
||||
def draw_lr(self):
|
||||
rk = self.data.get_rk_complete_profiles()
|
||||
lz = []
|
||||
rz = []
|
||||
|
||||
self.line_lr = []
|
||||
for z in self.data.get_z():
|
||||
lz.append(z[0])
|
||||
rz.append(z[-1])
|
||||
|
||||
line = self.canvas.axes.plot(
|
||||
rk, lz,
|
||||
color=self.color_plot_river_bottom,
|
||||
linestyle="dotted",
|
||||
lw=1.,
|
||||
)
|
||||
self.line_lr.append(line)
|
||||
|
||||
line = self.canvas.axes.plot(
|
||||
rk, rz,
|
||||
color=self.color_plot_river_bottom,
|
||||
linestyle="dotted",
|
||||
lw=1.,
|
||||
)
|
||||
self.line_lr.append(line)
|
||||
|
||||
def draw_gl(self):
|
||||
rk = self.data.get_rk_complete_profiles()
|
||||
|
||||
ind = 0
|
||||
self.line_rk_zgl = []
|
||||
for z in self.data.get_guidelines_z():
|
||||
# Is incomplete guideline?
|
||||
if len(z) != len(rk):
|
||||
continue
|
||||
|
||||
self.line_rk_zgl.append(
|
||||
self.canvas.axes.plot(
|
||||
rk, z, lw=1.,
|
||||
color=self.colors[ind % len(self.colors)],
|
||||
linestyle=self.linestyle[ind // len(self.colors)]
|
||||
)
|
||||
)
|
||||
ind += 1
|
||||
|
||||
def draw_bottom(self):
|
||||
rk = self.data.get_rk_complete_profiles()
|
||||
z_min = self.data.get_z_min()
|
||||
|
||||
self.line_rk_zmin, = self.canvas.axes.plot(
|
||||
rk, z_min,
|
||||
linestyle=":", lw=1.5,
|
||||
color='lightgrey'
|
||||
)
|
||||
|
||||
def draw_profiles_hs(self, reach):
|
||||
lhs = filter(
|
||||
lambda hs: hs._input_reach.reach is reach,
|
||||
filter(
|
||||
lambda hs: hs._input_reach is not None,
|
||||
self._study.river.hydraulic_structures.lst
|
||||
)
|
||||
)
|
||||
|
||||
for hs in lhs:
|
||||
x = hs.input_rk
|
||||
if x is not None:
|
||||
z_min = reach.get_z_min()
|
||||
z_max = reach.get_z_max()
|
||||
|
||||
self.canvas.axes.plot(
|
||||
[x, x],
|
||||
[min(z_min), max(z_max)],
|
||||
linestyle="--",
|
||||
lw=1.,
|
||||
color=self.color_plot_previous,
|
||||
)
|
||||
|
||||
self.canvas.axes.annotate(
|
||||
" > " + hs.name,
|
||||
(x, max(z_max)),
|
||||
horizontalalignment='left',
|
||||
verticalalignment='top',
|
||||
annotation_clip=True,
|
||||
fontsize=9, color=self.color_plot_previous,
|
||||
)
|
||||
|
||||
@timer
|
||||
def update(self):
|
||||
if not self._init:
|
||||
self.draw()
|
||||
return
|
||||
|
||||
self.update_lr()
|
||||
self.update_gl()
|
||||
self.update_current()
|
||||
|
||||
self.update_idle()
|
||||
|
||||
def update_current(self):
|
||||
if self._current_data_update:
|
||||
self._colors, self._style = self.color_hightlight()
|
||||
self.line_rk_zmin_zmax.set_colors(self._colors)
|
||||
self.line_rk_zmin_zmax.set_linestyle(self._style)
|
||||
|
||||
def update_gl(self):
|
||||
if self._current_data_update:
|
||||
return
|
||||
|
||||
rk = self.data.get_rk_complete_profiles()
|
||||
z_min = self.data.get_z_min()
|
||||
z_max = self.data.get_z_max()
|
||||
|
||||
self.line_rk_zmin.set_data(rk, z_min)
|
||||
|
||||
# TODO comprendre à quoi sert ce bout de code
|
||||
# ========>
|
||||
# self.line_rk_zmin_zmax.remove()
|
||||
# self._colors, self._style = self.color_hightlight()
|
||||
# self.line_rk_zmin_zmax = self.canvas.axes.vlines(
|
||||
# x=rk,
|
||||
# ymin=z_min,
|
||||
# ymax=z_max,
|
||||
# color=self._colors,
|
||||
# linestyle = self._style,
|
||||
# lw=1.
|
||||
# )
|
||||
# <========
|
||||
|
||||
z_complete = self.data.get_guidelines_z()
|
||||
try:
|
||||
for i in range(len(self.line_rk_zgl)):
|
||||
self.line_rk_zgl[i][0].set_data(
|
||||
rk, z_complete[i]
|
||||
)
|
||||
except Exception as e:
|
||||
logger.warning(f"Failed to update graphic RKZ: {e}")
|
||||
|
||||
def update_lr(self):
|
||||
for line in self.line_lr:
|
||||
line[0].remove()
|
||||
|
||||
self.draw_lr()
|
||||
+206
-89
@@ -18,22 +18,27 @@
|
||||
|
||||
from tools import timer, trace
|
||||
from View.Tools.PamhyrPlot import PamhyrPlot
|
||||
from matplotlib import collections
|
||||
import numpy as np
|
||||
|
||||
from PyQt5.QtCore import (
|
||||
QCoreApplication
|
||||
QCoreApplication, Qt, QItemSelectionModel,
|
||||
QItemSelection, QItemSelectionRange,
|
||||
)
|
||||
from PyQt5.QtWidgets import QApplication
|
||||
|
||||
_translate = QCoreApplication.translate
|
||||
|
||||
|
||||
class PlotXY(PamhyrPlot):
|
||||
def __init__(self, canvas=None, trad=None, data=None, toolbar=None,
|
||||
parent=None):
|
||||
table=None, parent=None):
|
||||
super(PlotXY, self).__init__(
|
||||
canvas=canvas,
|
||||
trad=trad,
|
||||
data=data,
|
||||
toolbar=toolbar,
|
||||
table=table,
|
||||
parent=parent
|
||||
)
|
||||
|
||||
@@ -46,6 +51,116 @@ class PlotXY(PamhyrPlot):
|
||||
self.before_plot_selected = None
|
||||
self.plot_selected = None
|
||||
self.after_plot_selected = None
|
||||
self.parent = parent
|
||||
self.line_xy_collection = None
|
||||
self._table = table
|
||||
self._colors = []
|
||||
self._style = []
|
||||
|
||||
def onpick(self, event):
|
||||
if event.mouseevent.inaxes != self.canvas.axes:
|
||||
return
|
||||
if event.mouseevent.button.value != 1:
|
||||
return
|
||||
|
||||
modifiers = QApplication.keyboardModifiers()
|
||||
if modifiers not in [Qt.ControlModifier,
|
||||
Qt.NoModifier,
|
||||
Qt.ShiftModifier]:
|
||||
return
|
||||
|
||||
ind, point = self._closest_section(event)
|
||||
if self._table is None:
|
||||
return
|
||||
self._table.blockSignals(True)
|
||||
if modifiers == Qt.ControlModifier:
|
||||
rows = list(
|
||||
set(
|
||||
(i.row() for i in self.parent.tableView.selectedIndexes())
|
||||
)
|
||||
)
|
||||
if ind in rows:
|
||||
rows.remove(ind)
|
||||
self._select_in_table(rows)
|
||||
else:
|
||||
self._select_in_table(rows + [ind])
|
||||
elif modifiers == Qt.ShiftModifier:
|
||||
rows = list(
|
||||
set(
|
||||
(i.row() for i in self.parent.tableView.selectedIndexes())
|
||||
)
|
||||
)
|
||||
if len(rows) > 0:
|
||||
i1 = min(rows[0], rows[-1], ind)
|
||||
i2 = max(rows[0], rows[-1], ind)
|
||||
else:
|
||||
i1 = ind
|
||||
i2 = ind
|
||||
self._select_range_in_table(i1, i2)
|
||||
else:
|
||||
self._select_in_table([ind])
|
||||
self._table.blockSignals(False)
|
||||
|
||||
return
|
||||
|
||||
def _closest_section(self, event):
|
||||
axes = self.canvas.axes
|
||||
mx = event.mouseevent.xdata
|
||||
my = event.mouseevent.ydata
|
||||
bx, by = axes.get_xlim(), axes.get_ylim()
|
||||
ratio = (bx[0] - bx[1]) / (by[0] - by[1])
|
||||
|
||||
segments = event.artist.get_segments()
|
||||
ind = event.ind
|
||||
|
||||
points = []
|
||||
for i in ind:
|
||||
points = points + [[i, j] for j in segments[i]]
|
||||
|
||||
def dist_mouse(point):
|
||||
x, y = point[1]
|
||||
d2 = (((mx - x) / ratio) ** 2) + ((my - y) ** 2)
|
||||
return d2
|
||||
|
||||
closest = min(
|
||||
points, key=dist_mouse
|
||||
)
|
||||
|
||||
return closest
|
||||
|
||||
def _select_in_table(self, ind):
|
||||
if self._table is None:
|
||||
return
|
||||
self._table.setFocus()
|
||||
selection = self._table.selectionModel()
|
||||
index = QItemSelection()
|
||||
if len(ind) == 0:
|
||||
return
|
||||
for i in ind:
|
||||
index.append(QItemSelectionRange(self._table.model().index(i, 0)))
|
||||
selection.select(
|
||||
index,
|
||||
QItemSelectionModel.Rows |
|
||||
QItemSelectionModel.ClearAndSelect |
|
||||
QItemSelectionModel.Select
|
||||
)
|
||||
|
||||
if len(ind) > 0:
|
||||
self._table.scrollTo(self._table.model().index(ind[-1], 0))
|
||||
|
||||
def _select_range_in_table(self, ind1, ind2):
|
||||
if self._table is not None:
|
||||
self._table.setFocus()
|
||||
selection = self._table.selectionModel()
|
||||
index = QItemSelection(self._table.model().index(ind1, 0),
|
||||
self._table.model().index(ind2, 0))
|
||||
selection.select(
|
||||
index,
|
||||
QItemSelectionModel.Rows |
|
||||
QItemSelectionModel.ClearAndSelect |
|
||||
QItemSelectionModel.Select
|
||||
)
|
||||
self._table.scrollTo(self._table.model().index(ind2, 0))
|
||||
|
||||
@timer
|
||||
def draw(self):
|
||||
@@ -61,34 +176,75 @@ class PlotXY(PamhyrPlot):
|
||||
return
|
||||
|
||||
self.draw_xy()
|
||||
self.draw_lr()
|
||||
self.draw_gl()
|
||||
self.draw_current()
|
||||
|
||||
self.idle()
|
||||
self._init = True
|
||||
|
||||
def draw_xy(self):
|
||||
kp = self.data.get_kp_complete_profiles()
|
||||
|
||||
kp_min, kp_max = (-1, -1)
|
||||
if self._highlight_data is not None:
|
||||
kp_min, kp_max = self._highlight_data
|
||||
|
||||
def color_hightlight(kp):
|
||||
if kp_min <= kp <= kp_max:
|
||||
return self.color_plot_highlight
|
||||
return self.color_plot
|
||||
|
||||
self.line_xy = []
|
||||
for x, y, kp in zip(self.data.get_x(),
|
||||
self.data.get_y(),
|
||||
kp):
|
||||
line = self.canvas.axes.plot(
|
||||
x, y,
|
||||
color=color_hightlight(kp),
|
||||
**self.plot_default_kargs
|
||||
for xy in zip(self.data.get_x(), self.data.get_y()):
|
||||
self.line_xy.append(np.column_stack(xy))
|
||||
|
||||
self._colors, self._style = self.color_hightlight()
|
||||
self.line_xy_collection = collections.LineCollection(
|
||||
self.line_xy,
|
||||
colors=self._colors,
|
||||
linestyle=self._style,
|
||||
picker=10
|
||||
)
|
||||
self.line_xy.append(line)
|
||||
self.canvas.axes.add_collection(self.line_xy_collection)
|
||||
|
||||
def color_hightlight(self):
|
||||
rows = sorted(list(
|
||||
set(
|
||||
(i.row() for i in self.parent.tableView.selectedIndexes())
|
||||
)
|
||||
))
|
||||
colors = [self.color_plot for row in range(len(self._data))]
|
||||
style = ["-" for row in range(len(self._data))]
|
||||
if len(rows) > 0:
|
||||
for row in rows:
|
||||
colors[row] = self.color_plot_current
|
||||
if rows[0] > 0:
|
||||
colors[rows[0]-1] = self.color_plot_previous
|
||||
style[rows[0]-1] = "--"
|
||||
if rows[-1] < len(self._data)-1:
|
||||
colors[rows[-1]+1] = self.color_plot_next
|
||||
style[rows[-1]+1] = "--"
|
||||
return colors, style
|
||||
|
||||
def draw_lr(self):
|
||||
lx = []
|
||||
ly = []
|
||||
rx = []
|
||||
ry = []
|
||||
|
||||
self.line_lr = []
|
||||
for x, y in zip(self.data.get_x(),
|
||||
self.data.get_y()):
|
||||
lx.append(x[0])
|
||||
ly.append(y[0])
|
||||
|
||||
rx.append(x[-1])
|
||||
ry.append(y[-1])
|
||||
|
||||
line = self.canvas.axes.plot(
|
||||
lx, ly,
|
||||
color=self.color_plot_river_bottom,
|
||||
linestyle="dotted",
|
||||
lw=1.,
|
||||
)
|
||||
self.line_lr.append(line)
|
||||
|
||||
line = self.canvas.axes.plot(
|
||||
rx, ry,
|
||||
color=self.color_plot_river_bottom,
|
||||
linestyle="dotted",
|
||||
lw=1.,
|
||||
)
|
||||
self.line_lr.append(line)
|
||||
|
||||
def draw_gl(self):
|
||||
x_complete = self.data.get_guidelines_x()
|
||||
@@ -98,38 +254,12 @@ class PlotXY(PamhyrPlot):
|
||||
self.line_gl = []
|
||||
for x, y in zip(x_complete, y_complete):
|
||||
line = self.canvas.axes.plot(
|
||||
x, y, color=self.colors[ind]
|
||||
x, y, color=self.colors[ind % len(self.colors)],
|
||||
linestyle=self.linestyle[ind // len(self.colors)]
|
||||
)
|
||||
self.line_gl.append(line)
|
||||
ind += 1
|
||||
|
||||
def draw_current(self):
|
||||
# Previous profile
|
||||
self.before_plot_selected, = self.canvas.axes.plot(
|
||||
self.data.profile(0).x(),
|
||||
self.data.profile(0).y(),
|
||||
color=self.color_plot_previous, linestyle="--",
|
||||
**self.plot_default_kargs
|
||||
)
|
||||
self.before_plot_selected.set_visible(False)
|
||||
|
||||
# Current profile
|
||||
self.plot_selected, = self.canvas.axes.plot(
|
||||
self.data.profile(0).x(),
|
||||
self.data.profile(0).y(),
|
||||
color=self.color_plot_current, **self.plot_default_kargs
|
||||
)
|
||||
self.plot_selected.set_visible(False)
|
||||
|
||||
# Next profile
|
||||
self.after_plot_selected, = self.canvas.axes.plot(
|
||||
self.data.profile(0).x(),
|
||||
self.data.profile(0).y(),
|
||||
color=self.color_plot_next, linestyle='--',
|
||||
**self.plot_default_kargs
|
||||
)
|
||||
self.after_plot_selected.set_visible(False)
|
||||
|
||||
@timer
|
||||
def update(self):
|
||||
if not self._init:
|
||||
@@ -139,6 +269,7 @@ class PlotXY(PamhyrPlot):
|
||||
if self.data is None:
|
||||
return
|
||||
|
||||
self.update_lr()
|
||||
self.update_gl()
|
||||
self.update_current()
|
||||
|
||||
@@ -149,21 +280,24 @@ class PlotXY(PamhyrPlot):
|
||||
x_complete = list(self.data.get_guidelines_x())
|
||||
y_complete = list(self.data.get_guidelines_y())
|
||||
|
||||
for i in range(self.data.number_profiles):
|
||||
if i < len(self.line_xy):
|
||||
self.line_xy[i][0].set_data(
|
||||
self.data.profile(i).x(),
|
||||
self.data.profile(i).y()
|
||||
)
|
||||
else:
|
||||
self.line_xy.append(
|
||||
self.canvas.axes.plot(
|
||||
self.data.profile(i).x(),
|
||||
self.data.profile(i).y(),
|
||||
color='r',
|
||||
**self.plot_default_kargs
|
||||
)
|
||||
)
|
||||
# TODO comprendre à quoi sert ce bout de code
|
||||
# ========>
|
||||
# for i in range(self.data.number_profiles):
|
||||
# if i < len(self.line_xy):
|
||||
# self.line_xy[i][0].set_data(
|
||||
# self.data.profile(i).x(),
|
||||
# self.data.profile(i).y()
|
||||
# )
|
||||
# else:
|
||||
# self.line_xy.append(
|
||||
# self.canvas.axes.plot(
|
||||
# self.data.profile(i).x(),
|
||||
# self.data.profile(i).y(),
|
||||
# color='r',
|
||||
# **self.plot_default_kargs
|
||||
# )
|
||||
# )
|
||||
# <========
|
||||
|
||||
for i in range(len(x_complete)):
|
||||
if i < len(self.line_gl):
|
||||
@@ -181,29 +315,12 @@ class PlotXY(PamhyrPlot):
|
||||
|
||||
def update_current(self):
|
||||
if self._current_data_update:
|
||||
ind = self._current_data
|
||||
before = ind - 1
|
||||
after = ind + 1
|
||||
self._colors, self._style = self.color_hightlight()
|
||||
self.line_xy_collection.set_colors(self._colors)
|
||||
self.line_xy_collection.set_linestyle(self._style)
|
||||
|
||||
self.before_plot_selected.set_visible(False)
|
||||
self.plot_selected.set_visible(False)
|
||||
self.after_plot_selected.set_visible(False)
|
||||
def update_lr(self):
|
||||
for line in self.line_lr:
|
||||
line[0].remove()
|
||||
|
||||
if 0 <= before < self.data.number_profiles:
|
||||
self.before_plot_selected.set_data(
|
||||
self.data.profile(before).x(),
|
||||
self.data.profile(before).y()
|
||||
)
|
||||
self.before_plot_selected.set_visible(True)
|
||||
|
||||
if 0 <= ind < self.data.number_profiles:
|
||||
self.plot_selected.set_data(self.data.profile(ind).x(),
|
||||
self.data.profile(ind).y())
|
||||
self.plot_selected.set_visible(True)
|
||||
|
||||
if 0 <= after < self.data.number_profiles:
|
||||
self.after_plot_selected.set_data(
|
||||
self.data.profile(after).x(),
|
||||
self.data.profile(after).y()
|
||||
)
|
||||
self.after_plot_selected.set_visible(True)
|
||||
self.draw_lr()
|
||||
@@ -18,14 +18,19 @@
|
||||
|
||||
import logging
|
||||
|
||||
from math import dist, sqrt
|
||||
|
||||
from tools import timer, trace
|
||||
from View.Tools.PamhyrPlot import PamhyrPlot
|
||||
from View.Tools.Plot.OnPickEvent import OnpickEvent
|
||||
|
||||
from PyQt5.QtCore import (
|
||||
QCoreApplication
|
||||
Qt, QCoreApplication, QItemSelectionModel,
|
||||
QItemSelection, QItemSelectionRange,
|
||||
)
|
||||
|
||||
from PyQt5.QtWidgets import QApplication
|
||||
from matplotlib.widgets import RectangleSelector
|
||||
|
||||
_translate = QCoreApplication.translate
|
||||
|
||||
logger = logging.getLogger()
|
||||
@@ -43,11 +48,15 @@ class Plot(PamhyrPlot):
|
||||
)
|
||||
|
||||
self._table = table
|
||||
self._parent = parent
|
||||
self._z_note = None
|
||||
self._z_line = None
|
||||
self._z_fill_between = None
|
||||
|
||||
self.line_xy = []
|
||||
self.line_gl = []
|
||||
|
||||
self.label_x = self._trad["unit_kp"]
|
||||
self.label_x = self._trad["unit_rk"]
|
||||
self.label_y = self._trad["unit_height"]
|
||||
|
||||
self.before_plot_selected = None
|
||||
@@ -56,6 +65,245 @@ class Plot(PamhyrPlot):
|
||||
|
||||
self._isometric_axis = False
|
||||
|
||||
self.hl_points = []
|
||||
self.highlight = (
|
||||
[], # Points list to highlight
|
||||
None # Hydrolic values (z, wet_area,
|
||||
# wet_preimeter, water_width)
|
||||
)
|
||||
self._onpickevent = None
|
||||
self._rect_select = RectangleSelector(
|
||||
ax=self.canvas.axes,
|
||||
onselect=self.rect_select_callback,
|
||||
useblit=True,
|
||||
button=[1], # don't use middle nor right button
|
||||
minspanx=2.0,
|
||||
minspany=2.0,
|
||||
spancoords='pixels',
|
||||
interactive=False
|
||||
)
|
||||
|
||||
def onrelease(self, event):
|
||||
# we need to do that to prevent conflicst
|
||||
# between onpick and rect_select_callback
|
||||
modifiers = QApplication.keyboardModifiers()
|
||||
points, hyd = self.highlight
|
||||
if self._onpickevent is not None:
|
||||
ind, point = self._closest_point(self._onpickevent)
|
||||
if modifiers == Qt.ControlModifier:
|
||||
rows = self._parent.index_selected_rows()
|
||||
if ind in rows:
|
||||
rows.remove(ind)
|
||||
del (points[ind])
|
||||
self.highlight = (points, hyd)
|
||||
self._select_in_table(rows)
|
||||
else:
|
||||
self.highlight = (points+[point], hyd)
|
||||
self._select_in_table(rows+[ind])
|
||||
elif modifiers == Qt.ShiftModifier:
|
||||
rows = self._parent.index_selected_rows()
|
||||
if len(rows) > 0:
|
||||
i1 = min(rows[0], rows[-1], ind)
|
||||
i2 = max(rows[0], rows[-1], ind)
|
||||
p = [
|
||||
[self.data.points[i].x, self.data.points[i].y]
|
||||
for i in range(i1, i2)
|
||||
]
|
||||
else:
|
||||
i1 = ind
|
||||
i2 = ind
|
||||
p = [point]
|
||||
self.highlight = (p, hyd)
|
||||
self._select_range_in_table(i1, i2)
|
||||
else:
|
||||
self.highlight = ([point], hyd)
|
||||
self._select_in_table([ind])
|
||||
|
||||
self._onpickevent = None
|
||||
|
||||
def onpick(self, event):
|
||||
if event.mouseevent.inaxes != self.canvas.axes:
|
||||
return
|
||||
if event.mouseevent.button.value != 1:
|
||||
return
|
||||
|
||||
modifiers = QApplication.keyboardModifiers()
|
||||
if modifiers not in [Qt.ControlModifier,
|
||||
Qt.NoModifier,
|
||||
Qt.ShiftModifier]:
|
||||
return
|
||||
|
||||
self._onpickevent = event
|
||||
return
|
||||
|
||||
def onclick(self, event):
|
||||
if event.inaxes != self.canvas.axes:
|
||||
return
|
||||
if event.button.value == 1:
|
||||
return
|
||||
|
||||
points, _ = self.highlight
|
||||
|
||||
z = self._get_z_from_click(event)
|
||||
if z < self.data.z_min() or event.button.value == 2:
|
||||
self.highlight = (points, None)
|
||||
self.update()
|
||||
return
|
||||
|
||||
a, p, w = self._compute_hydraulics(z)
|
||||
|
||||
logger.debug(f"{z, a, p, w}")
|
||||
|
||||
self.highlight = (points, (z, a, p, w))
|
||||
|
||||
self.update()
|
||||
return
|
||||
|
||||
def select_points_from_indices(self, indices):
|
||||
data = self.data
|
||||
_, hyd = self.highlight
|
||||
|
||||
points = list(
|
||||
map(
|
||||
lambda e: e[1],
|
||||
filter(
|
||||
lambda e: e[0] in indices,
|
||||
enumerate(
|
||||
zip(data.get_station(), data.z())
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
self.highlight = (points, hyd)
|
||||
self.update()
|
||||
|
||||
def _select_in_table(self, ind):
|
||||
if self._table is not None:
|
||||
self._table.blockSignals(True)
|
||||
self._table.setFocus()
|
||||
selection = self._table.selectionModel()
|
||||
index = QItemSelection()
|
||||
if len(ind) > 0:
|
||||
for i in ind:
|
||||
index.append(QItemSelectionRange(
|
||||
self._table.model().index(i, 0))
|
||||
)
|
||||
selection.select(
|
||||
index,
|
||||
QItemSelectionModel.Rows |
|
||||
QItemSelectionModel.ClearAndSelect |
|
||||
QItemSelectionModel.Select
|
||||
)
|
||||
|
||||
if len(ind) > 0:
|
||||
self._table.scrollTo(self._table.model().index(ind[-1], 0))
|
||||
self._table.blockSignals(False)
|
||||
|
||||
def _select_range_in_table(self, ind1, ind2):
|
||||
if self._table is not None:
|
||||
self._table.blockSignals(True)
|
||||
self._table.setFocus()
|
||||
selection = self._table.selectionModel()
|
||||
index = QItemSelection(self._table.model().index(ind1, 0),
|
||||
self._table.model().index(ind2, 0))
|
||||
selection.select(
|
||||
index,
|
||||
QItemSelectionModel.Rows |
|
||||
QItemSelectionModel.ClearAndSelect |
|
||||
QItemSelectionModel.Select
|
||||
)
|
||||
self._table.scrollTo(self._table.model().index(ind2, 0))
|
||||
self._table.blockSignals(False)
|
||||
|
||||
def _closest_point(self, event):
|
||||
points_ind = event.ind
|
||||
axes = self.canvas.axes
|
||||
bx, by = axes.get_xlim(), axes.get_ylim()
|
||||
ratio = (bx[0] - bx[1]) / (by[0] - by[1])
|
||||
|
||||
x = event.artist.get_xdata()
|
||||
y = event.artist.get_ydata()
|
||||
|
||||
# points = filter(
|
||||
# lambda e: e[0] in points_ind,
|
||||
# enumerate(zip(x, y))
|
||||
# )
|
||||
points = enumerate(zip(x, y))
|
||||
|
||||
mx = event.mouseevent.xdata
|
||||
my = event.mouseevent.ydata
|
||||
|
||||
def dist_mouse(point):
|
||||
x, y = point[1]
|
||||
d2 = ((mx - x) / ratio) ** 2 + ((my - y) ** 2)
|
||||
return d2
|
||||
|
||||
closest = min(
|
||||
points, key=dist_mouse
|
||||
)
|
||||
|
||||
return closest
|
||||
|
||||
def _get_z_from_click(self, event):
|
||||
return event.ydata
|
||||
|
||||
def rect_select_callback(self, eclick, erelease):
|
||||
|
||||
points, hyd = self.highlight
|
||||
x1, y1 = eclick.xdata, eclick.ydata
|
||||
x2, y2 = erelease.xdata, erelease.ydata
|
||||
|
||||
if (max(abs(x1-x2), abs(y1-y2)) < 0.001):
|
||||
return
|
||||
modifiers = QApplication.keyboardModifiers()
|
||||
|
||||
x1, y1 = eclick.xdata, eclick.ydata
|
||||
x2, y2 = erelease.xdata, erelease.ydata
|
||||
|
||||
inds, points2 = self._points_in_rectangle(x1, y1, x2, y2)
|
||||
self._onclickevent = None
|
||||
if modifiers == Qt.ControlModifier:
|
||||
rows = self._parent.index_selected_rows()
|
||||
if all(i in rows for i in inds):
|
||||
for ind in sorted(inds, reverse=True):
|
||||
rows.remove(ind)
|
||||
del (points[ind])
|
||||
self.highlight = (points, hyd)
|
||||
self._select_in_table(rows)
|
||||
else:
|
||||
self.highlight = (points+points2, hyd)
|
||||
self._select_in_table(rows+inds)
|
||||
else:
|
||||
self.highlight = (points2, hyd)
|
||||
self._select_in_table(inds)
|
||||
return
|
||||
|
||||
def _points_in_rectangle(self, x1, y1, x2, y2):
|
||||
# TODO: use lambdas
|
||||
listi = []
|
||||
listp = []
|
||||
station = self.data._get_station(self.data.points)
|
||||
for i, p in enumerate(self.data.points):
|
||||
if (min(x1, x2) < station[i] < max(x1, x2)
|
||||
and min(y1, y2) < p.z < max(y1, y2)):
|
||||
listi.append(i)
|
||||
listp.append((station[i], p.z))
|
||||
return listi, listp
|
||||
|
||||
def _compute_hydraulics(self, z):
|
||||
profile = self.data
|
||||
|
||||
points = profile.wet_points(z)
|
||||
station = profile._get_station(points)
|
||||
width = abs(station[0] - station[-1])
|
||||
|
||||
poly = profile.wet_polygon(z)
|
||||
area = poly.area
|
||||
perimeter = poly.length
|
||||
|
||||
return area, perimeter, width
|
||||
|
||||
@timer
|
||||
def draw(self):
|
||||
self.init_axes()
|
||||
@@ -65,19 +313,24 @@ class Plot(PamhyrPlot):
|
||||
x_carto = self.data.x()
|
||||
y_carto = self.data.y()
|
||||
|
||||
if (len(x_carto) < 3 or len(y_carto) < 3 or
|
||||
len(x) < 3):
|
||||
if (len(x_carto) < 3 or len(y_carto) < 3 or len(x) < 3):
|
||||
# Noting to do in this case
|
||||
return
|
||||
|
||||
gl = map(lambda p: p.name, self.data.points)
|
||||
|
||||
self.profile_line2D, = self.canvas.axes.plot(
|
||||
x, y, color=self.color_plot,
|
||||
lw=1.5, markersize=7, marker='+',
|
||||
picker=30
|
||||
picker=10
|
||||
)
|
||||
|
||||
self.draw_annotation(x, y)
|
||||
self.draw_highligth()
|
||||
|
||||
self.idle()
|
||||
|
||||
def draw_annotation(self, x, y):
|
||||
gl = map(lambda p: p.name, self.data.points)
|
||||
|
||||
# Add label on graph
|
||||
self.annotation = []
|
||||
for i, name in enumerate(list(gl)):
|
||||
@@ -113,30 +366,88 @@ class Plot(PamhyrPlot):
|
||||
self.canvas.axes.set_facecolor('#F9F9F9')
|
||||
self.canvas.figure.patch.set_facecolor('white')
|
||||
|
||||
self.onpick_event = OnpickEvent(
|
||||
self.canvas.axes,
|
||||
x, y, x_carto, y_carto,
|
||||
self._table
|
||||
def draw_highligth(self):
|
||||
points, hyd = self.highlight
|
||||
for p in self.hl_points:
|
||||
p[0].set_data([], [])
|
||||
|
||||
self.hl_points = []
|
||||
|
||||
for x, y in points:
|
||||
self.hl_points.append(
|
||||
self.canvas.axes.plot(
|
||||
x, y,
|
||||
color=self.color_plot_highlight,
|
||||
lw=1.5, markersize=7, marker='+',
|
||||
)
|
||||
self.canvas.figure.canvas\
|
||||
.mpl_connect(
|
||||
'pick_event',
|
||||
self.onpick_event.onpick
|
||||
)
|
||||
|
||||
self.onclick_event = OnpickEvent(
|
||||
self.canvas.axes,
|
||||
x, y, x_carto, y_carto,
|
||||
self._table
|
||||
)
|
||||
self.canvas.figure.canvas\
|
||||
.mpl_connect(
|
||||
'button_press_event',
|
||||
self.onclick_event.onclick
|
||||
if hyd is not None:
|
||||
self.draw_highligth_z_line(*hyd)
|
||||
else:
|
||||
if self._z_note is not None:
|
||||
self._z_note.set_visible(False)
|
||||
self._z_line[0].set_visible(False)
|
||||
self._z_fill_between.set_visible(False)
|
||||
|
||||
def draw_highligth_z_line(self, z, a, p, w):
|
||||
text = (
|
||||
f"Z = {z:.3f} m, " +
|
||||
f"{self._trad['width']} = {w:.3f} m,\n" +
|
||||
f"{self._trad['area']} = {a:.3f} m², " +
|
||||
f"{self._trad['perimeter']} = {p:.3f} m"
|
||||
)
|
||||
|
||||
self.idle()
|
||||
x = self.data.get_station()
|
||||
xlim = (x[0], x[-1])
|
||||
pos = (
|
||||
xlim[0] + (abs(xlim[0] - xlim[1]) * 0.05),
|
||||
z + 0.8
|
||||
)
|
||||
y = self.data.z()
|
||||
|
||||
if self._z_note is None:
|
||||
self.draw_highligth_z_line_fill(x, y, z)
|
||||
|
||||
self._z_line = self.canvas.axes.plot(
|
||||
xlim, [z, z],
|
||||
color=self.color_plot_river_water
|
||||
)
|
||||
self._z_line[0].set_visible(True)
|
||||
|
||||
self._z_note = self.canvas.axes.annotate(
|
||||
text, pos,
|
||||
horizontalalignment='left',
|
||||
verticalalignment='top',
|
||||
annotation_clip=True,
|
||||
color=self.color_plot_river_water,
|
||||
fontsize=9,
|
||||
fontweight='bold',
|
||||
alpha=0.7
|
||||
)
|
||||
self._z_note.set_visible(True)
|
||||
else:
|
||||
self.draw_highligth_z_line_fill(x, y, z)
|
||||
|
||||
self._z_line[0].set_data(xlim, [z, z])
|
||||
self._z_note.set_position(pos)
|
||||
self._z_note.set_text(text)
|
||||
self._z_line[0].set_visible(True)
|
||||
self._z_note.set_visible(True)
|
||||
|
||||
def draw_highligth_z_line_fill(self, x, y, z):
|
||||
if self._z_fill_between is not None:
|
||||
self._z_fill_between.remove()
|
||||
|
||||
self._z_fill_between = self.canvas.axes.fill_between(
|
||||
x, y, z,
|
||||
where=y <= z,
|
||||
facecolor=self.color_plot_river_water_zone,
|
||||
interpolate=True, alpha=0.7
|
||||
)
|
||||
|
||||
@timer
|
||||
def update(self):
|
||||
self.draw()
|
||||
self.draw_highligth()
|
||||
|
||||
self.update_idle()
|
||||
@@ -82,16 +82,6 @@ class GeometryProfileTableModel(PamhyrTableModel):
|
||||
elif value == self._data.z_max():
|
||||
return QColor("blue")
|
||||
|
||||
if role == Qt.ToolTipRole:
|
||||
if value == self._data.z_min():
|
||||
return _translate("MainWindowProfile",
|
||||
"La cote du fond",
|
||||
"Z minimale")
|
||||
elif value == self._data.z_max():
|
||||
return _translate("MainWindowProfile",
|
||||
"La cote maximale",
|
||||
"Z maximale")
|
||||
|
||||
if index.column() == 3:
|
||||
value = self._data.point(index.row()).name
|
||||
|
||||
@@ -104,16 +94,6 @@ class GeometryProfileTableModel(PamhyrTableModel):
|
||||
if role == Qt.ForegroundRole:
|
||||
return QColor("darkRed")
|
||||
|
||||
if role == Qt.ToolTipRole:
|
||||
if value.strip().upper() == "RG":
|
||||
return _translate(
|
||||
"MainWindowProfile", "Rive gauche"
|
||||
)
|
||||
else:
|
||||
return _translate(
|
||||
"MainWindowProfile", "Rive droite"
|
||||
)
|
||||
|
||||
return QVariant()
|
||||
|
||||
def setData(self, index, value, role=Qt.EditRole):
|
||||
@@ -250,6 +230,26 @@ class GeometryProfileTableModel(PamhyrTableModel):
|
||||
self.endMoveRows()
|
||||
self.layoutChanged.emit()
|
||||
|
||||
def purge(self, np_purge):
|
||||
|
||||
self._undo.push(
|
||||
PurgeCommand(
|
||||
self._data, np_purge
|
||||
)
|
||||
)
|
||||
|
||||
self.layoutChanged.emit()
|
||||
|
||||
def reverse(self):
|
||||
|
||||
self._undo.push(
|
||||
ReverseCommand(
|
||||
self._data
|
||||
)
|
||||
)
|
||||
|
||||
self.layoutChanged.emit()
|
||||
|
||||
def paste(self, row, header, data):
|
||||
if row > self._data.number_points:
|
||||
return
|
||||
|
||||
@@ -32,6 +32,10 @@ class GeometryProfileTranslate(GeometryTranslate):
|
||||
"Geometry", "Geometry cross-section"
|
||||
)
|
||||
|
||||
self._dict["width"] = _translate("Geometry", "Width")
|
||||
self._dict["area"] = _translate("Geometry", "Area")
|
||||
self._dict["perimeter"] = _translate("Geometry", "Perimeter")
|
||||
|
||||
self._sub_dict["table_headers"] = {
|
||||
"x": self._dict["x"],
|
||||
"y": self._dict["y"],
|
||||
|
||||
@@ -169,6 +169,34 @@ class MoveCommand(QUndoCommand):
|
||||
self._profile.move_down_point(self._i)
|
||||
|
||||
|
||||
class ReverseCommand(QUndoCommand):
|
||||
def __init__(self, profile):
|
||||
QUndoCommand.__init__(self)
|
||||
|
||||
self._profile = profile
|
||||
|
||||
def undo(self):
|
||||
self._profile.reverse()
|
||||
|
||||
def redo(self):
|
||||
self._profile.reverse()
|
||||
|
||||
|
||||
class PurgeCommand(QUndoCommand):
|
||||
def __init__(self, profile, np_purge):
|
||||
QUndoCommand.__init__(self)
|
||||
|
||||
self._profile = profile
|
||||
self._old = self._profile.points.copy()
|
||||
self._np_purge = np_purge
|
||||
|
||||
def undo(self):
|
||||
self._profile._points = self._old.copy()
|
||||
|
||||
def redo(self):
|
||||
self._profile.purge(self._np_purge)
|
||||
|
||||
|
||||
class PasteCommand(QUndoCommand):
|
||||
def __init__(self, profile, row, points):
|
||||
QUndoCommand.__init__(self)
|
||||
|
||||
@@ -20,8 +20,9 @@ import copy
|
||||
import sys
|
||||
import csv
|
||||
from time import time
|
||||
import logging
|
||||
|
||||
from tools import trace, timer
|
||||
from tools import trace, timer, logger_exception
|
||||
from Modules import Modules
|
||||
|
||||
from PyQt5.QtGui import (
|
||||
@@ -46,9 +47,12 @@ from View.Tools.Plot.PamhyrCanvas import MplCanvas
|
||||
from View.Geometry.Profile.Plot import Plot
|
||||
from View.Geometry.Profile.Table import GeometryProfileTableModel
|
||||
from View.Geometry.Profile.Translate import GeometryProfileTranslate
|
||||
from View.Geometry.PurgeDialog import PurgeDialog
|
||||
|
||||
_translate = QCoreApplication.translate
|
||||
|
||||
logger = logging.getLogger()
|
||||
|
||||
|
||||
class ProfileWindow(PamhyrWindow):
|
||||
_pamhyr_ui = "GeometryCrossSection"
|
||||
@@ -60,7 +64,7 @@ class ProfileWindow(PamhyrWindow):
|
||||
trad = GeometryProfileTranslate()
|
||||
name = (
|
||||
trad[self._pamhyr_name] +
|
||||
f" - {self._profile.name} {self._profile.kp}"
|
||||
f" - {self._profile.name} {self._profile.rk}"
|
||||
)
|
||||
|
||||
super(ProfileWindow, self).__init__(
|
||||
@@ -111,7 +115,8 @@ class ProfileWindow(PamhyrWindow):
|
||||
data=self._profile,
|
||||
trad=self._trad,
|
||||
toolbar=self._toolbar,
|
||||
table=self.find(QTableView, "tableView")
|
||||
table=self.find(QTableView, "tableView"),
|
||||
parent=self
|
||||
)
|
||||
self._plot.draw()
|
||||
|
||||
@@ -125,26 +130,49 @@ class ProfileWindow(PamhyrWindow):
|
||||
"action_down": self.move_down,
|
||||
"action_add": self.add,
|
||||
"action_delete": self.delete,
|
||||
"action_purge": self.purge,
|
||||
"action_reverse": self.reverse,
|
||||
}
|
||||
|
||||
for action in actions:
|
||||
self.find(QAction, action)\
|
||||
.triggered.connect(actions[action])
|
||||
|
||||
table = self.find(QTableView, f"tableView")
|
||||
table.selectionModel()\
|
||||
.selectionChanged\
|
||||
.connect(self.update_points_selection)
|
||||
|
||||
self._tablemodel.dataChanged.connect(self.update)
|
||||
|
||||
def update_points_selection(self):
|
||||
rows = self.index_selected_rows()
|
||||
self._plot.select_points_from_indices(rows)
|
||||
|
||||
def update(self):
|
||||
self.update_plot()
|
||||
self._propagate_update(key=Modules.GEOMETRY)
|
||||
|
||||
def _update(self, redraw=False, propagate=True):
|
||||
if redraw:
|
||||
self.update_plot()
|
||||
if propagate:
|
||||
self._propagate_update(key=Modules.GEOMETRY)
|
||||
|
||||
def update_plot(self):
|
||||
self._tablemodel.blockSignals(True)
|
||||
|
||||
# TODO: Do not rebuild all graph
|
||||
self._plot.update()
|
||||
|
||||
self._tablemodel.blockSignals(False)
|
||||
|
||||
def _propagated_update(self, key=Modules(0)):
|
||||
if Modules.GEOMETRY not in key:
|
||||
return
|
||||
|
||||
self._tablemodel.layoutChanged.emit()
|
||||
self._update(redraw=True, propagate=False)
|
||||
|
||||
def index_selected_row(self):
|
||||
table = self.find(QTableView, "tableView")
|
||||
rows = table.selectionModel()\
|
||||
@@ -154,6 +182,15 @@ class ProfileWindow(PamhyrWindow):
|
||||
|
||||
return rows[0].row()
|
||||
|
||||
def index_selected_rows(self):
|
||||
table = self.find(QTableView, "tableView")
|
||||
return list(
|
||||
map(
|
||||
lambda r: r.row(),
|
||||
table.selectionModel().selectedRows()
|
||||
)
|
||||
)
|
||||
|
||||
def add(self):
|
||||
table = self.find(QTableView, "tableView")
|
||||
if len(table.selectedIndexes()) == 0:
|
||||
@@ -221,6 +258,27 @@ class ProfileWindow(PamhyrWindow):
|
||||
|
||||
self.update()
|
||||
|
||||
def purge(self):
|
||||
self._tablemodel.purge()
|
||||
self.update()
|
||||
|
||||
def purge(self):
|
||||
try:
|
||||
dlg = PurgeDialog(
|
||||
trad=self._trad,
|
||||
parent=self
|
||||
)
|
||||
if dlg.exec():
|
||||
self._tablemodel.purge(dlg.np_purge)
|
||||
self._plot.draw()
|
||||
except Exception as e:
|
||||
logger_exception(e)
|
||||
return
|
||||
|
||||
def reverse(self):
|
||||
self._tablemodel.reverse()
|
||||
self.update()
|
||||
|
||||
def _copy(self):
|
||||
table = self.find(QTableView, "tableView")
|
||||
rows = table.selectionModel().selectedRows()
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
# PurgeDialog.py -- Pamhyr
|
||||
# Copyright (C) 2023-2024 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 -*-
|
||||
|
||||
from View.Tools.PamhyrWindow import PamhyrDialog
|
||||
|
||||
from PyQt5.QtGui import (
|
||||
QKeySequence,
|
||||
)
|
||||
|
||||
from PyQt5.QtCore import (
|
||||
Qt, QVariant, QAbstractTableModel,
|
||||
)
|
||||
|
||||
from PyQt5.QtWidgets import (
|
||||
QUndoStack, QShortcut, QSpinBox,
|
||||
)
|
||||
|
||||
|
||||
class PurgeDialog(PamhyrDialog):
|
||||
_pamhyr_ui = "PurgeOptions"
|
||||
_pamhyr_name = "Purge"
|
||||
|
||||
def __init__(self, trad=None, parent=None):
|
||||
super(PurgeDialog, self).__init__(
|
||||
title=trad[self._pamhyr_name],
|
||||
trad=trad,
|
||||
options=[],
|
||||
parent=parent
|
||||
)
|
||||
|
||||
self._init_default_values()
|
||||
|
||||
def _init_default_values(self):
|
||||
self._np_purge = 24
|
||||
|
||||
@property
|
||||
def np_purge(self):
|
||||
return self._np_purge
|
||||
|
||||
def accept(self):
|
||||
self._np_purge = self.get_spin_box("spinBox_np_purge")
|
||||
super().accept()
|
||||
|
||||
def reject(self):
|
||||
self.close()
|
||||
@@ -55,8 +55,8 @@ class GeometryReachTableModel(PamhyrTableModel):
|
||||
return self._data.profile(index.row()).name
|
||||
|
||||
if role == Qt.DisplayRole and index.column() == 1:
|
||||
kp = self._data.profile(index.row()).kp
|
||||
return f"{kp:.4f}"
|
||||
rk = self._data.profile(index.row()).rk
|
||||
return f"{rk:.4f}"
|
||||
|
||||
if role == Qt.DisplayRole and index.column() == 2:
|
||||
return str(self._data.profile(index.row()).nb_points)
|
||||
@@ -94,9 +94,9 @@ class GeometryReachTableModel(PamhyrTableModel):
|
||||
|
||||
if index.column() == 1:
|
||||
self._undo.push(
|
||||
SetKPCommand(
|
||||
SetRKCommand(
|
||||
self._data, index.row(),
|
||||
self._data.profile(index.row()).kp,
|
||||
self._data.profile(index.row()).rk,
|
||||
value
|
||||
)
|
||||
)
|
||||
@@ -240,9 +240,30 @@ class GeometryReachTableModel(PamhyrTableModel):
|
||||
|
||||
self._undo.push(
|
||||
MeshingCommand(
|
||||
self._data, mesher, data
|
||||
self._data, mesher, data, "mesh"
|
||||
)
|
||||
)
|
||||
|
||||
self.layoutAboutToBeChanged.emit()
|
||||
self.layoutChanged.emit()
|
||||
|
||||
def update_rk(self, mesher, data):
|
||||
self.layoutAboutToBeChanged.emit()
|
||||
|
||||
self._undo.push(
|
||||
MeshingCommand(
|
||||
self._data, mesher, data, "update_rk"
|
||||
)
|
||||
)
|
||||
|
||||
self.layoutAboutToBeChanged.emit()
|
||||
self.layoutChanged.emit()
|
||||
|
||||
def purge(self, np_purge):
|
||||
|
||||
self._undo.push(
|
||||
PurgeCommand(
|
||||
self._data, np_purge
|
||||
)
|
||||
)
|
||||
self.layoutChanged.emit()
|
||||
@@ -33,10 +33,12 @@ class GeometryTranslate(MainTranslate):
|
||||
|
||||
self._dict["open_file"] = _translate("Geometry", "Open a file")
|
||||
self._dict["file_st"] = _translate(
|
||||
"Geometry", "File mage geometry (*.ST *.st)")
|
||||
"Geometry", "Mage geometry file (*.ST *.st)")
|
||||
self._dict["file_m"] = _translate(
|
||||
"Geometry", "File mage meshed geometry (*.M *.m)")
|
||||
self._dict["file_all"] = _translate("Geometry", "All file (*)")
|
||||
"Geometry", "Mage meshed geometry file (*.M *.m)")
|
||||
self._dict["file_shp"] = _translate(
|
||||
"Geometry", "Shapefile (*.SHP *.shp)")
|
||||
self._dict["file_all"] = _translate("Geometry", "All files (*)")
|
||||
|
||||
self._dict["cross_section"] = _translate("Geometry", "cross-section")
|
||||
self._dict["cross_sections"] = _translate("Geometry", "cross-sections")
|
||||
@@ -59,14 +61,17 @@ class GeometryTranslate(MainTranslate):
|
||||
|
||||
self._sub_dict["table_headers"] = {
|
||||
"name": self._dict["name"],
|
||||
"kp": self._dict["unit_kp"],
|
||||
"rk": self._dict["unit_rk_short"],
|
||||
"poins": _translate("Geometry", "Points"),
|
||||
}
|
||||
|
||||
self._sub_dict["lm_dict"] = {
|
||||
"1": _translate("Geometry", "First guideline"),
|
||||
"2": _translate("Geometry", "Second guideline"),
|
||||
"3": _translate("Geometry", "Means between the two guideline"),
|
||||
"1": _translate("Geometry", "the first guide-line"),
|
||||
"2": _translate("Geometry", "the second guide-line"),
|
||||
"3": _translate(
|
||||
"Geometry",
|
||||
"the mean over the two guide-lines"
|
||||
),
|
||||
}
|
||||
|
||||
self._sub_dict["r_lm_dict"] = {}
|
||||
|
||||
@@ -19,13 +19,14 @@
|
||||
import logging
|
||||
|
||||
from copy import deepcopy
|
||||
from tools import trace, timer
|
||||
from tools import trace, timer, logger_exception
|
||||
|
||||
from PyQt5.QtWidgets import (
|
||||
QMessageBox, QUndoCommand, QUndoStack,
|
||||
)
|
||||
|
||||
from Model.Geometry import Reach
|
||||
from Model.Except import exception_message_box
|
||||
|
||||
from Meshing.Mage import MeshingWithMage
|
||||
|
||||
@@ -55,16 +56,16 @@ class SetNameCommand(SetDataCommand):
|
||||
self._reach.profile(self._index).name = self._new
|
||||
|
||||
|
||||
class SetKPCommand(SetDataCommand):
|
||||
class SetRKCommand(SetDataCommand):
|
||||
def __init__(self, reach, index, old_value, new_value):
|
||||
self.type = float
|
||||
super(SetKPCommand, self).__init__(reach, index, old_value, new_value)
|
||||
super(SetRKCommand, self).__init__(reach, index, old_value, new_value)
|
||||
|
||||
def undo(self):
|
||||
self._reach.profile(self._index).kp = self._old
|
||||
self._reach.profile(self._index).rk = self._old
|
||||
|
||||
def redo(self):
|
||||
self._reach.profile(self._index).kp = self._new
|
||||
self._reach.profile(self._index).rk = self._new
|
||||
|
||||
|
||||
class AddCommand(QUndoCommand):
|
||||
@@ -209,20 +210,25 @@ class ImportCommand(QUndoCommand):
|
||||
|
||||
def redo(self):
|
||||
if self._profiles is None:
|
||||
try:
|
||||
self._profiles = self._reach.import_geometry(self._filename)
|
||||
self._profiles.reverse()
|
||||
except Exception as e:
|
||||
logger_exception(e)
|
||||
exception_message_box(e)
|
||||
else:
|
||||
for profile in self._profiles:
|
||||
self._reach.insert_profile(self._row, profile)
|
||||
|
||||
|
||||
class MeshingCommand(QUndoCommand):
|
||||
def __init__(self, reach, mesher, data):
|
||||
def __init__(self, reach, mesher, data, command):
|
||||
QUndoCommand.__init__(self)
|
||||
|
||||
self._reach = reach
|
||||
self._data = data
|
||||
self._mesher = mesher
|
||||
self._command = command
|
||||
|
||||
self._profiles = reach.profiles.copy()
|
||||
self._profiles.reverse()
|
||||
@@ -237,6 +243,12 @@ class MeshingCommand(QUndoCommand):
|
||||
|
||||
def redo(self):
|
||||
if self._new_profiles is None:
|
||||
if self._command == "update_rk":
|
||||
self._mesher.update_rk(
|
||||
self._reach,
|
||||
**self._data
|
||||
)
|
||||
else:
|
||||
self._mesher.meshing(
|
||||
self._reach,
|
||||
**self._data
|
||||
@@ -249,3 +261,23 @@ class MeshingCommand(QUndoCommand):
|
||||
|
||||
for profile in self._new_profiles:
|
||||
self._reach.insert_profile(0, profile)
|
||||
|
||||
|
||||
class PurgeCommand(QUndoCommand):
|
||||
def __init__(self, reach, np_purge):
|
||||
QUndoCommand.__init__(self)
|
||||
|
||||
self._reach = reach
|
||||
self._np_purge = np_purge
|
||||
|
||||
self._old = []
|
||||
for profile in self._reach.profiles:
|
||||
self._old.append(profile.points.copy())
|
||||
|
||||
def undo(self):
|
||||
for i in range(self._reach.number_profiles):
|
||||
self._reach.profiles[i]._points = self._old[i].copy()
|
||||
|
||||
def redo(self):
|
||||
for profile in self._reach._profiles:
|
||||
profile.purge(self._np_purge)
|
||||
@@ -0,0 +1,164 @@
|
||||
# UpdateRKDialog.py -- Pamhyr
|
||||
# Copyright (C) 2023-2024 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 -*-
|
||||
|
||||
from View.Tools.PamhyrWindow import PamhyrDialog
|
||||
|
||||
from PyQt5.QtGui import (
|
||||
QKeySequence,
|
||||
)
|
||||
|
||||
from PyQt5.QtCore import (
|
||||
Qt, QVariant, QAbstractTableModel,
|
||||
)
|
||||
|
||||
from PyQt5.QtWidgets import (
|
||||
QDialogButtonBox, QComboBox, QUndoStack, QShortcut,
|
||||
QDoubleSpinBox, QButtonGroup,
|
||||
)
|
||||
|
||||
|
||||
class UpdateRKDialog(PamhyrDialog):
|
||||
_pamhyr_ui = "UpdateRKOptions"
|
||||
_pamhyr_name = "UpdateRK"
|
||||
|
||||
def __init__(self, reach, trad=None, parent=None):
|
||||
super(UpdateRKDialog, self).__init__(
|
||||
title=trad[self._pamhyr_name],
|
||||
trad=trad,
|
||||
options=[],
|
||||
parent=parent
|
||||
)
|
||||
|
||||
self._reach = reach
|
||||
self._profiles = None
|
||||
|
||||
self._init_default_values()
|
||||
|
||||
def _init_default_values(self):
|
||||
gl, _ = self._reach.compute_guidelines()
|
||||
self._gl = list(gl)
|
||||
|
||||
self._begin_dir = "un"
|
||||
self._end_dir = "np"
|
||||
|
||||
lower_gl = list(map(str.lower, self._gl))
|
||||
for i, gl in enumerate(lower_gl):
|
||||
if gl == "rd":
|
||||
self._begin_dir = self._gl[i]
|
||||
elif gl == "rg":
|
||||
self._end_dir = self._gl[i]
|
||||
|
||||
self._orientation = 0
|
||||
self._origin = self._reach.profile(0)
|
||||
self._origin_value = self._reach.profile(0).rk
|
||||
|
||||
self._init_default_values_profiles()
|
||||
self._init_default_values_guidelines()
|
||||
|
||||
def _init_default_values_profiles(self):
|
||||
profiles = self.profiles
|
||||
|
||||
self.combobox_add_items("comboBox_origin", profiles)
|
||||
|
||||
self.find(QComboBox, "comboBox_origin")\
|
||||
.currentIndexChanged\
|
||||
.connect(
|
||||
self.changed_profile
|
||||
)
|
||||
|
||||
buttonbox = self.find(QButtonGroup, "buttonGroup_orientation")
|
||||
|
||||
for button in buttonbox.buttons():
|
||||
name = button.objectName()
|
||||
i = int(name.split('_')[-1])
|
||||
|
||||
buttonbox.setId(button, i)
|
||||
|
||||
def changed_profile(self):
|
||||
origin = self.get_combobox_text("comboBox_origin")
|
||||
self.set_double_spin_box(
|
||||
"doubleSpinBox_origin",
|
||||
self._reach.profile(self.profiles.index(origin)).rk
|
||||
)
|
||||
|
||||
@property
|
||||
def profiles(self):
|
||||
if self._profiles is None:
|
||||
self._profiles = list(
|
||||
map(
|
||||
lambda p: self._profile_name(p),
|
||||
self._reach.profiles
|
||||
)
|
||||
)
|
||||
|
||||
return self._profiles
|
||||
|
||||
def _profile_name(self, profile):
|
||||
name = profile.name
|
||||
|
||||
if name == "":
|
||||
name = f"{profile.rk}"
|
||||
else:
|
||||
name += f" ({profile.rk})"
|
||||
|
||||
return name
|
||||
|
||||
def _init_default_values_guidelines(self):
|
||||
bgl = ['un'] + self._gl + ['np']
|
||||
egl = ['un'] + self._gl + ['np']
|
||||
|
||||
self.combobox_add_items("comboBox_begin_gl", bgl)
|
||||
self.combobox_add_items("comboBox_end_gl", egl)
|
||||
|
||||
self.set_combobox_text("comboBox_begin_gl", self._begin_dir)
|
||||
self.set_combobox_text("comboBox_end_gl", self._end_dir)
|
||||
|
||||
@property
|
||||
def orientation(self):
|
||||
return self._orientation
|
||||
|
||||
@property
|
||||
def origin(self):
|
||||
return self._origin
|
||||
|
||||
@property
|
||||
def origin_value(self):
|
||||
return self._origin_value
|
||||
|
||||
@property
|
||||
def begin_dir(self):
|
||||
return self._begin_dir
|
||||
|
||||
@property
|
||||
def end_dir(self):
|
||||
return self._end_dir
|
||||
|
||||
def accept(self):
|
||||
origin = self.get_combobox_text("comboBox_origin")
|
||||
self._origin = self.profiles.index(origin)
|
||||
self._origin_value = self.get_double_spin_box("doubleSpinBox_origin")
|
||||
self._begin_dir = self.get_combobox_text("comboBox_begin_gl")
|
||||
self._end_dir = self.get_combobox_text("comboBox_end_gl")
|
||||
self._orientation = self.get_checked_id_button_group(
|
||||
"buttonGroup_orientation"
|
||||
)
|
||||
|
||||
super().accept()
|
||||
|
||||
def reject(self):
|
||||
self.close()
|
||||
+108
-98
@@ -36,7 +36,7 @@ from PyQt5.QtCore import (
|
||||
from PyQt5.QtWidgets import (
|
||||
QApplication, QMainWindow, QFileDialog, QCheckBox,
|
||||
QUndoStack, QShortcut, QTableView, QHeaderView,
|
||||
QAction, QSlider, QPushButton, QVBoxLayout,
|
||||
QAction, QPushButton, QVBoxLayout,
|
||||
QLabel, QAbstractItemView,
|
||||
)
|
||||
|
||||
@@ -54,8 +54,10 @@ from Meshing.Mage import (
|
||||
from View.Geometry.Table import GeometryReachTableModel
|
||||
from View.Geometry.PlotXY import PlotXY
|
||||
from View.Geometry.PlotAC import PlotAC
|
||||
from View.Geometry.PlotKPZ import PlotKPZ
|
||||
from View.Geometry.PlotRKZ import PlotRKZ
|
||||
from View.Geometry.MeshingDialog import MeshingDialog
|
||||
from View.Geometry.UpdateRKDialog import UpdateRKDialog
|
||||
from View.Geometry.PurgeDialog import PurgeDialog
|
||||
from View.Geometry.Translate import GeometryTranslate
|
||||
from View.Geometry.Profile.Window import ProfileWindow
|
||||
|
||||
@@ -95,7 +97,6 @@ class GeometryWindow(PamhyrWindow):
|
||||
self.setup_plots()
|
||||
self.setup_statusbar()
|
||||
self.setup_connections()
|
||||
self.changed_slider_value()
|
||||
|
||||
def setup_table(self):
|
||||
table_headers = self._trad.get_dict("table_headers")
|
||||
@@ -104,7 +105,7 @@ class GeometryWindow(PamhyrWindow):
|
||||
self._table = GeometryReachTableModel(
|
||||
table_view=table,
|
||||
table_headers=table_headers,
|
||||
editable_headers=["name", "kp"],
|
||||
editable_headers=["name", "rk"],
|
||||
data=self._reach,
|
||||
undo=self._undo_stack
|
||||
)
|
||||
@@ -115,7 +116,7 @@ class GeometryWindow(PamhyrWindow):
|
||||
|
||||
def setup_plots(self):
|
||||
self.setup_plots_xy()
|
||||
self.setup_plots_kpc()
|
||||
self.setup_plots_rkc()
|
||||
self.setup_plots_ac()
|
||||
|
||||
def setup_plots_xy(self):
|
||||
@@ -130,17 +131,17 @@ class GeometryWindow(PamhyrWindow):
|
||||
self._plot_layout_xy.addWidget(self._canvas_xy)
|
||||
self.plot_xy()
|
||||
|
||||
def setup_plots_kpc(self):
|
||||
self._canvas_kpc = MplCanvas(width=6, height=4, dpi=100)
|
||||
self._canvas_kpc.setObjectName("canvas_kpc")
|
||||
self._toolbar_kpc = PamhyrPlotToolbar(
|
||||
self._canvas_kpc, self,
|
||||
def setup_plots_rkc(self):
|
||||
self._canvas_rkc = MplCanvas(width=6, height=4, dpi=100)
|
||||
self._canvas_rkc.setObjectName("canvas_rkc")
|
||||
self._toolbar_rkc = PamhyrPlotToolbar(
|
||||
self._canvas_rkc, self,
|
||||
items=["home", "zoom", "save", "iso", "back/forward", "move"]
|
||||
)
|
||||
self._plot_layout_kpc = self.find(QVBoxLayout, "verticalLayout_2")
|
||||
self._plot_layout_kpc.addWidget(self._toolbar_kpc)
|
||||
self._plot_layout_kpc.addWidget(self._canvas_kpc)
|
||||
self.plot_kpc()
|
||||
self._plot_layout_rkc = self.find(QVBoxLayout, "verticalLayout_2")
|
||||
self._plot_layout_rkc.addWidget(self._toolbar_rkc)
|
||||
self._plot_layout_rkc.addWidget(self._canvas_rkc)
|
||||
self.plot_rkc()
|
||||
|
||||
def setup_plots_ac(self):
|
||||
self._canvas_ac = MplCanvas(width=9, height=4, dpi=100)
|
||||
@@ -156,9 +157,12 @@ class GeometryWindow(PamhyrWindow):
|
||||
|
||||
def _compute_status_label(self):
|
||||
row = self.index_selected_row()
|
||||
if row is None:
|
||||
return
|
||||
|
||||
profile = self._reach.profile(row)
|
||||
|
||||
name = profile.name + " " + str(profile.kp)
|
||||
name = profile.name + " " + str(profile.rk)
|
||||
|
||||
return (
|
||||
"<font color=\"Grey\">" +
|
||||
@@ -188,19 +192,14 @@ class GeometryWindow(PamhyrWindow):
|
||||
"action_delete": self.delete,
|
||||
"action_edit": self.edit_profile,
|
||||
"action_meshing": self.edit_meshing,
|
||||
"action_update_rk": self.update_rk,
|
||||
"action_purge": self.purge,
|
||||
}
|
||||
|
||||
for action in actions:
|
||||
self.find(QAction, action)\
|
||||
.triggered.connect(actions[action])
|
||||
|
||||
self.find(QSlider, "verticalSlider").valueChanged.connect(
|
||||
self.changed_slider_value)
|
||||
self.find(QPushButton, "pushButton_up").clicked.connect(
|
||||
self.decrement_value_slider)
|
||||
self.find(QPushButton, "pushButton_down").clicked.connect(
|
||||
self.increment_value_slider)
|
||||
|
||||
# Profile selection when line change in table
|
||||
self.find(QTableView, "tableView").selectionModel()\
|
||||
.selectionChanged\
|
||||
@@ -217,17 +216,16 @@ class GeometryWindow(PamhyrWindow):
|
||||
def _update(self, redraw=False, propagate=True):
|
||||
if redraw:
|
||||
self._plot_xy.draw()
|
||||
self._plot_kpc.draw()
|
||||
self._plot_rkc.draw()
|
||||
self._plot_ac.draw()
|
||||
|
||||
self.select_current_profile()
|
||||
self.changed_slider_value()
|
||||
|
||||
if propagate:
|
||||
self._propagate_update(key=Modules.GEOMETRY)
|
||||
|
||||
def _propagated_update(self, key=Modules(0)):
|
||||
if Modules.NETWORK not in key:
|
||||
if Modules.NETWORK not in key and Modules.GEOMETRY not in key:
|
||||
return
|
||||
|
||||
self._update(propagate=False)
|
||||
@@ -241,6 +239,7 @@ class GeometryWindow(PamhyrWindow):
|
||||
file_types = [
|
||||
self._trad["file_st"],
|
||||
self._trad["file_m"],
|
||||
self._trad["file_shp"],
|
||||
self._trad["file_all"],
|
||||
]
|
||||
|
||||
@@ -296,7 +295,6 @@ class GeometryWindow(PamhyrWindow):
|
||||
"limites": [dlg.begin_cs, dlg.end_cs],
|
||||
"directrices": [dlg.begin_dir, dlg.end_dir],
|
||||
"lplan": dlg.lplan,
|
||||
"lm": dlg.lm,
|
||||
"linear": dlg.linear,
|
||||
}
|
||||
self._edit_meshing(data)
|
||||
@@ -317,10 +315,37 @@ class GeometryWindow(PamhyrWindow):
|
||||
src_except=e
|
||||
)
|
||||
|
||||
pyqtSlot(bool)
|
||||
def update_rk(self):
|
||||
try:
|
||||
dlg = UpdateRKDialog(
|
||||
reach=self._reach,
|
||||
trad=self._trad,
|
||||
parent=self
|
||||
)
|
||||
if dlg.exec():
|
||||
data = {
|
||||
"origin": dlg.origin,
|
||||
"directrices": [dlg.begin_dir, dlg.end_dir],
|
||||
"origin_value": dlg.origin_value,
|
||||
"orientation": dlg.orientation,
|
||||
}
|
||||
self._update_rk(data)
|
||||
except Exception as e:
|
||||
logger_exception(e)
|
||||
return
|
||||
|
||||
def changed_profile_slot(self, status):
|
||||
self.update_view1 = status
|
||||
def _update_rk(self, data):
|
||||
try:
|
||||
mesher = MeshingWithMageMailleurTT()
|
||||
self._table.update_rk(mesher, data)
|
||||
except Exception as e:
|
||||
logger_exception(e)
|
||||
raise ExternFileMissingError(
|
||||
module="mage",
|
||||
filename="MailleurTT",
|
||||
path=MeshingWithMageMailleurTT._path(),
|
||||
src_except=e
|
||||
)
|
||||
|
||||
def plot_xy(self):
|
||||
self.tableView.model().blockSignals(True)
|
||||
@@ -329,7 +354,9 @@ class GeometryWindow(PamhyrWindow):
|
||||
canvas=self._canvas_xy,
|
||||
data=self._reach,
|
||||
trad=self._trad,
|
||||
toolbar=self._toolbar_xy
|
||||
toolbar=self._toolbar_xy,
|
||||
table=self.find(QTableView, "tableView"),
|
||||
parent=self
|
||||
)
|
||||
self._plot_xy.draw()
|
||||
|
||||
@@ -340,22 +367,25 @@ class GeometryWindow(PamhyrWindow):
|
||||
self._plot_xy.update()
|
||||
self.tableView.model().blockSignals(False)
|
||||
|
||||
def plot_kpc(self):
|
||||
def plot_rkc(self):
|
||||
self.tableView.model().blockSignals(True)
|
||||
|
||||
self._plot_kpc = PlotKPZ(
|
||||
canvas=self._canvas_kpc,
|
||||
self._plot_rkc = PlotRKZ(
|
||||
canvas=self._canvas_rkc,
|
||||
study=self._study,
|
||||
data=self._reach,
|
||||
trad=self._trad,
|
||||
toolbar=self._toolbar_kpc
|
||||
toolbar=self._toolbar_rkc,
|
||||
table=self.find(QTableView, "tableView"),
|
||||
parent=self
|
||||
)
|
||||
self._plot_kpc.draw()
|
||||
self._plot_rkc.draw()
|
||||
|
||||
self.tableView.model().blockSignals(False)
|
||||
|
||||
def update_plot_kpc(self):
|
||||
def update_plot_rkc(self):
|
||||
self.tableView.model().blockSignals(True)
|
||||
self._plot_kpc.update()
|
||||
self._plot_rkc.update()
|
||||
self.tableView.model().blockSignals(False)
|
||||
|
||||
def plot_ac(self):
|
||||
@@ -373,7 +403,6 @@ class GeometryWindow(PamhyrWindow):
|
||||
|
||||
def update_plot_ac(self, ind: int):
|
||||
self.tableView.model().blockSignals(True)
|
||||
self._plot_ac.current = ind
|
||||
self._plot_ac.update()
|
||||
self.tableView.model().blockSignals(False)
|
||||
|
||||
@@ -389,10 +418,10 @@ class GeometryWindow(PamhyrWindow):
|
||||
self._plot_xy.update()
|
||||
self.tableView.model().blockSignals(False)
|
||||
|
||||
def select_plot_kpc(self, ind: int):
|
||||
def select_plot_rkc(self, ind: int):
|
||||
self.tableView.model().blockSignals(True)
|
||||
self._plot_kpc.current = ind
|
||||
self._plot_kpc.update()
|
||||
self._plot_rkc.current = ind
|
||||
self._plot_rkc.update()
|
||||
self.tableView.model().blockSignals(False)
|
||||
|
||||
def select_plot_ac(self, ind: int):
|
||||
@@ -401,64 +430,21 @@ class GeometryWindow(PamhyrWindow):
|
||||
self._plot_ac.update()
|
||||
self.tableView.model().blockSignals(False)
|
||||
|
||||
def select_row_profile_slider(self, ind: int = 0):
|
||||
if self.tableView is not None:
|
||||
selectionModel = self.tableView.selectionModel()
|
||||
index = self.tableView.model().index(ind, 0)
|
||||
|
||||
selectionModel.select(
|
||||
index,
|
||||
QItemSelectionModel.Rows |
|
||||
QItemSelectionModel.ClearAndSelect |
|
||||
QItemSelectionModel.Select
|
||||
)
|
||||
|
||||
self.tableView.scrollTo(index)
|
||||
|
||||
def select_current_profile(self):
|
||||
self.tableView.model().blockSignals(True)
|
||||
|
||||
if len(self.tableView.selectedIndexes()) > 0:
|
||||
row = self.index_selected_row()
|
||||
|
||||
self.find(QSlider, "verticalSlider").setValue(row)
|
||||
if row is not None:
|
||||
self.select_plot_xy(row)
|
||||
self.select_plot_kpc(row)
|
||||
self.select_plot_rkc(row)
|
||||
self.select_plot_ac(row)
|
||||
else:
|
||||
self._plot_xy.draw()
|
||||
self._plot_kpc.draw()
|
||||
self._plot_rkc.draw()
|
||||
self._plot_ac.draw()
|
||||
|
||||
self.tableView.model().blockSignals(False)
|
||||
|
||||
def changed_slider_value(self):
|
||||
self.tableView.model().blockSignals(True)
|
||||
|
||||
if self._table.rowCount() != 0:
|
||||
slider = self.find(QSlider, "verticalSlider")
|
||||
slider.setMaximum(self._table.rowCount() - 1)
|
||||
|
||||
slider_value = slider.value()
|
||||
kp = self._reach.profile(slider_value).kp
|
||||
|
||||
self.select_plot_xy(slider_value)
|
||||
self.select_plot_kpc(slider_value)
|
||||
self.select_row_profile_slider(slider_value)
|
||||
self.update_statusbar()
|
||||
|
||||
self.tableView.model().blockSignals(False)
|
||||
|
||||
def increment_value_slider(self):
|
||||
slider = self.find(QSlider, "verticalSlider")
|
||||
if 0 <= slider.value() < self._table.rowCount() - 1:
|
||||
slider.setValue(slider.value() + 1)
|
||||
|
||||
def decrement_value_slider(self):
|
||||
slider = self.find(QSlider, "verticalSlider")
|
||||
if 0 < slider.value() < self._table.rowCount():
|
||||
slider.setValue(slider.value() - 1)
|
||||
|
||||
def add(self):
|
||||
if len(self.tableView.selectedIndexes()) == 0:
|
||||
self._table.add(self._table.rowCount())
|
||||
@@ -479,32 +465,56 @@ class GeometryWindow(PamhyrWindow):
|
||||
self._table.delete(rows)
|
||||
|
||||
def index_selected_row(self):
|
||||
return self.tableView\
|
||||
r = self.tableView\
|
||||
.selectionModel()\
|
||||
.selectedRows()[0]\
|
||||
.row()
|
||||
.selectedRows()
|
||||
|
||||
if len(r) == 1:
|
||||
return r[0].row()
|
||||
else:
|
||||
return None
|
||||
|
||||
def sort_ascending(self):
|
||||
self._table.sort_profiles(False)
|
||||
self.select_current_profile()
|
||||
self.changed_slider_value()
|
||||
|
||||
def sort_descending(self):
|
||||
self._table.sort_profiles(True)
|
||||
|
||||
self.select_current_profile()
|
||||
self.changed_slider_value()
|
||||
|
||||
def move_up(self):
|
||||
row = self.index_selected_row()
|
||||
if row is None:
|
||||
return
|
||||
|
||||
self._table.move_up(row)
|
||||
self.select_current_profile()
|
||||
|
||||
def move_down(self):
|
||||
row = self.index_selected_row()
|
||||
if row is None:
|
||||
return
|
||||
|
||||
self._table.move_down(row)
|
||||
self.select_current_profile()
|
||||
|
||||
def purge(self):
|
||||
self._table.purge()
|
||||
self.update_redraw()
|
||||
|
||||
def purge(self):
|
||||
try:
|
||||
dlg = PurgeDialog(
|
||||
trad=self._trad,
|
||||
parent=self
|
||||
)
|
||||
if dlg.exec():
|
||||
self._table.purge(dlg.np_purge)
|
||||
except Exception as e:
|
||||
logger_exception(e)
|
||||
return
|
||||
|
||||
def duplicate(self):
|
||||
rows = [
|
||||
row.row() for row in
|
||||
@@ -529,12 +539,12 @@ class GeometryWindow(PamhyrWindow):
|
||||
.selectedRows()
|
||||
|
||||
table = []
|
||||
# table.append(["name", "kp"])
|
||||
# table.append(["name", "rk"])
|
||||
|
||||
for row in rows:
|
||||
profile = self._reach.profile(row.row())
|
||||
table.append(
|
||||
[profile.name, profile.kp]
|
||||
[profile.name, profile.rk]
|
||||
)
|
||||
|
||||
self.copyTableIntoClipboard(table)
|
||||
@@ -578,14 +588,14 @@ class GeometryWindow(PamhyrWindow):
|
||||
self.select_current_profile()
|
||||
# self.update_plot_ac()
|
||||
# self.update_plot_xy()
|
||||
# self.update_plot_kpc()
|
||||
# self.update_plot_rkc()
|
||||
|
||||
def _redo(self):
|
||||
self._table.redo()
|
||||
self.select_current_profile()
|
||||
# self.update_plot_ac()
|
||||
# self.update_plot_xy()
|
||||
# self.update_plot_kpc()
|
||||
# self.update_plot_rkc()
|
||||
|
||||
def export_to_file(self):
|
||||
settings = QSettings(
|
||||
@@ -632,13 +642,13 @@ class GeometryWindow(PamhyrWindow):
|
||||
c1 = f"{profile.code1:>6}"
|
||||
c2 = f"{profile.code2:>6}"
|
||||
t = f"{len(profile.points):>6}"
|
||||
kp = f"{profile.kp:>12f}"[0:12]
|
||||
rk = f"{profile.rk:>12f}"[0:12]
|
||||
pname = profile.name
|
||||
if profile.name == "":
|
||||
pname = f"p{profile.id:>3}".replace(" ", "0")
|
||||
name = f"{pname:<19}"
|
||||
|
||||
wfile.write(f"{num}{c1}{c2}{t} {kp} {pname}\n")
|
||||
wfile.write(f"{num}{c1}{c2}{t} {rk} {pname}\n")
|
||||
|
||||
for point in profile.points:
|
||||
self._export_to_file_st_point(wfile, point)
|
||||
|
||||
@@ -28,7 +28,7 @@ class BasicHydraulicStructuresTranslate(MainTranslate):
|
||||
super(BasicHydraulicStructuresTranslate, self).__init__()
|
||||
|
||||
self._dict["Basic Hydraulic Structures"] = _translate(
|
||||
"BasicHydraulicStructures", "Basic Hydraulic Structures"
|
||||
"BasicHydraulicStructures", "Basic Hydraulic Structure"
|
||||
)
|
||||
|
||||
self._dict['msg_type_change_title'] = _translate(
|
||||
@@ -138,7 +138,7 @@ hydraulic structure values?"
|
||||
"BasicHydraulicStructures", "Check valve"
|
||||
),
|
||||
"UD": _translate(
|
||||
"BasicHydraulicStructures", "User defined"
|
||||
"BasicHydraulicStructures", "User-defined"
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
@@ -150,9 +150,9 @@ class BasicHydraulicStructuresWindow(PamhyrWindow):
|
||||
self.plot_layout.addWidget(self.canvas)
|
||||
|
||||
reach = self._hs.input_reach
|
||||
profile_kp = self._hs.input_kp
|
||||
if profile_kp is not None:
|
||||
profiles = reach.reach.get_profiles_from_kp(float(profile_kp))
|
||||
profile_rk = self._hs.input_rk
|
||||
if profile_rk is not None:
|
||||
profiles = reach.reach.get_profiles_from_rk(float(profile_rk))
|
||||
else:
|
||||
profiles = None
|
||||
if profiles is not None:
|
||||
@@ -252,8 +252,9 @@ class BasicHydraulicStructuresWindow(PamhyrWindow):
|
||||
self._checkbox.setChecked(self._hs.basic_structure(row).enabled)
|
||||
|
||||
def _set_basic_structure_state(self):
|
||||
row = self.index_selected_row()
|
||||
|
||||
rows = self.index_selected_rows()
|
||||
if len(rows) != 0:
|
||||
for row in rows:
|
||||
if row is not None:
|
||||
self._table.enabled(
|
||||
row,
|
||||
|
||||
@@ -57,11 +57,11 @@ class PlotAC(PamhyrPlot):
|
||||
self.init_axes()
|
||||
|
||||
if self.data is None:
|
||||
self.line_kp = None
|
||||
self.line_rk = None
|
||||
return
|
||||
|
||||
if self._current_reach is None:
|
||||
self.line_kp = None
|
||||
self.line_rk = None
|
||||
return
|
||||
|
||||
self.draw_data()
|
||||
@@ -73,13 +73,13 @@ class PlotAC(PamhyrPlot):
|
||||
reach = self._current_reach
|
||||
|
||||
if self._current_profile is None:
|
||||
self.line_kp = None
|
||||
self.line_rk = None
|
||||
else:
|
||||
profile = self._current_profile
|
||||
x = profile.get_station()
|
||||
z = profile.z()
|
||||
|
||||
self.line_kp, = self.canvas.axes.plot(
|
||||
self.line_rk, = self.canvas.axes.plot(
|
||||
x, z,
|
||||
color=self.color_plot_river_bottom,
|
||||
**self.plot_default_kargs
|
||||
@@ -94,7 +94,7 @@ class PlotAC(PamhyrPlot):
|
||||
self.update()
|
||||
|
||||
def update(self):
|
||||
if self.line_kp is None:
|
||||
if self.line_rk is None:
|
||||
self.draw()
|
||||
return
|
||||
|
||||
@@ -110,11 +110,11 @@ class PlotAC(PamhyrPlot):
|
||||
x = profile.get_station()
|
||||
z = profile.z()
|
||||
|
||||
self.line_kp.set_data(x, z)
|
||||
self.line_rk.set_data(x, z)
|
||||
|
||||
def clear(self):
|
||||
self.update_clear()
|
||||
|
||||
def update_clear(self):
|
||||
if self.line_kp is not None:
|
||||
self.line_kp.set_data([], [])
|
||||
if self.line_rk is not None:
|
||||
self.line_rk.set_data([], [])
|
||||
@@ -1,4 +1,4 @@
|
||||
# PlotKPC.py -- Pamhyr
|
||||
# PlotRKC.py -- Pamhyr
|
||||
# Copyright (C) 2023-2024 INRAE
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
@@ -20,19 +20,20 @@ from tools import timer
|
||||
from View.Tools.PamhyrPlot import PamhyrPlot
|
||||
|
||||
from PyQt5.QtCore import (
|
||||
QCoreApplication
|
||||
QCoreApplication, Qt,
|
||||
)
|
||||
from PyQt5.QtWidgets import QApplication
|
||||
|
||||
from matplotlib.collections import LineCollection
|
||||
|
||||
_translate = QCoreApplication.translate
|
||||
|
||||
|
||||
class PlotKPC(PamhyrPlot):
|
||||
class PlotRKC(PamhyrPlot):
|
||||
def __init__(self, canvas=None, trad=None, toolbar=None,
|
||||
river=None, reach=None, profile=None,
|
||||
parent=None):
|
||||
super(PlotKPC, self).__init__(
|
||||
super(PlotRKC, self).__init__(
|
||||
canvas=canvas,
|
||||
trad=trad,
|
||||
data=river,
|
||||
@@ -43,13 +44,14 @@ class PlotKPC(PamhyrPlot):
|
||||
self._current_reach = reach
|
||||
self._current_profile = profile
|
||||
|
||||
self.label_x = self._trad["unit_kp"]
|
||||
self.label_x = self._trad["unit_rk"]
|
||||
self.label_y = self._trad["unit_elevation"]
|
||||
|
||||
self._isometric_axis = False
|
||||
|
||||
self._auto_relim_update = True
|
||||
self._autoscale_update = True
|
||||
self.parent = parent
|
||||
|
||||
@property
|
||||
def river(self):
|
||||
@@ -65,14 +67,14 @@ class PlotKPC(PamhyrPlot):
|
||||
|
||||
if self.data is None:
|
||||
self.profile = None
|
||||
self.line_kp_zmin_zmax = None
|
||||
self.line_kp_zmin = None
|
||||
self.line_rk_zmin_zmax = None
|
||||
self.line_rk_zmin = None
|
||||
return
|
||||
|
||||
if self._current_reach is None:
|
||||
self.profile = None
|
||||
self.line_kp_zmin_zmax = None
|
||||
self.line_kp_zmin = None
|
||||
self.line_rk_zmin_zmax = None
|
||||
self.line_rk_zmin = None
|
||||
return
|
||||
|
||||
self.draw_data()
|
||||
@@ -84,35 +86,43 @@ class PlotKPC(PamhyrPlot):
|
||||
def draw_data(self):
|
||||
reach = self._current_reach
|
||||
|
||||
kp = reach.reach.get_kp()
|
||||
rk = reach.reach.get_rk()
|
||||
z_min = reach.reach.get_z_min()
|
||||
z_max = reach.reach.get_z_max()
|
||||
|
||||
self.line_kp_zmin, = self.canvas.axes.plot(
|
||||
kp, z_min,
|
||||
self.line_rk_zmin, = self.canvas.axes.plot(
|
||||
rk, z_min,
|
||||
color=self.color_plot_river_bottom,
|
||||
lw=1.
|
||||
lw=1.,
|
||||
)
|
||||
|
||||
if len(kp) != 0:
|
||||
self.line_kp_zmin_zmax = self.canvas.axes.vlines(
|
||||
x=kp,
|
||||
self.line_rk_zmax, = self.canvas.axes.plot(
|
||||
rk, z_max,
|
||||
color=self.color_plot_river_bottom,
|
||||
lw=1.,
|
||||
alpha=0.0
|
||||
)
|
||||
|
||||
if len(rk) != 0:
|
||||
self.line_rk_zmin_zmax = self.canvas.axes.vlines(
|
||||
x=rk,
|
||||
ymin=z_min, ymax=z_max,
|
||||
color=self.color_plot,
|
||||
lw=1.
|
||||
lw=1.,
|
||||
picker=10
|
||||
)
|
||||
|
||||
def draw_current(self):
|
||||
if self._current_profile is None:
|
||||
self.profile = None
|
||||
else:
|
||||
kp = [self._current_profile.kp,
|
||||
self._current_profile.kp]
|
||||
rk = [self._current_profile.rk,
|
||||
self._current_profile.rk]
|
||||
min_max = [self._current_profile.z_min(),
|
||||
self._current_profile.z_max()]
|
||||
|
||||
self.profile = self.canvas.axes.plot(
|
||||
kp, min_max,
|
||||
rk, min_max,
|
||||
color=self.color_plot_current,
|
||||
lw=1.
|
||||
)
|
||||
@@ -131,7 +141,7 @@ class PlotKPC(PamhyrPlot):
|
||||
|
||||
def update_current_profile(self):
|
||||
reach = self._current_reach
|
||||
kp = reach.reach.get_kp()
|
||||
rk = reach.reach.get_rk()
|
||||
z_min = reach.reach.get_z_min()
|
||||
z_max = reach.reach.get_z_max()
|
||||
|
||||
@@ -139,7 +149,7 @@ class PlotKPC(PamhyrPlot):
|
||||
self.draw()
|
||||
else:
|
||||
self.profile.set_data(
|
||||
[self._current_profile.kp, self._current_profile.kp],
|
||||
[self._current_profile.rk, self._current_profile.rk],
|
||||
[self._current_profile.z_min(), self._current_profile.z_max()],
|
||||
)
|
||||
|
||||
@@ -149,12 +159,12 @@ class PlotKPC(PamhyrPlot):
|
||||
if self.profile is not None:
|
||||
self.profile[0].set_data([], [])
|
||||
|
||||
if self.line_kp_zmin_zmax is not None:
|
||||
self.line_kp_zmin_zmax.remove()
|
||||
self.line_kp_zmin_zmax = None
|
||||
if self.line_rk_zmin_zmax is not None:
|
||||
self.line_rk_zmin_zmax.remove()
|
||||
self.line_rk_zmin_zmax = None
|
||||
|
||||
if self.line_kp_zmin is not None:
|
||||
self.line_kp_zmin.set_data([], [])
|
||||
if self.line_rk_zmin is not None:
|
||||
self.line_rk_zmin.set_data([], [])
|
||||
|
||||
self.canvas.figure.canvas.draw_idle()
|
||||
|
||||
@@ -163,3 +173,40 @@ class PlotKPC(PamhyrPlot):
|
||||
self.profile.set_data([], [])
|
||||
|
||||
self.canvas.figure.canvas.draw_idle()
|
||||
|
||||
def onpick(self, event):
|
||||
if event.mouseevent.inaxes != self.canvas.axes:
|
||||
return
|
||||
if event.mouseevent.button.value != 1:
|
||||
return
|
||||
|
||||
modifiers = QApplication.keyboardModifiers()
|
||||
if modifiers not in [Qt.ControlModifier,
|
||||
Qt.NoModifier,
|
||||
Qt.ShiftModifier]:
|
||||
return
|
||||
|
||||
closest = self._closest_profile(event)
|
||||
index = self.parent.tableView.selectedIndexes()
|
||||
if self.parent._table is not None:
|
||||
self.parent._table.setData(index[2], closest)
|
||||
|
||||
return
|
||||
|
||||
def _closest_profile(self, event):
|
||||
|
||||
s = event.artist.get_segments()
|
||||
x = [i[0, 0] for i in s]
|
||||
mx = event.mouseevent.xdata
|
||||
points = enumerate(x)
|
||||
|
||||
def dist_mouse(point):
|
||||
x = point[1]
|
||||
d = abs(mx - x)
|
||||
return d
|
||||
|
||||
closest = min(
|
||||
points, key=dist_mouse
|
||||
)
|
||||
|
||||
return closest[1]
|
||||
@@ -37,7 +37,7 @@ from PyQt5.QtWidgets import (
|
||||
from View.Tools.PamhyrTable import PamhyrTableModel
|
||||
|
||||
from View.HydraulicStructures.UndoCommand import (
|
||||
SetNameCommand, SetReachCommand, SetKpCommand,
|
||||
SetNameCommand, SetReachCommand, SetRkCommand,
|
||||
SetEnabledCommand, AddCommand, DelCommand,
|
||||
)
|
||||
|
||||
@@ -58,14 +58,14 @@ class ComboBoxDelegate(QItemDelegate):
|
||||
self.editor = QComboBox(parent)
|
||||
|
||||
val = []
|
||||
if self._mode == "kp":
|
||||
if self._mode == "rk":
|
||||
reach = self._data.hydraulic_structures\
|
||||
.get(index.row())\
|
||||
.input_reach
|
||||
if reach is not None:
|
||||
val = list(
|
||||
map(
|
||||
lambda kp: str(kp), reach.reach.get_kp()
|
||||
lambda rk: str(rk), reach.reach.get_rk()
|
||||
)
|
||||
)
|
||||
else:
|
||||
@@ -126,8 +126,8 @@ class TableModel(PamhyrTableModel):
|
||||
if n is None:
|
||||
return self._trad['not_associated']
|
||||
return n.name
|
||||
elif self._headers[column] == "kp":
|
||||
n = self._lst.get(row).input_kp
|
||||
elif self._headers[column] == "rk":
|
||||
n = self._lst.get(row).input_rk
|
||||
if n is None:
|
||||
return self._trad['not_associated']
|
||||
return n
|
||||
@@ -158,12 +158,12 @@ class TableModel(PamhyrTableModel):
|
||||
self._lst, row, self._data.edge(value)
|
||||
)
|
||||
)
|
||||
elif self._headers[column] == "kp":
|
||||
elif self._headers[column] == "rk":
|
||||
if value == na:
|
||||
value = None
|
||||
|
||||
self._undo.push(
|
||||
SetKpCommand(
|
||||
SetRkCommand(
|
||||
self._lst, row, value
|
||||
)
|
||||
)
|
||||
|
||||
@@ -36,5 +36,5 @@ class HydraulicStructuresTranslate(MainTranslate):
|
||||
self._sub_dict["table_headers"] = {
|
||||
"name": self._dict["name"],
|
||||
"reach": self._dict["reach"],
|
||||
"kp": self._dict["unit_kp"],
|
||||
"rk": self._dict["unit_rk"],
|
||||
}
|
||||
@@ -52,34 +52,34 @@ class SetReachCommand(QUndoCommand):
|
||||
self._index = index
|
||||
self._old = self._h_s_lst.get(self._index).input_reach
|
||||
self._new = reach
|
||||
self._old_kp = self._h_s_lst.get(self._index).input_kp
|
||||
self._new_kp = None
|
||||
self._old_rk = self._h_s_lst.get(self._index).input_rk
|
||||
self._new_rk = None
|
||||
|
||||
def undo(self):
|
||||
i = self._h_s_lst.get(self._index)
|
||||
i.input_reach = self._old
|
||||
i.input_kp = self._old_kp
|
||||
i.input_rk = self._old_rk
|
||||
|
||||
def redo(self):
|
||||
i = self._h_s_lst.get(self._index)
|
||||
i.input_reach = self._new
|
||||
i.input_kp = self._new_kp
|
||||
i.input_rk = self._new_rk
|
||||
|
||||
|
||||
class SetKpCommand(QUndoCommand):
|
||||
def __init__(self, h_s_lst, index, kp):
|
||||
class SetRkCommand(QUndoCommand):
|
||||
def __init__(self, h_s_lst, index, rk):
|
||||
QUndoCommand.__init__(self)
|
||||
|
||||
self._h_s_lst = h_s_lst
|
||||
self._index = index
|
||||
self._old = self._h_s_lst.get(self._index).input_kp
|
||||
self._new = kp
|
||||
self._old = self._h_s_lst.get(self._index).input_rk
|
||||
self._new = rk
|
||||
|
||||
def undo(self):
|
||||
self._h_s_lst.get(self._index).input_kp = self._old
|
||||
self._h_s_lst.get(self._index).input_rk = self._old
|
||||
|
||||
def redo(self):
|
||||
self._h_s_lst.get(self._index).input_kp = self._new
|
||||
self._h_s_lst.get(self._index).input_rk = self._new
|
||||
|
||||
|
||||
class SetEnabledCommand(QUndoCommand):
|
||||
|
||||
@@ -39,7 +39,7 @@ from View.Tools.Plot.PamhyrCanvas import MplCanvas
|
||||
from View.Tools.Plot.PamhyrToolbar import PamhyrPlotToolbar
|
||||
|
||||
from View.HydraulicStructures.PlotAC import PlotAC
|
||||
from View.HydraulicStructures.PlotKPC import PlotKPC
|
||||
from View.HydraulicStructures.PlotRKC import PlotRKC
|
||||
|
||||
from View.HydraulicStructures.Table import (
|
||||
TableModel, ComboBoxDelegate
|
||||
@@ -89,21 +89,21 @@ class HydraulicStructuresWindow(PamhyrWindow):
|
||||
parent=self,
|
||||
mode="reaches"
|
||||
)
|
||||
self._delegate_kp = ComboBoxDelegate(
|
||||
self._delegate_rk = ComboBoxDelegate(
|
||||
trad=self._trad,
|
||||
data=self._study.river,
|
||||
parent=self,
|
||||
mode="kp"
|
||||
mode="rk"
|
||||
)
|
||||
|
||||
table = self.find(QTableView, f"tableView")
|
||||
self._table = TableModel(
|
||||
table_view=table,
|
||||
table_headers=self._trad.get_dict("table_headers"),
|
||||
editable_headers=["name", "reach", "kp"],
|
||||
editable_headers=["name", "reach", "rk"],
|
||||
delegates={
|
||||
"reach": self._delegate_reach,
|
||||
"kp": self._delegate_kp,
|
||||
"rk": self._delegate_rk,
|
||||
},
|
||||
trad=self._trad,
|
||||
data=self._study.river,
|
||||
@@ -135,15 +135,16 @@ class HydraulicStructuresWindow(PamhyrWindow):
|
||||
self.plot_layout.addWidget(self.toolbar)
|
||||
self.plot_layout.addWidget(self.canvas)
|
||||
|
||||
self.plot_kpc = PlotKPC(
|
||||
self.plot_rkc = PlotRKC(
|
||||
canvas=self.canvas,
|
||||
river=self._study.river,
|
||||
reach=None,
|
||||
profile=None,
|
||||
trad=self._trad,
|
||||
toolbar=self.toolbar
|
||||
toolbar=self.toolbar,
|
||||
parent=self
|
||||
)
|
||||
self.plot_kpc.draw()
|
||||
self.plot_rkc.draw()
|
||||
|
||||
self.canvas_2 = MplCanvas(width=5, height=4, dpi=100)
|
||||
self.canvas_2.setObjectName("canvas_2")
|
||||
@@ -262,7 +263,9 @@ class HydraulicStructuresWindow(PamhyrWindow):
|
||||
self._checkbox.setChecked(self._hs_lst.get(row).enabled)
|
||||
|
||||
def _set_structure_state(self):
|
||||
row = self.index_selected_row()
|
||||
rows = self.index_selected_rows()
|
||||
if len(rows) != 0:
|
||||
for row in rows:
|
||||
if row is not None:
|
||||
self._table.enabled(
|
||||
row,
|
||||
@@ -282,20 +285,20 @@ class HydraulicStructuresWindow(PamhyrWindow):
|
||||
|
||||
reach = self._hs_lst.get(rows[0]).input_reach
|
||||
if reach is not None:
|
||||
self.plot_kpc.set_reach(reach)
|
||||
self.plot_rkc.set_reach(reach)
|
||||
self.plot_ac.set_reach(reach)
|
||||
|
||||
profile_kp = self._hs_lst.get(rows[0]).input_kp
|
||||
if profile_kp is not None:
|
||||
profile_rk = self._hs_lst.get(rows[0]).input_rk
|
||||
if profile_rk is not None:
|
||||
profiles = reach.reach\
|
||||
.get_profiles_from_kp(
|
||||
float(profile_kp)
|
||||
.get_profiles_from_rk(
|
||||
float(profile_rk)
|
||||
)
|
||||
|
||||
if len(profiles) != 0 and profiles is not None:
|
||||
profile = profiles[0]
|
||||
|
||||
self.plot_kpc.set_profile(profile)
|
||||
self.plot_rkc.set_profile(profile)
|
||||
self.plot_ac.set_profile(profile)
|
||||
else:
|
||||
self._update_clear_profile()
|
||||
@@ -305,9 +308,9 @@ class HydraulicStructuresWindow(PamhyrWindow):
|
||||
self._update_clear_all()
|
||||
|
||||
def _update_clear_all(self):
|
||||
self.plot_kpc.clear()
|
||||
self.plot_rkc.clear()
|
||||
self.plot_ac.clear()
|
||||
|
||||
def _update_clear_profile(self):
|
||||
self.plot_ac.clear()
|
||||
self.plot_kpc.clear_profile()
|
||||
self.plot_rkc.clear_profile()
|
||||
@@ -1,4 +1,4 @@
|
||||
# PlotDKP.py -- Pamhyr
|
||||
# PlotDRK.py -- Pamhyr
|
||||
# Copyright (C) 2023-2024 INRAE
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
@@ -30,10 +30,10 @@ logger = logging.getLogger()
|
||||
_translate = QCoreApplication.translate
|
||||
|
||||
|
||||
class PlotDKP(PamhyrPlot):
|
||||
class PlotDRK(PamhyrPlot):
|
||||
def __init__(self, canvas=None, trad=None, toolbar=None,
|
||||
data=None, parent=None):
|
||||
super(PlotDKP, self).__init__(
|
||||
super(PlotDRK, self).__init__(
|
||||
canvas=canvas,
|
||||
trad=trad,
|
||||
data=data,
|
||||
@@ -41,7 +41,7 @@ class PlotDKP(PamhyrPlot):
|
||||
parent=parent
|
||||
)
|
||||
|
||||
self.label_x = self._trad["kp"]
|
||||
self.label_x = self._trad["rk"]
|
||||
self.label_y = self._trad["elevation"]
|
||||
|
||||
self._isometric_axis = False
|
||||
@@ -63,41 +63,41 @@ class PlotDKP(PamhyrPlot):
|
||||
self._init = True
|
||||
|
||||
def draw_river_bottom(self):
|
||||
kp = self.data.reach.reach.get_kp()
|
||||
rk = self.data.reach.reach.get_rk()
|
||||
z_min = self.data.reach.reach.get_z_min()
|
||||
|
||||
self.line_kp_zmin = self.canvas.axes.plot(
|
||||
kp, z_min,
|
||||
self.line_rk_zmin = self.canvas.axes.plot(
|
||||
rk, z_min,
|
||||
color=self.color_plot_river_bottom,
|
||||
lw=1.
|
||||
)
|
||||
|
||||
def draw_water(self):
|
||||
if len(self.data) != 0:
|
||||
kp = self.data.get_kp()
|
||||
rk = self.data.get_rk()
|
||||
elevation = self.data.get_elevation()
|
||||
|
||||
self.line_kp_elevation = self.canvas.axes.plot(
|
||||
kp, elevation,
|
||||
self.line_rk_elevation = self.canvas.axes.plot(
|
||||
rk, elevation,
|
||||
color=self.color_plot_river_water,
|
||||
**self.plot_default_kargs
|
||||
)
|
||||
|
||||
z_min = self.data.reach.reach.get_z_min()
|
||||
geometry_kp = self.data.reach.reach.get_kp()
|
||||
geometry_rk = self.data.reach.reach.get_rk()
|
||||
|
||||
filtred_elevation = list(
|
||||
map(
|
||||
lambda x: elevation[x[0]],
|
||||
filter(
|
||||
lambda x: x[1] in geometry_kp,
|
||||
enumerate(kp)
|
||||
lambda x: x[1] in geometry_rk,
|
||||
enumerate(rk)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
self.collection = self.canvas.axes.fill_between(
|
||||
geometry_kp, z_min, filtred_elevation,
|
||||
geometry_rk, z_min, filtred_elevation,
|
||||
color=self.color_plot_river_water_zone,
|
||||
alpha=0.7, interpolate=True
|
||||
)
|
||||
@@ -31,7 +31,7 @@ class PlotDischarge(PamhyrPlot):
|
||||
parent=parent
|
||||
)
|
||||
|
||||
self.label_x = self._trad["kp"]
|
||||
self.label_x = self._trad["rk"]
|
||||
self.label_y = self._trad["discharge"]
|
||||
|
||||
self._isometric_axis = False
|
||||
@@ -52,18 +52,41 @@ class PlotDischarge(PamhyrPlot):
|
||||
self._init = True
|
||||
|
||||
def draw_data(self):
|
||||
kp = self.data.reach.reach.get_kp()
|
||||
self.line_discharge = []
|
||||
|
||||
if len(self.data) != 0:
|
||||
kp = self.data.get_kp()
|
||||
rk = self.data.get_rk()
|
||||
discharge = self.data.get_discharge()
|
||||
|
||||
self.line_kp_zmin = self.canvas.axes.plot(
|
||||
kp, discharge,
|
||||
line, = self.canvas.axes.plot(
|
||||
rk, discharge,
|
||||
color=self.color_plot,
|
||||
**self.plot_default_kargs
|
||||
)
|
||||
self.line_discharge.append(line)
|
||||
|
||||
@timer
|
||||
def update(self, ind=None):
|
||||
if not self._init:
|
||||
self.draw()
|
||||
|
||||
self.update_data()
|
||||
|
||||
self.update_idle()
|
||||
|
||||
def update_data(self):
|
||||
if len(self.data) == len(self.line_discharge):
|
||||
rk = self.data.get_rk()
|
||||
discharge = self.data.get_discharge()
|
||||
|
||||
line, = self.canvas.axes.plot(
|
||||
rk, discharge,
|
||||
color=self.color_plot,
|
||||
**self.plot_default_kargs
|
||||
)
|
||||
self.line_discharge.append(line)
|
||||
else:
|
||||
for line in self.line_discharge:
|
||||
line.remove()
|
||||
|
||||
self._draw_data()
|
||||
@@ -37,7 +37,7 @@ from View.Tools.PamhyrTable import PamhyrTableModel
|
||||
|
||||
from View.InitialConditions.UndoCommand import (
|
||||
SetCommand, AddCommand, DelCommand,
|
||||
SortCommand, MoveCommand, PasteCommand,
|
||||
SortCommand, MoveCommand, InsertCommand,
|
||||
DuplicateCommand, GenerateCommand,
|
||||
)
|
||||
|
||||
@@ -59,7 +59,7 @@ class ComboBoxDelegate(QItemDelegate):
|
||||
list(
|
||||
map(
|
||||
str,
|
||||
self._reach.get_kp()
|
||||
self._reach.get_rk()
|
||||
)
|
||||
)
|
||||
)
|
||||
@@ -104,7 +104,18 @@ class InitialConditionTableModel(PamhyrTableModel):
|
||||
row = index.row()
|
||||
column = index.column()
|
||||
|
||||
if self._headers[column] not in ["name", "comment"]:
|
||||
if self._headers[column] is "speed":
|
||||
z = self._lst.get(row)["elevation"]
|
||||
q = self._lst.get(row)["discharge"]
|
||||
profile = self._reach.reach.get_profiles_from_rk(
|
||||
self._lst.get(row)["rk"]
|
||||
)
|
||||
if len(profile) >= 1:
|
||||
speed = profile[0].speed(q, z)
|
||||
return f"{speed:.4f}"
|
||||
|
||||
return ""
|
||||
elif self._headers[column] not in ["name", "comment"]:
|
||||
v = self._lst.get(row)[self._headers[column]]
|
||||
return f"{v:.4f}"
|
||||
else:
|
||||
@@ -186,7 +197,7 @@ class InitialConditionTableModel(PamhyrTableModel):
|
||||
self.endMoveRows()
|
||||
self.layoutChanged.emit()
|
||||
|
||||
def move_down(self, index, parent=QModelIndex()):
|
||||
def move_down(self, row, parent=QModelIndex()):
|
||||
if row > len(self._lst):
|
||||
return
|
||||
|
||||
@@ -203,6 +214,73 @@ class InitialConditionTableModel(PamhyrTableModel):
|
||||
self.endMoveRows()
|
||||
self.layoutChanged.emit()
|
||||
|
||||
def paste(self, index, header, data):
|
||||
if len(header) != 0:
|
||||
logger.error("Unexpected header in IC past data")
|
||||
return
|
||||
|
||||
if len(data) == 0:
|
||||
logger.error("Empty data")
|
||||
return
|
||||
|
||||
if len(data[0]) != 3:
|
||||
logger.error(f"Unexpected data size: [{data[0]}, ...]")
|
||||
return
|
||||
|
||||
self.layoutAboutToBeChanged.emit()
|
||||
|
||||
self._undo.push(
|
||||
InsertCommand(
|
||||
self._lst, index,
|
||||
list(
|
||||
map(
|
||||
lambda d: self._lst.new_from_data(*d),
|
||||
data
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
self.layoutAboutToBeChanged.emit()
|
||||
self.layoutChanged.emit()
|
||||
|
||||
def import_from_results(self, index, results):
|
||||
if results is None:
|
||||
logger.error("No results data")
|
||||
return
|
||||
|
||||
self.layoutAboutToBeChanged.emit()
|
||||
|
||||
ts = max(results.get("timestamps"))
|
||||
res_reach = results.river.get_reach_by_geometry(
|
||||
self._reach.reach
|
||||
)
|
||||
data = list(
|
||||
map(
|
||||
lambda p: [
|
||||
p.geometry.rk,
|
||||
p.get_ts_key(ts, "Q"),
|
||||
p.get_ts_key(ts, "Z"),
|
||||
],
|
||||
res_reach.profiles
|
||||
)
|
||||
)
|
||||
|
||||
self._undo.push(
|
||||
InsertCommand(
|
||||
self._lst, index,
|
||||
list(
|
||||
map(
|
||||
lambda d: self._lst.new_from_data(*d),
|
||||
data
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
self.layoutAboutToBeChanged.emit()
|
||||
self.layoutChanged.emit()
|
||||
|
||||
def undo(self):
|
||||
self._undo.undo()
|
||||
self.layoutChanged.emit()
|
||||
|
||||
@@ -106,7 +106,7 @@ class SortCommand(QUndoCommand):
|
||||
def redo(self):
|
||||
self._ics.sort(
|
||||
reverse=self._reverse,
|
||||
key=lambda x: x["kp"]
|
||||
key=lambda x: x["rk"]
|
||||
)
|
||||
if self._indexes is None:
|
||||
self._indexes = list(
|
||||
@@ -139,7 +139,7 @@ class MoveCommand(QUndoCommand):
|
||||
self._ics.move_down(self._i)
|
||||
|
||||
|
||||
class PasteCommand(QUndoCommand):
|
||||
class InsertCommand(QUndoCommand):
|
||||
def __init__(self, ics, row, ic):
|
||||
QUndoCommand.__init__(self)
|
||||
|
||||
|
||||
@@ -16,9 +16,10 @@
|
||||
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import os
|
||||
import logging
|
||||
|
||||
from tools import trace, timer
|
||||
from tools import trace, timer, logger_exception
|
||||
|
||||
from View.Tools.PamhyrWindow import PamhyrWindow
|
||||
|
||||
@@ -43,7 +44,7 @@ from Modules import Modules
|
||||
|
||||
from View.InitialConditions.UndoCommand import (
|
||||
SetCommand, AddCommand, DelCommand,
|
||||
SortCommand, MoveCommand, PasteCommand,
|
||||
SortCommand, MoveCommand, InsertCommand,
|
||||
DuplicateCommand,
|
||||
)
|
||||
|
||||
@@ -54,11 +55,14 @@ from View.InitialConditions.Table import (
|
||||
from View.Tools.Plot.PamhyrCanvas import MplCanvas
|
||||
from View.Tools.Plot.PamhyrToolbar import PamhyrPlotToolbar
|
||||
|
||||
from View.InitialConditions.PlotDKP import PlotDKP
|
||||
from View.InitialConditions.PlotDRK import PlotDRK
|
||||
from View.InitialConditions.PlotDischarge import PlotDischarge
|
||||
from View.InitialConditions.translate import ICTranslate
|
||||
from View.InitialConditions.DialogHeight import HeightDialog
|
||||
from View.InitialConditions.DialogDischarge import DischargeDialog
|
||||
from View.Results.ReadingResultsDialog import ReadingResultsDialog
|
||||
|
||||
from Solver.Mage import Mage8
|
||||
|
||||
_translate = QCoreApplication.translate
|
||||
|
||||
@@ -104,7 +108,7 @@ class InitialConditionsWindow(PamhyrWindow):
|
||||
|
||||
def setup_table(self):
|
||||
table = self.find(QTableView, f"tableView")
|
||||
self._delegate_kp = ComboBoxDelegate(
|
||||
self._delegate_rk = ComboBoxDelegate(
|
||||
reach=self._reach,
|
||||
parent=self
|
||||
)
|
||||
@@ -113,8 +117,8 @@ class InitialConditionsWindow(PamhyrWindow):
|
||||
reach=self._reach,
|
||||
table_view=table,
|
||||
table_headers=self._trad.get_dict("table_headers"),
|
||||
editable_headers=["kp", "discharge", "elevation", "height"],
|
||||
delegates={"kp": self._delegate_kp},
|
||||
editable_headers=["rk", "discharge", "elevation", "height"],
|
||||
delegates={"rk": self._delegate_rk},
|
||||
data=self._study,
|
||||
undo=self._undo_stack,
|
||||
trad=self._trad
|
||||
@@ -135,7 +139,7 @@ class InitialConditionsWindow(PamhyrWindow):
|
||||
self.plot_layout_1.addWidget(self.toolbar_1)
|
||||
self.plot_layout_1.addWidget(self.canvas_1)
|
||||
|
||||
self.plot_1 = PlotDKP(
|
||||
self.plot_1 = PlotDRK(
|
||||
canvas=self.canvas_1,
|
||||
data=self._ics,
|
||||
trad=self._trad,
|
||||
@@ -166,6 +170,8 @@ class InitialConditionsWindow(PamhyrWindow):
|
||||
self.find(QAction, "action_add").triggered.connect(self.add)
|
||||
self.find(QAction, "action_del").triggered.connect(self.delete)
|
||||
self.find(QAction, "action_sort").triggered.connect(self.sort)
|
||||
self.find(QAction, "action_import").triggered\
|
||||
.connect(self.import_from_file)
|
||||
|
||||
self.find(QPushButton, "pushButton_generate_1").clicked.connect(
|
||||
self.generate_growing_constante_height
|
||||
@@ -179,12 +185,21 @@ class InitialConditionsWindow(PamhyrWindow):
|
||||
|
||||
def index_selected_row(self):
|
||||
table = self.find(QTableView, f"tableView")
|
||||
return table.selectionModel()\
|
||||
.selectedRows()[0]\
|
||||
.row()
|
||||
rows = table.selectionModel()\
|
||||
.selectedRows()
|
||||
|
||||
if len(rows) == 0:
|
||||
return 0
|
||||
|
||||
return rows[0].row()
|
||||
|
||||
def update(self):
|
||||
self.update(propagate=False)
|
||||
|
||||
def _update(self, propagate=True):
|
||||
self._update_plot()
|
||||
|
||||
if propagate:
|
||||
self._propagate_update(key=Modules.INITIAL_CONDITION)
|
||||
|
||||
def _update_plot(self):
|
||||
@@ -230,6 +245,42 @@ class InitialConditionsWindow(PamhyrWindow):
|
||||
self._table.sort(False)
|
||||
self._update()
|
||||
|
||||
def import_from_file(self):
|
||||
workdir = os.path.dirname(self._study.filename)
|
||||
|
||||
return self.file_dialog(
|
||||
callback=lambda d: self._import_from_file(d[0]),
|
||||
directory=workdir,
|
||||
default_suffix=".BIN",
|
||||
file_filter=["Mage (*.BIN)"],
|
||||
)
|
||||
|
||||
def _import_from_file(self, file_name):
|
||||
solver = Mage8("dummy")
|
||||
name = os.path.basename(file_name)\
|
||||
.replace(".BIN", "")
|
||||
|
||||
def reading():
|
||||
self._tmp_results = solver.results(
|
||||
self._study,
|
||||
os.path.dirname(file_name),
|
||||
name=name
|
||||
)
|
||||
|
||||
dlg = ReadingResultsDialog(
|
||||
reading_fn=reading,
|
||||
parent=self
|
||||
)
|
||||
dlg.exec_()
|
||||
results = self._tmp_results
|
||||
self._import_from_results(results)
|
||||
|
||||
def _import_from_results(self, results):
|
||||
logger.debug(f"import from results: {results}")
|
||||
row = self.index_selected_row()
|
||||
|
||||
self._table.import_from_results(row, results)
|
||||
|
||||
def move_up(self):
|
||||
row = self.index_selected_row()
|
||||
self._table.move_up(row)
|
||||
@@ -241,11 +292,49 @@ class InitialConditionsWindow(PamhyrWindow):
|
||||
self._update()
|
||||
|
||||
def _copy(self):
|
||||
logger.info("TODO: copy")
|
||||
self._update()
|
||||
rows = list(
|
||||
map(
|
||||
lambda row: row.row(),
|
||||
self.tableView.selectionModel().selectedRows()
|
||||
)
|
||||
)
|
||||
|
||||
table = list(
|
||||
map(
|
||||
lambda eic: list(
|
||||
map(
|
||||
lambda k: eic[1][k],
|
||||
["rk", "discharge", "elevation"]
|
||||
)
|
||||
),
|
||||
filter(
|
||||
lambda eic: eic[0] in rows,
|
||||
enumerate(self._ics.lst())
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
self.copyTableIntoClipboard(table)
|
||||
|
||||
def _paste(self):
|
||||
logger.info("TODO: paste")
|
||||
header, data = self.parseClipboardTable()
|
||||
|
||||
if len(data) + len(header) == 0:
|
||||
return
|
||||
|
||||
logger.debug(
|
||||
"IC: Paste: " +
|
||||
f"header = {header}, " +
|
||||
f"data = {data}"
|
||||
)
|
||||
|
||||
try:
|
||||
row = self.index_selected_row()
|
||||
# self._table.paste(row, header, data)
|
||||
self._table.paste(row, [], data)
|
||||
except Exception as e:
|
||||
logger_exception(e)
|
||||
|
||||
self._update()
|
||||
|
||||
def _undo(self):
|
||||
|
||||
@@ -36,14 +36,14 @@ class ICTranslate(MainTranslate):
|
||||
|
||||
self._dict["elevation"] = self._dict["unit_elevation"]
|
||||
self._dict["discharge"] = self._dict["unit_discharge"]
|
||||
self._dict["kp"] = self._dict["unit_kp"]
|
||||
self._dict["rk"] = self._dict["unit_rk"]
|
||||
|
||||
self._sub_dict["table_headers"] = {
|
||||
# "name": _translate("InitialCondition", "Name"),
|
||||
"kp": self._dict["unit_kp"],
|
||||
# "speed": _translate("InitialCondition", "Speed (m/s)"),
|
||||
"rk": self._dict["unit_rk"],
|
||||
"discharge": self._dict["unit_discharge"],
|
||||
"elevation": self._dict["unit_elevation"],
|
||||
"height": self._dict["unit_height"],
|
||||
"speed": self._dict["unit_speed"],
|
||||
# "comment": _translate("InitialCondition", "Comment"),
|
||||
}
|
||||
@@ -99,7 +99,7 @@ class TableModel(PamhyrTableModel):
|
||||
logger.info(e)
|
||||
logger.debug(traceback.format_exc())
|
||||
|
||||
self.dataChanged.emit(index, index)
|
||||
self.update()
|
||||
return True
|
||||
|
||||
def add(self, row, parent=QModelIndex()):
|
||||
@@ -112,7 +112,7 @@ class TableModel(PamhyrTableModel):
|
||||
)
|
||||
|
||||
self.endInsertRows()
|
||||
self.layoutChanged.emit()
|
||||
self.update()
|
||||
|
||||
def delete(self, rows, parent=QModelIndex()):
|
||||
self.beginRemoveRows(parent, rows[0], rows[-1])
|
||||
@@ -135,7 +135,7 @@ class TableModel(PamhyrTableModel):
|
||||
)
|
||||
|
||||
self.layoutAboutToBeChanged.emit()
|
||||
self.layoutChanged.emit()
|
||||
self.update()
|
||||
|
||||
def move_up(self, row, parent=QModelIndex()):
|
||||
if row <= 0:
|
||||
@@ -152,7 +152,7 @@ class TableModel(PamhyrTableModel):
|
||||
)
|
||||
|
||||
self.endMoveRows()
|
||||
self.layoutChanged.emit()
|
||||
self.update()
|
||||
|
||||
def move_down(self, index, parent=QModelIndex()):
|
||||
if row > len(self._data):
|
||||
@@ -169,7 +169,7 @@ class TableModel(PamhyrTableModel):
|
||||
)
|
||||
|
||||
self.endMoveRows()
|
||||
self.layoutChanged.emit()
|
||||
self.update()
|
||||
|
||||
def paste(self, row, header, data):
|
||||
if len(data) == 0:
|
||||
@@ -190,4 +190,13 @@ class TableModel(PamhyrTableModel):
|
||||
)
|
||||
|
||||
self.layoutAboutToBeChanged.emit()
|
||||
self.update()
|
||||
|
||||
def auto_sort(self):
|
||||
self.layoutAboutToBeChanged.emit()
|
||||
self._data.sort(key=lambda x: x[0])
|
||||
self.layoutAboutToBeChanged.emit()
|
||||
|
||||
def update(self):
|
||||
# self.auto_sort()
|
||||
self.layoutChanged.emit()
|
||||
@@ -137,6 +137,7 @@ class EditLateralContributionWindow(PamhyrWindow):
|
||||
self.find(QAction, "action_sort").triggered.connect(self.sort)
|
||||
|
||||
self._table.dataChanged.connect(self.update)
|
||||
self._table.layoutChanged.connect(self.update)
|
||||
|
||||
def update(self):
|
||||
self.plot.update()
|
||||
|
||||
@@ -28,7 +28,7 @@ class LCETranslate(LCTranslate):
|
||||
def __init__(self):
|
||||
super(LCETranslate, self).__init__()
|
||||
self._dict["Edit lateral contribution"] = _translate(
|
||||
"LateralContribution", "Edit lateral contribution"
|
||||
"LateralContribution", "Edit lateral sources"
|
||||
)
|
||||
|
||||
self._sub_dict["table_headers"] = {
|
||||
|
||||
@@ -0,0 +1,260 @@
|
||||
# PlotXY.py -- Pamhyr
|
||||
# Copyright (C) 2023-2024 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 -*-
|
||||
|
||||
from tools import timer, trace
|
||||
from View.Tools.PamhyrPlot import PamhyrPlot
|
||||
from matplotlib import collections
|
||||
import numpy as np
|
||||
|
||||
from PyQt5.QtCore import (
|
||||
QCoreApplication, Qt, QItemSelectionModel,
|
||||
QItemSelection, QItemSelectionRange,
|
||||
)
|
||||
from PyQt5.QtWidgets import QApplication, QTableView
|
||||
|
||||
_translate = QCoreApplication.translate
|
||||
|
||||
|
||||
class PlotXY(PamhyrPlot):
|
||||
def __init__(self, canvas=None, trad=None, data=None, toolbar=None,
|
||||
table=None, parent=None):
|
||||
super(PlotXY, self).__init__(
|
||||
canvas=canvas,
|
||||
trad=trad,
|
||||
data=data,
|
||||
toolbar=toolbar,
|
||||
table=table,
|
||||
parent=parent
|
||||
)
|
||||
|
||||
self.line_xy = []
|
||||
self.line_gl = []
|
||||
|
||||
self.label_x = self._trad["x"]
|
||||
self.label_y = self._trad["y"]
|
||||
|
||||
self.before_plot_selected = None
|
||||
self.plot_selected = None
|
||||
self.after_plot_selected = None
|
||||
self.parent = parent
|
||||
self.line_xy_collection = None
|
||||
self._table = table
|
||||
self._colors = []
|
||||
self._style = []
|
||||
|
||||
def onpick(self, event):
|
||||
if event.mouseevent.inaxes != self.canvas.axes:
|
||||
return
|
||||
if event.mouseevent.button.value not in [1, 3]:
|
||||
return
|
||||
|
||||
closest = self._closest_section(event)
|
||||
t = self.parent.current_tab()
|
||||
tableView = self.parent.find(QTableView, f"tableView_{t}")
|
||||
table = self.parent._table[t]
|
||||
index = tableView.selectedIndexes()
|
||||
rk = self.data.get_rk()
|
||||
if self.parent._table is not None:
|
||||
if event.mouseevent.button.value == 1:
|
||||
table.setData(index[3], rk[closest[0]])
|
||||
if event.mouseevent.button.value == 3:
|
||||
table.setData(index[4], rk[closest[0]])
|
||||
|
||||
return
|
||||
|
||||
def _closest_section(self, event):
|
||||
axes = self.canvas.axes
|
||||
mx = event.mouseevent.xdata
|
||||
my = event.mouseevent.ydata
|
||||
bx, by = axes.get_xlim(), axes.get_ylim()
|
||||
ratio = (bx[0] - bx[1]) / (by[0] - by[1])
|
||||
|
||||
segments = event.artist.get_segments()
|
||||
ind = event.ind
|
||||
|
||||
points = []
|
||||
for i in ind:
|
||||
points = points + [[i, j] for j in segments[i]]
|
||||
|
||||
def dist_mouse(point):
|
||||
x, y = point[1]
|
||||
d2 = (((mx - x) / ratio) ** 2) + ((my - y) ** 2)
|
||||
return d2
|
||||
|
||||
closest = min(
|
||||
points, key=dist_mouse
|
||||
)
|
||||
|
||||
return closest
|
||||
|
||||
@timer
|
||||
def draw(self):
|
||||
self.init_axes()
|
||||
|
||||
if self.data is None:
|
||||
self.idle()
|
||||
return
|
||||
|
||||
if self.data.number_profiles == 0:
|
||||
self._init = False
|
||||
self.idle()
|
||||
return
|
||||
|
||||
self.draw_xy()
|
||||
self.draw_lr()
|
||||
self.draw_gl()
|
||||
|
||||
self.idle()
|
||||
self._init = True
|
||||
|
||||
def draw_xy(self):
|
||||
self.line_xy = []
|
||||
for xy in zip(self.data.get_x(), self.data.get_y()):
|
||||
self.line_xy.append(np.column_stack(xy))
|
||||
|
||||
self._colors, self._style = self.color_hightlight()
|
||||
self.line_xy_collection = collections.LineCollection(
|
||||
self.line_xy,
|
||||
colors=self._colors,
|
||||
linestyle=self._style,
|
||||
picker=10
|
||||
)
|
||||
self.canvas.axes.add_collection(self.line_xy_collection)
|
||||
|
||||
def color_hightlight(self):
|
||||
rk_min, rk_max = (-1, -1)
|
||||
if self._highlight_data is not None:
|
||||
rk_min, rk_max = self._highlight_data
|
||||
|
||||
colors = [self.color_plot for row in range(len(self._data))]
|
||||
for i, rk in enumerate(self.data.get_rk_complete_profiles()):
|
||||
if rk_min <= rk <= rk_max:
|
||||
colors[i] = self.color_plot_current
|
||||
style = ["-" for row in range(len(self._data))]
|
||||
|
||||
return colors, style
|
||||
|
||||
def draw_lr(self):
|
||||
lx = []
|
||||
ly = []
|
||||
rx = []
|
||||
ry = []
|
||||
|
||||
self.line_lr = []
|
||||
for x, y in zip(self.data.get_x(),
|
||||
self.data.get_y()):
|
||||
lx.append(x[0])
|
||||
ly.append(y[0])
|
||||
|
||||
rx.append(x[-1])
|
||||
ry.append(y[-1])
|
||||
|
||||
line = self.canvas.axes.plot(
|
||||
lx, ly,
|
||||
color=self.color_plot_river_bottom,
|
||||
linestyle="dotted",
|
||||
lw=1.,
|
||||
)
|
||||
self.line_lr.append(line)
|
||||
|
||||
line = self.canvas.axes.plot(
|
||||
rx, ry,
|
||||
color=self.color_plot_river_bottom,
|
||||
linestyle="dotted",
|
||||
lw=1.,
|
||||
)
|
||||
self.line_lr.append(line)
|
||||
|
||||
def draw_gl(self):
|
||||
x_complete = self.data.get_guidelines_x()
|
||||
y_complete = self.data.get_guidelines_y()
|
||||
|
||||
ind = 0
|
||||
self.line_gl = []
|
||||
for x, y in zip(x_complete, y_complete):
|
||||
line = self.canvas.axes.plot(
|
||||
x, y, color=self.colors[ind % len(self.colors)],
|
||||
linestyle=self.linestyle[ind // len(self.colors)]
|
||||
)
|
||||
self.line_gl.append(line)
|
||||
ind += 1
|
||||
|
||||
@timer
|
||||
def update(self):
|
||||
if not self._init:
|
||||
self.draw()
|
||||
return
|
||||
|
||||
if self.data is None:
|
||||
return
|
||||
|
||||
self.update_lr()
|
||||
self.update_gl()
|
||||
self.update_current()
|
||||
|
||||
self.update_idle()
|
||||
|
||||
def update_gl(self):
|
||||
self.data.compute_guidelines()
|
||||
x_complete = list(self.data.get_guidelines_x())
|
||||
y_complete = list(self.data.get_guidelines_y())
|
||||
|
||||
# TODO comprendre à quoi sert ce bout de code
|
||||
# ========>
|
||||
# for i in range(self.data.number_profiles):
|
||||
# if i < len(self.line_xy):
|
||||
# self.line_xy[i][0].set_data(
|
||||
# self.data.profile(i).x(),
|
||||
# self.data.profile(i).y()
|
||||
# )
|
||||
# else:
|
||||
# self.line_xy.append(
|
||||
# self.canvas.axes.plot(
|
||||
# self.data.profile(i).x(),
|
||||
# self.data.profile(i).y(),
|
||||
# color='r',
|
||||
# **self.plot_default_kargs
|
||||
# )
|
||||
# )
|
||||
# <========
|
||||
|
||||
for i in range(len(x_complete)):
|
||||
if i < len(self.line_gl):
|
||||
self.line_gl[i][0].set_data(
|
||||
x_complete[i],
|
||||
y_complete[i]
|
||||
)
|
||||
else:
|
||||
self.line_gl.append(
|
||||
self.canvas.axes.plot(
|
||||
x_complete[i],
|
||||
y_complete[i]
|
||||
)
|
||||
)
|
||||
|
||||
def update_current(self):
|
||||
if self._current_data_update:
|
||||
self._colors, self._style = self.color_hightlight()
|
||||
self.line_xy_collection.set_colors(self._colors)
|
||||
self.line_xy_collection.set_linestyle(self._style)
|
||||
|
||||
def update_lr(self):
|
||||
for line in self.line_lr:
|
||||
line[0].remove()
|
||||
|
||||
self.draw_lr()
|
||||
@@ -63,6 +63,14 @@ class ComboBoxDelegate(QItemDelegate):
|
||||
self._tab = tab
|
||||
self._trad = trad
|
||||
|
||||
@property
|
||||
def data(self):
|
||||
return self._data
|
||||
|
||||
@data.setter
|
||||
def data(self, data):
|
||||
self._data = data
|
||||
|
||||
def createEditor(self, parent, option, index):
|
||||
self.editor = QComboBox(parent)
|
||||
long_types = self._trad.get_dict("long_types")
|
||||
@@ -78,6 +86,17 @@ class ComboBoxDelegate(QItemDelegate):
|
||||
)
|
||||
)
|
||||
self.editor.addItems(lst)
|
||||
elif self._mode == "rk":
|
||||
if self._data is None:
|
||||
self.editor.addItems(
|
||||
["0"]
|
||||
)
|
||||
else:
|
||||
self.editor.addItems(
|
||||
list(
|
||||
map(str, self._data.reach.get_rk())
|
||||
)
|
||||
)
|
||||
else:
|
||||
self.editor.addItems(
|
||||
[self._trad['not_associated']] +
|
||||
@@ -135,10 +154,10 @@ class TableModel(PamhyrTableModel):
|
||||
if n is None:
|
||||
return self._trad['not_associated']
|
||||
return n.name
|
||||
elif self._headers[column] == "begin_kp":
|
||||
return self._lst.get(self._tab, row).begin_kp
|
||||
elif self._headers[column] == "end_kp":
|
||||
return self._lst.get(self._tab, row).end_kp
|
||||
elif self._headers[column] == "begin_rk":
|
||||
return str(self._lst.get(self._tab, row).begin_rk)
|
||||
elif self._headers[column] == "end_rk":
|
||||
return str(self._lst.get(self._tab, row).end_rk)
|
||||
|
||||
return QVariant()
|
||||
|
||||
@@ -170,13 +189,13 @@ class TableModel(PamhyrTableModel):
|
||||
self._lst, self._tab, row, self._data.edge(value)
|
||||
)
|
||||
)
|
||||
elif self._headers[column] == "begin_kp":
|
||||
elif self._headers[column] == "begin_rk":
|
||||
self._undo.push(
|
||||
SetBeginCommand(
|
||||
self._lst, self._tab, row, value
|
||||
)
|
||||
)
|
||||
elif self._headers[column] == "end_kp":
|
||||
elif self._headers[column] == "end_rk":
|
||||
self._undo.push(
|
||||
SetEndCommand(
|
||||
self._lst, self._tab, row, value
|
||||
|
||||
@@ -53,14 +53,14 @@ class SetBeginCommand(QUndoCommand):
|
||||
self._lcs = lcs
|
||||
self._tab = tab
|
||||
self._index = index
|
||||
self._old = self._lcs.get(self._tab, self._index).begin_kp
|
||||
self._old = self._lcs.get(self._tab, self._index).begin_rk
|
||||
self._new = float(new_value)
|
||||
|
||||
def undo(self):
|
||||
self._lcs.get(self._tab, self._index).begin_kp = float(self._old)
|
||||
self._lcs.get(self._tab, self._index).begin_rk = float(self._old)
|
||||
|
||||
def redo(self):
|
||||
self._lcs.get(self._tab, self._index).begin_kp = float(self._new)
|
||||
self._lcs.get(self._tab, self._index).begin_rk = float(self._new)
|
||||
|
||||
|
||||
class SetEndCommand(QUndoCommand):
|
||||
@@ -70,14 +70,14 @@ class SetEndCommand(QUndoCommand):
|
||||
self._lcs = lcs
|
||||
self._tab = tab
|
||||
self._index = index
|
||||
self._old = self._lcs.get(self._tab, self._index).end_kp
|
||||
self._old = self._lcs.get(self._tab, self._index).end_rk
|
||||
self._new = float(new_value)
|
||||
|
||||
def undo(self):
|
||||
self._lcs.get(self._tab, self._index).end_kp = float(self._old)
|
||||
self._lcs.get(self._tab, self._index).end_rk = float(self._old)
|
||||
|
||||
def redo(self):
|
||||
self._lcs.get(self._tab, self._index).end_kp = float(self._new)
|
||||
self._lcs.get(self._tab, self._index).end_rk = float(self._new)
|
||||
|
||||
|
||||
class SetEdgeCommand(QUndoCommand):
|
||||
|
||||
@@ -54,7 +54,7 @@ from View.LateralContribution.Table import (
|
||||
)
|
||||
|
||||
from View.Tools.Plot.PamhyrCanvas import MplCanvas
|
||||
from View.Geometry.PlotXY import PlotXY
|
||||
from View.LateralContribution.PlotXY import PlotXY
|
||||
from View.LateralContribution.translate import (
|
||||
LC_types, LCTranslate,
|
||||
)
|
||||
@@ -83,11 +83,14 @@ class LateralContributionWindow(PamhyrWindow):
|
||||
|
||||
self.setup_table()
|
||||
self.setup_graph()
|
||||
self.tabs = self.find(QTabWidget, "tabWidget")
|
||||
self.setup_connections()
|
||||
|
||||
def setup_table(self):
|
||||
self._table = {}
|
||||
|
||||
self._delegate_rk = []
|
||||
|
||||
for t in ["liquid", "solid", "suspenssion"]:
|
||||
self._delegate_type = ComboBoxDelegate(
|
||||
data=self._study.river,
|
||||
@@ -96,6 +99,16 @@ class LateralContributionWindow(PamhyrWindow):
|
||||
trad=self._trad,
|
||||
parent=self
|
||||
)
|
||||
|
||||
delegate_rk = ComboBoxDelegate(
|
||||
data=None,
|
||||
mode="rk",
|
||||
tab=t,
|
||||
trad=self._trad,
|
||||
parent=self
|
||||
)
|
||||
self._delegate_rk.append(delegate_rk)
|
||||
|
||||
self._delegate_edge = ComboBoxDelegate(
|
||||
data=self._study.river,
|
||||
mode="edge",
|
||||
@@ -112,6 +125,8 @@ class LateralContributionWindow(PamhyrWindow):
|
||||
delegates={
|
||||
"type": self._delegate_type,
|
||||
"edge": self._delegate_edge,
|
||||
"begin_rk": delegate_rk,
|
||||
"end_rk": delegate_rk,
|
||||
},
|
||||
data=self._study.river,
|
||||
undo=self._undo_stack,
|
||||
@@ -134,6 +149,7 @@ class LateralContributionWindow(PamhyrWindow):
|
||||
data=None,
|
||||
trad=self._trad,
|
||||
toolbar=None,
|
||||
parent=self
|
||||
)
|
||||
|
||||
def setup_connections(self):
|
||||
@@ -142,6 +158,8 @@ class LateralContributionWindow(PamhyrWindow):
|
||||
self.find(QAction, "action_edit").triggered.connect(self.edit)
|
||||
self.find(QAction, "action_sort").triggered.connect(self.sort)
|
||||
|
||||
self.tabs.currentChanged.connect(self._set_current_reach)
|
||||
|
||||
for t in ["liquid", "solid", "suspenssion"]:
|
||||
table = self.find(QTableView, f"tableView_{t}")
|
||||
table.selectionModel()\
|
||||
@@ -185,21 +203,24 @@ class LateralContributionWindow(PamhyrWindow):
|
||||
highlight = None
|
||||
|
||||
if len(rows) > 0:
|
||||
edge = self._study\
|
||||
.river\
|
||||
edge = self._study.river\
|
||||
.lateral_contribution\
|
||||
.get(tab, rows[0])\
|
||||
.edge
|
||||
if edge:
|
||||
data = edge.reach
|
||||
lc = self._lcs.get(tab, rows[0])
|
||||
highlight = (lc.begin_kp, lc.end_kp)
|
||||
highlight = (lc.begin_rk, lc.end_rk)
|
||||
|
||||
for delegate in self._delegate_rk:
|
||||
delegate.data = edge
|
||||
|
||||
self.plot = PlotXY(
|
||||
canvas=self.canvas,
|
||||
data=data,
|
||||
trad=self._trad,
|
||||
toolbar=None,
|
||||
parent=self
|
||||
)
|
||||
self.plot.highlight = highlight
|
||||
self.plot.update()
|
||||
|
||||
@@ -39,12 +39,12 @@ class LCTranslate(MainTranslate):
|
||||
super(LCTranslate, self).__init__()
|
||||
|
||||
self._dict["Lateral contribution"] = _translate(
|
||||
"LateralContribution", "Lateral contribution"
|
||||
"LateralContribution", "Lateral sources"
|
||||
)
|
||||
|
||||
self._sub_dict["long_types"] = {
|
||||
"ND": self._dict["not_defined"],
|
||||
"LC": _translate("LateralContribution", "Lateral contribution"),
|
||||
"LC": _translate("LateralContribution", "Lateral sources"),
|
||||
"RA": _translate("LateralContribution", "Rain"),
|
||||
"EV": _translate("LateralContribution", "Evaporation"),
|
||||
}
|
||||
@@ -57,6 +57,6 @@ class LCTranslate(MainTranslate):
|
||||
"name": self._dict["name"],
|
||||
"type": self._dict["type"],
|
||||
"edge": self._dict["reach"],
|
||||
"begin_kp": _translate("LateralContribution", "Begin kp (m)"),
|
||||
"end_kp": _translate("LateralContribution", "End kp (m)")
|
||||
"begin_rk": _translate("LateralContribution", "Start (m)"),
|
||||
"end_rk": _translate("LateralContribution", "End (m)")
|
||||
}
|
||||
+48
-7
@@ -28,7 +28,7 @@ from tools import logger_exception
|
||||
|
||||
from PyQt5 import QtGui
|
||||
from PyQt5.QtGui import (
|
||||
QKeySequence, QDesktopServices,
|
||||
QKeySequence, QDesktopServices, QIcon,
|
||||
)
|
||||
|
||||
from PyQt5.QtCore import (
|
||||
@@ -333,6 +333,15 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
|
||||
self.update_enable_action()
|
||||
# self.showMaximized()
|
||||
|
||||
image = os.path.abspath(
|
||||
os.path.join(
|
||||
os.path.dirname(__file__),
|
||||
"ui", "ressources",
|
||||
"Pamhyr2_logo.png"
|
||||
)
|
||||
)
|
||||
self.setWindowIcon(QIcon(image))
|
||||
|
||||
def set_debug_lvl(self, debug=True):
|
||||
if debug:
|
||||
logger.setLevel(logging.DEBUG)
|
||||
@@ -439,15 +448,32 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
|
||||
if Modules.CONFIG in keys:
|
||||
self._do_update_config()
|
||||
|
||||
if Modules.STUDY in keys:
|
||||
self._tab_widget_info.update()
|
||||
self._do_propagate_update_info_tab(keys)
|
||||
|
||||
logger.debug(f"Propagation of {keys}")
|
||||
for _, window in self.sub_win_list:
|
||||
window._propagated_update(key=keys)
|
||||
self._do_propagate_update_rec(window, keys)
|
||||
|
||||
self._tab_widget_checker.update(modules=keys)
|
||||
|
||||
def _do_propagate_update_info_tab(self, keys):
|
||||
modules = Modules.modelling_list()
|
||||
modules.append(Modules.STUDY)
|
||||
|
||||
has_info_mod = reduce(
|
||||
lambda acc, m: acc or (m in keys),
|
||||
modules, False
|
||||
)
|
||||
|
||||
if has_info_mod:
|
||||
self._tab_widget_info.update()
|
||||
|
||||
def _do_propagate_update_rec(self, window, keys):
|
||||
for _, win in window.sub_win_list:
|
||||
win._propagated_update(key=keys)
|
||||
self._do_propagate_update_rec(win, keys)
|
||||
|
||||
def update(self):
|
||||
self.set_title()
|
||||
|
||||
@@ -553,9 +579,13 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
|
||||
Nothing
|
||||
"""
|
||||
if self._study.filename is None or self._study.filename == "":
|
||||
options = QFileDialog.Options()
|
||||
options |= QFileDialog.DontUseNativeDialog
|
||||
|
||||
file_name, _ = QFileDialog.getSaveFileName(
|
||||
self, "Save File",
|
||||
"", "Pamhyr(*.pamhyr)"
|
||||
"", "Pamhyr(*.pamhyr)",
|
||||
options=options,
|
||||
)
|
||||
|
||||
if file_name.rsplit(".", 1)[-1] == "pamhyr":
|
||||
@@ -604,9 +634,13 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
|
||||
Returns:
|
||||
Nothing
|
||||
"""
|
||||
options = QFileDialog.Options()
|
||||
options |= QFileDialog.DontUseNativeDialog
|
||||
|
||||
file_name, _ = QFileDialog.getSaveFileName(
|
||||
self, "Save File",
|
||||
"", "Pamhyr(*.pamhyr)"
|
||||
"", "Pamhyr(*.pamhyr)",
|
||||
options=options,
|
||||
)
|
||||
|
||||
logger.debug(f"Save study as : {repr(file_name)}")
|
||||
@@ -886,7 +920,10 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
|
||||
Nothing
|
||||
"""
|
||||
if self._study is None:
|
||||
dialog = QFileDialog(self)
|
||||
options = QFileDialog.Options()
|
||||
options |= QFileDialog.DontUseNativeDialog
|
||||
|
||||
dialog = QFileDialog(self, options=options)
|
||||
dialog.setFileMode(QFileDialog.FileMode.ExistingFile)
|
||||
dialog.setDefaultSuffix(".pamhyr")
|
||||
# dialog.setFilter(dialog.filter() | QtCore.QDir.Hidden)
|
||||
@@ -1278,7 +1315,11 @@ class ApplicationWindow(QMainWindow, ListedSubWindow, WindowToolKit):
|
||||
if self._study.filename == "":
|
||||
return
|
||||
|
||||
dialog = QFileDialog(self)
|
||||
options = QFileDialog.Options()
|
||||
options |= QFileDialog.DontUseNativeDialog
|
||||
|
||||
dialog = QFileDialog(self, options=options)
|
||||
|
||||
dialog.setFileMode(QFileDialog.FileMode.ExistingFile)
|
||||
dialog.setDefaultSuffix(".BIN")
|
||||
# dialog.setFilter(dialog.filter() | QtCore.QDir.Hidden)
|
||||
|
||||
@@ -130,8 +130,9 @@ class WidgetChecker(PamhyrWidget):
|
||||
self._study = study
|
||||
|
||||
def _checkers_filtered(self, modules):
|
||||
impacted = modules | modules.impact()
|
||||
return filter(
|
||||
lambda c: c._modules in modules,
|
||||
lambda c: c._modules in impacted,
|
||||
self._checkers
|
||||
)
|
||||
|
||||
|
||||
@@ -191,7 +191,7 @@ class WidgetInfo(PamhyrWidget):
|
||||
n_na_hs = 0
|
||||
|
||||
for h in hs.lst:
|
||||
if h.input_reach is not None and h.input_kp is not None:
|
||||
if h.input_reach is not None and h.input_rk is not None:
|
||||
n_hs += 1
|
||||
else:
|
||||
n_na_hs += 1
|
||||
|
||||
Loaded 100 of 293 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user