Compare commits

..

8 Commits

Author SHA1 Message Date
Theophile Terraz 016f3fc73c Merge branch 'master' into scenarios 2025-10-14 11:52:44 +02:00
Theophile Terraz 7ef3184699 debug + disable rasterio 2025-10-14 11:52:28 +02:00
Theophile Terraz 5f6c823301 debug results 2025-10-14 11:45:45 +02:00
Theophile Terraz a42c46f4d2 speed up internal meshing 2025-10-14 09:41:51 +02:00
Theophile Terraz fb09cf254e speed up internal meshing 2025-10-14 09:40:43 +02:00
Theophile Terraz 315b913fd0 Merge branch 'scenarios' of gitlab.com:pamhyr/pamhyr2 into scenarios 2025-10-14 09:01:16 +02:00
Theophile Terraz 0383dc18ae pep8 2025-10-13 17:50:15 +02:00
Theophile Terraz f16856d23b debug mesh + update_rk (verry slow mesh) 2025-10-13 17:48:48 +02:00
10 changed files with 254 additions and 170 deletions

View File

@ -50,20 +50,23 @@ class InternalMeshing(AMeshingTool):
elif limites[0] == limites[1]: elif limites[0] == limites[1]:
return reach return reach
self.st_to_m(reach, limites) # we never modify original profiles, we work on copies
self.interpolate_transversal_step(reach, m_profiles = self.st_to_m(reach, limites)
limites, new_profiles = self.interpolate_transversal_step(m_profiles,
step) step)
self.purge_aligned_points(reach)
return reach for new_profiles2 in new_profiles:
for p in new_profiles2:
p.hard_purge_aligned_points()
return new_profiles
def st_to_m(self, reach, limites): def st_to_m(self, reach, limites):
gl = reach.compute_guidelines() gl = reach.compute_guidelines()
profiles = reach.profiles[limites[0]:limites[1]+1] profiles = [reach.profile(i).copy()
for i in range(limites[0], limites[1]+1)
print(profiles) ]
# we make sure that the lines are in the left-to-right order # we make sure that the lines are in the left-to-right order
guide_list = [ guide_list = [
@ -86,9 +89,9 @@ class InternalMeshing(AMeshingTool):
for i in range(len(guide_list) - 1): for i in range(len(guide_list) - 1):
index1 = p.named_point_index(guide_list[i]) index1 = p.named_point_index(guide_list[i])
index2 = p.named_point_index(guide_list[i+1]) index2 = p.named_point_index(guide_list[i+1])
if index2 - index1 > max_values[i + 1]: if index2 - index1 > max_values[i+1]:
max_values[i + 1] = index2 - index1 max_values[i+1] = index2 - index1
max_values_index[i + 1] = j max_values_index[i+1] = j
index1 = p.named_point_index(guide_list[-1]) index1 = p.named_point_index(guide_list[-1])
index2 = len(p) - 1 index2 = len(p) - 1
if index2 - index1 > max_values[-1]: if index2 - index1 > max_values[-1]:
@ -104,46 +107,46 @@ class InternalMeshing(AMeshingTool):
self.compl_sect(profiles[isect+1], self.compl_sect(profiles[isect+1],
profiles[isect], profiles[isect],
gl1[i], gl2[i]) gl1[i], gl2[i])
return profiles
def interpolate_transversal_step(self, def interpolate_transversal_step(self,
reach, profiles,
limites,
step): step):
# calcul number of intermediate profiles # calcul number of intermediate profiles
np = [] np = []
for i in range(limites[0], limites[1]): for i in range(len(profiles)-1):
np.append(int((reach.profiles[i+1].rk - np.append(int((profiles[i+1].rk -
reach.profiles[i].rk) / step) - 1) profiles[i].rk) / step) - 1)
if np[-1] < 0: if np[-1] < 0:
np[-1] = 0 np[-1] = 0
d = [] # ratios new_profiles = []
p = [] # new profiles for i in range(len(profiles)-1):
ptr = int(limites[0]) new_profiles2 = []
for i in range(limites[1] - limites[0]):
d = 1.0/float(np[i]+1) d = 1.0/float(np[i]+1)
ptr0 = ptr
for j in range(np[i]): for j in range(np[i]):
p = reach.profiles[ptr0].copy() p = profiles[i].copy()
# RATIO entre les deux sections initiales # RATIO entre les deux sections initiales
dj = float(j+1)*d dj = float(j+1)*d
ptr += 1 # next profile, original pt1 = profiles[i].points
for k in range(len(reach.profiles[ptr0].points)): pt2 = profiles[i+1].points
p.points[k].x = reach.profiles[ptr0].points[k].x + \ for k in range(len(pt1)):
dj*(reach.profiles[ptr].points[k].x - p.points[k].x = pt1[k].x + \
reach.profiles[ptr0].points[k].x) dj*(pt2[k].x -
p.points[k].y = reach.profiles[ptr0].points[k].y + \ pt1[k].x)
dj*(reach.profiles[ptr].points[k].y - p.points[k].y = pt1[k].y + \
reach.profiles[ptr0].points[k].y) dj*(pt2[k].y -
p.points[k].z = reach.profiles[ptr0].points[k].z + \ pt1[k].y)
dj*(reach.profiles[ptr].points[k].z - p.points[k].z = pt1[k].z + \
reach.profiles[ptr0].points[k].z) dj*(pt2[k].z -
p.rk = reach.profiles[ptr0].rk + \ pt1[k].z)
dj*(reach.profiles[ptr].rk - p.rk = profiles[i].rk + \
reach.profiles[ptr0].rk) dj*(profiles[i+1].rk -
profiles[i].rk)
p.name = f'interpol{p.rk}' p.name = f'interpol{p.rk}'
reach.insert_profile(ptr, p) new_profiles2.append(p)
ptr += 1 # next profile, original new_profiles.append(new_profiles2)
return new_profiles
def compl_sect(self, sect1, sect2, tag1, tag2): def compl_sect(self, sect1, sect2, tag1, tag2):
# sect1: reference section # sect1: reference section
@ -281,14 +284,14 @@ class InternalMeshing(AMeshingTool):
origin_value=0.0, origin_value=0.0,
orientation=0): orientation=0):
if reach is None or len(reach.profiles) == 0: if reach is None or len(reach.profiles) == 0:
return reach return []
nprof = reach.number_profiles nprof = reach.number_profiles
if origin >= nprof or origin < 0: if origin >= nprof or origin < 0:
logger.warning( logger.warning(
f"Origin outside reach" f"Origin outside reach"
) )
return reach return reach.get_rk()
# orientation is the orintation of the bief: # orientation is the orintation of the bief:
# 1: up -> downstream # 1: up -> downstream
@ -301,7 +304,8 @@ class InternalMeshing(AMeshingTool):
else: else:
sgn = sign(reach.profiles[-1].rk - reach.profiles[0].rk) sgn = sign(reach.profiles[-1].rk - reach.profiles[0].rk)
reach.profiles[origin].rk = origin_value rk = [0.0]*nprof
rk[origin] = origin_value
if origin < nprof - 1: if origin < nprof - 1:
for i in range(origin+1, nprof): for i in range(origin+1, nprof):
@ -312,8 +316,7 @@ class InternalMeshing(AMeshingTool):
d2 = reach.profiles[i-1].named_point( d2 = reach.profiles[i-1].named_point(
directrices[1] directrices[1]
).dist_2d(reach.profiles[i].named_point(directrices[1])) ).dist_2d(reach.profiles[i].named_point(directrices[1]))
reach.profiles[i].rk = reach.profiles[i-1].rk \ rk[i] = rk[i-1] + (sgn * (d1 + d2) / 2)
+ (sgn * (d1 + d2) / 2)
if origin > 0: if origin > 0:
for i in reversed(range(0, origin)): for i in reversed(range(0, origin)):
# 2D # 2D
@ -323,9 +326,6 @@ class InternalMeshing(AMeshingTool):
d2 = reach.profiles[i+1].named_point( d2 = reach.profiles[i+1].named_point(
directrices[1] directrices[1]
).dist_2d(reach.profiles[i].named_point(directrices[1])) ).dist_2d(reach.profiles[i].named_point(directrices[1]))
reach.profiles[i].rk = reach.profiles[i+1].rk \ rk[i] = rk[i+1] - (sgn * (d1 + d2) / 2)
- (sgn * (d1 + d2) / 2)
def purge_aligned_points(self, reach): return rk
for p in reach.profiles:
p.purge_aligned_points()

View File

@ -278,7 +278,7 @@ class PointXYZ(Point):
id=-1, id=-1,
name=self.name, name=self.name,
x=self.x, y=self.y, z=self.z, x=self.x, y=self.y, z=self.z,
profile=self.profile, profile=self._profile,
status=self._status status=self._status
) )
if self.is_deleted(): if self.is_deleted():
@ -406,13 +406,3 @@ class PointXYZ(Point):
d = np.sqrt(abs(a2 - pow((c2 - b2 + a2) / (2*np.sqrt(c2)), 2))) d = np.sqrt(abs(a2 - pow((c2 - b2 + a2) / (2*np.sqrt(c2)), 2)))
return d return d
def copy(self):
p = PointXYZ(self.x,
self.y,
self.z,
self.name,
self._profile,
self._status)
p.sl = self.sl
return p

View File

@ -1109,14 +1109,12 @@ class ProfileXYZ(Profile, SQLSubModel):
self.point(i+1).z = 0.5 * self.point(i).z + 0.5 * self.point(i+2).z self.point(i+1).z = 0.5 * self.point(i).z + 0.5 * self.point(i+2).z
def copy(self): def copy(self):
p = ProfileXYZ(self.id, p = ProfileXYZ(id=self.id,
self.name, name=self.name,
self.rk, rk=self.rk,
self.reach, reach=self.reach,
self.num, num=self.num,
0, status=self._status)
0, 0,
self._status)
for i, k in enumerate(self.points): for i, k in enumerate(self.points):
p.insert_point(i, k.copy()) p.insert_point(i, k.copy())
@ -1134,3 +1132,18 @@ class ProfileXYZ(Profile, SQLSubModel):
align = True align = True
self.delete_i([i]) self.delete_i([i])
break break
def hard_purge_aligned_points(self):
align = True
points = self.points
while (align):
align = False
for i in range(1, len(points) - 1):
d = points[i].dist_from_seg(points[i-1],
points[i+1])
if d < 0.00001 and points[i].name == "":
align = True
points.pop(i)
break
self._points = points
self.modified()

View File

@ -220,6 +220,26 @@ class Reach(SQLSubModel):
) )
self.modified() self.modified()
def hard_delete(self, indexes):
"""Delete some elements in profile list
Args:
indexes: The list of index to delete
Returns:
Nothing.
"""
self._profiles = list(
map(
lambda p: p[1],
filter(
lambda e: e[0] not in indexes,
enumerate(self._profiles)
)
)
)
self.modified()
def delete_profiles(self, profiles): def delete_profiles(self, profiles):
"""Delete some elements in profile list """Delete some elements in profile list

View File

@ -250,12 +250,12 @@ class GeometryReachTableModel(PamhyrTableModel):
self.layoutAboutToBeChanged.emit() self.layoutAboutToBeChanged.emit()
self.layoutChanged.emit() self.layoutChanged.emit()
def meshing(self, mesher, data): def meshing(self, mesher, data, tableView):
self.layoutAboutToBeChanged.emit() self.layoutAboutToBeChanged.emit()
self._undo.push( self._undo.push(
MeshingCommand( MeshingCommand(
self._data, mesher, data, "mesh" self._data, mesher, data, tableView
) )
) )
@ -266,8 +266,8 @@ class GeometryReachTableModel(PamhyrTableModel):
self.layoutAboutToBeChanged.emit() self.layoutAboutToBeChanged.emit()
self._undo.push( self._undo.push(
MeshingCommand( UpdateRKCommand(
self._data, mesher, data, "update_rk" self._data, mesher, data
) )
) )

View File

@ -22,8 +22,10 @@ from copy import deepcopy
from tools import trace, timer, logger_exception from tools import trace, timer, logger_exception
from PyQt5.QtWidgets import ( from PyQt5.QtWidgets import (
QMessageBox, QUndoCommand, QUndoStack, QTableView QMessageBox, QUndoCommand, QUndoStack, QTableView,
) )
from PyQt5.QtCore import (QItemSelection, QItemSelectionRange,
QItemSelectionModel)
from Model.Geometry import Reach from Model.Geometry import Reach
from Model.Except import exception_message_box from Model.Except import exception_message_box
@ -227,46 +229,110 @@ class ImportCommand(QUndoCommand):
self._reach.insert_profile(self._row, profile) self._reach.insert_profile(self._row, profile)
class MeshingCommand(QUndoCommand): class UpdateRKCommand(QUndoCommand):
def __init__(self, reach, mesher, data, command): def __init__(self, reach, mesher, data):
QUndoCommand.__init__(self) QUndoCommand.__init__(self)
self._reach = reach self._reach = reach
self._data = data self._data = data
self._mesher = mesher self._mesher = mesher
self._command = command self._rks = reach.get_rk()
self._profiles = [p.copy() for p in reach.profiles] self._new_rks = None
self._profiles.reverse()
self._new_profiles = None
def undo(self): def undo(self):
self._reach.purge() for rk, profile in zip(self._rks, self._reach.profiles):
profile.rk = rk
for profile in self._profiles: def redo(self):
self._reach.insert_profile(0, profile) if self._new_rks is None:
self._new_rks = self._mesher.update_rk(
self._reach,
**self._data
)
for rk, profile in zip(self._new_rks, self._reach.profiles):
profile.rk = rk
class MeshingCommand(QUndoCommand):
def __init__(self, reach, mesher, data, tableView):
QUndoCommand.__init__(self)
self._reach = reach
self._data = data
self._limites = data["limites"]
self._mesher = mesher
self._new_profiles = None
self._new_profiles_indexes = None
self._tableView = tableView
self._indexes = tableView.selectionModel().selection()
rows = list(
set(
(i.row() for i in tableView.selectedIndexes())
)
)
self._selected_rk = [self._reach.profile(r).rk for r in rows]
self._new_indexes = None
def undo(self):
self._reach.hard_delete(self._new_profiles_indexes)
# Update selection
selection = self._tableView.selectionModel()
selection.select(
self._indexes,
QItemSelectionModel.Rows |
QItemSelectionModel.ClearAndSelect |
QItemSelectionModel.Select
)
def redo(self): def redo(self):
if self._new_profiles is None: if self._new_profiles is None:
if self._command == "update_rk": self._new_profiles = self._mesher.meshing(
self._mesher.update_rk( self._reach,
self._reach, **self._data
**self._data )
)
else:
self._mesher.meshing(
self._reach,
**self._data
)
self._new_profiles = [p.copy() for p in self._reach.profiles] if self._new_profiles_indexes is None:
self._new_profiles.reverse() k = self._limites[0]
else: self._new_profiles_indexes = []
self._reach.purge() for i in range(self._limites[1] - self._limites[0]):
k += 1
for p in self._new_profiles[i]:
self._new_profiles_indexes.append(k)
k += 1
for profile in self._new_profiles: k = self._limites[0]
self._reach.insert_profile(0, profile) for i in range(self._limites[1] - self._limites[0]):
k += 1
for p in self._new_profiles[i]:
self._reach.insert_profile(k, p)
k += 1
# Update selection
if self._new_indexes is None:
ind = []
for i in range(self._reach.number_profiles):
if self._reach.profile(i).rk in self._selected_rk:
ind.append(i)
self._tableView.setFocus()
self._new_indexes = QItemSelection()
if len(ind) > 0:
for i in ind:
self._new_indexes.append(QItemSelectionRange(
self._tableView.model().index(i, 0)
))
selection = self._tableView.selectionModel()
selection.select(
self._new_indexes,
QItemSelectionModel.Rows |
QItemSelectionModel.ClearAndSelect |
QItemSelectionModel.Select
)
class PurgeCommand(QUndoCommand): class PurgeCommand(QUndoCommand):

View File

@ -64,8 +64,17 @@ class UpdateRKDialog(PamhyrDialog):
self._end_dir = self._gl[i] self._end_dir = self._gl[i]
self._orientation = 0 self._orientation = 0
self._origin = self._reach.profile(0) r = self.parent.tableView\
self._origin_value = self._reach.profile(0).rk .selectionModel()\
.selectedRows()
if len(r) < 1:
self._origin = self._reach.profile(0)
self._origin_value = self._reach.profile(0).rk
self._origin_index = 0
else:
self._origin = self._reach.profile(r[0].row())
self._origin_value = self._reach.profile(r[0].row()).rk
self._origin_index = r[0].row()
self._init_default_values_profiles() self._init_default_values_profiles()
self._init_default_values_guidelines() self._init_default_values_guidelines()
@ -80,6 +89,8 @@ class UpdateRKDialog(PamhyrDialog):
.connect( .connect(
self.changed_profile self.changed_profile
) )
self.find(QComboBox, "comboBox_origin")\
.setCurrentIndex(self._origin_index)
buttonbox = self.find(QButtonGroup, "buttonGroup_orientation") buttonbox = self.find(QButtonGroup, "buttonGroup_orientation")

View File

@ -323,29 +323,10 @@ class GeometryWindow(PamhyrWindow):
logger_exception(e) logger_exception(e)
return return
ind = []
for i in range(self._reach.number_profiles):
if self._reach.profile(i).rk in selected_rk:
ind.append(i)
self.tableView.setFocus()
selection = self.tableView.selectionModel()
index = QItemSelection()
if len(ind) > 0:
for i in ind:
index.append(QItemSelectionRange(
self.tableView.model().index(i, 0)
))
selection.select(
index,
QItemSelectionModel.Rows |
QItemSelectionModel.ClearAndSelect |
QItemSelectionModel.Select
)
def _edit_meshing(self, data): def _edit_meshing(self, data):
try: try:
mesher = InternalMeshing() mesher = InternalMeshing()
self._table.meshing(mesher, data) self._table.meshing(mesher, data, self.tableView)
except Exception as e: except Exception as e:
logger_exception(e) logger_exception(e)
@ -370,7 +351,7 @@ class GeometryWindow(PamhyrWindow):
def _update_rk(self, data): def _update_rk(self, data):
try: try:
mesher = MeshingWithMageMailleurTT() mesher = InternalMeshing()
self._table.update_rk(mesher, data) self._table.update_rk(mesher, data)
except Exception as e: except Exception as e:
logger_exception(e) logger_exception(e)

View File

@ -159,9 +159,9 @@ class TableModel(PamhyrTableModel):
if self._opt_data == "reach": if self._opt_data == "reach":
self._lst = _river.reachs self._lst = _river.reachs
elif self._opt_data == "profile" or self._opt_data == "raw_data": elif self._opt_data == "profile" or self._opt_data == "raw_data":
# self._lst = _river.reach(reach).profiles self._lst = _river.reach(reach).profiles
self._lst = list(compress(_river.reach(reach).profiles, # self._lst = list(compress(_river.reach(reach).profiles,
_river.reach(reach).profile_mask)) # _river.reach(reach).profile_mask))
elif self._opt_data == "solver": elif self._opt_data == "solver":
self._lst = self._parent._solvers self._lst = self._parent._solvers

View File

@ -19,7 +19,7 @@
import os import os
import csv import csv
import logging import logging
import rasterio # import rasterio
from numpy import sqrt from numpy import sqrt
@ -339,6 +339,8 @@ class ResultsWindow(PamhyrWindow):
# "action_export": self.export_current, # "action_export": self.export_current,
"action_Geo_tiff": self.import_geotiff "action_Geo_tiff": self.import_geotiff
} }
self.find(QAction, "action_Geo_tiff").setEnabled(False)
self.find(QAction, "action_Geo_tiff").setVisible(False)
if len(self._results) > 1: if len(self._results) > 1:
self.find(QAction, "action_reload").setEnabled(False) self.find(QAction, "action_reload").setEnabled(False)
@ -1167,47 +1169,48 @@ class ResultsWindow(PamhyrWindow):
self.update_table_selection_profile(profile_id) self.update_table_selection_profile(profile_id)
def import_geotiff(self): def import_geotiff(self):
options = QFileDialog.Options() # options = QFileDialog.Options()
settings = QSettings(QSettings.IniFormat, # settings = QSettings(QSettings.IniFormat,
QSettings.UserScope, 'MyOrg', ) # QSettings.UserScope, 'MyOrg', )
options |= QFileDialog.DontUseNativeDialog # options |= QFileDialog.DontUseNativeDialog
#
file_types = [ # file_types = [
self._trad["file_geotiff"], # self._trad["file_geotiff"],
self._trad["file_all"], # self._trad["file_all"],
] # ]
#
filename, _ = QFileDialog.getOpenFileName( # filename, _ = QFileDialog.getOpenFileName(
self, # self,
self._trad["open_file"], # self._trad["open_file"],
"", # "",
";; ".join(file_types), # ";; ".join(file_types),
options=options # options=options
) # )
#
if filename != "": # if filename != "":
with rasterio.open(filename) as data: # with rasterio.open(filename) as data:
img = data.read() # img = data.read()
b = data.bounds[:] # b = data.bounds[:]
# b[0] left # # b[0] left
# b[1] bottom # # b[1] bottom
# b[2] right # # b[2] right
# b[3] top # # b[3] top
xlim = self.canvas.axes.get_xlim() # xlim = self.canvas.axes.get_xlim()
ylim = self.canvas.axes.get_ylim() # ylim = self.canvas.axes.get_ylim()
if b[2] > b[0] and b[1] < b[3]: # if b[2] > b[0] and b[1] < b[3]:
self.canvas.axes.imshow(img.transpose((1, 2, 0)), # self.canvas.axes.imshow(img.transpose((1, 2, 0)),
extent=[b[0], b[2], b[1], b[3]]) # extent=[b[0], b[2], b[1], b[3]])
else: # else:
dlg = CoordinatesDialog( # dlg = CoordinatesDialog(
xlim, # xlim,
ylim, # ylim,
trad=self._trad, # trad=self._trad,
parent=self # parent=self
) # )
if dlg.exec(): # if dlg.exec():
self.canvas.axes.imshow(img.transpose((1, 2, 0)), # self.canvas.axes.imshow(img.transpose((1, 2, 0)),
extent=dlg.values) # extent=dlg.values)
self.plot_xy.idle() # self.plot_xy.idle()
self.canvas.axes.set_xlim(xlim) # self.canvas.axes.set_xlim(xlim)
self.canvas.axes.set_ylim(ylim) # self.canvas.axes.set_ylim(ylim)
return