wolfhece 2.2.38__py3-none-any.whl → 2.2.40__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- wolfhece/Coordinates_operations.py +5 -0
- wolfhece/GraphNotebook.py +72 -1
- wolfhece/GraphProfile.py +1 -1
- wolfhece/MulticriteriAnalysis.py +1579 -0
- wolfhece/PandasGrid.py +62 -1
- wolfhece/PyCrosssections.py +194 -43
- wolfhece/PyDraw.py +891 -73
- wolfhece/PyGui.py +913 -72
- wolfhece/PyGuiHydrology.py +528 -74
- wolfhece/PyPalette.py +26 -4
- wolfhece/PyParams.py +33 -0
- wolfhece/PyPictures.py +2 -2
- wolfhece/PyVertex.py +25 -0
- wolfhece/PyVertexvectors.py +94 -28
- wolfhece/PyWMS.py +52 -36
- wolfhece/acceptability/acceptability.py +15 -8
- wolfhece/acceptability/acceptability_gui.py +507 -360
- wolfhece/acceptability/func.py +80 -183
- wolfhece/apps/version.py +1 -1
- wolfhece/compare_series.py +480 -0
- wolfhece/drawing_obj.py +12 -1
- wolfhece/hydrology/Catchment.py +228 -162
- wolfhece/hydrology/Internal_variables.py +43 -2
- wolfhece/hydrology/Models_characteristics.py +69 -67
- wolfhece/hydrology/Optimisation.py +893 -182
- wolfhece/hydrology/PyWatershed.py +267 -165
- wolfhece/hydrology/SubBasin.py +185 -140
- wolfhece/hydrology/cst_exchanges.py +76 -1
- wolfhece/hydrology/forcedexchanges.py +413 -49
- wolfhece/hydrology/read.py +65 -5
- wolfhece/hydrometry/kiwis.py +14 -7
- wolfhece/insyde_be/INBE_func.py +746 -0
- wolfhece/insyde_be/INBE_gui.py +1776 -0
- wolfhece/insyde_be/__init__.py +3 -0
- wolfhece/interpolating_raster.py +366 -0
- wolfhece/irm_alaro.py +1457 -0
- wolfhece/irm_qdf.py +889 -57
- wolfhece/lazviewer/laz_viewer.py +4 -1
- wolfhece/lifewatch.py +6 -3
- wolfhece/picc.py +124 -8
- wolfhece/pyLandUseFlanders.py +146 -0
- wolfhece/pydownloader.py +35 -1
- wolfhece/pywalous.py +225 -31
- wolfhece/toolshydrology_dll.py +149 -0
- wolfhece/wolf_array.py +63 -25
- {wolfhece-2.2.38.dist-info → wolfhece-2.2.40.dist-info}/METADATA +3 -1
- {wolfhece-2.2.38.dist-info → wolfhece-2.2.40.dist-info}/RECORD +50 -41
- {wolfhece-2.2.38.dist-info → wolfhece-2.2.40.dist-info}/WHEEL +0 -0
- {wolfhece-2.2.38.dist-info → wolfhece-2.2.40.dist-info}/entry_points.txt +0 -0
- {wolfhece-2.2.38.dist-info → wolfhece-2.2.40.dist-info}/top_level.txt +0 -0
@@ -10,8 +10,9 @@ copying or distribution of this file, via any medium, is strictly prohibited.
|
|
10
10
|
|
11
11
|
from . import constant as cst
|
12
12
|
from enum import Enum
|
13
|
-
# Constants representing the exchanges - Fortran
|
13
|
+
# Constants representing the exchanges - Fortran (cfr. Fortran cst_exchange.f90)
|
14
14
|
|
15
|
+
# Types of exchanges between blocks/models
|
15
16
|
exchange_parameters_VHM_Umax = 20 #Paramètre modèle VHM
|
16
17
|
exchange_parameters_VHM_Uevap = 21 #Paramètre modèle VHM
|
17
18
|
exchange_parameters_VHM_au1 = 22 #Paramètre modèle VHM
|
@@ -59,6 +60,8 @@ exchange_parameters_SAC_riva = 77 #Paramètre modèle SAC-SMA (SACRAM
|
|
59
60
|
exchange_parameters_SAC_adimp = 78 #Paramètre modèle SAC-SMA (SACRAMENTO)
|
60
61
|
exchange_parameters_SAC_impv = 79 #Paramètre modèle SAC-SMA (SACRAMENTO)
|
61
62
|
|
63
|
+
exchange_parameters_SAC_kof = 120 #Paramètre modèle SAC-SMA (SACRAMENTO) with LR OF
|
64
|
+
|
62
65
|
exchange_parameters_NAM_UMAX = 82 #Paramètre modèle NAM
|
63
66
|
exchange_parameters_NAM_TOF = 83 #Paramètre modèle NAM
|
64
67
|
exchange_parameters_NAM_TIF = 84 #Paramètre modèle NAM
|
@@ -94,6 +97,65 @@ exchange_parameters_Dist_Horton_K = 116 #Paramètre modèle distribué Hor
|
|
94
97
|
exchange_parameters_Dist_kif = 117 #Paramètre modèle distribué pour réservoir linéaire couche épidermique (if)
|
95
98
|
exchange_parameters_Dist_qlif = 118 #Paramètre modèle distribué pour réservoir linéaire couche épidermique (if)
|
96
99
|
|
100
|
+
# Internal variables ids
|
101
|
+
iv_VHM_qof = 201
|
102
|
+
iv_VHM_qif = 202
|
103
|
+
iv_VHM_qbf = 203
|
104
|
+
iv_VHM_U = 204
|
105
|
+
iv_VHM_xu = 205
|
106
|
+
iv_VHM_xof = 206
|
107
|
+
iv_VHM_xif = 207
|
108
|
+
iv_VHM_xbf = 208
|
109
|
+
|
110
|
+
iv_2layers_linBF_qof = 701
|
111
|
+
iv_2layers_linBF_qif = 702
|
112
|
+
iv_2layers_linBF_U = 703
|
113
|
+
iv_2layers_linBF_S = 704
|
114
|
+
iv_2layers_linBF_xif = 705
|
115
|
+
iv_2layers_linBF_xp = 706
|
116
|
+
|
117
|
+
iv_HBV_qr = 901
|
118
|
+
iv_HBV_qif = 902
|
119
|
+
iv_HBV_qbf = 903
|
120
|
+
iv_HBV_qrech = 904
|
121
|
+
iv_HBV_soil_qcap = 905
|
122
|
+
iv_HBV_qperc = 906
|
123
|
+
iv_HBV_UZ_qcap = 907
|
124
|
+
iv_HBV_U = 908
|
125
|
+
iv_HBV_Su = 909
|
126
|
+
iv_HBV_etr = 910
|
127
|
+
|
128
|
+
iv_SACSMA_qof = 1001
|
129
|
+
iv_SACSMA_qif = 1002
|
130
|
+
iv_SACSMA_qbf = 1003
|
131
|
+
iv_SACSMA_qsubbf = 1004
|
132
|
+
iv_SACSMA_qsurf = 1005
|
133
|
+
iv_SACSMA_qbase = 1006
|
134
|
+
iv_SACSMA_etot = 1007
|
135
|
+
iv_SACSMA_e1 = 1008
|
136
|
+
iv_SACSMA_e2 = 1009
|
137
|
+
iv_SACSMA_e5 = 1010
|
138
|
+
iv_SACSMA_qqif = 1011
|
139
|
+
iv_SACSMA_qqsr = 1012
|
140
|
+
iv_SACSMA_qqdr = 1013
|
141
|
+
iv_SACSMA_CUZTW = 1014
|
142
|
+
iv_SACSMA_CUZFW = 1015
|
143
|
+
iv_SACSMA_CADIMP = 1016
|
144
|
+
iv_SACSMA_CLZTW = 1017
|
145
|
+
iv_SACSMA_CLZFP = 1018
|
146
|
+
iv_SACSMA_CLZFS = 1019
|
147
|
+
iv_SACSMA_e3 = 1020
|
148
|
+
iv_SACSMA_qoutLR = 1021
|
149
|
+
|
150
|
+
iv_NAM_qof = 1101
|
151
|
+
iv_NAM_qif = 1102
|
152
|
+
iv_NAM_qbf = 1103
|
153
|
+
iv_NAM_ea = 1104
|
154
|
+
iv_NAM_erz = 1105
|
155
|
+
iv_NAM_qg = 1106
|
156
|
+
iv_NAM_U = 1107
|
157
|
+
iv_NAM_L = 1108
|
158
|
+
|
97
159
|
|
98
160
|
# Constants representing the exchanges - Python
|
99
161
|
exchange_parameters_py_timeDelay = -11
|
@@ -497,6 +559,17 @@ SAC_SMA["Parameters"][exchange_parameters_SAC_impv]["Unit"] = "[-]"
|
|
497
559
|
SAC_SMA["Parameters"][exchange_parameters_SAC_impv]["Range"] = (0.0, 0.05)
|
498
560
|
|
499
561
|
|
562
|
+
SAC_SMA_LROF = SAC_SMA.copy()
|
563
|
+
SAC_SMA_LROF["Nb"] = 17
|
564
|
+
SAC_SMA_LROF["Parameters"][exchange_parameters_SAC_kof] = {}
|
565
|
+
SAC_SMA_LROF["Parameters"][exchange_parameters_SAC_kof]["Name"] = "Kof"
|
566
|
+
SAC_SMA_LROF["Parameters"][exchange_parameters_SAC_kof]["File"] = "simul_of.param"
|
567
|
+
SAC_SMA_LROF["Parameters"][exchange_parameters_SAC_kof]["Group"] = "Time Parameters"
|
568
|
+
SAC_SMA_LROF["Parameters"][exchange_parameters_SAC_kof]["Key"] = "Lagtime"
|
569
|
+
SAC_SMA_LROF["Parameters"][exchange_parameters_SAC_kof]["Unit"] = "[sec]"
|
570
|
+
SAC_SMA_LROF["Parameters"][exchange_parameters_SAC_kof]["Convertion Factor"] = 1/3600.0 # [sec] -> [h]
|
571
|
+
|
572
|
+
|
500
573
|
NAM = {}
|
501
574
|
NAM["Nb"] = 10
|
502
575
|
NAM["Parameters"] = {}
|
@@ -583,6 +656,7 @@ modelParamsDict[cst.tom_2layers_linIF]= UHDIST_LINBF
|
|
583
656
|
modelParamsDict[cst.tom_HBV]= HBV
|
584
657
|
modelParamsDict[cst.tom_SAC_SMA]= SAC_SMA
|
585
658
|
modelParamsDict[cst.tom_NAM]= NAM
|
659
|
+
modelParamsDict[cst.tom_SAC_SMA_LROF]= SAC_SMA_LROF
|
586
660
|
|
587
661
|
# %% Python-Fortran exchange constants
|
588
662
|
|
@@ -590,6 +664,7 @@ ptr_params = 1
|
|
590
664
|
ptr_opti_factors = 2
|
591
665
|
ptr_q_all = 3
|
592
666
|
ptr_time_delays = 4
|
667
|
+
ptr_iv_saved = 5
|
593
668
|
|
594
669
|
fptr_update = 1
|
595
670
|
fptr_get_cvg = 2
|
@@ -10,59 +10,423 @@ copying or distribution of this file, via any medium, is strictly prohibited.
|
|
10
10
|
"""
|
11
11
|
|
12
12
|
from os import path
|
13
|
+
from pathlib import Path
|
13
14
|
|
14
15
|
#from ..color_constants import *
|
15
16
|
from ..PyVertexvectors import *
|
16
17
|
from ..PyVertex import cloud_vertices,wolfvertex
|
17
|
-
from ..PyTranslate import _
|
18
|
-
|
18
|
+
from ..PyTranslate import _
|
19
|
+
|
19
20
|
class forced_exchanges:
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
self.
|
29
|
-
self.
|
30
|
-
|
31
|
-
|
32
|
-
self.
|
33
|
-
|
34
|
-
self.
|
35
|
-
self.
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
21
|
+
""" Forced exchanges For Hydrological model.
|
22
|
+
|
23
|
+
A forced exchange is a pair of vertices that are coupled together.
|
24
|
+
The first vertex is the upper one, the second is the lower one.
|
25
|
+
"""
|
26
|
+
|
27
|
+
def __init__(self, workingdir='', fname='', mapviewer=None) -> None:
|
28
|
+
|
29
|
+
self.mapviewer = mapviewer
|
30
|
+
self._workingdir = Path(workingdir)
|
31
|
+
|
32
|
+
self._color_up = (0, 238, 0) # Green
|
33
|
+
self._color_down = (255, 52, 179) # Pink
|
34
|
+
|
35
|
+
self.type='COORDINATES'
|
36
|
+
self._mycloudup = cloud_vertices(mapviewer=self.mapviewer)
|
37
|
+
self._myclouddown= cloud_vertices(mapviewer=self.mapviewer)
|
38
|
+
self._mysegs = Zones(mapviewer=self.mapviewer)
|
39
|
+
|
40
|
+
tmp_zone = zone(name='temporary')
|
41
|
+
self._mysegs.add_zone(tmp_zone, forceparent=True)
|
42
|
+
tmpvec = vector(name='temporary')
|
43
|
+
tmpvec.myprop.color = getIfromRGB((0, 0, 128))
|
44
|
+
tmpvec.myprop.width = 2
|
45
|
+
tmp_zone.add_vector(tmpvec, forceparent=True)
|
46
|
+
|
47
|
+
self._myzone = zone(name='segments_fe')
|
48
|
+
self._mysegs.add_zone(self._myzone, forceparent=True)
|
49
|
+
|
50
|
+
self._mycloudup.myprop.color = getIfromRGB((0,238,0))
|
51
|
+
self._mycloudup.myprop.filled = True
|
52
|
+
self._myclouddown.myprop.color = getIfromRGB((255,52,179))
|
53
|
+
self._myclouddown.myprop.filled= True
|
54
|
+
|
55
|
+
if fname:
|
56
|
+
if isinstance(fname, str):
|
57
|
+
if fname == 'N-O':
|
58
|
+
fname = self._workingdir / 'Coupled_pairs.txt'
|
59
|
+
else:
|
60
|
+
fname = self._workingdir / fname
|
61
|
+
if not fname.exists():
|
62
|
+
logging.error(f"The file {fname} does not exist.")
|
63
|
+
|
64
|
+
self._filename = fname if fname else self._workingdir / 'Coupled_pairs.txt'
|
65
|
+
|
66
|
+
try:
|
67
|
+
self._read_file()
|
68
|
+
except:
|
69
|
+
logging.error(f"Could not read the file {self._filename}. It may not be in the correct format or may be corrupted.")
|
70
|
+
|
71
|
+
self._myzone.find_minmax(True)
|
72
|
+
|
73
|
+
def is_empty(self):
|
74
|
+
""" Check if the forced exchanges are empty. """
|
75
|
+
return self._mysegs['segments_fe'].nbvectors == 0
|
76
|
+
|
77
|
+
@property
|
78
|
+
def pairs(self):
|
79
|
+
""" Get the list of pairs of vertices. """
|
80
|
+
seg_zone = self._mysegs['segments_fe']
|
81
|
+
if not seg_zone:
|
82
|
+
raise ValueError("The segments zone is not initialized or does not exist.")
|
83
|
+
|
84
|
+
return [[vec[0].x, vec[0].y, vec[-1].x, vec[-1].y] for vec in seg_zone.myvectors]
|
85
|
+
|
86
|
+
@property
|
87
|
+
def temporary_vector(self):
|
88
|
+
""" Get the temporary vector used for forced exchanges. """
|
89
|
+
return self._mysegs[('temporary', 'temporary')]
|
90
|
+
|
91
|
+
@property
|
92
|
+
def color_up_integer(self):
|
93
|
+
""" Get the color of the upper vertices as an integer. """
|
94
|
+
return getIfromRGB(self._color_up)
|
95
|
+
|
96
|
+
@property
|
97
|
+
def color_down_integer(self):
|
98
|
+
""" Get the color of the lower vertices as an integer. """
|
99
|
+
return getIfromRGB(self._color_down)
|
100
|
+
|
101
|
+
@property
|
102
|
+
def color_up_rgb(self):
|
103
|
+
""" Get the color of the upper vertices as an RGB tuple. """
|
104
|
+
return self._color_up
|
105
|
+
|
106
|
+
@property
|
107
|
+
def color_down_rgb(self):
|
108
|
+
""" Get the color of the lower vertices as an RGB tuple. """
|
109
|
+
return self._color_down
|
110
|
+
|
111
|
+
@color_up_rgb.setter
|
112
|
+
def color_up_rgb(self, value):
|
113
|
+
""" Set the color of the upper vertices from an RGB tuple. """
|
114
|
+
if isinstance(value, tuple) and len(value) == 3:
|
115
|
+
self._color_up = value
|
116
|
+
self._mycloudup.myprop.color = getIfromRGB(value)
|
117
|
+
else:
|
118
|
+
raise ValueError("color_up_rgb must be a tuple of three integers (R, G, B).")
|
119
|
+
|
120
|
+
@color_down_rgb.setter
|
121
|
+
def color_down_rgb(self, value):
|
122
|
+
""" Set the color of the lower vertices from an RGB tuple. """
|
123
|
+
if isinstance(value, tuple) and len(value) == 3:
|
124
|
+
self._color_down = value
|
125
|
+
self._myclouddown.myprop.color = getIfromRGB(value)
|
126
|
+
else:
|
127
|
+
raise ValueError("color_down_rgb must be a tuple of three integers (R, G, B).")
|
128
|
+
|
129
|
+
@color_up_integer.setter
|
130
|
+
def color_up_integer(self, value):
|
131
|
+
""" Set the color of the upper vertices from an integer. """
|
132
|
+
if isinstance(value, int):
|
133
|
+
self._color_up = getRGBfromI(value)
|
134
|
+
self._mycloudup.myprop.color = value
|
135
|
+
else:
|
136
|
+
raise ValueError("color_up_integer must be an integer representing a color.")
|
137
|
+
|
138
|
+
@color_down_integer.setter
|
139
|
+
def color_down_integer(self, value):
|
140
|
+
""" Set the color of the lower vertices from an integer. """
|
141
|
+
if isinstance(value, int):
|
142
|
+
self._color_down = getRGBfromI(value)
|
143
|
+
self._myclouddown.myprop.color = value
|
144
|
+
else:
|
145
|
+
raise ValueError("color_down_integer must be an integer representing a color.")
|
146
|
+
|
147
|
+
def _read_file(self):
|
148
|
+
""" Read the forced exchanges from a file. """
|
149
|
+
|
150
|
+
if not self._filename.exists():
|
151
|
+
logging.error(f"The file {self._filename} does not exist.")
|
152
|
+
return
|
153
|
+
|
154
|
+
with open(self._filename, 'rt') as f:
|
40
155
|
content = f.read().splitlines()
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
156
|
+
|
157
|
+
self.type = content[0]
|
158
|
+
idx=1
|
159
|
+
for curline in content[1:]:
|
160
|
+
coords=curline.split('\t')
|
161
|
+
coords = [float(x) for x in coords]
|
162
|
+
|
163
|
+
vert1 = wolfvertex(coords[0],coords[1])
|
164
|
+
vert2 = wolfvertex(coords[2],coords[3])
|
165
|
+
|
166
|
+
myseg = vector(name='fe'+str(idx))
|
167
|
+
myseg.myprop.width = 2
|
168
|
+
myseg.myprop.color = getIfromRGB((0,0,128))
|
169
|
+
myseg.add_vertex([vert1,vert2])
|
170
|
+
self._myzone.add_vector(myseg, forceparent=True)
|
171
|
+
|
172
|
+
self._mycloudup.add_vertex(vert1)
|
173
|
+
self._myclouddown.add_vertex(vert2)
|
174
|
+
idx+=1
|
175
|
+
|
176
|
+
def _save_file(self):
|
177
|
+
""" Save the forced exchanges to a file. """
|
178
|
+
|
179
|
+
with open(self._filename, 'wt') as f:
|
180
|
+
f.write(f"{self.type}\n")
|
181
|
+
for pair in self.pairs:
|
182
|
+
f.write(f"{pair[0]}\t{pair[1]}\t{pair[2]}\t{pair[3]}\n")
|
183
|
+
|
184
|
+
logging.info(f"Forced exchanges saved to {self._filename}.")
|
185
|
+
|
186
|
+
def save(self):
|
187
|
+
""" Save the forced exchanges to the file. """
|
188
|
+
if not self._filename:
|
189
|
+
raise ValueError("Filename is not set. Cannot save forced exchanges.")
|
190
|
+
|
191
|
+
self._save_file()
|
192
|
+
|
193
|
+
def add_pair_dict(self, pair:dict):
|
194
|
+
""" Add a pair of vertices to the forced exchanges from a dictionary.
|
195
|
+
|
196
|
+
:param pair: A dictionary with 'up' and 'down' keys containing the vertices.
|
197
|
+
:type pair: dict
|
198
|
+
"""
|
199
|
+
|
200
|
+
if not isinstance(pair, dict):
|
201
|
+
raise TypeError("pair must be a dictionary with 'up' and 'down' keys.")
|
202
|
+
|
203
|
+
if 'up' not in pair or 'down' not in pair:
|
204
|
+
raise KeyError("The dictionary must contain 'up' and 'down' keys.")
|
205
|
+
|
206
|
+
self.add_pair(pair['up'], pair['down'])
|
207
|
+
|
208
|
+
def add_pair_XY(self, x1, y1, x2, y2, reset_ogl:bool = False):
|
209
|
+
""" Add a pair of coordinates to the forced exchanges. """
|
210
|
+
|
211
|
+
if not isinstance(x1, (int, float)) or not isinstance(y1, (int, float)):
|
212
|
+
raise TypeError("x1 and y1 must be numeric values.")
|
213
|
+
if not isinstance(x2, (int, float)) or not isinstance(y2, (int, float)):
|
214
|
+
raise TypeError("x2 and y2 must be numeric values.")
|
215
|
+
|
216
|
+
vertex_up = wolfvertex(x1, y1)
|
217
|
+
vertex_down = wolfvertex(x2, y2)
|
218
|
+
|
219
|
+
vec = vector(name= self._find_first_available_name())
|
220
|
+
vec.add_vertex([vertex_up, vertex_down])
|
221
|
+
self._myzone.add_vector(vec, forceparent=True)
|
222
|
+
|
223
|
+
self._mycloudup.add_vertex(vertex_up)
|
224
|
+
self._myclouddown.add_vertex(vertex_down)
|
225
|
+
|
226
|
+
if reset_ogl:
|
227
|
+
self.reset_listogl()
|
228
|
+
|
229
|
+
def add_pairs_XY(self, ups: list[list[float, float]], downs: list[float, float]):
|
230
|
+
""" Add multiple upstreams to one downstream as forced exchanges.
|
231
|
+
|
232
|
+
:param ups: A list of lists containing the coordinates of the upstream vertices.
|
233
|
+
:type ups: list[list[float, float]]
|
234
|
+
:param downs: A pair containing the coordinates of the downstream vertex.
|
235
|
+
"""
|
236
|
+
if not isinstance(ups, list) or not all(isinstance(coord, list) and len(coord) == 2 for coord in ups):
|
237
|
+
raise TypeError("ups must be a list of lists with two numeric values each.")
|
238
|
+
if not isinstance(downs, list) or len(downs) != 2:
|
239
|
+
raise TypeError("downs must be a list with two numeric values.")
|
240
|
+
|
241
|
+
x_down, y_down = downs
|
242
|
+
for up in ups:
|
243
|
+
x_up, y_up = up
|
244
|
+
self.add_pair_XY(x_up, y_up, x_down, y_down)
|
245
|
+
|
246
|
+
self.reset_listogl()
|
247
|
+
|
248
|
+
def add_pair(self, vertex_up, vertex_down):
|
249
|
+
""" Add a pair of vertices to the forced exchanges. """
|
250
|
+
|
251
|
+
if not isinstance(vertex_up, wolfvertex) or not isinstance(vertex_down, wolfvertex):
|
252
|
+
raise TypeError("Both vertices must be of type wolfvertex.")
|
253
|
+
|
254
|
+
vec = vector(name= self._find_first_available_name())
|
255
|
+
vec.add_vertex([vertex_up, vertex_down])
|
256
|
+
self._myzone.add_vector(vec, forceparent=True)
|
257
|
+
|
258
|
+
self._mycloudup.add_vertex(vertex_up)
|
259
|
+
self._myclouddown.add_vertex(vertex_down)
|
260
|
+
|
261
|
+
self.reset_listogl()
|
262
|
+
|
263
|
+
def reset_listogl(self):
|
264
|
+
""" Reset the OpenGL lists for the forced exchanges. """
|
265
|
+
|
266
|
+
if not self._myclouddown or not self._mycloudup:
|
267
|
+
raise ValueError("Clouds for down and up vertices are not initialized.")
|
268
|
+
|
269
|
+
self._myclouddown.reset_listogl()
|
270
|
+
self._mycloudup.reset_listogl()
|
271
|
+
|
272
|
+
if not self._mysegs:
|
273
|
+
raise ValueError("Segments zone is not initialized.")
|
274
|
+
|
275
|
+
self._mysegs.reset_listogl()
|
276
|
+
|
277
|
+
def _find_nearest_pair(self, x, y):
|
278
|
+
""" Find the nearest pair of vertices to the given coordinates. """
|
279
|
+
|
280
|
+
if not isinstance(x, (int, float)) or not isinstance(y, (int, float)):
|
281
|
+
raise TypeError("x and y must be numeric values.")
|
282
|
+
|
283
|
+
if not self.pairs:
|
284
|
+
return None
|
285
|
+
|
286
|
+
min_dist = float('inf')
|
287
|
+
nearest_pair = None
|
288
|
+
|
289
|
+
for pair in self.pairs:
|
290
|
+
dist = ((pair[0] - x) ** 2 + (pair[1] - y) ** 2) ** 0.5
|
291
|
+
if dist < min_dist:
|
292
|
+
min_dist = dist
|
293
|
+
nearest_pair = pair
|
294
|
+
|
295
|
+
return nearest_pair
|
296
|
+
|
297
|
+
def get_nearest_pair(self, x:float, y:float) -> dict:
|
298
|
+
""" Get the nearest pair of vertices to the given coordinates.
|
299
|
+
|
300
|
+
:return: A dictionary with 'up' and 'down' keys containing the nearest vertices.
|
301
|
+
:rtype: dict
|
302
|
+
"""
|
303
|
+
|
304
|
+
nearest_pair = self._find_nearest_pair(x, y)
|
305
|
+
|
306
|
+
if nearest_pair is None:
|
307
|
+
return None
|
308
|
+
|
309
|
+
return {
|
310
|
+
'up': wolfvertex(nearest_pair[0], nearest_pair[1]),
|
311
|
+
'down': wolfvertex(nearest_pair[2], nearest_pair[3])
|
312
|
+
}
|
313
|
+
|
314
|
+
def get_nearest_pair_as_vector(self, x:float, y:float) -> vector:
|
315
|
+
""" Get the nearest pair of vertices as a vector. """
|
316
|
+
|
317
|
+
nearest_index = self._find_nearest_pair_index(x, y)
|
318
|
+
vec = self._myzone[f'fe{nearest_index}'] if nearest_index != -1 else None
|
319
|
+
|
320
|
+
return vec
|
321
|
+
|
322
|
+
def _find_nearest_pair_index(self, x, y):
|
323
|
+
""" Find the index of the nearest pair of vertices to the given coordinates. """
|
324
|
+
|
325
|
+
if not isinstance(x, (int, float)) or not isinstance(y, (int, float)):
|
326
|
+
raise TypeError("x and y must be numeric values.")
|
327
|
+
|
328
|
+
if not self.pairs:
|
329
|
+
return -1
|
330
|
+
|
331
|
+
min_dist = float('inf')
|
332
|
+
nearest_index = -1
|
333
|
+
|
334
|
+
for idx, pair in enumerate(self.pairs):
|
335
|
+
dist = ((pair[0] - x) ** 2 + (pair[1] - y) ** 2) ** 0.5
|
336
|
+
if dist < min_dist:
|
337
|
+
min_dist = dist
|
338
|
+
nearest_index = idx
|
339
|
+
|
340
|
+
return nearest_index
|
341
|
+
|
342
|
+
def remove_nearest_pair(self, x, y):
|
343
|
+
""" Remove the nearest pair of vertices to the given coordinates. """
|
344
|
+
|
345
|
+
nearest_index = self._find_nearest_pair_index(x, y)
|
346
|
+
|
347
|
+
if nearest_index != -1:
|
348
|
+
self._myclouddown.remove_vertex(nearest_index)
|
349
|
+
self._mycloudup.remove_vertex(nearest_index)
|
350
|
+
self._myzone.myvectors.pop(nearest_index)
|
351
|
+
|
352
|
+
self.reset_listogl()
|
353
|
+
|
354
|
+
def remove_nearest_pairs(self, xy:list[list[float, float]]):
|
355
|
+
""" Remove the nearest pairs of vertices to the given coordinates. """
|
356
|
+
|
357
|
+
if not isinstance(xy, list) or not all(isinstance(coord, list) and len(coord) == 2 for coord in xy):
|
358
|
+
raise TypeError("xy must be a list of lists with two numeric values each.")
|
359
|
+
|
360
|
+
idx_to_remove = list(set([self._find_nearest_pair_index(coords[0], coords[1]) for coords in xy]))
|
361
|
+
|
362
|
+
for i in reversed(idx_to_remove):
|
363
|
+
if i != -1:
|
364
|
+
self._myclouddown.remove_vertex(i)
|
365
|
+
self._mycloudup.remove_vertex(i)
|
366
|
+
self._myzone.myvectors.pop(i)
|
367
|
+
|
368
|
+
self.reset_listogl()
|
369
|
+
|
370
|
+
def remove_pairs_inside_vector(self, vec:vector):
|
371
|
+
""" Remove pairs of vertices that are inside the given vector.
|
372
|
+
|
373
|
+
:param vec: The vector to check against.
|
374
|
+
:type vec: vector
|
375
|
+
"""
|
376
|
+
|
377
|
+
if not isinstance(vec, vector):
|
378
|
+
raise TypeError("vec must be an instance of vector.")
|
379
|
+
|
380
|
+
idx_to_remove = []
|
381
|
+
for idx, pair in enumerate(self.pairs):
|
382
|
+
if vec.isinside(pair[0], pair[1]) or vec.isinside(pair[2], pair[3]):
|
383
|
+
idx_to_remove.append(idx)
|
384
|
+
|
385
|
+
idx_to_remove = list(set(idx_to_remove))
|
386
|
+
|
387
|
+
for idx in reversed(idx_to_remove):
|
388
|
+
self._myclouddown.remove_vertex(idx)
|
389
|
+
self._mycloudup.remove_vertex(idx)
|
390
|
+
self._myzone.myvectors.pop(idx)
|
391
|
+
|
392
|
+
self.reset_listogl()
|
393
|
+
|
394
|
+
def _find_first_available_name(self):
|
395
|
+
""" Find the first available name for a new forced exchange. """
|
396
|
+
|
397
|
+
idx = 1
|
398
|
+
names = [v.myname for v in self._myzone.myvectors]
|
399
|
+
while True:
|
400
|
+
name = f'fe{idx}'
|
401
|
+
if name not in self._myzone.myvectors:
|
402
|
+
return name
|
403
|
+
idx += 1
|
404
|
+
|
65
405
|
def paint(self):
|
66
|
-
self.
|
67
|
-
self.
|
68
|
-
self.
|
406
|
+
self._mycloudup.plot()
|
407
|
+
self._myclouddown.plot()
|
408
|
+
self._mysegs.plot()
|
409
|
+
|
410
|
+
def reset(self):
|
411
|
+
""" Reset the forced exchanges. """
|
412
|
+
self.reset_listogl()
|
413
|
+
|
414
|
+
self._mycloudup = cloud_vertices(mapviewer=self.mapviewer)
|
415
|
+
self._myclouddown = cloud_vertices(mapviewer=self.mapviewer)
|
416
|
+
self._mysegs = Zones(mapviewer=self.mapviewer)
|
417
|
+
|
418
|
+
tmp_zone = zone(name='temporary')
|
419
|
+
self._mysegs.add_zone(tmp_zone, forceparent=True)
|
420
|
+
tmpvec = vector(name='temporary')
|
421
|
+
tmpvec.myprop.color = getIfromRGB((0, 0, 128))
|
422
|
+
tmpvec.myprop.width = 2
|
423
|
+
tmp_zone.add_vector(tmpvec, forceparent=True)
|
424
|
+
|
425
|
+
self._myzone = zone(name='segments_fe')
|
426
|
+
self._mysegs.add_zone(self._myzone, forceparent=True)
|
427
|
+
|
428
|
+
self._mycloudup.myprop.color = getIfromRGB((0, 238, 0))
|
429
|
+
self._mycloudup.myprop.filled = True
|
430
|
+
self._myclouddown.myprop.color = getIfromRGB((255, 52, 179))
|
431
|
+
self._myclouddown.myprop.filled = True
|
432
|
+
|
wolfhece/hydrology/read.py
CHANGED
@@ -13,8 +13,7 @@ import os
|
|
13
13
|
import logging
|
14
14
|
from datetime import datetime as date
|
15
15
|
from datetime import timezone
|
16
|
-
from struct import unpack, calcsize, unpack_from
|
17
|
-
from pathlib import Path
|
16
|
+
from struct import unpack, calcsize, unpack_from, pack
|
18
17
|
|
19
18
|
|
20
19
|
# Constants
|
@@ -145,12 +144,16 @@ def read_bin(path, fileName, format="", nbBytes=[], uniform_format=-1, hydro=Fal
|
|
145
144
|
all_bytes = file.read()
|
146
145
|
nbL = int.from_bytes(all_bytes[:4], byteorder='little', signed=True)
|
147
146
|
nbC = int.from_bytes(all_bytes[4:8], byteorder='little', signed=True)
|
148
|
-
|
147
|
+
z = all_bytes[8:8+nbL*calcsize(format)]
|
148
|
+
flat_data = unpack(format[0] + format[1:]*nbL, z)
|
149
|
+
data = [list(flat_data[i * (nbC+1) : (i + 1) * (nbC+1)]) for i in range(nbL)]
|
150
|
+
# data = np.array(flat_data, dtype=np.float64).reshape(nbL, nbC+1)
|
149
151
|
else:
|
150
|
-
|
152
|
+
data = read_bin_old(path, fileName, nbBytes=nbBytes, uniform_format=uniform_format, hydro=hydro)
|
151
153
|
|
152
154
|
|
153
|
-
return
|
155
|
+
return data
|
156
|
+
|
154
157
|
|
155
158
|
|
156
159
|
def read_binary_file(path, fileName, format="", buffer_size=-1, init_offset=8):
|
@@ -196,6 +199,8 @@ def read_binary_file(path, fileName, format="", buffer_size=-1, init_offset=8):
|
|
196
199
|
file.seek(offset - len(buffer), 1)
|
197
200
|
# break
|
198
201
|
|
202
|
+
#print(f"Number of values read: {len(values_list)} / {nbL}")
|
203
|
+
|
199
204
|
return values_list
|
200
205
|
|
201
206
|
|
@@ -321,3 +326,58 @@ def check_path(fileName:str, prefix:str="", applyCWD:bool=True) -> tuple[bool, s
|
|
321
326
|
return info, fileName
|
322
327
|
|
323
328
|
return info, os.path.normpath(finalName)
|
329
|
+
|
330
|
+
|
331
|
+
def write_binary_file(path:str, fileName:str, data:list, format:str=""):
|
332
|
+
if not data:
|
333
|
+
raise ValueError("Data cannot be empty")
|
334
|
+
|
335
|
+
if format == "":
|
336
|
+
# Default format
|
337
|
+
format = "<bbhbbbd"
|
338
|
+
elif "<" not in format:
|
339
|
+
logging.warning("Format should start with '<' if you are on Windows.")
|
340
|
+
|
341
|
+
nbL = len(data)
|
342
|
+
nbC = len(format.replace("<", "")) - 1
|
343
|
+
|
344
|
+
with open(os.path.join(path, fileName), 'wb') as file:
|
345
|
+
# Write header: number of rows and columns as 4-byte little-endian signed integers
|
346
|
+
file.write(nbL.to_bytes(4, byteorder='little', signed=True))
|
347
|
+
file.write(nbC.to_bytes(4, byteorder='little', signed=True))
|
348
|
+
|
349
|
+
# Write the data rows
|
350
|
+
for row in data:
|
351
|
+
if len(row) != nbC+1:
|
352
|
+
raise ValueError(f"Each row must have {nbC} values according to the format.")
|
353
|
+
binary_row = pack(format, *row)
|
354
|
+
file.write(binary_row)
|
355
|
+
|
356
|
+
|
357
|
+
def read_txt_file(path:str, fileName:str, sep:str="\t", header:int=2) -> tuple[np.array, np.array]:
|
358
|
+
"""
|
359
|
+
Read a text file and return the data as two numpy arrays.
|
360
|
+
|
361
|
+
Args:
|
362
|
+
path (str): The path to the text file.
|
363
|
+
fileName (str): The name of the text file.
|
364
|
+
sep (str): The separator used in the text file. Default is tab.
|
365
|
+
header (int): The number of header lines to skip. Default is 0.
|
366
|
+
|
367
|
+
Returns:
|
368
|
+
tuple: A tuple containing two numpy arrays: time and values.
|
369
|
+
"""
|
370
|
+
data = np.loadtxt(os.path.join(path, fileName), delimiter=sep, skiprows=header)
|
371
|
+
# time = data[:, :-1]
|
372
|
+
# values = data[:, -1]
|
373
|
+
|
374
|
+
return data
|
375
|
+
|
376
|
+
|
377
|
+
def write_txt_file(path:str, fileName:str, data:np.array, sep:str="\t", header:str=None, format:list=['%d']*6+['%.15f']) -> None:
|
378
|
+
|
379
|
+
if header is None:
|
380
|
+
header = f"{data.shape[0]:d}\n{data.shape[1]:d}"
|
381
|
+
|
382
|
+
full_name = os.path.join(path, fileName)
|
383
|
+
np.savetxt(full_name, data, header=header, fmt=format, comments='',delimiter=sep)
|