wolfhece 2.2.17__py3-none-any.whl → 2.2.20__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
wolfhece/PyDraw.py CHANGED
@@ -2077,7 +2077,7 @@ class WolfMapViewer(wx.Frame):
2077
2077
  self.popupmenu = wx.Menu()
2078
2078
  self.popupmenu.Bind(wx.EVT_MENU, self.OnPopupItemSelected)
2079
2079
 
2080
- for text in [_('Save'), _('Save as'), _('Rename'), _('Duplicate'), _('Delete'), _('Up'), _('Down'), _('Check/Uncheck'), _('Properties')]:
2080
+ for text in [_('Save'), _('Save as'), _('Rename'), _('Duplicate'), _('Delete'), _('Up'), _('Down'), _('Check/Uncheck'), _('Properties'), _('Reload')]:
2081
2081
  item = self.popupmenu.Append(-1, text)
2082
2082
 
2083
2083
  self.menubar = wx.MenuBar()
@@ -2295,9 +2295,9 @@ class WolfMapViewer(wx.Frame):
2295
2295
 
2296
2296
  self.analyzeplot.AppendSeparator()
2297
2297
 
2298
- self.analyzemenu.Append(wx.ID_ANY,_('Plot...'),self.analyzeplot)
2299
- self.analyzemenu.Append(wx.ID_ANY,_('Export...'),self.analyzeexport)
2300
- self.analyzemenu.Append(wx.ID_ANY,_('Inpaint...'),self.analyzeinpaint)
2298
+ self.analyzemenu.Append(wx.ID_ANY,_('Plot...'), self.analyzeplot)
2299
+ self.analyzemenu.Append(wx.ID_ANY,_('Export...'), self.analyzeexport)
2300
+ self.analyzemenu.Append(wx.ID_ANY,_('Inpaint...'), self.analyzeinpaint)
2301
2301
 
2302
2302
 
2303
2303
  self.analyzeinpaint.Append(wx.ID_ANY, _("Inpaint active array..."), _("Inpaint active array"))
@@ -2700,12 +2700,19 @@ class WolfMapViewer(wx.Frame):
2700
2700
  self.trianglesmenu = wx.Menu()
2701
2701
  self.menubar.Append(self.trianglesmenu, _('&Triangulations'))
2702
2702
 
2703
- self._menuinteractptri = self.trianglesmenu.Append(wx.ID_ANY, _("Interpolate on active triangulation..."), _("InterpolateTri"))
2703
+ self._menuinteractptri = self.trianglesmenu.Append(wx.ID_ANY, _("Interpolate on active triangulation..."),
2704
+ _('Interpolate active array on active triangulation'))
2705
+
2706
+ self._menuinteractptri_above = self.trianglesmenu.Append(wx.ID_ANY, _("Interpolate on active triangulation (keep only above)..."),
2707
+ _('Interpolate active array on active triangulation but keep only values above the array'))
2708
+
2709
+ self._menuinteractptri_below = self.trianglesmenu.Append(wx.ID_ANY, _("Interpolate on active triangulation (keep only below)..."),
2710
+ _('Interpolate active array on active triangulation but keep only values below the array'))
2711
+
2704
2712
  self._menucomparetri = self.trianglesmenu.Append(wx.ID_ANY, _("Compare triangles to array..."), _("Comparison"))
2705
2713
  self._menumovetri = self.trianglesmenu.Append(wx.ID_ANY, _("Move triangles..."), _("Move triangles"))
2706
2714
  self._menurotatetri = self.trianglesmenu.Append(wx.ID_ANY, _("Rotate triangles..."), _("Rotate triangles"))
2707
2715
 
2708
-
2709
2716
  def create_cloud_menu(self):
2710
2717
  """ Menu for cloud points """
2711
2718
 
@@ -3001,6 +3008,29 @@ class WolfMapViewer(wx.Frame):
3001
3008
 
3002
3009
  return [xmin, ymin, xmax, ymax]
3003
3010
 
3011
+ def get_bounds(self):
3012
+ """
3013
+ Retourne les limites de la zone d'affichage, voir aussi get_canvas_bounds
3014
+
3015
+ :return: ([xmin, xmax], [ymin, ymax])
3016
+ """
3017
+ xmin, ymin, xmax, ymax = self.get_canvas_bounds()
3018
+ return ([xmin, xmax], [ymin, ymax])
3019
+
3020
+ def get_bounds_as_polygon(self) -> vector:
3021
+ """
3022
+ Retourne les limites de la zone d'affichage sous forme de polygone
3023
+ :return: vector
3024
+ """
3025
+ xmin, ymin, xmax, ymax = self.get_canvas_bounds()
3026
+ poly = vector()
3027
+ poly.add_vertex(wolfvertex(xmin, ymin))
3028
+ poly.add_vertex(wolfvertex(xmax, ymin))
3029
+ poly.add_vertex(wolfvertex(xmax, ymax))
3030
+ poly.add_vertex(wolfvertex(xmin, ymax))
3031
+ poly.force_to_close()
3032
+ return poly
3033
+
3004
3034
  def Onmenuwalous(self, event: wx.MenuEvent):
3005
3035
 
3006
3036
  id = event.GetId()
@@ -4390,7 +4420,11 @@ class WolfMapViewer(wx.Frame):
4390
4420
 
4391
4421
  self.myinterp.interp_on_array(self.active_array, method)
4392
4422
 
4393
- def interpolate_triangulation(self):
4423
+ def interpolate_triangulation(self, keep:Literal['all', 'above', 'below'] = 'all'):
4424
+ """ Alias to interpolate on triangulation
4425
+
4426
+ :param keep: 'all' to keep all points, 'above' to keep only points above the current array's value, 'below' to keep only points below the current array's value
4427
+ """
4394
4428
 
4395
4429
  if self.active_array is None:
4396
4430
  logging.warning(_('No active array -- Please activate an array first'))
@@ -4400,7 +4434,7 @@ class WolfMapViewer(wx.Frame):
4400
4434
  logging.warning(_('No active triangulation -- Please activate a triangulation first'))
4401
4435
  return
4402
4436
 
4403
- self.active_array.interpolate_on_triangulation(self.active_tri.pts, self.active_tri.tri, )
4437
+ self.active_array.interpolate_on_triangulation(self.active_tri.pts, self.active_tri.tri, keep=keep)
4404
4438
 
4405
4439
  def compare_cloud2array(self):
4406
4440
  """
@@ -8429,7 +8463,13 @@ class WolfMapViewer(wx.Frame):
8429
8463
  # self.import_3dfaces()
8430
8464
 
8431
8465
  elif itemlabel == _("Interpolate on active triangulation..."):
8432
- self.interpolate_triangulation()
8466
+ self.interpolate_triangulation(keep='all')
8467
+
8468
+ elif itemlabel == _("Interpolate on active triangulation (keep only above)..."):
8469
+ self.interpolate_triangulation(keep='above')
8470
+
8471
+ elif itemlabel == _("Interpolate on active triangulation (keep only below)..."):
8472
+ self.interpolate_triangulation(keep='below')
8433
8473
 
8434
8474
  elif itemlabel==_("Compare cloud to array..."):
8435
8475
  self.compare_cloud2array()
@@ -11565,6 +11605,31 @@ class WolfMapViewer(wx.Frame):
11565
11605
 
11566
11606
  dlg.Destroy()
11567
11607
 
11608
+ elif _('Reload') in text:
11609
+
11610
+ if isinstance(self.selected_object, WolfArray):
11611
+ if self.selected_object.filename is not None:
11612
+
11613
+ dlg = wx.MessageDialog(None, _('Do you want to reload the file ?'), _('Reload'), wx.YES_NO | wx.NO_DEFAULT)
11614
+ ret = dlg.ShowModal()
11615
+ if ret == wx.ID_YES:
11616
+ self.selected_object.read_all()
11617
+ self.selected_object.mask_data(self.selected_object.nullvalue)
11618
+ self.selected_object.reset_plot()
11619
+
11620
+ dlg.Destroy()
11621
+ # elif isinstance(self.selected_object, Zones):
11622
+ # if self.selected_object.filename is not None:
11623
+ # dlg = wx.MessageDialog(None, _('Do you want to reload the file ?'), _('Reload'), wx.YES_NO | wx.NO_DEFAULT)
11624
+ # ret = dlg.ShowModal()
11625
+ # if ret == wx.ID_YES:
11626
+ # self.selected_object.read()
11627
+
11628
+ # dlg.Destroy()
11629
+
11630
+ else:
11631
+ logging.warning(_('Reload not yet implemented for this type of object'))
11632
+
11568
11633
  def OnClose(self, event):
11569
11634
  """ Close the application """
11570
11635
 
@@ -13144,7 +13209,7 @@ class WolfMapViewer(wx.Frame):
13144
13209
  width, height = self.canvas.GetSize()
13145
13210
 
13146
13211
  if iwidth == 0 or iheight == 0:
13147
- logging.error(_('Width or height of the canvas is null -- Please check the "findminmax" routine in "Autoscale" !'))
13212
+ logging.warning(_('Width or height of the canvas is null -- Please check the "findminmax" routine in "Autoscale" !'))
13148
13213
  iwidth = 1
13149
13214
  iheight = 1
13150
13215
 
wolfhece/PyGui.py CHANGED
@@ -2915,6 +2915,12 @@ class Wolf2DGPUModel(GenMapManager):
2915
2915
  if force_reload:
2916
2916
  self._sim.reload_all()
2917
2917
  for cur in self.arrays.values():
2918
+
2919
+ if cur.idx == 'water surface elevation [m]':
2920
+ ## Force to recompute the water surface elevation
2921
+ cur.array.data[:,:] = self.arrays['bathymetry'].array.data[:,:] + self.arrays['h'].array.data[:,:]
2922
+ cur.array.mask[:,:] = self.arrays['bathymetry'].array.mask[:,:]
2923
+
2918
2924
  cur.reset_plot()
2919
2925
  # self.mapviewer.Refresh()
2920
2926
  else:
@@ -27,6 +27,7 @@ from matplotlib.axes import Axes
27
27
  from matplotlib.figure import Figure
28
28
  from matplotlib import cm
29
29
  from matplotlib.colors import Colormap
30
+ from matplotlib.tri import Triangulation as mpl_tri
30
31
 
31
32
  import struct
32
33
  import pyvista as pv
@@ -414,6 +415,22 @@ class Triangulation(Element_To_Draw):
414
415
  else:
415
416
  logging.warning('No triangles to plot')
416
417
 
418
+ @property
419
+ def mpl_triangulation(self) -> mpl_tri:
420
+ """ Return the triangulation as a Matplotlib Triangulation object """
421
+ if self.nb_tri>0:
422
+ return mpl_tri(self.pts[:,0], self.pts[:,1], self.tri)
423
+ else:
424
+ logging.warning('No triangles to plot')
425
+ return None
426
+
427
+ def plot_matplotlib_3D(self, ax:Axes, color='black', alpha=0.2, lw=1.5, edgecolor='k', shade=True, **kwargs):
428
+ """ Plot the triangulation in Matplotlib 3D
429
+ """
430
+ if self.nb_tri>0:
431
+ ax.plot_trisurf(self.mpl_triangulation, Z=self.pts[:,2], color=color, alpha=alpha, lw=lw, edgecolor=edgecolor, shade=shade, **kwargs)
432
+ else:
433
+ logging.warning('No triangles to plot')
417
434
 
418
435
  def find_minmax(self,force):
419
436
  """ Find the min and max of the triangulation
@@ -1107,9 +1124,9 @@ if :\n \
1107
1124
  self.myprops[('Move','Delta Y')] = 0.
1108
1125
 
1109
1126
  self.parent.update_lengths()
1110
- self.myprops[( 'Geometry','Length 2D')] = self.parent.length2D
1111
- self.myprops[( 'Geometry','Length 3D')] = self.parent.length3D
1112
- self.myprops[( 'Geometry','Surface')] = self.parent.area
1127
+ self.myprops[( 'Geometry','Length 2D')] = self.parent.length2D if self.parent.length2D is not None else 0.
1128
+ self.myprops[( 'Geometry','Length 3D')] = self.parent.length3D if self.parent.length3D is not None else 0.
1129
+ self.myprops[( 'Geometry','Surface')] = self.parent.area if self.parent.area is not None else 0.
1113
1130
 
1114
1131
  self.myprops.Populate()
1115
1132
  class vector:
@@ -1748,12 +1765,11 @@ class vector:
1748
1765
  prepare(self._polygon)
1749
1766
 
1750
1767
 
1751
- def projectontrace(self, trace):
1768
+ def projectontrace(self, trace:"vector"):
1752
1769
  """
1753
1770
  Projection du vecteur sur une trace (type 'vector')
1754
1771
 
1755
- Return :
1756
- Nouveau vecteur contenant les infos de position sur la trace et d'altitude (s,z)
1772
+ :return: Nouveau vecteur contenant les infos de position sur la trace et d'altitude (s,z) aux positions (x,y)
1757
1773
  """
1758
1774
 
1759
1775
  # trace:vector
@@ -1770,11 +1786,12 @@ class vector:
1770
1786
 
1771
1787
  return newvec
1772
1788
 
1773
- def parallel_offset(self, distance=5., side='left'):
1789
+ def parallel_offset(self, distance=5., side:Literal['left', 'right']='left'):
1774
1790
  """
1775
- The distance parameter must be a positive float value.
1791
+ Create a parallel offset of the vector
1776
1792
 
1777
- The side parameter may be ‘left’ or ‘right’. Left and right are determined by following the direction of the given geometric points of the LineString.
1793
+ :param distance: The distance parameter must be a positive float value.
1794
+ :param side: The side parameter may be ‘left’ or ‘right’. Left and right are determined by following the direction of the given geometric points of the LineString.
1778
1795
  """
1779
1796
 
1780
1797
  if self.nbvertices<2:
@@ -1883,6 +1900,9 @@ class vector:
1883
1900
  """
1884
1901
  Ajout de vertices depuis une matrice numpy -- shape = (nb_vert,2 ou 3)
1885
1902
  """
1903
+
1904
+ assert isinstance(xyz, np.ndarray), "xyz must be a numpy array of shape (nb_vert, 2 or 3)"
1905
+
1886
1906
  if xyz.dtype==np.int32:
1887
1907
  xyz = xyz.astype(np.float64)
1888
1908
 
@@ -2764,6 +2784,7 @@ class vector:
2764
2784
  exit=False
2765
2785
 
2766
2786
  if exit:
2787
+ logging.warning(_('No plotted linked arrays'))
2767
2788
  return
2768
2789
 
2769
2790
  k=0
@@ -3088,6 +3109,7 @@ class vector:
3088
3109
  curvert.x = newx
3089
3110
 
3090
3111
  self._reset_listogl()
3112
+ self.reset_linestring()
3091
3113
 
3092
3114
  @y.setter
3093
3115
  def y(self, new_y:np.ndarray | list):
@@ -3104,13 +3126,26 @@ class vector:
3104
3126
  curvert.y = newy
3105
3127
 
3106
3128
  self._reset_listogl()
3129
+ self.reset_linestring()
3107
3130
 
3108
3131
  @z.setter
3109
3132
  def z(self, new_z:np.ndarray | float | list):
3110
- """ Set the z values of the vertices """
3133
+ """ Set the z values of the vertices
3134
+
3135
+ /PARAM new_z: numpy array, float or list (but WolfArray is supported too)
3136
+ """
3137
+ from .wolf_array import WolfArray
3111
3138
 
3112
3139
  if isinstance(new_z, (int, float)):
3113
3140
  new_z = np.full(self.nbvertices, new_z, dtype=float)
3141
+ elif isinstance(new_z, WolfArray):
3142
+ wa = new_z
3143
+
3144
+ new_z = []
3145
+ for curvert in self.myvertices:
3146
+ i,j = wa.xy2ij(curvert.x, curvert.y)
3147
+ if i>0 and j>0 and i < wa.nbx and j < wa.nby:
3148
+ new_z.append(wa.array[i, j])
3114
3149
 
3115
3150
  if isinstance(new_z, list):
3116
3151
  new_z = np.array(new_z)
@@ -3127,6 +3162,7 @@ class vector:
3127
3162
  curvert.z = newz
3128
3163
 
3129
3164
  self._reset_listogl()
3165
+ self.reset_linestring()
3130
3166
 
3131
3167
  @xyz.setter
3132
3168
  def xyz(self, new_xyz:np.ndarray | list):
@@ -3151,6 +3187,8 @@ class vector:
3151
3187
  curvert.z = newxyz[2]
3152
3188
 
3153
3189
  self._reset_listogl()
3190
+ self.reset_linestring()
3191
+
3154
3192
 
3155
3193
  @xy.setter
3156
3194
  def xy(self, new_xy:np.ndarray | list):
@@ -3168,6 +3206,7 @@ class vector:
3168
3206
  curvert.y = newxy[1]
3169
3207
 
3170
3208
  self._reset_listogl()
3209
+ self.reset_linestring()
3171
3210
 
3172
3211
  @xz.setter
3173
3212
  def xz(self, new_xz:np.ndarray | list):
@@ -3190,6 +3229,7 @@ class vector:
3190
3229
  curvert.z = newxz[1]
3191
3230
 
3192
3231
  self._reset_listogl()
3232
+ self.reset_linestring()
3193
3233
 
3194
3234
  @xyzi.setter
3195
3235
  def xyzi(self, new_xyzi:np.ndarray | list):
@@ -3209,6 +3249,7 @@ class vector:
3209
3249
  curvert.in_use = newxyzi[3]
3210
3250
 
3211
3251
  self._reset_listogl()
3252
+ self.reset_linestring()
3212
3253
 
3213
3254
  @xyi.setter
3214
3255
  def xyi(self, new_xyi:np.ndarray | list):
@@ -3227,6 +3268,7 @@ class vector:
3227
3268
  curvert.in_use = newxyi[2]
3228
3269
 
3229
3270
  self._reset_listogl()
3271
+ self.reset_linestring()
3230
3272
 
3231
3273
  @i.setter
3232
3274
  def i(self, new_i:np.ndarray | list):
@@ -3243,6 +3285,23 @@ class vector:
3243
3285
  curvert.in_use = newi
3244
3286
 
3245
3287
  self._reset_listogl()
3288
+ self.reset_linestring()
3289
+
3290
+ @sz_curvi.setter
3291
+ def sz_curvi(self, sz_new:np.ndarray | list):
3292
+ """ Interpolate the vertices based on the curvilinear abscissa """
3293
+
3294
+ if isinstance(sz_new, list):
3295
+ sz_new = np.array(sz_new)
3296
+
3297
+ f = interp1d(sz_new[:,0],sz_new[:,1], bounds_error=False, fill_value='extrapolate')
3298
+
3299
+ s = self.s_curvi
3300
+ for idx, curvert in enumerate(self.myvertices):
3301
+ curvert.z = f(s[idx])
3302
+
3303
+ self._reset_listogl()
3304
+ self.reset_linestring()
3246
3305
 
3247
3306
  def __str__(self):
3248
3307
  return self.myname
@@ -3265,6 +3324,7 @@ class vector:
3265
3324
  if ndx>=0 and ndx < self.nbvertices:
3266
3325
  self.myvertices[ndx] = value
3267
3326
  self._reset_listogl()
3327
+ self.reset_linestring()
3268
3328
  else:
3269
3329
  logging.warning(_('Index out of range'))
3270
3330
 
@@ -3273,6 +3333,7 @@ class vector:
3273
3333
  if ndx>=0 and ndx < self.nbvertices:
3274
3334
  self.myvertices.pop(ndx)
3275
3335
  self._reset_listogl()
3336
+ self.reset_linestring()
3276
3337
  else:
3277
3338
  logging.warning(_('Index out of range'))
3278
3339
 
@@ -3290,6 +3351,7 @@ class vector:
3290
3351
 
3291
3352
  self.update_lengths()
3292
3353
  self._reset_listogl()
3354
+ self.reset_linestring()
3293
3355
 
3294
3356
  def cut(self, s:float, is3D:bool=True, adim:bool=True, frombegin:bool=True):
3295
3357
  """
@@ -3312,6 +3374,7 @@ class vector:
3312
3374
  newvec.update_lengths()
3313
3375
 
3314
3376
  self._reset_listogl()
3377
+ self.reset_linestring()
3315
3378
 
3316
3379
  return newvec
3317
3380
 
@@ -6333,7 +6396,7 @@ class Zones(wx.Frame, Element_To_Draw):
6333
6396
  """
6334
6397
  Retourne la zone sur base de son nom ou de sa position
6335
6398
 
6336
- :param ndx: Clé ou index de zone -- si tuple, alors (idx_zone, idx_vect)
6399
+ :param ndx: Clé ou index de zone -- si tuple, alors (idx_zone, idx_vect) ou (keyzone, keyvect)
6337
6400
 
6338
6401
  """
6339
6402
 
@@ -6698,16 +6761,26 @@ class Zones(wx.Frame, Element_To_Draw):
6698
6761
  self.xls.CreateGrid(10,6)
6699
6762
 
6700
6763
  sizer_add_update = BoxSizer(orient=wx.HORIZONTAL)
6701
- self.addrows = wx.Button(self,label=_('Add rows'))
6764
+ self.addrows = wx.Button(self,label=_('Rows+'))
6702
6765
  self.addrows.SetToolTip(_("Add rows to the grid --> Useful for manually adding some points to a vector"))
6703
6766
  self.addrows.Bind(wx.EVT_BUTTON,self.Onaddrows)
6704
6767
 
6705
- self.updatevertices = wx.Button(self,label=_('Update coordinates'))
6768
+ self.updatevertices = wx.Button(self,label=_('Update'))
6706
6769
  self.updatevertices.SetToolTip(_("Transfer the coordinates from the editor to the memory and update the plot"))
6707
6770
  self.updatevertices.Bind(wx.EVT_BUTTON,self.Onupdatevertices)
6708
6771
 
6772
+ self.plot_mpl = wx.Button(self,label=_('Plot xy'))
6773
+ self.plot_mpl.SetToolTip(_("Plot the active vector in a new window (matplotlib)"))
6774
+ self.plot_mpl.Bind(wx.EVT_BUTTON,self.Onplotmpl)
6775
+
6776
+ self.plot_mplsz = wx.Button(self,label=_('Plot sz'))
6777
+ self.plot_mplsz.SetToolTip(_("Plot the active vector in a new window (matplotlib)"))
6778
+ self.plot_mplsz.Bind(wx.EVT_BUTTON,self.Onplotmplsz)
6779
+
6709
6780
  sizer_add_update.Add(self.addrows,1, wx.EXPAND)
6710
6781
  sizer_add_update.Add(self.updatevertices,3, wx.EXPAND)
6782
+ sizer_add_update.Add(self.plot_mpl,1, wx.EXPAND)
6783
+ sizer_add_update.Add(self.plot_mplsz,1, wx.EXPAND)
6711
6784
 
6712
6785
  self.capturevertices = wx.Button(self,label=_('Add'))
6713
6786
  self.capturevertices.SetToolTip(_("Capture new points from mouse clicks \n\n Keyboard 'Return' to stop the action ! "))
@@ -6725,6 +6798,10 @@ class Zones(wx.Frame, Element_To_Draw):
6725
6798
  self.createapar.SetToolTip(_("Create a single parallel to the currently activated vector as a new vector in the same zone"))
6726
6799
  self.createapar.Bind(wx.EVT_BUTTON,self.OnAddPar)
6727
6800
 
6801
+ self._btn_simplify = wx.Button(self,label=_('Simplify'))
6802
+ self._btn_simplify.SetToolTip(_("Simplify the currently activated vector using the Douglas-Peucker algorithm"))
6803
+ self._btn_simplify.Bind(wx.EVT_BUTTON,self.Onsimplify)
6804
+
6728
6805
  sizer_reverse_split = BoxSizer(orient=wx.HORIZONTAL)
6729
6806
  self.reverseorder = wx.Button(self,label=_('Reverse points order'))
6730
6807
  self.reverseorder.SetToolTip(_("Reverse the order/sens of the currently activated vector -- Overwrite the data"))
@@ -6734,6 +6811,10 @@ class Zones(wx.Frame, Element_To_Draw):
6734
6811
  self.sascending.SetToolTip(_("Check whether the vertices of the activated vector are ordered according to increasing 's' defined as 2D geometric distance \n If needed, invert some positions and return information to the user"))
6735
6812
  self.sascending.Bind(wx.EVT_BUTTON,self.Onsascending)
6736
6813
 
6814
+ self._btn_buffer = wx.Button(self,label=_('Buffer'))
6815
+ self._btn_buffer.SetToolTip(_("Create a buffer around the currently activated vector\nThe buffer replaces the current vector"))
6816
+ self._btn_buffer.Bind(wx.EVT_BUTTON,self.Onbuffer)
6817
+
6737
6818
  self.insertvertices = wx.Button(self,label=_('Insert'))
6738
6819
  self.insertvertices.SetToolTip(_("Insert new vertex into the currently active vector from mouse clicks \n The new vertex is inserted along the nearest segment \n\n Keyboard 'Return' to stop the action ! "))
6739
6820
  self.insertvertices.Bind(wx.EVT_BUTTON,self.Oninsert)
@@ -6830,7 +6911,11 @@ class Zones(wx.Frame, Element_To_Draw):
6830
6911
  subboxmod.Add(self.insertvertices,1,wx.EXPAND)
6831
6912
  boxright.Add(subboxmod,0,wx.EXPAND)
6832
6913
 
6833
- boxright.Add(self.createapar,0,wx.EXPAND)
6914
+ subboxparsimpl = wx.BoxSizer(orient=wx.HORIZONTAL)
6915
+ subboxparsimpl.Add(self.createapar,1,wx.EXPAND)
6916
+ subboxparsimpl.Add(self._btn_simplify,1,wx.EXPAND)
6917
+
6918
+ boxright.Add(subboxparsimpl,0,wx.EXPAND)
6834
6919
 
6835
6920
  sizer_reverse_split.Add(self.reverseorder,1,wx.EXPAND)
6836
6921
  sizer_reverse_split.Add(self.splitvertices,1,wx.EXPAND)
@@ -6848,8 +6933,11 @@ class Zones(wx.Frame, Element_To_Draw):
6848
6933
  box_s.Add(self.getxyfromsz,1,wx.EXPAND) # Added
6849
6934
 
6850
6935
  boxright.Add(self.interpxyz,0,wx.EXPAND)
6851
- boxright.Add(self.sascending,0,wx.EXPAND)
6852
6936
 
6937
+ _sizer_ascbuffer = wx.BoxSizer(wx.HORIZONTAL)
6938
+ _sizer_ascbuffer.Add(self.sascending,1,wx.EXPAND)
6939
+ _sizer_ascbuffer.Add(self._btn_buffer,1,wx.EXPAND)
6940
+ boxright.Add(_sizer_ascbuffer,0,wx.EXPAND)
6853
6941
 
6854
6942
  sizer_values_surface = wx.BoxSizer(wx.HORIZONTAL)
6855
6943
  self.butgetval = wx.Button(self,label=_('Get values'))
@@ -7256,6 +7344,46 @@ class Zones(wx.Frame, Element_To_Draw):
7256
7344
  self.fill_structure()
7257
7345
  self.active_vector._reset_listogl()
7258
7346
 
7347
+ def Onsimplify(self, event:wx.MouseEvent):
7348
+ """
7349
+ Simplify the active vector using the Douglas-Peucker algorithm
7350
+ """
7351
+
7352
+ if self.verify_activevec():
7353
+ return
7354
+
7355
+ tolerance = 1.0
7356
+ if self.wx_exists:
7357
+
7358
+ dlg = wx.TextEntryDialog(None, _('Tolerance ?'), value='1.0')
7359
+ ret = dlg.ShowModal()
7360
+ tolerance = dlg.GetValue()
7361
+ dlg.Destroy()
7362
+ try:
7363
+ tolerance = float(tolerance)
7364
+ except:
7365
+ logging.warning(_('Bad value -- Retry !'))
7366
+ return
7367
+
7368
+ new_ls = self.active_vector.linestring.simplify(tolerance, preserve_topology=True)
7369
+
7370
+ xy = new_ls.xy # shape (2, n)
7371
+ xy = np.array(xy).T # shape (n, 2)
7372
+
7373
+ if len(xy) == 0:
7374
+ logging.warning(_('No points to add'))
7375
+ return
7376
+
7377
+ tmp = self.active_vector.deepcopy().linestring
7378
+ self.active_vector.reset()
7379
+ for x, y in xy:
7380
+ pt = Point(x, y)
7381
+ self.active_vector.add_vertex(wolfvertex(x, y, tmp.interpolate(tmp.project(pt)).z))
7382
+
7383
+ self.xls_active_vector()
7384
+ self.active_vector._reset_listogl()
7385
+
7386
+
7259
7387
  def OnAddPar(self, event:wx.MouseEvent):
7260
7388
  """
7261
7389
  Ajout d'une parallèle au vecteur courant via le bouton adhoc
@@ -7370,6 +7498,37 @@ class Zones(wx.Frame, Element_To_Draw):
7370
7498
  dlg.ShowModal()
7371
7499
  dlg.Destroy()
7372
7500
 
7501
+ def Onbuffer(self, e:wx.MouseEvent):
7502
+ """ Create a buffer around the currently activated vector.
7503
+ The buffer replaces the active vector in the same zone."""
7504
+
7505
+ if self.wx_exists:
7506
+ if self.verify_activevec():
7507
+ return
7508
+
7509
+ dlg = wx.TextEntryDialog(None, _('Buffer distance ?'), value='5.0')
7510
+ ret = dlg.ShowModal()
7511
+ dist = dlg.GetValue()
7512
+ dlg.Destroy()
7513
+ try:
7514
+ dist = float(dist)
7515
+ except:
7516
+ logging.warning(_('Bad value -- Retry !'))
7517
+ return
7518
+ if dist <= 0:
7519
+ logging.warning(_('Buffer distance must be > 0 -- Retry !'))
7520
+ return
7521
+
7522
+ if self.active_vector.nbvertices == 1:
7523
+ self.active_vector.myvertices = self.active_vector.myvertices * 3
7524
+ logging.warning(_('The active vector has only one vertex. It will be duplicated to create a buffer.'))
7525
+ if self.active_vector.nbvertices == 2:
7526
+ self.active_vector.myvertices = self.active_vector.myvertices + [self.active_vector.myvertices[0]]
7527
+ logging.warning(_('The active vector has only two vertices. The first one will be duplicated to create a buffer.'))
7528
+
7529
+ self.active_vector.buffer(dist)
7530
+ self.active_vector._reset_listogl()
7531
+
7373
7532
  def Onmodify(self, event:wx.MouseEvent):
7374
7533
  """
7375
7534
  Permet la modification interactive de vertex dans le vector actif
@@ -7941,6 +8100,9 @@ class Zones(wx.Frame, Element_To_Draw):
7941
8100
  # Getting s values and Z values from the xls grid
7942
8101
  # s in column 4 and z in column 2
7943
8102
  # The first row is the header
8103
+ nbrows = self.xls.GetNumberRows()
8104
+ if self.xls.GetCellValue(nbrows-1,4) != '':
8105
+ self.xls.AppendRows(1)
7944
8106
  i = 0
7945
8107
  while self.xls.GetCellValue(i,4) != '':
7946
8108
  s = self.xls.GetCellValue(i,4)
@@ -8024,7 +8186,21 @@ class Zones(wx.Frame, Element_To_Draw):
8024
8186
 
8025
8187
  self.find_minmax(True)
8026
8188
 
8027
- def update_from_sz_support(self, vec: vector, sz:np.ndarray, dialog_box = True):
8189
+ def update_from_sz_support(self,
8190
+ vec: vector,
8191
+ sz:np.ndarray,
8192
+ dialog_box = True,
8193
+ method:Literal['2D', '3D'] = '3D'):
8194
+ """ Update the coordinates from the support vector and a sz array.
8195
+
8196
+ The support vector is used to interpolate the z values, at the s values.
8197
+ It must long enough to cover the s values.
8198
+
8199
+ :param vec: The vector to update. It is also the support vector.
8200
+ :param sz: The sz array to use for the update
8201
+ :param dialog_box: If True, a dialog box will be shown to choose the method
8202
+ :param method: The method to use for the interpolation. '2D' or '3D'
8203
+ """
8028
8204
 
8029
8205
  if sz.shape[0] ==0:
8030
8206
  logging.warning(_('No data to update'))
@@ -8046,8 +8222,12 @@ class Zones(wx.Frame, Element_To_Draw):
8046
8222
 
8047
8223
  method=dlg.GetStringSelection()
8048
8224
  dlg.Destroy()
8049
- else:
8050
- method = '2D'
8225
+ # else:
8226
+ # method = '2D'
8227
+
8228
+ if method not in ['2D', '3D']:
8229
+ logging.warning(_('Method not supported -- only 2D and 3D are supported'))
8230
+ return
8051
8231
 
8052
8232
  if method == '2D':
8053
8233
  if sz[-1,0] > support_vec.length2D:
@@ -8060,18 +8240,21 @@ class Zones(wx.Frame, Element_To_Draw):
8060
8240
 
8061
8241
  vec.myvertices = []
8062
8242
  for s,z in sz:
8063
- new_vertex = support_vec.interpolate(s, method == '3D', adim= False)
8243
+ new_vertex = support_vec.interpolate(s, method == method, adim= False)
8064
8244
  new_vertex.z = z
8065
8245
  vec.add_vertex(new_vertex)
8066
8246
 
8247
+ vec._reset_listogl()
8248
+ vec.update_lengths()
8249
+
8067
8250
  self.find_minmax(True)
8068
8251
 
8069
- def evaluate_s (self, vec: vector =None, dialog_box = True):
8252
+ def evaluate_s(self, vec: vector =None, dialog_box = True):
8070
8253
  """
8071
- Calcule la position curviligne du vecteur encodé
8254
+ Calcule la position curviligne du vecteur encodé.
8072
8255
 
8073
- Le calcul peut être mené en 2D ou en 3D
8074
- Remplissage du tableur dans la 5ème colonne
8256
+ Le calcul peut être mené en 2D ou en 3D.
8257
+ Remplissage du tableur dans la 5ème colonne.
8075
8258
  """
8076
8259
 
8077
8260
  curv = vec
@@ -8143,6 +8326,33 @@ class Zones(wx.Frame, Element_To_Draw):
8143
8326
  self.active_vector.updatefromgrid(self.xls)
8144
8327
  self.find_minmax(True)
8145
8328
 
8329
+ def Onplotmpl(self, event:wx.MouseEvent):
8330
+ """
8331
+ Plot active vector in matplotlib
8332
+ """
8333
+
8334
+ if self.verify_activevec():
8335
+ return
8336
+
8337
+ fig, ax = plt.subplots()
8338
+ self.active_vector.plot_matplotlib(ax)
8339
+ ax.set_aspect('equal')
8340
+ fig.show()
8341
+
8342
+ def Onplotmplsz(self, event:wx.MouseEvent):
8343
+ """
8344
+ Plot active vector in matplotlib with sz values
8345
+ """
8346
+ if self.verify_activevec():
8347
+ return
8348
+
8349
+ fig, ax = plt.subplots()
8350
+ s, z = self.active_vector.sz_curvi
8351
+ ax.plot(s, z)
8352
+ ax.set_xlabel('s')
8353
+ ax.set_ylabel('z')
8354
+ fig.show()
8355
+
8146
8356
  def Onaddrows(self, event:wx.MouseEvent):
8147
8357
  """
8148
8358
  Ajout de lignes au tableur