Compare commits

..
2 Commits
Author SHA1 Message Date
Theophile Terraz 22771c7f3a debug + pep8 2025-11-06 16:40:12 +01:00
Theophile Terraz 6d60a2017c add sound option in results for Q 2025-11-06 16:28:59 +01:00
176 changed files with 1925 additions and 6092 deletions

No files matched your search

+6 -44
View File
@@ -38,7 +38,6 @@ variables:
variables:
MAGE_8_VERSION: "permalink/latest"
ADISTS_VERSION: "permalink/latest"
RUBAR_VERSION: "permalink/latest"
dl-mage8-doc:
stage: downloads
@@ -112,36 +111,6 @@ dl-adists-windows:
paths:
- adists-windows/adists.exe
dl-rubar-linux:
stage: downloads
tags:
- linux
rules:
- if: $CI_COMMIT_BRANCH == 'ci-test' || $CI_COMMIT_BRANCH == 'master' || $CI_COMMIT_TAG
script:
- mkdir -p rubar-linux
- cd rubar-linux
- curl -L -o rubar.tgz https://forge.inrae.fr/river-hydraulics/rubarbe/-/releases/$RUBAR_VERSION/downloads/packages/rubar_linux.tgz
- tar xf rubar.tgz
artifacts:
paths:
- rubar-linux/rubarbe
dl-rubar-windows:
stage: downloads
tags:
- linux
rules:
- if: $CI_COMMIT_BRANCH == 'ci-test' || $CI_COMMIT_BRANCH == 'master' || $CI_COMMIT_TAG
script:
- mkdir -p rubar-windows
- cd rubar-windows
- curl -L -o rubar.tgz https://forge.inrae.fr/river-hydraulics/rubarbe/-/releases/$RUBAR_VERSION/downloads/packages/rubar_windows.tgz
- tar xf rubar.tgz
artifacts:
paths:
- rubar-windows/rubarbe.exe
#############
# CONFIGURE #
#############
@@ -180,13 +149,13 @@ unittest:
- job: set-version
artifacts: true
script:
- python3.12 -m venv venv
- python3 -m venv venv
- . venv/bin/activate
- pip3 install -U pip
- pip3 install -r ./full-requirements.txt
- pip3 install -U -r ./full-requirements.txt
- cd src
- python3.12 -m unittest discover -t .
- python3 -m unittest discover -t .
test-pep8:
stage: test
@@ -198,7 +167,7 @@ test-pep8:
- job: unittest
script:
# Setup virtual env
- python3.12 -m venv venv
- python3 -m venv venv
- . venv/bin/activate
- pip3 install -U pip
- pip3 install -r ./requirements.txt
@@ -276,7 +245,7 @@ build-developers-doc:
- ./build.sh
# Create html documentation
- cd ../../src
- python3.12 -m venv doc-venv
- python3 -m venv doc-venv
- . doc-venv/bin/activate
- pip3 install -U pip
- pip3 install -r ../full-requirements.txt
@@ -304,8 +273,6 @@ build-linux:
needs:
- job: dl-adists-linux
artifacts: true
- job: dl-rubar-linux
artifacts: true
- job: dl-mage8-linux
artifacts: true
- job: dl-mage8-doc
@@ -326,13 +293,13 @@ build-linux:
- mkdir -p linux
- cd linux
# Setup virtual env
- python3.12 -m venv linux-venv
- python3 -m venv linux-venv
- . linux-venv/bin/activate
- pip3 install -U pip
- pip3 install -r ../requirements.txt
- pip3 install -U -r ../requirements.txt
# Run Pyinstaller
- pyinstaller -y $HIDDEN_IMPORTS --paths linux-venv/lib/python3.12/site-packages ../src/pamhyr.py
- pyinstaller -y $HIDDEN_IMPORTS --paths linux-venv/lib/python3.8/site-packages ../src/pamhyr.py
# Create directory
- mkdir -p pamhyr
- mkdir -p pamhyr/_internal
@@ -347,9 +314,6 @@ build-linux:
# Copy adists
- mkdir -p pamhyr/adists
- cp -v ../adists-linux/* pamhyr/adists/
# Copy rubar
- mkdir -p pamhyr/rubar
- cp -v ../rubar-linux/* pamhyr/rubar/
# Copy Pamhyr
- cp -r dist/pamhyr/* pamhyr/
# Pamhyr script to force x11
@@ -415,8 +379,6 @@ build-windows:
artifacts: true
- job: dl-adists-windows
artifacts: true
- job: dl-rubar-windows
artifacts: true
- job: dl-mage8-doc
artifacts: true
- job: set-version
+1 -2
View File
@@ -1,4 +1,3 @@
Sylvain COULIBALY, INRAE, 2020 - 2022
Théophile TERRAZ, INRAE, 2022 - 2024
Pierre-Antoine ROUBY, INRAE, 2023 - 2025
Dylan JEANNIN, INRAE, 2026
Pierre-Antoine ROUBY, INRAE, 2023 - 2025
+8 -8
View File
@@ -3,7 +3,7 @@
Pamhyr is a free and open source graphical user interface for 1D hydro-sedimentary
modelling of rivers.
![logo](https://gitlab.com/pamhyr/pamhyr2/-/raw/master/src/View/ui/ressources/Pamhyr2_logo.png)
![logo](https://gitlab.irstea.fr/theophile.terraz/pamhyr/-/raw/master/src/View/ui/ressources/Pamhyr2_logo.png)
## Features
@@ -21,9 +21,9 @@ modelling of rivers.
+ Create and edit study alternative scenario tree
Let see the
[documentation](https://gitlab.com/pamhyr/pamhyr2/-/wikis/home)
([:fr:](https://gitlab.com/pamhyr/pamhyr2/-/wikis/home-fr),
[:gb:](https://gitlab.com/pamhyr/pamhyr2/-/wikis/home-en))
[documentation](https://gitlab.irstea.fr/theophile.terraz/pamhyr/-/wikis/home)
([:fr:](https://gitlab.irstea.fr/theophile.terraz/pamhyr/-/wikis/home-fr),
[:gb:](https://gitlab.irstea.fr/theophile.terraz/pamhyr/-/wikis/home-en))
for more details.
## Install
@@ -31,11 +31,11 @@ for more details.
### GNU/Linux
See documentation:
- [French :fr:](https://gitlab.com/pamhyr/pamhyr2/-/wikis/home/fr/Install%20on%20GNULinux)
- [English :gb:](https://gitlab.com/pamhyr/pamhyr2/-/wikis/home/en/Install%20on%20GNULinux)
- [French :fr:](https://gitlab.irstea.fr/theophile.terraz/pamhyr/-/wikis/home/fr/Install%20on%20GNULinux)
- [English :gb:](https://gitlab.irstea.fr/theophile.terraz/pamhyr/-/wikis/home/en/Install%20on%20GNULinux)
### Windows
See documentation:
- [French :fr:](https://gitlab.com/pamhyr/pamhyr2/-/wikis/home/fr/Install%20on%20Windows)
- [English :gb:](https://gitlab.com/pamhyr/pamhyr2/-/wikis/home/en/Install%20on%20Windows)
- [French :fr:](https://gitlab.irstea.fr/theophile.terraz/pamhyr/-/wikis/home/fr/Install%20on%20Windows)
- [English :gb:](https://gitlab.irstea.fr/theophile.terraz/pamhyr/-/wikis/home/en/Install%20on%20Windows)
Binary file not shown.

Before

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

+6 -6
View File
@@ -157,12 +157,12 @@
%%
%% FIXME: Delete me for the first realease
%% \usepackage{draftwatermark}
%% \SetWatermarkLightness{0.8}
%% \SetWatermarkAngle{25}
%% \SetWatermarkScale{3}
%% \SetWatermarkFontSize{1cm}
%% \SetWatermarkText{Work in progress}
\usepackage{draftwatermark}
\SetWatermarkLightness{0.8}
\SetWatermarkAngle{25}
\SetWatermarkScale{3}
\SetWatermarkFontSize{1cm}
\SetWatermarkText{Work in progress}
%% Icons
+3 -3
View File
@@ -1,10 +1,10 @@
PyQt5==5.15.11
PyQt5==5.15.4
PyQt5-Qt5==5.15.2
PyQt5-sip==12.18.0
PyQt5-sip==12.12.2
PyQtWebEngine==5.15.6
QScintilla>=2.14.1
pyqtgraph>=0.12.1
matplotlib==3.8.3
matplotlib>=3.7.1
numpy>=1.24.2
colorama>=0.4.3
pyinstaller>=5.11.0
-4
View File
@@ -46,10 +46,6 @@ copy /y ..\mage8-windows\mage_extraire.exe pamhyr\mage8\
copy /y ..\mage8-windows\mailleurTT.exe pamhyr\mage8\
copy /y ..\mage8-windows\libbief.dll pamhyr\mage8\
rem rubar
mkdir pamhyr\rubar
copy /y ..\rubar-windows\rubarbe.exe pamhyr\rubar\
rem adists
mkdir pamhyr\adists
copy /y ..\adists-windows\adists.exe pamhyr\adists\
+3 -4
View File
@@ -1,10 +1,10 @@
PyQt5==5.15.11
PyQt5==5.15.4
PyQt5-Qt5==5.15.2
PyQt5-sip==12.18.0
PyQt5-sip==12.12.2
#PyQtWebEngine==5.15.6
QScintilla>=2.14.1
pyqtgraph>=0.12.1
matplotlib==3.8.3
matplotlib>=3.7.1
numpy>=1.24.2
colorama>=0.4.3
pyinstaller>=5.11.0
@@ -14,4 +14,3 @@ platformdirs>=4.2.0
pyshp>=2.3.1
rasterio==1.3.11
#fortranformat==2.0.3
+71 -89
View File
@@ -40,24 +40,20 @@ class InternalMeshing(AMeshingTool):
step: float = 50,
limites=[-1, -1],
linear: bool = True):
if reach is None or len(reach.profiles) == 0:
return reach
# pre process
if limites[0] > limites[1]:
lim = limites[1]
limites[1] = limites[0]
limites[0] = lim
elif limites[0] == limites[1]:
return reach
profiles = self.get_profiles(reach, limites)
if profiles is None:
return []
for p in profiles:
if len(p) < 1:
return []
guide_list = self.get_guide_list(reach, profiles)
if guide_list is None:
return []
# we never modify original profiles, we work on the copies
m_profiles = self.st_to_m(profiles, guide_list)
new_profiles = self.interpolate_transversal_step(m_profiles, step)
# we never modify original profiles, we work on copies
m_profiles = self.st_to_m(reach, limites)
new_profiles = self.interpolate_transversal_step(m_profiles,
step)
for new_profiles2 in new_profiles:
for p in new_profiles2:
@@ -65,32 +61,52 @@ class InternalMeshing(AMeshingTool):
return new_profiles
def st_to_m(self, profiles, guide_list):
guide_list = ["un"] + guide_list + ["np"]
max_values = [0] * (len(guide_list) - 1)
max_values_index = [0] * (len(guide_list) - 1)
def st_to_m(self, reach, limites):
gl = reach.compute_guidelines()
profiles = [reach.profile(i).copy()
for i in range(limites[0], limites[1]+1)
]
# we make sure that the lines are in the left-to-right order
guide_list = [
x.name
for x in profiles[0].named_points()
if x.name in gl[0]
]
gl1 = [""] + guide_list
gl2 = guide_list + [""]
max_values = [0] * (len(guide_list) + 1)
max_values_index = [0] * (len(guide_list) + 1)
# we work between each couple of guidelines
for j, p in enumerate(profiles):
index1 = 0
index2 = p.named_point_index(guide_list[0])
if index2 - index1 > max_values[0]:
max_values[0] = index2 - index1
max_values_index[0] = j
for i in range(len(guide_list) - 1):
index1 = p.named_point_index(guide_list[i])
index2 = p.named_point_index(guide_list[i+1])
if index2 - index1 > max_values[i]:
max_values[i] = index2 - index1
max_values_index[i] = j
if index2 - index1 > max_values[i+1]:
max_values[i+1] = index2 - index1
max_values_index[i+1] = j
index1 = p.named_point_index(guide_list[-1])
index2 = len(p) - 1
if index2 - index1 > max_values[-1]:
max_values[-1] = index2 - index1
max_values_index[-1] = j
for i in range(len(max_values)):
for isect in range(max_values_index[i], len(profiles)-1):
self.compl_sect(profiles[isect],
profiles[isect+1],
guide_list[i],
guide_list[i+1])
gl1[i], gl2[i])
for isect in reversed(range(0, max_values_index[i])):
self.compl_sect(profiles[isect+1],
profiles[isect],
guide_list[i],
guide_list[i+1])
gl1[i], gl2[i])
return profiles
def interpolate_transversal_step(self,
@@ -130,7 +146,6 @@ class InternalMeshing(AMeshingTool):
p.name = f'interpol{p.rk}'
new_profiles2.append(p)
new_profiles.append(new_profiles2)
return new_profiles
def compl_sect(self, sect1, sect2, tag1, tag2):
@@ -141,17 +156,26 @@ class InternalMeshing(AMeshingTool):
# len1 and 2: number of intervals
# len1: target len
start1 = sect1.named_point_index(tag1)
start2 = sect2.named_point_index(tag1)
if tag1 == '': # left end point
start1 = 0
start2 = 0
else:
start1 = sect1.named_point_index(tag1)
start2 = sect2.named_point_index(tag1)
end1 = sect1.named_point_index(tag2)
end2 = sect2.named_point_index(tag2)
if tag2 == '': # right end point
end1 = sect1.nb_points-1
end2 = sect2.nb_points-1
else:
end1 = sect1.named_point_index(tag2)
end2 = sect2.named_point_index(tag2)
len1 = end1 - start1
len2 = end2 - start2
if len1 < len2:
self.compl_sect(sect2, sect1, tag1, tag2)
self.compl_sect(sect2, sect1, start2, start1,
len2, len1, tag1, tag2)
return
elif len1 == len2:
return
@@ -166,25 +190,27 @@ class InternalMeshing(AMeshingTool):
for i in range(len1):
ltot1 += sect1.point(start1+i).dist(sect1.point(start1+i+1))
alpha.append(ltot1)
alpha = list(map(lambda x: x/ltot1, alpha)) # target ratios
for i in range(len2):
ltot2 += sect2.point(start2+i).dist(sect2.point(start2+i+1))
beta.append(ltot2)
beta = list(map(lambda x: x/ltot2, beta)) # current ratios
for i in range(len1 - len2):
beta.append(1.0)
beta.append(0.0) # beta[-1]
if len2 < 1 or ltot2 < 0.0001:
# unique point (copy len1 times)
# we are at an edge of the profile
for i in range(len1-len2):
p = sect2.point(start2).copy()
sect2.insert_point(start2, p)
len2 += 1
if tag1 == '': # left end point
sect2.point(start2).name = ''
if tag2 == '': # left end point
sect2.point(start2+1).name = ''
elif ltot1 < 0.0001:
sect2.add_npoints(len1-len2)
len2 = len1
else: # regular case
alpha = list(map(lambda x: x/ltot1, alpha)) # target ratios
beta = list(map(lambda x: x/ltot2, beta)) # current ratios
for i in range(len1 - len2):
beta.append(1.0)
beta.append(0.0) # beta[-1]
# loop on the points to insert
for k in range(len1-len2):
trouve = False
@@ -216,7 +242,7 @@ class InternalMeshing(AMeshingTool):
ratio = (alpha[j0] - beta[j0-1]) \
/ (beta[j0] - beta[j0-1])
if ratio < 0.0:
logger.warning(f"ratio négatif {ratio}")
print(f"ratio négatif {ratio}")
# on double le point a gauche
p = sect2.point(start2+j0-1).copy()
sect2.insert_point(start2+j0-1, p)
@@ -243,11 +269,8 @@ class InternalMeshing(AMeshingTool):
p = sect2.point(start2+len2).copy()
sect2.insert_point(start2+len2, p)
sect2.point(start2+len2).name = ''
if tag1 != "un":
sect2.point(start2).name = tag1
if tag2 != "np":
sect2.point(start2+len2).name = tag2
sect2.point(start2).name = tag1
sect2.point(start2+len2).name = tag2
sect2.modified()
def update_rk(self, reach, begin_rk, end_rk,
@@ -281,8 +304,6 @@ class InternalMeshing(AMeshingTool):
sgn = -1.0
else:
sgn = old_orientation
if abs(sgn) < 0.5:
sgn = 1.0
new_rk = [0.0]*nprof
new_rk[origin] = origin_value
@@ -323,42 +344,3 @@ class InternalMeshing(AMeshingTool):
return new_rk, new_end_rk, new_begin_rk
else:
return new_rk, new_begin_rk, new_end_rk
def get_guide_list(self, reach, profiles):
gl = reach.compute_guidelines()
# we make sure that the lines are in the left-to-right order
guide_list = [
[
x.name
for x in p.named_points()
if x.name in gl[0]
]
for p in profiles]
# we make sure the lines never cross
for gl in guide_list:
for i in range(len(guide_list[0])):
if gl[i] != guide_list[0][i]:
return None
return guide_list[0]
def get_profiles(self, reach, limites):
if reach is None or len(reach.profiles) == 0:
return None
if limites[0] > limites[1]:
lim = limites[1]
limites[1] = limites[0]
limites[0] = lim
elif limites[0] == limites[1]:
return None
profiles = [reach.profile(i).copy()
for i in range(limites[0], limites[1]+1)
]
return profiles
@@ -232,8 +232,6 @@ class BoundaryCondition(SQLSubModel):
type TEXT NOT NULL,
tab TEXT NOT NULL,
node INTEGER,
d50 REAL DEFAULT 0.002,
sigma REAL DEFAULT 1,
{Scenario.create_db_add_scenario()},
{Scenario.create_db_add_scenario_fk()},
FOREIGN KEY(node) REFERENCES river_node(pamhyr_id),
@@ -260,17 +258,6 @@ class BoundaryCondition(SQLSubModel):
"ADD COLUMN deleted BOOLEAN NOT NULL DEFAULT FALSE"
)
if major == "0" and int(minor) <= 2:
if int(release) < 4:
execute(
"ALTER TABLE boundary_condition " +
"ADD COLUMN d50 REAL DEFAULT 0.002"
)
execute(
"ALTER TABLE boundary_condition " +
"ADD COLUMN sigma REAL DEFAULT 1"
)
return cls._update_submodel(execute, version, data)
@classmethod
@@ -329,8 +316,7 @@ class BoundaryCondition(SQLSubModel):
return new
table = execute(
"SELECT pamhyr_id, deleted, name, type, node, d50, sigma, " +
"scenario " +
"SELECT pamhyr_id, deleted, name, type, node, scenario " +
"FROM boundary_condition " +
f"WHERE tab = '{tab}' " +
f"AND scenario = {scenario.id} " +
@@ -345,8 +331,6 @@ class BoundaryCondition(SQLSubModel):
name = next(it)
t = next(it)
node = next(it)
d50 = next(it)
sigma = next(it)
owner_scenario = next(it)
ctor = cls._get_ctor_from_type(t)
@@ -356,14 +340,9 @@ class BoundaryCondition(SQLSubModel):
status=data['status'],
owner_scenario=owner_scenario
)
if deleted:
bc.set_as_deleted()
if t == "SL":
bc.d50 = d50
bc.sigma = sigma
bc.node = None
if node != -1:
bc.node = next(filter(lambda n: n.id == node, nodes), None)
@@ -396,22 +375,15 @@ class BoundaryCondition(SQLSubModel):
if self._node is not None:
node = self._node.id
d50 = 0.002
sigma = 1
if self._type == "SL":
d50 = self._d50
sigma = self._sigma
execute(
"INSERT INTO " +
"boundary_condition(" +
"pamhyr_id, deleted, name, type, tab, node, d50, sigma, scenario" +
"pamhyr_id, deleted, name, type, tab, node, scenario" +
") " +
"VALUES (" +
f"{self._pamhyr_id}, {self._db_format(self.is_deleted())}, " +
f"'{self._db_format(self._name)}', " +
f"'{self._db_format(self._type)}', '{tab}', {node}, " +
f"{d50}, {sigma}, " +
f"{self._status.scenario_id}" +
")"
)
@@ -496,24 +468,6 @@ class BoundaryCondition(SQLSubModel):
def has_node(self):
return self._node is not None
@property
def d50(self):
return self._d50
@d50.setter
def d50(self, value):
self._d50 = float(value)
self.modified()
@property
def sigma(self):
return self._sigma
@sigma.setter
def sigma(self, value):
self._sigma = float(value)
self.modified()
@property
def header(self):
return self._header.copy()
@@ -70,8 +70,9 @@ class Data(SQLSubModel):
def _db_update(cls, execute, version, data=None):
major, minor, release = version.strip().split(".")
created = False
if major == "0" and minor == "0":
if int(release) < 11:
if major == "0" and int(minor) <= 1:
if int(release) < 7:
cls._db_create(execute)
created = True
@@ -142,9 +143,8 @@ class Data(SQLSubModel):
table = execute(
"SELECT pamhyr_id, deleted, data0, data1, scenario FROM " +
"boundary_condition_data_adists " +
f"WHERE scenario = {scenario.id} " +
# f"AND pamhyr_id NOT IN ({', '.join(map(str, loaded))}) " +
f"AND bca = {bca.id}"
f"WHERE bca = {bca.id} " +
f"AND scenario = {scenario.id}"
)
if table is not None:
@@ -187,9 +187,8 @@ class Data(SQLSubModel):
execute(
"INSERT INTO " +
"boundary_condition_data_adists(" +
"pamhyr_id, deleted, data0, data1, bca, scenario) " +
f"VALUES ({self.id}, {self._db_format(self.is_deleted())}, " +
f"'{data0}', {data1}, {bca.id}, " +
"pamhyr_id, data0, data1, bca, scenario) " +
f"VALUES ({self.id}, '{data0}', {data1}, {bca.id}, " +
f"{self._status.scenario_id})"
)
@@ -248,13 +247,15 @@ class BoundaryConditionAdisTS(SQLSubModel):
major, minor, release = version.strip().split(".")
created = False
if major == "0" and int(minor) < 2:
if cls.is_table_exists(execute, "boundary_condition_adists"):
cls._db_update_to_0_2_0(execute, data)
else:
if major == "0" and int(minor) <= 1:
if int(release) < 7:
cls._db_create(execute)
created = True
if major == "0" and int(minor) < 2:
if not created:
cls._db_update_to_0_2_0(execute, data)
if not created:
return cls._update_submodel(execute, version, data)
@@ -299,14 +300,6 @@ class BoundaryConditionAdisTS(SQLSubModel):
if pol_id == -1:
continue
if pol_id not in pid_pol:
# ⚠️ cas important : probablement déjà migré
logger.warning(
f"pol_id {pol_id} not in pid_pol " +
"→ probably already migrated"
)
continue
execute(
f"UPDATE boundary_condition_adists " +
f"SET pollutant = {pid_pol[pol_id]} " +
@@ -319,21 +312,17 @@ class BoundaryConditionAdisTS(SQLSubModel):
status = data['status']
nodes = data['nodes']
scenario = data["scenario"]
pollutant = data.get("pollutant")
loaded = data['loaded_pid']
if scenario is None:
return new
sql = (
"SELECT pamhyr_id, deleted, pollutant, type, node, scenario "
table = execute(
"SELECT pamhyr_id, deleted, pollutant, type, node, scenario " +
"FROM boundary_condition_adists " +
f"WHERE scenario = {scenario.id} "
f"WHERE scenario = {scenario.id} " +
f"AND pamhyr_id NOT IN ({', '.join(map(str, loaded))}) "
)
if pollutant is not None:
sql += f"AND pollutant = {pollutant.id} "
table = execute(sql)
if table is not None:
for row in table:
@@ -358,7 +347,7 @@ class BoundaryConditionAdisTS(SQLSubModel):
bc.type = bc_type
bc.node = None
if node != -1:
if row[3] != -1:
tmp = next(
filter(
lambda n: n.id == node, nodes
@@ -437,14 +426,7 @@ class BoundaryConditionAdisTS(SQLSubModel):
d._data_traversal(predicate, modifier, data)
def __len__(self):
return len(
list(
filter(
lambda el: el is not None and not el.is_deleted(),
self._data
)
)
)
return len(self._data)
@classmethod
def time_convert(cls, data):
@@ -469,7 +451,7 @@ class BoundaryConditionAdisTS(SQLSubModel):
@node.setter
def node(self, node):
self._node = node
self.modified()
self._status.modified()
@property
def header(self):
@@ -478,7 +460,7 @@ class BoundaryConditionAdisTS(SQLSubModel):
@header.setter
def header(self, header):
self._header = header
self.modified()
self._status.modified()
@property
def pollutant(self):
@@ -487,7 +469,7 @@ class BoundaryConditionAdisTS(SQLSubModel):
@pollutant.setter
def pollutant(self, pollutant):
self._pollutant = pollutant
self.modified()
self._status.modified()
@property
def type(self):
@@ -496,16 +478,11 @@ class BoundaryConditionAdisTS(SQLSubModel):
@type.setter
def type(self, type):
self._type = type
self.modified()
self._status.modified()
@property
def data(self):
return list(
filter(
lambda el: el is not None and not el.is_deleted(),
self._data
)
)
return self._data.copy()
@property
def _default_0(self):
@@ -534,36 +511,33 @@ class BoundaryConditionAdisTS(SQLSubModel):
return (new_0, new_1)
def add(self, index: int):
value = Data(
self._default_0, self._default_1,
status=self._status
)
value = (self._default_0, self._default_1)
self._data.insert(index, value)
self.modified()
self._status.modified()
return value
def insert(self, index: int, value):
self._data.insert(index, value)
self.modified()
self._status.modified()
def delete_i(self, indexes):
list(
self._data = list(
map(
lambda e: e[1].set_as_deleted(),
lambda e: e[1],
filter(
lambda e: e[0] in indexes,
lambda e: e[0] not in indexes,
enumerate(self.data)
)
)
)
self.modified()
self._status.modified()
def sort(self, _reverse=False, key=None):
if key is None:
self._data.sort(reverse=_reverse)
else:
self._data.sort(reverse=_reverse, key=key)
self.modified()
self._status.modified()
def index(self, bc):
self._data.index(bc)
@@ -578,10 +552,10 @@ class BoundaryConditionAdisTS(SQLSubModel):
return lst
def _set_i_c_v(self, index, column, value):
v = self._data[index]
v = list(self._data[index])
v[column] = self._types[column](value)
self._data[index] = v
self.modified()
self._data[index] = tuple(v)
self._status.modified()
def set_i_0(self, index: int, value):
self._set_i_c_v(index, 0, value)
@@ -38,13 +38,9 @@ class BoundaryConditionsAdisTSList(PamhyrModelList):
if data is None:
data = {}
previous_pollutant = data.get("pollutant")
data.pop("pollutant", None)
new._lst = BoundaryConditionAdisTS._db_load(execute, data)
if previous_pollutant is not None:
data["pollutant"] = previous_pollutant
new._lst = BoundaryConditionAdisTS._db_load(
execute, data
)
return new
+6 -4
View File
@@ -73,13 +73,15 @@ class D90AdisTS(SQLSubModel):
major, minor, release = version.strip().split(".")
created = False
if major == "0" and int(minor) < 2:
if cls.is_table_exists(execute, "d90_adists"):
cls._db_update_to_0_2_0(execute, data)
else:
if major == "0" and int(minor) <= 1:
if int(release) < 6:
cls._db_create(execute)
created = True
if major == "0" and int(minor) < 2:
if not created:
cls._db_update_to_0_2_0(execute, data)
if not created:
return cls._update_submodel(execute, version, data)
+15 -15
View File
@@ -70,12 +70,16 @@ class D90AdisTSSpec(SQLSubModel):
@classmethod
def _db_update(cls, execute, version, data=None):
major, minor, release = version.strip().split(".")
created = False
if major == 0 and minor < 1:
if int(release) < 6:
cls._db_create(execute)
created = True
if major == "0" and int(minor) < 2:
if cls.is_table_exists(execute, "d90_spec"):
if not created:
cls._db_update_to_0_2_0(execute, data)
else:
cls._db_create(execute)
return True
@@ -83,7 +87,6 @@ class D90AdisTSSpec(SQLSubModel):
def _db_update_to_0_2_0(cls, execute, data):
table = "d90_spec"
table_new = "d90_adists_spec"
reachs = data['id2pid']['river_reach']
cls.update_db_add_pamhyr_id(execute, table, data)
Scenario.update_db_add_scenario(execute, table)
@@ -92,10 +95,8 @@ class D90AdisTSSpec(SQLSubModel):
execute(
f"INSERT INTO {table_new}_tmp " +
"(pamhyr_id, d90_default, name, reach, " +
" start_rk, end_rk, d90, enabled, scenario) " +
"SELECT pamhyr_id, d90_default, name, reach, " +
" start_rk, end_rk, d90, enabled, scenario " +
"(pamhyr_id, name, d90, enabled, scenario) " +
"SELECT pamhyr_id, name, d90, enabled, scenario " +
f"FROM {table}"
)
@@ -103,13 +104,12 @@ class D90AdisTSSpec(SQLSubModel):
execute(f"ALTER TABLE {table_new}_tmp RENAME TO {table_new}")
cls._db_update_to_0_2_0_set_d90_pid(execute, data)
cls._db_update_to_0_2_0_set_reach_pid(execute, table_new, reachs)
@classmethod
def _db_update_to_0_2_0_set_d90_pid(cls, execute, data):
pid_d90 = data["id2pid"]["d90_adists"]
els = execute(
f"SELECT pamhyr_id, d90_default FROM d90_adists_spec"
f"SELECT pamhyr_id, d90 FROM d90_adists_spec"
)
for row in els:
@@ -121,8 +121,8 @@ class D90AdisTSSpec(SQLSubModel):
continue
execute(
f"UPDATE d90_adists_spec " +
f"SET d90_default = {pid_d90[d90_id]} " +
f"UPDATE d90_adists_spec" +
f"SET d90 = {pid_d90[d90_id]} " +
f"WHERE pamhyr_id = {pid}"
)
@@ -167,7 +167,7 @@ class D90AdisTSSpec(SQLSubModel):
owner_scenario=owner_scenario
)
if deleted:
new_spec.set_as_deleted()
new_spec.is_deleted()
new_spec.reach = reach
new_spec.start_rk = start_rk
@@ -196,12 +196,12 @@ class D90AdisTSSpec(SQLSubModel):
"d90_default, name, reach, " +
"start_rk, end_rk, d90, enabled, scenario) " +
"VALUES (" +
f"{self.id}, {self._db_format(self.is_deleted())}, " +
f"{self.id}, {self._db_format(self.is_deleted())}" +
f"{d90_default}, " +
f"'{self._db_format(self._name_section)}', " +
f"{self._reach}, " +
f"{self._start_rk}, {self._end_rk}, " +
f"{self._d90}, {self._enabled}, " +
f"{self._d90}, {self._enabled}" +
f"{self._status.scenario_id}" +
")"
)
+16 -14
View File
@@ -79,13 +79,15 @@ class DIFAdisTS(SQLSubModel):
major, minor, release = version.strip().split(".")
created = False
if major == "0" and int(minor) < 2:
if cls.is_table_exists(execute, "dif_adists"):
cls._db_update_to_0_2_0(execute, data)
else:
if major == "0" and int(minor) <= 1:
if int(release) < 6:
cls._db_create(execute)
created = True
if major == "0" and int(minor) < 2:
if not created:
cls._db_update_to_0_2_0(execute, data)
if not created:
return cls._update_submodel(execute, version, data)
@@ -140,24 +142,24 @@ class DIFAdisTS(SQLSubModel):
enabled = (next(it) == 1)
owner_scenario = next(it)
DIF = cls(
dif = cls(
id=pid,
name=name,
status=status,
owner_scenario=owner_scenario,
)
DIF.method = method
DIF.dif = dif
DIF.b = b
DIF.c = c
DIF.enabled = enabled
dif.method = method
dif.dif = dif
dif.b = b
dif.c = c
dif.enabled = enabled
data['dif_default_id'] = pid
DIF._data = DIFAdisTSSpec._db_load(execute, data)
dif._data = DIFAdisTSSpec._db_load(execute, data)
loaded.add(pid)
new.append(DIF)
new.append(dif)
data["scenario"] = scenario.parent
new += cls._db_load(execute, data)
@@ -188,7 +190,7 @@ class DIFAdisTS(SQLSubModel):
b = self.b
c = -1.
if self.c is not None:
if self.dif is not None:
c = self.c
sql = (
@@ -199,7 +201,7 @@ class DIFAdisTS(SQLSubModel):
"VALUES (" +
f"{self.id}, '{self._db_format(self._name)}', " +
f"'{self._db_format(self._method)}', " +
f"{dif}, {b}, {c}, {self._enabled}, " +
f"{dif.id}, {b}, {c}, {self._enabled}, " +
f"{self._status.scenario_id}" +
")"
)
+20 -13
View File
@@ -33,14 +33,16 @@ class DIFAdisTSSpec(SQLSubModel):
_id_cnt = 0
def __init__(self, id: int = -1, method: str = "",
status=None, owner_scenario=-1):
super(DIFAdisTSSpec, self).__init__(
id=id, status=status,
owner_scenario=owner_scenario
)
status=None):
super(DIFAdisTSSpec, self).__init__()
self._status = status
if id == -1:
self.id = DIFAdisTSSpec._id_cnt
else:
self.id = id
self._method = method
self._reach = None
self._start_rk = None
@@ -50,6 +52,8 @@ class DIFAdisTSSpec(SQLSubModel):
self._c = None
self._enabled = True
DIFAdisTSSpec._id_cnt = max(DIFAdisTSSpec._id_cnt + 1, self.id)
@classmethod
def _db_create(cls, execute, ext=""):
execute(f"""
@@ -77,12 +81,16 @@ class DIFAdisTSSpec(SQLSubModel):
@classmethod
def _db_update(cls, execute, version, data=None):
major, minor, release = version.strip().split(".")
created = False
if major == 0 and minor < 1:
if int(release) < 6:
cls._db_create(execute)
created = True
if major == "0" and int(minor) < 2:
if cls.is_table_exists(execute, "dif_spec"):
if not created:
cls._db_update_to_0_2_0(execute, data)
else:
cls._db_create(execute)
return True
@@ -187,7 +195,7 @@ class DIFAdisTSSpec(SQLSubModel):
new_spec.c = c
new_spec.enabled = enabled
loaded.add(id)
loaded.add(pid)
new.append(new_spec)
data["scenario"] = scenario.parent
@@ -202,23 +210,22 @@ class DIFAdisTSSpec(SQLSubModel):
dif_default = data['dif_default_id']
sql = (
execute(
"INSERT INTO " +
"dif_adists_spec(pamhyr_id, deleted, " +
"dif_default, method, reach, " +
"start_rk, end_rk, dif, b, c, enabled, scenario) " +
"VALUES (" +
f"{self.id}, {self._db_format(self.is_deleted())}, " +
f"{self.id}, {self._db_format(self.is_deleted())}" +
f"{dif_default}, " +
f"'{self._db_format(self._method)}', " +
f"{self._reach}, " +
f"{self._start_rk}, {self._end_rk}, " +
f"{self._dif}, {self._b}, {self._c}, " +
f"{self._enabled}, " +
f"{self._enabled}" +
f"{self._status.scenario_id}" +
")"
)
execute(sql)
return True
+3 -10
View File
@@ -228,10 +228,9 @@ class Friction(SQLSubModel):
ssi = self._status.scenario_id
return (
self._begin_strickler is not None
and (self._begin_strickler._owner_scenario == ssi
# or self._end_strickler._owner_scenario == ssi
or super(Friction, self).must_be_saved())
self._begin_strickler._owner_scenario == ssi
# or self._end_strickler._owner_scenario == ssi
or super(Friction, self).must_be_saved()
)
def _db_save(self, execute, data=None):
@@ -365,9 +364,3 @@ class Friction(SQLSubModel):
# self.end_strickler.medium])
return minor, medium
def update_rks(self, begin_rk, end_rk):
self._begin_rk = begin_rk
self._end_rk = end_rk
self.modified()
-368
View File
@@ -1,368 +0,0 @@
# GeoTIFF.py -- Pamhyr
# Copyright (C) 2024-2025 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 struct
import logging
from functools import reduce
from tools import trace, timer
from Model.Tools.PamhyrDB import SQLSubModel
from Model.Except import NotImplementedMethodeError
from Model.Scenario import Scenario
try:
import rasterio
import rasterio.control
import rasterio.crs
import rasterio.sample
import rasterio.vrt
import rasterio._features
from rasterio.io import MemoryFile
_rasterio_loaded = True
except Exception as e:
print(f"Module 'rasterio' is not available: {e}")
_rasterio_loaded = False
logger = logging.getLogger()
class GeoTIFF(SQLSubModel):
_sub_classes = []
def __init__(self, id: int = -1, enabled=True,
name="", description="",
path="", coordinates=None,
status=None, owner_scenario=-1):
super(GeoTIFF, self).__init__(
id=id, status=status,
owner_scenario=owner_scenario
)
self._enabled = enabled
self._name = f"GeoTIFF #{self._pamhyr_id}" if name == "" else name
self._description = description
self._file_bytes = b''
if coordinates is None:
self._coordinates = {
"bottom": 0.0,
"top": 0.0,
"left": 0.0,
"right": 0.0,
}
else:
self._coordinates = coordinates
self._file_name = ""
if path != "":
self.read_file(path)
self._memfile = None
def __getitem__(self, key):
value = None
if key == "enabled":
value = self._enabled
elif key == "name":
value = self.name
elif key == "description":
value = self.description
elif key == "file_name":
value = self.file_name
elif key == "coordinates":
value = self.coordinates
elif key == "coordinates_bottom":
value = self.coordinates["bottom"]
elif key == "coordinates_top":
value = self.coordinates["top"]
elif key == "coordinates_left":
value = self.coordinates["left"]
elif key == "coordinates_right":
value = self.coordinates["right"]
elif key == "memfile":
value = self.memfile
return value
def __setitem__(self, key, value):
if key == "enabled":
self.enabled = value
elif key == "name":
self.name = value
elif key == "description":
self.description = value
elif key == "file_name":
if self._file_name != value:
self.read_file(value)
elif key == "coordinates":
self.coordinates = value
elif key == "coordinates_bottom" or key == "bottom":
self.coordinates["bottom"] = value
elif key == "coordinates_top" or key == "top":
self.coordinates["top"] = value
elif key == "coordinates_left" or key == "left":
self.coordinates["left"] = value
elif key == "coordinates_right" or key == "right":
self.coordinates["right"] = value
self.modified()
@property
def enabled(self):
return self._enabled
@enabled.setter
def enabled(self, enabled):
self._enabled = enabled
self.modified()
def is_enabled(self):
return self._enabled
@property
def name(self):
return self._name
@name.setter
def name(self, name):
self._name = name
self.modified()
@property
def description(self):
return self._description
@description.setter
def description(self, description):
self._description = description
self.modified()
@property
def file_name(self):
return self._file_name
@file_name.setter
def file_name(self, file_name):
self._file_name = file_name
self.modified()
@property
def coordinates(self):
return self._coordinates
@coordinates.setter
def coordinates(self, coordinates):
self._coordinates = coordinates
self.modified()
@property
def coord_bottom(self):
if self._coordinates is None:
return 0.0
return self._coordinates["bottom"]
@property
def coord_top(self):
if self._coordinates is None:
return 0.0
return self._coordinates["top"]
@property
def coord_left(self):
if self._coordinates is None:
return 0.0
return self._coordinates["left"]
@property
def coord_right(self):
if self._coordinates is None:
return 0.0
return self._coordinates["right"]
@property
def memfile(self):
if not _rasterio_loaded:
return None
if self._file_bytes == b'':
return None
if self._memfile is None:
self._memfile = MemoryFile()
self._memfile.write(self._file_bytes)
return self._memfile
def read_file(self, path):
logger.debug(f"Read GeoTIFF file at : '{path}'")
self._file_name = path
self._file_bytes = b''
self._memfile = None
nbytes = 0
with open(path, "rb") as f:
while True:
data = f.read(4096)
if not data:
break
nbytes += len(data)
self._file_bytes += data
logger.debug(f"Read GeoTIFF: {nbytes} bytes readed")
def write_file(self, path):
with open(path, "w+b") as f:
f.write(self._file_bytes)
@classmethod
def _db_create(cls, execute, ext=""):
execute(f"""
CREATE TABLE geotiff{ext} (
{cls.create_db_add_pamhyr_id()},
enabled BOOLEAN NOT NULL,
deleted BOOLEAN NOT NULL DEFAULT FALSE,
name TEXT NOT NULL,
description TEXT NOT NULL,
file_name TEXT NOT NULL,
file_bytes BLOB NOT NULL,
coordinates_bottom REAL NOT NULL,
coordinates_top REAL NOT NULL,
coordinates_left REAL NOT NULL,
coordinates_right REAL NOT NULL,
{Scenario.create_db_add_scenario()},
{Scenario.create_db_add_scenario_fk()},
PRIMARY KEY(pamhyr_id, scenario)
)
""")
return cls._create_submodel(execute)
@classmethod
def _db_update(cls, execute, version, data=None):
major, minor, release = version.strip().split(".")
if major == "0" and int(minor) < 2:
cls._db_create(execute)
if major == "0" and int(minor) == 2:
if int(release) < 3:
cls._db_create(execute)
return True
@classmethod
def _db_load(cls, execute, data=None):
new = []
scenario = data["scenario"]
loaded = data['loaded_pid']
if scenario is None:
return new
table = execute(
"SELECT pamhyr_id, enabled, deleted, " +
"name, description, file_name, file_bytes, " +
"coordinates_bottom, coordinates_top, " +
"coordinates_left, coordinates_right, " +
"scenario " +
"FROM geotiff " +
f"WHERE scenario = {scenario.id} " +
f"AND pamhyr_id NOT IN ({', '.join(map(str, loaded))})"
)
if table is not None:
for row in table:
it = iter(row)
id = next(it)
enabled = (next(it) == 1)
deleted = (next(it) == 1)
name = next(it)
description = next(it)
file_name = next(it)
file_bytes = next(it)
coordinates_bottom = next(it)
coordinates_top = next(it)
coordinates_left = next(it)
coordinates_right = next(it)
owner_scenario = next(it)
f = cls(
id=id, enabled=enabled, name=name,
description=description, coordinates={
"bottom": coordinates_bottom,
"top": coordinates_top,
"left": coordinates_left,
"right": coordinates_right,
},
status=data['status'],
owner_scenario=owner_scenario
)
if deleted:
f.set_as_deleted()
f._file_bytes = file_bytes
loaded.add(id)
new.append(f)
data["scenario"] = scenario.parent
new += cls._db_load(execute, data)
data["scenario"] = scenario
return new
def _db_save(self, execute, data=None):
if not self.must_be_saved():
return True
execute(
"INSERT INTO geotiff (" +
"pamhyr_id, enabled, deleted, " +
"name, description, file_name, file_bytes, " +
"coordinates_bottom, coordinates_top, " +
"coordinates_left, coordinates_right, " +
"scenario) " +
"VALUES (?,?,?,?,?,?,?,?,?,?,?,?)",
self._pamhyr_id,
self._enabled,
self.is_deleted(),
self.name,
self.description,
self.file_name,
self._file_bytes,
self.coordinates['bottom'],
self.coordinates['top'],
self.coordinates['left'],
self.coordinates['right'],
self._status.scenario_id,
)
return True
-59
View File
@@ -1,59 +0,0 @@
# GeoTIFFList.py -- Pamhyr
# Copyright (C) 2024-2025 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 trace, timer
from Model.Except import NotImplementedMethodeError
from Model.Tools.PamhyrListExt import PamhyrModelList
from Model.GeoTIFF.GeoTIFF import GeoTIFF
class GeoTIFFList(PamhyrModelList):
_sub_classes = [GeoTIFF]
@classmethod
def _db_load(cls, execute, data=None):
new = cls(status=data["status"])
new._lst = GeoTIFF._db_load(execute, data)
return new
def _db_save(self, execute, data=None):
ok = True
# Delete previous data
execute(
"DELETE FROM geotiff " +
f"WHERE scenario = {self._status.scenario_id}"
)
for gt in self._lst:
ok &= gt._db_save(execute, data)
return ok
@property
def files(self):
return self.lst
def new(self, index):
n = GeoTIFF(status=self._status)
self.insert(index, n)
self._status.modified()
return n
-19
View File
@@ -66,11 +66,6 @@ class PointXYZ(Point):
)
""")
execute(
"CREATE INDEX idx_point " +
"ON geometry_pointXYZ(profile, scenario);"
)
return cls._create_submodel(execute)
@classmethod
@@ -95,13 +90,6 @@ class PointXYZ(Point):
"ADD COLUMN deleted BOOLEAN NOT NULL DEFAULT FALSE"
)
if major == "0" and minor == "2":
if int(release) < 5:
execute(
"CREATE INDEX idx_point " +
"ON geometry_pointXYZ(profile, scenario);"
)
return cls._update_submodel(execute, version, data)
@classmethod
@@ -350,13 +338,6 @@ class PointXYZ(Point):
self._z = float(value)
self.modified()
def shift(self, x, y, z):
self.x += x
self.y += y
self.z += z
self.modified()
def get_coordinate(self):
return (self._x, self._y, self._z)
+4 -24
View File
@@ -76,32 +76,12 @@ class Profile(object):
return self.points[index]
def named_point(self, name):
if name == "un":
return self.points[0]
elif name == "np":
return self.points[-1]
elif name == "talweg":
zm = self.z_min()
return next((p for p in self.points if p.z == zm), None)
else:
return next((p for p in self.points if p.name == name), None)
return next((p for p in self.points if p.name == name), None)
def named_point_index(self, name):
if name == "un":
return 0
elif name == "np":
return len(self.points) - 1
elif name == "talweg":
zm = self.z_min()
return next(
(p for p in enumerate(self.points) if p[1].z == zm),
None
)[0]
else:
return next(
(p for p in enumerate(self.points) if p[1].name == name),
None
)[0]
return next(
(p for p in enumerate(self.points) if p[1].name == name), None
)[0]
@property
def reach(self):
+12 -4
View File
@@ -24,7 +24,7 @@ from typing import List
from functools import reduce
from dataclasses import dataclass
from tools import trace, timer, flatten
from tools import timer, flatten
from shapely import geometry
from Model.Tools.PamhyrDB import SQLSubModel
@@ -396,6 +396,7 @@ class ProfileXYZ(Profile, SQLSubModel):
for point in self._points:
p = point.copy()
print(p)
new_p._points.append(p)
new_p.modified()
@@ -1073,10 +1074,16 @@ class ProfileXYZ(Profile, SQLSubModel):
def shift(self, x, y, z):
for p in self._points:
p.shift(x, y, z)
p.x = p.x + x
p.y = p.y + y
p.z = p.z + z
self.modified()
def modified(self):
self.tab_up_to_date = False
self.station_up_to_date = False
def add_npoints(self, npoints):
# add npoints in a profile
for k in range(npoints):
@@ -1094,11 +1101,12 @@ class ProfileXYZ(Profile, SQLSubModel):
self.point(i+1).z = 0.5 * self.point(i).z + 0.5 * self.point(i+2).z
def copy(self):
p = ProfileXYZ(name=self.name,
p = ProfileXYZ(id=self.id,
name=self.name,
rk=self.rk,
reach=self.reach,
num=self.num,
status=self._status)
for i, k in enumerate(self.points):
p.insert_point(i, k.copy())
+5 -49
View File
@@ -52,11 +52,6 @@ class Reach(SQLSubModel):
self._guidelines_is_valid = False
self._guidelines = {}
super(Reach, self).__init__(
id=parent.pamhyr_id,
status=status,
)
@property
def pamhyr_id(self):
return self._parent.pamhyr_id
@@ -65,10 +60,6 @@ class Reach(SQLSubModel):
def _pamhyr_id(self):
return self._parent.pamhyr_id
@_pamhyr_id.setter
def _pamhyr_id(self, id):
return
@classmethod
def _db_create(cls, execute):
return cls._create_submodel(execute)
@@ -112,9 +103,6 @@ class Reach(SQLSubModel):
predicate=lambda obj, data: True,
modifier=lambda obj, data: None,
data={}):
if predicate(self, data):
modifier(self, data)
for p in self._profiles:
p._data_traversal(predicate, modifier, data)
@@ -207,16 +195,13 @@ class Reach(SQLSubModel):
"""
if profile in self._profiles:
self.undelete([profile])
profile.num = index
else:
gi = self.get_global_profil_index(index)
profile.num = gi
self._profiles.insert(gi, profile)
self._profiles.insert(index, profile)
self.modified()
def delete(self, indexes):
"""Set some elements as deleted in profile list
"""Delete some elements in profile list
Args:
indexes: The list of index to delete
@@ -224,12 +209,11 @@ class Reach(SQLSubModel):
Returns:
Nothing.
"""
gi = [self.get_global_profil_index(i) for i in indexes]
list(
map(
lambda p: p[1].set_as_deleted(),
filter(
lambda e: e[0] in gi,
lambda e: e[0] in indexes,
enumerate(self._profiles)
)
)
@@ -245,12 +229,11 @@ class Reach(SQLSubModel):
Returns:
Nothing.
"""
gi = [self.get_global_profil_index(i) for i in indexes]
self._profiles = list(
map(
lambda p: p[1],
filter(
lambda e: e[0] not in gi,
lambda e: e[0] not in indexes,
enumerate(self._profiles)
)
)
@@ -258,7 +241,7 @@ class Reach(SQLSubModel):
self.modified()
def delete_profiles(self, profiles):
"""Set profiles list as deleted
"""Delete some elements in profile list
Args:
profiles: The list of profile to delete
@@ -946,30 +929,3 @@ class Reach(SQLSubModel):
nr2._profiles = list(map(lambda p: p.cloned_for(nr2), nr2_profiles))
return nr1, nr2
def get_local_profil_index(self, index):
local_index = -1
for i, p in enumerate(self._profiles):
if not p.is_deleted():
local_index += 1
if i == index:
break
return local_index
def get_global_profil_index(self, index):
local_index = -1
for i, p in enumerate(self._profiles):
if not p.is_deleted():
local_index += 1
if local_index == index:
global_index = i
break
return global_index
def update_rks(self, rks):
for rk, profile in zip(rks, self.profiles):
profile.rk = rk
self.modified()
@@ -183,6 +183,7 @@ class BasicHS(SQLSubModel):
bhs._data = BHSValue._db_load(
execute, data
)
print(f"{bhs_pid} : {deleted}")
if deleted:
bhs.set_as_deleted()
@@ -274,9 +274,6 @@ class HydraulicStructure(SQLSubModel):
return hs
def _db_save(self, execute, data=None):
if not self.must_be_saved():
return True
execute(
"DELETE FROM hydraulic_structures " +
f"WHERE pamhyr_id = {self.pamhyr_id} " +
@@ -225,8 +225,6 @@ class Data(SQLSubModel):
return new
def _db_save(self, execute, data=None):
if not self.must_be_saved():
return True
ind = data["ind"]
execute(
@@ -398,7 +396,7 @@ class InitialConditions(SQLSubModel):
@reach.setter
def reach(self, new):
self._reach = new
self._reach = reach
self.modified()
@property
@@ -539,10 +537,7 @@ class InitialConditions(SQLSubModel):
strickler = 25.0
if not compute_discharge:
if profile.rk in data_discharge:
discharge = data_discharge[profile.rk]
else:
discharge = 0.0
discharge = data_discharge[profile.rk]
else:
discharge = (
# ((width * 0.8)
@@ -588,6 +583,8 @@ class InitialConditions(SQLSubModel):
for data in self._data:
data_height[data["rk"].rk] = data["height"]
print(data_height)
incline = self._reach.reach.get_incline_median_mean()
logger.debug(f"incline = {incline}")
@@ -608,10 +605,7 @@ class InitialConditions(SQLSubModel):
strickler = 25.0
if not compute_height:
if profile.rk in data_height:
height = data_height[profile.rk]
else:
height = 0.0
height = data_height[profile.rk]
else:
if abs(incline) <= 0:
height = 0.0
@@ -662,10 +656,7 @@ class InitialConditions(SQLSubModel):
for profile in profiles:
if not compute_discharge:
if profile.rk in data_discharge:
d = data_discharge[profile.rk]
else:
d = 0.0
d = data_discharge[profile.rk]
else:
d = discharge
elevation = interp(profile.rk,
@@ -79,12 +79,16 @@ class InitialConditionsAdisTS(SQLSubModel):
@classmethod
def _db_update(cls, execute, version, data=None):
major, minor, release = version.strip().split(".")
created = False
if major == "0" and int(minor) <= 1:
if int(release) < 6:
cls._db_create(execute)
created = True
if major == "0" and int(minor) < 2:
if cls.is_table_exists(execute, "initial_conditions_adists"):
if not created:
cls._db_update_to_0_2_0(execute, data)
else:
cls._db_create(execute)
return True
@@ -263,7 +267,7 @@ class InitialConditionsAdisTS(SQLSubModel):
@name.setter
def name(self, name):
self._name = name
self.modified()
self._status.modified()
@property
def pollutant(self):
@@ -272,7 +276,7 @@ class InitialConditionsAdisTS(SQLSubModel):
@pollutant.setter
def pollutant(self, pollutant):
self._pollutant = pollutant
self.modified()
self._status.modified()
@property
def concentration(self):
@@ -281,7 +285,7 @@ class InitialConditionsAdisTS(SQLSubModel):
@concentration.setter
def concentration(self, concentration):
self._concentration = concentration
self.modified()
self._status.modified()
@property
def eg(self):
@@ -290,7 +294,7 @@ class InitialConditionsAdisTS(SQLSubModel):
@eg.setter
def eg(self, eg):
self._eg = eg
self.modified()
self._status.modified()
@property
def em(self):
@@ -299,7 +303,7 @@ class InitialConditionsAdisTS(SQLSubModel):
@em.setter
def em(self, em):
self._em = em
self.modified()
self._status.modified()
@property
def ed(self):
@@ -308,7 +312,7 @@ class InitialConditionsAdisTS(SQLSubModel):
@ed.setter
def ed(self, ed):
self._ed = ed
self.modified()
self._status.modified()
@property
def enabled(self):
@@ -317,45 +321,28 @@ class InitialConditionsAdisTS(SQLSubModel):
@enabled.setter
def enabled(self, enabled):
self._enabled = enabled
self.modified()
self._status.modified()
def new(self, index):
n = ICAdisTSSpec(status=self._status)
self._data.insert(index, n)
self.modified()
self._status.modified()
return n
def delete(self, data):
list(
map(
lambda x: x.set_as_deleted(),
data
self._data = list(
filter(
lambda x: x not in data,
self._data
)
)
self.modified()
self._status.modified()
def delete_i(self, indexes):
list(
map(
lambda e: e[1].set_as_deleted(),
filter(
lambda e: e[0] in indexes,
enumerate(self._data)
)
)
)
self.modified()
for ind in indexes:
del self._data[ind]
self._status.modified()
def insert(self, index, data):
if data in self._data:
self.undelete([data])
else:
self._data.insert(index, data)
self.modified()
def undelete(self, lst):
for x in lst:
x.set_as_not_deleted()
self.modified()
self._data.insert(index, data)
self._status.modified()
@@ -31,7 +31,7 @@ class ICAdisTSSpec(SQLSubModel):
_sub_classes = []
def __init__(self, id: int = -1, name: str = "",
status=None, owner_scenario=None):
status=None):
super(ICAdisTSSpec, self).__init__()
self._status = status
@@ -77,12 +77,16 @@ class ICAdisTSSpec(SQLSubModel):
@classmethod
def _db_update(cls, execute, version, data=None):
major, minor, release = version.strip().split(".")
created = False
if major == 0 and minor < 1:
if int(release) < 6:
cls._db_create(execute)
created = True
if major == "0" and int(minor) < 2:
if cls.is_table_exists(execute, "initial_conditions_adists_spec"):
if not created:
cls._db_update_to_0_2_0(execute, data)
else:
cls._db_create(execute)
return True
@@ -190,7 +194,7 @@ class ICAdisTSSpec(SQLSubModel):
new_spec.rate = rate
new_spec.enabled = enabled
# loaded.add(pid)
loaded.add(pid)
new.append(new_spec)
data["scenario"] = scenario.parent
@@ -207,7 +211,7 @@ class ICAdisTSSpec(SQLSubModel):
sql = (
"INSERT INTO " +
"initial_conditions_adists_spec(pamhyr_id, deleted, " +
"initial_conditions_adists_spec(id, deleted, " +
"ic_default, name, reach, " +
"start_rk, end_rk, concentration, eg, em, ed, rate, " +
"enabled, scenario) " +
@@ -147,9 +147,9 @@ class Data(SQLSubModel):
it = iter(v)
pid = next(it)
deleted = next(it)
data0 = lc._types[0](next(it))
data1 = lc._types[1](next(it))
delete = next(it)
data0 = bc._types[0](next(it))
data1 = bc._types[1](next(it))
owner_scenario = next(it)
nd = cls(
@@ -69,12 +69,14 @@ class Data(SQLSubModel):
major, minor, release = version.strip().split(".")
created = False
if major == "0" and int(minor) < 2:
if cls.is_table_exists(
execute, "lateral_contribution_data_adists"):
cls._db_update_to_0_2_0(execute, data)
else:
if major == "0" and int(minor) <= 1:
if int(release) < 7:
cls._db_create(execute)
created = True
if major == "0" and int(minor) < 2:
if not created:
cls._db_update_to_0_2_0(execute, data)
return True
@@ -130,13 +132,11 @@ class Data(SQLSubModel):
if scenario is None:
return new
lca = data["lca"]
table = execute(
"SELECT pamhyr_id, deleted, data0, data1, scenario " +
"SELECT pamhyr_id, deleted, data0, data1 " +
"FROM lateral_contribution_data_adists " +
f"WHERE scenario = {scenario.id} " +
f"AND pamhyr_id NOT IN ({', '.join(map(str, loaded))}) " +
f"AND pamhyr_id NOT IN ({', '.join(map(str, loaded))}) "
f"AND lca = '{lca.id}'"
)
@@ -150,16 +150,16 @@ class Data(SQLSubModel):
data1 = next(it)
owner_scenario = next(it)
lc = cls(
data = cls(
data0, data1,
id=pid, status=status,
owner_scenario=owner_scenario
)
if deleted:
lc.set_as_deleted()
data.set_as_deleted()
loaded.add(pid)
new.append(lc)
new.append(data)
data["scenario"] = scenario.parent
new += cls._db_load(execute, data)
@@ -175,7 +175,7 @@ class Data(SQLSubModel):
execute(
"INSERT INTO " +
"lateral_contribution_data_adists(pamhyr_id, deleted," +
"lateral_contribution_adists(pamhyr_id, deleted," +
"data0, data1, lca, scenario) " +
"VALUES (" +
f"{self.id}, {self._db_format(self.is_deleted())}, " +
@@ -241,13 +241,15 @@ class LateralContributionAdisTS(SQLSubModel):
major, minor, release = version.strip().split(".")
created = False
if major == "0" and int(minor) < 2:
if cls.is_table_exists(execute, "lateral_contribution_adists"):
cls._db_update_to_0_2_0(execute, data)
else:
if major == "0" and int(minor) <= 1:
if int(release) < 7:
cls._db_create(execute)
created = True
if major == "0" and int(minor) < 2:
if not created:
cls._db_update_to_0_2_0(execute, data)
if not created:
return cls._update_submodel(execute, version, data)
@@ -370,18 +372,16 @@ class LateralContributionAdisTS(SQLSubModel):
execute(
"INSERT INTO " +
"lateral_contribution_adists(pamhyr_id, deleted, " +
"lateral_contribution_adists(id, deleted," +
"pollutant, reach, begin_rk, end_rk, scenario) " +
"VALUES (" +
f"{self.id}, {self._db_format(self.is_deleted())}, " +
f"{self._pollutant}, {self.reach}, " +
f"{self._begin_rk}, {self._end_rk}, " +
f"{self._begin_rk}, {self._end_rk}" +
f"{self._status.scenario_id}" +
")"
)
data["lca"] = self
ind = 0
for d in self._data:
data["ind"] = ind
@@ -406,7 +406,7 @@ class LateralContributionAdisTS(SQLSubModel):
return len(
list(
filter(
lambda el: el is not None and not el.is_deleted(),
lambda el: not el.is_deleted(),
self._data
)
)
@@ -452,12 +452,7 @@ class LateralContributionAdisTS(SQLSubModel):
@property
def data(self):
return list(
filter(
lambda el: el is not None and not el.is_deleted(),
self._data
)
)
return self._data.copy()
@property
def begin_rk(self):
@@ -476,57 +471,3 @@ class LateralContributionAdisTS(SQLSubModel):
def end_rk(self, end_rk):
self._end_rk = end_rk
self.modified()
@property
def _default_0(self):
return self._types[0](0)
@property
def _default_1(self):
return self._types[1](0.0)
def add(self, index: int):
value = Data(self._default_0, self._default_1, status=self._status)
self._data.insert(index, value)
self.modified()
return value
def insert(self, index: int, data: Data):
self._data.insert(index, data)
self.modified()
def delete_i(self, indexes):
self._data = list(
map(
lambda e: e[1].set_as_deleted(),
filter(
lambda e: e[0] not in indexes,
enumerate(self.data)
)
)
)
self.modified()
def index(self, bc):
self._data.index(bc)
def get_i(self, index: int):
return self._data[index]
def get_range(self, _range):
lst = []
for r in _range:
lst.append(r)
return lst
def _set_i_c_v(self, index: int, column: int, value):
v = self._data[index]
v[column] = self._types[column](value)
self._data[index] = v
self.modified()
def set_i_0(self, index: int, value):
self._set_i_c_v(index, 0, value)
def set_i_1(self, index: int, value):
self._set_i_c_v(index, 1, value)
+8 -4
View File
@@ -95,12 +95,16 @@ class OutputRKAdists(SQLSubModel):
@classmethod
def _db_update(cls, execute, version, data=None):
major, minor, release = version.strip().split(".")
created = False
if major == "0" and int(minor) <= 1:
if int(release) < 7:
cls._db_create(execute)
created = True
if major == "0" and int(minor) < 2:
if cls.is_table_exists(execute, "OutputRKAdists"):
if not created:
cls._db_update_to_0_2_0(execute, data)
else:
cls._db_create(execute)
return True
@@ -189,7 +193,7 @@ class OutputRKAdists(SQLSubModel):
"output_rk_adists(pamhyr_id, deleted, " +
"reach, rk, title, scenario) " +
"VALUES (" +
f"{self.id}, {self._db_format(self.is_deleted())}, " +
f"{self.id}, {self._db_format(self.is_deleted())}" +
f"{self._reach}, {self._rk}, " +
f"'{self._db_format(self._title)}', " +
f"{self._status.scenario_id}" +
+19 -80
View File
@@ -28,9 +28,6 @@ from tools import (
from Model.Tools.PamhyrDB import SQLSubModel
from Model.Except import NotImplementedMethodeError
from Model.Scenario import Scenario
from Model.BoundaryConditionsAdisTS.BoundaryConditionAdisTS import (
BoundaryConditionAdisTS,
)
logger = logging.getLogger()
@@ -104,12 +101,6 @@ class PollutantCharacteristics(SQLSubModel):
self._ac = value
elif key == 8:
self._bc = value
self.propagate_modified()
def propagate_modified(self):
self.modified()
if hasattr(self, "_pollutant") and self._pollutant:
self._pollutant.modified()
@classmethod
def _db_create(cls, execute, ext=""):
@@ -138,12 +129,16 @@ class PollutantCharacteristics(SQLSubModel):
@classmethod
def _db_update(cls, execute, version, data=None):
major, minor, release = version.strip().split(".")
created = False
if major == "0" and int(minor) <= 1:
if int(release) < 7:
cls._db_create(execute)
created = True
if major == "0" and int(minor) < 2:
if cls.is_table_exists(execute, "Pollutants_characteristics"):
if not created:
cls._db_update_to_0_2_0(execute, data)
else:
cls._db_create(execute)
return cls._update_submodel(execute, version, data)
@@ -258,12 +253,6 @@ class PollutantCharacteristics(SQLSubModel):
if not self.must_be_saved():
return True
execute(
"DELETE FROM pollutants_characteristics " +
f"WHERE pamhyr_id = {self.id} " +
f"AND scenario = {self._status.scenario_id}"
)
execute(
"INSERT INTO " +
"pollutants_characteristics(pamhyr_id, deleted, " +
@@ -286,10 +275,7 @@ class PollutantCharacteristics(SQLSubModel):
class Pollutants(SQLSubModel):
_sub_classes = [
PollutantCharacteristics,
# BoundaryConditionAdisTS
]
_sub_classes = [PollutantCharacteristics]
def __init__(self, id: int = -1, name: str = "",
status=None, owner_scenario=-1):
@@ -307,11 +293,6 @@ class Pollutants(SQLSubModel):
self._enabled = True
self._data = []
self._boundary_conditions_adists = []
@property
def boundary_conditions_adists(self):
return self._boundary_conditions_adists
@property
def name(self):
@@ -320,37 +301,18 @@ class Pollutants(SQLSubModel):
@name.setter
def name(self, name):
self._name = name
self.modified()
self._status.modified()
@property
def data(self):
return self._data
@data.setter
def data(self, data):
self._data = [
PollutantCharacteristics(
type=int(data[0]),
diametre=float(data[1]),
rho=float(data[2]),
porosity=float(data[3]),
cdc_riv=float(data[4]),
cdc_cas=float(data[5]),
apd=float(data[6]),
ac=float(data[7]),
bc=float(data[8]),
pollutant=self,
status=self._status
)
]
@classmethod
def _db_create(cls, execute, ext=""):
execute(f"""
CREATE TABLE pollutants{ext}(
{cls.create_db_add_pamhyr_id()},
deleted BOOLEAN NOT NULL DEFAULT FALSE,
enabled BOOLEAN NOT NULL DEFAULT TRUE,
name TEXT NOT NULL,
{Scenario.create_db_add_scenario()},
{Scenario.create_db_add_scenario_fk()}
@@ -367,20 +329,14 @@ class Pollutants(SQLSubModel):
major, minor, release = version.strip().split(".")
created = False
if major == "0" and int(minor) < 2:
if cls.is_table_exists(execute, "Pollutants"):
cls._db_update_to_0_2_0(execute, data)
else:
if major == "0" and int(minor) <= 1:
if int(release) < 7:
cls._db_create(execute)
created = True
if major == "0" and minor == "2":
if int(release) < 6:
execute(f"ALTER TABLE pollutants " +
f"ADD COLUMN enabled BOOLEAN NOT NULL DEFAULT TRUE")
# cls._db_update_to_0_2_5(execute, data)
# # created = True
if major == "0" and int(minor) < 2:
if not created:
cls._db_update_to_0_2_0(execute, data)
if not created:
return cls._update_submodel(execute, version, data)
@@ -419,8 +375,7 @@ class Pollutants(SQLSubModel):
return new
table = execute(
"SELECT pamhyr_id, deleted, enabled, name, scenario " +
"FROM pollutants " +
"SELECT pamhyr_id, deleted, name FROM pollutants " +
f"WHERE scenario = {scenario.id} " +
f"AND pamhyr_id NOT IN ({', '.join(map(str, loaded))})"
)
@@ -431,30 +386,18 @@ class Pollutants(SQLSubModel):
pid = next(it)
deleted = (next(it) == 1)
enabled = (next(it) == 1)
name = next(it)
owner_scenario = next(it)
new_pollutant = cls(
id=pid, name=name,
status=status,
owner_scenario=owner_scenario
status=status
)
if deleted:
new_pollutant.set_as_deleted()
if not enabled:
new_pollutant.enabled = False
data["pollutant"] = new_pollutant
new_pollutant._data = PollutantCharacteristics._db_load(
execute, data=data
)
new_pollutant._boundary_conditions_adists = (
BoundaryConditionAdisTS._db_load(
execute,
data=data
)
execute, data.copy()
)
loaded.add(pid)
@@ -478,17 +421,16 @@ class Pollutants(SQLSubModel):
f"AND scenario = {self._status.scenario_id}"
)
execute(
"DELETE FROM pollutants_characteristics " +
f"DELETE FROM pollutants_characteristics " +
f"WHERE pollutant = {self.id} " +
f"AND scenario = {self._status.scenario_id}"
)
execute(
"INSERT INTO " +
"pollutants(pamhyr_id, deleted, enabled, name, scenario) " +
"pollutants(pamhyr_id, deleted, name, scenario) " +
"VALUES (" +
f"{self.id}, {self._db_format(self.is_deleted())}, " +
f"{self._db_format(self._enabled)}, " +
f"'{self._db_format(self._name)}', " +
f"{self._status.scenario_id}" +
")"
@@ -497,9 +439,6 @@ class Pollutants(SQLSubModel):
for d in self._data:
ok &= d._db_save(execute, data)
for bc in self._boundary_conditions_adists:
ok &= bc._db_save(execute, data)
return ok
def _data_traversal(self,
+1 -1
View File
@@ -19,7 +19,7 @@
from tools import trace, timer
from Model.Except import NotImplementedMethodeError
from Model.Tools.PamhyrListExt import PamhyrModelList
from Model.Tools.PamhyrList import PamhyrModelList
from Model.Pollutants.Pollutants import Pollutants
+4 -2
View File
@@ -263,6 +263,8 @@ class Reservoir(SQLSubModel):
cls._db_update_to_0_2_0_set_node_pid(execute, table, nodes)
Scenario.update_db_add_scenario(execute, table)
print(execute(f"SELECT * FROM {table}"))
cls._db_create(execute, ext="_tmp")
execute(
f"INSERT INTO {table}_tmp " +
@@ -308,11 +310,11 @@ class Reservoir(SQLSubModel):
new_reservoir.set_as_deleted()
new_reservoir._node = None
if node_id != -1 and node_id is not None:
if node_id != -1:
new_reservoir._node = next(
filter(
lambda n: n.id == node_id, data["nodes"]
), None
)
)
data["reservoir"] = new_reservoir
+2 -153
View File
@@ -29,134 +29,8 @@ from Model.Results.River.River import River
logger = logging.getLogger()
class AdditionalData(SQLSubModel):
_sub_classes = []
def __init__(self, id=-1, study=None, data=None):
super(AdditionalData, self).__init__(
id=id, status=study.status,
owner_scenario=study.status.scenario.id
)
self._study = study
self._data = data
@property
def data(self):
return self._data
@classmethod
def _db_create(cls, execute, ext=""):
execute(f"""
CREATE TABLE results_add_data{ext} (
{cls.create_db_add_pamhyr_id()},
result INTEGER NOT NULL,
type_x TEXT NOT NULL,
type_y TEXT NOT NULL,
legend TEXT NOT NULL,
unit TEXT NOT NULL,
data_len INTEGER NOT NULL,
x BLOB NOT NULL,
y BLOB NOT NULL,
{Scenario.create_db_add_scenario()},
{Scenario.create_db_add_scenario_fk()},
FOREIGN KEY(result) REFERENCES results(pamhyr_id),
PRIMARY KEY(pamhyr_id, result, scenario)
)
""")
if ext != "":
return True
return cls._create_submodel(execute)
@classmethod
def _db_update(cls, execute, version, data=None):
major, minor, release = version.strip().split(".")
if major == "0" and int(minor) == 2 and int(release) <= 1:
cls._db_create(execute)
return cls._update_submodel(execute, version, data)
@classmethod
def _db_load(cls, execute, data=None):
new = []
study = data['study']
status = data['status']
scenario = data["scenario"]
table = execute(
"SELECT pamhyr_id, type_x, type_y, " +
"legend, unit, data_len, x, y, " +
"scenario " +
"FROM results_add_data " +
f"WHERE scenario = {scenario.id}"
)
if table is None:
return new
for v in table:
it = iter(v)
pid = next(it)
type_x = next(it)
type_y = next(it)
legend = next(it)
unit = next(it)
data_len = next(it)
bx = next(it)
by = next(it)
owner_scenario = next(it)
data_format = ">" + ''.join(itertools.repeat("d", data_len))
x = struct.unpack(data_format, bx)
y = struct.unpack(data_format, by)
data = {
'type_x': type_x,
'type_y': type_y,
'legend': legend,
'unit': unit,
'x': x, 'y': y
}
new_data = cls(study=study)
new_data._data = data
new.append(new_data)
return new
def _db_save(self, execute, data=None):
if self._status.scenario.id != self._owner_scenario:
return
pid = self._pamhyr_id
data_len = len(self._data["x"])
data_format = ">" + ''.join(itertools.repeat("d", data_len))
bx = struct.pack(data_format, *self._data["x"])
by = struct.pack(data_format, *self._data["y"])
execute(
"INSERT INTO " +
"results_add_data (pamhyr_id, result, " +
"type_x, type_y, " +
"legend, unit, data_len, x, y, " +
"scenario) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
self._pamhyr_id, data["result"],
self._data["type_x"], self._data["type_y"],
self._data["legend"], self._data["unit"],
data_len, bx, by, self._owner_scenario
)
return True
class Results(SQLSubModel):
_sub_classes = [River, AdditionalData]
_sub_classes = [River]
def __init__(self, id=-1, study=None, solver=None,
repertory="", name="0"):
@@ -176,7 +50,6 @@ class Results(SQLSubModel):
# Keep results creation date
"creation_date": datetime.now(),
"study_revision": study.status.version,
"additional_data": [],
}
if solver is not None:
@@ -219,11 +92,6 @@ class Results(SQLSubModel):
qlog=None,
)
def bufferize(self, key):
if self.is_valid:
for reach in self._river._reachs:
reach.bufferize(self._meta_data["timestamps"], key)
def timestamps_to_struct(self):
ts = self._meta_data["timestamps"]
sf = ">" + ''.join(itertools.repeat("d", len(ts)))
@@ -316,11 +184,6 @@ class Results(SQLSubModel):
data["timestamps"] = sorted(ts)
new_results._river = River._db_load(execute, data)
new_results.set(
"additional_data",
AdditionalData._db_load(execute, data)
)
yield (solver_type, new_results)
def _db_save_clear(self, execute, solver_type, data=None):
@@ -343,21 +206,10 @@ class Results(SQLSubModel):
f"WHERE scenario = {self._owner_scenario} " +
f"AND result = {pid}"
)
execute(
"DELETE FROM results_add_data " +
f"WHERE scenario = {self._owner_scenario} " +
f"AND result = {pid}"
)
def _is_valid(self):
return "timestamps" in self._meta_data
def _db_save(self, execute, data=None):
if self._status.scenario.id != self._owner_scenario:
return True
if not self._is_valid():
return False
return
pid = self._pamhyr_id
if self._solver is None:
@@ -386,7 +238,4 @@ class Results(SQLSubModel):
data["result"] = self._pamhyr_id
self._river._db_save(execute, data)
for add_data in self.get("additional_data"):
add_data._db_save(execute, data)
return True
+1 -8
View File
@@ -21,13 +21,12 @@ import os
from copy import deepcopy
from datetime import datetime
from Model.Tools.PamhyrDB import SQLSubModel
from Model.Results.River.RiverAdisTS import River
logger = logging.getLogger()
class Results(SQLSubModel):
class Results(object):
def __init__(self, study=None, solver=None,
repertory="", name="0", type_pol=None):
self._study = study
@@ -51,9 +50,6 @@ class Results(SQLSubModel):
self._phys_var_list = ["C", "G", "M", "D", "L", "N", "R"]
if solver is not None:
self.set("solver_type", solver._type)
@property
def date(self):
date = self._meta_data["creation_date"]
@@ -91,6 +87,3 @@ class Results(SQLSubModel):
self._repertory,
qlog=None,
)
def _db_save(self, execute, data=None):
return False
+12 -20
View File
@@ -17,7 +17,6 @@
import struct
import logging
import itertools
import numpy as np
from tools import flatten
from functools import reduce
@@ -77,12 +76,9 @@ class Profile(SQLSubModel):
return self._data[timestamp][key]
return None
def has_sediment_layers(self):
def has_sediment(self):
return any(map(lambda ts: "sl" in self._data[ts], self._data))
def has_bedload(self):
return any(map(lambda ts: "zfd" in self._data[ts], self._data))
@classmethod
def _db_create(cls, execute, ext=""):
execute(f"""
@@ -109,6 +105,16 @@ class Profile(SQLSubModel):
@classmethod
def _db_update(cls, execute, version, data=None):
major, minor, release = version.strip().split(".")
create = False
if major == "0" and int(minor) < 2:
cls._db_create(execute)
create = True
if major == "0" and int(minor) == 2:
if int(release) < 1 and not create:
cls._db_create(execute)
create = True
return cls._update_submodel(execute, version, data)
@@ -279,7 +285,6 @@ class Reach(SQLSubModel):
lambda p: p.name[0:8] != 'interpol', self._profiles
)
)
self._buffers = {}
def __len__(self):
return len(self._profiles)
@@ -300,10 +305,6 @@ class Reach(SQLSubModel):
def profile_mask(self):
return self._profile_mask
@property
def buffers(self):
return self._buffers
def profile(self, id):
return self._profiles[id]
@@ -311,16 +312,7 @@ class Reach(SQLSubModel):
self._profiles[profile_id].set(timestamp, key, data)
def has_sediment(self):
return any(map(lambda profile: profile.has_sediment_layers(),
self._profiles))
def has_bedload(self):
return any(map(lambda profile: profile.has_bedload(), self._profiles))
def bufferize(self, timestamps, key):
self._buffers[key] = np.zeros((len(timestamps), len(self)))
for i, p in enumerate(self._profiles):
self._buffers[key][:, i] = p.get_key(key)
return any(map(lambda profile: profile.has_sediment(), self._profiles))
@classmethod
def _db_create(cls, execute, ext=""):
+3 -22
View File
@@ -59,7 +59,6 @@ from Model.LateralContributionsAdisTS.LateralContributionsAdisTSList \
import LateralContributionsAdisTSList
from Model.D90AdisTS.D90AdisTSList import D90AdisTSList
from Model.DIFAdisTS.DIFAdisTSList import DIFAdisTSList
from Model.GeoTIFF.GeoTIFFList import GeoTIFFList
from Model.Results.Results import Results
logger = logging.getLogger()
@@ -180,9 +179,6 @@ class RiverNode(Node):
return nodes
def _db_save(self, execute, data=None):
if not self.must_be_saved():
return True
execute(
"INSERT OR REPLACE INTO river_node(" +
"pamhyr_id, deleted, name, x, y, scenario" +
@@ -364,7 +360,7 @@ class RiverReach(Edge):
)
new.enable(enable=enabled)
if deleted:
new.set_as_deleted()
nd.set_as_deleted()
data["reach"] = new
new._reach = Reach._db_load(execute, data)
@@ -380,9 +376,6 @@ class RiverReach(Edge):
return reachs
def _db_save(self, execute, data=None):
if not self.must_be_saved():
return True
execute(
"INSERT OR REPLACE INTO " +
"river_reach(" +
@@ -392,7 +385,7 @@ class RiverReach(Edge):
"VALUES (" +
f"{self.pamhyr_id}, {self._db_format(self.is_deleted())}, " +
f"'{self._db_format(self._name)}', " +
f"{self._db_format(self.is_enable())}," +
f"{self._db_format(self.is_enable())},"
f"{self.node1.pamhyr_id}, {self.node2.pamhyr_id}, " +
f"{self._status.scenario_id}" +
")"
@@ -475,7 +468,6 @@ class River(Graph):
LateralContributionsAdisTSList,
D90AdisTSList,
DIFAdisTSList,
GeoTIFFList,
Results
]
@@ -513,8 +505,6 @@ class River(Graph):
self._D90AdisTS = D90AdisTSList(status=self._status)
self._DIFAdisTS = DIFAdisTSList(status=self._status)
self._geotiff = GeoTIFFList(status=self._status)
self._results = {}
@classmethod
@@ -549,7 +539,7 @@ class River(Graph):
new._nodes = RiverNode._db_load(
execute, data
)
data["nodes"] = new._nodes
data["nodes"] = new.nodes()
data['loaded_pid'] = set()
new._edges = RiverReach._db_load(
@@ -627,8 +617,6 @@ class River(Graph):
new._DIFAdisTS = DIFAdisTSList._db_load(execute, data)
new._geotiff = GeoTIFFList._db_load(execute, data)
return new
def _db_load_results(self, execute, data=None):
@@ -662,8 +650,6 @@ class River(Graph):
objs.append(self._D90AdisTS)
objs.append(self._DIFAdisTS)
objs.append(self._geotiff)
for solv_type in self.results:
objs.append(self.results[solv_type])
@@ -740,7 +726,6 @@ class River(Graph):
self._BoundaryConditionsAdisTS,
self._LateralContributionsAdisTS,
self._D90AdisTS, self._DIFAdisTS,
self._geotiff,
]
for solver in self._parameters:
@@ -833,10 +818,6 @@ Last export at: @date."""
def additional_files(self):
return self._additional_files
@property
def geotiff(self):
return self._geotiff
@property
def rep_lines(self):
return self._rep_lines
+25 -99
View File
@@ -17,56 +17,21 @@
# -*- coding: utf-8 -*-
import os
import logging
from tools import logger_exception, timer, flatten
from tools import logger_exception
from Model.Tools.PamhyrDB import SQLSubModel
logger = logging.getLogger()
class Scenario(SQLSubModel):
_id_cnt = 0
_sub_classes = []
tables_with_deleted_column = [
# Adists
"output_rk_adists",
"boundary_condition_adists", "boundary_condition_data_adists",
"lateral_contribution_adists", "lateral_contribution_data_adists",
"initial_conditions_adists", "initial_conditions_adists_spec",
"d90_adists", "d90_adists_spec",
"dif_adists_spec",
"pollutants", "pollutants_characteristics",
# Hydraulic
"additional_files",
"boundary_condition", "boundary_condition_data",
"lateral_contribution", "lateral_contribution_data",
"friction", "stricklers",
"hydraulic_structures",
"hydraulic_structures_basic",
"initial_conditions",
"sedimentary_layer", "sedimentary_layer_layer",
"reservoir", "reservoir_data",
"rep_lines",
"geometry_pointXYZ", "geometry_profileXYZ",
"river_reach", "river_node",
"geotiff", "reservoir", "reservoir_data",
]
related_tables = tables_with_deleted_column + [
"dif_adists",
"solver_parameter",
"hydraulic_structures_basic_value",
"results", "results_data", "results_add_data",
]
def __init__(self,
id: int = -1,
name: str = "",
description: str = "",
x: int = 1000.0, y: int = 1000.0,
x: int = 0.0, y: int = 0.0,
revision: int = 0,
parent=None):
super(Scenario, self).__init__()
@@ -206,7 +171,29 @@ class Scenario(SQLSubModel):
def drop_all(self, execute):
execute(f"DELETE FROM scenario WHERE id = {self.id}")
tables = self.related_tables
tables = [
# Adists
"output_rk_adists",
"boundary_condition_adists", "boundary_condition_data_adists",
"lateral_contribution_adists", "lateral_contribution_data_adists",
"initial_conditions_adists",
"d90_adists", "dif_adists",
"pollutants", "pollutants_characteristics",
# Hydraulic
"additional_files",
"boundary_condition", "boundary_condition_data",
"lateral_contribution", "lateral_contribution_data",
"friction", "stricklers",
"hydraulic_structures",
"hydraulic_structures_basic", "hydraulic_structures_basic_value",
"initial_conditions",
"sedimentary_layer", "sedimentary_layer_layer",
"reservoir", "reservoir_data",
"rep_lines",
"solver_parameter",
"geometry_pointXYZ", "geometry_profileXYZ",
"river_reach", "river_node",
]
for table in tables:
execute(
@@ -216,67 +203,6 @@ class Scenario(SQLSubModel):
return True
def get_parent_branch(self):
def aux(scenario, acc):
if scenario is None:
return acc
return aux(scenario.parent, [scenario.id] + acc)
return aux(self, [])
@timer
def clean_deleted_data(self, execute):
tables = self.tables_with_deleted_column
branch = self.get_parent_branch()
all_ids = []
for table in tables:
if self.parent is None:
# This scenario is the default scenario, so we can
# delete all data marked as deleted
execute(
f"DELETE FROM {table} " +
f"WHERE scenario = {self.id} " +
"AND deleted = TRUE"
)
continue
# Select pamhyr_id for each deleted data in this scenario
# who do not exists into parents scenarios
ids = execute(
f"""
SELECT pamhyr_id FROM {table}
WHERE deleted = TRUE
AND scenario = {self.id}
AND pamhyr_id NOT IN (
SELECT pamhyr_id FROM {table}
WHERE scenario IN
({', '.join(map(str, branch[:-1]))})
)
""", fetch_one=False
)
if ids is None or len(ids) == 0:
continue
ids = flatten(ids)
logger.debug(
f"({self.name}) Drop deleted data into '{table}' : {ids}"
)
execute(
f"DELETE FROM {table} " +
f"WHERE scenario = {self.id} " +
"AND pamhyr_id IN " +
f"({', '.join(map(str, ids))})"
)
all_ids += ids
return all_ids
@property
def id(self):
return self._id
-11
View File
@@ -16,8 +16,6 @@
# -*- coding: utf-8 -*-
from functools import reduce
from tools import logger_exception
from Model.Tools.PamhyrDict import PamhyrModelDict
@@ -80,12 +78,3 @@ class Scenarios(PamhyrModelDict):
self._dict[key].set_as_deleted()
self._status.modified()
def is_leaf(self, scenario):
return not reduce(
lambda acc, s: (
acc or (not s.is_deleted() and s.parent is scenario)
),
self._dict.values(),
False
)
-13
View File
@@ -34,7 +34,6 @@ class StudyStatus(object):
self._status = Status.EDIT
self._scenario = scenario
self._saved = True
self._loading = False
@property
def scenario_id(self):
@@ -87,15 +86,6 @@ class StudyStatus(object):
def is_saved(self):
return self._saved
def is_loading(self):
return self._loading
def start_loading(self):
self._loading = True
def stop_loading(self):
self._loading = False
def save(self):
logger.debug("model status set as saved")
self._saved = True
@@ -107,9 +97,6 @@ class StudyStatus(object):
self._saved = True
def modified(self):
if self._loading:
return
if self._saved:
self.version += 1
+11 -81
View File
@@ -24,21 +24,12 @@ from datetime import datetime
from tools import timer, timestamp
from functools import reduce
from Model.Tools.PamhyrListExt import PamhyrModelList
from Model.Tools.PamhyrDict import PamhyrModelDict
from Model.Tools.PamhyrDB import SQLModel
from Model.Scenarios import Scenarios
from Model.Scenario import Scenario
from Model.Status import StudyStatus
from Model.Except import NotImplementedMethodeError
from Model.River import River
from Model.Geometry.Reach import Reach
from Model.HydraulicStructures.HydraulicStructures import (
HydraulicStructure
)
from Model.HydraulicStructures.Basic.HydraulicStructures import (
BasicHS
)
from Checker.Study import *
@@ -46,7 +37,7 @@ logger = logging.getLogger()
class Study(SQLModel):
_version = "0.2.6"
_version = "0.2.1"
_sub_classes = [
Scenario,
@@ -458,60 +449,8 @@ class Study(SQLModel):
[self.scenarios, self._river],
data=progress
)
# Clear DB for current scenario
ids = self.status.scenario.clean_deleted_data(self.execute)
self.memory_clean(ids)
progress()
self.commit()
@timer
def memory_clean(self, ids):
if len(ids) == 0:
return
reach_class = Reach
hs_classes = [HydraulicStructure, BasicHS]
list_classes = set(PamhyrModelList.__subclasses__())
dict_classes = set(PamhyrModelDict.__subclasses__())
def modifier(obj, data):
t = type(obj)
if t is reach_class:
obj._profiles = list(
filter(
lambda el: el.id not in ids,
obj._profiles
)
)
elif t in hs_classes:
obj._data = list(
filter(
lambda el: el.id not in ids,
obj._data
)
)
elif t in dict_classes:
new = {}
for key in obj._dict:
if obj._dict[key].id not in ids:
new[key] = obj._dict[key]
obj._dict = new
elif t in list_classes:
obj._lst = list(
filter(
lambda el: el.id not in ids,
obj._lst
)
)
self.river._data_traversal(
lambda o, d: True, modifier, {}
)
def sql_save_request_count(self, *args, **kargs):
return self._count()
@@ -520,10 +459,7 @@ class Study(SQLModel):
[self.scenarios, self._river]
)
logger.debug(cnt)
# HACK: 8 is the number of SQL request do and dont counted in
# submodel save_count (let's count sql execute into the
# '_save' method)
return cnt + 8
return cnt + 7
def close(self):
"""Close db connection
@@ -549,8 +485,7 @@ class Study(SQLModel):
self.status.scenario = scenario
if reduce(
lambda a, s: a or (s.parent is scenario
and not s.is_deleted()),
lambda a, s: a or (s.parent is scenario),
self.scenarios.lst,
False
):
@@ -576,20 +511,15 @@ class Study(SQLModel):
"scenario": scenario
}
self.status.start_loading()
try:
# Load river data
river = River._db_load(
sql_exec, data=data
)
data["study"] = self
river._db_load_results(sql_exec, data=data)
finally:
self.status.stop_loading()
self.status.scenario = old_scenario
# Load river data
river = River._db_load(
sql_exec, data=data
)
data["study"] = self
river._db_load_results(sql_exec, data=data)
# FIXME: Disable scenario cache to save memory usage
# self._river_scenario_cache[scenario] = river
self._river_scenario_cache[scenario] = river
self.status.scenario = old_scenario
return river
+2 -23
View File
@@ -26,8 +26,6 @@ from functools import reduce
from SQL import SQL
from Model.Except import NotImplementedMethodeError
from tools import trace
from Model.Tools.PamhyrID import PamhyrID
logger = logging.getLogger()
@@ -50,9 +48,11 @@ class SQLModel(SQL):
self._cur = self._db.cursor()
if is_new:
logger.info("Create database")
self._create() # Create db
# self._save() # Save
else:
logger.info("Update database")
self._update() # Update db scheme if necessary
# self._load() # Load data
@@ -202,8 +202,6 @@ class SQLSubModel(PamhyrID):
"""
if self._status is None:
return
if self._status.is_loading():
return
self._owner_scenario = self._status.scenario_id
self._status.modified()
@@ -220,8 +218,6 @@ class SQLSubModel(PamhyrID):
"""
if self._status is None:
return
if self._status.is_loading():
return
self._owner_scenario = self._status.scenario_id
self._status.modified()
@@ -301,15 +297,6 @@ class SQLSubModel(PamhyrID):
"""
raise NotImplementedMethodeError(cls, cls._db_update)
@classmethod
def is_table_exists(cls, execute, table):
return any(
execute(
"SELECT name FROM sqlite_master " +
f"WHERE type='table' AND name='{table}'"
)
)
@classmethod
def _db_update_to_0_2_0_set_node_pid(cls, execute, table, nodes):
els = execute(
@@ -324,14 +311,6 @@ class SQLSubModel(PamhyrID):
if node_id == -1:
continue
if node_id not in nodes:
# ⚠️ cas important : probablement déjà migré
logger.warning(
f"node_id {node_id} not in nodes " +
"→ probably already migrated"
)
continue
execute(
f"UPDATE {table} " +
f"SET node = {nodes[node_id]} " +
-3
View File
@@ -97,9 +97,6 @@ class PamhyrModelDict(SQLSubModel):
Nothing
"""
if predicate(self, data):
modifier(self, data)
for key in self._dict:
self._dict[key]\
._data_traversal(predicate, modifier, data)
-3
View File
@@ -100,9 +100,6 @@ class PamhyrModelList(SQLSubModel):
Nothing
"""
if predicate(self, data):
modifier(self, data)
for el in self._lst:
el._data_traversal(predicate, modifier, data)
+1 -4
View File
@@ -107,9 +107,6 @@ class PamhyrModelList(SQLSubModel):
Returns:
Nothing
"""
if predicate(self, data):
modifier(self, data)
for el in self._lst:
el._data_traversal(predicate, modifier, data)
@@ -171,7 +168,7 @@ class PamhyrModelList(SQLSubModel):
lambda x: x[1],
filter(
lambda x: x[0] in indexes,
enumerate(self.lst)
enumerate(self._lst)
)
)
)
-6
View File
@@ -56,7 +56,6 @@ class Modules(IterableFlag):
SEDIMENT_LAYER = auto()
ADDITIONAL_FILES = auto()
OUTPUT_RK = auto()
GEOTIFF = auto()
# Results
RESULTS = auto()
@@ -82,7 +81,6 @@ class Modules(IterableFlag):
cls.RESULTS,
cls.WINDOW_LIST,
cls.OUTPUT_RK,
cls.GEOTIFF
]
@classmethod
@@ -101,7 +99,6 @@ class Modules(IterableFlag):
| cls.HYDRAULIC_STRUCTURES
| cls.RESERVOIR
| cls.SEDIMENT_LAYER
| cls.GEOTIFF
)
@classmethod
@@ -117,7 +114,6 @@ class Modules(IterableFlag):
cls.HYDRAULIC_STRUCTURES,
cls.RESERVOIR,
cls.SEDIMENT_LAYER,
cls.GEOTIFF,
]
@classmethod
@@ -133,7 +129,6 @@ class Modules(IterableFlag):
cls.HYDRAULIC_STRUCTURES: "Hydraulic structures",
cls.RESERVOIR: "Reservoir",
cls.SEDIMENT_LAYER: "Sediment layer",
cls.GEOTIFF: "GeoTIFF",
}
def impact(self):
@@ -173,5 +168,4 @@ _impact = {
Modules.HYDRAULIC_STRUCTURES: [],
Modules.RESERVOIR: [],
Modules.SEDIMENT_LAYER: [],
Modules.GEOTIFF: [],
}
+78 -294
View File
@@ -96,262 +96,18 @@ class AdisTS(CommandLineSolver):
return lst
##########
# Export #
##########
_alph = list(
map(
chr,
chain(
range(48, 58), # 0..9
range(65, 91), # A..Z
range(97, 123) # a..z
)
)
)
_l_alph = len(_alph)
_nodes_cnt = 0
_nodes_names = {}
_nodes_views = set()
def get_reach_name(self, reach):
return f"Reach_{reach.pamhyr_id:>3}".replace(" ", "0")
def get_node_name(self, node):
"""Generate a 3 char name for node
Args:
node: The node
Returns:
A 3 char name string
"""
n = node.pamhyr_id
if n in self._nodes_names:
return self._nodes_names[n]
name = ""
checked_new = False
while not checked_new:
self._nodes_cnt += 1
nc = self._nodes_cnt
name = "".join(
map(
lambda i: self._alph[i % self._l_alph],
[
int(nc / (self._l_alph * self._l_alph)),
int(nc / self._l_alph),
nc
]
)
)
checked_new = name not in self._nodes_views
self._nodes_views.add(name)
self._nodes_names[n] = name
return name
def cmd_args(self, study):
lst = super(AdisTS, self).cmd_args(study)
return lst
def input_param(self):
name = self._study.name.replace(" ", "_")
name = self._study.name
return f"{name}.REP"
def log_file(self):
name = self._study.name.replace(" ", "_")
name = self._study.name
return f"{name}.TRA"
def _export_ST(self, study, repertory, qlog, name="0"):
files = []
if qlog is not None:
qlog.put("Export ST file")
os.makedirs(os.path.join(repertory, "net"), exist_ok=True)
# Write header
edges = study.river.enable_edges()
for edge in edges:
name = f"Reach_{edge.id + 1:>3}".replace(" ", "0")
with adists_file_open(
os.path.join(repertory, "net", f"{name}.ST"),
"w+"
) as f:
files.append(str(os.path.join("net", f"{name}.ST")))
cnt_num = 1
for profile in edge.reach.profiles:
self._export_ST_profile_header(
f, files, profile, cnt_num
)
cnt_num += 1
# Points
for point in profile.points:
self._export_ST_point_line(
f, files, point
)
# Profile last line
f.write(f" 999.9990 999.9990 999.9990\n")
def _export_ST_profile_header(self, wfile, files,
profile, cnt):
num = f"{cnt:>6}"
c1 = f"{profile.code1:>6}"
c2 = f"{profile.code2:>6}"
t = f"{len(profile.points):>6}"
rk = f"{profile.rk:>12f}"[0:12]
pname = profile.name
if profile.name == "":
# Generate name from profile id prefixed with
# 'p' (and replace space char with '0' char)
pname = f"p{profile.id:>3}".replace(" ", "0")
name = f"{pname:<19}"
# Generate sediment additional data if available
sediment = ""
if profile.sl is not None:
if not any(filter(lambda f: ".GRA" in f, files)):
files.append(self._gra_file)
# Number of layers
nl = len(profile.sl)
sediment = f" {nl:>3}"
# Layers data
for layer in profile.sl.layers:
sediment += (
f" {layer.height:>10} {layer.d50:>10} " +
f"{layer.sigma:>10} " +
f"{layer.critical_constraint:>10}"
)
# Profile header line
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]
y = f"{point.y:<12.4f}"[0:12]
z = f"{point.z:<12.4f}"[0:12]
n = f"{point.name:<3}"
# Generate sediment additional data if available
sediment = ""
prev = point.z
if point.sl is not None:
# Number of layers
nl = len(point.sl)
sediment = f"{nl:>3}"
# Layers data
for layer in point.sl.layers:
prev = round(prev - layer.height, 5)
sediment += (
f" {prev:>10} {layer.d50:>10} " +
f"{layer.sigma:>10} " +
f"{layer.critical_constraint:>10}"
)
# Point line
wfile.write(f"{x} {y} {z} {n} {sediment}\n")
def _export_NET(self, study, repertory, qlog=None, name="0"):
if qlog is not None:
qlog.put("Export NET file")
with adists_file_open(
os.path.join(repertory, f"{name}.NET"), "w+") as f:
edges = study.river.enable_edges()
for e in edges:
name = f"Reach_{e.id + 1:>3}".replace(" ", "0")
id = name
n1 = f"{self.get_node_name(e.node1):3}".replace(" ", "x")
n2 = f"{self.get_node_name(e.node2):3}".replace(" ", "x")
file = os.path.join("net", name + ".ST")
f.write(f"{id} {n1} {n2} {file}\n")
def _export_fake_INI(self, study, repertory, qlog=None, name="0"):
if qlog is not None:
qlog.put("Export fake INI file")
with adists_file_open(
os.path.join(
repertory, "Mage_fin.ini"
), "w+"
) as f:
edges = study.river.enable_edges()
for i, edge in enumerate(edges):
lst = list(filter(
lambda f: f.is_full_defined(),
edge.frictions.frictions
))
rk_min = 9999999.9
rk_max = -9999999.9
coeff_min = -1.0
coeff_max = -1.0
for s in lst: # TODO optimise ?
if s.begin_rk > rk_max:
rk_max = s.begin_rk
coeff_max = s.begin_strickler
if s.begin_rk < rk_min:
rk_min = s.begin_rk
coeff_min = s.begin_strickler
if s.end_rk > rk_max:
rk_max = s.end_rk
coeff_max = s.end_strickler
if s.end_rk < rk_min:
rk_min = s.end_rk
coeff_min = s.end_strickler
print("min max", rk_min, rk_max)
def get_stricklers_from_rk(rk, lst):
print("rk", rk)
coeff = None
if rk > rk_max:
coeff = coeff_max
elif rk < rk_min:
coeff = coeff_min
else:
for s in lst:
if (rk >= s.begin_rk and rk <= s.end_rk or
rk <= s.begin_rk and rk >= s.end_rk):
coeff = s.begin_strickler # TODO: inerpolate
break
# TODO interpolation if rk is not in frictons
if coeff is None:
logger.error(
"Study frictions are not fully defined"
)
return None
return coeff.minor, coeff.medium
for j, profile in enumerate(edge.reach.profiles):
coef_min, coef_moy = get_stricklers_from_rk(profile.rk,
lst)
f.write(
f" {i+1:3}{j+1:4}{' '*116}{coef_min:9}{coef_moy:9}\n")
def _export_REP_additional_lines(self, study, rep_file):
lines = filter(
lambda line: line.is_enabled(),
@@ -373,7 +129,7 @@ class AdisTS(CommandLineSolver):
), "w+"
) as f:
path = os.path.join("..", mage_rep, name)
f.write(f"NET {name}.NET\n")
f.write(f"NET {path}.NET\n")
f.write(f"REP {path}.REP\n")
for file in files:
@@ -382,25 +138,13 @@ class AdisTS(CommandLineSolver):
self._export_REP_additional_lines(study, f)
self._export_ST(study, repertory, qlog, name=name)
self._export_NET(study, repertory, qlog, name=name)
path_mage_net = os.path.join(os.path.abspath(
os.path.join(repertory, os.pardir)
), os.path.join(mage_rep, "net"))
path_adists_net = os.path.join(repertory, "net")
# fake mage_fin.ini:
path_mage = os.path.join(os.path.abspath(
os.path.join(repertory, os.pardir)), mage_rep)
self._export_fake_INI(study, path_mage,
qlog, name=name)
# path_mage_net = os.path.join(os.path.abspath(
# os.path.join(repertory, os.pardir)
# ), os.path.join(mage_rep, "net"))
# path_adists_net = os.path.join(repertory, "net")
# if os.path.exists(path_mage_net):
# shutil.copytree(path_mage_net,
# path_adists_net,
# dirs_exist_ok=True)
if os.path.exists(path_mage_net):
shutil.copytree(path_mage_net, path_adists_net, dirs_exist_ok=True)
@timer
def export(self, study, repertory, qlog=None):
@@ -453,7 +197,48 @@ class AdisTS(CommandLineSolver):
_nodes_views = set()
def get_reach_name(self, reach):
return f"Reach_{reach.pamhyr_id:>3}".replace(" ", "0")
return f"Reach_{reach.id:>3}".replace(" ", "0")
def get_node_name(self, node):
"""Generate a 3 char name for node
Args:
node: The node
Returns:
A 3 char name string
"""
n = node.id
# print("node name id: ", n)
# if n in self._nodes_names:
# return self._nodes_names[n]
name = ""
checked_new = False
while not checked_new:
self._nodes_cnt += 1
nc = self._nodes_cnt
name = "".join(
map(
lambda i: self._alph[i % self._l_alph],
[
int(nc / (self._l_alph * self._l_alph)),
int(nc / self._l_alph),
nc
]
)
)
checked_new = name not in self._nodes_views
self._nodes_views.add(name)
self._nodes_names[n] = name
return name
#################################
# Adis-TS in weak coupling mode #
@@ -553,9 +338,9 @@ class AdisTSwc(AdisTS):
) as f:
for LC in POL_LC:
reach = next(filter(
lambda edge: edge.id == LC.edge, study.river.enable_edges()
lambda edge: edge.id == LC.edge, study.river.edges()
)) # .name
reach_name = self.get_reach_name(reach)
reach_name = self.get_reach_name(self, reach)
f.write(f"${reach_name} {LC.begin_rk} {LC.end_rk}\n")
f.write(f"*temps |débit massique (kg/s)\n")
f.write(f"*---------++++++++++\n")
@@ -580,7 +365,8 @@ class AdisTSwc(AdisTS):
node = next(filter(
lambda x: x.id == BC.node, study.river._nodes
))
f.write(f"${self.get_node_name(node)}\n")
node_name = f"{node.id:3}".replace(" ", "x")
f.write(f"${node_name}\n")
if BC.type == "Concentration":
f.write(f"*temps |concentration\n")
@@ -901,29 +687,29 @@ class AdisTSwc(AdisTS):
) - k)
data = np.fromfile(f, dtype=np.int32, count=1) # (end)
# fifth line (useless)
data = np.fromfile(f, dtype=np.int32, count=1) # (start)
zmin_OLD = np.fromfile(f, dtype=np.float32, count=1)[0]
data = np.fromfile(f, dtype=np.int32, count=1) # (end)
# sixth line
zf = np.zeros(ismax, dtype=np.float32)
z = np.zeros(ismax * 3, dtype=np.float32)
for k in range(0, ismax, kbl):
# fifth line (useless)
data = np.fromfile(f, dtype=np.int32, count=1) # (start)
z[3 * k:3 * min(k + kbl, ismax)] = \
np.fromfile(f,
dtype=np.float32,
count=3 * (min(k + kbl, ismax) - k)
)
# z[i*3+1] and z[i*3+2] are useless
zmin_OLD = np.fromfile(f, dtype=np.float32, count=1)[0]
data = np.fromfile(f, dtype=np.int32, count=1) # (end)
zf = [z[i * 3] for i in range(ismax)]
# seventh line (useless)
for k in range(0, ismax, kbl):
data = np.fromfile(f, dtype=np.int32, count=1) # (start)
zero = np.fromfile(f, dtype=np.int32, count=ismax)
data = np.fromfile(f, dtype=np.int32, count=1) # (end)
# end header
# sixth line
zf = np.zeros(ismax, dtype=np.float32)
z = np.zeros(ismax * 3, dtype=np.float32)
for k in range(0, ismax, kbl):
data = np.fromfile(f, dtype=np.int32, count=1) # (start)
z[3 * k:3 * min(k + kbl, ismax)] = \
np.fromfile(f,
dtype=np.float32,
count=3 * (min(k + kbl, ismax) - k)
)
# z[i*3+1] and z[i*3+2] are useless
data = np.fromfile(f, dtype=np.int32, count=1) # (end)
zf = [z[i * 3] for i in range(ismax)]
# seventh line (useless)
for k in range(0, ismax, kbl):
data = np.fromfile(f, dtype=np.int32, count=1) # (start)
zero = np.fromfile(f, dtype=np.int32, count=ismax)
data = np.fromfile(f, dtype=np.int32, count=1) # (end)
# end header
def ip_to_r(i):
return iprofiles[
@@ -945,7 +731,6 @@ class AdisTSwc(AdisTS):
for file_bin in path_files:
key_pol = os.path.basename(file_bin)[0:-4]
data_tmp[key_pol] = {}
logger.info(f"read_bin: Start reading '{file_bin}' ...")
with open(file_bin, 'rb') as f:
# header
# first line
@@ -997,9 +782,7 @@ class AdisTSwc(AdisTS):
# seventh line (useless)
for k in range(0, ismax, kbl):
data = np.fromfile(f, dtype=np.int32, count=1) # (start)
zero = np.fromfile(
f, dtype=np.int32, count=min(k + kbl, ismax) - k
)
zero = np.fromfile(f, dtype=np.int32, count=ismax)
data = np.fromfile(f, dtype=np.int32, count=1) # (end)
# end header
# data
@@ -1030,6 +813,7 @@ class AdisTSwc(AdisTS):
for r, reach in enumerate(reachs):
for i in range(is1[r]-1, is2[r]):
p_i = ip_to_ri(reach, i)
for t_data in timestamps_keys:
pol_view = []
for pol in pollutants_keys:
+25 -66
View File
@@ -19,8 +19,6 @@
import os
import logging
import numpy as np
import time
from functools import reduce
from itertools import chain
from tools import timer, trace, logger_exception
@@ -327,7 +325,7 @@ class Mage(CommandLineSolver):
v0 = d[0]
v1 = d[1]
if t in ["HYD", "LIM"]:
if t in ["HYD", "QSO", "LIM"]:
v0 /= 60 # Convert first column to minute
f.write(f"{v0:9} {v1:9} \n")
@@ -386,26 +384,26 @@ class Mage(CommandLineSolver):
return files
def _export_LC(self, study, lateral, f, qlog, name="0"):
if lateral.reach is None:
if lateral.edge is None:
return
if lateral.is_deleted():
return
edges = study.river.enable_edges()
if lateral.reach not in edges:
if lateral.edge not in edges:
return
eid, _ = next(
filter(
lambda e: e[1] == lateral.reach,
lambda e: e[1] == lateral.edge,
enumerate(edges)
)
)
name = f"{eid+1:>3}"
# name = f"Reach_{lateral.reach.id + 1:>3}".replace(" ", "0")
f.write(f"* {lateral.reach.name} ({name}) {lateral.lctype}\n")
# name = f"Reach_{lateral.edge.id + 1:>3}".replace(" ", "0")
f.write(f"* {lateral.edge.name} ({name}) {lateral.lctype}\n")
f.write(
f"${name} " +
f"{lateral.begin_rk:>10.4f} {lateral.end_rk:>10.4f}\n"
@@ -546,15 +544,6 @@ class Mage(CommandLineSolver):
return files
def skip_hs(self, hs):
return (
hs.input_reach is None
or not hs.input_reach.is_enable()
or not hs.enabled
or hs.input_section is None
or hs.is_deleted()
)
def _export_SIN(self, study, repertory, qlog, name="0"):
files = []
@@ -580,7 +569,19 @@ class Mage(CommandLineSolver):
files.append(f"{name}.SIN")
for hs in hydraulic_structures:
if self.skip_hs(hs):
if hs.input_reach is None:
continue
if not hs.input_reach.is_enable():
continue
if not hs.enabled:
continue
if hs.input_section is None:
continue
if hs.is_deleted():
continue
f.write(
@@ -987,7 +988,7 @@ class Mage8(Mage):
# Data
for d in bound.data:
f.write(f"{d[0]/60:10.3f}{d[1]:10.3f}\n")
f.write(f"{d[0]:10.3f}{d[1]:10.3f}\n")
return files
@@ -1034,7 +1035,6 @@ class Mage8(Mage):
@timer
def read_bin(self, study, fname, results, qlog=None, name="0"):
logger.info(f"read_bin: Start reading '{fname}' ...")
start = time.time()
with mage_file_open(fname, "r") as f:
def newline(): return np.fromfile(f, dtype=np.int32, count=1)
@@ -1117,8 +1117,7 @@ class Mage8(Mage):
# Z and Y (4th line)
newline()
tmp = read_float(3*nb_profile)
zfd = tmp[::3]
_ = read_float(3*nb_profile)
endline()
# Data
@@ -1181,9 +1180,10 @@ class Mage8(Mage):
logger.info(f"compute tab...")
for r in reachs:
for p in r.profiles:
p.geometry.compute_tabulation()
if not p.geometry.tab_up_to_date:
p.geometry.compute_tabulation()
logger.info(f"compute velocity...")
logger.info(f"compute velocily...")
for r in reachs:
for t in ts_list:
@@ -1195,11 +1195,6 @@ class Mage8(Mage):
r.set(i, t, "V", v)
logger.info(f"read_bin: ... end with {len(ts)} timestamp read")
results.bufferize("Z")
results.bufferize("Q")
results.bufferize("V")
logger.info(f"reading time: '{time.time() - start}'")
@timer
def read_gra(self, study, repertory, results, qlog=None, name="0"):
if not study.river.has_sediment():
@@ -1207,7 +1202,6 @@ class Mage8(Mage):
fname = os.path.join(repertory, f"{name}.GRA")
logger.info(f"read_gra: Start reading '{fname}' ...")
start = time.time()
with mage_file_open(fname, "r") as f:
def newline(): return np.fromfile(f, dtype=np.int32, count=1)
@@ -1338,7 +1332,7 @@ class Mage8(Mage):
i_pts = 0
i_data = 0
# Loop on cross sections
# Loop on cross section
for i in range(n):
sec_sl = []
reach = ip_to_r(i)
@@ -1367,43 +1361,8 @@ class Mage8(Mage):
)
end = newline().size <= 0
ts_list = sorted(ts)
logger.info(f"compute river bed elevation...")
for r in reachs:
z_min = r.geometry.get_z_min()
sls = list(map(
lambda p: p.get_ts_key(ts_list[0], "sl")[0],
r.profiles
))
z_br = list(map(
lambda z, sl: reduce(
lambda z, h: z - h[0],
sl, z
),
z_min, # Original geometry
sls # Original sediment layers
))
for t in ts_list:
sls = list(map(
lambda p: p.get_ts_key(t, "sl")[0],
r.profiles
))
zfd = list(map(
lambda z, sl: reduce(
lambda z, h: z + h[0],
sl, z
),
z_br, # bedrock
sls # Original sediment layers
))
for i, p in enumerate(r.profiles):
r.set(i, t, "zfd", zfd[i])
results.set("sediment_timestamps", ts)
logger.info(f"read_gra: ... end with {len(ts)} timestamp read")
logger.debug(f"reading time: '{time.time() - start}'")
@timer
def results(self, study, repertory,
+16 -138
View File
@@ -58,12 +58,12 @@ class Rubar3(CommandLineSolver):
("rubarbe_iovis", "n"),
("rubarbe_rep", "n"),
("rubarbe_tinit", "000:00:00:00"),
("rubarbe_tmax", "000:01:00:00"), # 1 day
("rubarbe_tmax", "999:99:99:00"),
("rubarbe_tiopdt", "000:00:00:00"),
("rubarbe_dt", "5.0"),
("rubarbe_ts", "999:99:99:00"),
("rubarbe_dtsauv", "00:00:05:00"),
("rubarbe_psave", "00:00:05:00"),
("rubarbe_dtsauv", "00:00:00:05"),
("rubarbe_psave", "00:00:00:05"),
("rubarbe_fdeb1", "1"),
("rubarbe_fdeb2", "10"),
("rubarbe_fdeb3", "100"),
@@ -169,8 +169,9 @@ class Rubar3(CommandLineSolver):
it = iter(params)
param = next(it, None)
while param is not None:
line = 0
while line < 27:
param = next(it)
name = param.name
value = param.value
@@ -203,7 +204,7 @@ class Rubar3(CommandLineSolver):
# New line
f.write(f"\n")
param = next(it, None)
line += 1
def _export_ts(self, study, repertory, qlog, name="0"):
if qlog is not None:
@@ -426,6 +427,7 @@ class Rubar3(CommandLineSolver):
coeff = coeff_min
else:
for s in lst:
print(s.begin_rk, s.end_rk)
if (rk >= s.begin_rk and rk <= s.end_rk or
rk <= s.begin_rk and rk >= s.end_rk):
coeff = s.begin_strickler # TODO: inerpolate
@@ -472,6 +474,7 @@ class Rubar3(CommandLineSolver):
last = profiles[-1]
if first not in data or last not in data:
print(data)
logger.error(
"Study initial condition is not fully defined"
)
@@ -572,12 +575,8 @@ class Rubar3(CommandLineSolver):
for bc in bcs:
f.write(f"{len(bc)}\n")
if bc.bctype == "ZD":
for d0, d1 in bc.data:
f.write(f"{d1} {d0}\n")
else:
for d0, d1 in bc.data:
f.write(f"{d0} {d1}\n")
for d0, d1 in bc.data:
f.write(f"{d1} {d0}\n")
def read_profil(self, study, fname, results, qlog=None, name="0"):
logger.info(f"read: profil.{name}")
@@ -588,15 +587,10 @@ class Rubar3(CommandLineSolver):
# Add results reach to reach list
res_reach = results.river.add(i)
reachs.append((res_reach, len(reach) + 1))
results.set("nb_reach", f"{len(reachs)}")
results.set("nb_profile", f"{sum([r[1] - 1 for r in reachs])}")
def read_data_line(f):
line = f.readline().split()
if len(line) > 3:
return tuple(map(float, line))
else:
return (0.0, 0.0, 0.0, 0.0)
return tuple(map(float, line))
def set_and_compute_limites(reach, ind, z, q, s):
reach.set(ind, timestamp, "Z", z)
@@ -610,28 +604,6 @@ class Rubar3(CommandLineSolver):
)
ts = set()
timestamp = next(filter(
lambda p: p.name == 'rubarbe_tinit',
study.river.get_params(self._type).parameters
)).value
if timestamp.count(':') == 3:
timestamp = old_pamhyr_date_to_timestamp(timestamp)
ts.add(timestamp)
# add initial condition
for r, edge in enumerate(study.river.enable_edges()):
reach = edge.reach
ics = study.river.initial_conditions.get(edge)
q = ics.get_discharge()
z = ics.get_elevation()
k = 0
for i, j in zip(z, q):
v = reach.profiles[k].speed(i, j)
set_and_compute_limites(reachs[r][0], k, i, j, v)
k += 1
# start read
end = False
while True:
line = f.readline()
@@ -656,9 +628,9 @@ class Rubar3(CommandLineSolver):
h, s, q, z = read_data_line(f)
set_and_compute_limites(
reach, ind,
(pz + z + ph + h) / 2,
(pq + q) / 2,
(ps + s) / 2
(ph + pz + ph + h) / 2,
(pz + s) / 2,
(pq + q) / 2
)
ph = h
@@ -672,107 +644,13 @@ class Rubar3(CommandLineSolver):
set_and_compute_limites(reach, ind, z+h, q, s)
@timer
def write_bin(self, study, fname, results, qlog=None, name="0"):
logger.info(f"write_bin: Start writing '{fname}' ...")
with open(fname, "wb") as f:
def newline(j): return np.asarray([j], dtype=np.int32).tofile(f)
def endline(j): return np.asarray([j], dtype=np.int32).tofile(f)
def write_int(i):
newline(len(i)*4)
np.array(i, dtype=np.int32).tofile(f)
endline(len(i)*4)
def write_float(i):
newline(len(i)*4)
np.array(i, dtype=np.float32).tofile(f)
endline(len(i)*4)
def write_float64(i):
newline(len(i)*8)
np.array(i, dtype=np.float64).tofile(f)
endline(len(i)*8)
def write_float64(i):
newline(len(i)*8)
np.array(i, dtype=np.float64).tofile(f)
endline(len(i)*8)
def write_data(npts, t, a, val):
newline(npts*4 + 13)
np.array(npts, dtype=np.int32).tofile(f)
np.array(t, dtype=np.float64).tofile(f)
np.array(bytearray(a.encode()), dtype=np.byte).tofile(f)
np.array(val, dtype=np.float32).tofile(f)
endline(npts*4 + 13)
ts_list = sorted(results.get("timestamps"))
profiles = []
for r in results.river.reachs:
profiles += r.profiles
# Meta data (1st line)
nb_reach = len(results.river)
nb_profile = len(profiles)
write_int([nb_reach, nb_profile, "82"])
# Reach information (2nd line)
is1 = []
is2 = []
ltmp = []
i = 1
for r in results.river.reachs:
j = i+len(r)-1
ltmp += [i, j]
is1.append(i)
is2.append(j)
i += i+len(r)
write_int(ltmp)
# X (3rd line)
rk = []
for r in results.river.reachs:
rk += r.geometry.get_rk()
write_float(rk)
# Z and Y (4th line)
# ltmp = []
# for r in results.river.reachs:
# for p in r.prifiles:
# ltpm.append(p.rk)
write_float(3*nb_profile*[0.0])
# Data
for timestamp in ts_list:
q = list(
map(
lambda p: p.get_ts_key(timestamp, "Q"),
profiles
)
)
write_data(nb_profile, timestamp, "Q", q)
z = list(
map(
lambda p: p.get_ts_key(timestamp, "Z"),
profiles
)
)
write_data(nb_profile, timestamp, "Z", z)
logger.info(f"write_bin: ... end with {len(ts_list)} timestamps")
@timer
def results(self, study, repertory, qlog=None, name=None):
def results(self, study, repertory, qlog=None, name="0"):
results = Results(
study=study,
solver=self,
repertory=repertory,
name=name,
)
if name is None:
name = study.name.replace(" ", "_")
results_file = f"profil.{name}"
fname = os.path.join(repertory, results_file)
+1 -1
View File
@@ -55,7 +55,7 @@ class AboutWindow(PamhyrDialog):
label = self.get_label_text("label_version")
label = label.replace("@version", version)
label = label.replace("@codename", "")
label = label.replace("@codename", "(Adis-TS)")
self.set_label_text("label_version", label)
# Authors
+1 -1
View File
@@ -54,7 +54,7 @@ class Plot(PamhyrPlot):
self._isometric_axis = False
self._auto_relim_update = True
self._autoscale_update = False
self._autoscale_update = True
def custom_ticks(self):
if self.data.header[0] != "time":
+1 -8
View File
@@ -58,11 +58,7 @@ logger = logging.getLogger()
class TableModel(PamhyrTableModel):
def get_true_data_row(self, row):
if len(self._data.data) > 0:
bc = self._data.data[row]
else:
return 0
bc = self._data.data[row]
return next(
map(
@@ -107,9 +103,6 @@ class TableModel(PamhyrTableModel):
if not index.isValid() or role != Qt.EditRole:
return False
if self.is_same_data(index, value):
return False
row = index.row()
column = index.column()
-3
View File
@@ -167,9 +167,6 @@ class TableModel(PamhyrTableModel):
if not index.isValid() or role != Qt.EditRole:
return False
if self.is_same_data(index, value):
return False
row = index.row()
column = index.column()
@@ -55,7 +55,7 @@ class Plot(PamhyrPlot):
self._isometric_axis = False
self._auto_relim_update = True
self._autoscale_update = False
self._autoscale_update = True
def custom_ticks(self):
if self.data.header[0] != "time":
@@ -50,22 +50,6 @@ logger = logging.getLogger()
class TableModel(PamhyrTableModel):
def get_true_data_row(self, row):
if len(self._data.data) > 0:
bc = self._data.data[row]
else:
return 0
return next(
map(
lambda e: e[0],
filter(
lambda e: e[1] == bc,
enumerate(self._data._data)
)
), 0
)
def data(self, index, role):
if role == Qt.TextAlignmentRole:
return Qt.AlignHCenter | Qt.AlignVCenter
@@ -79,7 +63,6 @@ class TableModel(PamhyrTableModel):
value = QVariant()
if 0 <= column < 2:
row = self.get_true_data_row(row)
v = self._data._data[row][column]
if self._data._types[column] == float:
value = f"{v:.4f}"
@@ -97,15 +80,10 @@ class TableModel(PamhyrTableModel):
if not index.isValid() or role != Qt.EditRole:
return False
if self.is_same_data(index, value):
return False
row = index.row()
column = index.column()
try:
row = self.get_true_data_row(row)
self._undo.push(
SetDataCommand(
self._data, row, column, value
@@ -132,20 +110,14 @@ class TableModel(PamhyrTableModel):
def delete(self, rows, parent=QModelIndex()):
self.beginRemoveRows(parent, rows[0], rows[-1])
_rows = list(
map(
lambda r: self.get_true_data_row(r),
rows
)
)
self._undo.push(
DelCommand(
self._data, _rows
self._data, rows
)
)
self.endRemoveRows()
self.layoutChanged.emit()
def sort(self, _reverse, parent=QModelIndex()):
self.layoutAboutToBeChanged.emit()
@@ -61,7 +61,9 @@ class AddCommand(QUndoCommand):
def redo(self):
if self._new is None:
self._new = self._data.add(self._index)
self._new = self._data.insert(self._index, (
self._data._types[0](0), self._data._types[1](0.0)
))
else:
self._data.insert(self._index, self._new)
@@ -80,11 +82,10 @@ class DelCommand(QUndoCommand):
def undo(self):
for row, el in self._bc:
el.set_as_not_deleted()
self._data.insert(row, el)
def redo(self):
for row, el in self._bc:
el.set_as_deleted()
self._data.delete_i(self._rows)
class PasteCommand(QUndoCommand):
@@ -63,16 +63,6 @@ class EditBoundaryConditionWindow(PamhyrWindow):
name = trad[self._pamhyr_name]
if self._data is not None:
n = self._data.node
node_name = next(filter(
lambda x: x.id == n, study.river._nodes
)).name
name += (
f" - {study.name} " +
f"({node_name})"
)
super(EditBoundaryConditionWindow, self).__init__(
title=name,
study=study,
@@ -81,6 +71,16 @@ class EditBoundaryConditionWindow(PamhyrWindow):
parent=parent
)
if self._data is not None:
n = self._data.node
node_name = next(filter(
lambda x: x.id == n, self._study.river._nodes
)).name
name += (
f" - {study.name} " +
f"({node_name})"
)
self._hash_data.append(data)
self.setup_table()
@@ -89,12 +89,6 @@ class EditBoundaryConditionWindow(PamhyrWindow):
def setup_table(self):
table_headers = self._trad.get_dict("table_headers")
if self._study.is_editable():
editable_headers = self._trad.get_dict("table_headers")
else:
editable_headers = []
if self._data.type == "Concentration":
self._data.header = ["time", "concentration"]
else:
@@ -114,7 +108,7 @@ class EditBoundaryConditionWindow(PamhyrWindow):
self._table = TableModel(
table_view=table,
table_headers=headers,
editable_headers=editable_headers,
editable_headers=self._data.header,
delegates={
# "time": self._delegate_time,
},
@@ -147,10 +141,9 @@ class EditBoundaryConditionWindow(PamhyrWindow):
self.plot.draw()
def setup_connections(self):
if self._study.is_editable():
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_add").triggered.connect(self.add)
self.find(QAction, "action_del").triggered.connect(self.delete)
self.find(QAction, "action_sort").triggered.connect(self.sort)
self._table.dataChanged.connect(self.update)
def update(self):
+43 -55
View File
@@ -43,7 +43,7 @@ from View.Tools.PamhyrTable import PamhyrTableModel
from View.BoundaryConditionsAdisTS.UndoCommand import (
SetNodeCommand, SetTypeCommand,
AddCommand, DelCommand
AddCommand, DelCommand, SetPolCommand
)
from View.BoundaryCondition.translate import BC_types
@@ -112,37 +112,18 @@ class ComboBoxDelegate(QItemDelegate):
class TableModel(PamhyrTableModel):
def __init__(self, bc_list=None, pollutant_bc_list=None,
trad=None, **kwargs):
def __init__(self, pollutant=None, bc_list=None, trad=None, **kwargs):
self._trad = trad
self._bc_list = bc_list
self._pollutant = pollutant_bc_list.id
self._pollutant = pollutant
super(TableModel, self).__init__(trad=trad, **kwargs)
def _setup_lst(self):
self._lst = list(
filter(
lambda bc: bc.pollutant == self._pollutant,
self._bc_list.lst
)
)
def get(self, row):
if row < 0 or row >= len(self._lst):
return None
return self._lst[row]
def _global_row(self, row):
bc = self.get(row)
if bc is None:
return None
return self._bc_list.index(bc)
def rowCount(self, parent=QModelIndex()):
return len(self._lst)
def rowCount(self, parent):
return len(self._bc_list)
def data(self, index, role):
if role != Qt.ItemDataRole.DisplayRole:
return QVariant()
@@ -150,12 +131,12 @@ class TableModel(PamhyrTableModel):
column = index.column()
if self._headers[column] == "type":
n = self._lst[row].type
n = self._bc_list.get(row).type
if n is None or n == "":
return self._trad["not_associated"]
return n
elif self._headers[column] == "node":
n = self._lst[row].node
n = self._bc_list.get(row).node
if n is None:
return self._trad["not_associated"]
tmp = next(filter(lambda x: x.id == n, self._data._nodes), None)
@@ -163,6 +144,18 @@ class TableModel(PamhyrTableModel):
return tmp.name
else:
return self._trad["not_associated"]
elif self._headers[column] == "pol":
n = self._bc_list.get(row).pollutant
if n is None or n == "not_associated" or n == "":
return self._trad["not_associated"]
tmp = next(filter(lambda x: x.id == n,
self._data._Pollutants.Pollutants_List
),
None)
if tmp is not None:
return tmp.name
else:
return self._trad["not_associated"]
return QVariant()
@@ -170,32 +163,38 @@ class TableModel(PamhyrTableModel):
if not index.isValid() or role != Qt.EditRole:
return False
if self.is_same_data(index, value):
return False
row = index.row()
column = index.column()
try:
if self._headers[column] == "type":
global_row = self._global_row(row)
self._undo.push(
SetTypeCommand(
self._bc_list, global_row, value
self._bc_list, row, value
)
)
elif self._headers[column] == "node":
global_row = self._global_row(row)
node = next(
filter(lambda n: n.name == value, self._data.nodes()),
None
)
self._undo.push(
SetNodeCommand(
self._bc_list, global_row, node
self._bc_list, row, self._data.node(value)
)
)
elif self._headers[column] == "pol":
if value == self._trad["not_associated"]:
self._undo.push(
SetPolCommand(
self._bc_list, row, None
)
)
else:
pol = next(filter(lambda x: x.name == value,
self._data._Pollutants.Pollutants_List)
)
self._undo.push(
SetPolCommand(
self._bc_list, row, pol.id
)
)
except Exception as e:
logger.info(e)
logger.debug(traceback.format_exc())
@@ -204,44 +203,33 @@ class TableModel(PamhyrTableModel):
return True
def add(self, row, parent=QModelIndex()):
row = len(self._lst)
self.beginInsertRows(parent, row, row)
self.beginInsertRows(parent, row, row - 1)
self._undo.push(
AddCommand(
self._pollutant, self._bc_list, len(self._bc_list)
self._pollutant, self._bc_list, row
)
)
self._setup_lst()
self.endInsertRows()
self.layoutChanged.emit()
def delete(self, rows, parent=QModelIndex()):
self.beginRemoveRows(parent, rows[0], rows[-1])
row_by_bc = {
id(bc): row for row, bc in enumerate(self._bc_list._lst)
}
global_rows = [
row_by_bc[id(self._lst[row])]
for row in rows
if 0 <= row < len(self._lst)
]
self._undo.push(
DelCommand(
self._bc_list, global_rows
self._bc_list, rows
)
)
self._setup_lst()
self.endRemoveRows()
self.layoutChanged.emit()
def undo(self):
self._undo.undo()
self._setup_lst()
self.layoutChanged.emit()
def redo(self):
self._undo.redo()
self._setup_lst()
self.layoutChanged.emit()
@@ -36,7 +36,7 @@ class SetNodeCommand(QUndoCommand):
self._bcs = bcs
self._index = index
self._old = self._bcs.get(self._index).node
self._new = node.id if node is not None else None
self._new = node.id
def undo(self):
self._bcs.get(self._index).node = self._old
@@ -62,6 +62,22 @@ class SetTypeCommand(QUndoCommand):
self._bcs.get(self._index).type = self._new
class SetPolCommand(QUndoCommand):
def __init__(self, bcs, index, pollutant):
QUndoCommand.__init__(self)
self._bcs = bcs
self._index = index
self._old = self._bcs.get(self._index).pollutant
self._new = pollutant
def undo(self):
self._bcs.get(self._index).pollutant = self._old
def redo(self):
self._bcs.get(self._index).pollutant = self._new
class AddCommand(QUndoCommand):
def __init__(self, pollutant, bcs, index):
QUndoCommand.__init__(self)
@@ -90,7 +106,7 @@ class DelCommand(QUndoCommand):
self._bc = []
for row in rows:
self._bc.append((row, self._bcs._lst[row]))
self._bc.append((row, self._bcs.get(row)))
self._bc.sort()
def undo(self):
+17 -26
View File
@@ -58,22 +58,11 @@ class BoundaryConditionAdisTSWindow(PamhyrWindow):
_pamhyr_ui = "BoundaryConditionsAdisTS"
_pamhyr_name = "Boundary conditions AdisTS"
def __init__(self, data=None, pollutant_id=None, study=None,
config=None, parent=None):
self._data = data
self._pollutant_id = pollutant_id
def __init__(self, study=None, config=None, parent=None):
trad = BCAdisTSTranslate()
self._pollutant_name = next(
(x.name for x in study._river._Pollutants.Pollutants_List
if x.id == self._pollutant_id),
None
)
name = (
trad[self._pamhyr_name] +
" - " + study.name +
" - " + self._pollutant_name
" - " + study.name
)
super(BoundaryConditionAdisTSWindow, self).__init__(
@@ -84,12 +73,15 @@ class BoundaryConditionAdisTSWindow(PamhyrWindow):
parent=parent
)
self._pollutants_lst = self._study._river._Pollutants
self._bcs = self._study.river.boundary_conditions_adists
self.setup_graph()
self.setup_table()
self.setup_connections()
self.ui.setWindowTitle(self._title)
def setup_table(self):
self._delegate_type = ComboBoxDelegate(
trad=self._trad,
@@ -103,24 +95,25 @@ class BoundaryConditionAdisTSWindow(PamhyrWindow):
mode="node",
parent=self
)
if self._study.is_editable():
editable_headers = self._trad.get_dict("table_headers")
else:
editable_headers = []
self._delegate_pol = ComboBoxDelegate(
trad=self._trad,
data=self._study.river,
mode="pol",
parent=self
)
table = self.find(QTableView, f"tableView")
self._table = TableModel(
table_view=table,
table_headers=self._trad.get_dict("table_headers"),
editable_headers=editable_headers,
editable_headers=["type", "node", "pol"],
delegates={
"type": self._delegate_type,
"node": self._delegate_node,
"pol": self._delegate_pol,
},
trad=self._trad,
bc_list=self._study.river.boundary_conditions_adists,
pollutant_bc_list=self._data,
undo=self._undo_stack,
data=self._study.river
)
@@ -140,10 +133,8 @@ class BoundaryConditionAdisTSWindow(PamhyrWindow):
self.graph_layout.addWidget(self.graph_widget)
def setup_connections(self):
if self._study.is_editable():
self.find(QAction, "action_add").triggered.connect(self.add)
self.find(QAction, "action_del").triggered.connect(self.delete)
self.find(QAction, "action_add").triggered.connect(self.add)
self.find(QAction, "action_del").triggered.connect(self.delete)
self.find(QAction, "action_edit").triggered.connect(self.edit)
def index_selected_row(self):
@@ -165,7 +156,7 @@ class BoundaryConditionAdisTSWindow(PamhyrWindow):
)
def add(self):
self._table.add(self._table.rowCount())
self._table.add(len(self._bcs))
def delete(self):
rows = self.index_selected_rows()
@@ -189,7 +180,7 @@ class BoundaryConditionAdisTSWindow(PamhyrWindow):
def edit(self):
rows = self.index_selected_rows()
for row in rows:
data = self._table.get(row)
data = self._bcs.get(row)
if data.node is None:
continue
@@ -34,4 +34,5 @@ class BCAdisTSTranslate(MainTranslate):
self._sub_dict["table_headers"] = {
"type": self._dict["type"],
"node": _translate("BoundaryCondition", "Node"),
"pol": _translate("BoundaryCondition", "Pollutant")
}
+8 -31
View File
@@ -46,28 +46,25 @@ _translate = QCoreApplication.translate
class ComboBoxDelegate(QItemDelegate):
def __init__(self, data=None, d90_spec_lst=None,
def __init__(self, data=None, ic_spec_lst=None,
trad=None, parent=None, mode="reaches"):
super(ComboBoxDelegate, self).__init__(parent)
self._data = data
self._mode = mode
self._trad = trad
self._d90_spec_lst = d90_spec_lst
self._ic_spec_lst = ic_spec_lst
def createEditor(self, parent, option, index):
self.editor = QComboBox(parent)
val = []
if self._mode == "rk":
reach_id = self._d90_spec_lst[index.row()].reach
reach_id = self._ic_spec_lst[index.row()].reach
reach = next(
filter(
lambda edge: edge.id == reach_id, self._data.edges()
),
None
)
reach = next(filter(
lambda edge: edge.id == reach_id, self._data.edges()
))
if reach_id is not None:
val = list(
@@ -119,16 +116,9 @@ class D90TableModel(PamhyrTableModel):
super(D90TableModel, self).__init__(data=data, **kwargs)
self._data = data
if self._undo is not None:
self._undo.indexChanged.connect(lambda _: self.update())
def _setup_lst(self):
self._lst = list(
filter(
lambda d90: d90._deleted is False,
self._data._data
)
)
self._lst = self._data._data
def rowCount(self, parent):
return len(self._lst)
@@ -174,9 +164,6 @@ class D90TableModel(PamhyrTableModel):
if not index.isValid() or role != Qt.EditRole:
return False
if self.is_same_data(index, value):
return False
row = index.row()
column = index.column()
@@ -210,35 +197,25 @@ class D90TableModel(PamhyrTableModel):
)
)
self._setup_lst()
self.endInsertRows()
self.layoutChanged.emit()
def delete(self, rows, parent=QModelIndex()):
self.beginRemoveRows(parent, rows[0], rows[-1])
data_rows = {
id(d90): i for i, d90 in enumerate(self._data._data)
}
self._undo.push(
DelCommand(
self._data,
self._data._data,
[data_rows[id(self._lst[row])] for row in rows
if 0 <= row < len(self._lst)]
self._data, self._lst, rows
)
)
self._setup_lst()
self.endRemoveRows()
self.layoutChanged.emit()
def undo(self):
self._undo.undo()
self._setup_lst()
self.layoutChanged.emit()
def redo(self):
self._undo.redo()
self._setup_lst()
self.layoutChanged.emit()
+2 -2
View File
@@ -55,9 +55,9 @@ class D90TableDefaultModel(PamhyrTableModel):
row = index.row()
column = index.column()
if self._headers[column] == "name":
if self._headers[column] is "name":
return self._data[row].name
elif self._headers[column] == "d90":
elif self._headers[column] is "d90":
n = self._data[row].d90
if n is None:
return self._trad['not_associated']
+9 -8
View File
@@ -112,11 +112,11 @@ class SetCommandSpec(QUndoCommand):
class AddCommand(QUndoCommand):
def __init__(self, data, d90_spec, index):
def __init__(self, data, ics_spec, index):
QUndoCommand.__init__(self)
self._data = data
self._d90_spec = d90_spec
self._ics_spec = ics_spec
self._index = index
self._new = None
@@ -131,20 +131,21 @@ class AddCommand(QUndoCommand):
class DelCommand(QUndoCommand):
def __init__(self, data, d90_spec, rows):
def __init__(self, data, ics_spec, rows):
QUndoCommand.__init__(self)
self._data = data
self._d90_spec = d90_spec
self._ics_spec = ics_spec
self._rows = rows
# self._data = data
self._d90 = []
self._ic = []
for row in rows:
self._d90.append((row, self._d90_spec[row]))
self._d90.sort()
self._ic.append((row, self._ics_spec[row]))
self._ic.sort()
def undo(self):
for row, el in self._d90:
for row, el in self._ic:
self._data.insert(row, el)
def redo(self):
+15 -44
View File
@@ -43,7 +43,7 @@ from PyQt5.QtWidgets import (
from Modules import Modules
from View.D90AdisTS.UndoCommand import (
from View.InitialConditionsAdisTS.UndoCommand import (
SetCommand,
)
@@ -93,21 +93,18 @@ class D90AdisTSWindow(PamhyrWindow):
self.setup_table()
self.ui.setWindowTitle(self._title)
def setup_table(self):
path_icons = os.path.join(self._get_ui_directory(), f"ressources")
if self._study.is_editable():
editable_headers = self._trad.get_dict("table_headers")
else:
editable_headers = []
table_default = self.find(QTableView, f"tableView")
self._table = D90TableDefaultModel(
table_view=table_default,
table_headers=self._trad.get_dict("table_headers"),
editable_headers=editable_headers,
editable_headers=["name", "d90"],
delegates={},
data=self._data,
undo=self._undo_stack,
@@ -127,15 +124,10 @@ class D90AdisTSWindow(PamhyrWindow):
action_add = QAction(self)
action_add.setIcon(QIcon(os.path.join(path_icons, f"add.png")))
if self._study.is_editable():
action_add.triggered.connect(self.add)
action_add.triggered.connect(self.add)
action_delete = QAction(self)
action_delete.setIcon(QIcon(os.path.join(path_icons, f"del.png")))
if self._study.is_editable():
action_delete.triggered.connect(self.delete)
action_delete.triggered.connect(self.delete)
toolBar.addAction(action_add)
toolBar.addAction(action_delete)
@@ -146,27 +138,22 @@ class D90AdisTSWindow(PamhyrWindow):
self._delegate_reach = ComboBoxDelegate(
trad=self._trad,
data=self._study.river,
d90_spec_lst=self._data[0]._data,
ic_spec_lst=self._data[0]._data,
parent=self,
mode="reaches"
)
self._delegate_rk = ComboBoxDelegate(
trad=self._trad,
data=self._study.river,
d90_spec_lst=self._data[0]._data,
ic_spec_lst=self._data[0]._data,
parent=self,
mode="rk"
)
if self._study.is_editable():
editable_headers_spec = self._trad.get_dict("table_headers_spec")
else:
editable_headers_spec = []
self._table_spec = D90TableModel(
table_view=self.table_spec,
table_headers=self._trad.get_dict("table_headers_spec"),
editable_headers=editable_headers_spec,
editable_headers=["name", "reach", "start_rk", "end_rk", "d90"],
delegates={
"reach": self._delegate_reach,
"start_rk": self._delegate_rk,
@@ -240,15 +227,15 @@ class D90AdisTSWindow(PamhyrWindow):
table = list(
map(
lambda ed90: list(
lambda eic: list(
map(
lambda k: ed90[1][k],
lambda k: eic[1][k],
["rk", "discharge", "elevation"]
)
),
filter(
lambda ed90: ed90[0] in rows,
enumerate(self._d90.lst())
lambda eic: eic[0] in rows,
enumerate(self._ics.lst())
)
)
)
@@ -277,27 +264,11 @@ class D90AdisTSWindow(PamhyrWindow):
self._update()
def _undo(self):
undo_stack = self._undo_stack
if undo_stack is None or not undo_stack.canUndo():
return
if isinstance(undo_stack.command(undo_stack.index() - 1), SetCommand):
self._table.undo()
else:
self._table_spec.undo()
self._table.undo()
self._update()
def _redo(self):
undo_stack = self._undo_stack
if undo_stack is None or not undo_stack.canRedo():
return
if isinstance(undo_stack.command(undo_stack.index()), SetCommand):
self._table.redo()
else:
self._table_spec.redo()
self._table.redo()
self._update()
def add(self):
+10 -35
View File
@@ -46,28 +46,25 @@ _translate = QCoreApplication.translate
class ComboBoxDelegate(QItemDelegate):
def __init__(self, data=None, dif_spec_lst=None,
def __init__(self, data=None, ic_spec_lst=None,
trad=None, parent=None, mode="reaches"):
super(ComboBoxDelegate, self).__init__(parent)
self._data = data
self._mode = mode
self._trad = trad
self._dif_spec_lst = dif_spec_lst
self._ic_spec_lst = ic_spec_lst
def createEditor(self, parent, option, index):
self.editor = QComboBox(parent)
val = []
if self._mode == "rk":
reach_id = self._dif_spec_lst[index.row()].reach
reach_id = self._ic_spec_lst[index.row()].reach
reach = next(
filter(
lambda edge: edge.id == reach_id, self._data.edges()
),
None
)
reach = next(filter(
lambda edge: edge.id == reach_id, self._data.edges()
))
if reach_id is not None:
val = list(
@@ -121,16 +118,9 @@ class DIFTableModel(PamhyrTableModel):
super(DIFTableModel, self).__init__(data=data, **kwargs)
self._data = data
if self._undo is not None:
self._undo.indexChanged.connect(lambda _: self.update())
def _setup_lst(self):
self._lst = list(
filter(
lambda dif: dif._deleted is False,
self._data._data
)
)
self._lst = self._data._data
def rowCount(self, parent):
return len(self._lst)
@@ -186,9 +176,6 @@ class DIFTableModel(PamhyrTableModel):
if not index.isValid() or role != Qt.EditRole:
return False
if self.is_same_data(index, value):
return False
row = index.row()
column = index.column()
@@ -196,10 +183,7 @@ class DIFTableModel(PamhyrTableModel):
if self._headers[column] != "reach":
self._undo.push(
SetCommandSpec(
self._lst,
row,
self._headers[column],
value
self._lst, row, self._headers[column], value
)
)
elif self._headers[column] == "reach":
@@ -227,34 +211,25 @@ class DIFTableModel(PamhyrTableModel):
)
)
self._setup_lst()
self.endInsertRows()
self.layoutChanged.emit()
def delete(self, rows, parent=QModelIndex()):
self.beginRemoveRows(parent, rows[0], rows[-1])
data_rows = {
id(dif): i for i, dif in enumerate(self._data._data)
}
self._undo.push(
DelCommand(
self._data,
self._data._data,
[data_rows[id(self._lst[row])] for row in rows
if 0 <= row < len(self._lst)]
self._data, self._lst, rows
)
)
self._setup_lst()
self.endRemoveRows()
self.layoutChanged.emit()
def undo(self):
self._undo.undo()
self._setup_lst()
self.layoutChanged.emit()
def redo(self):
self._undo.redo()
self._setup_lst()
self.layoutChanged.emit()
+9 -8
View File
@@ -136,11 +136,11 @@ class SetCommandSpec(QUndoCommand):
class AddCommand(QUndoCommand):
def __init__(self, data, dif_spec, index):
def __init__(self, data, ics_spec, index):
QUndoCommand.__init__(self)
self._data = data
self._dif_spec = dif_spec
self._ics_spec = ics_spec
self._index = index
self._new = None
@@ -155,20 +155,21 @@ class AddCommand(QUndoCommand):
class DelCommand(QUndoCommand):
def __init__(self, data, dif_spec, rows):
def __init__(self, data, ics_spec, rows):
QUndoCommand.__init__(self)
self._data = data
self._dif_spec = dif_spec
self._ics_spec = ics_spec
self._rows = rows
# self._data = data
self._dif = []
self._ic = []
for row in rows:
self._dif.append((row, self._dif_spec[row]))
self._dif.sort()
self._ic.append((row, self._ics_spec[row]))
self._ic.sort()
def undo(self):
for row, el in self._dif:
for row, el in self._ic:
self._data.insert(row, el)
def redo(self):
+12 -39
View File
@@ -51,8 +51,6 @@ from View.DIFAdisTS.Table import (
DIFTableModel, ComboBoxDelegate,
)
from View.DIFAdisTS.UndoCommand import SetCommand
from View.DIFAdisTS.translate import DIFAdisTSTranslate
from Solver.Mage import Mage8
@@ -91,21 +89,18 @@ class DIFAdisTSWindow(PamhyrWindow):
self.setup_table()
self.ui.setWindowTitle(self._title)
def setup_table(self):
path_icons = os.path.join(self._get_ui_directory(), f"ressources")
table_default = self.find(QTableView, f"tableView")
if self._study.is_editable():
editable_headers = self._trad.get_dict("table_headers")
else:
editable_headers = []
self._delegate_method = ComboBoxDelegate(
trad=self._trad,
data=self._study.river,
dif_spec_lst=self._data[0]._data,
ic_spec_lst=self._data[0]._data,
parent=self,
mode="method"
)
@@ -113,7 +108,7 @@ class DIFAdisTSWindow(PamhyrWindow):
self._table = DIFTableDefaultModel(
table_view=table_default,
table_headers=self._trad.get_dict("table_headers"),
editable_headers=editable_headers,
editable_headers=["method", "dif", "b", "c"],
delegates={
"method": self._delegate_method
},
@@ -135,14 +130,10 @@ class DIFAdisTSWindow(PamhyrWindow):
action_add = QAction(self)
action_add.setIcon(QIcon(os.path.join(path_icons, f"add.png")))
if self._study.is_editable():
action_add.triggered.connect(self.add)
action_add.triggered.connect(self.add)
action_delete = QAction(self)
action_delete.setIcon(QIcon(os.path.join(path_icons, f"del.png")))
if self._study.is_editable():
action_delete.triggered.connect(self.delete)
action_delete.triggered.connect(self.delete)
toolBar.addAction(action_add)
toolBar.addAction(action_delete)
@@ -153,27 +144,23 @@ class DIFAdisTSWindow(PamhyrWindow):
self._delegate_reach = ComboBoxDelegate(
trad=self._trad,
data=self._study.river,
dif_spec_lst=self._data[0]._data,
ic_spec_lst=self._data[0]._data,
parent=self,
mode="reaches"
)
self._delegate_rk = ComboBoxDelegate(
trad=self._trad,
data=self._study.river,
dif_spec_lst=self._data[0]._data,
ic_spec_lst=self._data[0]._data,
parent=self,
mode="rk"
)
if self._study.is_editable():
editable_headers_spec = self._trad.get_dict("table_headers_spec")
else:
editable_headers_spec = []
self._table_spec = DIFTableModel(
table_view=self.table_spec,
table_headers=self._trad.get_dict("table_headers_spec"),
editable_headers=editable_headers_spec,
editable_headers=["method", "reach", "start_rk",
"end_rk", "dif", "b", "c"],
delegates={
"method": self._delegate_method,
"reach": self._delegate_reach,
@@ -285,25 +272,11 @@ class DIFAdisTSWindow(PamhyrWindow):
self._update()
def _undo(self):
undo_stack = self._undo_stack
if undo_stack is None or not undo_stack.canUndo():
return
if isinstance(undo_stack.command(undo_stack.index() - 1), SetCommand):
self._table.undo()
else:
self._table_spec.undo()
self._table.undo()
self._update()
def _redo(self):
undo_stack = self._undo_stack
if undo_stack is None or not undo_stack.canRedo():
return
if isinstance(undo_stack.command(undo_stack.index()), SetCommand):
self._table.redo()
else:
self._table_spec.redo()
self._table.redo()
self._update()
def add(self):
+1 -3
View File
@@ -17,7 +17,6 @@
# -*- coding: utf-8 -*-
import logging
import traceback
from tools import trace, timer
@@ -93,8 +92,7 @@ class ReplWindow(PamhyrWindow):
value = exec(rich_code)
value = self.__debug_exec_result__
except Exception as e:
value = f"<font color=\"red\">" + str(e) + "</font>\n"
value += f"<font color=\"grey\">{traceback.format_exc()}</font>"
value = f"<font color=\"red\">" + str(e) + "</font>"
# Display code
msg = f"<font color=\"grey\"> # " + code + " #</font>"
+1 -1
View File
@@ -43,7 +43,7 @@ class PlotRKZ(PlotRKC):
self._isometric_axis = False
self._auto_relim_update = True
self._autoscale_update = False
self._autoscale_update = True
self.parent = parent
def onpick(self, event):
+1 -1
View File
@@ -50,7 +50,7 @@ class PlotStricklers(PamhyrPlot):
self._auto_relim = False
self._auto_relim_update = False
self._autoscale_update = False
self._autoscale_update = True
@timer
def draw(self, highlight=None):
+6 -8
View File
@@ -134,9 +134,6 @@ class FrictionTableModel(PamhyrTableModel):
if not index.isValid() or role != Qt.EditRole:
return False
if self.is_same_data(index, value):
return False
row = index.row()
column = index.column()
@@ -277,14 +274,15 @@ class FrictionTableModel(PamhyrTableModel):
with open(file_name, encoding="utf-8") as rug_file:
for line in rug_file:
if line.upper().startswith("K"):
if int(line[1:4]) == reach_id:
data.append(line[4:].split())
line = line.split()
if int(line[1]) == reach_id:
data.append(line[1:])
new_data = []
for d in data:
new = None
minor = float(d[2])
medium = float(d[3])
minor = float(d[3])
medium = float(d[4])
for s in self._study.river.stricklers.stricklers:
if s.minor == minor and s.medium == medium:
new = s
@@ -294,5 +292,5 @@ class FrictionTableModel(PamhyrTableModel):
self._study.river.stricklers))
new.minor = minor
new.medium = medium
new_data.append([self._data, float(d[0]), float(d[1]), new, new])
new_data.append([self._data, float(d[1]), float(d[2]), new, new])
self.replace_data(new_data)
-296
View File
@@ -1,296 +0,0 @@
# Window.py -- Pamhyr
# Copyright (C) 2024-2025 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 Modules import Modules
from View.Tools.PamhyrWindow import PamhyrWindow
from PyQt5.QtWidgets import (
QLabel, QPlainTextEdit, QPushButton, QCheckBox,
QFileDialog, QVBoxLayout, QDoubleSpinBox,
)
from PyQt5.QtCore import (
QSettings
)
from View.GeoTIFF.Translate import GeoTIFFTranslate
from View.GeoTIFF.UndoCommand import (
SetCommand
)
from View.Tools.Plot.PamhyrToolbar import PamhyrPlotToolbar
from View.Tools.Plot.PamhyrCanvas import MplCanvas
from View.PlotXY import PlotXY
try:
import rasterio
import rasterio.control
import rasterio.crs
import rasterio.sample
import rasterio.vrt
import rasterio._features
from rasterio.io import MemoryFile
_rasterio_loaded = True
except Exception as e:
print(f"Module 'rasterio' is not available: {e}")
_rasterio_loaded = False
logger = logging.getLogger()
class EditGeoTIFFWindow(PamhyrWindow):
_pamhyr_ui = "GeoTIFF"
_pamhyr_name = "Edit GeoTIFF"
def __init__(self, study=None, config=None, geotiff=None,
trad=None, undo=None, parent=None):
super(EditGeoTIFFWindow, self).__init__(
title=self._pamhyr_name,
study=study,
config=config,
trad=trad,
options=[],
parent=parent
)
self._geotiff = geotiff
self._file_name = geotiff.file_name
self._hash_data.append(self._geotiff)
self._undo = undo
self.setup_values()
self.setup_graph()
self.setup_connection()
def setup_graph(self):
self.canvas = MplCanvas(width=5, height=4, dpi=100)
self.canvas.setObjectName("canvas")
self.plot_layout = self.find(QVBoxLayout,
"verticalLayout_geotiff")
self._toolbar = PamhyrPlotToolbar(
self.canvas, self,
items=["home", "zoom", "save", "iso", "back/forward", "move"]
)
self.plot_layout.addWidget(self._toolbar)
self.plot_layout.addWidget(self.canvas)
self.plot = PlotXY(
canvas=self.canvas,
data=self._study.river.enable_edges(),
trad=self._trad,
toolbar=None,
parent=self
)
self.plot.update()
self._plot_img = None
memfile = self._geotiff.memfile
if memfile is not None:
self.draw_geotiff(memfile=memfile)
def setup_values(self):
self.set_check_box("checkBox", self._geotiff.enabled)
self.set_line_edit_text("lineEdit_name", self._geotiff.name)
self.set_line_edit_text("lineEdit_description",
self._geotiff.description)
bounds = list(self._geotiff.coordinates.values())
self._set_values_from_bounds(bounds)
self._set_default_values_from_bounds(bounds)
self._reset_spinboxes()
if self._study.is_read_only():
self.set_check_box_enable("checkBox", False)
self.set_line_edit_enable("lineEdit_name", False)
self.set_line_edit_enable("lineEdit_description", False)
for button in ["import", "bottom", "top", "left", "right"]:
self.find(QPushButton, f"pushButton_{button}")\
.setEnabled(False)
for spin in ["bottom", "top", "left", "right"]:
self.find(QDoubleSpinBox, f"doubleSpinBox_{spin}")\
.setEnabled(False)
def _set_values_from_bounds(self, bounds):
self._values = {
"bottom": bounds[0],
"top": bounds[1],
"left": bounds[2],
"right": bounds[3],
}
def _set_default_values_from_bounds(self, bounds):
self._values_default = {
"bottom": bounds[0],
"top": bounds[1],
"left": bounds[2],
"right": bounds[3],
}
def _reset_spinboxes(self):
for key in self._values:
self._reset_spinbox(key)
def _reset_spinbox(self, key):
self.set_double_spin_box(
f"doubleSpinBox_{key}", self._values_default[key]
)
def setup_connection(self):
self.find(QPushButton, "pushButton_cancel")\
.clicked.connect(self.close)
self.find(QPushButton, "pushButton_ok")\
.clicked.connect(self.accept)
self.find(QPushButton, "pushButton_import")\
.clicked.connect(self._import)
self.find(QPushButton, "pushButton_bottom")\
.clicked.connect(lambda: self._reset_spinbox("bottom"))
self.find(QPushButton, "pushButton_top")\
.clicked.connect(lambda: self._reset_spinbox("top"))
self.find(QPushButton, f"pushButton_left")\
.clicked.connect(lambda: self._reset_spinbox("left"))
self.find(QPushButton, f"pushButton_right")\
.clicked.connect(lambda: self._reset_spinbox("right"))
self.find(QDoubleSpinBox, f"doubleSpinBox_bottom")\
.valueChanged.connect(
lambda: self.update_values_from_spinbox("bottom")
)
self.find(QDoubleSpinBox, f"doubleSpinBox_top")\
.valueChanged.connect(
lambda: self.update_values_from_spinbox("top")
)
self.find(QDoubleSpinBox, f"doubleSpinBox_left")\
.valueChanged.connect(
lambda: self.update_values_from_spinbox("left")
)
self.find(QDoubleSpinBox, f"doubleSpinBox_right")\
.valueChanged.connect(
lambda: self.update_values_from_spinbox("right")
)
def update_values_from_spinbox(self, key):
self._values[key] = self.get_double_spin_box(f"doubleSpinBox_{key}")
left = self._values["left"]
right = self._values["right"]
bottom = self._values["bottom"]
top = self._values["top"]
self._plot_img.set_extent((left, right, bottom, top))
self.plot.idle()
def draw_geotiff(self, memfile=None):
if not _rasterio_loaded:
return
if memfile is None:
if self._file_name == "":
return
with rasterio.open(self._file_name) as data:
img = data.read()
b = data.bounds[:] # left, bottom, right, top
if b[2] > b[0] and b[1] < b[3]:
coord = [b[1], b[3], b[0], b[2]]
else:
xlim = self.canvas.axes.get_xlim()
ylim = self.canvas.axes.get_ylim()
coord = ylim + xlim
self._set_values_from_bounds(coord)
self._set_default_values_from_bounds(coord)
else:
with memfile.open() as gt:
img = gt.read()
if self._plot_img is not None:
self._plot_img.remove()
left = self._values["left"]
right = self._values["right"]
bottom = self._values["bottom"]
top = self._values["top"]
self._plot_img = self.canvas.axes.imshow(
img.transpose((1, 2, 0)),
extent=(left, right, bottom, top)
)
self.plot.idle()
self._reset_spinboxes()
def _import(self):
options = QFileDialog.Options()
settings = QSettings(QSettings.IniFormat,
QSettings.UserScope, 'MyOrg', )
options |= QFileDialog.DontUseNativeDialog
file_types = [
self._trad["file_geotiff"],
self._trad["file_all"],
]
filename, _ = QFileDialog.getOpenFileName(
self,
self._trad["open_file"],
"",
";; ".join(file_types),
options=options
)
if filename != "":
self._file_name = filename
self.draw_geotiff()
def accept(self):
if self._study.is_editable():
is_enabled = self.get_check_box("checkBox")
name = self.get_line_edit_text("lineEdit_name")
description = self.get_line_edit_text("lineEdit_description")
coord_bottom = self.get_double_spin_box("doubleSpinBox_bottom")
coord_top = self.get_double_spin_box("doubleSpinBox_top")
coord_left = self.get_double_spin_box("doubleSpinBox_left")
coord_right = self.get_double_spin_box("doubleSpinBox_right")
self._undo.push(
SetCommand(
self._geotiff, enabled=is_enabled,
name=name, description=description,
coordinates_bottom=coord_bottom,
coordinates_top=coord_top,
coordinates_left=coord_left,
coordinates_right=coord_right,
file_name=self._file_name,
)
)
self._propagate_update(key=Modules.GEOTIFF)
self.close()
-96
View File
@@ -1,96 +0,0 @@
# List.py -- Pamhyr
# Copyright (C) 2024-2025 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 functools import reduce
from tools import trace, timer
from PyQt5.QtCore import (
Qt, QVariant,
)
from PyQt5.QtGui import (
QColor, QBrush,
)
from View.Tools.PamhyrList import PamhyrListModel
from View.GeoTIFF.UndoCommand import (
AddCommand, DelCommand
)
logger = logging.getLogger()
class ListModel(PamhyrListModel):
def get_true_data_row(self, row):
el = self._data.get(row)
return next(
map(
lambda e: e[0],
filter(
lambda e: e[1] == el,
enumerate(self._data._lst)
)
), 0
)
def data(self, index, role):
row = index.row()
column = index.column()
file = self._data.files[row]
if role == Qt.ForegroundRole:
color = Qt.gray
if file.is_enabled():
color = QColor("black")
else:
color = QColor("grey")
return QBrush(color)
if role == Qt.ItemDataRole.DisplayRole:
text = f"{file.name}: '{file.description}'"
if not file.is_enabled():
text += " (disabled)"
return text
return QVariant()
def add(self, row):
row = self.get_true_data_row(row)
self._undo.push(
AddCommand(
self._data, row
)
)
self.update()
def delete(self, row):
self._undo.push(
DelCommand(
self._data, self._data.files[row]
)
)
self.update()
-35
View File
@@ -1,35 +0,0 @@
# Translate.py -- Pamhyr
# Copyright (C) 2024-2025 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.Translate import MainTranslate
_translate = QCoreApplication.translate
class GeoTIFFTranslate(MainTranslate):
def __init__(self):
super(GeoTIFFTranslate, self).__init__()
self._dict["GeoTIFF files"] = _translate(
"GeoTIFF", "GeoTIFF files"
)
self._dict["Edit additional file"] = _translate(
"GeoTIFF", "Edit GeoTIFF file"
)
-81
View File
@@ -1,81 +0,0 @@
# UndoCommand.py -- Pamhyr
# Copyright (C) 2024-2025 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 trace, timer
from PyQt5.QtWidgets import (
QMessageBox, QUndoCommand, QUndoStack,
)
class SetCommand(QUndoCommand):
def __init__(self, geotiff, **kwargs):
QUndoCommand.__init__(self)
self._geotiff = geotiff
self._new = kwargs
self._old = None
def undo(self):
f = self._geotiff
for key in self._old:
f[key] = self._old[key]
def redo(self):
f = self._geotiff
if self._old is None:
self._old = {}
for key in self._new:
self._old[key] = f[key]
for key in self._new:
f[key] = self._new[key]
class AddCommand(QUndoCommand):
def __init__(self, files, row):
QUndoCommand.__init__(self)
self._files = files
self._row = row
self._new = None
def undo(self):
self._new.set_as_deleted()
def redo(self):
if self._new is None:
self._new = self._files.new(self._row)
else:
self._new.set_as_not_deleted()
class DelCommand(QUndoCommand):
def __init__(self, files, line):
QUndoCommand.__init__(self)
self._files = files
self._line = line
def undo(self):
self._line.set_as_not_deleted()
def redo(self):
self._line.set_as_deleted()
-223
View File
@@ -1,223 +0,0 @@
# Window.py -- Pamhyr
# Copyright (C) 2024-2025 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 trace, timer, logger_exception
from PyQt5.QtWidgets import (
QAction, QListView, QVBoxLayout,
)
from matplotlib.patches import Rectangle
from Modules import Modules
from View.Tools.PamhyrWindow import PamhyrWindow
from View.GeoTIFF.List import ListModel
from View.GeoTIFF.Translate import GeoTIFFTranslate
from View.GeoTIFF.Edit.Window import EditGeoTIFFWindow
from View.Tools.Plot.PamhyrToolbar import PamhyrPlotToolbar
from View.Tools.Plot.PamhyrCanvas import MplCanvas
from View.PlotXY import PlotXY
try:
import rasterio
import rasterio.control
import rasterio.crs
import rasterio.sample
import rasterio.vrt
import rasterio._features
from rasterio.io import MemoryFile
_rasterio_loaded = True
except Exception as e:
print(f"Module 'rasterio' is not available: {e}")
_rasterio_loaded = False
class GeoTIFFListWindow(PamhyrWindow):
_pamhyr_ui = "GeoTIFFList"
_pamhyr_name = "GeoTIFF files"
def __init__(self, study=None, config=None,
parent=None):
trad = GeoTIFFTranslate()
name = trad[self._pamhyr_name] + " - " + study.name
super(GeoTIFFListWindow, self).__init__(
title=name,
study=study,
config=config,
trad=trad,
options=[],
parent=parent
)
self.setup_list()
self.setup_graph()
self.setup_connections()
def setup_list(self):
lst = self.find(QListView, "listView")
self._list = ListModel(
list_view=lst,
data=self._study.river.geotiff,
undo=self._undo_stack,
trad=self._trad,
)
def setup_graph(self):
self.canvas = MplCanvas(width=5, height=4, dpi=100)
self.canvas.setObjectName("canvas")
self._plot_layout = self.find(QVBoxLayout, "verticalLayout")
self._toolbar = PamhyrPlotToolbar(
self.canvas, self,
items=["home", "zoom", "save", "iso", "back/forward", "move"]
)
self._plot_layout.addWidget(self._toolbar)
self._plot_layout.addWidget(self.canvas)
self._plot = PlotXY(
canvas=self.canvas,
data=self._study.river.enable_edges(),
geotiff=self._study.river.geotiff,
trad=self._trad,
toolbar=None,
parent=self
)
self._plot_rect = []
self._plot.update()
def setup_connections(self):
if self._study.is_editable():
self.find(QAction, "action_add").triggered.connect(self.add)
self.find(QAction, "action_delete").triggered.connect(self.delete)
else:
self.find(QAction, "action_add").setEnabled(False)
self.find(QAction, "action_delete").setEnabled(False)
self.find(QAction, "action_edit").triggered.connect(self.edit)
self.find(QListView, "listView")\
.selectionModel()\
.selectionChanged\
.connect(self._update_rectangle)
def _propagated_update(self, key=Modules(0)):
if Modules.GEOMETRY not in key and Modules.GEOTIFF not in key:
return
self.update()
def update(self):
self._list.update()
self._plot.update()
self._update_rectangle()
def _update_rectangle(self):
for rect in self._plot_rect:
try:
rect.remove()
except Exception as e:
logger_exception(e)
self._plot_rect = []
rows = self.selected_rows()
if len(rows) <= 0:
return
for row in rows:
files = self._study.river.geotiff.files
if len(files) <= row:
continue
geotiff = files[row]
coord = geotiff.coordinates
xy = (coord["left"], coord["bottom"])
width = abs(coord["right"] - coord["left"])
height = abs(coord["top"] - coord["bottom"])
rect = Rectangle(
xy, width, height,
edgecolor='red', facecolor='none',
lw=2
)
self._plot_rect.append(rect)
self.canvas.axes.add_patch(
rect
)
self._plot.idle()
def selected_rows(self):
lst = self.find(QListView, f"listView")
return list(map(lambda i: i.row(), lst.selectedIndexes()))
def add(self):
rows = self.selected_rows()
if len(rows) > 0:
row = rows[0]
else:
row = 0
self._list.add(row)
def delete(self):
rows = self.selected_rows()
if len(rows) == 0:
return
self._list.delete(rows[0])
self.update()
def edit(self):
rows = self.selected_rows()
for row in rows:
geotiff = self._study.river.geotiff.files[row]
if self.sub_window_exists(
EditGeoTIFFWindow,
data=[self._study, self._config, geotiff]
):
continue
win = EditGeoTIFFWindow(
study=self._study,
config=self._config,
geotiff=geotiff,
trad=self._trad,
undo=self._undo_stack,
parent=self,
)
win.show()
def _undo(self):
self._undo_stack.undo()
self.update()
def _redo(self):
self._undo_stack.redo()
self.update()
+2 -2
View File
@@ -38,7 +38,7 @@ class PlotAC(PamhyrPlot):
self._isometric_axis = False
self._auto_relim_update = True
self._autoscale_update = False
self._autoscale_update = True
self.label_x = self._trad["transverse_abscissa"]
self.label_y = self._trad["unit_elevation"]
@@ -143,7 +143,7 @@ class PlotAC(PamhyrPlot):
]
else:
color = self.color_incomplete_gl[
lincomplete.index(txt) % len(self.color_incomplete_gl)
lincomplete.index(txt)
]
annotation = self.canvas.axes.annotate(
-3
View File
@@ -114,9 +114,6 @@ class GeometryProfileTableModel(PamhyrTableModel):
column = index.column()
if role == Qt.EditRole:
if self.is_same_data(index, value):
return False
try:
if column == 0:
self._undo.push(
-32
View File
@@ -17,10 +17,6 @@
# -*- coding: utf-8 -*-
from View.Tools.PamhyrWindow import PamhyrDialog
from PyQt5.QtWidgets import QDoubleSpinBox
from View.Tools.FlexibleDoubleSpinBox import FlexibleDoubleSpinBox
from PyQt5.QtWidgets import QDoubleSpinBox
from View.Tools.FlexibleDoubleSpinBox import FlexibleDoubleSpinBox
class ShiftDialog(PamhyrDialog):
@@ -35,36 +31,8 @@ class ShiftDialog(PamhyrDialog):
parent=parent
)
self._replace_spinboxes()
self._init_default_values()
def _replace_spinboxes(self):
for name in ["doubleSpinBox_X", "doubleSpinBox_Y", "doubleSpinBox_Z"]:
old = self.find(QDoubleSpinBox, name)
if old is None:
continue
new = FlexibleDoubleSpinBox(old.parent())
new.setObjectName(old.objectName())
# Copier propriétés utiles
new.setDecimals(old.decimals())
new.setMinimum(old.minimum())
new.setMaximum(old.maximum())
new.setSingleStep(old.singleStep())
new.setValue(old.value())
new.setGeometry(old.geometry())
# Remplacement dans layout si présent
layout = old.parent().layout()
if layout is not None:
layout.replaceWidget(old, new)
old.hide()
old.setParent(None)
old.deleteLater()
def _init_default_values(self):
self._dx = 0.0
self._dy = 0.0
+4 -15
View File
@@ -95,9 +95,6 @@ class GeometryReachTableModel(PamhyrTableModel):
column = index.column()
if role == Qt.EditRole and index.column() != 2:
if self.is_same_data(index, value):
return False
try:
if index.column() == 0:
self._undo.push(
@@ -254,22 +251,11 @@ class GeometryReachTableModel(PamhyrTableModel):
self.layoutChanged.emit()
def meshing(self, mesher, data, tableView):
new_profiles = mesher.meshing(
self._data,
**data
)
if new_profiles is None:
return
if len(new_profiles) == 0:
return
self.layoutAboutToBeChanged.emit()
self._undo.push(
MeshingCommand(
self._data, new_profiles, data, tableView
self._data, mesher, data, tableView
)
)
@@ -289,6 +275,7 @@ class GeometryReachTableModel(PamhyrTableModel):
self.layoutChanged.emit()
def purge(self, np_purge):
self._undo.push(
PurgeCommand(
self._data, np_purge
@@ -297,6 +284,7 @@ class GeometryReachTableModel(PamhyrTableModel):
self.layoutChanged.emit()
def shift(self, rows, dx, dy, dz):
self._undo.push(
ShiftCommand(
self._data, rows, dx, dy, dz
@@ -305,6 +293,7 @@ class GeometryReachTableModel(PamhyrTableModel):
self.layoutChanged.emit()
def change_reach(self, new_reach, parent):
self._undo.push(
ChangeReachCommand(
new_reach,
-18
View File
@@ -91,21 +91,3 @@ class GeometryTranslate(MainTranslate):
self._dict["Shift"] = _translate(
"Geometry", "Shift"
)
self._dict["warning"] = _translate(
"Geometry", "Warning"
)
self._dict["format_not_exportable"] = _translate(
"Geometry", "The format of the file is not exportable."
)
self._dict["update_rk_error"] = _translate(
"Geometry",
"RK update can't be executed."
)
self._dict["update_rk_empty_profiles"] = _translate(
"Geometry",
"Some profiles don't contain any point."
)
self._dict["update_rk_empty_profiles_info"] = _translate(
"Geometry",
"Profiles concerned:"
)
+19 -10
View File
@@ -247,13 +247,14 @@ class UpdateRKCommand(QUndoCommand):
self._new_end_rk = None
def undo(self):
self._reach.update_rks(self._rks)
for rk, profile in zip(self._rks, self._reach.profiles):
profile.rk = rk
for begin_rk, end_rk, friction in zip(self._begin_rk,
self._end_rk,
self._frictions):
friction.update_rks(begin_rk, end_rk)
friction.begin_rk = begin_rk
friction.end_rk = end_rk
def redo(self):
if self._new_rks is None:
@@ -263,23 +264,25 @@ class UpdateRKCommand(QUndoCommand):
**self._data
)
self._reach.update_rks(self._new_rks)
for rk, profile in zip(self._new_rks, self._reach.profiles):
profile.rk = rk
for begin_rk, end_rk, friction in zip(self._new_begin_rk,
self._new_end_rk,
self._frictions):
friction.update_rks(begin_rk, end_rk)
friction.begin_rk = begin_rk
friction.end_rk = end_rk
class MeshingCommand(QUndoCommand):
def __init__(self, reach, new_profiles, data, tableView):
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 = new_profiles
self._new_profiles = None
self._new_profiles_indexes = None
self._tableView = tableView
self._indexes = tableView.selectionModel().selection()
@@ -305,6 +308,12 @@ class MeshingCommand(QUndoCommand):
)
def redo(self):
if self._new_profiles is None:
self._new_profiles = self._mesher.meshing(
self._reach,
**self._data
)
if self._new_profiles_indexes is None:
k = self._limites[0]
self._new_profiles_indexes = []
@@ -409,13 +418,13 @@ class ShiftCommand(QUndoCommand):
def undo(self):
for i in self._rows:
profile = self._reach.profiles[i]
profile.shift(
self._reach.profiles[i].shift(
-self._dx, -self._dy, -self._dz
)
def redo(self):
for i in self._rows:
profile = self._reach.profiles[i]
profile.shift(
self._reach.profiles[i].shift(
self._dx, self._dy, self._dz
)
+2 -2
View File
@@ -130,8 +130,8 @@ class UpdateRKDialog(PamhyrDialog):
return name
def _init_default_values_guidelines(self):
bgl = ['un'] + self._gl + ['np', 'talweg']
egl = ['un'] + self._gl + ['np', 'talweg']
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)
+6 -59
View File
@@ -20,7 +20,6 @@ import os
import sys
import time
import pathlib
from pathlib import Path
import logging
from copy import deepcopy
@@ -302,6 +301,7 @@ class GeometryWindow(PamhyrWindow):
self.tableView.model().blockSignals(False)
def edit_meshing(self):
rows = list(
set(
(i.row() for i in self.tableView.selectedIndexes())
@@ -325,25 +325,6 @@ class GeometryWindow(PamhyrWindow):
logger_exception(e)
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):
try:
mesher = InternalMeshing()
@@ -371,29 +352,6 @@ class GeometryWindow(PamhyrWindow):
parent=self
)
if dlg.exec():
empty_profiles = [
profile for profile in self._reach.profiles
if profile.number_points == 0
]
if empty_profiles:
profile_names = ", ".join(
profile.name or str(profile.num)
for profile in empty_profiles
)
self.message_box(
(
self._trad["update_rk_error"] +
"\n" +
self._trad["update_rk_empty_profiles"]
),
(
self._trad["update_rk_empty_profiles_info"] +
f" {profile_names}"
),
window_title=self._trad["warning"]
)
return
data = {
"origin": dlg.origin,
"directrices": [dlg.begin_dir, dlg.end_dir],
@@ -412,6 +370,9 @@ class GeometryWindow(PamhyrWindow):
except Exception as e:
logger_exception(e)
raise ExternFileMissingError(
module="mage",
filename="MailleurTT",
path=MeshingWithMageMailleurTT._path(),
src_except=e
)
@@ -724,24 +685,10 @@ class GeometryWindow(PamhyrWindow):
)
if filename != '' and filename is not None:
suffix = Path(filename).suffix
if suffix != "":
if suffix == ".st" or suffix == ".ST":
self._export_to_file_st(filename[:-3])
else:
# Warning popup when the format is not exportable
win = QtWidgets.QMessageBox()
win.setIcon(QtWidgets.QMessageBox.Warning)
win.setWindowTitle(self._trad["warning"])
win.setText(self._trad["format_not_exportable"])
win.exec()
pass
else:
self._export_to_file_st(filename)
self._export_to_file_st(filename)
def _export_to_file_st(self, filename):
with open(filename+".st", "w+") as f:
with open(filename, "w+") as f:
f.write("# Exported from Pamhyr2\n")
self._export_to_file_st_reach(f, self._reach)
@@ -138,9 +138,6 @@ class TableModel(PamhyrTableModel):
if not index.isValid() or role != Qt.EditRole:
return False
if self.is_same_data(index, value):
return False
row = index.row()
column = index.column()
@@ -291,9 +288,6 @@ class ParametersTableModel(PamhyrTableModel):
if not index.isValid() or role != Qt.EditRole:
return False
if self.is_same_data(index, value):
return False
if self._hs_index is None:
return QVariant()
+1 -1
View File
@@ -42,7 +42,7 @@ class PlotAC(PamhyrPlot):
self._isometric_axis = False
self._auto_relim_update = True
self._autoscale_update = False
self._autoscale_update = True
@property
def river(self):
+6 -12
View File
@@ -50,7 +50,7 @@ class PlotRKC(PamhyrPlot):
self._isometric_axis = False
self._auto_relim_update = True
self._autoscale_update = False
self._autoscale_update = True
self.parent = parent
self.anotate_lst = []
@@ -136,18 +136,13 @@ class PlotRKC(PamhyrPlot):
hs_color = []
index = self.parent.tableView.selectedIndexes()
for i, hs in enumerate(lhs):
section = hs.input_section
if section is None:
continue
if i == index[0].row():
color = "blue"
hs_color.append("blue")
elif hs.enabled:
color = "red"
hs_color.append("red")
else:
color = "darkgrey"
x = section.rk
hs_color.append("darkgrey")
x = hs.input_section.rk
if x is not None:
a = self.canvas.axes.annotate(
" > " + hs.name,
@@ -156,13 +151,12 @@ class PlotRKC(PamhyrPlot):
verticalalignment='top',
annotation_clip=True,
fontsize=9,
color=color,
color=hs_color[-1],
)
self.anotate_lst.append(a)
vx.append(x)
vymin.append(min(z_min))
vymax.append(max(z_max))
hs_color.append(color)
self.hs_vlines = self.canvas.axes.vlines(
x=vx, ymin=vymin, ymax=vymax,
-3
View File
@@ -169,9 +169,6 @@ class TableModel(PamhyrTableModel):
if not index.isValid() or role != Qt.EditRole:
return False
if self.is_same_data(index, value):
return False
row = index.row()
column = index.column()
na = self._trad['not_associated']
+1 -1
View File
@@ -47,7 +47,7 @@ class PlotDRK(PamhyrPlot):
self._isometric_axis = False
self._auto_relim_update = True
self._autoscale_update = False
self._autoscale_update = True
@timer
def draw(self, highlight=None):
+1 -1
View File
@@ -37,7 +37,7 @@ class PlotDischarge(PamhyrPlot):
self._isometric_axis = False
self._auto_relim_update = True
self._autoscale_update = False
self._autoscale_update = True
@timer
def draw(self):
+4 -25
View File
@@ -153,9 +153,6 @@ class InitialConditionTableModel(PamhyrTableModel):
if not index.isValid() or role != Qt.EditRole:
return False
if self.is_same_data(index, value):
return False
row = index.row()
column = index.column()
@@ -176,7 +173,7 @@ class InitialConditionTableModel(PamhyrTableModel):
def add(self, row, parent=QModelIndex()):
self.beginInsertRows(parent, row, row - 1)
# row = self.get_true_data_row(row)
row = self.get_true_data_row(row)
self._undo.push(
AddCommand(
@@ -333,27 +330,9 @@ class InitialConditionTableModel(PamhyrTableModel):
if not (line.startswith("#") or
line.startswith("*") or
line.startswith("$")):
line_split = line.split()
formated = False
if len(line_split[0]) > 3:
formated = True
elif len(line_split[2]) > 10:
formated = True
elif len(line_split[3]) > 11:
formated = True
elif len(line_split[4]) > 9:
formated = True
if formated: # old PamHyr format
if int(line[1:4]) == reach_id:
data.append([line[31:40],
line[10:20],
line[20:31]])
else:
if int(line_split[0]) == reach_id:
data.append([line_split[4],
line_split[2],
line_split[3]])
line = line.split()
if int(line[0]) == reach_id:
data.append([line[4], line[2], line[3]])
self._undo.push(
ReplaceDataCommand(
+2 -4
View File
@@ -107,6 +107,8 @@ class InitialConditionsWindow(PamhyrWindow):
self.setup_connections()
self.setub_dialogs()
self.ui.setWindowTitle(self._title)
def setup_table(self):
table = self.find(QTableView, f"tableView")
self._delegate_rk = ComboBoxDelegate(
@@ -222,8 +224,6 @@ class InitialConditionsWindow(PamhyrWindow):
def _update_plot(self):
self.plot_1.draw()
self.plot_2.draw()
self.plot_1.idle()
self.plot_2.idle()
def _propagated_update(self, key=Modules(0)):
if Modules.GEOMETRY not in key:
@@ -320,12 +320,10 @@ class InitialConditionsWindow(PamhyrWindow):
def _import_from_results(self, results):
logger.debug(f"import from results: {results}")
self._table.import_from_results(results)
self._update()
def _import_from_ini_file(self, file_name):
logger.debug(f"import from INI file: {file_name}")
self._table.read_from_ini(file_name)
self._update()
def move_up(self):
row = self.index_selected_row()
+2 -19
View File
@@ -117,12 +117,7 @@ class InitialConditionTableModel(PamhyrTableModel):
self._data = data
def _setup_lst(self):
self._lst = list(
filter(
lambda ica: ica._deleted is False,
self._data._data
)
)
self._lst = self._data._data
def rowCount(self, parent):
return len(self._lst)
@@ -188,9 +183,6 @@ class InitialConditionTableModel(PamhyrTableModel):
if not index.isValid() or role != Qt.EditRole:
return False
if self.is_same_data(index, value):
return False
row = index.row()
column = index.column()
@@ -224,34 +216,25 @@ class InitialConditionTableModel(PamhyrTableModel):
)
)
self._setup_lst()
self.endInsertRows()
self.layoutChanged.emit()
def delete(self, rows, parent=QModelIndex()):
self.beginRemoveRows(parent, rows[0], rows[-1])
data_rows = {
id(ica): i for i, ica in enumerate(self._data._data)
}
self._undo.push(
DelCommand(
self._data,
self._data._data,
[data_rows[id(self._lst[row])] for row in rows]
self._data, self._lst, rows
)
)
self._setup_lst()
self.endRemoveRows()
self.layoutChanged.emit()
def undo(self):
self._undo.undo()
self._setup_lst()
self.layoutChanged.emit()
def redo(self):
self._undo.redo()
self._setup_lst()
self.layoutChanged.emit()
@@ -181,6 +181,7 @@ class DelCommand(QUndoCommand):
self._data = data
self._ics_spec = ics_spec
self._rows = rows
# self._data = data
self._ic = []
for row in rows:
+12 -45
View File
@@ -68,22 +68,15 @@ class InitialConditionsAdisTSWindow(PamhyrWindow):
_pamhyr_ui = "InitialConditionsAdisTS"
_pamhyr_name = "Initial condition AdisTS"
def __init__(self, data=None, pollutant_id=None, study=None,
config=None, parent=None):
def __init__(self, data=None, study=None, config=None, parent=None):
self._data = []
self._data.append(data)
self._pollutant_id = pollutant_id
trad = IcAdisTSTranslate()
self._pollutant_name = next(
(x.name for x in study._river._Pollutants.Pollutants_List
if x.id == self._pollutant_id),
None
)
name = (
trad[self._pamhyr_name] +
" - " + study.name +
" - " + self._pollutant_name
" - " + self._data[0].name
)
super(InitialConditionsAdisTSWindow, self).__init__(
@@ -100,21 +93,18 @@ class InitialConditionsAdisTSWindow(PamhyrWindow):
self.setup_table()
self.ui.setWindowTitle(self._title)
def setup_table(self):
path_icons = os.path.join(self._get_ui_directory(), f"ressources")
table_default = self.find(QTableView, f"tableView")
if self._study.is_editable():
editable_headers = self._trad.get_dict("table_headers")
else:
editable_headers = []
self._table = InitialConditionTableDefaultModel(
table_view=table_default,
table_headers=self._trad.get_dict("table_headers"),
editable_headers=editable_headers,
editable_headers=["name", "concentration", "eg", "em", "ed"],
delegates={},
data=self._data,
undo=self._undo_stack,
@@ -134,14 +124,10 @@ class InitialConditionsAdisTSWindow(PamhyrWindow):
action_add = QAction(self)
action_add.setIcon(QIcon(os.path.join(path_icons, f"add.png")))
if self._study.is_editable():
action_add.triggered.connect(self.add)
action_add.triggered.connect(self.add)
action_delete = QAction(self)
action_delete.setIcon(QIcon(os.path.join(path_icons, f"del.png")))
if self._study.is_editable():
action_delete.triggered.connect(self.delete)
action_delete.triggered.connect(self.delete)
toolBar.addAction(action_add)
toolBar.addAction(action_delete)
@@ -164,15 +150,12 @@ class InitialConditionsAdisTSWindow(PamhyrWindow):
mode="rk"
)
if self._study.is_editable():
editable_headers_spec = self._trad.get_dict("table_headers_spec")
else:
editable_headers_spec = []
self._table_spec = InitialConditionTableModel(
table_view=self.table_spec,
table_headers=self._trad.get_dict("table_headers_spec"),
editable_headers=editable_headers_spec,
editable_headers=["name", "reach", "start_rk",
"end_rk", "concentration",
"eg", "em", "ed", "rate"],
delegates={
"reach": self._delegate_reach,
"start_rk": self._delegate_rk,
@@ -283,27 +266,11 @@ class InitialConditionsAdisTSWindow(PamhyrWindow):
self._update()
def _undo(self):
undo_stack = self._undo_stack
if undo_stack is None or not undo_stack.canUndo():
return
if isinstance(undo_stack.command(undo_stack.index() - 1), SetCommand):
self._table.undo()
else:
self._table_spec.undo()
self._table.undo()
self._update()
def _redo(self):
undo_stack = self._undo_stack
if undo_stack is None or not undo_stack.canRedo():
return
if isinstance(undo_stack.command(undo_stack.index()), SetCommand):
self._table.redo()
else:
self._table_spec.redo()
self._table.redo()
self._update()
def add(self):
Loaded 100 of 176 files, more files were not shown because too many files have changed in this diff. Show more