diff --git a/src/Meshing/Mage.py b/src/Meshing/Mage.py
index 90f44d56..1e8f4c2d 100644
--- a/src/Meshing/Mage.py
+++ b/src/Meshing/Mage.py
@@ -298,7 +298,7 @@ class MeshingWithMageMailleurTT(AMeshingTool):
lplan: bool = False,
lm: int = 3,
linear: bool = False,
- origin_value = 0.0):
+ origin_value=0.0):
if reach is None or len(reach.profiles) == 0:
return reach
@@ -371,7 +371,7 @@ class MeshingWithMageMailleurTT(AMeshingTool):
lplan: bool = False,
lm: int = 3,
linear: bool = False,
- origin_value = 0.0):
+ origin_value=0.0):
if reach is None or len(reach.profiles) == 0:
return reach
diff --git a/src/Model/Geometry/ProfileXYZ.py b/src/Model/Geometry/ProfileXYZ.py
index 0bd8d83d..ff5ae599 100644
--- a/src/Model/Geometry/ProfileXYZ.py
+++ b/src/Model/Geometry/ProfileXYZ.py
@@ -583,27 +583,44 @@ class ProfileXYZ(Profile, SQLSubModel):
"""
Remove points to keep at most np_purge points.
"""
+ if (self.nb_points <= np_purge):
+ return
- if (self.nb_points <= np_purge): return
-
- nb_named = 2 # we consider the first and last point as named
+ 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(
+ 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
+ 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 (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))
+ area[i] = PointXYZ.areatriangle3d(
+ self.point(i-1),
+ self.point(i),
+ self.point(i+1)
+ )
diff --git a/src/Model/InitialConditions/InitialConditions.py b/src/Model/InitialConditions/InitialConditions.py
index 4eeb5225..d77e2498 100644
--- a/src/Model/InitialConditions/InitialConditions.py
+++ b/src/Model/InitialConditions/InitialConditions.py
@@ -266,6 +266,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 +298,15 @@ class InitialConditions(SQLSubModel):
self._data.insert(index, n)
self._status.modified()
+ def new_from_data(self, kp, discharge, elevation):
+ n = Data(reach=self._reach, status=self._status)
+
+ n['kp'] = kp
+ n['discharge'] = discharge
+ n['elevation'] = elevation
+
+ return n
+
def insert(self, index, data):
self._data.insert(index, data)
self._status.modified()
diff --git a/src/Model/Results/River/River.py b/src/Model/Results/River/River.py
index 57823688..4505b145 100644
--- a/src/Model/Results/River/River.py
+++ b/src/Model/Results/River/River.py
@@ -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
+ )
+ )
diff --git a/src/View/Geometry/PlotKPZ.py b/src/View/Geometry/PlotKPZ.py
index f4dbd790..1c80af12 100644
--- a/src/View/Geometry/PlotKPZ.py
+++ b/src/View/Geometry/PlotKPZ.py
@@ -25,8 +25,11 @@ logger = logging.getLogger()
class PlotKPZ(PamhyrPlot):
- def __init__(self, canvas=None, trad=None, data=None, toolbar=None,
+ def __init__(self, canvas=None, trad=None,
+ study=None, data=None, toolbar=None,
parent=None):
+ self._study = study
+
super(PlotKPZ, self).__init__(
canvas=canvas,
trad=trad,
@@ -75,6 +78,7 @@ class PlotKPZ(PamhyrPlot):
self.draw_current()
self.draw_gl()
self.draw_bottom()
+ self.draw_profiles_hs(self._data)
self.idle()
self._init = True
@@ -184,6 +188,34 @@ class PlotKPZ(PamhyrPlot):
color='lightgrey'
)
+ def draw_profiles_hs(self, reach):
+ lhs = filter(
+ lambda hs: hs._input_reach.reach is reach,
+ self._study.river.hydraulic_structures.lst
+ )
+
+ for hs in lhs:
+ x = hs.input_kp
+ 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:
diff --git a/src/View/Geometry/UpdateKPDialog.py b/src/View/Geometry/UpdateKPDialog.py
index 08abf1f4..87afc977 100644
--- a/src/View/Geometry/UpdateKPDialog.py
+++ b/src/View/Geometry/UpdateKPDialog.py
@@ -74,8 +74,10 @@ class UpdateKPDialog(PamhyrDialog):
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)).kp)
+ self.set_double_spin_box(
+ "doubleSpinBox_origin",
+ self._reach.profile(self.profiles.index(origin)).kp
+ )
@property
def profiles(self):
diff --git a/src/View/Geometry/Window.py b/src/View/Geometry/Window.py
index b624aa1c..3e972349 100644
--- a/src/View/Geometry/Window.py
+++ b/src/View/Geometry/Window.py
@@ -375,6 +375,7 @@ class GeometryWindow(PamhyrWindow):
self._plot_kpc = PlotKPZ(
canvas=self._canvas_kpc,
+ study=self._study,
data=self._reach,
trad=self._trad,
toolbar=self._toolbar_kpc
diff --git a/src/View/InitialConditions/Table.py b/src/View/InitialConditions/Table.py
index ee67a8ac..5461e862 100644
--- a/src/View/InitialConditions/Table.py
+++ b/src/View/InitialConditions/Table.py
@@ -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,
)
@@ -197,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
@@ -214,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.kp,
+ 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()
diff --git a/src/View/InitialConditions/UndoCommand.py b/src/View/InitialConditions/UndoCommand.py
index 06274ae3..fecb6c6d 100644
--- a/src/View/InitialConditions/UndoCommand.py
+++ b/src/View/InitialConditions/UndoCommand.py
@@ -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)
diff --git a/src/View/InitialConditions/Window.py b/src/View/InitialConditions/Window.py
index 74262f5a..82946e84 100644
--- a/src/View/InitialConditions/Window.py
+++ b/src/View/InitialConditions/Window.py
@@ -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,
)
@@ -59,6 +60,9 @@ 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
@@ -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,9 +185,13 @@ 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_plot()
@@ -230,6 +240,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 +287,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],
+ ["kp", "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):
diff --git a/src/View/LateralContribution/Table.py b/src/View/LateralContribution/Table.py
index 3b632392..c6684f6c 100644
--- a/src/View/LateralContribution/Table.py
+++ b/src/View/LateralContribution/Table.py
@@ -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 == "kp":
+ if self._data is None:
+ self.editor.addItems(
+ ["0"]
+ )
+ else:
+ self.editor.addItems(
+ list(
+ map(str, self._data.reach.get_kp())
+ )
+ )
else:
self.editor.addItems(
[self._trad['not_associated']] +
@@ -136,9 +155,9 @@ class TableModel(PamhyrTableModel):
return self._trad['not_associated']
return n.name
elif self._headers[column] == "begin_kp":
- return self._lst.get(self._tab, row).begin_kp
+ return str(self._lst.get(self._tab, row).begin_kp)
elif self._headers[column] == "end_kp":
- return self._lst.get(self._tab, row).end_kp
+ return str(self._lst.get(self._tab, row).end_kp)
return QVariant()
diff --git a/src/View/LateralContribution/Window.py b/src/View/LateralContribution/Window.py
index 48ec3cc2..879955bb 100644
--- a/src/View/LateralContribution/Window.py
+++ b/src/View/LateralContribution/Window.py
@@ -88,6 +88,8 @@ class LateralContributionWindow(PamhyrWindow):
def setup_table(self):
self._table = {}
+ self._delegate_kp = []
+
for t in ["liquid", "solid", "suspenssion"]:
self._delegate_type = ComboBoxDelegate(
data=self._study.river,
@@ -96,6 +98,16 @@ class LateralContributionWindow(PamhyrWindow):
trad=self._trad,
parent=self
)
+
+ delegate_kp = ComboBoxDelegate(
+ data=None,
+ mode="kp",
+ tab=t,
+ trad=self._trad,
+ parent=self
+ )
+ self._delegate_kp.append(delegate_kp)
+
self._delegate_edge = ComboBoxDelegate(
data=self._study.river,
mode="edge",
@@ -112,6 +124,8 @@ class LateralContributionWindow(PamhyrWindow):
delegates={
"type": self._delegate_type,
"edge": self._delegate_edge,
+ "begin_kp": delegate_kp,
+ "end_kp": delegate_kp,
},
data=self._study.river,
undo=self._undo_stack,
@@ -185,8 +199,7 @@ class LateralContributionWindow(PamhyrWindow):
highlight = None
if len(rows) > 0:
- edge = self._study\
- .river\
+ edge = self._study.river\
.lateral_contribution\
.get(tab, rows[0])\
.edge
@@ -195,6 +208,9 @@ class LateralContributionWindow(PamhyrWindow):
lc = self._lcs.get(tab, rows[0])
highlight = (lc.begin_kp, lc.end_kp)
+ for delegate in self._delegate_kp:
+ delegate.data = edge
+
self.plot = PlotXY(
canvas=self.canvas,
data=data,
diff --git a/src/View/Results/PlotKPC.py b/src/View/Results/PlotKPC.py
index d6f5feae..77e101dc 100644
--- a/src/View/Results/PlotKPC.py
+++ b/src/View/Results/PlotKPC.py
@@ -79,6 +79,7 @@ class PlotKPC(PamhyrPlot):
self.draw_water_elevation_max(reach)
self.draw_water_elevation_overflow(reach)
self.draw_current(reach)
+ self.draw_profiles_hs(reach)
# self.enable_legend()
@@ -105,6 +106,34 @@ class PlotKPC(PamhyrPlot):
self._river_bottom = z
+ def draw_profiles_hs(self, reach):
+ lhs = filter(
+ lambda hs: hs._input_reach.reach is reach.geometry,
+ self.results.study.river.hydraulic_structures.lst
+ )
+
+ for hs in lhs:
+ x = hs.input_kp
+ z_min = reach.geometry.get_z_min()
+ z_max = reach.geometry.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,
+ )
+
def sl_compute_bedrock(self, reach):
z_min = reach.geometry.get_z_min()
sl = self.sl_compute_initial(reach)
@@ -115,8 +144,8 @@ class PlotKPC(PamhyrPlot):
lambda z, h: z - h[0],
sl, z
),
- z_min, # Original geometry
- sl # Original sediment layers
+ z_min, # Original geometry
+ sl # Original sediment layers
)
)
@@ -132,8 +161,8 @@ class PlotKPC(PamhyrPlot):
lambda z, h: z + h[0],
sl, z
),
- z_br, # Bedrock elevation
- sl # Current sediment layers
+ z_br, # Bedrock elevation
+ sl # Current sediment layers
)
)
diff --git a/src/View/Tools/ASubWindow.py b/src/View/Tools/ASubWindow.py
index b3e131e1..78655358 100644
--- a/src/View/Tools/ASubWindow.py
+++ b/src/View/Tools/ASubWindow.py
@@ -87,7 +87,9 @@ class WindowToolKit(object):
def file_dialog(self, select_file=True,
callback=lambda x: None,
- directory=None):
+ directory=None,
+ default_suffix=None,
+ file_filter=None):
"""Open a new file dialog and send result to callback function
Args:
@@ -95,7 +97,8 @@ class WindowToolKit(object):
callback: The callback function with one arguments, files
selection list
directory: Defaut directory
-
+ default_suffix: Default file suffix
+ file_filter: List of file filter
Returns:
The returns of callback
"""
@@ -110,6 +113,13 @@ class WindowToolKit(object):
if directory is not None:
dialog.setDirectory(directory)
+ if select_file:
+ if default_suffix is not None:
+ dialog.setDefaultSuffix(default_suffix)
+
+ if file_filter is not None:
+ dialog.setNameFilters(file_filter)
+
if dialog.exec_():
file_names = dialog.selectedFiles()
return callback(file_names)
diff --git a/src/View/Tools/Plot/PamhyrToolbar.py b/src/View/Tools/Plot/PamhyrToolbar.py
index 0d060ec4..2a851c92 100644
--- a/src/View/Tools/Plot/PamhyrToolbar.py
+++ b/src/View/Tools/Plot/PamhyrToolbar.py
@@ -200,7 +200,6 @@ class PamhyrPlotToolbar(NavigationToolbar2QT):
self.icons.append(("save_figure", icon_save))
-
def save_figure(self, *args):
file_types = self.canvas.get_supported_filetypes_grouped()
default_file_type = self.canvas.get_default_filetype()
diff --git a/src/View/ui/InitialConditions.ui b/src/View/ui/InitialConditions.ui
index 272398b2..38f96734 100644
--- a/src/View/ui/InitialConditions.ui
+++ b/src/View/ui/InitialConditions.ui
@@ -81,6 +81,7 @@
false
+
@@ -121,6 +122,18 @@
Sort inital condition
+
+
+
+ ressources/import.pngressources/import.png
+
+
+ Import
+
+
+ Import from file
+
+