wolfhece 2.1.16__py3-none-any.whl → 2.1.18__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.
@@ -130,6 +130,33 @@ class Node_Watershed:
130
130
 
131
131
  return np.sqrt(pow(self.x-x,2)+pow(self.y-y,2))
132
132
 
133
+ def get_up_nodes(self, excluded_node:list["Node_Watershed"]=[]):
134
+ """
135
+ Get all upstream nodes
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
+ all_up.append(curup)
148
+ if curup.river:
149
+ all_rivers.append(curup)
150
+ else:
151
+ all_runoff.append(curup)
152
+
153
+ up, river, runoff = curup.get_up_nodes(excluded_node)
154
+ all_up.extend(up)
155
+ all_rivers.extend(river)
156
+ all_runoff.extend(runoff)
157
+
158
+ return all_up, all_rivers, all_runoff
159
+
133
160
  def get_up_nodes_same_sub(self, excluded_node:list["Node_Watershed"]=[]):
134
161
  """
135
162
  Get all upstream nodes in the same sub-basin
@@ -162,30 +189,50 @@ class Node_Watershed:
162
189
 
163
190
  return all_up, all_rivers, all_runoff
164
191
 
192
+ def get_up_runoff_nodes(self):
193
+ """
194
+ Get all upstream runoff nodes
195
+ """
196
+
197
+ all_up = [self]
198
+ for curup in self.up:
199
+ if not curup.river:
200
+ all_up += curup.get_up_runoff_nodes()
201
+
202
+ return all_up
203
+
165
204
  def get_up_runoff_nodes_same_sub(self):
166
205
  """
167
206
  Get all upstream runoff nodes in the same sub-basin
168
207
  """
169
208
 
170
- all_up = []
209
+ all_up = [self]
171
210
  for curup in self.up:
172
211
  if curup.sub == self.sub and not curup.river:
173
- all_up.append(curup)
174
-
175
212
  all_up += curup.get_up_runoff_nodes_same_sub()
176
213
 
177
214
  return all_up
178
215
 
216
+
217
+ def get_up_rivernodes(self):
218
+ """
219
+ Get all upstream river nodes
220
+ """
221
+
222
+ all_up = [self]
223
+ for curup in self.upriver:
224
+ all_up += curup.get_up_rivernodes()
225
+
226
+ return all_up
227
+
179
228
  def get_up_rivernodes_same_sub(self):
180
229
  """
181
230
  Get all upstream river nodes in the same sub-basin
182
231
  """
183
232
 
184
- all_up = []
233
+ all_up = [self]
185
234
  for curup in self.upriver:
186
235
  if curup.sub == self.sub:
187
- all_up.append(curup)
188
-
189
236
  all_up += curup.get_up_rivernodes_same_sub()
190
237
 
191
238
  return all_up
@@ -214,6 +261,28 @@ class Node_Watershed:
214
261
 
215
262
  return np.unique(all_down).tolist()
216
263
 
264
+ def get_down_nodes(self):
265
+ """
266
+ Get all downstream nodes
267
+ """
268
+
269
+ all_down = [self]
270
+ if self.down is not None:
271
+ all_down += self.down.get_down_nodes()
272
+
273
+ return all_down
274
+
275
+ def get_down_nodes_same_sub(self):
276
+ """
277
+ Get all downstream nodes in the same sub-basin
278
+ """
279
+
280
+ all_down = [self]
281
+ if self.down is not None:
282
+ if self.down.sub == self.sub:
283
+ all_down += self.down.get_down_nodes_same_sub()
284
+
285
+ return all_down
217
286
 
218
287
  class RiverSystem:
219
288
  """
@@ -1716,6 +1785,102 @@ class Watershed:
1716
1785
 
1717
1786
  return newsub
1718
1787
 
1788
+ def get_xy_downstream_node(self,
1789
+ starting_node:Node_Watershed,
1790
+ limit_to_sub:bool = False):
1791
+ """
1792
+ Récupération des coordonnées du noeud aval
1793
+ """
1794
+
1795
+ if limit_to_sub:
1796
+ down = starting_node.get_down_nodes_same_sub()
1797
+ else:
1798
+ down = starting_node.get_down_nodes()
1799
+
1800
+ return [[cur.x, cur.y] for cur in down]
1801
+
1802
+ def get_xy_upstream_node(self,
1803
+ starting_node:Node_Watershed,
1804
+ limit_to_sub:bool = False,
1805
+ limit_to_river:bool = False,
1806
+ limit_to_runoff:bool = False) -> list[list[float]]:
1807
+ """
1808
+ Récupération des coordonnées des noeuds amont
1809
+ """
1810
+
1811
+ if limit_to_sub:
1812
+
1813
+ if limit_to_river:
1814
+ up = starting_node.get_up_rivernodes_same_sub()
1815
+ elif limit_to_runoff:
1816
+ up = starting_node.get_up_runoff_nodes_same_sub()
1817
+ else:
1818
+ up, all_river, all_runoff = starting_node.get_up_nodes_same_sub()
1819
+
1820
+ else:
1821
+ if limit_to_river:
1822
+ up = starting_node.get_up_rivernodes()
1823
+ elif limit_to_runoff:
1824
+ up = starting_node.get_up_runoff_nodes()
1825
+ else:
1826
+ up, all_river, all_runoff = starting_node.get_up_nodes()
1827
+
1828
+ return [[cur.x, cur.y] for cur in up]
1829
+
1830
+ def get_array_from_upstream_node(self,
1831
+ starting_node:Node_Watershed,
1832
+ limit_to_sub:bool = False):
1833
+ """
1834
+ Récupération de l'array à partir d'un noeud amont
1835
+ """
1836
+
1837
+ up = self.get_xy_upstream_node(starting_node, limit_to_sub=limit_to_sub)
1838
+
1839
+ xmin = min([x[0] for x in up])
1840
+ xmax = max([x[0] for x in up])
1841
+ ymin = min([x[1] for x in up])
1842
+ ymax = max([x[1] for x in up])
1843
+
1844
+ newhead = header_wolf()
1845
+ newhead.dx = self.header.dx
1846
+ newhead.dy = self.header.dy
1847
+
1848
+ newhead.origx = xmin - self.header.dx/2. - self.header.dx
1849
+ newhead.origy = ymin - self.header.dy/2. - self.header.dy
1850
+
1851
+ newhead.nbx = int(np.ceil((xmax - xmin) / self.header.dx) + 3)
1852
+ newhead.nby = int(np.ceil((ymax - ymin) / self.header.dy) + 3)
1853
+
1854
+ if newhead.nbx == 0 or newhead.nby == 0:
1855
+ logging.error(_('No upstream nodes found!'))
1856
+ return None
1857
+
1858
+ newarray = WolfArray(srcheader=newhead)
1859
+ newarray.array[:,:] = 0.
1860
+
1861
+ ij = newhead.xy2ij_np(up)
1862
+
1863
+ newarray.array[ij[:,0], ij[:,1]] = 1.
1864
+
1865
+ newarray.mask_data(0.)
1866
+
1867
+ return newarray
1868
+
1869
+ def get_vector_from_upstream_node(self,
1870
+ starting_node:Node_Watershed,
1871
+ limit_to_sub:bool = False):
1872
+ """ Return a vector contouring the upstream area """
1873
+
1874
+ up_array = self.get_array_from_upstream_node(starting_node, limit_to_sub=limit_to_sub)
1875
+
1876
+ if up_array is None:
1877
+ return None
1878
+
1879
+ __, __, vect, __ = up_array.suxsuy_contour()
1880
+
1881
+ vect.find_minmax()
1882
+ return vect
1883
+
1719
1884
  def get_subwatershed(self, idx_sorted_or_name:int | str) -> SubWatershed:
1720
1885
  """
1721
1886
  Récupération d'un sous-bassin sur base de l'index trié
wolfhece/wolf_array.py CHANGED
@@ -357,12 +357,23 @@ class header_wolf():
357
357
  :return : numpy array containing (i, j, [k]) indices - shape (n, 2) or (n, 3)
358
358
  """
359
359
 
360
- locxy = xy.copy()
360
+ if isinstance(xy,tuple):
361
+ if len(xy) == 2:
362
+ if (isinstance(xy[0],np.ndarray)) and (isinstance(xy[1],np.ndarray)):
363
+ if len(xy[0]) == len(xy[1]):
364
+ locxy = np.vstack((xy[0], xy[1])).T
365
+ logging.warning(_('get_ij_from_xy_array - xy is a tuple of 2 arrays, it is converted to a 2D array'))
366
+ else:
367
+ locxy = np.array(xy)
368
+ elif isinstance(xy,list):
369
+ locxy = np.array(xy)
370
+ else:
371
+ locxy = xy.copy()
361
372
 
362
373
  if forcedims2:
363
- locij = np.zeros((xy.shape[0],2), dtype=np.int32)
374
+ locij = np.zeros((locxy.shape[0],2), dtype=np.int32)
364
375
  else:
365
- locij = np.zeros(xy.shape, dtype=np.int32)
376
+ locij = np.zeros(locxy.shape, dtype=np.int32)
366
377
 
367
378
  locxy[:,0] -= self.origx
368
379
  locxy[:,1] -= self.origy
@@ -446,7 +457,7 @@ class header_wolf():
446
457
  """
447
458
  Converts array coordinates (numpy cells) to this array's world coodinates.
448
459
 
449
- :param ij = numpy array containing (i, j, [k]) indices
460
+ :param ij = numpy array containing (i, j, [k]) indices - shape (n, 2) or (n, 3)
450
461
  :param scale = scaling of the spatial resolution (dx,dy,[dz])
451
462
  :param aswolf = if True, input is one-based (as Wolf VB6 or Fortran), otherwise 0-based (default Python standard)
452
463
  :param abs = if True, add translation to results (x, y, [z]) (coordinate to global space)
@@ -464,7 +475,18 @@ class header_wolf():
464
475
  if len(ij[0]) == len(ij[1]):
465
476
  ij = np.vstack((ij[0], ij[1])).T
466
477
  logging.warning(_('get_xy_from_ij_array - ij is a tuple of 2 arrays, it is converted to a 2D array'))
467
-
478
+ else:
479
+ ij = np.array(ij)
480
+
481
+ elif isinstance(ij,list):
482
+ if len(ij) == 2:
483
+ if (isinstance(ij[0],np.ndarray)) and (isinstance(ij[1],np.ndarray)):
484
+ if len(ij[0]) == len(ij[1]):
485
+ ij = np.vstack((ij[0], ij[1])).T
486
+ logging.warning(_('get_xy_from_ij_array - ij is a list of 2 arrays, it is converted to a 2D array'))
487
+ else:
488
+ ij = np.array(ij)
489
+
468
490
  if abs:
469
491
  tr_x = self.translx
470
492
  tr_y = self.transly
@@ -529,7 +551,7 @@ class header_wolf():
529
551
 
530
552
  :return : numpy array containing (i, j, [k]) indices - shape (n, 2) or (n, 3)
531
553
  """
532
- return self.get_xy_from_ij_array(xy, scale, aswolf, abs)
554
+ return self.get_ij_from_xy_array(xy, scale, aswolf, abs)
533
555
 
534
556
  def xyz2ijk_np(self, xyz:np.ndarray, scale:float=1., aswolf:bool=False, abs:bool=True) -> np.ndarray:
535
557
  """ alias for get_xy_from_ij_array """
@@ -627,6 +649,49 @@ class header_wolf():
627
649
  filename = str(filename)
628
650
 
629
651
  if filename.endswith('.tif'):
652
+ from osgeo import gdal
653
+
654
+ raster:gdal.Dataset
655
+ raster = gdal.Open(filename)
656
+ geotr = raster.GetGeoTransform()
657
+ self.dx = geotr[1]
658
+ self.dy = abs(geotr[5])
659
+ self.origx = geotr[0]
660
+ self.origy = geotr[3]
661
+ self.nbx = raster.RasterXSize
662
+ self.nby = raster.RasterYSize
663
+
664
+ """
665
+ https://docs.qgis.org/3.34/en/docs/user_manual/processing_algs/gdal/rasterconversion.html
666
+
667
+ 0 — Use Input Layer Data Type
668
+ 1 — Byte (Eight bit unsigned integer (quint8))
669
+ 2 — Int16 (Sixteen bit signed integer (qint16))
670
+ 3 — UInt16 (Sixteen bit unsigned integer (quint16))
671
+ 4 — UInt32 (Thirty two bit unsigned integer (quint32))
672
+ 5 — Int32 (Thirty two bit signed integer (qint32))
673
+ 6 — Float32 (Thirty two bit floating point (float))
674
+ 7 — Float64 (Sixty four bit floating point (double))
675
+ 8 — CInt16 (Complex Int16)
676
+ 9 — CInt32 (Complex Int32)
677
+ 10 — CFloat32 (Complex Float32)
678
+ 11 — CFloat64 (Complex Float64)
679
+ 12 — Int8 (Eight bit signed integer (qint8))
680
+ """
681
+
682
+ dtype = raster.GetRasterBand(1).DataType
683
+
684
+ if dtype in [1, 4]:
685
+ self.wolftype = WOLF_ARRAY_FULL_INTEGER
686
+ elif dtype ==6:
687
+ self.wolftype = WOLF_ARRAY_FULL_SINGLE
688
+ elif dtype == 7:
689
+ self.wolftype = WOLF_ARRAY_FULL_DOUBLE
690
+ else:
691
+ logging.error(_('The datatype of the raster is not supported -- {}'.format(dtype)))
692
+ logging.error(_('Please convert the raster to a supported datatype - or upgrade the code to support this datatype'))
693
+ logging.error(_('See : read_txt_header and import_geotif in wolf_array.py'))
694
+ return
630
695
  return
631
696
 
632
697
  if filename.endswith('.flt'):
@@ -1192,8 +1257,10 @@ class Ops_Array(wx.Frame):
1192
1257
  self.vectmp = vector(name='tmp')
1193
1258
  self.fnsave = ''
1194
1259
 
1195
- self.myzonetmp.add_vector(self.vectmp)
1196
- self.myzones.add_zone(self.myzonetmp)
1260
+ self.myzonetmp.add_vector(self.vectmp, forceparent=True)
1261
+ self.myzones.add_zone(self.myzonetmp, forceparent=True)
1262
+
1263
+ self.myzones.mapviewer = mapviewer
1197
1264
 
1198
1265
  if self.wx_exists:
1199
1266
  self.set_GUI()
@@ -2123,14 +2190,22 @@ class Ops_Array(wx.Frame):
2123
2190
  def OnManageVectors(self, event:wx.MouseEvent):
2124
2191
  """ Open vector manager """
2125
2192
 
2193
+ self.show_structure_OpsVectors()
2194
+
2195
+ def show_structure_OpsVectors(self):
2196
+ """ Show the structure of the vector manager """
2197
+
2126
2198
  if self.mapviewer is not None:
2127
2199
  if self.mapviewer.linked:
2200
+ # The viewer is linked to other viewers
2128
2201
  if self.mapviewer.link_shareopsvect:
2129
- if self.myzones.get_mapviewer() in self.mapviewer.linkedList:
2130
- self.myzones.showstructure()
2131
- return
2202
+ # The viewer shares the vector manager with the other viewers
2203
+ if self.myzones.get_mapviewer() in self.mapviewer.linkedList:
2204
+ # The viewer is in the active linked viewers
2205
+ self.myzones.showstructure()
2206
+ return
2132
2207
 
2133
- self.myzones.showstructure()
2208
+ self.myzones.showstructure()
2134
2209
 
2135
2210
  def OnLoadvec(self, event:wx.MouseEvent):
2136
2211
  """ Load vector file """
@@ -2593,6 +2668,12 @@ class SelectionData():
2593
2668
  self.hideselection = False
2594
2669
  self.numlist_select = 0 # OpenGL list index
2595
2670
 
2671
+ def set_selection_from_list_xy(self, xylist: list[tuple[float, float]]):
2672
+ """ Set the current selection from a list of (x, y) coordinates """
2673
+
2674
+ self.myselection = xylist
2675
+ self.update_nb_nodes_selection()
2676
+
2596
2677
  @property
2597
2678
  def dx(self) -> float:
2598
2679
  """ Resolution in x """
@@ -4285,6 +4366,11 @@ class WolfArray(Element_To_Draw, header_wolf):
4285
4366
 
4286
4367
  return self.mngselection
4287
4368
 
4369
+ @property
4370
+ def Operations(self) -> Ops_Array:
4371
+ """ Return the operations on the array """
4372
+ return self.myops
4373
+
4288
4374
  @property
4289
4375
  def active_blocks(self) -> list["WolfArray"]:
4290
4376
  """ Return the active blocks """
@@ -4623,9 +4709,76 @@ class WolfArray(Element_To_Draw, header_wolf):
4623
4709
 
4624
4710
  if crop is not None :
4625
4711
  if os.path.exists(fn):
4712
+
4713
+ tmpdx = self.dx
4714
+
4715
+ fn_crop = fn + '_crop.tif'
4716
+ if type(crop) is np.ndarray:
4717
+ pass
4718
+ elif type(crop) is list:
4719
+ pass
4720
+ else:
4721
+ if not self.wx_exists:
4722
+ logging.error(_('Crop must be a list or a numpy array with 4 values - xmin, xmax, ymin, ymax'))
4723
+ return
4724
+
4725
+ raster:gdal.Dataset
4726
+ raster = gdal.Open(fn)
4727
+ geotr = raster.GetGeoTransform()
4728
+ self.dx = geotr[1]
4729
+ self.dy = abs(geotr[5])
4730
+
4731
+
4732
+ newcrop = CropDialog(None)
4733
+
4734
+ if self.wx_exists:
4735
+ bounds = self.mapviewer.get_canvas_bounds()
4736
+
4737
+ newcrop.dx.Value = str(self.dx)
4738
+ newcrop.dy.Value = str(self.dy)
4739
+
4740
+ newcrop.dx.Enable(False)
4741
+ newcrop.dy.Enable(False)
4742
+
4743
+ newcrop.ox.Value = str(float((bounds[0] // 50.) * 50.))
4744
+ newcrop.ex.Value = str(float((bounds[2] // 50.) * 50.))
4745
+ newcrop.oy.Value = str(float((bounds[1] // 50.) * 50.))
4746
+ newcrop.ey.Value = str(float((bounds[3] // 50.) * 50.))
4747
+
4748
+ badvalues = True
4749
+ while badvalues:
4750
+ badvalues = False
4751
+
4752
+ ret = newcrop.ShowModal()
4753
+ if ret == wx.ID_CANCEL:
4754
+ newcrop.Destroy()
4755
+ return
4756
+ else:
4757
+ crop = [float(newcrop.ox.Value), float(newcrop.ex.Value),
4758
+ float(newcrop.oy.Value), float(newcrop.ey.Value)]
4759
+
4760
+ tmpdx = float(newcrop.dx.Value)
4761
+ tmpdy = float(newcrop.dy.Value)
4762
+
4763
+ if self.dx != tmpdx or self.dy != tmpdy:
4764
+ if tmpdx / self.dx != tmpdy / self.dy:
4765
+ badvalues = True
4766
+
4767
+ newcrop.Destroy()
4768
+
4626
4769
  xmin, xmax, ymin, ymax = crop
4627
- gdal.Translate(fn+'_crop.tif', fn, projWin=[xmin, ymax, xmax, ymin])
4628
- fn +='_crop.tif'
4770
+
4771
+ with wx.FileDialog(None, _('Save the cropped file for later'), wildcard="Tiff files (*.tif)|*.tif",
4772
+ style=wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT) as fileDialog:
4773
+
4774
+ if fileDialog.ShowModal() == wx.ID_CANCEL:
4775
+ return
4776
+
4777
+ fn_crop = fileDialog.GetPath()
4778
+
4779
+
4780
+ gdal.Translate(fn_crop, fn, projWin=[xmin, ymax, xmax, ymin])
4781
+ fn = fn_crop
4629
4782
 
4630
4783
  raster:gdal.Dataset
4631
4784
  raster = gdal.Open(fn)
@@ -6409,6 +6562,13 @@ class WolfArray(Element_To_Draw, header_wolf):
6409
6562
  else:
6410
6563
  newcrop = CropDialog(None)
6411
6564
 
6565
+ if self.mapviewer is not None:
6566
+ bounds = self.mapviewer.get_canvas_bounds()
6567
+ newcrop.ox.Value = float((bounds[0] // 50.) * 50.)
6568
+ newcrop.ex.Value = float((bounds[2] // 50.) * 50.)
6569
+ newcrop.oy.Value = float((bounds[1] // 50.) * 50.)
6570
+ newcrop.ey.Value = float((bounds[3] // 50.) * 50.)
6571
+
6412
6572
  badvalues = True
6413
6573
  while badvalues:
6414
6574
  badvalues = False
@@ -7663,7 +7823,7 @@ class WolfArrayMB(WolfArray):
7663
7823
  """
7664
7824
 
7665
7825
  if copyarray:
7666
- arr = WolfArray(mold=arr)
7826
+ arr = WolfArray(mold=arr, nullvalue=arr.nullvalue)
7667
7827
  force_idx = True
7668
7828
 
7669
7829
  if force_idx: