wolfhece 2.1.13__py3-none-any.whl → 2.1.14__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/PyDraw.py +19 -2
- wolfhece/PyGui.py +27 -4
- wolfhece/PyParams.py +1 -1
- wolfhece/PyVertexvectors.py +14 -0
- wolfhece/apps/version.py +1 -1
- wolfhece/fonts/arial.ttf +0 -0
- wolfhece/hydrology/Catchment.py +87 -3
- wolfhece/hydrology/Comparison.py +46 -7
- wolfhece/hydrology/Optimisation.py +63 -4
- wolfhece/hydrology/PyWatershed.py +552 -14
- wolfhece/hydrology/RetentionBasin.py +246 -41
- wolfhece/hydrology/SubBasin.py +42 -9
- wolfhece/hydrology/plot_hydrology.py +58 -0
- wolfhece/libs/WolfDll.dll +0 -0
- wolfhece/picc.py +35 -4
- wolfhece/wolf_array.py +152 -30
- {wolfhece-2.1.13.dist-info → wolfhece-2.1.14.dist-info}/METADATA +1 -1
- {wolfhece-2.1.13.dist-info → wolfhece-2.1.14.dist-info}/RECORD +21 -21
- {wolfhece-2.1.13.dist-info → wolfhece-2.1.14.dist-info}/WHEEL +1 -1
- {wolfhece-2.1.13.dist-info → wolfhece-2.1.14.dist-info}/entry_points.txt +0 -0
- {wolfhece-2.1.13.dist-info → wolfhece-2.1.14.dist-info}/top_level.txt +0 -0
@@ -6,9 +6,11 @@ from matplotlib.axes import Axes
|
|
6
6
|
import matplotlib.pyplot as plt
|
7
7
|
import logging
|
8
8
|
from tqdm import tqdm
|
9
|
+
import numpy as np
|
9
10
|
|
10
11
|
from ..PyTranslate import _
|
11
12
|
from ..PyVertex import wolfvertex, cloud_vertices
|
13
|
+
from ..PyVertexvectors import Zones, zone, vector
|
12
14
|
from ..wolf_array import *
|
13
15
|
from ..PyCrosssections import crosssections as CrossSections
|
14
16
|
from ..GraphNotebook import PlotNotebook
|
@@ -57,6 +59,11 @@ class Node_Watershed:
|
|
57
59
|
|
58
60
|
flatindex:int = -1 # index de la zone de plat
|
59
61
|
|
62
|
+
def __init__(self):
|
63
|
+
self.cums=0.
|
64
|
+
self.up=None
|
65
|
+
self.down=None
|
66
|
+
|
60
67
|
def incr_curvi(self):
|
61
68
|
"""Incrémentation de la longueur curviligne"""
|
62
69
|
|
@@ -64,8 +71,7 @@ class Node_Watershed:
|
|
64
71
|
self.cums=0.
|
65
72
|
else:
|
66
73
|
self.cums = self.down.cums+self.incrs
|
67
|
-
|
68
|
-
for curup in self.up:
|
74
|
+
for curup in self.up:
|
69
75
|
curup.incr_curvi()
|
70
76
|
|
71
77
|
def mean_slope_up(self, threshold:float)-> float:
|
@@ -119,6 +125,95 @@ class Node_Watershed:
|
|
119
125
|
"""
|
120
126
|
self.strahler = strahler
|
121
127
|
|
128
|
+
def distance(self, x:float, y:float) -> float:
|
129
|
+
""" Distance euclidienne """
|
130
|
+
|
131
|
+
return np.sqrt(pow(self.x-x,2)+pow(self.y-y,2))
|
132
|
+
|
133
|
+
def get_up_nodes_same_sub(self, excluded_node:list["Node_Watershed"]=[]):
|
134
|
+
"""
|
135
|
+
Get all upstream nodes in the same sub-basin
|
136
|
+
"""
|
137
|
+
|
138
|
+
all_up = [self]
|
139
|
+
all_rivers = [self] if self.river else []
|
140
|
+
all_runoff = [] if self.river else [self]
|
141
|
+
|
142
|
+
for curup in self.up:
|
143
|
+
|
144
|
+
if curup in excluded_node:
|
145
|
+
continue
|
146
|
+
|
147
|
+
added = False
|
148
|
+
if curup.sub == self.sub:
|
149
|
+
all_up.append(curup)
|
150
|
+
added = True
|
151
|
+
|
152
|
+
if curup.river:
|
153
|
+
all_rivers.append(curup)
|
154
|
+
else:
|
155
|
+
all_runoff.append(curup)
|
156
|
+
|
157
|
+
if added:
|
158
|
+
up, river, runoff = curup.get_up_nodes_same_sub(excluded_node)
|
159
|
+
all_up.extend(up)
|
160
|
+
all_rivers.extend(river)
|
161
|
+
all_runoff.extend(runoff)
|
162
|
+
|
163
|
+
return all_up, all_rivers, all_runoff
|
164
|
+
|
165
|
+
def get_up_runoff_nodes_same_sub(self):
|
166
|
+
"""
|
167
|
+
Get all upstream runoff nodes in the same sub-basin
|
168
|
+
"""
|
169
|
+
|
170
|
+
all_up = []
|
171
|
+
for curup in self.up:
|
172
|
+
if curup.sub == self.sub and not curup.river:
|
173
|
+
all_up.append(curup)
|
174
|
+
|
175
|
+
all_up += curup.get_up_runoff_nodes_same_sub()
|
176
|
+
|
177
|
+
return all_up
|
178
|
+
|
179
|
+
def get_up_rivernodes_same_sub(self):
|
180
|
+
"""
|
181
|
+
Get all upstream river nodes in the same sub-basin
|
182
|
+
"""
|
183
|
+
|
184
|
+
all_up = []
|
185
|
+
for curup in self.upriver:
|
186
|
+
if curup.sub == self.sub:
|
187
|
+
all_up.append(curup)
|
188
|
+
|
189
|
+
all_up += curup.get_up_rivernodes_same_sub()
|
190
|
+
|
191
|
+
return all_up
|
192
|
+
|
193
|
+
def get_up_reaches_same_sub(self) -> list[int]:
|
194
|
+
"""
|
195
|
+
Get all upstream reaches in the same sub-basin
|
196
|
+
"""
|
197
|
+
|
198
|
+
all_up = [self.reach]
|
199
|
+
for curup in self.upriver:
|
200
|
+
if curup.sub == self.sub:
|
201
|
+
all_up += curup.get_up_reaches_same_sub()
|
202
|
+
|
203
|
+
return np.unique(all_up).tolist()
|
204
|
+
|
205
|
+
def get_down_reaches_same_sub(self) -> list[int]:
|
206
|
+
"""
|
207
|
+
Get all downstream reaches in the same sub-basin
|
208
|
+
"""
|
209
|
+
|
210
|
+
all_down = [self.reach]
|
211
|
+
if self.down is not None:
|
212
|
+
if self.down.sub == self.sub:
|
213
|
+
all_down += self.down.get_down_reaches_same_sub()
|
214
|
+
|
215
|
+
return np.unique(all_down).tolist()
|
216
|
+
|
122
217
|
|
123
218
|
class RiverSystem:
|
124
219
|
"""
|
@@ -129,8 +224,10 @@ class RiverSystem:
|
|
129
224
|
# reaches
|
130
225
|
# |__['reaches']
|
131
226
|
# | |__[idx]
|
132
|
-
# | |__['upstream']
|
133
|
-
# | |__['baselist']
|
227
|
+
# | |__['upstream'] # all reaches in upstream
|
228
|
+
# | |__['baselist'] # list of nodes in the reach
|
229
|
+
# | |__['up'] # **if upstream** node in upstream
|
230
|
+
# | |__['fromuptodown'] # **if upstream** list of nodes from upstream to downstream
|
134
231
|
# |__['indexed']
|
135
232
|
# |__['strahler']
|
136
233
|
|
@@ -205,7 +302,7 @@ class RiverSystem:
|
|
205
302
|
xy = [[curnode.x, curnode.y] for curnode in nodes]
|
206
303
|
self.kdtree = KDTree(xy)
|
207
304
|
|
208
|
-
def get_nearest_nodes(self, xy:np.ndarray, nb=
|
305
|
+
def get_nearest_nodes(self, xy:np.ndarray | vector, nb:int = 1) -> tuple[np.ndarray | float, list[Node_Watershed] | Node_Watershed]:
|
209
306
|
"""
|
210
307
|
Return the distance and the nearest Node_Watershed
|
211
308
|
|
@@ -214,10 +311,105 @@ class RiverSystem:
|
|
214
311
|
|
215
312
|
return
|
216
313
|
"""
|
314
|
+
|
315
|
+
if isinstance(xy, vector):
|
316
|
+
centroid = xy.asshapely_pol().centroid
|
317
|
+
xy = np.array([[centroid.x, centroid.y]])
|
318
|
+
|
217
319
|
dd, ii = self.kdtree.query(xy, nb)
|
218
320
|
|
219
|
-
|
321
|
+
if isinstance(ii, int | np.int64):
|
322
|
+
return dd, self.all_nodes[ii]
|
323
|
+
elif isinstance(ii, np.ndarray | list):
|
324
|
+
if len(ii) == 1:
|
325
|
+
return dd[0], self.all_nodes[ii[0]]
|
326
|
+
else:
|
327
|
+
return dd, [self.all_nodes[curi] for curi in ii]
|
328
|
+
|
329
|
+
def get_nodes_in_reaches(self, reaches:list[int])->list[Node_Watershed]:
|
330
|
+
"""
|
331
|
+
Get nodes in a reaches
|
332
|
+
"""
|
333
|
+
all_nodes = []
|
334
|
+
for cur_reach in reaches:
|
335
|
+
all_nodes.extend(self.reaches['reaches'][cur_reach]['baselist'])
|
336
|
+
|
337
|
+
return all_nodes
|
338
|
+
|
339
|
+
def get_downstream_node_in_reach(self, reach:int)->Node_Watershed:
|
340
|
+
"""
|
341
|
+
Get downstream node in a reach
|
342
|
+
"""
|
343
|
+
|
344
|
+
return self.reaches['reaches'][reach]['baselist'][0]
|
345
|
+
|
346
|
+
def get_upstream_node_in_reach(self, reach:int)->Node_Watershed:
|
347
|
+
"""
|
348
|
+
Get upstream node in a reach
|
349
|
+
"""
|
350
|
+
|
351
|
+
return self.reaches['reaches'][reach]['baselist'][-1]
|
352
|
+
|
353
|
+
def get_downstream_reaches(self, node:Node_Watershed)->list[int]:
|
354
|
+
"""
|
355
|
+
Get index of downstream reaches
|
356
|
+
"""
|
220
357
|
|
358
|
+
curnode = node
|
359
|
+
downreaches = []
|
360
|
+
while not curnode is None:
|
361
|
+
downreaches.append(curnode.reach)
|
362
|
+
curnode = curnode.down
|
363
|
+
|
364
|
+
return list(np.unique(downreaches))
|
365
|
+
|
366
|
+
def get_kdtree_downstream(self, node:Node_Watershed)-> tuple[list[Node_Watershed], KDTree]:
|
367
|
+
"""
|
368
|
+
Get KDTree of downstream reaches
|
369
|
+
"""
|
370
|
+
|
371
|
+
downreaches = self.get_downstream_reaches(node)
|
372
|
+
return self.get_kdtree_from_reaches(downreaches)
|
373
|
+
|
374
|
+
def get_kdtree_from_reaches(self, reaches:list[int])->tuple[list[Node_Watershed], KDTree]:
|
375
|
+
"""
|
376
|
+
Get KDTree from a list of reaches
|
377
|
+
"""
|
378
|
+
|
379
|
+
nodes = self.get_nodes_in_reaches(reaches)
|
380
|
+
xy = [[curnode.x, curnode.y] for curnode in nodes]
|
381
|
+
return nodes, KDTree(xy)
|
382
|
+
|
383
|
+
def get_downstream_reaches_excluded(self, node:Node_Watershed, excluded:list[int])->list[int]:
|
384
|
+
"""
|
385
|
+
Get index of downstream reaches, excepted the excluded ones
|
386
|
+
"""
|
387
|
+
|
388
|
+
list_reaches = self.get_downstream_reaches(node)
|
389
|
+
|
390
|
+
for cur in excluded:
|
391
|
+
if cur in list_reaches:
|
392
|
+
list_reaches.remove(cur)
|
393
|
+
|
394
|
+
return list_reaches
|
395
|
+
|
396
|
+
def go_downstream_until_reach_found(self, node:Node_Watershed, reach:int | list[int])->Node_Watershed:
|
397
|
+
""" Go downstream until a reach is found """
|
398
|
+
|
399
|
+
curnode = node
|
400
|
+
if isinstance(reach, int):
|
401
|
+
while not curnode is None:
|
402
|
+
if curnode.reach == reach:
|
403
|
+
break
|
404
|
+
curnode = curnode.down
|
405
|
+
elif isinstance(reach, list):
|
406
|
+
while not curnode is None:
|
407
|
+
if curnode.reach in reach:
|
408
|
+
break
|
409
|
+
curnode = curnode.down
|
410
|
+
|
411
|
+
return curnode
|
412
|
+
|
221
413
|
def get_cums(self, whichreach:int=None, whichup:int=None):
|
222
414
|
"""
|
223
415
|
Récupération de la position curvi
|
@@ -298,6 +490,25 @@ class RiverSystem:
|
|
298
490
|
|
299
491
|
return slope
|
300
492
|
|
493
|
+
def get_upstreams_coords(self):
|
494
|
+
"""
|
495
|
+
Récupération des coordonnées des amonts
|
496
|
+
"""
|
497
|
+
|
498
|
+
xy = [[curnode.x, curnode.y] for curnode in self.upstreams['list']]
|
499
|
+
return np.array(xy)
|
500
|
+
|
501
|
+
def get_nearest_upstream(self, xy:np.ndarray, nb:int) -> tuple[np.ndarray, list[Node_Watershed]]:
|
502
|
+
"""
|
503
|
+
Recherche des amonts les plus proches
|
504
|
+
"""
|
505
|
+
|
506
|
+
xy_up = self.get_upstreams_coords()
|
507
|
+
loc_kd = KDTree(xy_up)
|
508
|
+
dd, ii =loc_kd.query(xy, nb)
|
509
|
+
|
510
|
+
return dd, [self.upstreams['list'][curi] for curi in ii]
|
511
|
+
|
301
512
|
def create_index(self):
|
302
513
|
"""
|
303
514
|
Incrément d'index depuis l'amont jusque l'exutoire final
|
@@ -1132,19 +1343,222 @@ class SubWatershed:
|
|
1132
1343
|
runoff:list[Node_Watershed],
|
1133
1344
|
rivers:list[Node_Watershed]) -> None:
|
1134
1345
|
|
1135
|
-
self.parent = parent
|
1136
|
-
self.index
|
1137
|
-
self.name
|
1138
|
-
self.mask
|
1139
|
-
self.
|
1140
|
-
|
1141
|
-
self.
|
1346
|
+
self.parent:"Watershed" = parent
|
1347
|
+
self.index:int = idx # index of subwatershed - **NOT** sorted like in the array
|
1348
|
+
self.name:str = name # name of the subwatershed
|
1349
|
+
self.mask:WolfArray = mask # WolfArray of the subwatershed -- All nodes are masked except the subwatershed
|
1350
|
+
self.mask.count()
|
1351
|
+
|
1352
|
+
self.nodes:list[Node_Watershed] = nodes # all nodes in the subwatershed
|
1353
|
+
self.rivers:list[Node_Watershed] = rivers # only rivers - sorted by dem value --> outlet is the first one
|
1354
|
+
self.runoff:list[Node_Watershed] = runoff
|
1355
|
+
|
1142
1356
|
self.idx_reaches = np.unique(np.asarray([x.reach for x in rivers]))
|
1143
1357
|
|
1358
|
+
self._index_sorted = idx
|
1359
|
+
|
1360
|
+
self._is_virtual:bool = False
|
1361
|
+
self._src_sub:"SubWatershed" = None
|
1362
|
+
|
1363
|
+
@property
|
1364
|
+
def is_virtual(self) -> bool:
|
1365
|
+
""" Vérification si le sous-bassin est virtuel """
|
1366
|
+
|
1367
|
+
return self._is_virtual
|
1368
|
+
|
1144
1369
|
@property
|
1145
1370
|
def surface(self) -> float:
|
1371
|
+
""" Surface du bassin versant en m² """
|
1146
1372
|
return self.mask.nbnotnull * self.mask.dx * self.mask.dy
|
1373
|
+
|
1374
|
+
@property
|
1375
|
+
def area(self) -> float:
|
1376
|
+
""" Surface du bassin versant en km² """
|
1377
|
+
return self.surface / 1.e6
|
1378
|
+
|
1379
|
+
@property
|
1380
|
+
def area_outlet(self) -> float:
|
1381
|
+
""" Surface du bassin à l'exutoire """
|
1382
|
+
|
1383
|
+
return self.outlet.uparea
|
1384
|
+
|
1385
|
+
@property
|
1386
|
+
def outlet(self) -> Node_Watershed:
|
1387
|
+
""" Outlet of the subbasin """
|
1388
|
+
|
1389
|
+
return self.rivers[0]
|
1390
|
+
|
1391
|
+
def is_reach_in_sub(self, idx_reach:int) -> bool:
|
1392
|
+
""" Vérification si un bief est dans le sous-bassin """
|
1393
|
+
|
1394
|
+
return idx_reach in self.idx_reaches
|
1395
|
+
|
1396
|
+
def is_in_rivers(self, node:Node_Watershed) -> bool:
|
1397
|
+
""" Vérification si un noeud est dans les rivières """
|
1398
|
+
|
1399
|
+
return node in self.rivers
|
1400
|
+
|
1401
|
+
def get_list_nodes_river(self, idx_reach:int) -> list[Node_Watershed]:
|
1402
|
+
"""
|
1403
|
+
Récupération des noeuds d'une rivière
|
1404
|
+
"""
|
1405
|
+
|
1406
|
+
return [x for x in self.rivers if x.reach==idx_reach]
|
1407
|
+
|
1408
|
+
def get_nearest_river(self, x, y) -> Node_Watershed:
|
1409
|
+
"""
|
1410
|
+
Récupération du noeud de rivière le plus proche
|
1411
|
+
"""
|
1412
|
+
|
1413
|
+
return min(self.rivers, key=lambda x: x.distance(x,y))
|
1414
|
+
|
1415
|
+
def get_max_area_in_reach(self, idx_reach:int) -> float:
|
1416
|
+
"""
|
1417
|
+
Récupération de la surface maximale dans un bief
|
1418
|
+
"""
|
1419
|
+
|
1420
|
+
return max([x.uparea for x in self.get_list_nodes_river(idx_reach)])
|
1421
|
+
|
1422
|
+
def get_min_area_in_reach(self, idx_reach:int) -> float:
|
1423
|
+
"""
|
1424
|
+
Récupération de la surface minimale dans un bief
|
1425
|
+
"""
|
1426
|
+
|
1427
|
+
return min([x.uparea for x in self.get_list_nodes_river(idx_reach)])
|
1428
|
+
|
1429
|
+
def get_min_area_along_reaches(self, reaches:list[int], starting_node:Node_Watershed = None) -> float:
|
1430
|
+
""" Aire drainée à la limite amont du ss-bassin """
|
1431
|
+
|
1432
|
+
if starting_node is None:
|
1433
|
+
starting_node = self.outlet
|
1434
|
+
|
1435
|
+
upriver = starting_node.upriver
|
1436
|
+
|
1437
|
+
area_min = []
|
1438
|
+
|
1439
|
+
idx_reach = 0
|
1440
|
+
for curnode in upriver:
|
1441
|
+
if curnode.reach in reaches:
|
1442
|
+
idx_reach = curnode.reach
|
1443
|
+
area_min.append(self.get_min_area_in_reach(idx_reach))
|
1444
|
+
|
1445
|
+
if len(area_min) == 0:
|
1446
|
+
return None
|
1447
|
+
else:
|
1448
|
+
return min(area_min)
|
1449
|
+
|
1450
|
+
def get_up_rivernode_outside_sub(self, starting_node:Node_Watershed, reaches:list[int]) -> Node_Watershed:
|
1451
|
+
""" Récupération du noeud de rivière en amont du sous-bassin """
|
1452
|
+
|
1453
|
+
def up_in_reaches(node:Node_Watershed, reaches:list[int]) -> Node_Watershed:
|
1454
|
+
|
1455
|
+
if len(node.upriver) == 0:
|
1456
|
+
# No upstream node
|
1457
|
+
return node
|
1458
|
+
else:
|
1459
|
+
# Iterate over upriver nodes
|
1460
|
+
for curup in node.upriver:
|
1461
|
+
if curup.reach in reaches:
|
1462
|
+
# If the reach is in the list, return the node
|
1463
|
+
return curup
|
1464
|
+
|
1465
|
+
# No node found in the list of reaches
|
1466
|
+
return node
|
1467
|
+
|
1468
|
+
if self._is_virtual:
|
1469
|
+
return self._src_sub.get_up_rivernode_outside_sub(starting_node, reaches)
|
1470
|
+
|
1471
|
+
up = up_in_reaches(starting_node, reaches)
|
1472
|
+
loc_up = None
|
1473
|
+
while up is not starting_node and up.sub == starting_node.sub and up is not loc_up:
|
1474
|
+
# bouclage parfois utile en fonction de la superposition ou non du tracé
|
1475
|
+
# vectoriel de lit mineur vis-à-vis de la discrétsation hydrologique
|
1476
|
+
loc_up = up
|
1477
|
+
up = up_in_reaches(up, reaches)
|
1478
|
+
|
1479
|
+
return up if up is not loc_up else None
|
1480
|
+
|
1481
|
+
def get_area_outside_sub_if_exists(self, starting_node:Node_Watershed, reaches:list[int]) -> float:
|
1482
|
+
""" Aire drainée en amont du sous-bassin """
|
1483
|
+
|
1484
|
+
up_outside = self.get_up_rivernode_outside_sub(starting_node, reaches)
|
1485
|
+
|
1486
|
+
if up_outside is None:
|
1487
|
+
return 0.
|
1488
|
+
else:
|
1489
|
+
return up_outside.uparea
|
1490
|
+
|
1491
|
+
def get_river_nodes_from_upareas(self, min_area:float, max_area:float) -> list[Node_Watershed]:
|
1492
|
+
"""
|
1493
|
+
Récupération des noeuds de rivière entre deux surfacesde BV.
|
1494
|
+
|
1495
|
+
Les surfaces sont exprimées en km².
|
1496
|
+
|
1497
|
+
Les bornes sont incluses.
|
1498
|
+
|
1499
|
+
:param min_area: surface minimale
|
1500
|
+
:param max_area: surface maximale
|
1501
|
+
"""
|
1147
1502
|
|
1503
|
+
return [x for x in self.rivers if x.uparea>=min_area and x.uparea<=max_area]
|
1504
|
+
|
1505
|
+
def is_in_subwatershed(self, vec:vector) -> bool:
|
1506
|
+
""" Vérification si un vecteur est dans le sous-bassin """
|
1507
|
+
|
1508
|
+
centroid = vec.asshapely_pol().centroid
|
1509
|
+
|
1510
|
+
i, j = self.mask.get_ij_from_xy(centroid.x, centroid.y)
|
1511
|
+
|
1512
|
+
return self.mask.array.mask[i,j] == False
|
1513
|
+
|
1514
|
+
def filter_zones(self, zones_to_filter:Zones, force_virtual_if_any:bool = False) -> list[vector]:
|
1515
|
+
"""
|
1516
|
+
Filtrage des zones pour ne garder que celle se trouvant dans le sous-bassin
|
1517
|
+
"""
|
1518
|
+
|
1519
|
+
if self._is_virtual and not force_virtual_if_any:
|
1520
|
+
return self._src_sub.filter_zones(zones_to_filter)
|
1521
|
+
|
1522
|
+
return [curvec for curzone in zones_to_filter.myzones for curvec in curzone.myvectors if self.is_in_subwatershed(curvec)]
|
1523
|
+
|
1524
|
+
def get_virtual_subwatershed(self, outlet:Node_Watershed, excluded_nodes:list[Node_Watershed] = []) -> "SubWatershed":
|
1525
|
+
"""
|
1526
|
+
Création d'un sous-bassin virtuel sur base d'une maille rivière aval
|
1527
|
+
"""
|
1528
|
+
|
1529
|
+
if not outlet.river:
|
1530
|
+
logging.error(_('The outlet should be a river node'))
|
1531
|
+
return None
|
1532
|
+
|
1533
|
+
mymask = WolfArray(mold = self.mask)
|
1534
|
+
mymask.array.mask[:,:] = True
|
1535
|
+
|
1536
|
+
all, river, runoff = outlet.get_up_nodes_same_sub(excluded_nodes)
|
1537
|
+
|
1538
|
+
for curnode in all:
|
1539
|
+
mymask.array.mask[curnode.i,curnode.j] = False
|
1540
|
+
|
1541
|
+
newsub = SubWatershed(self.parent,
|
1542
|
+
self.name + '_virtual',
|
1543
|
+
self.parent.nb_subs + 1,
|
1544
|
+
mymask,
|
1545
|
+
all,
|
1546
|
+
runoff,
|
1547
|
+
river,
|
1548
|
+
)
|
1549
|
+
|
1550
|
+
newsub._is_virtual = True
|
1551
|
+
newsub._src_sub = self
|
1552
|
+
|
1553
|
+
return newsub
|
1554
|
+
|
1555
|
+
def get_downstream_node_in_reach(self, reach:int) -> Node_Watershed:
|
1556
|
+
"""
|
1557
|
+
Récupération du noeud aval dans un bief
|
1558
|
+
"""
|
1559
|
+
|
1560
|
+
# rivers are sorted by dem value, so the first one is the outlet
|
1561
|
+
return self.get_list_nodes_river(reach)[0]
|
1148
1562
|
class Watershed:
|
1149
1563
|
"""Classe bassin versant"""
|
1150
1564
|
|
@@ -1164,6 +1578,7 @@ class Watershed:
|
|
1164
1578
|
couplednodes:list # forced exchanges
|
1165
1579
|
|
1166
1580
|
subcatchments: list[SubWatershed]
|
1581
|
+
virtualcatchments : list[SubWatershed]
|
1167
1582
|
statisticss: dict
|
1168
1583
|
|
1169
1584
|
couplednodesxy:list[float,float,float,float]
|
@@ -1188,6 +1603,8 @@ class Watershed:
|
|
1188
1603
|
dir_mnt_subpixels:str=None,
|
1189
1604
|
*args, **kwargs):
|
1190
1605
|
|
1606
|
+
self.virtualcatchments = []
|
1607
|
+
|
1191
1608
|
logging.info(_('Read files...'))
|
1192
1609
|
|
1193
1610
|
self.dir=os.path.normpath(dir)
|
@@ -1209,7 +1626,11 @@ class Watershed:
|
|
1209
1626
|
|
1210
1627
|
if lines[0]=='COORDINATES':
|
1211
1628
|
for xy in enumerate(lines[1:]):
|
1212
|
-
|
1629
|
+
xy_split = xy[1].split('\t')
|
1630
|
+
if len(xy_split)==4:
|
1631
|
+
xup,yup,xdown,ydown=xy_split
|
1632
|
+
else:
|
1633
|
+
xup,yup,xdown,ydown=xy_split[:4]
|
1213
1634
|
self.couplednodesxy.append([float(xup),float(yup),float(xdown),float(ydown)])
|
1214
1635
|
self.couplednodesij.append([self.subs_array.get_ij_from_xy(float(xup),float(yup)),self.subs_array.get_ij_from_xy(float(xdown),float(ydown))])
|
1215
1636
|
|
@@ -1258,6 +1679,71 @@ class Watershed:
|
|
1258
1679
|
@property
|
1259
1680
|
def resolution(self):
|
1260
1681
|
return self.header.dx
|
1682
|
+
|
1683
|
+
# def impose_sorted_index_subbasins(self, new_idx=list[int]):
|
1684
|
+
# """
|
1685
|
+
# Tri des sous-bassins
|
1686
|
+
# """
|
1687
|
+
|
1688
|
+
# for cursub in self.subcatchments:
|
1689
|
+
# cursub._index_sorted = new_idx[cursub.index]
|
1690
|
+
|
1691
|
+
def set_names_subbasins(self, new_names:list[tuple[int,str]]):
|
1692
|
+
"""
|
1693
|
+
Renommage des sous-bassins
|
1694
|
+
"""
|
1695
|
+
|
1696
|
+
for cursub, curname in new_names:
|
1697
|
+
self.get_subwatershed(cursub).name = curname
|
1698
|
+
|
1699
|
+
def add_virtual_subwatershed(self, subwater:SubWatershed):
|
1700
|
+
"""
|
1701
|
+
Ajout d'un sous-bassin virtuel
|
1702
|
+
"""
|
1703
|
+
|
1704
|
+
self.virtualcatchments.append(subwater)
|
1705
|
+
|
1706
|
+
subwater.name += str(len(self.virtualcatchments))
|
1707
|
+
|
1708
|
+
def create_virtual_subwatershed(self, outlet:Node_Watershed, excluded_nodes:list[Node_Watershed] = []):
|
1709
|
+
"""
|
1710
|
+
Création d'un sous-bassin virtuel
|
1711
|
+
"""
|
1712
|
+
|
1713
|
+
newsub = self.get_subwatershed(outlet.sub).get_virtual_subwatershed(outlet, excluded_nodes=excluded_nodes)
|
1714
|
+
|
1715
|
+
self.add_virtual_subwatershed(newsub)
|
1716
|
+
|
1717
|
+
return newsub
|
1718
|
+
|
1719
|
+
def get_subwatershed(self, idx_sorted_or_name:int | str) -> SubWatershed:
|
1720
|
+
"""
|
1721
|
+
Récupération d'un sous-bassin sur base de l'index trié
|
1722
|
+
"""
|
1723
|
+
|
1724
|
+
if isinstance(idx_sorted_or_name, str):
|
1725
|
+
for cur_sub in self.subcatchments:
|
1726
|
+
if cur_sub.name == idx_sorted_or_name:
|
1727
|
+
return cur_sub
|
1728
|
+
|
1729
|
+
if len(self.virtualcatchments)>0:
|
1730
|
+
for cur_sub in self.virtualcatchments:
|
1731
|
+
if cur_sub.name == idx_sorted_or_name:
|
1732
|
+
return cur_sub
|
1733
|
+
|
1734
|
+
elif isinstance(idx_sorted_or_name, int):
|
1735
|
+
for cur_sub in self.subcatchments:
|
1736
|
+
if cur_sub._index_sorted+1 == idx_sorted_or_name:
|
1737
|
+
return cur_sub
|
1738
|
+
|
1739
|
+
if len(self.virtualcatchments)>0:
|
1740
|
+
for cur_sub in self.virtualcatchments:
|
1741
|
+
if cur_sub._index_sorted+1 == idx_sorted_or_name:
|
1742
|
+
return cur_sub
|
1743
|
+
else:
|
1744
|
+
logging.error(_('Index must be an integer or a string!'))
|
1745
|
+
|
1746
|
+
return None
|
1261
1747
|
|
1262
1748
|
def get_node_from_ij(self, i:int,j:int):
|
1263
1749
|
"""
|
@@ -1498,6 +1984,8 @@ class Watershed:
|
|
1498
1984
|
|
1499
1985
|
self.rivers=list(filter(lambda x: x.river,self.nodes))
|
1500
1986
|
self.rivers.sort(key=lambda x: x.dem['dem_after_corr'])
|
1987
|
+
|
1988
|
+
# FIXME : Caution the following iterative function can induce a RecursionError in Debug for bigger systems
|
1501
1989
|
sys.setrecursionlimit(len(self.nodes))
|
1502
1990
|
self.outlet.incr_curvi()
|
1503
1991
|
|
@@ -1980,3 +2468,53 @@ class Watershed:
|
|
1980
2468
|
cur_node.time = wolf_time[cur_node.i, cur_node.j]
|
1981
2469
|
|
1982
2470
|
self.to_update_times = False
|
2471
|
+
|
2472
|
+
def get_subwatershed_from_ij(self, i:int, j:int) -> SubWatershed:
|
2473
|
+
"""
|
2474
|
+
Récupération d'un sous-bassin sur base des indices (i,j)
|
2475
|
+
|
2476
|
+
:return: SubWatershed : sous-bassin or None
|
2477
|
+
"""
|
2478
|
+
|
2479
|
+
if self.subs_array.array.mask[i,j]:
|
2480
|
+
return None
|
2481
|
+
|
2482
|
+
idx_sub = self.subs_array.array[i,j]
|
2483
|
+
|
2484
|
+
return self.subcatchments[idx_sub-1]
|
2485
|
+
|
2486
|
+
def get_subwatershed_from_xy(self, x:float, y:float) -> SubWatershed:
|
2487
|
+
"""
|
2488
|
+
Récupération d'un sous-bassin sur base des coordonnées (x,y)
|
2489
|
+
|
2490
|
+
:return: SubWatershed : sous-bassin or None
|
2491
|
+
"""
|
2492
|
+
|
2493
|
+
i,j = self.header.get_ij_from_xy(x,y)
|
2494
|
+
return self.get_subwatershed_from_ij(i,j)
|
2495
|
+
|
2496
|
+
def get_subwatershed_from_vector(self, vec:vector) -> tuple[SubWatershed, bool, list[SubWatershed]]:
|
2497
|
+
"""
|
2498
|
+
Récupération d'un sous-bassin sur base d'un vecteur.
|
2499
|
+
|
2500
|
+
Recherche sur base du centroid du vecteur
|
2501
|
+
|
2502
|
+
:param vec: vecteur
|
2503
|
+
|
2504
|
+
:return: tuple(SubWatershed, bool, list[SubWatershed]) : sous-bassin, entièrement dans le sous-bassin, autres sous-bassins
|
2505
|
+
"""
|
2506
|
+
|
2507
|
+
centroid = vec.asshapely_pol().centroid
|
2508
|
+
|
2509
|
+
sub = self.get_subwatershed_from_xy(centroid.x, centroid.y)
|
2510
|
+
|
2511
|
+
entirely = True
|
2512
|
+
others = []
|
2513
|
+
for curvert in vec.myvertices:
|
2514
|
+
cursub = self.get_subwatershed_from_xy(curvert.x, curvert.y)
|
2515
|
+
if cursub is not None and cursub != sub:
|
2516
|
+
logging.warning(_('The vector is not entirely in the same subcatchment'))
|
2517
|
+
entirely = False
|
2518
|
+
others.append(cursub)
|
2519
|
+
|
2520
|
+
return sub, entirely, others
|