Scenarios: Merge remote-tracking branch 'origin/master'.

test_sound
Pierre-Antoine Rouby 2024-09-06 10:58:05 +02:00
commit e43c20c1e6
38 changed files with 1205 additions and 189 deletions

View File

@ -1334,7 +1334,7 @@ https://gitlab.irstea.fr/theophile.terraz/pamhyr
You can improve or add translation for the project. To contribute to
Pamhyr2 translate, you need to use Qt Linguist[fn:qt-linguist]. Open
Qt-linguist and edite the translation ({{{file(.ts)}}}) file, finally,
Qt-linguist and edit the translation ({{{file(.ts)}}}) file, finally,
commit the new version of file and make a merge request.
If you want add a new language, edit the script

Binary file not shown.

View File

@ -411,7 +411,7 @@ class MeshingWithMageMailleurTT(AMeshingTool):
str,
[
st_file, m_file,
"update_rk", step,
"update_kp", step,
limites[0], limites[1],
directrices[0], directrices[1],
orientation, lm, linear, origin, origin_value

View File

@ -509,3 +509,15 @@ class BoundaryCondition(SQLSubModel):
d = self._data
d[index], d[prev] = d[prev], d[index]
self._status.modified()
def reach(self, river):
r = []
if self._node is not None:
if river is not None:
for edge in river.edges():
if edge.node1.name == self._node.name:
r.append(edge.reach)
if edge.node2.name == self._node.name:
r.append(edge.reach)
return r

View File

@ -23,6 +23,8 @@ from tools import trace, timer
from Model.Tools.PamhyrDB import SQLSubModel
from Model.Scenario import Scenario
from numpy import interp
logger = logging.getLogger()
@ -290,3 +292,17 @@ class Friction(SQLSubModel):
def end_strickler(self, strickler):
self._end_strickler = strickler
self._status.modified()
def get_friction(self, rk):
if not self.contains_rk(rk):
return None
minor = interp(rk,
[self.begin_rk, self.end_rk],
[self.begin_strickler.minor,
self.end_strickler.minor])
medium = interp(rk,
[self.begin_rk, self.end_rk],
[self.begin_strickler.medium,
self.end_strickler.medium])
return minor, medium

View File

@ -332,8 +332,17 @@ class Profile(object):
def wet_points(self, z):
raise NotImplementedMethodeError(self, self.wet_point)
def wet_width(self, z):
raise NotImplementedMethodeError(self, self.wet_width)
def wet_perimeter(self, z):
raise NotImplementedMethodeError(self, self.wet_perimeter)
def wet_area(self, z):
raise NotImplementedMethodeError(self, self.wet_area)
def wet_radius(self, z):
raise NotImplementedMethodeError(self, self.wet_radius)
def get_nb_wet_areas(self, z):
raise NotImplementedMethodeError(self, self.get_nb_wet_areas)

View File

@ -486,31 +486,125 @@ class ProfileXYZ(Profile, SQLSubModel):
return abs(rg.dist(rd))
def wet_perimeter(self, z):
poly = self.wet_polygon(z)
def wet_width(self, z):
start, end = self.get_all_water_limits_ac(z)
if poly is None:
if len(start) == 0:
return 0
return poly.length
length = 0.0
for s, e in zip(start, end):
length += abs(s - e)
return length
def wet_perimeter(self, z):
lines = self.wet_lines(z)
if lines is None:
return 0
length = 0.0
for line in lines:
length += line.length
return length
def wet_area(self, z):
poly = self.wet_polygon(z)
lines = self.wet_lines(z)
if poly is None:
if lines is None:
return 0
return poly.area
area = 0.0
for line in lines:
if len(line.coords) > 2:
poly = geometry.Polygon(line)
area += poly.area
return area
def wet_radius(self, z):
p = self.wet_perimeter(z)
a = self.wet_area(z)
if p == 0:
return 0
return a/p
def wet_line(self, z):
points = self.wet_points(z)
if len(points) < 3:
return None
zz = map(lambda p: p.z, points)
station = self._get_station(points)
line = geometry.LineString(list(zip(station, zz)))
return line
def wet_lines(self, z):
points = self._points
if len(points) < 3:
return None
lines = []
zz = list(map(lambda p: p.z, points))
station = self._get_station(points)
line = []
for i in range(self.number_points-1):
if zz[i] >= z and zz[i+1] < z:
y = np.interp(
z,
[zz[i], zz[i+1]],
[station[i], station[i+1]]
)
line.append([y, z])
if zz[i] < z:
line.append([station[i], zz[i]])
if zz[i] <= z and zz[i+1] >= z:
y = np.interp(
z,
[zz[i], zz[i+1]],
[station[i], station[i+1]]
)
line.append([y, z])
if len(line) > 2:
lines.append(geometry.LineString(line))
line = []
if zz[self.number_points-1] < z:
line.append([station[self.number_points-1], z])
if len(line) > 2:
lines.append(geometry.LineString(line))
line = []
return lines
def max_water_depth(self, z):
return z - self.z_min()
def mean_water_depth(self, z):
a = self.wet_area(z)
w = self.wet_width(z)
if w == 0:
return 0
return a/w
def wet_polygon(self, z):
points = self.wet_points(z)
if len(points) < 3:
return None
z = map(lambda p: p.z, points)
zz = map(lambda p: p.z, points)
station = self._get_station(points)
poly = geometry.Polygon(list(zip(station, z)))
poly = geometry.Polygon(list(zip(station, zz)))
return poly
def wet_points(self, z):
@ -522,6 +616,63 @@ class ProfileXYZ(Profile, SQLSubModel):
return points
def get_nb_wet_areas(self, z):
n_zones = 0
points = self._points
if points[0].z <= z:
n_zones += 1
for i in range(self.number_points-1):
if points[i].z > z and points[i+1].z <= z:
n_zones += 1
return n_zones
def get_all_water_limits_ac(self, z):
"""
Determine all water limits for z elevation.
"""
points = self._points
if len(points) < 3:
return None
zz = list(map(lambda p: p.z, points))
station = self._get_station(points)
start = []
if points[0].z <= z:
start.append(station[0])
for i in range(self.number_points-1):
if zz[i] > z and zz[i+1] <= z:
y = np.interp(
z,
[zz[i], zz[i+1]],
[station[i], station[i+1]]
)
start.append(y)
end = []
if points[-1].z <= z:
end.append(station[-1])
for i in reversed(range(self.number_points-1)):
if zz[i] <= z and zz[i+1] > z:
y = np.interp(
z,
[zz[i], zz[i+1]],
[station[i], station[i+1]]
)
end.append(y)
if len(start) != len(end):
logger.error(f"ERROR in get_all_water_limits_ac")
return [], []
return start, list(reversed(end))
def get_water_limits(self, z):
"""
Determine left and right limits of water elevation.
@ -585,8 +736,8 @@ class ProfileXYZ(Profile, SQLSubModel):
Returns:
Projection of the points of the profile on a plane.
"""
if self.nb_points < 3:
return None
if self.nb_points < 2:
return [0.0]
else:
return self._get_station(self.points)
@ -718,3 +869,9 @@ class ProfileXYZ(Profile, SQLSubModel):
self.point(i),
self.point(i+1)
)
def shift(self, x, y, z):
for p in self.points:
p.x = p.x + x
p.y = p.y + y
p.z = p.z + z

View File

@ -479,8 +479,14 @@ class InitialConditions(SQLSubModel):
logger.debug(f"incline = {incline}")
self._data = []
for profile in profiles:
width = profile.width_approximation()
strickler = 25
width = profile.wet_width(profile.z_min() + height)
frictions = self._reach.frictions.frictions
strickler = None
for f in frictions:
if f.contains_rk(profile.rk):
strickler = f.get_friction(profile.rk)[0]
if strickler is None:
strickler = 25.0
if not compute_discharge:
discharge = data_discharge[profile.rk]
@ -533,7 +539,13 @@ class InitialConditions(SQLSubModel):
self._data = []
for profile in profiles:
width = profile.width_approximation()
strickler = 25
frictions = self._reach.frictions.frictions
strickler = None
for f in frictions:
if f.contains_rk(profile.rk):
strickler = f.get_friction(profile.rk)[0]
if strickler is None:
strickler = 25.0
if not compute_height:
height = data_height[profile.rk]

View File

@ -324,7 +324,7 @@ class Mage(CommandLineSolver):
if t in ["HYD", "QSO", "LIM"]:
v0 /= 60 # Convert first column to minute
f.write(f"{v0:10}{v1:10}\n")
f.write(f"{v0:9} {v1:9} \n")
return files

View File

@ -0,0 +1,68 @@
# GenerateDialog.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, QCheckBox, QPushButton
)
class GenerateDialog(PamhyrDialog):
_pamhyr_ui = "BoundaryConditionsDialogGenerator"
_pamhyr_name = "Boundary Condition Options"
def __init__(self,
value,
reach,
title="Boundary Condition Options",
trad=None,
parent=None):
super(GenerateDialog, self).__init__(
title=trad[self._pamhyr_name],
options=[],
trad=trad,
parent=parent
)
self.value = value
self.find(QDoubleSpinBox, "doubleSpinBox").setValue(self.value)
self.reach = reach
self.find(QPushButton, "EstimateButton").clicked.connect(
self.estimate
)
def accept(self):
self.value = self.find(QDoubleSpinBox, "doubleSpinBox").value()
super().accept()
def reject(self):
self.close()
def estimate(self):
self.value = abs(self.reach.get_incline_median_mean())
self.find(QDoubleSpinBox, "doubleSpinBox").setValue(self.value)

View File

@ -48,6 +48,7 @@ from Model.BoundaryCondition.BoundaryConditionTypes import (
from View.BoundaryCondition.Edit.UndoCommand import (
SetDataCommand, AddCommand, DelCommand,
SortCommand, MoveCommand, PasteCommand,
ReplaceDataCommand,
)
_translate = QCoreApplication.translate
@ -200,3 +201,13 @@ class TableModel(PamhyrTableModel):
def update(self):
# self.auto_sort()
self.layoutChanged.emit()
def replace_data(self, data1, data2):
self.layoutAboutToBeChanged.emit()
self._undo.push(
ReplaceDataCommand(
self._data, data1, data2
)
)
self.layoutAboutToBeChanged.emit()
self.update()

View File

@ -181,3 +181,30 @@ class PasteCommand(QUndoCommand):
def redo(self):
for bc in self._bcs:
self._data.insert(self._row, bc)
class ReplaceDataCommand(QUndoCommand):
def __init__(self, data, data1, data2):
QUndoCommand.__init__(self)
self._data = data
self._old_rows = len(data)
self._data1 = data1
self._data2 = data2
self._rows = len(data1)
self._old_bc = []
for row in range(self._old_rows):
self._old_bc.append((row, self._data.get_i(row)))
self._old_bc.sort()
def undo(self):
self._data.delete_i(list(range(self._rows)))
for row, el in self._old_bc:
self._data.insert(row, el)
def redo(self):
self._data.delete_i(list(range(self._old_rows)))
for row in range(self._rows):
self._data.add(row)
self._data._set_i_c_v(row, 0, self._data1[row])
self._data._set_i_c_v(row, 1, self._data2[row])

View File

@ -20,6 +20,8 @@ import logging
from tools import timer, trace
from numpy import sqrt
from View.Tools.PamhyrWindow import PamhyrWindow
from View.Tools.PamhyrWidget import PamhyrWidget
from View.Tools.PamhyrDelegate import PamhyrExTimeDelegate
@ -48,6 +50,7 @@ from View.BoundaryCondition.Edit.translate import BCETranslate
from View.BoundaryCondition.Edit.UndoCommand import SetMetaDataCommand
from View.BoundaryCondition.Edit.Table import TableModel
from View.BoundaryCondition.Edit.Plot import Plot
from View.BoundaryCondition.Edit.GenerateDialog import GenerateDialog
_translate = QCoreApplication.translate
@ -104,6 +107,7 @@ class EditBoundaryConditionWindow(PamhyrWindow):
self._data = data
trad = BCETranslate()
self._long_types = trad.get_dict("long_types")
self._study = study
name = trad[self._pamhyr_name]
if self._data is not None:
@ -200,6 +204,18 @@ class EditBoundaryConditionWindow(PamhyrWindow):
self.find(QAction, "action_del").triggered.connect(self.delete)
self.find(QAction, "action_sort").triggered.connect(self.sort)
self.find(QAction, "action_generate_uniform")\
.triggered.connect(self.generate_uniform)
self.find(QAction, "action_generate_critical")\
.triggered.connect(self.generate_critical)
if self._data.bctype != "ZD" or not self._data.has_node:
self.find(QAction, "action_generate_uniform").setVisible(False)
self.find(QAction, "action_generate_critical").setVisible(False)
else:
self.find(QAction, "action_generate_uniform").setVisible(True)
self.find(QAction, "action_generate_critical").setVisible(True)
self._table.dataChanged.connect(self.update)
self._table.layoutChanged.connect(self.update)
@ -320,3 +336,46 @@ class EditBoundaryConditionWindow(PamhyrWindow):
self._table.redo()
self.plot.update()
self.widget_update()
def generate_uniform(self):
if self._data.has_node:
node = self._data.node
reach = self._data.reach(self._study.river)[0]
profile = reach.profiles[-1]
incline = abs(reach.get_incline_median_mean())
dlg = GenerateDialog(incline,
reach,
trad=self._trad,
parent=self)
if dlg.exec():
incline = dlg.value
frictions = reach._parent.frictions.frictions
z_min = profile.z_min()
z_max = profile.z_max()
strickler = None
for f in frictions:
if f.contains_rk(profile.rk):
strickler = f.get_friction(profile.rk)[0]
if strickler is None:
strickler = 25.0
height = [(i)*(z_max-z_min)/50 for i in range(51)]
q = [((profile.wet_width(z_min + h) * 0.8) * strickler
* (h ** (5/3)) * (abs(incline) ** (0.5)))
for h in height]
self._table.replace_data(height, q)
return
def generate_critical(self):
if self._data.has_node:
node = self._data.node
reach = self._data.reach(self._study.river)[0]
profile = reach.profiles[-1]
z_min = profile.z_min()
z_max = profile.z_max()
height = [(i)*(z_max-z_min)/50 for i in range(51)]
q = [sqrt(9.81 * (profile.wet_area(z_min + h) ** 3)
/ profile.wet_width(z_min + h))
for h in height]
self._table.replace_data(height, q)
return

View File

@ -32,6 +32,8 @@ class BCETranslate(BCTranslate):
self._dict["Edit Boundary Conditions"] = _translate(
"BoundaryCondition", "Edit boundary conditions"
)
self._dict["Boundary Condition Options"] = _translate(
"BoundaryCondition", "Boundary Condition Options")
self._sub_dict["table_headers"] = {
"x": _translate("BoundaryCondition", "X"),

View File

@ -313,10 +313,6 @@ 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):
# Noting to do in this case
return
self.profile_line2D, = self.canvas.axes.plot(
x, y, color=self.color_plot,
lw=1.5, markersize=7, marker='+',

View File

@ -0,0 +1,60 @@
# ShiftDialog.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
class ShiftDialog(PamhyrDialog):
_pamhyr_ui = "GeometryReachShift"
_pamhyr_name = "Shift"
def __init__(self, trad=None, parent=None):
super(ShiftDialog, self).__init__(
title=trad[self._pamhyr_name],
trad=trad,
options=[],
parent=parent
)
self._init_default_values()
def _init_default_values(self):
self._dx = 0.0
self._dy = 0.0
self._dz = 0.0
@property
def dx(self):
return self._dx
@property
def dy(self):
return self._dy
@property
def dz(self):
return self._dz
def accept(self):
self._dx = self.get_double_spin_box("doubleSpinBox_X")
self._dy = self.get_double_spin_box("doubleSpinBox_Y")
self._dz = self.get_double_spin_box("doubleSpinBox_Z")
super().accept()
def reject(self):
self.close()

View File

@ -267,3 +267,12 @@ class GeometryReachTableModel(PamhyrTableModel):
)
)
self.layoutChanged.emit()
def shift(self, rows, dx, dy, dz):
self._undo.push(
ShiftCommand(
self._data, rows, dx, dy, dz
)
)
self.layoutChanged.emit()

View File

@ -82,3 +82,12 @@ class GeometryTranslate(MainTranslate):
self._dict["Meshing"] = _translate(
"Geometry", "Meshing"
)
self._dict["UpdateRK"] = _translate(
"Geometry", "UpdateRK"
)
self._dict["Purge"] = _translate(
"Geometry", "Purge"
)
self._dict["Shift"] = _translate(
"Geometry", "Shift"
)

View File

@ -281,3 +281,32 @@ class PurgeCommand(QUndoCommand):
def redo(self):
for profile in self._reach._profiles:
profile.purge(self._np_purge)
class ShiftCommand(QUndoCommand):
def __init__(self, reach, rows, dx, dy, dz):
QUndoCommand.__init__(self)
self._reach = reach
self._rows = rows
self._dx = dx
self._dy = dy
self._dz = dz
self._old = []
for profile in self._reach.profiles:
self._old.append(profile.points.copy())
def undo(self):
for i in self._rows:
profile = self._reach.profiles[i]
self._reach.profiles[i].shift(-self._dx,
-self._dy,
-self._dz)
def redo(self):
for i in self._rows:
profile = self._reach.profiles[i]
self._reach.profiles[i].shift(self._dx,
self._dy,
self._dz)

View File

@ -58,6 +58,7 @@ 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.ShiftDialog import ShiftDialog
from View.Geometry.Translate import GeometryTranslate
from View.Geometry.Profile.Window import ProfileWindow
@ -205,6 +206,7 @@ class GeometryWindow(PamhyrWindow):
"action_meshing": self.edit_meshing,
"action_update_rk": self.update_rk,
"action_purge": self.purge,
"action_shift": self.shift,
}
for action in actions:
@ -511,10 +513,6 @@ class GeometryWindow(PamhyrWindow):
self._table.move_down(row)
self.select_current_profile()
def purge(self):
self._table.purge()
self.update_redraw()
def purge(self):
try:
dlg = PurgeDialog(
@ -527,6 +525,28 @@ class GeometryWindow(PamhyrWindow):
logger_exception(e)
return
def shift(self):
rows = sorted(
list(
set(
[index.row() for index in self.tableView.selectedIndexes()]
)
)
)
try:
dlg = ShiftDialog(
trad=self._trad,
parent=self
)
if dlg.exec():
self._table.shift(rows,
dlg.dx,
dlg.dy,
dlg.dz)
except Exception as e:
logger_exception(e)
return
def duplicate(self):
rows = [
row.row() for row in
@ -615,7 +635,7 @@ class GeometryWindow(PamhyrWindow):
QSettings.UserScope, 'MyOrg'
)
if self._study.filename != "" or self._study.filename is not None:
if self._study.filename != "" and self._study.filename is not None:
default_directory = os.path.basename(self._study.filename)
current_dir = settings.value(
'current_directory',

View File

@ -200,7 +200,7 @@ class InitialConditionsWindow(PamhyrWindow):
return rows[0].row()
def update(self):
self.update(propagate=False)
self._update(propagate=False)
def _update(self, propagate=True):
self._update_plot()

View File

@ -20,6 +20,7 @@ import logging
from functools import reduce
from datetime import datetime
from numpy import sqrt
from tools import timer
from View.Tools.PamhyrPlot import PamhyrPlot
@ -32,6 +33,11 @@ unit = {
"elevation": "0-meter",
"water_elevation": "0-meter",
"discharge": "1-m3s",
"velocity": "2-ms",
"depth": "3-meter",
"mean_depth": "3-meter",
"froude": "4-dimensionless",
"wet_area": "5-m2",
}
@ -76,74 +82,155 @@ class CustomPlot(PamhyrPlot):
reach = results.river.reach(self._reach)
rk = reach.geometry.get_rk()
z_min = reach.geometry.get_z_min()
q = list(
map(
lambda p: p.get_ts_key(self._timestamp, "Q"),
reach.profiles
)
)
z = list(
map(
lambda p: p.get_ts_key(self._timestamp, "Z"),
reach.profiles
)
)
# self.canvas.axes.set_xlim(
# left=min(rk), right=max(rk)
# )
meter_axes = self.canvas.axes
m3S_axes = self.canvas.axes
if "0-meter" in self._y_axes and "1-m3s" in self._y_axes:
m3s_axes = self._axes["1-m3s"]
shift = 0
compt = 0
for ax in sorted(self._axes):
if compt == 0:
self._axes[ax].spines['left'].set_position(('outward', shift))
compt += 1
else:
self._axes[ax].spines['right'].set_position(('outward', shift))
shift += 60
lines = {}
if "elevation" in self._y:
# meter_axes.set_ylim(
# bottom=min(0, min(z_min)),
# top=max(z_min) + 1
# )
line = meter_axes.plot(
ax = self._axes[unit["elevation"]]
line = ax.plot(
rk, z_min,
color='grey', lw=1.,
)
lines["elevation"] = line
if "water_elevation" in self._y:
# Water elevation
water_z = list(
map(
lambda p: p.get_ts_key(self._timestamp, "Z"),
reach.profiles
)
)
# meter_axes.set_ylim(
# bottom=min(0, min(z_min)),
# top=max(water_z) + 1
# )
line = meter_axes.plot(
rk, water_z, lw=1.,
ax = self._axes[unit["water_elevation"]]
line = ax.plot(
rk, z, lw=1.,
color='blue',
)
lines["water_elevation"] = line
if "elevation" in self._y:
meter_axes.fill_between(
rk, z_min, water_z,
ax.fill_between(
rk, z_min, z,
color='blue', alpha=0.5, interpolate=True
)
if "discharge" in self._y:
q = list(
map(
lambda p: p.get_ts_key(self._timestamp, "Q"),
reach.profiles
)
)
# m3s_axes.set_ylim(
# bottom=min(0, min(q)),
# top=max(q) + 1
# )
line = m3s_axes.plot(
ax = self._axes[unit["discharge"]]
line = ax.plot(
rk, q, lw=1.,
color='r',
)
lines["discharge"] = line
if "velocity" in self._y:
ax = self._axes[unit["velocity"]]
v = list(
map(
lambda p: p.geometry.speed(
p.get_ts_key(self._timestamp, "Q"),
p.get_ts_key(self._timestamp, "Z")),
reach.profiles
)
)
line = ax.plot(
rk, v, lw=1.,
color='g',
)
lines["velocity"] = line
if "depth" in self._y:
ax = self._axes[unit["depth"]]
d = list(
map(
lambda p: p.geometry.max_water_depth(
p.get_ts_key(self._timestamp, "Z")),
reach.profiles
)
)
line = ax.plot(
rk, d,
color='brown', lw=1.,
)
lines["depth"] = line
if "mean_depth" in self._y:
ax = self._axes[unit["mean_depth"]]
d = list(
map(
lambda p: p.geometry.mean_water_depth(
p.get_ts_key(self._timestamp, "Z")),
reach.profiles
)
)
line = ax.plot(
rk, d,
color='orange', lw=1.,
)
lines["mean_depth"] = line
if "froude" in self._y:
ax = self._axes[unit["froude"]]
fr = list(
map(
lambda p:
p.geometry.speed(
p.get_ts_key(self._timestamp, "Q"),
p.get_ts_key(self._timestamp, "Z")) /
sqrt(9.81 * (
p.geometry.wet_area(
p.get_ts_key(self._timestamp, "Z")) /
p.geometry.wet_width(
p.get_ts_key(self._timestamp, "Z"))
)),
reach.profiles
)
)
line = ax.plot(
rk, fr, color='black', linestyle='--', lw=1.,
)
lines["froude"] = line
if "wet_area" in self._y:
ax = self._axes[unit["wet_area"]]
d = list(
map(
lambda p: p.geometry.wet_area(
p.get_ts_key(self._timestamp, "Z")),
reach.profiles
)
)
line = ax.plot(
rk, d,
color='blue', linestyle='--', lw=1.,
)
lines["wet_area"] = line
# Legend
lns = reduce(
lambda acc, line: acc + line,
@ -151,7 +238,7 @@ class CustomPlot(PamhyrPlot):
[]
)
labs = list(map(lambda line: self._trad[line], lines))
self.canvas.axes.legend(lns, labs, loc="lower left")
self.canvas.axes.legend(lns, labs, loc="best")
def _customize_x_axes_time(self, ts, mode="time"):
# Custom time display
@ -198,79 +285,137 @@ class CustomPlot(PamhyrPlot):
reach = results.river.reach(self._reach)
profile = reach.profile(self._profile)
meter_axes = self.canvas.axes
m3S_axes = self.canvas.axes
if "0-meter" in self._y_axes and "1-m3s" in self._y_axes:
m3s_axes = self._axes["1-m3s"]
shift = 0
compt = 0
for ax in sorted(self._axes):
if compt == 0:
self._axes[ax].spines['left'].set_position(('outward', shift))
compt += 1
else:
self._axes[ax].spines['right'].set_position(('outward', shift))
shift += 60
ts = list(results.get("timestamps"))
ts.sort()
# self.canvas.axes.set_xlim(
# left=min(ts), right=max(ts)
# )
q = profile.get_key("Q")
z = profile.get_key("Z")
z_min = profile.geometry.z_min()
ts_z_min = list(
map(
lambda ts: z_min,
ts
)
)
x = ts
lines = {}
if "elevation" in self._y:
# Z min is constant in time
z_min = profile.geometry.z_min()
ts_z_min = list(
map(
lambda ts: z_min,
ts
)
)
line = meter_axes.plot(
ax = self._axes[unit["elevation"]]
line = ax.plot(
ts, ts_z_min,
color='grey', lw=1.
)
lines["elevation"] = line
if "water_elevation" in self._y:
# Water elevation
z = profile.get_key("Z")
# meter_axes.set_ylim(
# bottom=min(0, min(z)),
# top=max(z) + 1
# )
line = meter_axes.plot(
ax = self._axes[unit["water_elevation"]]
line = ax.plot(
ts, z, lw=1.,
color='b',
)
lines["water_elevation"] = line
if "elevation" in self._y:
z_min = profile.geometry.z_min()
ts_z_min = list(
map(
lambda ts: z_min,
ts
)
)
meter_axes.fill_between(
ax.fill_between(
ts, ts_z_min, z,
color='blue', alpha=0.5, interpolate=True
)
if "discharge" in self._y:
q = profile.get_key("Q")
# m3s_axes.set_ylim(
# bottom=min(0, min(q)),
# top=max(q) + 1
# )
line = m3s_axes.plot(
ax = self._axes[unit["discharge"]]
line = ax.plot(
ts, q, lw=1.,
color='r',
)
lines["discharge"] = line
if "velocity" in self._y:
ax = self._axes[unit["velocity"]]
v = list(
map(
lambda q, z: profile.geometry.speed(q, z),
q, z
)
)
line = ax.plot(
ts, v, lw=1.,
color='g',
)
lines["velocity"] = line
if "depth" in self._y:
ax = self._axes[unit["depth"]]
d = list(
map(lambda z: profile.geometry.max_water_depth(z), z)
)
line = ax.plot(
ts, d,
color='brown', lw=1.,
)
lines["depth"] = line
if "mean_depth" in self._y:
ax = self._axes[unit["mean_depth"]]
d = list(
map(lambda z: profile.geometry.mean_water_depth(z), z)
)
line = ax.plot(
ts, d,
color='orange', lw=1.,
)
lines["depth"] = line
if "froude" in self._y:
ax = self._axes[unit["froude"]]
d = list(
map(lambda z, q:
profile.geometry.speed(q, z) /
sqrt(9.81 * (
profile.geometry.wet_area(z) /
profile.geometry.wet_width(z))
), z, q)
)
line = ax.plot(
ts, d, color='black', linestyle='--', lw=1.,
)
lines["froude"] = line
if "wet_area" in self._y:
ax = self._axes[unit["wet_area"]]
d = list(
map(lambda z: profile.geometry.wet_area(z), z)
)
line = ax.plot(
ts, d, color='blue', linestyle='--', lw=1.,
)
lines["wet_area"] = line
self._customize_x_axes_time(ts)
# Legend
@ -280,7 +425,7 @@ class CustomPlot(PamhyrPlot):
[]
)
labs = list(map(lambda line: self._trad[line], lines))
self.canvas.axes.legend(lns, labs, loc="lower left")
self.canvas.axes.legend(lns, labs, loc="best")
@timer
def draw(self):
@ -300,6 +445,7 @@ class CustomPlot(PamhyrPlot):
color='black', fontsize=10
)
self._axes[self._y_axes[0]] = self.canvas.axes
for axes in self._y_axes[1:]:
if axes in self._axes:
self._axes[axes].clear()
@ -316,8 +462,22 @@ class CustomPlot(PamhyrPlot):
self._draw_rk()
elif self._x == "time":
self._draw_time()
if self._x == "rk":
reach = self.data.river.reach(self._reach)
profile = reach.profile(self._profile)
x = profile.rk
elif self._x == "time":
x = self._timestamp
self._current, = self.canvas.axes.plot(
[x, x],
self.canvas.axes.get_ylim(),
# label=self.label_timestamp,
color='grey',
linestyle="dashed",
lw=1.,
)
self.canvas.figure.tight_layout()
self.canvas.figure.canvas.draw_idle()
if self.toolbar is not None:
self.toolbar.update()
@ -326,6 +486,7 @@ class CustomPlot(PamhyrPlot):
def update(self):
if not self._init:
self.draw()
self.draw_current()
return
def set_reach(self, reach_id):
@ -339,9 +500,23 @@ class CustomPlot(PamhyrPlot):
if self._x != "rk":
self.update()
else:
self.draw_current()
def set_timestamp(self, timestamp):
self._timestamp = timestamp
if self._x != "time":
self.update()
else:
self.draw_current()
def draw_current(self):
if self._x == "rk":
reach = self.data.river.reach(self._reach)
profile = reach.profile(self._profile)
x = profile.rk
elif self._x == "time":
x = self._timestamp
self._current.set_data([x, x], self.canvas.axes.get_ylim())
self.canvas.figure.canvas.draw_idle()

View File

@ -40,6 +40,14 @@ class CustomPlotTranslate(ResultsTranslate):
self._dict['elevation'] = _translate(
"CustomPlot", "Bed elevation (m)"
)
self._dict['velocity'] = self._dict["unit_speed"]
self._dict['width'] = self._dict["unit_width"]
self._dict['max_depth'] = self._dict["unit_max_height"]
self._dict['mean_depth'] = self._dict["unit_mean_height"]
self._dict['wet_area'] = self._dict["unit_wet_area"]
self._dict['wet_perimeter'] = self._dict["unit_wet_perimeter"]
self._dict['hydraulic_radius'] = self._dict["unit_hydraulic_radius"]
self._dict['froude'] = self._dict["unit_froude"]
# Unit corresponding long name (plot axes display)
@ -47,6 +55,10 @@ class CustomPlotTranslate(ResultsTranslate):
"CustomPlot", "Bed elevation (m)"
)
self._dict['1-m3s'] = self._dict["unit_discharge"]
self._dict['2-ms'] = self._dict["unit_speed"]
self._dict['3-meter'] = self._dict["unit_height"]
self._dict['4-dimensionless'] = self._dict["unit_froude"]
self._dict['5-m2'] = self._dict["wet_area"]
# SubDict
@ -58,4 +70,9 @@ class CustomPlotTranslate(ResultsTranslate):
"elevation": self._dict["elevation"],
"water_elevation": self._dict["water_elevation"],
"discharge": self._dict["discharge"],
"velocity": self._dict["velocity"],
"depth": self._dict["max_depth"],
"mean_depth": self._dict["mean_depth"],
"froude": self._dict["froude"],
"wet_area": self._dict["wet_area"],
}

View File

@ -88,13 +88,14 @@ class PlotH(PamhyrPlot):
self.draw_max(reach)
self.draw_data(reach, profile)
self.draw_current(reach, profile)
self.draw_current()
self.set_ticks_time_formater()
self.enable_legend()
self.idle()
self.update_current()
self._init = True
def draw_data(self, reach, profile):
@ -111,21 +112,12 @@ class PlotH(PamhyrPlot):
**self.plot_default_kargs
)
def draw_current(self, reach, profile):
min_y, max_y = reduce(
lambda acc, p: (
acc[0] + [min(p.get_key("Q"))],
acc[1] + [max(p.get_key("Q"))]
),
reach.profiles,
([], [])
)
def draw_current(self):
self._current, = self.canvas.axes.plot(
[self._current_timestamp, self._current_timestamp],
[min(min_y), max(max_y)],
self.canvas.axes.get_ylim(),
# label=self.label_timestamp,
color=self.color_plot_river_bottom,
color="grey",
linestyle="dashed",
lw=1.,
)
@ -162,14 +154,14 @@ class PlotH(PamhyrPlot):
def set_timestamp(self, timestamp):
self._current_timestamp = timestamp
self.update()
self.update_current()
self.update_idle()
def update(self):
if not self._init:
self.draw()
self.update_data()
self.update_idle()
def update_data(self):
@ -181,8 +173,13 @@ class PlotH(PamhyrPlot):
self._line.set_data(x, y)
_, min_max = self._current.get_data()
self._current.set_data(
self._current_timestamp,
min_max
self.canvas.axes.get_ylim()
)
def update_current(self):
self._current.set_data(
self._current_timestamp,
self.canvas.axes.get_ylim()
)

View File

@ -19,6 +19,8 @@
import logging
import traceback
from numpy import sqrt
from tools import timer, trace
from PyQt5.QtGui import (
@ -86,12 +88,46 @@ class TableModel(PamhyrTableModel):
elif self._headers[column] == "discharge":
v = self._lst[row].get_ts_key(self._timestamp, "Q")
return f"{v:.4f}"
elif self._headers[column] == "speed":
elif self._headers[column] == "velocity":
q = self._lst[row].get_ts_key(self._timestamp, "Q")
z = self._lst[row].get_ts_key(self._timestamp, "Z")
v = self._lst[row].geometry.speed(q, z)
return f"{v:.4f}"
elif self._headers[column] == "width":
z = self._lst[row].get_ts_key(self._timestamp, "Z")
v = self._lst[row].geometry.wet_width(z)
return f"{v:.4f}"
elif self._headers[column] == "max_depth":
z = self._lst[row].get_ts_key(self._timestamp, "Z")
v = self._lst[row].geometry.max_water_depth(z)
return f"{v:.4f}"
elif self._headers[column] == "mean_depth":
z = self._lst[row].get_ts_key(self._timestamp, "Z")
v = self._lst[row].geometry.mean_water_depth(z)
return f"{v:.4f}"
elif self._headers[column] == "wet_area":
z = self._lst[row].get_ts_key(self._timestamp, "Z")
v = self._lst[row].geometry.wet_area(z)
return f"{v:.4f}"
elif self._headers[column] == "wet_perimeter":
z = self._lst[row].get_ts_key(self._timestamp, "Z")
v = self._lst[row].geometry.wet_perimeter(z)
return f"{v:.4f}"
elif self._headers[column] == "hydraulic_radius":
z = self._lst[row].get_ts_key(self._timestamp, "Z")
v = self._lst[row].geometry.wet_radius(z)
return f"{v:.4f}"
elif self._headers[column] == "froude":
q = self._lst[row].get_ts_key(self._timestamp, "Q")
z = self._lst[row].get_ts_key(self._timestamp, "Z")
v = self._lst[row].geometry.speed(q, z)
a = self._lst[row].geometry.wet_area(z)
b = self._lst[row].geometry.wet_width(z)
froude = v / sqrt(9.81 * (a / b))
return f"{froude:.4f}"
else:
v = 0.0
return f"{v:.4f}"
return QVariant()

View File

@ -40,7 +40,7 @@ from PyQt5.QtWidgets import (
QFileDialog, QTableView, QAbstractItemView,
QUndoStack, QShortcut, QAction, QItemDelegate,
QComboBox, QVBoxLayout, QHeaderView, QTabWidget,
QSlider, QLabel, QWidget, QGridLayout,
QSlider, QLabel, QWidget, QGridLayout, QTabBar
)
from View.Tools.Plot.PamhyrCanvas import MplCanvas
@ -108,9 +108,9 @@ class ResultsWindow(PamhyrWindow):
try:
self._timestamps = sorted(list(self._results.get("timestamps")))
self.setup_slider()
self.setup_table()
self.setup_plots()
self.setup_slider()
self.setup_statusbar()
self.setup_connections()
except Exception as e:
@ -128,12 +128,11 @@ class ResultsWindow(PamhyrWindow):
undo=self._undo_stack,
opt_data=t
)
self._table[t]._timestamp = self._timestamps[
self._slider_time.value()]
def setup_slider(self):
self._slider_profile = self.find(QSlider, f"verticalSlider_profile")
default_reach = self._results.river.reach(0)
self._slider_profile.setMaximum(len(default_reach.profiles) - 1)
self._slider_profile.setValue(0)
self._slider_time = self.find(QSlider, f"horizontalSlider_time")
self._slider_time.setMaximum(len(self._timestamps) - 1)
@ -158,6 +157,12 @@ class ResultsWindow(PamhyrWindow):
def setup_plots(self):
self.canvas = MplCanvas(width=5, height=4, dpi=100)
tab_widget = self.find(QTabWidget, f"tabWidget")
tab_widget.setTabsClosable(True)
tab_widget.tabCloseRequested.connect(self.delete_tab)
tab_widget.tabBar().setTabButton(0, QTabBar.RightSide, None)
tab_widget.tabBar().setTabButton(1, QTabBar.RightSide, None)
tab_widget.tabBar().setTabButton(2, QTabBar.RightSide, None)
self.canvas.setObjectName("canvas")
self.toolbar = PamhyrPlotToolbar(
self.canvas, self, items=[
@ -303,7 +308,8 @@ class ResultsWindow(PamhyrWindow):
actions = {
"action_reload": self._reload,
"action_add": self._add_custom_plot,
"action_export": self.export,
# "action_export": self.export,
"action_export": self.export_current,
}
for action in actions:
@ -327,8 +333,6 @@ class ResultsWindow(PamhyrWindow):
self._table[t].dataChanged.connect(fun[t])
self._slider_profile.valueChanged.connect(
self._set_current_profile_slider)
self._slider_time.valueChanged.connect(self._set_current_timestamp)
self._button_play.setChecked(False)
self._button_play.clicked.connect(self._pause)
@ -442,7 +446,6 @@ class ResultsWindow(PamhyrWindow):
ind = indexes[0].row()
self.update(profile_id=ind)
self._slider_profile.setValue(ind)
def _set_current_profile_raw_data(self):
table = self.find(QTableView, f"tableView_raw_data")
@ -452,11 +455,6 @@ class ResultsWindow(PamhyrWindow):
ind = indexes[0].row()
self.update(profile_id=ind)
self._slider_profile.setValue(ind)
def _set_current_profile_slider(self):
pid = self._slider_profile.value()
self.update(profile_id=pid)
def _set_current_timestamp(self):
timestamp = self._timestamps[self._slider_time.value()]
@ -500,7 +498,7 @@ class ResultsWindow(PamhyrWindow):
tab_widget = self.find(QTabWidget, f"tabWidget")
# This plot already exists
if name in self._additional_plot:
if name in [tab_widget.tabText(i) for i in range(tab_widget.count())]:
tab_widget.setCurrentWidget(
tab_widget.findChild(QWidget, wname)
)
@ -591,12 +589,15 @@ class ResultsWindow(PamhyrWindow):
)
def export_to(self, directory):
timestamps = sorted(self._results.get("timestamps"))
for reach in self._results.river.reachs:
self.export_reach(reach, directory)
self.export_reach(reach, directory, timestamps)
def export_reach(self, reach, directory):
def export_reach(self, reach, directory, timestamps):
name = reach.name
name = name.replace(" ", "-")
if len(timestamps) == 1:
name = f"{name}_t{timestamps[0]}"
file_name = os.path.join(
directory,
@ -606,28 +607,40 @@ class ResultsWindow(PamhyrWindow):
with open(file_name, 'w', newline='') as csvfile:
writer = csv.writer(csvfile, delimiter=',',
quotechar='|', quoting=csv.QUOTE_MINIMAL)
writer.writerow(["name", "rk", "data-file"])
for profile in reach.profiles:
p_file_name = os.path.join(
directory,
f"cs_{profile.geometry.id}.csv"
)
if len(timestamps) > 1:
writer.writerow(["name", "rk", "data-file"])
for profile in reach.profiles:
p_file_name = os.path.join(
directory,
f"cs_{profile.geometry.id}.csv"
)
writer.writerow([
profile.name,
profile.rk,
p_file_name
])
writer.writerow([
profile.name,
profile.rk,
p_file_name
])
self.export_profile(reach, profile, p_file_name)
self.export_profile(reach,
profile,
p_file_name,
timestamps)
else:
ts = timestamps[0]
writer.writerow(self._table["raw_data"]._headers)
for row in range(self._table["raw_data"].rowCount()):
line = []
for column in range(self._table["raw_data"].columnCount()):
index = self._table["raw_data"].index(row, column)
line.append(self._table["raw_data"].data(index))
writer.writerow(line)
def export_profile(self, reach, profile, file_name):
def export_profile(self, reach, profile, file_name, timestamps):
with open(file_name, 'w', newline='') as csvfile:
writer = csv.writer(csvfile, delimiter=',',
quotechar='|', quoting=csv.QUOTE_MINIMAL)
writer.writerow(["timestamp", "z", "q"])
timestamps = sorted(self._results.get("timestamps"))
for ts in timestamps:
writer.writerow([
@ -635,3 +648,18 @@ class ResultsWindow(PamhyrWindow):
profile.get_ts_key(ts, "Z"),
profile.get_ts_key(ts, "Q"),
])
def export_current(self):
self.file_dialog(
select_file=False,
callback=lambda d: self.export_current_to(d[0])
)
def export_current_to(self, directory):
reach = self._results.river.reachs[self._get_current_reach()]
self.export_reach(reach, directory, [self._get_current_timestamp()])
def delete_tab(self, index):
tab_widget = self.find(QTabWidget, f"tabWidget")
self._additional_plot.pop(tab_widget.tabText(index))
tab_widget.removeTab(index)

View File

@ -57,5 +57,12 @@ class ResultsTranslate(MainTranslate):
"name": _translate("Results", "Profile"),
"water_elevation": self._dict["unit_water_elevation"],
"discharge": self._dict["unit_discharge"],
"speed": self._dict["unit_speed"],
"velocity": self._dict["unit_speed"],
"width": self._dict["unit_width"],
"max_depth": self._dict["unit_max_height"],
"mean_depth": self._dict["unit_mean_height"],
"wet_area": self._dict["unit_wet_area"],
"wet_perimeter": self._dict["unit_wet_perimeter"],
"hydraulic_radius": self._dict["unit_hydraulic_radius"],
"froude": self._dict["unit_froude"],
}

View File

@ -192,7 +192,6 @@ class PamhyrPlot(APlot):
self.canvas.axes.autoscale_view(True, True, True)
self.canvas.axes.autoscale()
self.canvas.figure.tight_layout()
self.canvas.figure.canvas.draw_idle()
self.toolbar_update()
@ -205,7 +204,6 @@ class PamhyrPlot(APlot):
self.canvas.axes.autoscale_view(True, True, True)
self.canvas.axes.autoscale()
self.canvas.figure.tight_layout()
self.canvas.figure.canvas.draw_idle()
self.toolbar_update()

View File

@ -23,7 +23,7 @@ class MplCanvas(FigureCanvasQTAgg):
fig = Figure(
figsize=(width, height),
dpi=dpi,
layout='tight',
layout='constrained',
)
super(MplCanvas, self).__init__(fig)
@ -36,7 +36,6 @@ class MplCanvas(FigureCanvasQTAgg):
self.axes.yaxis.tick_left()
self.axes.xaxis.tick_bottom()
self.axes.spines[['top', 'right']].set_color('none')
self.figure.tight_layout()
self.add_arrows()
def add_arrows(self):

View File

@ -55,16 +55,18 @@ class UnitTranslate(CommonWordTranslate):
def __init__(self):
super(UnitTranslate, self).__init__()
self._dict["unit_rk"] = _translate("Unit", "River Kilometric (m)")
self._dict["unit_rk"] = _translate("Unit", "River Kilometer (m)")
self._dict["unit_width"] = _translate("Unit", "Width (m)")
self._dict["unit_height"] = _translate("Unit", "Height (m)")
self._dict["unit_height"] = _translate("Unit", "Depth (m)")
self._dict["unit_max_height"] = _translate("Unit", "Max Depth (m)")
self._dict["unit_mean_height"] = _translate("Unit", "Mean Depth (m)")
self._dict["unit_diameter"] = _translate("Unit", "Diameter (m)")
self._dict["unit_thickness"] = _translate("Unit", "Thickness (m)")
self._dict["unit_elevation"] = _translate("Unit", "Elevation (m)")
self._dict["unit_water_elevation"] = _translate(
"Unit", "Water elevation (m)"
)
self._dict["unit_speed"] = _translate("Unit", "Speed (m/s)")
self._dict["unit_speed"] = _translate("Unit", "Velocity (m/s)")
self._dict["unit_discharge"] = _translate("Unit", "Discharge (m³/s)")
self._dict["unit_area"] = _translate("Unit", "Area (hectare)")
@ -74,6 +76,15 @@ class UnitTranslate(CommonWordTranslate):
self._dict["unit_date_s"] = _translate("Unit", "Date (sec)")
self._dict["unit_date_iso"] = _translate("Unit", "Date (ISO format)")
self._dict["unit_wet_area"] = _translate("Unit", "Wet Area (m²)")
self._dict["unit_wet_perimeter"] = _translate(
"Unit", "Wet Perimeter (m)"
)
self._dict["unit_hydraulic_radius"] = _translate(
"Unit", "Hydraulic Radius (m)"
)
self._dict["unit_froude"] = _translate("Unit", "Froude number")
class MainTranslate(UnitTranslate):
def __init__(self):

View File

@ -0,0 +1,98 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Dialog</class>
<widget class="QDialog" name="Dialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>356</width>
<height>107</height>
</rect>
</property>
<property name="windowTitle">
<string>Options</string>
</property>
<property name="accessibleName">
<string/>
</property>
<property name="locale">
<locale language="English" country="Europe"/>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="2" column="0">
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
<item row="0" column="0">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Slope</string>
</property>
</widget>
</item>
<item>
<widget class="QDoubleSpinBox" name="doubleSpinBox">
<property name="decimals">
<number>6</number>
</property>
<property name="maximum">
<double>999999.998999999952503</double>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="EstimateButton">
<property name="text">
<string>Estimate</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>Dialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>Dialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@ -73,6 +73,8 @@
<addaction name="action_add"/>
<addaction name="action_del"/>
<addaction name="action_sort"/>
<addaction name="action_generate_uniform"/>
<addaction name="action_generate_critical"/>
</widget>
<action name="action_add">
<property name="checkable">
@ -119,6 +121,25 @@
<string>Sort points</string>
</property>
</action>
<action name="action_generate_uniform">
<property name="text">
<string>Generate uniform</string>
</property>
<property name="iconText">
<string>Generate uniform</string>
</property>
<property name="toolTip">
<string>Generate rating curve from Manning law</string>
</property>
</action>
<action name="action_generate_critical">
<property name="text">
<string>Generate critical</string>
</property>
<property name="toolTip">
<string>Generate rating curve as Q(z) = Sqrt(g*S(z)^3/L(z))</string>
</property>
</action>
</widget>
<resources/>
<connections/>

View File

@ -92,6 +92,7 @@
<addaction name="action_meshing"/>
<addaction name="action_update_rk"/>
<addaction name="action_purge"/>
<addaction name="action_shift"/>
</widget>
<action name="action_import">
<property name="icon">
@ -226,6 +227,14 @@
<string>Purge cross-sections to keep a given number of points</string>
</property>
</action>
<action name="action_shift">
<property name="text">
<string>Shift</string>
</property>
<property name="toolTip">
<string>Shift selected sections coordinates</string>
</property>
</action>
</widget>
<resources/>
<connections/>

View File

@ -0,0 +1,134 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Dialog</class>
<widget class="QDialog" name="Dialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>381</width>
<height>144</height>
</rect>
</property>
<property name="windowTitle">
<string>Dialog</string>
</property>
<property name="layoutDirection">
<enum>Qt::LeftToRight</enum>
</property>
<layout class="QGridLayout" name="gridLayout_4">
<item row="3" column="0">
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
<item row="1" column="0">
<layout class="QGridLayout" name="gridLayout">
<item row="1" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Y coordinate</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_1">
<property name="text">
<string>X coordinate</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QDoubleSpinBox" name="doubleSpinBox_Y">
<property name="decimals">
<number>4</number>
</property>
<property name="minimum">
<double>-99999999.000000000000000</double>
</property>
<property name="maximum">
<double>99999999.000000000000000</double>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QDoubleSpinBox" name="doubleSpinBox_X">
<property name="decimals">
<number>4</number>
</property>
<property name="minimum">
<double>-99999999.000000000000000</double>
</property>
<property name="maximum">
<double>99999999.000000000000000</double>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_3">
<property name="enabled">
<bool>true</bool>
</property>
<property name="text">
<string>Z coordinate</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QDoubleSpinBox" name="doubleSpinBox_Z">
<property name="decimals">
<number>4</number>
</property>
<property name="minimum">
<double>-99999999.000000000000000</double>
</property>
<property name="maximum">
<double>99999999.000000000000000</double>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>Dialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>Dialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@ -27,14 +27,14 @@
<item>
<widget class="QPushButton" name="pushButton_generate_1">
<property name="text">
<string>Generate from height</string>
<string>Generate height</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButton_generate_2">
<property name="text">
<string>Generate from discharge</string>
<string>Generate discharge</string>
</property>
</widget>
</item>

View File

@ -39,19 +39,6 @@
<item>
<widget class="QTableView" name="tableView_profile"/>
</item>
<item>
<widget class="QSlider" name="verticalSlider_profile">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="invertedAppearance">
<bool>true</bool>
</property>
<property name="invertedControls">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
@ -145,7 +132,10 @@
<item row="0" column="0">
<widget class="QTabWidget" name="tabWidget">
<property name="currentIndex">
<number>1</number>
<number>0</number>
</property>
<property name="tabsClosable">
<bool>true</bool>
</property>
<widget class="QWidget" name="tab_4">
<attribute name="title">

View File

@ -2168,13 +2168,13 @@
</message>
<message>
<location filename="../View/ui/InitialConditions.ui" line="30"/>
<source>Generate from height</source>
<translation>Générer pour une hauteur donnée</translation>
<source>Generate height</source>
<translation>Générer une hauteur</translation>
</message>
<message>
<location filename="../View/ui/InitialConditions.ui" line="37"/>
<source>Generate from discharge</source>
<translation>Générer pour un débit donné</translation>
<source>Generate discharge</source>
<translation>Générer un débit</translation>
</message>
<message>
<location filename="../View/ui/InitialConditions.ui" line="98"/>
@ -3057,7 +3057,7 @@
</message>
<message>
<location filename="../View/Translate.py" line="57"/>
<source>Height (m)</source>
<source>Depth (m)</source>
<translation>Hauteur (m)</translation>
</message>
<message>