wolfhece 1.8.7__py3-none-any.whl → 1.8.9__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.
@@ -0,0 +1,1951 @@
1
+ import os
2
+ from scipy.interpolate import interpolate
3
+ from scipy.spatial import KDTree
4
+ from matplotlib import figure as mplfig
5
+ from matplotlib.axes import Axes
6
+ import matplotlib.pyplot as plt
7
+ import logging
8
+ from tqdm import tqdm
9
+
10
+ from ..PyTranslate import _
11
+ from ..PyVertex import wolfvertex, cloud_vertices
12
+ from ..wolf_array import *
13
+ from ..PyCrosssections import crosssections as CrossSections
14
+ from ..GraphNotebook import PlotNotebook
15
+ from .read import *
16
+
17
+ LISTDEM=['dem_before_corr','dem_after_corr','dem_10m','dem_20m','crosssection']
18
+ #LISTDEM=['dem_after_corr']
19
+
20
+ class Node_Watershed:
21
+ """Noeud du modèle hydrologique maillé"""
22
+ i:int #indice i dans la matrice
23
+ j:int #indice j dans la matrice
24
+
25
+ x:float # coordonnée X
26
+ y:float # coordonnée Y
27
+
28
+ index:int # Numérotation de la maille dans la liste de l'objet Watershed qui l'a initialisé
29
+
30
+ dem:dict[str,float] # dictionnaire des valeurs d'altitudes
31
+ demdelta:float # correction apportée dans la phase de prépro
32
+
33
+ crosssections:list # sections en travers sontenues dans la maille
34
+
35
+ time:float # temps de propagation - cf Drainage_basin.time
36
+ slope:float # pente calculée ne prépro
37
+ sloped8:float # pente selon les 8 voisins
38
+
39
+ slopecorr:dict # pente corrigée
40
+ demcorr:dict # dictionnaire d'alrtitude corrigées
41
+
42
+ river:bool # maille rivière => True
43
+ reach:int # index du bief
44
+ sub:int # inde du sous-bassin
45
+ forced:bool # maille d'échange forcé
46
+ uparea:float # surface drainée - cf Drainage_basin.cnv
47
+
48
+ strahler:int # indice de Strahler - cf "create_index"
49
+ reachlevel:int # Niveau du bief - cf "create_index"
50
+
51
+ cums:float # longueur curviligne cumulée **depuis l'aval** -- cf "incr_curvi"
52
+ incrs:float # incrément de longueur curvi - dx ou sqrt(2)*dx si voisin en crois ou en diagonale
53
+
54
+ down:"Node_Watershed" # pointeur vers le noeud aval
55
+ up:list["Node_Watershed"] # pointeurs vers le(s) noeud(s) amont
56
+ upriver:list["Node_Watershed"] # pointeurs vers le(s) noeud(s) **rivière** amont
57
+
58
+ flatindex:int = -1 # index de la zone de plat
59
+
60
+ def incr_curvi(self):
61
+ """Incrémentation de la longueur curviligne"""
62
+
63
+ if self.down is None:
64
+ self.cums=0.
65
+ else:
66
+ self.cums = self.down.cums+self.incrs
67
+
68
+ for curup in self.up:
69
+ curup.incr_curvi()
70
+
71
+ def mean_slope_up(self, threshold:float)-> float:
72
+ """Pente moyenne sur depuis les mailles amont"""
73
+ curnode: Node_Watershed
74
+ meanslope=0.
75
+ nbmean=0
76
+ for curnode in self.up:
77
+ if curnode.slope>threshold:
78
+ nbmean+=1.
79
+ meanslope+=curnode.slope
80
+ if nbmean>0:
81
+ meanslope=meanslope/nbmean
82
+
83
+ return meanslope
84
+
85
+ def slope_down(self, threshold:float)->float:
86
+ """
87
+ Recherche d'une pente supérieure à un seuil
88
+ Parcours vers l'aval
89
+ """
90
+ slopedown=0.
91
+ curnode=self
92
+ while curnode.slope < threshold:
93
+ if curnode.down is None:
94
+ break
95
+ curnode=curnode.down
96
+
97
+ slopedown = curnode.slope
98
+ return slopedown
99
+
100
+ def slope_upriver(self,threshold:float)->float:
101
+ """
102
+ Recherche d'une pente supérieure à un seuil
103
+ Parcours vers l'amont uniquement selon les rivières
104
+ """
105
+ slopeup=0.
106
+ if self.slope<threshold:
107
+ if len(self.upriver)>0:
108
+ slopeup=self.upriver[0].slope_upriver(threshold)
109
+ else:
110
+ slopeup=-1.
111
+ else:
112
+ slopeup = self.slope
113
+
114
+ return slopeup
115
+
116
+ def set_strahler(self, strahler:int):
117
+ """
118
+
119
+ """
120
+ self.strahler = strahler
121
+
122
+
123
+ class RiverSystem:
124
+ """
125
+ Classe du réseau de rivières d'un modèle hydrologique WOLF
126
+ """
127
+ nbreaches:int # nombre de biefs
128
+
129
+ # reaches
130
+ # |__['reaches']
131
+ # | |__[idx]
132
+ # | |__['upstream']
133
+ # | |__['baselist']
134
+ # |__['indexed']
135
+ # |__['strahler']
136
+
137
+ reaches:dict # dictionnaire des biefs
138
+
139
+ kdtree:KDTree # structure de recherche de voisinage
140
+
141
+ upmin:dict # cf slope_correctionmin
142
+ upmax:dict # cf slope_correctionmax
143
+
144
+ parent:"Watershed" # objet Watershed parent
145
+ upstreams:dict # dictionnaire des noeuds en amont
146
+
147
+ maxlevels:int # nombre total de niveaux
148
+ maxstrahler:int # indice de Strahler max
149
+
150
+ tslopemin:float =None # seuil de pente minimale
151
+ tslopemax:float =None # seuil de pente maximale
152
+
153
+ plotter:PlotNotebook = None # gestionnaire de graphiques
154
+ savedir:str # répertoire de sauvegarde
155
+
156
+ def __init__(self,
157
+ rivers:list[Node_Watershed],
158
+ parent:"Watershed",
159
+ thslopemin:float,
160
+ thslopemax:float,
161
+ savedir:str='',
162
+ computecorr:bool=False,
163
+ *args,
164
+ **kwargs):
165
+
166
+ self.savedir = savedir
167
+ self.parent = parent
168
+
169
+ self.all_nodes = rivers
170
+
171
+ self.init_kdtree(self.all_nodes)
172
+
173
+ self.nbreaches = max([x.reach for x in rivers])
174
+ self.reaches = {}
175
+ self.reaches['reaches'] = {}
176
+
177
+ self.upstreams = {}
178
+ self.upstreams['list'] = []
179
+
180
+ for curreach in range(1,self.nbreaches+1):
181
+ # attention numérotation 1-based
182
+ listreach, curup = parent.find_rivers(whichreach=curreach)
183
+
184
+ if len(curup.upriver) == 0:
185
+ # on est en tête de réseau
186
+ self.upstreams['list'].append(curup)
187
+
188
+ self.reaches['reaches'][curreach]={}
189
+ curdict=self.reaches['reaches'][curreach]
190
+ curdict['upstream']=curup
191
+ curdict['baselist']=listreach
192
+
193
+ self.create_index() # index et Strahler
194
+
195
+ if computecorr:
196
+ self.tslopemin=thslopemin
197
+ self.tslopemax=thslopemax
198
+ self.slope_correctionmin()
199
+ self.slope_correctionmax()
200
+
201
+ return super().__init__(*args, **kwargs)
202
+
203
+ def init_kdtree(self, nodes:list[Node_Watershed]):
204
+ """Create a KDTree structure from coordinates"""
205
+ xy = [[curnode.x, curnode.y] for curnode in nodes]
206
+ self.kdtree = KDTree(xy)
207
+
208
+ def get_nearest_nodes(self, xy:np.ndarray, nb=int) -> tuple[np.ndarray, list[Node_Watershed]]:
209
+ """
210
+ Return the distance and the nearest Node_Watershed
211
+
212
+ :param xy = np.ndarray - shape (n,2)
213
+ :param nb = number of neighbors
214
+
215
+ return
216
+ """
217
+ dd, ii = self.kdtree.query(xy, nb)
218
+
219
+ return dd, [self.all_nodes[curi] for curi in ii]
220
+
221
+ def get_cums(self, whichreach:int=None, whichup:int=None):
222
+ """
223
+ Récupération de la position curvi
224
+ """
225
+ curnode:Node_Watershed
226
+ if whichreach is not None:
227
+ nodeslist=self.reaches['reaches'][whichreach]['baselist']
228
+ x=[curnode.cums for curnode in nodeslist]
229
+ elif whichup is not None:
230
+ x=[]
231
+ curnode=self.upstreams['list'][whichup]
232
+ while curnode is not None:
233
+ x.append(curnode.cums)
234
+ curnode=curnode.down
235
+ else:
236
+ x=[]
237
+
238
+ return x
239
+
240
+ def get_dem(self, whichdem:str, whichreach:int=None, whichup:int=None):
241
+ """
242
+ Récupération de l'altitude pour une matrice spécifique
243
+ """
244
+ if whichreach is not None:
245
+ nodeslist=self.reaches['reaches'][whichreach]['baselist']
246
+ dem=[curnode.dem[whichdem] for curnode in nodeslist]
247
+ elif whichup is not None:
248
+ curnode:Node_Watershed
249
+ dem=[]
250
+ curnode=self.upstreams['list'][whichup]
251
+ while curnode is not None:
252
+ dem.append(curnode.dem[whichdem])
253
+ curnode=curnode.down
254
+ return dem
255
+
256
+ def get_dem_corr(self, whichdem:str, whichreach:int=None, whichup:int=None):
257
+ """
258
+ Récupération de l'altitude corrigée pour une matrice spécifique
259
+ """
260
+ if whichreach is not None:
261
+ nodeslist=self.reaches['reaches'][whichreach]['baselist']
262
+ dem=[curnode.demcorr[whichdem] for curnode in nodeslist]
263
+ elif whichup is not None:
264
+ curnode:Node_Watershed
265
+ dem=[]
266
+ curnode=self.upstreams['list'][whichup]
267
+ while curnode is not None:
268
+ dem.append(curnode.dem[whichdem])
269
+ curnode=curnode.down
270
+ return dem
271
+
272
+ def get_slope(self, whichslope:str=None, whichreach:int=None, whichup:int=None):
273
+ """
274
+ Récupération de la pente
275
+ """
276
+ if whichslope is None:
277
+ if whichreach is not None:
278
+ nodeslist=self.reaches['reaches'][whichreach]['baselist']
279
+ slope=[curnode.slope for curnode in nodeslist]
280
+ elif whichup is not None:
281
+ curnode:Node_Watershed
282
+ slope=[]
283
+ curnode=self.upstreams['list'][whichup]
284
+ while curnode is not None:
285
+ slope.append(curnode.slope)
286
+ curnode=curnode.down
287
+ else:
288
+ if whichreach is not None:
289
+ nodeslist=self.reaches['reaches'][whichreach]['baselist']
290
+ slope=[curnode.slopecorr[whichslope]['value'] for curnode in nodeslist]
291
+ elif whichup is not None:
292
+ curnode:Node_Watershed
293
+ slope=[]
294
+ curnode=self.upstreams['list'][whichup]
295
+ while curnode is not None:
296
+ slope.append(curnode.slopecorr[whichslope]['value'])
297
+ curnode=curnode.down
298
+
299
+ return slope
300
+
301
+ def create_index(self):
302
+ """
303
+ Incrément d'index depuis l'amont jusque l'exutoire final
304
+ Parcours des mailles rivières depuis tous les amonts et Incrémentation d'une unité
305
+ Résultat :
306
+ - tous les biefs en amont sont à 1
307
+ - Les autres biefs contiennent le nombre de biefs en amont
308
+
309
+ Indice de Strahler
310
+ """
311
+ for curup in self.upstreams['list']:
312
+ curnode:Node_Watershed
313
+ curnode=curup
314
+ while not curnode is None:
315
+ curnode.reachlevel +=1
316
+ curnode=curnode.down
317
+
318
+ #recherche de l'index max --> à l'exutoire
319
+ self.maxlevels = self.parent.outlet.reachlevel
320
+ self.maxstrahler=0
321
+ self.reaches['indexed']={}
322
+ for i in range(1,self.maxlevels+1):
323
+ self.reaches['indexed'][i]=[]
324
+
325
+ #création de listes pour chaque niveau
326
+ for curreach in self.reaches['reaches']:
327
+ curdict=self.reaches['reaches'][curreach]
328
+ listreach=curdict['baselist']
329
+ curlevel=listreach[0].reachlevel
330
+ self.reaches['indexed'][curlevel].append(curreach)
331
+
332
+ #création de listes pour chaque amont
333
+ # on parcourt toutes les mailles depuis chaque amont et on ajoute les index de biefs qui sont différents
334
+ for idx,curup in enumerate(self.upstreams['list']):
335
+ curdict=self.upstreams[idx]={}
336
+ curdict['up']=curup
337
+ curdict['fromuptodown']=[]
338
+ curdict['fromuptodown'].append(curup.reach)
339
+ curnode=curup.down
340
+ while not curnode is None:
341
+ if curnode.reach!=curdict['fromuptodown'][-1]:
342
+ curdict['fromuptodown'].append(curnode.reach)
343
+ curnode=curnode.down
344
+
345
+ #création de l'indice de Strahler
346
+ self.reaches['strahler']={}
347
+ #on commence par ajouter les biefs de 1er niveau qui sont à coup sûr d'indice 1
348
+ self.reaches['strahler'][1]=self.reaches['indexed'][1]
349
+ for curreach in self.reaches['strahler'][1]:
350
+ self.set_strahler_in_nodes(curreach,1)
351
+
352
+ #on parcourt les différents niveaux
353
+ for i in range(2,self.maxlevels+1):
354
+ listlevel=self.reaches['indexed'][i]
355
+ for curreach in listlevel:
356
+ curup:Node_Watershed
357
+ curup=self.reaches['reaches'][curreach]['upstream']
358
+ upidx=list(x.strahler for x in curup.upriver)
359
+ sameidx=upidx[0]==upidx[-1]
360
+ maxidx=max(upidx)
361
+
362
+ curidx=maxidx
363
+ if sameidx:
364
+ curidx+=1
365
+ if not curidx in self.reaches['strahler'].keys():
366
+ #création de la liste du niveau supérieur
367
+ self.reaches['strahler'][curidx]=[]
368
+ self.maxstrahler=curidx
369
+
370
+ self.reaches['strahler'][curidx].append(curreach)
371
+ self.set_strahler_in_nodes(curreach,curidx)
372
+
373
+
374
+ myarray=WolfArray(mold=self.parent.subs_array)
375
+ myarray.reset()
376
+ curnode:Node_Watershed
377
+ for curreach in self.reaches['reaches']:
378
+ curdict=self.reaches['reaches'][curreach]
379
+ listreach=curdict['baselist']
380
+ for curnode in listreach:
381
+ i=curnode.i
382
+ j=curnode.j
383
+ myarray.array[i,j]=curnode.strahler
384
+ myarray.filename = self.parent.dir+'\\Characteristic_maps\\Drainage_basin.strahler'
385
+ myarray.write_all()
386
+ myarray.reset()
387
+ for curreach in self.reaches['reaches']:
388
+ curdict=self.reaches['reaches'][curreach]
389
+ listreach=curdict['baselist']
390
+ for curnode in listreach:
391
+ i=curnode.i
392
+ j=curnode.j
393
+ myarray.array[i,j]=curnode.reachlevel
394
+ myarray.filename = self.parent.dir+'\\Characteristic_maps\\Drainage_basin.reachlevel'
395
+ myarray.write_all()
396
+
397
+ def set_strahler_in_nodes(self, whichreach:int, strahler:int):
398
+ """
399
+ Mise à jour de la propriété dans chaque noeud du bief
400
+ """
401
+ listnodes = self.reaches['reaches'][whichreach]['baselist']
402
+
403
+ curnode:Node_Watershed
404
+ for curnode in listnodes:
405
+ curnode.set_strahler(strahler)
406
+
407
+ def plot_dem(self, which:int=-1):
408
+ """
409
+ Graphiques
410
+ """
411
+ mymarkers=['x','+','1','2','3','4']
412
+ if which==-1:
413
+ fig=self.plotter.add('All Reaches')
414
+
415
+ ax=fig.add_ax()
416
+
417
+ for curreach in self.reaches['reaches']:
418
+ x=np.array(self.get_cums(whichreach=curreach))
419
+ for idx,curdem in enumerate(LISTDEM):
420
+ y=np.array(self.get_dem(curdem,whichreach=curreach))
421
+
422
+ xmask=np.ma.masked_where(y==99999.,x)
423
+ ymask=np.ma.masked_where(y==99999.,y)
424
+
425
+ ax.scatter(xmask,ymask,marker=mymarkers[idx],label=curdem)
426
+ ax.legend()
427
+ fig.canvas.draw()
428
+
429
+ elif which==-99:
430
+ size=int(np.ceil(np.sqrt(self.nbreaches)))
431
+
432
+ fig=self.plotter.add('reaches')
433
+
434
+ for index,curreach in enumerate(self.reaches['reaches']):
435
+ #curax=ax[int(np.floor(index/size)),int(np.mod(index,size))]
436
+ curax=fig.add_ax()
437
+
438
+ curdict=self.reaches['reaches'][curreach]
439
+ x=np.array(self.get_cums(whichreach=curreach))
440
+
441
+ for idx,curdem in enumerate(LISTDEM):
442
+ y=np.array(self.get_dem(curdem,whichreach=curreach))
443
+
444
+ xmask=np.ma.masked_where(y==99999.,x)
445
+ ymask=np.ma.masked_where(y==99999.,y)
446
+
447
+ curax.scatter(xmask,ymask,marker=mymarkers[idx],label=curdem)
448
+ curax.legend()
449
+ fig.canvas.draw()
450
+
451
+ elif which==-98:
452
+ size=int(np.ceil(np.sqrt(len(self.upstreams['list']))))
453
+
454
+ fig=self.plotter.add('reaches')
455
+
456
+ for idxup,curup in enumerate(self.upstreams['list']):
457
+ curax=fig.add_ax()
458
+
459
+ x=np.array(self.get_cums(whichup=idxup))
460
+
461
+ for idx,curdem in enumerate(LISTDEM):
462
+ y=np.array(self.get_dem(curdem,whichup=idxup))
463
+
464
+ xmask=np.ma.masked_where(y==99999.,x)
465
+ ymask=np.ma.masked_where(y==99999.,y)
466
+ curax.scatter(xmask,ymask,marker=mymarkers[idx],label=curdem)
467
+
468
+ curax.legend()
469
+ fig.canvas.draw()
470
+
471
+ elif which>-1:
472
+ if which<len(self.upstreams['list']):
473
+ if not self.plotter is None:
474
+ fig=self.plotter.add('Upstream n°'+str(which))
475
+ else:
476
+ fig=plt.figure()
477
+
478
+ ax=fig.add_ax()
479
+
480
+ x=np.array(self.get_cums(whichup=which))
481
+ for idx,curdem in enumerate(LISTDEM):
482
+ y=np.array(self.get_dem(curdem,whichup=which))
483
+
484
+ xmask=np.ma.masked_where(y==99999.,x)
485
+ ymask=np.ma.masked_where(y==99999.,y)
486
+ ax.scatter(xmask,ymask,marker=mymarkers[idx],label=curdem)
487
+
488
+ ax.legend()
489
+ fig.canvas.draw()
490
+
491
+ def plot_dem_and_corr(self, which:int=-1, whichdem:str='dem_after_corr'):
492
+ """
493
+ Graphiques
494
+ """
495
+
496
+ if which<len(self.upstreams['list']):
497
+ if not self.plotter is None:
498
+ fig=self.plotter.add('Upstream n°'+str(which))
499
+ else:
500
+ fig=plt.figure()
501
+ fig.suptitle('Upstream n°'+str(which))
502
+
503
+ ax=fig.add_ax()
504
+
505
+ x=np.array(self.get_cums(whichup=which))
506
+ y=np.array(self.get_dem(whichdem,whichup=which))
507
+
508
+ xcorr=self.upmin[which][whichdem][0]
509
+ ycorr=self.upmin[which][whichdem][1]
510
+
511
+ xmask=np.ma.masked_where(y==99999.,x)
512
+ ymask=np.ma.masked_where(y==99999.,y)
513
+
514
+ ax.scatter(xmask,ymask,marker='x',label=whichdem)
515
+ ax.scatter(xcorr,ycorr,marker='+',label='selected points')
516
+
517
+ ax.legend()
518
+ fig.canvas.draw()
519
+
520
+ if not self.savedir=='':
521
+ plt.savefig(self.savedir+'\\Up'+str(which)+'_'+whichdem+'.png')
522
+
523
+ def plot_slope(self, which:int=-1):
524
+ """
525
+ Graphiques
526
+ """
527
+ mymarkers=['x','+','1','2','3','4']
528
+ if which==-1:
529
+ fig=self.plotter.add('reaches')
530
+ ax=fig.add_ax()
531
+
532
+ for curreach in self.reaches['reaches']:
533
+ x=self.get_cums(whichreach=curreach)
534
+ for idx,curdem in enumerate(LISTDEM):
535
+ y=self.get_slope(curdem,whichreach=curreach)
536
+ ax.scatter(x,y,marker=mymarkers[idx],label=curdem)
537
+ fig.canvas.draw()
538
+
539
+ elif which==-99:
540
+ size=int(np.ceil(np.sqrt(self.nbreaches)))
541
+ fig=self.plotter.add('reaches')
542
+
543
+ for index,curreach in enumerate(self.reaches['reaches']):
544
+ curax=fig.add_ax()
545
+
546
+ x=self.get_cums(whichreach=curreach)
547
+
548
+ for idx,curdem in enumerate(LISTDEM):
549
+ y=self.get_slope(curdem,whichreach=curreach)
550
+ curax.scatter(x,y,marker=mymarkers[idx],label=curdem)
551
+ curax.legend()
552
+ fig.canvas.draw()
553
+
554
+ elif which==-98:
555
+ size=int(np.ceil(np.sqrt(len(self.upstreams['list']))))
556
+
557
+ fig=self.plotter.add('reaches')
558
+
559
+ for idxup,curup in enumerate(self.upstreams['list']):
560
+ curax=fig.add_ax()
561
+ x=self.get_cums(whichup=idxup)
562
+
563
+ for idx,curdem in enumerate(LISTDEM):
564
+ y=self.get_slope(curdem,whichup=idxup)
565
+ curax.scatter(x,y,marker=mymarkers[idx],label=curdem)
566
+ curax.legend()
567
+ fig.canvas.draw()
568
+
569
+ def write_slopes(self):
570
+ """
571
+ Ecriture sur disque
572
+ """
573
+ #Uniquement les pentes rivières
574
+ for curlist in LISTDEM:
575
+ slopes= WolfArray(self.parent.dir+'\\Characteristic_maps\\Drainage_basin.slope')
576
+ slopes.reset()
577
+ for curreach in self.reaches['reaches']:
578
+ curdict=self.reaches['reaches'][curreach]
579
+ listreach=curdict['baselist']
580
+
581
+ curnode:Node_Watershed
582
+ ijval = np.asarray([[curnode.i, curnode.j, curnode.slopecorr[curlist]['value']] for curnode in listreach])
583
+ slopes.array[np.int32(ijval[:,0]),np.int32(ijval[:,1])]=ijval[:,2]
584
+
585
+ slopes.filename = self.parent.dir+'\\Characteristic_maps\\Drainage_basin.slope_corr_riv_'+curlist
586
+ slopes.write_all()
587
+
588
+ def slope_correctionmin(self):
589
+ """
590
+ Correction pente minimale
591
+ """
592
+ if self.tslopemin is not None:
593
+ logging.info(_('select min - river'))
594
+ self.selectmin()
595
+ logging.info(_('slope correction min - river'))
596
+ self.compute_slopescorr(self.upmin)
597
+
598
+ def slope_correctionmax(self):
599
+ """
600
+ Correction pente maximale
601
+ """
602
+ if self.tslopemax is not None:
603
+ logging.info(_('select max - river'))
604
+ self.selectmax()
605
+ logging.info(_('slope correction max - river'))
606
+ self.compute_slopescorr(self.upmax)
607
+
608
+ def selectmin(self):
609
+ """
610
+ Sélection des valeurs minimales afin de conserver une topo décroissante vers l'aval --> une pente positive
611
+ """
612
+ self.upmin={}
613
+
614
+ #on initialise le dictionnaire de topo min pour chaque amont
615
+ for idx,curup in enumerate(self.upstreams['list']):
616
+ self.upmin[idx]={}
617
+
618
+ curnode:Node_Watershed
619
+ for curdem in LISTDEM:
620
+ logging.info(_('Current DEM : {}'.format(curdem)))
621
+ for idx,curup in enumerate(self.upstreams['list']):
622
+ #on part de l'amont
623
+ curnode=curup
624
+ x=[]
625
+ y=[]
626
+
627
+ x.append(curnode.cums)
628
+
629
+ if curdem=='crosssection':
630
+ basey=min(curnode.dem[curdem],curnode.dem['dem_after_corr'])
631
+ else:
632
+ basey=curnode.dem[curdem]
633
+
634
+ y.append(basey)
635
+ curnode=curnode.down
636
+
637
+ locs= self.parent.resolution
638
+ while not curnode is None:
639
+ if curdem=='crosssection':
640
+ yloc=min(curnode.dem[curdem],curnode.dem['dem_after_corr'])
641
+ else:
642
+ yloc=curnode.dem[curdem]
643
+
644
+ #on ajoute la maille si la pente est suffisante, sinon cekla créera un trou dans le parcours
645
+ if (basey-yloc)/locs>self.tslopemin:
646
+ x.append(curnode.cums)
647
+ y.append(yloc)
648
+ basey=yloc
649
+ locs= self.parent.resolution
650
+ else:
651
+ locs+= self.parent.resolution
652
+
653
+ #if curnode.i==232 and curnode.j==226:
654
+ # a=1
655
+
656
+ curnode=curnode.down
657
+
658
+ #on stocke les vecteurs de coordonnées curvi et d'altitudes pour les zones respectant les critères
659
+ self.upmin[idx][curdem]=[x,y]
660
+
661
+ def selectmax(self):
662
+ """
663
+ Sélection des valeurs maximales afin de conserver une topo décroissante vers l'aval --> une pente positive
664
+ """
665
+ # on travaille sur base de la topo corrigée min
666
+ self.upmax={}
667
+
668
+ #on initialise le dictionnaire de topo max pour chaque amont
669
+ for idx,curup in enumerate(self.upstreams['list']):
670
+ self.upmax[idx]={}
671
+
672
+ ds=self.parent.resolution
673
+ curnode:Node_Watershed
674
+ for curdem in LISTDEM:
675
+ logging.info(_('Current DEM : {}'.format(curdem)))
676
+ for idx,curup in enumerate(self.upstreams['list']):
677
+ curnode=curup
678
+ x=[]
679
+ y=[]
680
+
681
+ basey=curnode.demcorr[curdem]['value']
682
+
683
+ x.append(curnode.cums)
684
+ y.append(basey)
685
+ curnode=curnode.down
686
+
687
+ locs= ds
688
+ while not curnode is None:
689
+ yloc=curnode.demcorr[curdem]['value']
690
+
691
+ if (basey-yloc)/locs>self.tslopemax:
692
+ while len(x)>1 and (basey-yloc)/locs>self.tslopemax:
693
+ x.pop()
694
+ y.pop()
695
+ basey=y[-1]
696
+ locs+=ds
697
+
698
+ if yloc<y[-1]:
699
+ x.append(curnode.cums)
700
+ y.append(yloc)
701
+ basey=yloc
702
+ locs=ds
703
+
704
+ curnode=curnode.down
705
+
706
+ self.upmax[idx][curdem]=[x,y]
707
+
708
+ def compute_slopescorr(self, whichdict:dict):
709
+ """
710
+ Calcul des pents corrigées
711
+ """
712
+ curnode:Node_Watershed
713
+ for curdem in LISTDEM:
714
+ logging.info(_('Current DEM : {}'.format(curdem)))
715
+ for idx,curup in enumerate(self.upstreams['list']):
716
+ curdict=whichdict[idx][curdem]
717
+ xmin=curdict[0]
718
+ if len(xmin)>1:
719
+ ymin=curdict[1]
720
+ x=self.get_cums(whichup=idx)
721
+
722
+ #on cale une fonction d'interpolation sur la sélection dans lequalle on a oublié les pentes faibles --> à trou
723
+ f=interpolate.interp1d(xmin,ymin, fill_value='extrapolate')
724
+ #on interpole sur tous les x --> on remplit les trous
725
+ y=f(x)
726
+ #calcul des pentes sur base des noeuds aval
727
+ slopes=self.compute_slope_down(x,y)
728
+
729
+ #on remplit le dictionnaire de résultat
730
+ curnode=curup
731
+ i=0
732
+ while not curnode is None:
733
+ #if curnode.i==232 and curnode.j==226:
734
+ # a=1
735
+ curnode.demcorr[curdem]['parts'].append(y[i])
736
+ curnode.slopecorr[curdem]['parts'].append(slopes[i])
737
+ i+=1
738
+ curnode=curnode.down
739
+
740
+ #calcul de la moyenne sur base des valeurs partielles
741
+ for curdem in LISTDEM:
742
+ for curreach in self.reaches['reaches']:
743
+ nodeslist=self.reaches['reaches'][curreach]['baselist']
744
+ for curnode in nodeslist:
745
+ #if curnode.i==232 and curnode.j==226:
746
+ # a=1
747
+ if len(nodeslist)<2:
748
+ if not self.tslopemin is None:
749
+ curnode.slopecorr[curdem]['value']=max(self.tslopemin,curnode.slope)
750
+ else:
751
+ curnode.slopecorr[curdem]['value']=self.tslopemin=1.e-4
752
+
753
+ if not self.tslopemax is None:
754
+ curnode.slopecorr[curdem]['value']=min(self.tslopemax,curnode.slope)
755
+ else:
756
+ curnode.demcorr[curdem]['value']=np.mean(curnode.demcorr[curdem]['parts'])
757
+ curnode.slopecorr[curdem]['value']=np.mean(curnode.slopecorr[curdem]['parts'])
758
+
759
+ #on vide les parts
760
+ curnode.demcorr[curdem]['parts']=[]
761
+ curnode.slopecorr[curdem]['parts']=[]
762
+
763
+ def compute_slope_down(self, x, y):
764
+ """
765
+ Calcul de pente sur base de x et y
766
+ """
767
+ slope=[]
768
+ for i in range(len(x)-1):
769
+ slope.append((y[i+1]-y[i])/(x[i+1]-x[i]))
770
+ slope.append(slope[-1])
771
+ return slope
772
+
773
+ def plot_all_in_notebook(self):
774
+ """
775
+ Graphiques
776
+ """
777
+ self.plotter = PlotNotebook()
778
+
779
+ for i in range(self.nbreaches):
780
+ self.plot_dem_and_corr(i,whichdem='crosssection')
781
+ self.plot_dem()
782
+ self.plot_slope(-98)
783
+ self.plot_dem(-98)
784
+
785
+ class RunoffSystem:
786
+ """Classe de l'ensemble des mailles de ruissellement d'un modèle hydrologique WOLF"""
787
+ nodes:list[Node_Watershed] # liste de noeuds
788
+
789
+ parent:"Watershed"
790
+ upstreams:dict
791
+
792
+ tslopemin:float
793
+ tslopemax:float
794
+
795
+ upmin:dict
796
+ upmax:dict
797
+
798
+ def __init__(self,
799
+ runoff:list[Node_Watershed],
800
+ parent:"Watershed",
801
+ thslopemin:float = None,
802
+ thslopemax:float = None,
803
+ computecorr:bool=False,
804
+ *args,
805
+ **kwargs):
806
+
807
+ self.parent = parent
808
+ self.nodes = runoff
809
+ self.upstreams={}
810
+
811
+ #sélection des mailles qui ont une surface unitaire comme surface drainée
812
+ areaup = pow(parent.resolution,2)/1.e6
813
+ self.upstreams['list']=list(filter(lambda x: (x.uparea-areaup)<1.e-6 ,runoff))
814
+
815
+ if computecorr:
816
+ self.tslopemin = thslopemin
817
+ self.tslopemax = thslopemax
818
+
819
+ self.slope_correctionmin()
820
+ self.slope_correctionmax()
821
+
822
+ return super().__init__(*args, **kwargs)
823
+
824
+ def get_oneup(self, idx:int) -> Node_Watershed:
825
+ """
826
+ Récupération d'un amont sur base de l'index
827
+ """
828
+ return self.upstreams['list'][idx]
829
+
830
+ def get_cums(self,whichup:int=None):
831
+
832
+ if not whichup is None:
833
+ curnode:Node_Watershed
834
+ x=[]
835
+ curnode=self.get_oneup(whichup)
836
+ while not curnode.river:
837
+ x.append(curnode.cums)
838
+ curnode=curnode.down
839
+ if len(x)==1:
840
+ x.append(curnode.cums)
841
+ else:
842
+ x=[]
843
+
844
+ return x
845
+
846
+ def get_dem(self, whichdem:str, whichup:int=None):
847
+ if not whichdem in LISTDEM:
848
+ return
849
+
850
+ if not whichup is None:
851
+ curnode:Node_Watershed
852
+ dem=[]
853
+ curnode=self.get_oneup(whichup)
854
+ while not curnode.river:
855
+ dem.append(curnode.dem[whichdem])
856
+ curnode=curnode.down
857
+ return dem
858
+
859
+ def get_dem_corr(self, whichdem:str, whichup:int=None):
860
+ if not whichdem in LISTDEM:
861
+ return
862
+
863
+ if not whichup is None:
864
+ curnode:Node_Watershed
865
+ dem=[]
866
+ curnode=self.get_oneup(whichup)
867
+ while not curnode.river:
868
+ dem.append(curnode.dem[whichdem])
869
+ curnode=curnode.down
870
+ return dem
871
+
872
+ def get_slope(self, whichslope:str=None, whichup:int=None):
873
+
874
+ if whichslope is None:
875
+ if not whichup is None:
876
+ curnode:Node_Watershed
877
+ slope=[]
878
+ curnode=self.get_oneup(whichup)
879
+ while not curnode.river:
880
+ slope.append(curnode.slope)
881
+ curnode=curnode.down
882
+ else:
883
+ if not whichup is None:
884
+ curnode:Node_Watershed
885
+ slope=[]
886
+ curnode=self.get_oneup(whichup)
887
+ while not curnode.river:
888
+ slope.append(curnode.slopecorr[whichslope]['value'])
889
+ curnode=curnode.down
890
+
891
+ return slope
892
+
893
+ def plot_dem(self, which:int=-1):
894
+
895
+ mymarkers=['x','+','1','2','3','4']
896
+ if which>-1:
897
+ if which<len(self.upstreams['list']):
898
+ fig=plt.figure()
899
+ fig.suptitle('Upstream n°'+str(which))
900
+
901
+ x=np.array(self.get_cums(whichup=which))
902
+ for idx,curdem in enumerate(LISTDEM):
903
+ y=np.array(self.get_dem(curdem,whichup=which))
904
+
905
+ xmask=np.ma.masked_where(y==99999.,x)
906
+ ymask=np.ma.masked_where(y==99999.,y)
907
+ plt.scatter(xmask,ymask,marker=mymarkers[idx],label=curdem)
908
+
909
+ plt.legend()
910
+ plt.show()
911
+
912
+ def plot_dem_and_corr(self, which:int=-1, whichdem:str='dem_after_corr'):
913
+
914
+ if which<len(self.upstreams['list']):
915
+ fig=plt.figure()
916
+ fig.suptitle('Upstream n°'+str(which))
917
+
918
+ x=np.array(self.get_cums(whichup=which))
919
+ y=np.array(self.get_dem(whichdem,whichup=which))
920
+
921
+ xcorr=self.upmin[which][whichdem][0]
922
+ ycorr=self.upmin[which][whichdem][1]
923
+
924
+ xmask=np.ma.masked_where(y==99999.,x)
925
+ ymask=np.ma.masked_where(y==99999.,y)
926
+
927
+ plt.scatter(xmask,ymask,marker='x',label=whichdem)
928
+ plt.scatter(xcorr,ycorr,marker='+',label='selected points')
929
+
930
+ plt.legend()
931
+ plt.savefig(r'D:\OneDrive\OneDrive - Universite de Liege\Crues\2021-07 Vesdre\Simulations\Hydrologie\Up'+str(which)+'_'+whichdem+'.png')
932
+ #plt.show()
933
+
934
+ def write_slopes(self):
935
+ #Uniquement les pentes runoff
936
+ for curlist in LISTDEM:
937
+ slopes= WolfArray(self.parent.dir+'\\Characteristic_maps\\Drainage_basin.slope')
938
+ slopes.reset()
939
+ curnode:Node_Watershed
940
+ for curnode in self.nodes:
941
+ i=curnode.i
942
+ j=curnode.j
943
+ slopes.array[i,j]=curnode.slopecorr[curlist]['value']
944
+
945
+ slopes.filename = self.parent.dir+'\\Characteristic_maps\\Drainage_basin.slope_corr_run_'+curlist
946
+ slopes.write_all()
947
+
948
+ def slope_correctionmin(self):
949
+ if not self.tslopemin is None:
950
+ logging.info(_('select min - runoff'))
951
+ self.selectmin()
952
+ logging.info(_('slope correction min - runoff'))
953
+ self.compute_slopescorr(self.upmin)
954
+
955
+ def slope_correctionmax(self):
956
+ if not self.tslopemax is None:
957
+ logging.info(_('select max - runoff'))
958
+ self.selectmax()
959
+ logging.info(_('slope correction max - runoff'))
960
+ self.compute_slopescorr(self.upmax)
961
+
962
+ def selectmin(self):
963
+ #Sélection des valeurs minimales afin de conserver une topo décroissante vers l'aval --> une pente positive
964
+ self.upmin={}
965
+
966
+ #on initialise le dictionnaire de topo min pour chaque amont
967
+ for idx,curup in enumerate(self.upstreams['list']):
968
+ self.upmin[idx]={}
969
+
970
+ ds=self.parent.resolution
971
+ curnode:Node_Watershed
972
+ for curdem in LISTDEM:
973
+ logging.info(_('Current DEM : {}'.format(curdem)))
974
+ for idx,curup in enumerate(self.upstreams['list']):
975
+ curnode=curup
976
+ x=[]
977
+ y=[]
978
+
979
+ if curdem=='crosssection':
980
+ basey=min(curnode.dem[curdem],curnode.dem['dem_after_corr'])
981
+ else:
982
+ basey=curnode.dem[curdem]
983
+
984
+ x.append(curnode.cums)
985
+ y.append(basey)
986
+ curnode=curnode.down
987
+
988
+ locs=ds
989
+ while not curnode is None:
990
+ if curdem=='crosssection':
991
+ yloc=min(curnode.dem[curdem],curnode.dem['dem_after_corr'])
992
+ else:
993
+ yloc=curnode.dem[curdem]
994
+
995
+ if (basey-yloc)/locs>self.tslopemin:
996
+ x.append(curnode.cums)
997
+ y.append(yloc)
998
+ basey=yloc
999
+ locs=ds
1000
+ if curnode.river:
1001
+ break
1002
+ else:
1003
+ locs+=ds
1004
+ curnode=curnode.down
1005
+
1006
+ self.upmin[idx][curdem]=[x,y]
1007
+
1008
+ def selectmax(self):
1009
+ #Sélection des valeurs minimales afin de conserver une topo décroissante vers l'aval --> une pente positive
1010
+ self.upmax={}
1011
+
1012
+ #on initialise le dictionnaire de topo min pour chaque amont
1013
+ for idx,curup in enumerate(self.upstreams['list']):
1014
+ self.upmax[idx]={}
1015
+
1016
+ ds=self.parent.resolution
1017
+ curnode:Node_Watershed
1018
+ for curdem in LISTDEM:
1019
+ logging.info(_('Current DEM : {}'.format(curdem)))
1020
+ for idx,curup in enumerate(self.upstreams['list']):
1021
+ curnode=curup
1022
+ x=[]
1023
+ y=[]
1024
+
1025
+ """
1026
+ if curdem=='crosssection':
1027
+ basey=min(curnode.demcorr[curdem]['value'],curnode.demcorr['dem_after_corr']['value'])
1028
+ else:
1029
+ basey=curnode.demcorr[curdem]['value']
1030
+ """
1031
+ basey=curnode.demcorr[curdem]['value']
1032
+
1033
+ x.append(curnode.cums)
1034
+ y.append(basey)
1035
+ curnode=curnode.down
1036
+
1037
+ locs= ds
1038
+ while not curnode is None:
1039
+ """
1040
+ if curdem=='crosssection':
1041
+ yloc=min(curnode.demcorr[curdem]['value'],curnode.demcorr['dem_after_corr']['value'])
1042
+ else:
1043
+ yloc=curnode.demcorr[curdem]['value']
1044
+ """
1045
+ yloc=curnode.demcorr[curdem]['value']
1046
+
1047
+ if (basey-yloc)/locs>self.tslopemax:
1048
+ while len(x)>1 and (basey-yloc)/locs>self.tslopemax:
1049
+ x.pop()
1050
+ y.pop()
1051
+ basey=y[-1]
1052
+ locs+=ds
1053
+
1054
+ if yloc<y[-1]:
1055
+ x.append(curnode.cums)
1056
+ y.append(yloc)
1057
+ basey=yloc
1058
+ locs=ds
1059
+ if curnode.river:
1060
+ break
1061
+
1062
+ curnode=curnode.down
1063
+ #if curnode.i==187 and curnode.j==207:
1064
+ # a=1
1065
+
1066
+ self.upmax[idx][curdem]=[x,y]
1067
+
1068
+ def compute_slopescorr(self, whichdict:dict):
1069
+ curnode:Node_Watershed
1070
+ for curdem in LISTDEM:
1071
+ logging.info(_('Current DEM : {}'.format(curdem)))
1072
+ for idx,curup in enumerate(self.upstreams['list']):
1073
+ curdict=whichdict[idx][curdem]
1074
+ xmin=curdict[0]
1075
+ if len(xmin)>1:
1076
+ ymin=curdict[1]
1077
+ x=self.get_cums(whichup=idx)
1078
+
1079
+ f=interpolate.interp1d(xmin,ymin, fill_value='extrapolate')
1080
+ y=f(x)
1081
+ slopes=self.compute_slope_down(x,y)
1082
+
1083
+ curnode=curup
1084
+ i=0
1085
+ while not curnode.river:
1086
+ #if curnode.i==187 and curnode.j==207:
1087
+ # a=1
1088
+ curnode.demcorr[curdem]['parts'].append(y[i])
1089
+ curnode.slopecorr[curdem]['parts'].append(slopes[i])
1090
+ i+=1
1091
+ curnode=curnode.down
1092
+ #calcul de la moyenne sur base des valeurs partielles
1093
+ for curdem in LISTDEM:
1094
+ for curnode in self.nodes:
1095
+ #if curnode.i==187 and curnode.j==207:
1096
+ # a=1
1097
+ if len(curnode.slopecorr[curdem]['parts'])<2:
1098
+ #Ce cas particulier peut arriver si des mailles BV sont remplies par une zone plate qui s'étend en rivière
1099
+ # Comme on ne recherche de mailles plus basses que dans la partie BV, il n'est pas possible de corriger les pentes
1100
+ if not self.tslopemin is None:
1101
+ curnode.slopecorr[curdem]['value']=max(self.tslopemin,curnode.slope)
1102
+ else:
1103
+ curnode.slopecorr[curdem]['value']=1.e-4
1104
+
1105
+ if not self.tslopemax is None:
1106
+ curnode.slopecorr[curdem]['value']=min(self.tslopemax,curnode.slope)
1107
+ else:
1108
+ curnode.demcorr[curdem]['value']=np.mean(curnode.demcorr[curdem]['parts'])
1109
+ curnode.slopecorr[curdem]['value']=np.mean(curnode.slopecorr[curdem]['parts'])
1110
+
1111
+ curnode.demcorr[curdem]['parts']=[]
1112
+ curnode.slopecorr[curdem]['parts']=[]
1113
+
1114
+ def compute_slope_down(self, x, y):
1115
+ """
1116
+ Calcul de la pente sur base de listes X et Y
1117
+ """
1118
+ slope=[]
1119
+ for i in range(len(x)-1):
1120
+ slope.append((y[i+1]-y[i])/(x[i+1]-x[i]))
1121
+ slope.append(slope[-1])
1122
+ return slope
1123
+
1124
+ class SubWatershed:
1125
+ """ Classe sous-bassin versant """
1126
+ def __init__(self,
1127
+ parent:"Watershed",
1128
+ name:str,
1129
+ idx:int,
1130
+ mask:WolfArray,
1131
+ nodes:list[Node_Watershed],
1132
+ runoff:list[Node_Watershed],
1133
+ rivers:list[Node_Watershed]) -> None:
1134
+
1135
+ self.parent = parent
1136
+ self.index = idx
1137
+ self.name = name
1138
+ self.mask = mask
1139
+ self.nodes = nodes
1140
+ self.rivers = rivers
1141
+ self.runoff = runoff
1142
+ self.idx_reaches = np.unique(np.asarray([x.reach for x in rivers]))
1143
+
1144
+ @property
1145
+ def surface(self) -> float:
1146
+ return self.mask.nbnotnull * self.mask.dx * self.mask.dy
1147
+
1148
+ class Watershed:
1149
+ """Classe bassin versant"""
1150
+
1151
+ header:header_wolf # header_wolf of "Drainage_basin.sub" wolf_array
1152
+
1153
+ dir: str # Répertoire de modélisation
1154
+
1155
+ outlet:Node_Watershed # exutoire
1156
+
1157
+ subs_array: WolfArray # "Drainage_basin.sub" wolf_array
1158
+
1159
+ nodes:list[Node_Watershed] # all nodes
1160
+ nodesindex:np.ndarray # indirect access to mynodes, contains index of instance in list
1161
+ rivers:list[Node_Watershed] # all river nodes
1162
+ runoff:list[Node_Watershed] # all runoff nodes
1163
+
1164
+ couplednodes:list # forced exchanges
1165
+
1166
+ subcatchments: list[SubWatershed]
1167
+ statisticss: dict
1168
+
1169
+ couplednodesxy:list[float,float,float,float]
1170
+ couplednodesij:list[tuple[int,int],tuple[int,int]]
1171
+
1172
+ riversystem:RiverSystem # réseau de rivières
1173
+ runoffsystem:RunoffSystem # écoulement diffus/hydrologique
1174
+
1175
+ def __init__(self,
1176
+ dir:str,
1177
+ thzmin:float=None,
1178
+ thslopemin:float=None,
1179
+ thzmax:float=None,
1180
+ thslopemax:float=None,
1181
+ crosssections:CrossSections=None,
1182
+ computestats:bool=False,
1183
+ computecorr:bool=False,
1184
+ plotstats:bool=False,
1185
+ plotriversystem=False,
1186
+ *args, **kwargs):
1187
+
1188
+ logging.info(_('Read files...'))
1189
+ self.dir=os.path.normpath(dir)
1190
+
1191
+ self.subs_array = WolfArray(self.dir+'\\Characteristic_maps\\Drainage_basin.sub')
1192
+
1193
+ self.header = self.subs_array.get_header()
1194
+
1195
+ #FIXME
1196
+ isOk, fe_file = check_path(os.path.join(self.dir, "Coupled_pairs.txt"), prefix=self.dir)
1197
+ self.couplednodesxy=[]
1198
+ self.couplednodesij=[]
1199
+
1200
+ if isOk>=0:
1201
+ f = open(fe_file, 'r')
1202
+ lines = f.read().splitlines()
1203
+ f.close()
1204
+
1205
+ if lines[0]=='COORDINATES':
1206
+ for xy in enumerate(lines[1:]):
1207
+ xup,yup,xdown,ydown=xy[1].split('\t')
1208
+ self.couplednodesxy.append([float(xup),float(yup),float(xdown),float(ydown)])
1209
+ 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))])
1210
+
1211
+ logging.info(_('Initialization of nodes...'))
1212
+ self.nodesindex = np.zeros([self.subs_array.nbx,self.subs_array.nby], dtype=int)
1213
+ self.outlet = None
1214
+ self.up = None
1215
+ self.init_nodes()
1216
+
1217
+ logging.info(_('Initialization of subwatersheds...'))
1218
+ self.init_subs()
1219
+
1220
+ if not crosssections is None:
1221
+ logging.info(_('Cross sections...'))
1222
+ self.crosssections = crosssections
1223
+ self.attrib_cs_to_nodes()
1224
+ else:
1225
+ self.crosssections = None
1226
+
1227
+ logging.info(_('Slopes corrections...'))
1228
+ self.riversystem = RiverSystem(self.rivers , self,thslopemin=thslopemin, thslopemax=thslopemax, computecorr=computecorr)
1229
+ self.runoffsystem = RunoffSystem(self.runoff, self,thslopemin=thslopemin, thslopemax=thslopemax, computecorr=computecorr)
1230
+
1231
+ if computestats or plotstats:
1232
+ logging.info(_('Statistics...'))
1233
+ self.compute_stats(plotstats)
1234
+
1235
+ #Ecriture des résultats de correction des pentes
1236
+ if computecorr:
1237
+ logging.info(_('Writing data to disk'))
1238
+ self.write_dem()
1239
+ self.write_slopes()
1240
+
1241
+ if plotriversystem:
1242
+ logging.info(_('Plot rivers'))
1243
+ self.riversystem.plot_all_in_notebook()
1244
+
1245
+ logging.info(_('Done!'))
1246
+
1247
+ @property
1248
+ def nb_subs(self):
1249
+ return int(ma.max(self.subs_array.array))
1250
+
1251
+ @property
1252
+ def resolution(self):
1253
+ return self.header.dx
1254
+
1255
+ def get_node_from_ij(self, i:int,j:int):
1256
+ """
1257
+ Récupération d'un objet Node_Watershed sur base des indices (i,j)
1258
+ """
1259
+ shape = self.nodesindex.shape
1260
+ if i<0 or i>=shape[0]:
1261
+ return None
1262
+ if j<0 or j>=shape[1]:
1263
+ return None
1264
+ idx = self.nodesindex[i,j]
1265
+ if idx<0 or idx >= len(self.nodes):
1266
+ return None
1267
+
1268
+ return self.nodes[idx]
1269
+
1270
+ def get_node_from_xy(self, x:float, y:float):
1271
+ """
1272
+ Récupération d'un objet Node_Watershed sur base des coordonnées (x,y)
1273
+ """
1274
+ i,j = self.header.get_ij_from_xy(x,y)
1275
+ return self.get_node_from_ij(i,j)
1276
+
1277
+ def write_slopes(self):
1278
+ """
1279
+ Ecriture sur disque
1280
+ """
1281
+ for curlist in LISTDEM:
1282
+ curpath=self.dir+'\\Characteristic_maps\\corrslopes\\'+curlist
1283
+ os.makedirs(curpath,exist_ok=True)
1284
+ slopes= WolfArray(self.dir+'\\Characteristic_maps\\Drainage_basin.slope')
1285
+
1286
+ ijval = np.asarray([[curnode.i, curnode.j, curnode.slopecorr[curlist]['value']] for curnode in self.nodes])
1287
+ slopes.array[np.int32(ijval[:,0]),np.int32(ijval[:,1])]=ijval[:,2]
1288
+
1289
+ slopes.filename = curpath +'\\Drainage_basin.slope_corr'
1290
+ slopes.write_all()
1291
+
1292
+ def write_dem(self):
1293
+ """
1294
+ Ecriture sur disque
1295
+ """
1296
+ for curlist in LISTDEM:
1297
+ curpath=self.dir+'\\Characteristic_maps\\corrdem\\'+curlist
1298
+ os.makedirs(curpath,exist_ok=True)
1299
+ dem= WolfArray(self.dir+'\\Characteristic_maps\\Drainage_basincorr.b')
1300
+
1301
+ ijval = np.asarray([[curnode.i, curnode.j, curnode.demcorr[curlist]['value']] for curnode in self.nodes])
1302
+ dem.array[np.int32(ijval[:,0]),np.int32(ijval[:,1])]=ijval[:,2]
1303
+
1304
+ dem.filename = curpath +'\\Drainage_basincorr.b'
1305
+ dem.write_all()
1306
+
1307
+ @property
1308
+ def crosssections(self):
1309
+ return self._cs
1310
+
1311
+ @crosssections.setter
1312
+ def crosssections(self, value:CrossSections):
1313
+ self._cs = value
1314
+
1315
+ def attrib_cs_to_nodes(self):
1316
+ """
1317
+ Attribution des sections en travers aux noeuds
1318
+ """
1319
+ if self.crosssections is not None:
1320
+ for curlist in self.crosssections:
1321
+ for namecs in curlist.myprofiles:
1322
+ curvert:wolfvertex
1323
+ curcs=curlist.myprofiles[namecs]
1324
+
1325
+ try:
1326
+ curvert=curcs['bed']
1327
+ except:
1328
+ curvert=curlist.get_min(whichprofile=curcs)
1329
+
1330
+ i,j=self.subs_array.get_ij_from_xy(curvert.x,curvert.y)
1331
+ curnode:Node_Watershed
1332
+ curnode =self.nodes[self.nodesindex[i,j]]
1333
+
1334
+ if curnode.river:
1335
+ if curnode.crosssections is None:
1336
+ curnode.crosssections=[]
1337
+ curnode.crosssections.append(curcs)
1338
+ curnode.dem['crosssection']=min(curnode.dem['crosssection'],curvert.z)
1339
+
1340
+ def init_nodes(self):
1341
+ """
1342
+ Initialisation des noeuds
1343
+ """
1344
+
1345
+ self.nodes=[Node_Watershed() for i in range(self.subs_array.nbnotnull)]
1346
+
1347
+ dem_before_corr= WolfArray(self.dir+'\\Characteristic_maps\\Drainage_basin.b')
1348
+ dem_after_corr= WolfArray(self.dir+'\\Characteristic_maps\\Drainage_basincorr.b')
1349
+ demdelta= WolfArray(self.dir+'\\Characteristic_maps\\Drainage_basindiff.b')
1350
+ slopes= WolfArray(self.dir+'\\Characteristic_maps\\Drainage_basin.slope',masknull=False)
1351
+ reaches= WolfArray(self.dir+'\\Characteristic_maps\\Drainage_basin.reachs')
1352
+ cnv= WolfArray(self.dir+'\\Characteristic_maps\\Drainage_basin.cnv')
1353
+ times= WolfArray(self.dir+'\\Characteristic_maps\\Drainage_basin.time')
1354
+
1355
+ dem_after_corr.array.mask = self.subs_array.array.mask
1356
+
1357
+ nb=0
1358
+ for index,x in tqdm(np.ndenumerate(self.subs_array.array), 'Numbering'):
1359
+ if(x>0):
1360
+ i=int(index[0])
1361
+ j=int(index[1])
1362
+ self.nodesindex[i,j]=nb
1363
+ nb+=1
1364
+
1365
+ curnode:Node_Watershed
1366
+ nb=0
1367
+ for index, i_sub in tqdm(np.ndenumerate(self.subs_array.array), 'Initialization'):
1368
+ if(i_sub>0):
1369
+ i=int(index[0])
1370
+ j=int(index[1])
1371
+ x, y = self.header.get_xy_from_ij(i,j)
1372
+ curnode =self.nodes[self.nodesindex[i,j]]
1373
+
1374
+ curnode.i = i
1375
+ curnode.j = j
1376
+
1377
+ curnode.x = x
1378
+ curnode.y = y
1379
+
1380
+ curnode.crosssections = None
1381
+ curnode.down = None
1382
+
1383
+ curnode.index=nb
1384
+ curnode.dem={}
1385
+ curnode.dem['dem_before_corr']=dem_before_corr.array[i,j]
1386
+ curnode.dem['dem_after_corr']=dem_after_corr.array[i,j]
1387
+ curnode.dem['crosssection']=99999.
1388
+ curnode.demdelta=demdelta.array[i,j]
1389
+ curnode.slope=slopes.array[i,j]
1390
+
1391
+ curnode.slopecorr={}
1392
+ for curlist in LISTDEM:
1393
+ curnode.slopecorr[curlist]={}
1394
+ curnode.slopecorr[curlist]['parts']=[]
1395
+ curnode.slopecorr[curlist]['value']=curnode.slope
1396
+
1397
+ curnode.demcorr={}
1398
+ for curlist in LISTDEM:
1399
+ curnode.demcorr[curlist]={}
1400
+ curnode.demcorr[curlist]['parts']=[]
1401
+ curnode.demcorr[curlist]['value']=curnode.dem['dem_after_corr']
1402
+
1403
+ curnode.sub=int(i_sub)
1404
+ curnode.time=times.array[i,j]
1405
+ curnode.uparea=cnv.array[i,j]
1406
+ curnode.river=not reaches.array.mask[i,j]
1407
+ if curnode.river:
1408
+ curnode.reach=int(reaches.array[i,j])
1409
+ curnode.forced=False
1410
+ curnode.up=[]
1411
+ curnode.upriver=[]
1412
+ curnode.strahler=0
1413
+ curnode.reachlevel=0
1414
+ nb+=1
1415
+
1416
+ curdown:Node_Watershed
1417
+ #Liaison échanges forcés
1418
+ incr=slopes.dx
1419
+ for curexch in self.couplednodesij:
1420
+ i=int(curexch[0][0])
1421
+ j=int(curexch[0][1])
1422
+ curnode=self.nodes[self.nodesindex[i,j]]
1423
+ curnode.forced=True
1424
+ idown = int(curexch[1][0])
1425
+ jdown = int(curexch[1][1])
1426
+ curdown = self.nodes[self.nodesindex[idown,jdown]]
1427
+ curnode.down = curdown
1428
+ curdown.up.append(curnode)
1429
+ if curnode.river:
1430
+ curdown.upriver.append(curnode)
1431
+ curnode.incrs = incr * np.sqrt(pow(curdown.i-i,2)+pow(curdown.j-j,2))
1432
+
1433
+ #Liaison hors échanges forcés
1434
+ for curnode in tqdm(self.nodes, 'Linking'):
1435
+ if not curnode.forced:
1436
+ i=curnode.i
1437
+ j=curnode.j
1438
+
1439
+ curtop=curnode.dem['dem_after_corr']
1440
+
1441
+ neigh = [[i-1,j],[i+1,j],[i,j-1], [i,j+1]]
1442
+ diff = [dem_after_corr.array[curi,curj]-curtop if not dem_after_corr.array.mask[curi,curj] else 100000. for curi,curj in neigh]
1443
+ mindiff = np.min(diff)
1444
+ if mindiff<0:
1445
+ index = diff.index(mindiff)
1446
+ if index==0:
1447
+ curdown = self.nodes[self.nodesindex[i-1,j]]
1448
+ elif index==1:
1449
+ curdown = self.nodes[self.nodesindex[i+1,j]]
1450
+ elif index==2:
1451
+ curdown = self.nodes[self.nodesindex[i,j-1]]
1452
+ else:
1453
+ curdown = self.nodes[self.nodesindex[i,j+1]]
1454
+
1455
+ curnode.down = curdown
1456
+ curdown.up.append(curnode)
1457
+ if curnode.river:
1458
+ curdown.upriver.append(curnode)
1459
+ curnode.incrs=incr
1460
+ else:
1461
+ self.outlet = curnode
1462
+
1463
+ #Rechreche de la pente dans les voisins en croix dans la topo non remaniée
1464
+ for curnode in tqdm(self.nodes, 'Finding slope'):
1465
+ if not curnode.forced:
1466
+ i=curnode.i
1467
+ j=curnode.j
1468
+
1469
+ curtop = curnode.dem['dem_before_corr']
1470
+
1471
+ neigh = [[i-1,j], [i+1,j], [i,j-1], [i,j+1], [i-1,j-1], [i+1,j+1], [i+1,j-1], [i-1,j+1]]
1472
+ diff = [dem_before_corr.array[curi,curj]-curtop if not dem_before_corr.array.mask[curi,curj] else 100000. for curi,curj in neigh ]
1473
+ mindiff = np.min(diff)
1474
+
1475
+ fact=1.
1476
+ if mindiff<0:
1477
+ index = diff.index(mindiff)
1478
+ if index>3:
1479
+ fact=np.sqrt(2)
1480
+
1481
+ curnode.sloped8 = -mindiff/(self.resolution*fact)
1482
+
1483
+ self.rivers=list(filter(lambda x: x.river,self.nodes))
1484
+ self.rivers.sort(key=lambda x: x.dem['dem_after_corr'])
1485
+ sys.setrecursionlimit(len(self.nodes))
1486
+ self.outlet.incr_curvi()
1487
+
1488
+ self.find_dem_subpixels()
1489
+
1490
+ self.runoff=self.find_runoffnodes()
1491
+
1492
+ def find_rivers(self, whichsub:int=0, whichreach:int=0) -> tuple[list[Node_Watershed], Node_Watershed]:
1493
+ """
1494
+ Recherche des mailles rivières
1495
+ @param whichsub : numéro du sous-bassin à traiter
1496
+ @param whicreach : numéro du tronçon à identifier
1497
+ """
1498
+ if whichsub>0 and whichsub<=self.nb_subs:
1499
+ if whichreach>0:
1500
+ myrivers=list(filter(lambda x: x.river and x.sub==whichsub and x.reach==whichreach,self.rivers))
1501
+ else:
1502
+ myrivers=list(filter(lambda x: x.river and x.sub==whichsub,self.rivers))
1503
+ else:
1504
+ if whichreach>0:
1505
+ myrivers=list(filter(lambda x: x.river and x.reach==whichreach,self.rivers))
1506
+ else:
1507
+ myrivers=list(filter(lambda x: x.river,self.rivers))
1508
+
1509
+ myrivers.sort(key=lambda x: x.dem['dem_after_corr'])
1510
+
1511
+ up=None
1512
+ if len(myrivers)>0:
1513
+ up=myrivers[-1]
1514
+
1515
+ return myrivers,up
1516
+
1517
+ def find_sub(self, whichsub:int=0) -> list[Node_Watershed]:
1518
+ """
1519
+ Recherche des mailles du sous-bassin versant
1520
+ @param whichsub : numéro du sous-bassin à traiter
1521
+ """
1522
+ if whichsub>0 and whichsub<=self.nb_subs:
1523
+ mysub=list(filter(lambda x: x.sub==whichsub, self.nodes))
1524
+ else:
1525
+ mysub=self.nodes.copy()
1526
+
1527
+ mysub.sort(key=lambda x: x.dem['dem_after_corr'])
1528
+
1529
+ return mysub
1530
+
1531
+ def init_subs(self):
1532
+ """
1533
+ Initialize Sub-Catchments
1534
+ """
1535
+ self.subcatchments=[]
1536
+
1537
+ #Initialisation de la matrice de mask (d'une extension et d'une résolution similaire aux données radar)
1538
+ for i in tqdm(range(1,self.nb_subs+1), 'Subwatershed'):
1539
+ curmask = WolfArray(mold=self.subs_array)
1540
+ curmask.mask_allexceptdata(float(i))
1541
+ all_river_nodes, _ = self.find_rivers(i)
1542
+
1543
+ cursub = SubWatershed(self,
1544
+ name = 'sub n'+str(i),
1545
+ idx=i-1,
1546
+ mask = curmask,
1547
+ nodes = self.find_sub(i),
1548
+ runoff=self.find_runoffnodes(i),
1549
+ rivers=all_river_nodes,
1550
+ )
1551
+
1552
+ self.subcatchments.append(cursub)
1553
+
1554
+ def find_runoffnodes(self, whichsub:int=0) -> list[Node_Watershed]:
1555
+ """
1556
+ Recherche des mailles du bassin versant seul (sans les rivières)
1557
+ @param whichsub : numéro du sous-bassin à traiter
1558
+ """
1559
+ if whichsub>0 and whichsub<=self.nb_subs:
1560
+ myrunoff=list(filter(lambda x: not x.river and x.sub==whichsub,self.nodes))
1561
+ else:
1562
+ myrunoff=list(filter(lambda x: not x.river,self.nodes))
1563
+
1564
+ myrunoff.sort(key=lambda x: x.dem['dem_after_corr'])
1565
+
1566
+ return myrunoff
1567
+
1568
+ def index_flatzone(self, listofsortednodes:list, threshold:float):
1569
+ """
1570
+ Indexation des zones de plat
1571
+ """
1572
+
1573
+ curnode:Node_Watershed
1574
+ curflat:Node_Watershed
1575
+
1576
+ curindex=0
1577
+ for curnode in listofsortednodes[-1:1:-1]:
1578
+ addone=False
1579
+ while curnode.slope<threshold and curnode.flatindex==-1:
1580
+ addone=True
1581
+ curnode.flatindex=curindex
1582
+ if curnode.down is None:
1583
+ break
1584
+ curnode=curnode.down
1585
+ if addone:
1586
+ curindex+=1
1587
+
1588
+ return curindex
1589
+
1590
+ def find_flatnodes(self, listofsortednodes:list):
1591
+ """
1592
+ Recherche des mailles dans des zones de faibles pentes
1593
+ @param listofsortednodes : liste triée de mailles
1594
+ """
1595
+ myflatnodes=list(filter(lambda x: x.flatindex>-1,listofsortednodes))
1596
+
1597
+ return myflatnodes
1598
+
1599
+ def find_flatzones(self, listofsortednodes:list, maxindex:int):
1600
+ """
1601
+ Recherche des mailles dans des zones de faibles pentes
1602
+ @param listofsortednodes : liste triée de mailles
1603
+ """
1604
+ myflatzones=[[]] * maxindex
1605
+ for i in range(maxindex):
1606
+ myflatzones[i]=list(filter(lambda x: x.flatindex==i,listofsortednodes))
1607
+
1608
+ return myflatzones
1609
+
1610
+ def find_dem_subpixels(self):
1611
+ """
1612
+ Recherche des altitudes dans un mnt plus dense
1613
+ """
1614
+ demsubs = {}
1615
+ file_10m = os.path.join(self.dir,'mnt10m.bin')
1616
+ isOk, file_10m = check_path(file_10m, prefix=self.dir)
1617
+ if isOk>=0:
1618
+ dem_10m=WolfArray(file_10m)
1619
+ demsubs["dem_10m"] = dem_10m
1620
+
1621
+ file_20m = os.path.join(self.dir,'mnt20m.bin')
1622
+ isOk, file_20m = check_path(file_20m, prefix=self.dir)
1623
+ if isOk>=0:
1624
+ dem_20m=WolfArray(file_20m)
1625
+ demsubs["dem_20m"] = dem_20m
1626
+
1627
+
1628
+ # demsubs={'dem_10m':dem_10m,'dem_20m':dem_20m}
1629
+ if len(demsubs)==0:
1630
+ return
1631
+
1632
+ curnode:Node_Watershed
1633
+ for curdem in tqdm(demsubs, 'Sub-pixeling'):
1634
+ locdem=demsubs[curdem]
1635
+ dx=locdem.dx
1636
+ dy=locdem.dy
1637
+
1638
+ for curnode in tqdm(self.nodes):
1639
+ curi=curnode.i
1640
+ curj=curnode.j
1641
+
1642
+ curx,cury=self.subs_array.get_xy_from_ij(curi,curj)
1643
+
1644
+ decalx=(self.resolution-dx)/2.
1645
+ decaly=(self.resolution-dy)/2.
1646
+ x1=curx-decalx
1647
+ y1=cury-decaly
1648
+ x2=curx+decalx
1649
+ y2=cury+decaly
1650
+
1651
+ i1,j1=locdem.get_ij_from_xy(x1,y1)
1652
+ i2,j2=locdem.get_ij_from_xy(x2,y2)
1653
+
1654
+ curnode.dem[curdem]=np.min(locdem.array[i1:i2+1,j1:j2+1])
1655
+
1656
+ def compute_stats(self, plot:bool=False):
1657
+ """
1658
+ Calcul des statistiques de pente
1659
+ """
1660
+ self.statisticss={}
1661
+
1662
+ slopes=np.array(list(x.slope for x in self.nodes))
1663
+ slopesrunoff=np.array(list(x.slope for x in list(filter(lambda x: not x.river,self.nodes))))
1664
+ slopesriver=np.array(list(x.slope for x in list(filter(lambda x: x.river,self.nodes))))
1665
+
1666
+ curdict=self.statisticss
1667
+ curdict['slopemin'] = np.min(slopes)
1668
+ curdict['slopemax'] = np.max(slopes)
1669
+ curdict['slopemedian'] = np.median(slopes)
1670
+ curdict['slopemean'] = np.mean(slopes)
1671
+ curdict['hist'] = slopes
1672
+ curdict['hist_watershed'] = slopesrunoff
1673
+ curdict['hist_reaches'] = slopesriver
1674
+ curdict['count_neg'] = np.count_nonzero(slopes < 0.)
1675
+
1676
+ logging.info(_('Min : {}'.format(curdict['slopemin'])))
1677
+ logging.info(_('Max : {}'.format(curdict['slopemax'])))
1678
+ logging.info(_('Median : {}'.format(curdict['slopemedian'])))
1679
+ logging.info(_('Mean : {}'.format(curdict['slopemean'])))
1680
+ logging.info(_('Non Zero : {}'.format(curdict['count_neg'])))
1681
+
1682
+ for curlist in LISTDEM:
1683
+ curdict=self.statisticss[curlist]={}
1684
+
1685
+ slopes=np.array(list(x.slopecorr[curlist]['value'] for x in self.nodes))
1686
+ slopesrunoff=np.array(list(x.slopecorr[curlist]['value'] for x in list(filter(lambda x: not x.river,self.nodes))))
1687
+ slopesriver=np.array(list(x.slopecorr[curlist]['value'] for x in list(filter(lambda x: x.river,self.nodes))))
1688
+
1689
+ curdict['slopemin'] = np.min(slopes)
1690
+ curdict['slopemax'] = np.max(slopes)
1691
+ curdict['slopemedian'] = np.median(slopes)
1692
+ curdict['slopemean'] = np.mean(slopes)
1693
+ curdict['hist'] = slopes
1694
+ curdict['hist_watershed'] = slopesrunoff
1695
+ curdict['hist_reaches'] = slopesriver
1696
+ curdict['count_neg'] = np.count_nonzero(slopes < 0.)
1697
+
1698
+ logging.info(_('Current list : '.format(curlist)))
1699
+ logging.info(_('Min : {}'.format(curdict['slopemin'])))
1700
+ logging.info(_('Max : {}'.format(curdict['slopemax'])))
1701
+ logging.info(_('Median : {}'.format(curdict['slopemedian'])))
1702
+ logging.info(_('Mean : {}'.format(curdict['slopemean'])))
1703
+ logging.info(_('Non Zero : {}'.format(curdict['count_neg'])))
1704
+
1705
+ if plot:
1706
+ self.plot_stats()
1707
+
1708
+ def plot_stats(self):
1709
+
1710
+ self.myplotterstats = PlotNotebook()
1711
+
1712
+ bin1=np.array([1.e-8,1.e-7,1.e-6,5.e-6])
1713
+ bin2=np.linspace(1.e-5,1e-3,num=20)
1714
+ bin3=np.linspace(2.e-3,1e-1,num=20)
1715
+ bin4=np.linspace(.11,1,num=100)
1716
+ bins=np.concatenate((bin1,bin2,bin3,bin4))
1717
+
1718
+ fig=self.myplotterstats.add(_('Slope distribution - log'))
1719
+
1720
+ ax = fig.add_ax()
1721
+ ax.hist(self.statisticss['hist'],bins,cumulative=True,density=True,histtype=u'step',label='base')
1722
+ ax.set_xscale('log')
1723
+ ax.set_xlabel(_('All meshes'))
1724
+
1725
+ for curlist in LISTDEM:
1726
+ curdict=self.statisticss[curlist]
1727
+ ax.hist(curdict['hist'],bins,cumulative=True,density=True,histtype=u'step',label=curlist)
1728
+
1729
+ ax = fig.add_ax()
1730
+ ax.hist(self.statisticss['hist_watershed'],bins,cumulative=True,density=True,histtype=u'step',label='base')
1731
+ ax.set_xscale('log')
1732
+ ax.set_xlabel(_('Watershed'))
1733
+
1734
+ for curlist in LISTDEM:
1735
+ curdict=self.statisticss[curlist]
1736
+ ax.hist(curdict['hist_watershed'],bins,cumulative=True,density=True,histtype=u'step',label=curlist)
1737
+
1738
+ ax = fig.add_ax()
1739
+ ax.hist(self.statisticss['hist_reaches'],bins,cumulative=True,density=True,histtype=u'step',label='base')
1740
+ ax.set_xscale('log')
1741
+ ax.set_xlabel(_('River'))
1742
+
1743
+ for curlist in LISTDEM:
1744
+ curdict=self.statisticss[curlist]
1745
+ ax.hist(curdict['hist_reaches'],bins,cumulative=True,density=True,histtype=u'step',label=curlist)
1746
+
1747
+ ax.legend()
1748
+ fig.canvas.draw()
1749
+
1750
+ fig=self.myplotterstats.add(_('Slope distribution'))
1751
+ ax:plt.axis
1752
+
1753
+ ax = fig.add_ax()
1754
+ ax.hist(self.statisticss['hist'],bins,cumulative=True,density=True,histtype=u'step',label='base')
1755
+ ax.set_xlabel(_('All meshes'))
1756
+
1757
+ for curlist in LISTDEM:
1758
+ curdict=self.statisticss[curlist]
1759
+ ax.hist(curdict['hist'],bins,cumulative=True,density=True,histtype=u'step',label=curlist)
1760
+
1761
+ ax = fig.add_ax()
1762
+ ax.hist(self.statisticss['hist_watershed'],bins,cumulative=True,density=True,histtype=u'step',label='base')
1763
+ ax.set_xlabel(_('Watershed'))
1764
+
1765
+ for curlist in LISTDEM:
1766
+ curdict=self.statisticss[curlist]
1767
+ ax.hist(curdict['hist_watershed'],bins,cumulative=True,density=True,histtype=u'step',label=curlist)
1768
+
1769
+ ax = fig.add_ax()
1770
+ ax.hist(self.statisticss['hist_reaches'],bins,cumulative=True,density=True,histtype=u'step',label='base')
1771
+ ax.set_xlabel(_('River'))
1772
+
1773
+ for curlist in LISTDEM:
1774
+ curdict=self.statisticss[curlist]
1775
+ ax.hist(curdict['hist_reaches'],bins,cumulative=True,density=True,histtype=u'step',label=curlist)
1776
+
1777
+ ax.legend()
1778
+ fig.canvas.draw()
1779
+
1780
+ def analyze_flatzones(self):
1781
+ """
1782
+ Analyse des zones de plat
1783
+ """
1784
+ self.myplotterflat = PlotNotebook()
1785
+
1786
+ ### Flat zones
1787
+ eps=1e-7
1788
+ #indexation des zones "indépendantes" de plats - ruissellement
1789
+ maxindex=self.index_flatzone(self.runoff,eps)
1790
+ #identification des mailles dans les zones
1791
+ myflatnodes=self.find_flatnodes(self.runoff)
1792
+ #création de listes avec les noeuds dans chaque zone
1793
+ myflats=self.find_flatzones(myflatnodes,maxindex)
1794
+
1795
+ #calcul de la longueur de la zone de plat --> sommation du nombre de mailles
1796
+ lenflats=np.zeros((maxindex),dtype=np.int32)
1797
+ for i in range(maxindex):
1798
+ lenflats[i]=len(myflats[i])
1799
+
1800
+ #indexation des zones "indépendantes" de plats - rivières
1801
+ maxindexrivers=self.index_flatzone(self.rivers,eps)
1802
+ #création de listes avec les noeuds dans chaque zone - rivières
1803
+ myflatsrivers=self.find_flatzones(self.rivers,maxindexrivers)
1804
+
1805
+ #calcul de la longueur de la zone de plat --> sommation du nombre de mailles
1806
+ lenflatsrivers=np.zeros((maxindexrivers),dtype=np.int32)
1807
+ for i in range(maxindexrivers):
1808
+ lenflatsrivers[i]=len(myflatsrivers[i])
1809
+
1810
+ fig:mplfig.Figure
1811
+ fig=self.myplotterflat.add("Nb nodes in flat area")
1812
+ ax=fig.add_ax()
1813
+ mybins=np.arange(0.5,np.max(lenflats),1.)
1814
+ myticks=np.arange(1,np.ceil(np.max(lenflats)),1)
1815
+ ax.hist(lenflats,bins=mybins)
1816
+ ax.set_xlabel(_('Nb nodes in flat area - runoff'))
1817
+ ax.set_xticks(myticks)
1818
+ ax.set_xbound(.5,np.max(lenflats))
1819
+ ax.set_ylabel('Nb flat areas')
1820
+ ax.set_yscale('log')
1821
+
1822
+ ax=fig.add_ax()
1823
+ mybinsrivers=np.arange(0.5,np.max(lenflatsrivers),1.)
1824
+ myticksrivers=np.arange(1,np.ceil(np.max(lenflatsrivers)),1)
1825
+ ax.hist(lenflatsrivers,bins=mybinsrivers)
1826
+ ax.set_xlabel(_('Nb nodes in flat area - rivers'))
1827
+ ax.set_xticks(myticksrivers)
1828
+ ax.set_xbound(.5,np.max(lenflatsrivers))
1829
+ ax.set_ylabel('Nb flat areas')
1830
+ ax.set_yscale('log')
1831
+
1832
+ fig=self.myplotterflat.add("Nb nodes in flat area")
1833
+ ax=fig.add_ax()
1834
+ ax.hist(lenflats,bins=mybins,cumulative=True,density=True)
1835
+ ax.set_xlabel(_('Nb nodes in flat area - runoff'))
1836
+ ax.set_xticks(myticks)
1837
+ ax.set_xbound(.5,np.max(lenflats))
1838
+ ax.set_ylabel('Cumulative flat areas')
1839
+ #ax.set_yscale('log')
1840
+
1841
+ ax=fig.add_ax()
1842
+ ax.hist(lenflatsrivers,bins=mybinsrivers,cumulative=True,density=True)
1843
+ ax.set_xlabel(_('Nb nodes in flat area - rivers'))
1844
+ ax.set_xticks(myticksrivers)
1845
+ ax.set_xbound(.5,np.max(lenflatsrivers))
1846
+ ax.set_ylabel('Cumulative flat areas')
1847
+ #ax.set_yscale('log')
1848
+ fig.canvas.draw()
1849
+
1850
+ #Tri des pentes dans différentes listes
1851
+
1852
+ #toutes les mailles
1853
+ sdown=[]
1854
+ sup=[]
1855
+ for curflat in myflats:
1856
+ for curnode in curflat:
1857
+ #recherche de la pente aval plus grande que le seuil
1858
+ sdown.append(curnode.slope_down(eps))
1859
+ #recherche de la pente amont moyenne - uniquement pour les mailles qui ont une pente supérieure au seuil
1860
+ sup.append(curnode.mean_slope_up(eps))
1861
+
1862
+ sflat=[]
1863
+ sdownraw=[]
1864
+ for curflat in myflats:
1865
+ for curnode in curflat:
1866
+ #pente de la maille aval
1867
+ sdownraw.append(curnode.down.slope)
1868
+ #pente courante
1869
+ sflat.append(curnode.slope)
1870
+
1871
+ #mailles rivières
1872
+ sdownriv=[]
1873
+ supriv=[]
1874
+ suponlyriv=[]
1875
+ for curflat in myflatsrivers:
1876
+ for curnode in curflat:
1877
+ #recherche de la pente aval plus grande que le seuil
1878
+ sdownriv.append(curnode.slope_down(eps))
1879
+ #recherche de la pente amont moyenne - uniquement pour les mailles qui ont une pente supérieure au seuil
1880
+ supriv.append(curnode.mean_slope_up(eps))
1881
+ #recherche de la pente amont > seuil
1882
+ suponlyriv.append(curnode.slope_upriver(eps))
1883
+
1884
+ sdownd8=[]
1885
+ suponlyriv1=[]
1886
+ for curflat in myflatsrivers:
1887
+ for curnode in curflat:
1888
+ #pente aval selon voisines D8
1889
+ sdownd8.append(curnode.sloped8)
1890
+ #recherche de la pente amont > seuil
1891
+ suponlyriv1.append(curnode.slope_upriver(eps))
1892
+
1893
+ sflatriver=[]
1894
+ sdownrawriver=[]
1895
+ sd8rawriver=[]
1896
+ for curflat in myflatsrivers:
1897
+ if len(curflat)==1:
1898
+ for curnode in curflat:
1899
+ if not curnode.down is None:
1900
+ sd8rawriver.append(curnode.sloped8)
1901
+ sdownrawriver.append(curnode.down.slope)
1902
+ sflatriver.append(curnode.slope)
1903
+
1904
+
1905
+ #tracage des graphiques
1906
+ fig=self.myplotterflat.add("Scatter plots")
1907
+ ax=fig.add_ax()
1908
+ ax.scatter(sdownrawriver,sflatriver,marker='o',label='slope down vs flat slope')
1909
+ ax.scatter(sdownriv,suponlyriv,marker='+',label='slope down vs slope d8')
1910
+ ax=fig.add_ax()
1911
+ ax.scatter(sdownraw,sflat,marker='0',label='slope down vs flat slope')
1912
+ ax.scatter(sdown,sup,marker='+',label='slope down vs slope up')
1913
+ fig.canvas.draw()
1914
+
1915
+ fig=self.myplotterflat.add("Scatter plots 2")
1916
+ curax=fig.add_ax()
1917
+ curax.scatter(sdown,sup,marker='+')
1918
+ curax.set_xlabel(_('Slope down [-]'))
1919
+ curax.set_ylabel(_('Mean slope up [-]'))
1920
+ curax.set_aspect('equal','box')
1921
+ curax.set_xbound(0,.55)
1922
+ curax.set_ybound(0,.55)
1923
+ curax.set_title('Runoff')
1924
+
1925
+ curax=fig.add_ax()
1926
+ curax.scatter(sdownriv,supriv,marker='+')
1927
+ curax.set_xlabel(_('Slope down [-]'))
1928
+ curax.set_ylabel(_('Mean slope up [-]'))
1929
+ curax.set_aspect('equal','box')
1930
+ curax.set_xbound(0,.55)
1931
+ curax.set_ybound(0,.55)
1932
+ curax.set_title('River')
1933
+
1934
+ curax=fig.add_ax()
1935
+ curax.scatter(sdownriv,suponlyriv,marker='+')
1936
+ curax.set_xlabel(_('Slope down [-]'))
1937
+ curax.set_ylabel(_('Slope up only river [-]'))
1938
+ curax.set_aspect('equal','box')
1939
+ curax.set_xbound(0,.55)
1940
+ curax.set_ybound(0,.55)
1941
+ curax.set_title('River')
1942
+
1943
+ curax=fig.add_ax()
1944
+ curax.scatter(sdownd8,suponlyriv1,marker='+')
1945
+ curax.set_xlabel(_('Slope D8 [-]'))
1946
+ curax.set_ylabel(_('Slope up only river [-]'))
1947
+ curax.set_aspect('equal','box')
1948
+ curax.set_xbound(0,.3)
1949
+ curax.set_ybound(0,.3)
1950
+ curax.set_title('River')
1951
+ fig.canvas.draw()