wolfhece 2.2.34__py3-none-any.whl → 2.2.35__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/PyCrosssections.py +293 -16
- wolfhece/PyDraw.py +24 -7
- wolfhece/PyGui.py +9 -1
- wolfhece/PyVertexvectors.py +1 -1
- wolfhece/__init__.py +1 -0
- wolfhece/analyze_poly.py +1 -1
- wolfhece/apps/version.py +1 -1
- wolfhece/wolf_array.py +1233 -305
- wolfhece/wolf_zi_db.py +155 -0
- {wolfhece-2.2.34.dist-info → wolfhece-2.2.35.dist-info}/METADATA +1 -1
- {wolfhece-2.2.34.dist-info → wolfhece-2.2.35.dist-info}/RECORD +14 -14
- {wolfhece-2.2.34.dist-info → wolfhece-2.2.35.dist-info}/WHEEL +0 -0
- {wolfhece-2.2.34.dist-info → wolfhece-2.2.35.dist-info}/entry_points.txt +0 -0
- {wolfhece-2.2.34.dist-info → wolfhece-2.2.35.dist-info}/top_level.txt +0 -0
wolfhece/wolf_array.py
CHANGED
@@ -756,7 +756,7 @@ class header_wolf():
|
|
756
756
|
def ij2xy_np(self, ij:np.ndarray, scale:float=1., aswolf:bool=False, abs:bool=True) -> np.ndarray:
|
757
757
|
""" alias for get_xy_from_ij_array
|
758
758
|
|
759
|
-
:param ij: numpy array containing (i, j, [k]) indices
|
759
|
+
:param ij: numpy array containing (i, j, [k]) indices - like np.argwhere() - shape (n, 2) or (n, 3)
|
760
760
|
:param scale: scaling of the spatial resolution (dx,dy,[dz])
|
761
761
|
:param aswolf: if True, input is one-based (as Wolf VB6 or Fortran), otherwise 0-based (default Python standard)
|
762
762
|
:param abs: if True, add translation to results (x, y, [z]) (coordinate to global space)
|
@@ -770,6 +770,13 @@ class header_wolf():
|
|
770
770
|
"""
|
771
771
|
return self.get_xy_from_ij_array(ij, scale, aswolf, abs)
|
772
772
|
|
773
|
+
def ij2xy_np_ij_from_npwhere(self, ij:np.ndarray, scale:float=1., aswolf:bool=False, abs:bool=True) -> np.ndarray:
|
774
|
+
""" alias for get_xy_from_ij_array but with ij as np.where result
|
775
|
+
"""
|
776
|
+
|
777
|
+
ij = np.vstack((ij[0], ij[1])).T
|
778
|
+
return self.get_xy_from_ij_array(ij, scale, aswolf, abs)
|
779
|
+
|
773
780
|
def xy2ij(self, x:float, y:float, z:float=0., scale:float=1., aswolf:bool=False, abs:bool=True, forcedims2:bool=False) -> Union[tuple[np.int32,np.int32], tuple[np.int32,np.int32,np.int32]]:
|
774
781
|
""" alias for get_ij_from_xy """
|
775
782
|
return self.get_ij_from_xy(x, y, z, scale, aswolf, abs, forcedims2)
|
@@ -1874,6 +1881,11 @@ class Ops_Array(wx.Frame):
|
|
1874
1881
|
if self.wx_exists:
|
1875
1882
|
self.set_GUI()
|
1876
1883
|
|
1884
|
+
@property
|
1885
|
+
def usemask(self):
|
1886
|
+
""" Return the usemask Value """
|
1887
|
+
return self.selectrestricttomask.GetValue()
|
1888
|
+
|
1877
1889
|
@property
|
1878
1890
|
def idx(self):
|
1879
1891
|
""" Return the idx of the parentarray """
|
@@ -2192,9 +2204,15 @@ class Ops_Array(wx.Frame):
|
|
2192
2204
|
bSizer16_2 = wx.BoxSizer(wx.VERTICAL)
|
2193
2205
|
bSizer16_3 = wx.BoxSizer(wx.VERTICAL)
|
2194
2206
|
|
2195
|
-
selectmethodChoices = [_("by clicks"),
|
2196
|
-
_("inside
|
2197
|
-
_("
|
2207
|
+
selectmethodChoices = [_("by clicks"),
|
2208
|
+
_("inside active vector"),
|
2209
|
+
_("inside active zone"),
|
2210
|
+
_("inside temporary vector"),
|
2211
|
+
_("along active vector"),
|
2212
|
+
_("along active zone"),
|
2213
|
+
_("along temporary vector"),
|
2214
|
+
# _("outside active vector")
|
2215
|
+
]
|
2198
2216
|
self.selectmethod = wx.RadioBox(self.selection, wx.ID_ANY, _("How to select nodes?"), wx.DefaultPosition,
|
2199
2217
|
wx.DefaultSize, selectmethodChoices, 1, wx.RA_SPECIFY_COLS)
|
2200
2218
|
self.selectmethod.SetSelection(0)
|
@@ -2445,21 +2463,65 @@ class Ops_Array(wx.Frame):
|
|
2445
2463
|
sizermask.Add(maskdata, 1, wx.EXPAND)
|
2446
2464
|
maskdata.Bind(wx.EVT_BUTTON, self.Onmask)
|
2447
2465
|
|
2466
|
+
sizer_unmask = wx.BoxSizer(wx.HORIZONTAL)
|
2448
2467
|
unmaskall = wx.Button(self.mask, wx.ID_ANY, _("Unmask all"), wx.DefaultPosition, wx.DefaultSize, 0)
|
2449
|
-
|
2468
|
+
sizer_unmask.Add(unmaskall, 1, wx.EXPAND)
|
2450
2469
|
unmaskall.Bind(wx.EVT_BUTTON, self.Unmaskall)
|
2451
2470
|
unmaskall.SetToolTip(_("Unmask all values in the current array"))
|
2452
2471
|
|
2453
2472
|
unmasksel = wx.Button(self.mask, wx.ID_ANY, _("Unmask selection"), wx.DefaultPosition, wx.DefaultSize, 0)
|
2454
|
-
|
2455
|
-
unmasksel.Bind(wx.EVT_BUTTON, self.
|
2473
|
+
sizer_unmask.Add(unmasksel, 1, wx.EXPAND)
|
2474
|
+
unmasksel.Bind(wx.EVT_BUTTON, self.Unmask_selection)
|
2456
2475
|
unmasksel.SetToolTip(_("Unmask all values in the current selection \n If you wish to unmask some of the currently masked data, you have to first select the desired nodes by unchecking the 'Use mask to retrict' on the 'Selection' panel, otherwise it is impossible to select these nodes"))
|
2457
2476
|
|
2477
|
+
sizermask.Add(sizer_unmask, 1, wx.EXPAND)
|
2478
|
+
|
2458
2479
|
invertmask = wx.Button(self.mask, wx.ID_ANY, _("Invert mask"), wx.DefaultPosition, wx.DefaultSize, 0)
|
2459
2480
|
sizermask.Add(invertmask, 1, wx.EXPAND)
|
2460
2481
|
invertmask.Bind(wx.EVT_BUTTON, self.InvertMask)
|
2461
2482
|
invertmask.SetToolTip(_("Logical operation on mask -- mask = ~mask"))
|
2462
2483
|
|
2484
|
+
sizer_maskunmask_poly = wx.BoxSizer(wx.HORIZONTAL)
|
2485
|
+
|
2486
|
+
mask_in_poly = wx.Button(self.mask, wx.ID_ANY, _("Mask inside active vector (+NoData)"), wx.DefaultPosition, wx.DefaultSize, 0)
|
2487
|
+
sizer_maskunmask_poly.Add(mask_in_poly, 1, wx.EXPAND)
|
2488
|
+
mask_in_poly.Bind(wx.EVT_BUTTON, self.Mask_inside_active_vector)
|
2489
|
+
mask_in_poly.SetToolTip(_("Mask all values inside the active vector and set NoData to masked values"))
|
2490
|
+
|
2491
|
+
mask_out_poly = wx.Button(self.mask, wx.ID_ANY, _("Mask outside active vector (+NoData)"), wx.DefaultPosition, wx.DefaultSize, 0)
|
2492
|
+
sizer_maskunmask_poly.Add(mask_out_poly, 1, wx.EXPAND)
|
2493
|
+
mask_out_poly.Bind(wx.EVT_BUTTON, self.Mask_outside_active_vector)
|
2494
|
+
mask_out_poly.SetToolTip(_("Mask all values outside the active vector and set NoData to masked values"))
|
2495
|
+
|
2496
|
+
sizermask.Add(sizer_maskunmask_poly, 1, wx.EXPAND)
|
2497
|
+
|
2498
|
+
sizer_maskunmask_poly_wonodata = wx.BoxSizer(wx.HORIZONTAL)
|
2499
|
+
|
2500
|
+
mask_in_poly_nodata = wx.Button(self.mask, wx.ID_ANY, _("Mask inside active vector"), wx.DefaultPosition, wx.DefaultSize, 0)
|
2501
|
+
sizer_maskunmask_poly_wonodata.Add(mask_in_poly_nodata, 1, wx.EXPAND)
|
2502
|
+
mask_in_poly_nodata.Bind(wx.EVT_BUTTON, self.Mask_inside_active_vector_wo_nodata)
|
2503
|
+
mask_in_poly_nodata.SetToolTip(_("Mask all values inside the active vector"))
|
2504
|
+
|
2505
|
+
mask_out_poly_nodata = wx.Button(self.mask, wx.ID_ANY, _("Mask outside active vector"), wx.DefaultPosition, wx.DefaultSize, 0)
|
2506
|
+
sizer_maskunmask_poly_wonodata.Add(mask_out_poly_nodata, 1, wx.EXPAND)
|
2507
|
+
mask_out_poly_nodata.Bind(wx.EVT_BUTTON, self.Mask_outside_active_vector_wo_nodata)
|
2508
|
+
mask_out_poly_nodata.SetToolTip(_("Mask all values outside the active vector"))
|
2509
|
+
|
2510
|
+
sizermask.Add(sizer_maskunmask_poly_wonodata, 1, wx.EXPAND)
|
2511
|
+
|
2512
|
+
mask_inside_polys =wx.BoxSizer(wx.HORIZONTAL)
|
2513
|
+
mask_in_polys = wx.Button(self.mask, wx.ID_ANY, _("Mask inside active zone (+NoData)"), wx.DefaultPosition, wx.DefaultSize, 0)
|
2514
|
+
mask_inside_polys.Add(mask_in_polys, 1, wx.EXPAND)
|
2515
|
+
mask_in_polys.Bind(wx.EVT_BUTTON, self.Mask_inside_active_zone)
|
2516
|
+
mask_in_polys.SetToolTip(_("Mask all values inside the active zone and set NoData to masked values"))
|
2517
|
+
|
2518
|
+
mask_inside_polys_nodata = wx.Button(self.mask, wx.ID_ANY, _("Mask inside active zone"), wx.DefaultPosition, wx.DefaultSize, 0)
|
2519
|
+
mask_inside_polys.Add(mask_inside_polys_nodata, 1, wx.EXPAND)
|
2520
|
+
mask_inside_polys_nodata.Bind(wx.EVT_BUTTON, self.Mask_inside_active_zone_wo_nodata)
|
2521
|
+
mask_inside_polys_nodata.SetToolTip(_("Mask all values inside the active zone"))
|
2522
|
+
|
2523
|
+
sizermask.Add(mask_inside_polys, 1, wx.EXPAND)
|
2524
|
+
|
2463
2525
|
self.mask.Layout()
|
2464
2526
|
sizermask.Fit(self.mask)
|
2465
2527
|
|
@@ -2630,13 +2692,13 @@ class Ops_Array(wx.Frame):
|
|
2630
2692
|
self.parentarray.mask_reset()
|
2631
2693
|
self.refresh_array()
|
2632
2694
|
|
2633
|
-
def
|
2695
|
+
def Unmask_selection(self, event:wx.MouseEvent):
|
2634
2696
|
"""
|
2635
2697
|
Enlève le masque des éléments sélectionnés
|
2636
2698
|
@author Pierre Archambeau
|
2637
2699
|
"""
|
2638
2700
|
|
2639
|
-
self.parentarray.SelectionData.
|
2701
|
+
self.parentarray.SelectionData.unmask_selection()
|
2640
2702
|
|
2641
2703
|
|
2642
2704
|
def InvertMask(self, event: wx.MouseEvent):
|
@@ -2645,6 +2707,42 @@ class Ops_Array(wx.Frame):
|
|
2645
2707
|
self.parentarray.mask_invert()
|
2646
2708
|
self.refresh_array()
|
2647
2709
|
|
2710
|
+
def Mask_inside_active_vector(self, event: wx.MouseEvent):
|
2711
|
+
""" Mask inside the active vector """
|
2712
|
+
if self.active_vector is not None:
|
2713
|
+
self.parentarray.mask_insidepoly(self.active_vector,)
|
2714
|
+
self.refresh_array()
|
2715
|
+
|
2716
|
+
def Mask_inside_active_vector_wo_nodata(self, event: wx.MouseEvent):
|
2717
|
+
""" Mask inside the active vector without setting NoData """
|
2718
|
+
if self.active_vector is not None:
|
2719
|
+
self.parentarray.mask_insidepoly(self.active_vector, set_nullvalue=False)
|
2720
|
+
self.refresh_array()
|
2721
|
+
|
2722
|
+
def Mask_outside_active_vector(self, event: wx.MouseEvent):
|
2723
|
+
""" Mask outside the active vector """
|
2724
|
+
if self.active_vector is not None:
|
2725
|
+
self.parentarray.mask_outsidepoly(self.active_vector)
|
2726
|
+
self.refresh_array()
|
2727
|
+
|
2728
|
+
def Mask_inside_active_zone(self, event: wx.MouseEvent):
|
2729
|
+
""" Mask inside the active zone """
|
2730
|
+
if self.active_zone is not None:
|
2731
|
+
self.parentarray.mask_insidepolys(self.active_zone.myvectors)
|
2732
|
+
self.refresh_array()
|
2733
|
+
|
2734
|
+
def Mask_inside_active_zone_wo_nodata(self, event: wx.MouseEvent):
|
2735
|
+
""" Mask inside the active zone without setting NoData """
|
2736
|
+
if self.active_zone is not None:
|
2737
|
+
self.parentarray.mask_insidepolys(self.active_zone.myvectors, set_nullvalue=False)
|
2738
|
+
self.refresh_array()
|
2739
|
+
|
2740
|
+
def Mask_outside_active_vector_wo_nodata(self, event: wx.MouseEvent):
|
2741
|
+
""" Mask outside the active vector without setting NoData """
|
2742
|
+
if self.active_vector is not None:
|
2743
|
+
self.parentarray.mask_outsidepoly(self.active_vector, set_nullvalue=False)
|
2744
|
+
self.refresh_array()
|
2745
|
+
|
2648
2746
|
def interp2Dpolygons(self, event: wx.MouseEvent):
|
2649
2747
|
"""
|
2650
2748
|
Bouton d'interpolation sous tous les polygones d'une zone
|
@@ -2685,48 +2783,6 @@ class Ops_Array(wx.Frame):
|
|
2685
2783
|
|
2686
2784
|
self.parentarray.SelectionData.volumesurface()
|
2687
2785
|
|
2688
|
-
# def _volumesurface(self, show=True):
|
2689
|
-
# """
|
2690
|
-
# Evaluation of stage-storage-surface relation
|
2691
|
-
# """
|
2692
|
-
|
2693
|
-
# if self.mapviewer is not None:
|
2694
|
-
# if self.mapviewer.linked:
|
2695
|
-
# array1 = self.mapviewer.linkedList[0].active_array
|
2696
|
-
# array2 = self.mapviewer.linkedList[1].active_array
|
2697
|
-
|
2698
|
-
# # transfert des mailles sélectionnées dans l'autre matrice
|
2699
|
-
# if array1 is self.parentarray:
|
2700
|
-
# array2.mngselection.myselection = array1.mngselection.myselection.copy()
|
2701
|
-
# if array2 is self.parentarray:
|
2702
|
-
# array1.mngselection.myselection = array2.mngselection.myselection.copy()
|
2703
|
-
|
2704
|
-
# if len(self.parentarray.mngselection.myselection) == 0 or self.parentarray.mngselection.myselection == 'all':
|
2705
|
-
# myarray = array1
|
2706
|
-
# axs = myarray.volume_estimation()
|
2707
|
-
# myarray = array2
|
2708
|
-
# axs = myarray.volume_estimation(axs)
|
2709
|
-
# else:
|
2710
|
-
# myarray = array1.mngselection.get_newarray()
|
2711
|
-
# axs = myarray.volume_estimation()
|
2712
|
-
# myarray = array2.mngselection.get_newarray()
|
2713
|
-
# axs = myarray.volume_estimation(axs)
|
2714
|
-
# else:
|
2715
|
-
# if len(self.parentarray.mngselection.myselection) == 0 or self.parentarray.mngselection.myselection == 'all':
|
2716
|
-
# myarray = self.parentarray
|
2717
|
-
# else:
|
2718
|
-
# myarray = self.parentarray.mngselection.get_newarray()
|
2719
|
-
# myarray.volume_estimation()
|
2720
|
-
# else:
|
2721
|
-
# if len(self.parentarray.mngselection.myselection) == 0 or self.parentarray.mngselection.myselection == 'all':
|
2722
|
-
# myarray = self.parentarray
|
2723
|
-
# else:
|
2724
|
-
# myarray = self.parentarray.mngselection.get_newarray()
|
2725
|
-
# myarray.volume_estimation()
|
2726
|
-
|
2727
|
-
# if show:
|
2728
|
-
# plt.show()
|
2729
|
-
|
2730
2786
|
def OnAllSelect(self, event):
|
2731
2787
|
"""
|
2732
2788
|
Select all --> just put "all" in "myselection"
|
@@ -2784,9 +2840,7 @@ class Ops_Array(wx.Frame):
|
|
2784
2840
|
else:
|
2785
2841
|
structure = np.ones((3, 3))
|
2786
2842
|
|
2787
|
-
|
2788
|
-
|
2789
|
-
self.parentarray.SelectionData.erode_selection(nb, usemask, structure)
|
2843
|
+
self.parentarray.SelectionData.erode_selection(nb, self.usemask, structure)
|
2790
2844
|
self.refresh_array()
|
2791
2845
|
|
2792
2846
|
def OnExpandSelection(self, event):
|
@@ -2797,9 +2851,7 @@ class Ops_Array(wx.Frame):
|
|
2797
2851
|
else:
|
2798
2852
|
structure = np.ones((3, 3))
|
2799
2853
|
|
2800
|
-
|
2801
|
-
|
2802
|
-
self.parentarray.SelectionData.dilate_selection(nb, usemask, structure)
|
2854
|
+
self.parentarray.SelectionData.dilate_selection(nb, self.usemask, structure)
|
2803
2855
|
self.refresh_array()
|
2804
2856
|
|
2805
2857
|
def OnExpandUnselectInterior(self, event):
|
@@ -2811,9 +2863,7 @@ class Ops_Array(wx.Frame):
|
|
2811
2863
|
else:
|
2812
2864
|
structure = np.ones((3, 3))
|
2813
2865
|
|
2814
|
-
|
2815
|
-
|
2816
|
-
self.parentarray.SelectionData.dilate_contour_selection(nb, usemask, structure)
|
2866
|
+
self.parentarray.SelectionData.dilate_contour_selection(nb, self.usemask, structure)
|
2817
2867
|
self.refresh_array()
|
2818
2868
|
|
2819
2869
|
def OnUnselectInterior(self, event):
|
@@ -2947,7 +2997,7 @@ class Ops_Array(wx.Frame):
|
|
2947
2997
|
# condition value
|
2948
2998
|
curcondvalue = float(self.condvalue.GetValue())
|
2949
2999
|
|
2950
|
-
self.parentarray.SelectionData.condition_select(curcond, curcondvalue)
|
3000
|
+
self.parentarray.SelectionData.condition_select(curcond, curcondvalue, usemask=self.usemask)
|
2951
3001
|
|
2952
3002
|
self.refresh_array()
|
2953
3003
|
|
@@ -3470,6 +3520,21 @@ class Ops_Array(wx.Frame):
|
|
3470
3520
|
self._select_vector_inside_manager(self.active_vector)
|
3471
3521
|
self.refresh_array()
|
3472
3522
|
|
3523
|
+
def select_vector_outside_manager(self):
|
3524
|
+
""" Select nodes outside the active vector (manager) """
|
3525
|
+
if self.active_vector is None:
|
3526
|
+
logging.warning(_('Please activate a vector !'))
|
3527
|
+
return
|
3528
|
+
|
3529
|
+
if self.active_vector.nbvertices == 0:
|
3530
|
+
logging.warning(_('Please add points to vector or select another !'))
|
3531
|
+
return
|
3532
|
+
|
3533
|
+
logging.info(_('Select nodes outside the active polygon/vector...'))
|
3534
|
+
self._select_vector_outside_manager(self.active_vector)
|
3535
|
+
self.refresh_array()
|
3536
|
+
|
3537
|
+
|
3473
3538
|
def _select_vector_inside_manager(self, vect: vector):
|
3474
3539
|
""" Select nodes inside a vector or set action to add vertices to a vector by clicks"""
|
3475
3540
|
|
@@ -3488,6 +3553,24 @@ class Ops_Array(wx.Frame):
|
|
3488
3553
|
firstvert = wolfvertex(0., 0.)
|
3489
3554
|
self.vectmp.add_vertex(firstvert)
|
3490
3555
|
|
3556
|
+
def _select_vector_outside_manager(self, vect: vector):
|
3557
|
+
""" Select nodes outside a vector or set action to add vertices to a vector by clicks"""
|
3558
|
+
|
3559
|
+
if vect.nbvertices > 2:
|
3560
|
+
self.parentarray.SelectionData.select_outsidepoly(vect)
|
3561
|
+
|
3562
|
+
elif self.mapviewer is not None:
|
3563
|
+
if vect.nbvertices < 3:
|
3564
|
+
logging.info(_('Please add points to vector !'))
|
3565
|
+
|
3566
|
+
self.mapviewer.start_action('select by vector outside', _('Please draw a polygon...'))
|
3567
|
+
self.mapviewer.active_array = self.parentarray
|
3568
|
+
self.mapviewer.set_label_selecteditem(self.parentarray.idx)
|
3569
|
+
self.Active_vector(vect)
|
3570
|
+
|
3571
|
+
firstvert = wolfvertex(0., 0.)
|
3572
|
+
self.vectmp.add_vertex(firstvert)
|
3573
|
+
|
3491
3574
|
def select_zone_under_manager(self):
|
3492
3575
|
""" Select nodes along the active zone (manager) """
|
3493
3576
|
|
@@ -3597,6 +3680,9 @@ class Ops_Array(wx.Frame):
|
|
3597
3680
|
logging.info(_(' Choose vector by clicks...'))
|
3598
3681
|
logging.info(_(''))
|
3599
3682
|
self.select_vector_under_tmp()
|
3683
|
+
# elif id == 7:
|
3684
|
+
# logging.info(_('Node selection outside active vector (manager)'))
|
3685
|
+
# self.select_vector_outside_manager()
|
3600
3686
|
|
3601
3687
|
def onclose(self, event:wx.MouseEvent):
|
3602
3688
|
""" Hide the window """
|
@@ -3819,40 +3905,370 @@ class Ops_Array(wx.Frame):
|
|
3819
3905
|
dlg.Destroy()
|
3820
3906
|
|
3821
3907
|
|
3908
|
+
ALL_SELECTED = 'all'
|
3909
|
+
|
3910
|
+
class StorageMode(Enum):
|
3911
|
+
""" Storage mode for selections in WolfArray """
|
3912
|
+
LIST = 0
|
3913
|
+
ARRAY = 1
|
3914
|
+
|
3822
3915
|
class SelectionData():
|
3823
3916
|
"""
|
3824
3917
|
User-selected data in a WolfArray
|
3825
3918
|
|
3826
3919
|
Contains two storage elements :
|
3827
|
-
- myselection
|
3920
|
+
- myselection
|
3828
3921
|
- selections( dict): Stored selection(s) to be used, for example, in a spatial interpolation operation.
|
3829
3922
|
These selections are only lost in the event of a general reset.
|
3830
3923
|
|
3831
3924
|
The selected nodes are stored using their "world" spatial coordinates so that they can be easily transferred to other objects.
|
3925
|
+
|
3926
|
+
The "myselection" can be stored in two modes :
|
3927
|
+
- LIST: 'all' or list of (x, y) coordinates or tuple of ('all', np.ndarray) for all nodes and excluded nodes
|
3928
|
+
- ARRAY: np.ndarray of selected nodes (boolean array) with shape (nbx, nby) where nbx and nby are the number of nodes in the x and y directions respectively.
|
3929
|
+
The boolean array is True if the node is selected, False if not selected.
|
3930
|
+
|
3931
|
+
The selection can be converted from one mode to another automatically based on the number of nodes in the array.
|
3932
|
+
If the number of nodes exceeds a threshold (default is 100,000), the selection is stored as an ARRAY.
|
3933
|
+
Otherwise, it is stored as a LIST.
|
3934
|
+
|
3935
|
+
The selection can be forced to a specific storage mode using the `force_storage_mode` method.
|
3832
3936
|
"""
|
3833
3937
|
|
3834
|
-
|
3938
|
+
# 'all' or list of (x, y) coordinates or a tuple of ('all', np.ndarray) for all nodes and excluded nodes
|
3939
|
+
_myselection:list[tuple[float, float]] | str | tuple[str, np.ndarray]
|
3940
|
+
|
3835
3941
|
selections: dict[str:dict['select':list[tuple[float, float]], 'idgllist':int, 'color':list[float]]]
|
3836
3942
|
|
3837
|
-
def __init__(self, parent:"WolfArray") -> None:
|
3943
|
+
def __init__(self, parent:"WolfArray", threshold_array_mode:int = 100_000) -> None:
|
3944
|
+
""" Initialize the SelectionData object.
|
3945
|
+
|
3946
|
+
:param parent: The parent WolfArray object to which this selection data belongs.
|
3947
|
+
:param threshold_array_mode: The threshold number of nodes to switch from LIST to ARRAY storage mode.
|
3948
|
+
"""
|
3838
3949
|
|
3839
3950
|
self.parent: WolfArray
|
3840
3951
|
self.parent = parent
|
3841
3952
|
|
3842
3953
|
self.wx_exists = wx.GetApp() is not None
|
3843
3954
|
|
3844
|
-
self.
|
3955
|
+
self._myselection = []
|
3845
3956
|
self.selections = {}
|
3846
3957
|
|
3958
|
+
self._boolarray: np.ndarray | None = None # boolean array for selection - True if selected, False if not selected
|
3959
|
+
|
3960
|
+
self._storage_mode = StorageMode.LIST # 0: 'all' or list of (x, y) coordinates or tuple of ('all', np.ndarray) for all nodes and excluded nodes, 1 for np.ndarray of selected nodes
|
3961
|
+
|
3847
3962
|
self.update_plot_selection = False # force to update OpenGL list if True
|
3848
3963
|
self.hideselection = False
|
3849
3964
|
self.numlist_select = 0 # OpenGL list index
|
3965
|
+
self.threshold_array_mode = threshold_array_mode # Default threshold for switching storage mode
|
3966
|
+
|
3967
|
+
def _auto_storage_mode(self):
|
3968
|
+
""" Choose the storage mode based on the number of nodes in the array """
|
3969
|
+
|
3970
|
+
if self.nb > self.threshold_array_mode:
|
3971
|
+
_storage_mode = StorageMode.ARRAY
|
3972
|
+
else:
|
3973
|
+
_storage_mode = StorageMode.LIST
|
3974
|
+
|
3975
|
+
if self._storage_mode != _storage_mode:
|
3976
|
+
self._convert_to_storage_mode(_storage_mode)
|
3977
|
+
|
3978
|
+
def _convert_to_storage_mode(self, new_mode:StorageMode):
|
3979
|
+
""" Convert the selection to the new storage mode.
|
3980
|
+
|
3981
|
+
:param new_mode: The new storage mode to convert to (StorageMode.LIST or StorageMode.ARRAY).
|
3982
|
+
"""
|
3983
|
+
|
3984
|
+
logging.info('Switching storage mode to {}'.format(new_mode))
|
3985
|
+
|
3986
|
+
if self._storage_mode == StorageMode.LIST:
|
3987
|
+
# Convert from 'all' or list of (x, y) coordinates or tuple of ('all', np.ndarray) to np.ndarray
|
3988
|
+
_bool_array = self._myselection_as_array
|
3989
|
+
|
3990
|
+
if self._myselection == ALL_SELECTED:
|
3991
|
+
_bool_array[:,:] = True
|
3992
|
+
|
3993
|
+
elif isinstance(self._myselection, list):
|
3994
|
+
|
3995
|
+
if len(self._myselection) == 0:
|
3996
|
+
_bool_array[:,:] = False
|
3997
|
+
else:
|
3998
|
+
_bool_array[:,:] = False
|
3999
|
+
xy = np.asarray(self._myselection)
|
4000
|
+
ij = self.parent.xy2ij_np(xy)
|
4001
|
+
_bool_array[ij[:, 0], ij[:, 1]] = True
|
4002
|
+
|
4003
|
+
elif isinstance(self._myselection, tuple) and len(self._myselection) == 2 and self._myselection[0] == ALL_SELECTED:
|
4004
|
+
_bool_array[:,:] = True
|
4005
|
+
_excluded_nodes = self._myselection[1]
|
4006
|
+
|
4007
|
+
if _excluded_nodes.size > 0:
|
4008
|
+
_excluded_nodes = self.parent.xy2ij_np(_excluded_nodes)
|
4009
|
+
_bool_array[_excluded_nodes[:, 0], _excluded_nodes[:, 1]] = False
|
4010
|
+
|
4011
|
+
self._myselection = []
|
4012
|
+
|
4013
|
+
elif self._storage_mode == StorageMode.ARRAY:
|
4014
|
+
|
4015
|
+
if self.is_all_selected():
|
4016
|
+
self._myselection = ALL_SELECTED
|
4017
|
+
else:
|
4018
|
+
nb = self.nb
|
4019
|
+
if nb == 0:
|
4020
|
+
self._myselection = []
|
4021
|
+
elif nb / max(self.parent.nbnotnull, 1) > 0.5:
|
4022
|
+
ij_not_selected = np.argwhere(~self._myselection_as_array)
|
4023
|
+
xy_not_selected = self.parent.ij2xy_np(ij_not_selected)
|
4024
|
+
self._myselection = (ALL_SELECTED, xy_not_selected)
|
4025
|
+
else:
|
4026
|
+
ij_selected = np.argwhere(self._myselection_as_array)
|
4027
|
+
xy_selected = self.parent.ij2xy_np(ij_selected)
|
4028
|
+
self._myselection = list(map(tuple, xy_selected))
|
4029
|
+
|
4030
|
+
self._storage_mode = new_mode
|
4031
|
+
|
4032
|
+
def force_storage_mode(self, new_mode:StorageMode):
|
4033
|
+
""" Force the storage mode to the new mode.
|
4034
|
+
|
4035
|
+
:param new_mode: The new storage mode to force (StorageMode.LIST or StorageMode.ARRAY).
|
4036
|
+
"""
|
4037
|
+
|
4038
|
+
if new_mode not in StorageMode:
|
4039
|
+
logging.error('Invalid storage mode - must be LIST or ARRAY')
|
4040
|
+
return
|
4041
|
+
if self._storage_mode != new_mode:
|
4042
|
+
self._convert_to_storage_mode(new_mode)
|
4043
|
+
|
4044
|
+
@property
|
4045
|
+
def myselection(self) -> list[tuple[float, float]] | str:
|
4046
|
+
""" Current selection of nodes.
|
4047
|
+
|
4048
|
+
Returns:
|
4049
|
+
- 'all' if all nodes are selected
|
4050
|
+
- list of (x, y) coordinates if specific nodes are selected
|
4051
|
+
"""
|
4052
|
+
|
4053
|
+
if self._storage_mode == StorageMode.LIST:
|
4054
|
+
if self._myselection == ALL_SELECTED:
|
4055
|
+
return ALL_SELECTED
|
4056
|
+
elif isinstance(self._myselection, list):
|
4057
|
+
return self._myselection
|
4058
|
+
elif isinstance(self._myselection, tuple) and len(self._myselection) == 2 and self._myselection[0] == ALL_SELECTED:
|
4059
|
+
|
4060
|
+
excluded_nodes = self._myselection[1]
|
4061
|
+
if excluded_nodes.shape[0] == 0:
|
4062
|
+
return ALL_SELECTED
|
4063
|
+
else:
|
4064
|
+
if self.parent.usemask:
|
4065
|
+
# Return all nodes except the excluded ones
|
4066
|
+
loc_mask = ~ self.parent.array.mask.copy()
|
4067
|
+
else:
|
4068
|
+
loc_mask = np.ones((self.parent.nbx, self.parent.nby), dtype=bool)
|
4069
|
+
|
4070
|
+
ij_excluded = self.parent.xy2ij_np(excluded_nodes)
|
4071
|
+
loc_mask[ij_excluded[:, 0], ij_excluded[:, 1]] = False
|
4072
|
+
ij = np.argwhere(loc_mask)
|
4073
|
+
xy = self.parent.ij2xy_np(ij)
|
4074
|
+
return list(map(tuple, xy)) # Convert to list of tuples
|
4075
|
+
|
4076
|
+
elif self._storage_mode == StorageMode.ARRAY:
|
4077
|
+
if self.is_all_selected():
|
4078
|
+
return ALL_SELECTED
|
4079
|
+
else:
|
4080
|
+
nbsel = np.count_nonzero(self._myselection_as_array)
|
4081
|
+
if nbsel == 0:
|
4082
|
+
return []
|
4083
|
+
else:
|
4084
|
+
# Convert boolean array to list of (x, y) coordinates
|
4085
|
+
ij_selected = np.argwhere(self._myselection_as_array)
|
4086
|
+
xy_selected = self.parent.ij2xy_np(ij_selected)
|
4087
|
+
return list(map(tuple, xy_selected))
|
4088
|
+
|
4089
|
+
else:
|
4090
|
+
logging.error('Invalid storage mode - must be LIST or ARRAY')
|
4091
|
+
return []
|
4092
|
+
|
4093
|
+
@property
|
4094
|
+
def _myselection_as_array(self) -> np.ndarray:
|
4095
|
+
""" Current selection of nodes as a numpy array.
|
4096
|
+
|
4097
|
+
Returns:
|
4098
|
+
- np.ndarray of shape (nbx, nby) where nbx and nby are the number of nodes in the x and y directions respectively.
|
4099
|
+
- True if the node is selected, False if not selected.
|
4100
|
+
"""
|
4101
|
+
|
4102
|
+
if self._storage_mode == StorageMode.LIST:
|
4103
|
+
|
4104
|
+
if self._boolarray is not None:
|
4105
|
+
return self._boolarray
|
4106
|
+
|
4107
|
+
self._boolarray = np.zeros((self.parent.nbx, self.parent.nby), dtype=bool)
|
4108
|
+
|
4109
|
+
if self._myselection == ALL_SELECTED:
|
4110
|
+
if self.parent.usemask:
|
4111
|
+
self._boolarray[:,:] = ~self.parent.array.mask[:,:]
|
4112
|
+
else:
|
4113
|
+
self._boolarray[:,:] = True
|
4114
|
+
|
4115
|
+
elif isinstance(self._myselection, list):
|
4116
|
+
# Convert list of (x, y) coordinates to boolean array
|
4117
|
+
if len(self._myselection) > 0:
|
4118
|
+
xy= np.asarray(self._myselection)
|
4119
|
+
ij = self.parent.xy2ij_np(xy)
|
4120
|
+
self._boolarray[ij[:, 0], ij[:, 1]] = True
|
4121
|
+
|
4122
|
+
elif isinstance(self._myselection, tuple) and len(self._myselection) == 2 and self._myselection[0] == ALL_SELECTED:
|
4123
|
+
|
4124
|
+
# tuple of ('all', np.ndarray) for all nodes and excluded nodes
|
4125
|
+
if self.parent.usemask:
|
4126
|
+
self._boolarray[:,:] = ~self.parent.array.mask[:,:]
|
4127
|
+
else:
|
4128
|
+
self._boolarray[:,:] = True
|
4129
|
+
|
4130
|
+
# Exclude nodes from the second element of the tuple
|
4131
|
+
excluded_nodes = self.myselection[1]
|
4132
|
+
if excluded_nodes.size > 0:
|
4133
|
+
excluded_ij = self.parent.xy2ij_np(excluded_nodes)
|
4134
|
+
self._boolarray[excluded_ij[:, 0], excluded_ij[:, 1]] = False
|
4135
|
+
|
4136
|
+
elif self._storage_mode == StorageMode.ARRAY:
|
4137
|
+
|
4138
|
+
if self._boolarray is None:
|
4139
|
+
self._boolarray = np.zeros((self.parent.nbx, self.parent.nby), dtype=bool)
|
4140
|
+
|
4141
|
+
return self._boolarray
|
4142
|
+
|
4143
|
+
@_myselection_as_array.setter
|
4144
|
+
def _myselection_as_array(self, value: np.ndarray):
|
4145
|
+
""" Set the current selection of nodes as a numpy array.
|
4146
|
+
|
4147
|
+
:param value: numpy array of shape (nbx, nby) where nbx and nby are the number of nodes in the x and y directions respectively.
|
4148
|
+
True if the node is selected, False if not selected.
|
4149
|
+
"""
|
4150
|
+
|
4151
|
+
if not isinstance(value, np.ndarray):
|
4152
|
+
logging.error('Invalid value for myselection_as_array - must be a numpy array')
|
4153
|
+
if value.shape != (self.parent.nbx, self.parent.nby):
|
4154
|
+
logging.error('Invalid shape for myselection_as_array - must be ({}, {})'.format
|
4155
|
+
(self.parent.nbx, self.parent.nby))
|
4156
|
+
|
4157
|
+
if self._boolarray is None:
|
4158
|
+
self._boolarray = value.copy()
|
4159
|
+
else:
|
4160
|
+
self._boolarray[:,:] = value[:,:]
|
4161
|
+
|
4162
|
+
@property
|
4163
|
+
def myselection_npargwhere(self) -> np.ndarray:
|
4164
|
+
""" Current selection of nodes as a numpy array using np.argwhere """
|
4165
|
+
return np.argwhere(self._myselection_as_array)
|
4166
|
+
|
4167
|
+
@myselection.setter
|
4168
|
+
def myselection(self, value: list[tuple[float, float]] | str | tuple[str, np.ndarray]):
|
4169
|
+
""" Set the current selection of nodes.
|
4170
|
+
|
4171
|
+
:param value: 'all' to select all nodes, a list of (x, y) coordinates to select specific nodes,
|
4172
|
+
or a tuple of ('all', np.ndarray) for all nodes and excluded nodes.
|
4173
|
+
"""
|
4174
|
+
|
4175
|
+
if self._storage_mode == StorageMode.ARRAY:
|
4176
|
+
# Convert to the new storage mode if necessary
|
4177
|
+
if isinstance(value, list):
|
4178
|
+
if len(value) == 0:
|
4179
|
+
self._myselection_as_array[:,:] = False
|
4180
|
+
else:
|
4181
|
+
ij = self.parent.xy2ij_np(np.array(value))
|
4182
|
+
self._myselection_as_array[:,:] = False
|
4183
|
+
self._myselection_as_array[ij[:, 0], ij[:, 1]] = True
|
4184
|
+
|
4185
|
+
elif isinstance(value, str):
|
4186
|
+
|
4187
|
+
if value == ALL_SELECTED:
|
4188
|
+
if self.parent.usemask:
|
4189
|
+
self._myselection_as_array[:,:] = ~self.parent.array.mask[:,:]
|
4190
|
+
else:
|
4191
|
+
self._myselection_as_array[:,:] = True
|
4192
|
+
else:
|
4193
|
+
logging.error('Invalid selection value - must be "all" or a list of (x, y) coordinates')
|
4194
|
+
|
4195
|
+
elif isinstance(value, tuple) and len(value) == 2 and value[0] == ALL_SELECTED:
|
4196
|
+
# tuple of ('all', np.ndarray) for all nodes and excluded nodes
|
4197
|
+
if self.parent.usemask:
|
4198
|
+
self._myselection_as_array[:,:] = ~self.parent.array.mask[:,:]
|
4199
|
+
else:
|
4200
|
+
self._myselection_as_array[:,:] = True
|
4201
|
+
|
4202
|
+
excluded_nodes = value[1]
|
4203
|
+
if excluded_nodes.size > 0:
|
4204
|
+
ij_excluded = self.parent.xy2ij_np(excluded_nodes)
|
4205
|
+
self._myselection_as_array[ij_excluded[:, 0], ij_excluded[:, 1]] = False
|
4206
|
+
|
4207
|
+
else:
|
4208
|
+
logging.error('Invalid selection value - must be "all" or a list of (x, y) coordinates or a tuple of ("all", np.ndarray)')
|
4209
|
+
|
4210
|
+
elif self._storage_mode == StorageMode.LIST:
|
4211
|
+
|
4212
|
+
if isinstance(value, list):
|
4213
|
+
if len(value) == 0:
|
4214
|
+
self._myselection = []
|
4215
|
+
else:
|
4216
|
+
self._myselection = value
|
4217
|
+
|
4218
|
+
elif isinstance(value, str):
|
4219
|
+
if value == ALL_SELECTED:
|
4220
|
+
self._myselection = ALL_SELECTED
|
4221
|
+
else:
|
4222
|
+
logging.error('Invalid selection value - must be "all" or a list of (x, y) coordinates')
|
4223
|
+
|
4224
|
+
elif isinstance(value, tuple) and len(value) == 2 and value[0] == ALL_SELECTED:
|
4225
|
+
# tuple of ('all', np.ndarray) for all nodes and excluded nodes
|
4226
|
+
self._myselection = value
|
4227
|
+
|
4228
|
+
else:
|
4229
|
+
logging.error('Invalid selection value - must be "all" or a list of (x, y) coordinates or a tuple of ("all", np.ndarray)')
|
4230
|
+
|
4231
|
+
self.update_nb_nodes_selection()
|
4232
|
+
|
4233
|
+
def is_all_selected(self) -> bool:
|
4234
|
+
""" Check if all nodes are selected.
|
4235
|
+
|
4236
|
+
:return: True if all nodes are selected, False otherwise
|
4237
|
+
"""
|
4238
|
+
|
4239
|
+
if self._storage_mode == StorageMode.LIST:
|
4240
|
+
|
4241
|
+
if self.myselection == ALL_SELECTED:
|
4242
|
+
return True
|
4243
|
+
elif isinstance(self.myselection, list):
|
4244
|
+
if self.parent.usemask:
|
4245
|
+
return len(self.myselection) == self.parent.nbnotnull
|
4246
|
+
else :
|
4247
|
+
return len(self.myselection) == self.parent.nbx * self.parent.nby
|
4248
|
+
elif isinstance(self.myselection, tuple) and len(self.myselection) == 2 and self.myselection[0] == ALL_SELECTED:
|
4249
|
+
# tuple of ('all', np.ndarray) for all nodes and excluded nodes
|
4250
|
+
if self.myselection[1].size == 0:
|
4251
|
+
return True
|
4252
|
+
return False
|
4253
|
+
|
4254
|
+
elif self._storage_mode == StorageMode.ARRAY:
|
4255
|
+
if self.nb > 0:
|
4256
|
+
# Check if all nodes are selected in the boolean array
|
4257
|
+
if self.parent.usemask:
|
4258
|
+
return np.all(self._myselection_as_array)
|
4259
|
+
else:
|
4260
|
+
return np.all(self._myselection_as_array == ~self.parent.array.mask)
|
4261
|
+
else:
|
4262
|
+
return False
|
3850
4263
|
|
3851
4264
|
def set_selection_from_list_xy(self, xylist: list[tuple[float, float]]):
|
3852
|
-
""" Set the current selection from a list of (x, y) coordinates
|
4265
|
+
""" Set the current selection from a list of (x, y) coordinates.
|
4266
|
+
|
4267
|
+
Alias for myselection setter to set a list of (x, y) coordinates.
|
4268
|
+
This will convert the list to the appropriate storage mode if necessary.
|
4269
|
+
"""
|
3853
4270
|
|
3854
4271
|
self.myselection = xylist
|
3855
|
-
self.update_nb_nodes_selection()
|
3856
4272
|
|
3857
4273
|
@property
|
3858
4274
|
def dx(self) -> float:
|
@@ -3874,51 +4290,60 @@ class SelectionData():
|
|
3874
4290
|
|
3875
4291
|
@property
|
3876
4292
|
def nb(self) -> int:
|
3877
|
-
""" Number of selected nodes """
|
4293
|
+
""" Number of selected nodes. """
|
3878
4294
|
|
3879
|
-
if self.
|
3880
|
-
return self.parent.nbnotnull
|
3881
|
-
else:
|
3882
|
-
return len(self.myselection)
|
4295
|
+
if self._storage_mode == StorageMode.LIST:
|
3883
4296
|
|
3884
|
-
|
3885
|
-
|
4297
|
+
if self._myselection == ALL_SELECTED:
|
4298
|
+
if self.parent.usemask:
|
4299
|
+
return self.parent.nbnotnull
|
4300
|
+
else:
|
4301
|
+
return self.parent.nbx * self.parent.nby
|
3886
4302
|
|
3887
|
-
|
3888
|
-
|
4303
|
+
elif isinstance(self._myselection, tuple) and len(self._myselection) == 2 and self._myselection[0] == ALL_SELECTED:
|
4304
|
+
# tuple of ('all', np.ndarray) for all nodes and excluded nodes
|
4305
|
+
if self.parent.usemask:
|
4306
|
+
return self.parent.nbnotnull - self._myselection[1].shape[0]
|
4307
|
+
else:
|
4308
|
+
return self.parent.nbx * self.parent.nby - self._myselection[1].shape[0]
|
4309
|
+
else:
|
4310
|
+
return len(self._myselection)
|
3889
4311
|
|
3890
|
-
|
3891
|
-
return
|
3892
|
-
else:
|
3893
|
-
destxy = self.myselection
|
4312
|
+
elif self._storage_mode == StorageMode.ARRAY:
|
3894
4313
|
|
3895
|
-
|
3896
|
-
logging.error(_('No selection to unmask'))
|
3897
|
-
return
|
4314
|
+
return np.count_nonzero(self._myselection_as_array)
|
3898
4315
|
|
3899
|
-
|
3900
|
-
|
3901
|
-
|
3902
|
-
|
4316
|
+
def unmask_selection(self, resetplot:bool=True):
|
4317
|
+
""" Unmask selection """
|
4318
|
+
|
4319
|
+
if self.nb == 0:
|
4320
|
+
return
|
3903
4321
|
|
3904
|
-
|
4322
|
+
self.parent.array.mask[self.myselection_npargwhere] = False
|
3905
4323
|
|
3906
4324
|
if resetplot:
|
3907
|
-
|
4325
|
+
self.parent.reset_plot()
|
3908
4326
|
|
3909
4327
|
def reset(self):
|
3910
4328
|
""" Reset the selection """
|
3911
4329
|
|
3912
4330
|
self.myselection = []
|
4331
|
+
self._boolarray = None # Reset the boolean array
|
4332
|
+
self._storage_mode = StorageMode.LIST # Reset the storage mode to LIST
|
3913
4333
|
|
3914
4334
|
def reset_all(self):
|
3915
4335
|
""" Reset the selection """
|
3916
4336
|
|
3917
|
-
self.
|
4337
|
+
self.reset()
|
3918
4338
|
self.selections = {}
|
3919
4339
|
|
3920
4340
|
def get_string(self, which:str = None, all_memories:bool= False) -> str:
|
3921
|
-
""" Get string of the current selection or of a stored one
|
4341
|
+
""" Get string of the current selection or of a stored one.
|
4342
|
+
|
4343
|
+
:param which: id/key of the selection to get the string for. If None, get the current selection.
|
4344
|
+
:param all_memories: If True, include all stored selections in the output string.
|
4345
|
+
:return: String representation of the selection.
|
4346
|
+
"""
|
3922
4347
|
|
3923
4348
|
if which is None:
|
3924
4349
|
curlist = self.myselection
|
@@ -3937,7 +4362,7 @@ class SelectionData():
|
|
3937
4362
|
if len(curlist) == 0:
|
3938
4363
|
return ''
|
3939
4364
|
|
3940
|
-
if curlist ==
|
4365
|
+
if curlist == ALL_SELECTED:
|
3941
4366
|
txt += 'all\n'
|
3942
4367
|
return txt
|
3943
4368
|
|
@@ -3956,9 +4381,13 @@ class SelectionData():
|
|
3956
4381
|
return txt
|
3957
4382
|
|
3958
4383
|
def get_script(self, which:int = None) -> str:
|
3959
|
-
""" Get script of the current selection or of a stored one
|
4384
|
+
""" Get script of the current selection or of a stored one.
|
4385
|
+
|
4386
|
+
:param which: id/key of the selection to get the script for. If None, get the current selection.
|
4387
|
+
:return: Script representation of the selection.
|
4388
|
+
"""
|
3960
4389
|
|
3961
|
-
if self.myselection ==
|
4390
|
+
if self.myselection == ALL_SELECTED:
|
3962
4391
|
logging.error(_('Cannot create script for "all" selection'))
|
3963
4392
|
return ''
|
3964
4393
|
|
@@ -3998,7 +4427,14 @@ class SelectionData():
|
|
3998
4427
|
return txt
|
3999
4428
|
|
4000
4429
|
def copy_to_clipboard(self, which:int = None, typestr:Literal['string', 'script'] = 'string'):
|
4001
|
-
""" Copy current selection to clipboard
|
4430
|
+
""" Copy current selection to clipboard.
|
4431
|
+
|
4432
|
+
-- ONLY WORKS WITH wxPython --
|
4433
|
+
|
4434
|
+
:param which: id/key of the selection to copy. If None, copy the current selection.
|
4435
|
+
:param typestr: Type of data to copy to clipboard - 'string' for string representation, 'script' for script representation.
|
4436
|
+
"""
|
4437
|
+
|
4002
4438
|
if self.wx_exists:
|
4003
4439
|
if wx.TheClipboard.Open():
|
4004
4440
|
wx.TheClipboard.Clear()
|
@@ -4010,15 +4446,22 @@ class SelectionData():
|
|
4010
4446
|
wx.TheClipboard.Close()
|
4011
4447
|
else:
|
4012
4448
|
logging.warning(_('Cannot open the clipboard'))
|
4449
|
+
else:
|
4450
|
+
logging.warning(_('Clipboard is not available in this environment'))
|
4013
4451
|
|
4014
4452
|
def reselect_from_memory(self, idx:list[str] = None):
|
4015
4453
|
"""
|
4016
4454
|
Reselect a stored selection
|
4017
4455
|
|
4018
|
-
:param idx: id/key of the selection
|
4456
|
+
:param idx: id/key of the selection to reselect. If None, show a dialog to choose from available selections.
|
4019
4457
|
"""
|
4020
4458
|
|
4021
4459
|
if idx is None:
|
4460
|
+
if not self.wx_exists:
|
4461
|
+
logging.error(_('Cannot reselect from memory - no wxPython available'))
|
4462
|
+
logging.error(_('Please use the method reselect_from_memory with a list of keys'))
|
4463
|
+
return
|
4464
|
+
|
4022
4465
|
keys = list(self.selections.keys())
|
4023
4466
|
|
4024
4467
|
keys = [cur for cur in keys if len(self.selections[cur]['select']) > 0]
|
@@ -4041,11 +4484,12 @@ class SelectionData():
|
|
4041
4484
|
|
4042
4485
|
for curidx in idx:
|
4043
4486
|
if curidx in self.selections:
|
4044
|
-
self.
|
4487
|
+
self.add_nodes_to_selection(self.selections[curidx]['select'])
|
4488
|
+
# self.myselection += self.selections[curidx]['select']
|
4045
4489
|
else:
|
4046
4490
|
logging.error(_('Selection {} does not exist').format(idx))
|
4047
4491
|
|
4048
|
-
self.update_nb_nodes_selection()
|
4492
|
+
# self.update_nb_nodes_selection()
|
4049
4493
|
self.parent.reset_plot()
|
4050
4494
|
|
4051
4495
|
def move_selectionto(self, idx:str, color:list[float], resetplot:bool=True):
|
@@ -4054,6 +4498,7 @@ class SelectionData():
|
|
4054
4498
|
|
4055
4499
|
:param idx: id/key of the selection
|
4056
4500
|
:param color: color of the selection - list of 4 integers between 0 and 255
|
4501
|
+
:param resetplot: if True, reset the plot after moving the selection
|
4057
4502
|
"""
|
4058
4503
|
|
4059
4504
|
assert len(color) == 4, "color must be a list of 4 integers between 0 and 255"
|
@@ -4068,7 +4513,7 @@ class SelectionData():
|
|
4068
4513
|
curdict['color'] = color
|
4069
4514
|
|
4070
4515
|
self.myselection = [] # reset current selection
|
4071
|
-
self.update_nb_nodes_selection()
|
4516
|
+
# self.update_nb_nodes_selection()
|
4072
4517
|
|
4073
4518
|
if resetplot:
|
4074
4519
|
self.parent.reset_plot()
|
@@ -4083,7 +4528,7 @@ class SelectionData():
|
|
4083
4528
|
if len(self.selections) > 0:
|
4084
4529
|
# plot stored selections
|
4085
4530
|
for cur in self.selections.values():
|
4086
|
-
if cur['select'] !=
|
4531
|
+
if cur['select'] != ALL_SELECTED:
|
4087
4532
|
self.update_plot_selection = update_select
|
4088
4533
|
col = cur['color']
|
4089
4534
|
cur['idgllist'] = self._plot_selection(cur['select'],
|
@@ -4092,9 +4537,9 @@ class SelectionData():
|
|
4092
4537
|
cur['idgllist'])
|
4093
4538
|
|
4094
4539
|
|
4095
|
-
if self.
|
4540
|
+
if self._myselection != ALL_SELECTED and not isinstance(self._myselection, tuple):
|
4096
4541
|
# plot current selection in RED if not 'all'
|
4097
|
-
if
|
4542
|
+
if self.nb > 0:
|
4098
4543
|
self.update_plot_selection = update_select
|
4099
4544
|
self.numlist_select = self._plot_selection(self.myselection,
|
4100
4545
|
(1., 0., 0.),
|
@@ -4163,8 +4608,8 @@ class SelectionData():
|
|
4163
4608
|
"""
|
4164
4609
|
Add one coordinate to the selection
|
4165
4610
|
|
4166
|
-
:param x: x coordinate
|
4167
|
-
:param y: y coordinate
|
4611
|
+
:param x: x coordinate - float
|
4612
|
+
:param y: y coordinate - float
|
4168
4613
|
:param verif: if True, the coordinates are checked to avoid duplicates
|
4169
4614
|
"""
|
4170
4615
|
|
@@ -4179,48 +4624,79 @@ class SelectionData():
|
|
4179
4624
|
else:
|
4180
4625
|
return -1 # useful for MB
|
4181
4626
|
|
4182
|
-
|
4627
|
+
self.update_nb_nodes_selection()
|
4628
|
+
|
4629
|
+
def add_nodes_to_selection(self, xy:list[float] | np.ndarray, verif:bool=True):
|
4183
4630
|
"""
|
4184
|
-
Add multiple coordinates to the selection
|
4631
|
+
Add multiple coordinates to the selection.
|
4185
4632
|
|
4186
|
-
:param xy: list of coordinates
|
4633
|
+
:param xy: list of coordinates or numpy array of shape (nb_nodes, 2) where nb_nodes is the number of nodes
|
4634
|
+
or a numpy array of shape (nbx, nby) where nbx and nby are the number of nodes in the x and y directions respectively.
|
4635
|
+
If a numpy array of shape (nbx, nby) is provided, it is assumed to be a boolean array where True indicates a selected node.
|
4187
4636
|
:param verif: if True, the coordinates are checked to avoid duplicates
|
4188
4637
|
"""
|
4189
4638
|
|
4190
4639
|
# on repasse par les i,j car les coordonnées transférées peuvent venir d'un click souris
|
4191
4640
|
# le but est de ne conserver que les coordonnées des CG de mailles
|
4192
|
-
|
4193
|
-
|
4641
|
+
if isinstance(xy, np.ndarray):
|
4642
|
+
self._add_nodes_to_selection_np(xy, verif)
|
4643
|
+
else:
|
4644
|
+
ij = [self.parent.get_ij_from_xy(x, y) for x, y in xy]
|
4645
|
+
self._add_nodes_to_selectionij(ij, verif)
|
4194
4646
|
|
4195
4647
|
def _add_node_to_selectionij(self, i:int, j:int, verif=True):
|
4196
4648
|
"""
|
4197
|
-
Add one ij coordinate to the selection
|
4649
|
+
Add one ij coordinate to the selection.
|
4198
4650
|
|
4199
4651
|
:param i: i coordinate
|
4200
4652
|
:param j: j coordinate
|
4201
4653
|
:param verif: if True, the coordinates are checked to avoid duplicates
|
4202
4654
|
"""
|
4203
4655
|
|
4204
|
-
|
4205
|
-
|
4206
|
-
if isinstance(self.myselection, str):
|
4207
|
-
self.myselection = []
|
4208
|
-
|
4209
|
-
if verif:
|
4210
|
-
try:
|
4211
|
-
ret = self.myselection.index((x1, y1))
|
4212
|
-
except:
|
4213
|
-
ret = -1
|
4214
|
-
if ret >= 0:
|
4215
|
-
self.myselection.pop(ret)
|
4656
|
+
if self._storage_mode == StorageMode.ARRAY:
|
4216
4657
|
|
4217
|
-
|
4658
|
+
if verif:
|
4659
|
+
self._boolarray[i, j] = not self._boolarray[i, j]
|
4218
4660
|
else:
|
4219
|
-
self.
|
4220
|
-
|
4221
|
-
|
4222
|
-
self.
|
4223
|
-
|
4661
|
+
self._boolarray[i, j] = True
|
4662
|
+
|
4663
|
+
elif self._storage_mode == StorageMode.LIST:
|
4664
|
+
x1, y1 = self.parent.get_xy_from_ij(i, j)
|
4665
|
+
|
4666
|
+
if self._myselection == ALL_SELECTED:
|
4667
|
+
# If all nodes are selected, we need to exclude the current node
|
4668
|
+
self._myselection = (ALL_SELECTED, np.array([[x1, y1]])) # Exclude the current node
|
4669
|
+
elif isinstance(self._myselection, tuple) and len(self._myselection) == 2 and self._myselection[0] == ALL_SELECTED:
|
4670
|
+
# check is the current node is in the excluded nodes
|
4671
|
+
excluded_nodes = self._myselection[1].tolist()
|
4672
|
+
if (x1, y1) in excluded_nodes:
|
4673
|
+
# If the current node is in the excluded nodes, we remove it from the excluded nodes
|
4674
|
+
ret = excluded_nodes.index((x1, y1))
|
4675
|
+
excluded_nodes.pop(ret)
|
4676
|
+
if len(excluded_nodes) == 0:
|
4677
|
+
self._myselection = ALL_SELECTED # All nodes are selected again
|
4678
|
+
else:
|
4679
|
+
self._myselection = (ALL_SELECTED, np.array(excluded_nodes))
|
4680
|
+
else:
|
4681
|
+
# The node is included in the selection, we add it to the excluded nodes
|
4682
|
+
excluded_nodes.append((x1, y1))
|
4683
|
+
self._myselection = (ALL_SELECTED, np.array(excluded_nodes))
|
4684
|
+
else:
|
4685
|
+
if verif:
|
4686
|
+
try:
|
4687
|
+
ret = self._myselection.index((x1, y1))
|
4688
|
+
except:
|
4689
|
+
ret = -1
|
4690
|
+
if ret >= 0:
|
4691
|
+
self._myselection.pop(ret)
|
4692
|
+
|
4693
|
+
return 0
|
4694
|
+
else:
|
4695
|
+
self._myselection.append((x1, y1))
|
4696
|
+
return 0
|
4697
|
+
else:
|
4698
|
+
self._myselection.append((x1, y1))
|
4699
|
+
return 0
|
4224
4700
|
|
4225
4701
|
def _add_nodes_to_selectionij(self, ij:list[tuple[float, float]], verif:bool=True):
|
4226
4702
|
"""
|
@@ -4230,94 +4706,171 @@ class SelectionData():
|
|
4230
4706
|
:param verif: if True, the coordinates are checked to avoid duplicates
|
4231
4707
|
"""
|
4232
4708
|
|
4233
|
-
if isinstance(self.myselection, str):
|
4234
|
-
self.myselection = []
|
4235
|
-
|
4236
|
-
|
4237
4709
|
if len(ij)==0:
|
4238
4710
|
logging.info(_('Nothing to do in add_nodes_to_selectionij !'))
|
4239
4711
|
return
|
4240
4712
|
|
4241
|
-
nbini =
|
4713
|
+
nbini = self.nb
|
4242
4714
|
|
4243
|
-
|
4715
|
+
ij = np.asarray(ij)
|
4244
4716
|
|
4245
|
-
self.
|
4246
|
-
|
4247
|
-
if nbini != 0:
|
4717
|
+
if self._storage_mode == StorageMode.ARRAY:
|
4248
4718
|
if verif:
|
4249
|
-
|
4250
|
-
|
4719
|
+
self._boolarray[ij[:, 0], ij[:, 1]] = np.logical_xor(self._boolarray[ij[:, 0], ij[:, 1]], True)
|
4720
|
+
else:
|
4721
|
+
self._boolarray[ij[:, 0], ij[:, 1]] = True
|
4722
|
+
|
4723
|
+
elif self._storage_mode == StorageMode.LIST:
|
4724
|
+
|
4725
|
+
xy = self.parent.ij2xy_np(ij)
|
4251
4726
|
|
4727
|
+
if self._myselection == ALL_SELECTED:
|
4728
|
+
# If all nodes are selected, we need to exclude the current nodes
|
4729
|
+
self._myselection = (ALL_SELECTED, xy) # Exclude the current nodes
|
4730
|
+
|
4731
|
+
elif isinstance(self._myselection, tuple) and len(self._myselection) == 2 and self._myselection[0] == ALL_SELECTED:
|
4732
|
+
# check if the current nodes are in the excluded nodes
|
4733
|
+
excluded_nodes = self._myselection[1]
|
4734
|
+
# extend the excluded nodes with the new ones
|
4735
|
+
excluded_nodes = np.vstack((excluded_nodes, xy))
|
4736
|
+
|
4737
|
+
# Find the duplicates in the excluded nodes
|
4738
|
+
selunique, counts = np.unique(excluded_nodes, return_counts=True, axis=0)
|
4252
4739
|
# les éléments énumérés plus d'une fois doivent être enlevés
|
4253
|
-
# on trie par ordre décroissant
|
4254
4740
|
locsort = sorted(zip(counts.tolist(), selunique.tolist()), reverse=True)
|
4255
4741
|
counts = [x[0] for x in locsort]
|
4256
4742
|
sel = [tuple(x[1]) for x in locsort]
|
4257
|
-
|
4258
4743
|
# on recherche le premier 1
|
4259
4744
|
if 1 in counts:
|
4260
4745
|
idx = counts.index(1)
|
4261
4746
|
# on ne conserve que la portion de liste utile
|
4262
|
-
self.
|
4747
|
+
self._myselection = (ALL_SELECTED, np.array(sel[idx:]))
|
4748
|
+
else:
|
4749
|
+
self._myselection = ALL_SELECTED # All nodes are selected again
|
4750
|
+
else:
|
4751
|
+
# Add the new nodes to the selection
|
4752
|
+
|
4753
|
+
if nbini != 0:
|
4754
|
+
self._myselection += xy.tolist()
|
4755
|
+
|
4756
|
+
if verif:
|
4757
|
+
# trouve les éléments uniques dans la liste de tuples (--> axis=0) et retourne également le comptage
|
4758
|
+
selunique, counts = np.unique(self._myselection, return_counts=True, axis=0)
|
4759
|
+
|
4760
|
+
# les éléments énumérés plus d'une fois doivent être enlevés
|
4761
|
+
# on trie par ordre décroissant
|
4762
|
+
locsort = sorted(zip(counts.tolist(), selunique.tolist()), reverse=True)
|
4763
|
+
counts = [x[0] for x in locsort]
|
4764
|
+
sel = [tuple(x[1]) for x in locsort]
|
4765
|
+
|
4766
|
+
# on recherche le premier 1
|
4767
|
+
if 1 in counts:
|
4768
|
+
idx = counts.index(1)
|
4769
|
+
# on ne conserve que la portion de liste utile
|
4770
|
+
self._myselection = sel[idx:]
|
4771
|
+
else:
|
4772
|
+
self._myselection = []
|
4773
|
+
else:
|
4774
|
+
self._myselection = np.unique(self.myselection, axis=0).tolist()
|
4775
|
+
else:
|
4776
|
+
self._myselection = xy.tolist()
|
4777
|
+
|
4778
|
+
self.update_nb_nodes_selection()
|
4779
|
+
|
4780
|
+
def _add_nodes_to_selection_np(self, ij:np.ndarray, verif:bool=True):
|
4781
|
+
""" Add multiple coordinates to the selection from a numpy array
|
4782
|
+
|
4783
|
+
:param ij: numpy array of coordinates - same shape as the parent array (nbx, nby) or (nb_nodes, 2)
|
4784
|
+
:param verif: if True, the coordinates are checked to avoid duplicates
|
4785
|
+
"""
|
4786
|
+
|
4787
|
+
assert ij.shape == (self.parent.nbx, self.parent.nby) or ij.shape[1]==2, _('Invalid shape for ij - must be ({}, {})'.format(self.parent.nbx, self.parent.nby))
|
4788
|
+
|
4789
|
+
dtype = ij.dtype
|
4790
|
+
if dtype not in [np.bool, np.int32, np.int64]:
|
4791
|
+
assert ij.shape[1] == 2, _('Invalid shape for ij - must be ({}, {}) or (nb_nodes, 2)'.format(self.parent.nbx, self.parent.nby))
|
4792
|
+
ij = self.parent.xy2ij_np(ij)
|
4793
|
+
|
4794
|
+
if self._storage_mode == StorageMode.ARRAY:
|
4795
|
+
if verif:
|
4796
|
+
if ij.shape[1] == 2:
|
4797
|
+
# using xy from np.where
|
4798
|
+
self._myselection_as_array[ij[:,0], ij[:,1]] = np.logical_xor(self._myselection_as_array[ij[:, 0], ij[:, 1]], True)
|
4799
|
+
else:
|
4800
|
+
self._myselection_as_array[:,:] = np.logical_xor(self._myselection_as_array, ij)
|
4801
|
+
else:
|
4802
|
+
if ij.shape[1] == 2:
|
4803
|
+
# using xy from np.where
|
4804
|
+
self._myselection_as_array[ij[:,0], ij[:,1]] = True
|
4263
4805
|
else:
|
4264
|
-
self.
|
4806
|
+
self._myselection_as_array[ij] = True
|
4807
|
+
|
4808
|
+
elif self._storage_mode == StorageMode.LIST:
|
4809
|
+
if ij.shape[1] == 2:
|
4810
|
+
# using xy from np.where
|
4811
|
+
self._add_nodes_to_selectionij(ij, verif)
|
4265
4812
|
else:
|
4266
|
-
|
4813
|
+
# Convert the numpy array to a list of tuples
|
4814
|
+
ij = np.argwhere(ij)
|
4815
|
+
self._add_nodes_to_selectionij(ij, verif)
|
4267
4816
|
|
4268
4817
|
def select_insidepoly(self, myvect: vector):
|
4269
|
-
""" Select nodes inside a polygon
|
4818
|
+
""" Select nodes inside a polygon.
|
4270
4819
|
|
4271
|
-
|
4820
|
+
:param myvect: vector defining the polygon
|
4821
|
+
"""
|
4272
4822
|
|
4273
|
-
|
4274
|
-
mypoints, _tmpij = self.parent.get_xy_infootprint_vect(myvect)
|
4275
|
-
path = mpltPath.Path(myvect.asnparray())
|
4276
|
-
inside = path.contains_points(mypoints)
|
4823
|
+
nbini = self.nb
|
4277
4824
|
|
4278
|
-
self.hideselection=
|
4279
|
-
if self.parent.myops is not None:
|
4280
|
-
if self.parent.myops.selectrestricttomask.IsChecked():
|
4281
|
-
self.hideselection=True
|
4825
|
+
self.hideselection = self.parent.usemask
|
4282
4826
|
|
4283
|
-
self.
|
4827
|
+
inside = self.parent.get_ij_inside_polygon(myvect, usemask=self.hideselection)
|
4284
4828
|
|
4285
|
-
if
|
4286
|
-
|
4287
|
-
|
4288
|
-
|
4829
|
+
if inside.shape[0] == 0:
|
4830
|
+
logging.info(_('No nodes inside the polygon'))
|
4831
|
+
return
|
4832
|
+
else:
|
4833
|
+
if inside.shape[0] > self.threshold_array_mode:
|
4834
|
+
self.force_storage_mode(StorageMode.ARRAY)
|
4835
|
+
|
4836
|
+
self._add_nodes_to_selectionij(inside, verif= nbini != 0)
|
4289
4837
|
|
4290
4838
|
self.hideselection=False
|
4291
|
-
self.update_nb_nodes_selection()
|
4292
4839
|
|
4293
4840
|
def select_underpoly(self, myvect: vector):
|
4294
|
-
""" Select nodes along a polyline
|
4841
|
+
""" Select nodes along a polyline
|
4842
|
+
|
4843
|
+
:param myvect: vector defining the polyline
|
4844
|
+
"""
|
4295
4845
|
|
4296
|
-
nbini =
|
4846
|
+
nbini = self.nb
|
4297
4847
|
|
4298
4848
|
myvect.find_minmax()
|
4299
4849
|
mypoints = self.parent.get_ij_under_polyline(myvect)
|
4300
4850
|
|
4851
|
+
if self.parent.usemask:
|
4852
|
+
# If the parent uses a mask, we need to check if the points are not masked
|
4853
|
+
mypoints = mypoints[~self.parent.array.mask[mypoints[:, 0], mypoints[:, 1]]]
|
4854
|
+
|
4301
4855
|
if len(mypoints) == 0:
|
4302
4856
|
logging.info(_('No nodes under the polyline'))
|
4303
4857
|
return
|
4304
4858
|
|
4305
|
-
self.
|
4306
|
-
|
4307
|
-
if self.parent.myops is not None:
|
4308
|
-
if self.parent.myops.selectrestricttomask.IsChecked():
|
4309
|
-
self.condition_select('Mask',0)
|
4310
|
-
|
4311
|
-
self.update_nb_nodes_selection()
|
4859
|
+
self.add_nodes_to_selection(mypoints, verif = nbini != 0)
|
4312
4860
|
|
4313
4861
|
def dilate_selection(self, nb_iterations:int, use_mask:bool = True, structure:np.ndarray = None):
|
4314
|
-
""" Extend the selection
|
4862
|
+
""" Extend the selection.
|
4863
|
+
|
4864
|
+
:param nb_iterations: Number of iterations to dilate the selection
|
4865
|
+
:param use_mask: If True, use the mask of the parent array to limit the dilation
|
4866
|
+
:param structure: Structuring element for dilation, default is a cross shape (nodes directly adjacent in the x or y direction)
|
4867
|
+
"""
|
4315
4868
|
|
4316
|
-
if self.
|
4869
|
+
if self.is_all_selected():
|
4317
4870
|
logging.info(_('Cannot extend selection when all nodes are selected'))
|
4318
4871
|
return
|
4319
4872
|
|
4320
|
-
if
|
4873
|
+
if self.nb == 0:
|
4321
4874
|
logging.info(_('No nodes selected'))
|
4322
4875
|
return
|
4323
4876
|
|
@@ -4331,34 +4884,27 @@ class SelectionData():
|
|
4331
4884
|
|
4332
4885
|
from scipy import ndimage
|
4333
4886
|
|
4334
|
-
|
4335
|
-
ij = [self.parent.get_ij_from_xy(x, y) for x, y in xy]
|
4336
|
-
|
4337
|
-
selected = np.zeros(self.parent.array.shape, dtype=bool)
|
4338
|
-
for i, j in ij:
|
4339
|
-
selected[i, j] = True
|
4340
|
-
|
4341
|
-
selected = ndimage.binary_dilation(selected,
|
4887
|
+
self._myselection_as_array[:,:] = ndimage.binary_dilation(self._myselection_as_array,
|
4342
4888
|
iterations=nb_iterations,
|
4343
4889
|
mask=~self.parent.array.mask if use_mask else None,
|
4344
4890
|
structure=structure)
|
4345
4891
|
|
4346
|
-
|
4347
|
-
ij = np.vstack([ij[:, 0], ij[:, 1]]).T
|
4348
|
-
xy = self.parent.ij2xy_np(ij)
|
4349
|
-
|
4350
|
-
self.myselection = [(cur[0], cur[1]) for cur in xy]
|
4351
|
-
|
4892
|
+
self._storage_mode = StorageMode.ARRAY # Ensure we are in ARRAY mode
|
4352
4893
|
self.update_nb_nodes_selection()
|
4353
4894
|
|
4354
4895
|
def erode_selection(self, nb_iterations:int, use_mask:bool = True, structure:np.ndarray = None):
|
4355
|
-
""" Reduce the selection
|
4896
|
+
""" Reduce the selection.
|
4897
|
+
|
4898
|
+
:param nb_iterations: Number of iterations to erode the selection
|
4899
|
+
:param use_mask: If True, use the mask of the parent array to limit the erosion
|
4900
|
+
:param structure: Structuring element for erosion, default is a cross shape (nodes directly adjacent in the x or y direction)
|
4901
|
+
"""
|
4356
4902
|
|
4357
|
-
if self.
|
4903
|
+
if self.is_all_selected():
|
4358
4904
|
logging.info(_('Cannot reduce selection when all nodes are selected'))
|
4359
4905
|
return
|
4360
4906
|
|
4361
|
-
if
|
4907
|
+
if self.nb == 0:
|
4362
4908
|
logging.info(_('No nodes selected'))
|
4363
4909
|
return
|
4364
4910
|
|
@@ -4372,53 +4918,54 @@ class SelectionData():
|
|
4372
4918
|
|
4373
4919
|
from scipy import ndimage
|
4374
4920
|
|
4375
|
-
|
4376
|
-
ij = [self.parent.get_ij_from_xy(x, y) for x, y in xy]
|
4377
|
-
|
4378
|
-
selected = np.zeros(self.parent.array.shape, dtype=bool)
|
4379
|
-
|
4380
|
-
for i, j in ij:
|
4381
|
-
selected[i, j] = True
|
4382
|
-
|
4383
|
-
selected = ndimage.binary_erosion(selected,
|
4921
|
+
self._myselection_as_array[:,:] = ndimage.binary_erosion(self._myselection_as_array,
|
4384
4922
|
iterations=nb_iterations,
|
4385
4923
|
mask=~self.parent.array.mask if use_mask else None,
|
4386
4924
|
structure=structure)
|
4387
4925
|
|
4388
|
-
|
4389
|
-
ij = np.vstack([ij[:, 0], ij[:, 1]]).T
|
4390
|
-
xy = self.parent.ij2xy_np(ij)
|
4391
|
-
|
4392
|
-
self.myselection = [(cur[0], cur[1]) for cur in xy]
|
4393
|
-
|
4926
|
+
self._storage_mode = StorageMode.ARRAY # Ensure we are in ARRAY mode
|
4394
4927
|
self.update_nb_nodes_selection()
|
4395
4928
|
|
4396
4929
|
def dilate_contour_selection(self, nbiter:int= 1, use_mask:bool = True, structure:np.ndarray = np.ones((3,3))):
|
4397
|
-
""" Dilate the contour of the selection
|
4930
|
+
""" Dilate the contour of the selection.
|
4931
|
+
|
4932
|
+
:param nbiter: Number of iterations to dilate the selection
|
4933
|
+
:param use_mask: If True, use the mask of the parent array to limit the dilation
|
4934
|
+
:param structure: Structuring element for dilation, default is a 3x3 square
|
4935
|
+
"""
|
4398
4936
|
|
4399
4937
|
if self.nb > 0:
|
4400
|
-
oldsel = self.
|
4938
|
+
oldsel = self._myselection_as_array.copy()
|
4401
4939
|
self.dilate_selection(nbiter, use_mask, structure)
|
4402
|
-
|
4403
|
-
self.
|
4940
|
+
# We keep only the new nodes that were not in the old selection
|
4941
|
+
self._myselection_as_array[:,:] = np.logical_and(self._myselection_as_array, ~oldsel)
|
4942
|
+
self._storage_mode = StorageMode.ARRAY # Ensure we are in ARRAY mode
|
4404
4943
|
self.update_nb_nodes_selection()
|
4405
4944
|
else:
|
4406
4945
|
logging.info('No selection to expand/dilate')
|
4407
4946
|
|
4408
4947
|
def erode_contour_selection(self):
|
4409
|
-
""" Erode the contour of the selection
|
4948
|
+
""" Erode the contour of the selection.
|
4949
|
+
|
4950
|
+
:param nbiter: Number of iterations to erode the selection
|
4951
|
+
:param use_mask: If True, use the mask of the parent array to limit the erosion
|
4952
|
+
:param structure: Structuring element for erosion, default is a 3x3 square
|
4953
|
+
"""
|
4410
4954
|
|
4411
4955
|
if self.nb > 0:
|
4412
|
-
oldselect = self.
|
4956
|
+
oldselect = self._myselection_as_array.copy()
|
4413
4957
|
self.erode_selection(1)
|
4414
|
-
|
4415
|
-
self.
|
4958
|
+
self._myselection_as_array[:,:] = np.logical_and(self._myselection_as_array, oldselect)
|
4959
|
+
self._storage_mode = StorageMode.ARRAY # Ensure we are in ARRAY mode
|
4416
4960
|
self.update_nb_nodes_selection()
|
4417
4961
|
else:
|
4418
4962
|
logging.info('No selection to contract/erode')
|
4419
4963
|
|
4420
4964
|
def save_selection(self, filename:str=None):
|
4421
|
-
""" Save the selection to a file
|
4965
|
+
""" Save the selection to a file.
|
4966
|
+
|
4967
|
+
:param filename: Name of the file to save the selection to. If None, a dialog will be shown to choose the file.
|
4968
|
+
"""
|
4422
4969
|
|
4423
4970
|
if filename is None:
|
4424
4971
|
with wx.FileDialog(None, 'Save selection', wildcard='Text files (*.txt)|*.txt',
|
@@ -4431,7 +4978,10 @@ class SelectionData():
|
|
4431
4978
|
f.write(self.get_string(all_memories=True))
|
4432
4979
|
|
4433
4980
|
def load_selection(self, filename:str=None):
|
4434
|
-
""" Load a selection from a file
|
4981
|
+
""" Load a selection from a file.
|
4982
|
+
|
4983
|
+
:param filename: Name of the file to load the selection from. If None, a dialog will be shown to choose the file.
|
4984
|
+
"""
|
4435
4985
|
|
4436
4986
|
if filename is None:
|
4437
4987
|
with wx.FileDialog(None, 'Load selection', wildcard='Text files (*.txt)|*.txt',
|
@@ -4488,13 +5038,35 @@ class SelectionData():
|
|
4488
5038
|
|
4489
5039
|
self.parent.reset_plot()
|
4490
5040
|
|
4491
|
-
def update_nb_nodes_selection(self):
|
4492
|
-
""" Update the number of selected nodes
|
5041
|
+
def update_nb_nodes_selection(self, autostoragemode:bool=True) -> tuple[int, float, float, float, float]:
|
5042
|
+
""" Update the number of selected nodes.
|
4493
5043
|
|
4494
|
-
if
|
4495
|
-
|
4496
|
-
|
4497
|
-
|
5044
|
+
:param autostoragemode: If True, automatically switch to ARRAY mode if the number of selected nodes exceeds the threshold.
|
5045
|
+
:return: Tuple containing the number of selected nodes, and the bounds of the selection (xmin, xmax, ymin, ymax).
|
5046
|
+
"""
|
5047
|
+
|
5048
|
+
if autostoragemode:
|
5049
|
+
self._auto_storage_mode()
|
5050
|
+
|
5051
|
+
if self._storage_mode == StorageMode.ARRAY:
|
5052
|
+
nb = np.count_nonzero(self._myselection_as_array)
|
5053
|
+
elif self._storage_mode == StorageMode.LIST:
|
5054
|
+
if self.is_all_selected():
|
5055
|
+
if self.parent.usemask:
|
5056
|
+
nb = self.parent.nbnotnull
|
5057
|
+
else:
|
5058
|
+
nb = self.parent.nbx * self.parent.nby
|
5059
|
+
elif isinstance(self._myselection, tuple) and len(self._myselection) == 2 and self._myselection[0] == ALL_SELECTED:
|
5060
|
+
# tuple of ('all', np.ndarray) for all nodes and excluded nodes
|
5061
|
+
|
5062
|
+
if self.parent.usemask:
|
5063
|
+
nb = self.parent.nbnotnull
|
5064
|
+
else:
|
5065
|
+
nb = self.parent.nbx * self.parent.nby
|
5066
|
+
|
5067
|
+
nb -= len(self._myselection[1])
|
5068
|
+
else:
|
5069
|
+
nb = len(self._myselection)
|
4498
5070
|
|
4499
5071
|
self.update_plot_selection = True
|
4500
5072
|
if self.wx_exists:
|
@@ -4515,13 +5087,24 @@ class SelectionData():
|
|
4515
5087
|
self.update_plot_selection = True
|
4516
5088
|
|
4517
5089
|
if nb>0:
|
4518
|
-
if self.
|
5090
|
+
if self.is_all_selected():
|
4519
5091
|
[xmin, xmax], [ymin, ymax] = self.parent.get_bounds()
|
4520
5092
|
else:
|
4521
|
-
|
4522
|
-
|
4523
|
-
|
4524
|
-
|
5093
|
+
if self._storage_mode == StorageMode.ARRAY:
|
5094
|
+
# Get the bounds of the selection from the boolean array
|
5095
|
+
sel = np.argwhere(self._myselection_as_array)
|
5096
|
+
xmin = np.min(sel[:, 0])
|
5097
|
+
ymin = np.min(sel[:, 1])
|
5098
|
+
xmax = np.max(sel[:, 0])
|
5099
|
+
ymax = np.max(sel[:, 1])
|
5100
|
+
xmin, ymin = self.parent.get_xy_from_ij(xmin, ymin)
|
5101
|
+
xmax, ymax = self.parent.get_xy_from_ij(xmax, ymax)
|
5102
|
+
|
5103
|
+
elif self._storage_mode == StorageMode.LIST:
|
5104
|
+
xmin = np.min(np.asarray(self.myselection)[:, 0])
|
5105
|
+
ymin = np.min(np.asarray(self.myselection)[:, 1])
|
5106
|
+
xmax = np.max(np.asarray(self.myselection)[:, 0])
|
5107
|
+
ymax = np.max(np.asarray(self.myselection)[:, 1])
|
4525
5108
|
else:
|
4526
5109
|
xmin = -99999.
|
4527
5110
|
ymin = -99999.
|
@@ -4532,6 +5115,7 @@ class SelectionData():
|
|
4532
5115
|
|
4533
5116
|
self.parent.myops.nbselect.SetLabelText(str(nb))
|
4534
5117
|
self.parent.myops.nbselect2.SetLabelText(str(nb))
|
5118
|
+
|
4535
5119
|
if nb>0:
|
4536
5120
|
|
4537
5121
|
self.parent.myops.minx.SetLabelText('{:.3f}'.format(xmin))
|
@@ -4541,9 +5125,17 @@ class SelectionData():
|
|
4541
5125
|
|
4542
5126
|
return nb, xmin, xmax, ymin, ymax
|
4543
5127
|
|
4544
|
-
def condition_select(self, cond, condval, condval2=0, usemask=False):
|
5128
|
+
def condition_select(self, cond:str | int, condval, condval2 = 0, usemask:bool = False):
|
5129
|
+
""" Select nodes based on a condition.
|
5130
|
+
|
5131
|
+
:param cond: condition to apply (0: '<', 1: '<=', 2: '==', 3: '>=', 4: '>', 5: 'NaN', 6: '>=<=', 7: '><', 8: '<>')
|
5132
|
+
:param condval: value to compare with
|
5133
|
+
:param condval2: second value to compare with (for ranges)
|
5134
|
+
:param usemask: whether to use the mask or not
|
5135
|
+
"""
|
5136
|
+
|
4545
5137
|
array = self.parent.array
|
4546
|
-
nbini =
|
5138
|
+
nbini = self.nb
|
4547
5139
|
|
4548
5140
|
if array.dtype == np.float32:
|
4549
5141
|
condval = np.float32(condval)
|
@@ -4606,8 +5198,14 @@ class SelectionData():
|
|
4606
5198
|
return
|
4607
5199
|
else:
|
4608
5200
|
try:
|
4609
|
-
|
4610
|
-
|
5201
|
+
if self.nb > self.threshold_array_mode:
|
5202
|
+
# If the selection is large, we use the boolean array
|
5203
|
+
ijall = np.argwhere(self._myselection_as_array)
|
5204
|
+
else:
|
5205
|
+
# Otherwise, we use the selection list
|
5206
|
+
sel = np.asarray(self.myselection)
|
5207
|
+
ijall = self.parent.xy2ij_np(sel)
|
5208
|
+
|
4611
5209
|
if cond == 0 or cond=='<':
|
4612
5210
|
# <
|
4613
5211
|
ij = np.argwhere((array[ijall[:, 0], ijall[:, 1]] < condval) & (mask[ijall[:, 0], ijall[:, 1]]))
|
@@ -4684,8 +5282,11 @@ class SelectionData():
|
|
4684
5282
|
return
|
4685
5283
|
else:
|
4686
5284
|
try:
|
4687
|
-
|
4688
|
-
|
5285
|
+
if self.nb > self.threshold_array_mode:
|
5286
|
+
ijall = np.argwhere(self._myselection_as_array)
|
5287
|
+
else:
|
5288
|
+
sel = np.asarray(self.myselection)
|
5289
|
+
ijall = self.parent.xy2ij_np(sel)
|
4689
5290
|
|
4690
5291
|
if cond == 0 or cond=='<':
|
4691
5292
|
# <
|
@@ -4727,9 +5328,16 @@ class SelectionData():
|
|
4727
5328
|
logging.error(_('Error in condition_select ! -- Please report this bug, specifying the context'))
|
4728
5329
|
return
|
4729
5330
|
|
4730
|
-
self.update_nb_nodes_selection()
|
5331
|
+
# self.update_nb_nodes_selection()
|
4731
5332
|
|
4732
|
-
def treat_select(self, op, cond, opval, condval):
|
5333
|
+
def treat_select(self, op:int, cond:int, opval, condval):
|
5334
|
+
""" Apply an operation on the selected nodes based on a condition.
|
5335
|
+
|
5336
|
+
:param op: operation to apply (0: '+', 1: '-', 2: '*', 3: '/', 4: 'replace')
|
5337
|
+
:param cond: condition to apply (0: '<', 1: '<=', 2: '==', 3: '>=', 4: '>', 5: 'NaN')
|
5338
|
+
:param opval: value to apply the operation with
|
5339
|
+
:param condval: value to compare with
|
5340
|
+
"""
|
4733
5341
|
# operationChoices = [ u"+", u"-", u"*", u"/", u"replace'" ]
|
4734
5342
|
# conditionChoices = [ u"<", u"<=", u"=", u">=", u">",u"isNaN" ]
|
4735
5343
|
def test(val, cond, condval):
|
@@ -4770,7 +5378,7 @@ class SelectionData():
|
|
4770
5378
|
logging.error(_('Unknown dtype in treat_select !'))
|
4771
5379
|
return
|
4772
5380
|
|
4773
|
-
if self.myselection ==
|
5381
|
+
if self.myselection == ALL_SELECTED:
|
4774
5382
|
if op == 0:
|
4775
5383
|
if cond == 0:
|
4776
5384
|
# <
|
@@ -4933,7 +5541,15 @@ class SelectionData():
|
|
4933
5541
|
|
4934
5542
|
self.parent.reset_plot()
|
4935
5543
|
|
4936
|
-
def mask_condition(self, op, cond, opval, condval):
|
5544
|
+
def mask_condition(self, op:int, cond:int, opval, condval):
|
5545
|
+
""" Mask nodes based on a condition.
|
5546
|
+
|
5547
|
+
:param op: operation to apply (0: '+', 1: '-', 2: '*', 3: '/', 4: 'replace')
|
5548
|
+
:param cond: condition to apply (0: '<', 1: '<=', 2: '==', 3: '>=', 4: '>', 5: 'NaN')
|
5549
|
+
:param opval: value to apply the operation with
|
5550
|
+
:param condval: value to compare with
|
5551
|
+
"""
|
5552
|
+
|
4937
5553
|
# operationChoices = [ u"+", u"-", u"*", u"/", u"replace'" ]
|
4938
5554
|
# conditionChoices = [ u"<", u"<=", u"=", u">=", u">",u"isNaN" ]
|
4939
5555
|
def test(val, cond, condval):
|
@@ -4974,7 +5590,7 @@ class SelectionData():
|
|
4974
5590
|
logging.error(_('Unknown dtype in treat_select !'))
|
4975
5591
|
return
|
4976
5592
|
|
4977
|
-
if self.myselection ==
|
5593
|
+
if self.myselection == ALL_SELECTED:
|
4978
5594
|
if cond == 0:
|
4979
5595
|
# <
|
4980
5596
|
ind = np.argwhere(np.logical_and(array < condval, np.logical_not(array.mask)))
|
@@ -4997,35 +5613,42 @@ class SelectionData():
|
|
4997
5613
|
array.mask[ind[:, 0], ind[:, 1]] = True
|
4998
5614
|
|
4999
5615
|
else:
|
5000
|
-
|
5616
|
+
npself = np.asarray(self.myselection)
|
5617
|
+
ij = self.parent.xy2ij_np(npself)
|
5618
|
+
array.mask[ij[:, 0], ij[:, 1]] = test(array.data[ij[:, 0], ij[:, 1]], cond, condval)
|
5619
|
+
|
5620
|
+
# ij = [self.parent.get_ij_from_xy(cur[0], cur[1]) for cur in self.myselection]
|
5001
5621
|
|
5002
|
-
for i, j in ij:
|
5003
|
-
|
5004
|
-
|
5622
|
+
# for i, j in ij:
|
5623
|
+
# if test(array.data[i, j], cond, condval):
|
5624
|
+
# array.mask[i, j] = True
|
5005
5625
|
|
5006
5626
|
self.parent.nbnotnull = array.count()
|
5007
5627
|
self.parent.updatepalette()
|
5008
5628
|
self.parent.delete_lists()
|
5009
5629
|
|
5010
|
-
def get_values_sel(self):
|
5630
|
+
def get_values_sel(self) -> np.ndarray:
|
5631
|
+
""" Get the values of the selected nodes.
|
5011
5632
|
|
5012
|
-
if
|
5633
|
+
:return: numpy array of values of the selected nodes, or -99999 if all nodes are selected.
|
5634
|
+
"""
|
5635
|
+
|
5636
|
+
if self.myselection == ALL_SELECTED:
|
5013
5637
|
return -99999
|
5014
5638
|
else:
|
5015
5639
|
sel = np.asarray(self.myselection)
|
5016
5640
|
if len(sel) == 1:
|
5017
|
-
ijall =
|
5641
|
+
ijall = self.parent.xy2ij_np(sel)
|
5018
5642
|
z = self.parent.array[ijall[0], ijall[1]]
|
5019
5643
|
else:
|
5020
|
-
ijall =
|
5644
|
+
ijall = self.parent.xy2ij_np(sel)
|
5021
5645
|
z = self.parent.array[ijall[:, 0], ijall[:, 1]].flatten()
|
5022
5646
|
|
5023
5647
|
return z
|
5024
5648
|
|
5025
|
-
def _get_header(self):
|
5649
|
+
def _get_header(self) -> header_wolf | None:
|
5026
5650
|
""" Header corresponding to the selection """
|
5027
5651
|
|
5028
|
-
array = self.parent
|
5029
5652
|
sel = np.asarray(self.myselection)
|
5030
5653
|
|
5031
5654
|
myhead = header_wolf()
|
@@ -5050,13 +5673,17 @@ class SelectionData():
|
|
5050
5673
|
|
5051
5674
|
return myhead
|
5052
5675
|
|
5053
|
-
def get_newarray(self):
|
5054
|
-
""" Create a new array from the selection
|
5676
|
+
def get_newarray(self) -> "WolfArray":
|
5677
|
+
""" Create a new array from the selection.
|
5678
|
+
|
5679
|
+
:return: a new WolfArray object containing the selected nodes, or None if the selection is empty. Null values are set to -99999.
|
5680
|
+
If all nodes are selected, returns a WolfArray molded from the parent.
|
5681
|
+
"""
|
5055
5682
|
|
5056
5683
|
if self.nb == 0:
|
5057
5684
|
return None
|
5058
5685
|
|
5059
|
-
if self.myselection ==
|
5686
|
+
if self.myselection == ALL_SELECTED:
|
5060
5687
|
return WolfArray(mold=self.parent)
|
5061
5688
|
|
5062
5689
|
newarray = WolfArray()
|
@@ -5069,10 +5696,12 @@ class SelectionData():
|
|
5069
5696
|
|
5070
5697
|
sel = np.asarray(self.myselection)
|
5071
5698
|
if len(sel) == 1:
|
5072
|
-
ijall = np.asarray(self.parent.get_ij_from_xy(sel[0, 0], sel[0, 1])).transpose()
|
5699
|
+
# ijall = np.asarray(self.parent.get_ij_from_xy(sel[0, 0], sel[0, 1])).transpose()
|
5700
|
+
ijall = self.parent.xy2ij_np(sel)
|
5073
5701
|
z = self.parent.array[ijall[0], ijall[1]]
|
5074
5702
|
else:
|
5075
5703
|
ijall = np.asarray(self.parent.get_ij_from_xy(sel[:, 0], sel[:, 1])).transpose()
|
5704
|
+
ijall = self.parent.xy2ij_np(sel)
|
5076
5705
|
z = self.parent.array[ijall[:, 0], ijall[:, 1]].flatten()
|
5077
5706
|
|
5078
5707
|
newarray.array[:, :] = -99999.
|
@@ -5083,10 +5712,12 @@ class SelectionData():
|
|
5083
5712
|
return newarray
|
5084
5713
|
|
5085
5714
|
def select_all(self):
|
5086
|
-
""" Select all nodes
|
5715
|
+
""" Select all nodes.
|
5087
5716
|
|
5088
|
-
|
5089
|
-
|
5717
|
+
It will set the selection to ALL_SELECTED, which is a special value indicating that all nodes are selected.
|
5718
|
+
"""
|
5719
|
+
|
5720
|
+
self.myselection = ALL_SELECTED
|
5090
5721
|
|
5091
5722
|
def interp2Dpolygons(self, working_zone:zone,
|
5092
5723
|
method:Literal["nearest", "linear", "cubic"] = None,
|
@@ -5095,6 +5726,10 @@ class SelectionData():
|
|
5095
5726
|
"""
|
5096
5727
|
Interpolation sous tous les polygones d'une zone
|
5097
5728
|
cf parent.interp2Dpolygon
|
5729
|
+
|
5730
|
+
Useful for WX GUI, where the method is chosen by the user.
|
5731
|
+
From script, you can call this method directly from the parent array
|
5732
|
+
with the desired method and keep parameters.
|
5098
5733
|
"""
|
5099
5734
|
|
5100
5735
|
if method is None:
|
@@ -5120,6 +5755,10 @@ class SelectionData():
|
|
5120
5755
|
"""
|
5121
5756
|
Interpolation sous un polygone
|
5122
5757
|
cf parent.interp2Dpolygon
|
5758
|
+
|
5759
|
+
Useful for WX GUI, where the method is chosen by the user.
|
5760
|
+
From script, you can call this method directly from the parent array
|
5761
|
+
with the desired method and keep parameters.
|
5123
5762
|
"""
|
5124
5763
|
|
5125
5764
|
if method is None:
|
@@ -5142,6 +5781,10 @@ class SelectionData():
|
|
5142
5781
|
"""
|
5143
5782
|
Interpolation sous toutes les polylignes de la zone
|
5144
5783
|
cf parent.interp2Dpolyline
|
5784
|
+
|
5785
|
+
Useful for WX GUI, where the method is chosen by the user.
|
5786
|
+
From script, you can call this method directly from the parent array
|
5787
|
+
with the desired method and keep parameters.
|
5145
5788
|
"""
|
5146
5789
|
|
5147
5790
|
self.parent.interpolate_on_polylines(working_zone)
|
@@ -5153,6 +5796,10 @@ class SelectionData():
|
|
5153
5796
|
"""
|
5154
5797
|
Interpolation sous la polyligne active
|
5155
5798
|
cf parent.interp2Dpolyline
|
5799
|
+
|
5800
|
+
Useful for WX GUI, where the method is chosen by the user.
|
5801
|
+
From script, you can call this method directly from the parent array
|
5802
|
+
with the desired method and keep parameters.
|
5156
5803
|
"""
|
5157
5804
|
|
5158
5805
|
self.parent.interpolate_on_polyline(working_vector)
|
@@ -5160,14 +5807,31 @@ class SelectionData():
|
|
5160
5807
|
if resetplot:
|
5161
5808
|
self.parent.reset_plot()
|
5162
5809
|
|
5163
|
-
|
5164
5810
|
def copy(self, source:"SelectionData"):
|
5811
|
+
""" Copy the selection data from another SelectionData object.
|
5812
|
+
|
5813
|
+
:param source: another SelectionData object to copy from.
|
5814
|
+
"""
|
5815
|
+
|
5816
|
+
if source._storage_mode == StorageMode.ARRAY:
|
5817
|
+
self._storage_mode = StorageMode.ARRAY
|
5818
|
+
self._myselection_as_array = source._myselection_as_array.copy()
|
5165
5819
|
|
5166
|
-
|
5820
|
+
elif source._storage_mode == StorageMode.LIST:
|
5821
|
+
self._storage_mode = StorageMode.LIST
|
5822
|
+
self._myselection = source._myselection.copy()
|
5167
5823
|
|
5168
5824
|
def volumesurface(self, show=True):
|
5169
5825
|
"""
|
5170
|
-
Evaluation of stage-storage-surface relation
|
5826
|
+
Evaluation of stage-storage-surface relation from "volume_estimation" routine of the WolfArray.
|
5827
|
+
|
5828
|
+
If the selection is empty, it will use the whole array.
|
5829
|
+
If the selection is ALL_SELECTED, it will use the whole array.
|
5830
|
+
If the selection is not empty, it will use the selected nodes only -- see "get_newarray" routine.
|
5831
|
+
|
5832
|
+
If the parent array is linked to a MapViewer, it will apply the same operation to all linked active arrays.
|
5833
|
+
|
5834
|
+
:param show: if True, the figure will be shown.
|
5171
5835
|
"""
|
5172
5836
|
|
5173
5837
|
if self.parent.get_mapviewer() is not None:
|
@@ -5176,38 +5840,36 @@ class SelectionData():
|
|
5176
5840
|
|
5177
5841
|
if mapviewer.linked:
|
5178
5842
|
|
5179
|
-
|
5180
|
-
|
5181
|
-
|
5182
|
-
|
5183
|
-
|
5184
|
-
|
5185
|
-
|
5186
|
-
|
5187
|
-
|
5188
|
-
|
5189
|
-
|
5190
|
-
myarray = array1
|
5191
|
-
fig, axs = myarray.volume_estimation()
|
5192
|
-
|
5193
|
-
myarray = array2
|
5194
|
-
fig, axs = myarray.volume_estimation(axs)
|
5843
|
+
# If linked arrays, we copy the selection data to all linked arrays
|
5844
|
+
arrays = [cur.active_array for cur in mapviewer.linkedList]
|
5845
|
+
for cur in arrays:
|
5846
|
+
if cur is not self.parent:
|
5847
|
+
cur.SelectionData.copy(self.parent.SelectionData)
|
5848
|
+
|
5849
|
+
if self.nb == 0 or self.myselection == ALL_SELECTED:
|
5850
|
+
for cur in arrays:
|
5851
|
+
fig, axs = cur.volume_estimation()
|
5852
|
+
if show:
|
5853
|
+
fig.show()
|
5195
5854
|
else:
|
5196
|
-
|
5197
|
-
|
5198
|
-
|
5199
|
-
|
5200
|
-
|
5855
|
+
for cur in arrays:
|
5856
|
+
myarray = cur.SelectionData.get_newarray()
|
5857
|
+
fig, axs = myarray.volume_estimation()
|
5858
|
+
if show:
|
5859
|
+
fig.show()
|
5201
5860
|
else:
|
5202
|
-
if len(self.parent.
|
5861
|
+
if len(self.parent.SelectionData.myselection) == 0 or self.parent.SelectionData.myselection == ALL_SELECTED:
|
5203
5862
|
myarray = self.parent
|
5204
5863
|
else:
|
5205
|
-
myarray = self.parent.
|
5206
|
-
myarray.SelectionData.selections = self.parent.
|
5864
|
+
myarray = self.parent.SelectionData.get_newarray()
|
5865
|
+
myarray.SelectionData.selections = self.parent.SelectionData.selections.copy()
|
5207
5866
|
|
5208
5867
|
fig, axs = myarray.volume_estimation()
|
5868
|
+
|
5869
|
+
if show:
|
5870
|
+
fig.show()
|
5209
5871
|
else:
|
5210
|
-
if self.nb == 0 or self.myselection ==
|
5872
|
+
if self.nb == 0 or self.myselection == ALL_SELECTED:
|
5211
5873
|
myarray = self.parent
|
5212
5874
|
else:
|
5213
5875
|
myarray = self.get_newarray()
|
@@ -5215,11 +5877,16 @@ class SelectionData():
|
|
5215
5877
|
|
5216
5878
|
fig, axs = myarray.volume_estimation()
|
5217
5879
|
|
5218
|
-
|
5219
|
-
|
5880
|
+
if show:
|
5881
|
+
fig.show()
|
5220
5882
|
|
5221
5883
|
class SelectionDataMB(SelectionData):
|
5222
|
-
""" Extension of SelectionData to manage multiple blocks
|
5884
|
+
""" Extension of SelectionData to manage multiple blocks.
|
5885
|
+
|
5886
|
+
All routines are not implemented yet, as they depend on the specificities of the blocks.
|
5887
|
+
|
5888
|
+
For the rest, it is mainly a simple wrapper around the SelectionData of each block.
|
5889
|
+
"""
|
5223
5890
|
|
5224
5891
|
def __init__(self, parent:"WolfArrayMB"):
|
5225
5892
|
SelectionData.__init__(self, parent)
|
@@ -5231,10 +5898,10 @@ class SelectionDataMB(SelectionData):
|
|
5231
5898
|
|
5232
5899
|
return np.sum([cur.SelectionData.nb for cur in self.parent.active_blocks])
|
5233
5900
|
|
5234
|
-
def
|
5901
|
+
def unmask_selection(self):
|
5235
5902
|
|
5236
5903
|
for curblock in self.parent.active_blocks:
|
5237
|
-
curblock.SelectionData.
|
5904
|
+
curblock.SelectionData.unmask_selection(resetplot=False)
|
5238
5905
|
|
5239
5906
|
self.parent.reset_plot()
|
5240
5907
|
|
@@ -5280,7 +5947,7 @@ class SelectionDataMB(SelectionData):
|
|
5280
5947
|
for curblock in self.parent.active_blocks:
|
5281
5948
|
ret = curblock.SelectionData.add_node_to_selection(x, y, verif)
|
5282
5949
|
|
5283
|
-
def add_nodes_to_selection(self, xy:list[float], verif:bool=True):
|
5950
|
+
def add_nodes_to_selection(self, xy:list[float] | np.ndarray, verif:bool=True):
|
5284
5951
|
""" Add nodes to the selection """
|
5285
5952
|
|
5286
5953
|
for curblock in self.parent.active_blocks:
|
@@ -5686,6 +6353,14 @@ class WolfArray(Element_To_Draw, header_wolf):
|
|
5686
6353
|
|
5687
6354
|
self.add_ops_sel() # Ajout d'un gestionnaire de sélection et d'opérations
|
5688
6355
|
|
6356
|
+
@property
|
6357
|
+
def usemask(self):
|
6358
|
+
""" Check if we want to use the mask in the operations """
|
6359
|
+
if self.myops is not None:
|
6360
|
+
return self.myops.usemask
|
6361
|
+
else:
|
6362
|
+
return False
|
6363
|
+
|
5689
6364
|
def __getstate__(self):
|
5690
6365
|
""" Récupération de l'état de l'objet pour la sérialisation """
|
5691
6366
|
state = self.__dict__.copy()
|
@@ -5800,8 +6475,8 @@ class WolfArray(Element_To_Draw, header_wolf):
|
|
5800
6475
|
""" Get the centers of the cells """
|
5801
6476
|
|
5802
6477
|
if usenap:
|
5803
|
-
ij = np.
|
5804
|
-
xy = self.get_xy_from_ij_array(
|
6478
|
+
ij = np.argwhere(self.array.mask==False,)
|
6479
|
+
xy = self.get_xy_from_ij_array(ij).copy().flatten()
|
5805
6480
|
else:
|
5806
6481
|
ij = np.meshgrid(np.arange(self.nbx), np.arange(self.nby))
|
5807
6482
|
ij = np.asarray([ij[0].flatten(), ij[1].flatten()]).T
|
@@ -6182,7 +6857,7 @@ class WolfArray(Element_To_Draw, header_wolf):
|
|
6182
6857
|
logging.info(_('No selection -- no filtering'))
|
6183
6858
|
return
|
6184
6859
|
|
6185
|
-
if self.SelectionData.myselection ==
|
6860
|
+
if self.SelectionData.myselection == ALL_SELECTED:
|
6186
6861
|
logging.info(_('All nodes selected -- no filtering'))
|
6187
6862
|
return
|
6188
6863
|
|
@@ -6282,7 +6957,7 @@ class WolfArray(Element_To_Draw, header_wolf):
|
|
6282
6957
|
|
6283
6958
|
vals = self.array[ij[:,0], ij[:,1]]
|
6284
6959
|
|
6285
|
-
elif self.SelectionData.nb == 0 or self.SelectionData.myselection ==
|
6960
|
+
elif self.SelectionData.nb == 0 or self.SelectionData.myselection == ALL_SELECTED:
|
6286
6961
|
logging.info(_('No selection -- statistics on the whole array'))
|
6287
6962
|
vals = self.array[~self.array.mask].ravel().data # all values
|
6288
6963
|
|
@@ -7150,7 +7825,7 @@ class WolfArray(Element_To_Draw, header_wolf):
|
|
7150
7825
|
See : https://docs.scipy.org/doc/scipy/reference/generated/scipy.interpolate.griddata.html
|
7151
7826
|
"""
|
7152
7827
|
|
7153
|
-
if self.mngselection.myselection == [] or self.mngselection.myselection ==
|
7828
|
+
if self.mngselection.myselection == [] or self.mngselection.myselection == ALL_SELECTED:
|
7154
7829
|
|
7155
7830
|
decalx = self.origx + self.translx
|
7156
7831
|
decaly = self.origy + self.transly
|
@@ -7210,7 +7885,7 @@ class WolfArray(Element_To_Draw, header_wolf):
|
|
7210
7885
|
|
7211
7886
|
try:
|
7212
7887
|
if self.mngselection is not None:
|
7213
|
-
if self.mngselection.myselection != [] and self.mngselection.myselection !=
|
7888
|
+
if self.mngselection.myselection != [] and self.mngselection.myselection != ALL_SELECTED:
|
7214
7889
|
# interpolation only in the selected cells
|
7215
7890
|
|
7216
7891
|
# Convert coordinates to indices
|
@@ -7268,7 +7943,7 @@ class WolfArray(Element_To_Draw, header_wolf):
|
|
7268
7943
|
|
7269
7944
|
self.array.data[ij[:, 0], ij[:, 1]] = newvalues
|
7270
7945
|
|
7271
|
-
elif self.mngselection.myselection ==
|
7946
|
+
elif self.mngselection.myselection == ALL_SELECTED and (grid_x is None and grid_y is None):
|
7272
7947
|
# interpolation in all the cells
|
7273
7948
|
|
7274
7949
|
# Creating a grid for all cells
|
@@ -7918,6 +8593,248 @@ class WolfArray(Element_To_Draw, header_wolf):
|
|
7918
8593
|
|
7919
8594
|
return fig, axs
|
7920
8595
|
|
8596
|
+
def surface_volume_estimation_from_elevation(self, axs:plt.Axes= None,
|
8597
|
+
desired_zmin:float = None, desired_zmax:float = None,
|
8598
|
+
nb:int = 100,
|
8599
|
+
method:Literal['all below', 'largest area', 'selected'] = 'largest area',
|
8600
|
+
selected_cells:list[tuple[float, float]] = None,
|
8601
|
+
dirout:str | Path = None,
|
8602
|
+
invert_z:bool = False,
|
8603
|
+
array_to_integrate:"WolfArray" = None
|
8604
|
+
):
|
8605
|
+
""" Estimation of the surface and volume from an elevation array.
|
8606
|
+
|
8607
|
+
:param axs: Axes to plot the results
|
8608
|
+
:param desired_zmin: Minimum elevation to consider
|
8609
|
+
:param desired_zmax: Maximum elevation to consider
|
8610
|
+
:param nb: Number of elevation steps
|
8611
|
+
:param method: Method to use for the estimation ('all below', 'largest area', 'selected')
|
8612
|
+
:param selected_cells: List of kernel cells (if method is 'selected') -- Consider only the areas containing these cells
|
8613
|
+
:param dirout: Directory to save the results
|
8614
|
+
:return: Figure and Axes with the results
|
8615
|
+
:rtype: tuple[plt.Figure, plt.Axes]
|
8616
|
+
"""
|
8617
|
+
|
8618
|
+
assert method in ['all below', 'largest area', 'selected'], _('Method must be one of "all below", "largest area", or "selected"')
|
8619
|
+
if array_to_integrate is not None:
|
8620
|
+
assert isinstance(array_to_integrate, WolfArray), _('array_to_integrate must be a WolfArray instance')
|
8621
|
+
assert array_to_integrate.nbx == self.nbx and array_to_integrate.nby == self.nby, _('array_to_integrate must have the same dimensions as the current WolfArray')
|
8622
|
+
|
8623
|
+
vect = self.array[np.logical_not(self.array.mask)].flatten()
|
8624
|
+
zmin = np.amin(vect)
|
8625
|
+
zmax = np.amax(vect)
|
8626
|
+
|
8627
|
+
if desired_zmin is not None:
|
8628
|
+
zmin = desired_zmin
|
8629
|
+
|
8630
|
+
if desired_zmax is not None:
|
8631
|
+
zmax = desired_zmax
|
8632
|
+
|
8633
|
+
deltaz = (zmax - zmin) / nb
|
8634
|
+
|
8635
|
+
curz = zmin
|
8636
|
+
labeled_areas = []
|
8637
|
+
stockage = []
|
8638
|
+
z = []
|
8639
|
+
|
8640
|
+
if method == 'all below':
|
8641
|
+
labeled = False
|
8642
|
+
else:
|
8643
|
+
labeled = True
|
8644
|
+
if method == 'largest area':
|
8645
|
+
use_memory = False
|
8646
|
+
else:
|
8647
|
+
xy_key = np.asarray(selected_cells, dtype=np.float64)
|
8648
|
+
if xy_key is None:
|
8649
|
+
logging.error(_('Empty selection'))
|
8650
|
+
return
|
8651
|
+
|
8652
|
+
if len(xy_key) == 0:
|
8653
|
+
logging.error(_('Empty selection'))
|
8654
|
+
return
|
8655
|
+
|
8656
|
+
ij_key = self.xy2ij_np(xy_key)
|
8657
|
+
|
8658
|
+
use_memory = True
|
8659
|
+
|
8660
|
+
extensionmax = WolfArray(mold=self)
|
8661
|
+
extensionmax.array[:, :] = 0.
|
8662
|
+
|
8663
|
+
if labeled:
|
8664
|
+
for i in tqdm(range(nb + 1)):
|
8665
|
+
z.append(curz)
|
8666
|
+
|
8667
|
+
# Compute difference between the array and the current elevation
|
8668
|
+
if i == 0:
|
8669
|
+
diff = self.array - (curz + 1.e-3)
|
8670
|
+
else:
|
8671
|
+
diff = self.array - curz
|
8672
|
+
|
8673
|
+
# Keep only the negative values
|
8674
|
+
diff[diff > 0] = 0.
|
8675
|
+
diff.data[diff.mask] = 0.
|
8676
|
+
|
8677
|
+
if np.count_nonzero(diff < 0.) > 1:
|
8678
|
+
# More than one cell below the elevation
|
8679
|
+
|
8680
|
+
# Labeling of the cells below the elevation
|
8681
|
+
labeled_array, num_features = label(diff.data)
|
8682
|
+
# Applying the same mask as the original array
|
8683
|
+
labeled_array = ma.asarray(labeled_array)
|
8684
|
+
labeled_array.mask = self.array.mask
|
8685
|
+
|
8686
|
+
if use_memory:
|
8687
|
+
# Use only the labeled areas containing the cells stored in selected_cells
|
8688
|
+
labeled_areas = []
|
8689
|
+
for curij in ij_key:
|
8690
|
+
labeled_areas.append(labeled_array[curij[0], curij[1]])
|
8691
|
+
|
8692
|
+
# Remove masked value
|
8693
|
+
labeled_areas = [x for x in labeled_areas if x is not ma.masked]
|
8694
|
+
# Remove duplicates
|
8695
|
+
labeled_areas = list(set(labeled_areas))
|
8696
|
+
|
8697
|
+
for curarea in labeled_areas:
|
8698
|
+
if curarea == 0:
|
8699
|
+
volume = 0.
|
8700
|
+
surface = 0.
|
8701
|
+
continue
|
8702
|
+
|
8703
|
+
# Search
|
8704
|
+
mask = labeled_array == curarea
|
8705
|
+
area = np.argwhere(mask)
|
8706
|
+
|
8707
|
+
if array_to_integrate is None:
|
8708
|
+
volume = -self.dx * self.dy * np.ma.sum(diff[area])
|
8709
|
+
else:
|
8710
|
+
volume = self.dx * self.dy * np.ma.sum(array_to_integrate.array[area[:,0], area[:,1]])
|
8711
|
+
|
8712
|
+
surface = self.dx * self.dy * area.shape[0]
|
8713
|
+
extensionmax.array[np.logical_and(mask, extensionmax.array == 0.)] = float(i + 1)
|
8714
|
+
|
8715
|
+
else:
|
8716
|
+
labeled_areas = list(sum_labels(np.ones(labeled_array.shape, dtype=np.int32), labeled_array, range(1, num_features+1)))
|
8717
|
+
labeled_areas = [[x, y] for x, y in zip(labeled_areas, range(1, num_features+1))]
|
8718
|
+
labeled_areas.sort(key=lambda x: x[0], reverse=True)
|
8719
|
+
jmax = labeled_areas[0][1]
|
8720
|
+
nbmax = labeled_areas[0][0]
|
8721
|
+
|
8722
|
+
if array_to_integrate is None:
|
8723
|
+
volume = -self.dx * self.dy * np.ma.sum(diff[labeled_array == jmax])
|
8724
|
+
else:
|
8725
|
+
volume = self.dx * self.dy * np.ma.sum(array_to_integrate.array[labeled_array == jmax])
|
8726
|
+
|
8727
|
+
surface = self.dx * self.dy * nbmax
|
8728
|
+
extensionmax.array[np.logical_and(labeled_array == jmax, extensionmax.array == 0.)] = float(i + 1)
|
8729
|
+
else:
|
8730
|
+
# Only one cell below the elevation
|
8731
|
+
if array_to_integrate is None:
|
8732
|
+
volume = -self.dx * self.dy * np.ma.sum(diff)
|
8733
|
+
else:
|
8734
|
+
volume = self.dx * self.dy * np.ma.sum(array_to_integrate.array[diff < 0.])
|
8735
|
+
|
8736
|
+
surface = self.dx * self.dy * np.count_nonzero(diff<0.)
|
8737
|
+
extensionmax.array[np.logical_and(diff[:,:]<0., extensionmax.array[:, :] == 0.)] = float(i + 1)
|
8738
|
+
|
8739
|
+
stockage.append([abs(volume), abs(surface)])
|
8740
|
+
curz += deltaz
|
8741
|
+
|
8742
|
+
else:
|
8743
|
+
for i in tqdm(range(nb + 1)):
|
8744
|
+
z.append(curz)
|
8745
|
+
|
8746
|
+
if i == 0:
|
8747
|
+
diff = self.array - (curz + 1.e-3)
|
8748
|
+
else:
|
8749
|
+
diff = self.array - curz
|
8750
|
+
|
8751
|
+
diff[diff > 0] = 0.
|
8752
|
+
diff.data[diff.mask] = 0.
|
8753
|
+
|
8754
|
+
if array_to_integrate is None:
|
8755
|
+
volume = -self.dx * self.dy * np.ma.sum(diff)
|
8756
|
+
surface = self.dx * self.dy * np.count_nonzero(diff<0.)
|
8757
|
+
stockage.append([abs(volume), abs(surface)])
|
8758
|
+
else:
|
8759
|
+
ij = np.argwhere(diff < 0.)
|
8760
|
+
volume = self.dx * self.dy * np.ma.sum(array_to_integrate.array[ij[:,0], ij[:,1]])
|
8761
|
+
surface = self.dx * self.dy * ij.shape[0]
|
8762
|
+
stockage.append([volume, abs(surface)])
|
8763
|
+
|
8764
|
+
curz += deltaz
|
8765
|
+
|
8766
|
+
extensionmax.array[np.logical_and(diff[:,:]<0., extensionmax.array[:, :] == 0.)] = float(i + 1)
|
8767
|
+
|
8768
|
+
if dirout is None:
|
8769
|
+
dirout = Path(self.filename).parent / 'surface_volume'
|
8770
|
+
|
8771
|
+
if isinstance(dirout, str):
|
8772
|
+
dirout = Path(dirout)
|
8773
|
+
if not dirout.exists():
|
8774
|
+
dirout.mkdir(parents=True, exist_ok=True)
|
8775
|
+
|
8776
|
+
extensionmax.filename = str(dirout / 'surface_volume_extension.tif')
|
8777
|
+
extensionmax.write_all()
|
8778
|
+
|
8779
|
+
if axs is None:
|
8780
|
+
fig, axs = plt.subplots(1, 2, tight_layout=True)
|
8781
|
+
else:
|
8782
|
+
fig = axs[0].get_figure()
|
8783
|
+
|
8784
|
+
if invert_z:
|
8785
|
+
z = [abs(zmin - x) for x in z]
|
8786
|
+
labelx = _("Water depth [m]")
|
8787
|
+
else:
|
8788
|
+
labelx = _("Elevation [m]")
|
8789
|
+
|
8790
|
+
axs[0].plot(z, [x[0] for x in stockage])
|
8791
|
+
axs[0].scatter(z, [x[0] for x in stockage])
|
8792
|
+
axs[0].set_xlabel(labelx, size=10)
|
8793
|
+
axs[0].set_ylabel(_("Volume [$m^3$]"), size=10)
|
8794
|
+
axs[1].step(z, [x[1] for x in stockage], where='post')
|
8795
|
+
axs[1].scatter(z, [x[1] for x in stockage])
|
8796
|
+
axs[1].set_xlabel(labelx, size=10)
|
8797
|
+
axs[1].set_ylabel(_("Surface [$m^2$]"), size=10)
|
8798
|
+
fig.suptitle(_("Retention capacity"), fontsize=12)
|
8799
|
+
|
8800
|
+
fig.savefig(dirout / 'surface_volume.png', dpi=300)
|
8801
|
+
|
8802
|
+
fn = dirout / 'surface_volume_hvs.txt'
|
8803
|
+
with open(fn, 'w') as f:
|
8804
|
+
f.write('H [m]\tZ [m DNG]\tVolume [$m^3$]\tSurface [$m^2$]\n')
|
8805
|
+
for curz, (curv, curs) in zip(z, stockage):
|
8806
|
+
f.write('{}\t{}\t{}\t{}\n'.format(curz - zmin, curz, curv, curs))
|
8807
|
+
|
8808
|
+
return fig, axs
|
8809
|
+
|
8810
|
+
def surface_volume_estimation_from_waterdepth(self, axs:plt.Axes=None,
|
8811
|
+
desired_zmin:float = None, desired_zmax:float = None,
|
8812
|
+
nb:int = 100,
|
8813
|
+
method:Literal['all below', 'largest area', 'selected'] = 'largest',
|
8814
|
+
selected_cells:list[tuple[float, float]] = None,
|
8815
|
+
dirout:str | Path = None):
|
8816
|
+
""" Estimation of the surface and volume from a water depth array.
|
8817
|
+
:param axs: Axes to plot the results
|
8818
|
+
:param desired_zmin: Minimum water depth to consider
|
8819
|
+
:param desired_zmax: Maximum water depth to consider
|
8820
|
+
:param nb: Number of water depth steps
|
8821
|
+
:param method: Method to use for the estimation ('all below', 'largest area', 'selected')
|
8822
|
+
:param selected_cells: List of kernel cells (if method is 'selected') -- Consider only the areas containing these cells
|
8823
|
+
:param dirout: Directory to save the results
|
8824
|
+
:return: Figure and Axes with the results
|
8825
|
+
:rtype: tuple[plt.Figure, plt.Axes]
|
8826
|
+
"""
|
8827
|
+
|
8828
|
+
neg_array = WolfArray(mold=self)
|
8829
|
+
neg_array.array[:,:] = -self.array
|
8830
|
+
return neg_array.surface_volume_estimation_from_elevation(desired_zmin=-desired_zmax if desired_zmax is not None else None,
|
8831
|
+
desired_zmax=-desired_zmin if desired_zmin is not None else None,
|
8832
|
+
nb=nb, method=method,
|
8833
|
+
selected_cells=selected_cells,
|
8834
|
+
dirout=dirout if dirout is not None else Path(self.filename).parent / 'surface_volume',
|
8835
|
+
axs=axs, invert_z=True)
|
8836
|
+
|
8837
|
+
|
7921
8838
|
def paste_all(self, fromarray:"WolfArray", mask_after:bool=True):
|
7922
8839
|
""" Paste all the values from another WolfArray """
|
7923
8840
|
|
@@ -7972,14 +8889,17 @@ class WolfArray(Element_To_Draw, header_wolf):
|
|
7972
8889
|
z = np.asarray(z)
|
7973
8890
|
|
7974
8891
|
if len(sel) == 1:
|
7975
|
-
ijall =
|
7976
|
-
i = ijall[0]
|
7977
|
-
j = ijall[1]
|
8892
|
+
ijall = self.xy2ij_np(sel)
|
7978
8893
|
|
7979
|
-
|
8894
|
+
i, j = ijall[0, 0], ijall[0, 1]
|
8895
|
+
if i < 0 or i >= self.nbx or j < 0 or j >= self.nby:
|
8896
|
+
logging.error(_('Selected point is out of bounds'))
|
8897
|
+
return
|
8898
|
+
else:
|
7980
8899
|
self.array[i, j] = z
|
7981
8900
|
else:
|
7982
|
-
ijall = np.asarray(self.get_ij_from_xy(sel[:, 0], sel[:, 1])).transpose()
|
8901
|
+
# ijall = np.asarray(self.get_ij_from_xy(sel[:, 0], sel[:, 1])).transpose()
|
8902
|
+
ijall = self.xy2ij_np(sel)
|
7983
8903
|
|
7984
8904
|
useful = np.where((ijall[:, 0] >= 0) & (ijall[:, 0] < self.nbx) & (ijall[:, 1] >= 0) & (ijall[:, 1] < self.nby))
|
7985
8905
|
|
@@ -8056,7 +8976,7 @@ class WolfArray(Element_To_Draw, header_wolf):
|
|
8056
8976
|
ij = [self.get_ij_from_xy(cur[0], cur[1]) for cur in curlist]
|
8057
8977
|
z = [self.array.data[curij[0], curij[1]] for curij in ij]
|
8058
8978
|
|
8059
|
-
if cursel ==
|
8979
|
+
if cursel == ALL_SELECTED:
|
8060
8980
|
xall = np.linspace(self.origx + self.dx / 2., self.origx + (float(self.nbx) - .5) * self.dx,
|
8061
8981
|
self.nbx)
|
8062
8982
|
yall = np.linspace(self.origy + self.dy / 2., self.origy + (float(self.nby) - .5) * self.dy,
|
@@ -8549,12 +9469,16 @@ class WolfArray(Element_To_Draw, header_wolf):
|
|
8549
9469
|
# trouve les indices dans le polygone
|
8550
9470
|
for myvect in myvects:
|
8551
9471
|
myij = self.get_ij_inside_polygon(myvect, usemask=False, eps=eps, method=method)
|
8552
|
-
# masquage des mailles contenues
|
8553
|
-
self.array.mask[myij[:,0],myij[:,1]] = True
|
8554
9472
|
|
8555
|
-
if
|
8556
|
-
|
8557
|
-
|
9473
|
+
if myij.shape[0] == 0:
|
9474
|
+
logging.warning(_("No indices found in the polygon {}").format(myvect.myname))
|
9475
|
+
else:
|
9476
|
+
# masquage des mailles contenues
|
9477
|
+
self.array.mask[myij[:,0],myij[:,1]] = True
|
9478
|
+
|
9479
|
+
if set_nullvalue:
|
9480
|
+
# annulation des valeurs en dehors du polygone
|
9481
|
+
self.array.data[myij[:,0],myij[:,1]] = self.nullvalue
|
8558
9482
|
|
8559
9483
|
self.count()
|
8560
9484
|
|
@@ -9242,6 +10166,8 @@ class WolfArray(Element_To_Draw, header_wolf):
|
|
9242
10166
|
if newpath is not None:
|
9243
10167
|
self.filename = newpath
|
9244
10168
|
|
10169
|
+
self.filename = str(self.filename)
|
10170
|
+
|
9245
10171
|
if self.filename.endswith('.tif') or self.filename.endswith('.tiff'):
|
9246
10172
|
self.export_geotif(EPSG=EPSG)
|
9247
10173
|
elif self.filename.endswith('.npy'):
|
@@ -11830,6 +12756,8 @@ class WolfArrayMB(WolfArray):
|
|
11830
12756
|
"""
|
11831
12757
|
Adds a properly configured block this multiblock.
|
11832
12758
|
|
12759
|
+
Do not forget to call "set_header_from_added_blocks" after adding all blocks.
|
12760
|
+
|
11833
12761
|
:param arr: The block to add.
|
11834
12762
|
:param force_idx: If True, the index/key will be set on `arr`. If False, the index/key must already be set on `arr`.
|
11835
12763
|
"""
|