wolfhece 2.2.37__py3-none-any.whl → 2.2.39__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.
Files changed (53) hide show
  1. wolfhece/Coordinates_operations.py +5 -0
  2. wolfhece/GraphNotebook.py +72 -1
  3. wolfhece/GraphProfile.py +1 -1
  4. wolfhece/MulticriteriAnalysis.py +1579 -0
  5. wolfhece/PandasGrid.py +62 -1
  6. wolfhece/PyCrosssections.py +194 -43
  7. wolfhece/PyDraw.py +891 -73
  8. wolfhece/PyGui.py +913 -72
  9. wolfhece/PyGuiHydrology.py +528 -74
  10. wolfhece/PyPalette.py +26 -4
  11. wolfhece/PyParams.py +33 -0
  12. wolfhece/PyPictures.py +2 -2
  13. wolfhece/PyVertex.py +32 -0
  14. wolfhece/PyVertexvectors.py +147 -75
  15. wolfhece/PyWMS.py +52 -36
  16. wolfhece/acceptability/acceptability.py +15 -8
  17. wolfhece/acceptability/acceptability_gui.py +507 -360
  18. wolfhece/acceptability/func.py +80 -183
  19. wolfhece/apps/version.py +1 -1
  20. wolfhece/compare_series.py +480 -0
  21. wolfhece/drawing_obj.py +12 -1
  22. wolfhece/hydrology/Catchment.py +228 -162
  23. wolfhece/hydrology/Internal_variables.py +43 -2
  24. wolfhece/hydrology/Models_characteristics.py +69 -67
  25. wolfhece/hydrology/Optimisation.py +893 -182
  26. wolfhece/hydrology/PyWatershed.py +267 -165
  27. wolfhece/hydrology/SubBasin.py +185 -140
  28. wolfhece/hydrology/climate_data.py +334 -0
  29. wolfhece/hydrology/constant.py +11 -0
  30. wolfhece/hydrology/cst_exchanges.py +76 -1
  31. wolfhece/hydrology/forcedexchanges.py +413 -49
  32. wolfhece/hydrology/hyetograms.py +2095 -0
  33. wolfhece/hydrology/read.py +65 -5
  34. wolfhece/hydrometry/kiwis.py +42 -26
  35. wolfhece/hydrometry/kiwis_gui.py +7 -2
  36. wolfhece/insyde_be/INBE_func.py +746 -0
  37. wolfhece/insyde_be/INBE_gui.py +1776 -0
  38. wolfhece/insyde_be/__init__.py +3 -0
  39. wolfhece/interpolating_raster.py +366 -0
  40. wolfhece/irm_alaro.py +1457 -0
  41. wolfhece/irm_qdf.py +889 -57
  42. wolfhece/lifewatch.py +6 -3
  43. wolfhece/picc.py +124 -8
  44. wolfhece/pyLandUseFlanders.py +146 -0
  45. wolfhece/pydownloader.py +2 -1
  46. wolfhece/pywalous.py +225 -31
  47. wolfhece/toolshydrology_dll.py +149 -0
  48. wolfhece/wolf_array.py +63 -25
  49. {wolfhece-2.2.37.dist-info → wolfhece-2.2.39.dist-info}/METADATA +3 -1
  50. {wolfhece-2.2.37.dist-info → wolfhece-2.2.39.dist-info}/RECORD +53 -42
  51. {wolfhece-2.2.37.dist-info → wolfhece-2.2.39.dist-info}/WHEEL +0 -0
  52. {wolfhece-2.2.37.dist-info → wolfhece-2.2.39.dist-info}/entry_points.txt +0 -0
  53. {wolfhece-2.2.37.dist-info → wolfhece-2.2.39.dist-info}/top_level.txt +0 -0
@@ -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
- def __init__(self,workingdir='') -> None:
22
-
23
- self.type='COORDINATES'
24
- self.mypairs=[]
25
- self.mycloudup=cloud_vertices()
26
- self.myclouddown=cloud_vertices()
27
-
28
- self.mysegs = Zones()
29
- self.myzone=zone(name='segments_fe')
30
- self.mysegs.add_zone(self.myzone)
31
-
32
- self.mycloudup.myprop.color=getIfromRGB((0,238,0))
33
- self.mycloudup.myprop.filled=True
34
- self.myclouddown.myprop.color=getIfromRGB((255,52,179))
35
- self.myclouddown.myprop.filled=True
36
-
37
- fname=path.join(workingdir,'Coupled_pairs.txt')
38
- if path.exists(fname):
39
- f=open(fname,'rt')
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
- f.close()
42
-
43
- self.type = content[0]
44
- idx=1
45
- for curline in content[1:]:
46
- coords=curline.split('\t')
47
- coords = [float(x) for x in coords]
48
- self.mypairs.append(coords)
49
-
50
- vert1 = wolfvertex(coords[0],coords[1])
51
- vert2 = wolfvertex(coords[2],coords[3])
52
-
53
- myseg = vector(name='fe'+str(idx))
54
- myseg.myprop.width = 2
55
- myseg.myprop.color = getIfromRGB((0,0,128))
56
- myseg.add_vertex([vert1,vert2])
57
- self.myzone.add_vector(myseg)
58
-
59
- self.mycloudup.add_vertex(vert1)
60
- self.myclouddown.add_vertex(vert2)
61
- idx+=1
62
-
63
- self.myzone.find_minmax(True)
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.mycloudup.plot()
67
- self.myclouddown.plot()
68
- self.mysegs.plot()
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
+