wolfhece 2.2.8__py3-none-any.whl → 2.2.10__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.
Files changed (33) hide show
  1. wolfhece/PyDraw.py +94 -24
  2. wolfhece/PyGui.py +1 -0
  3. wolfhece/PyVertex.py +127 -19
  4. wolfhece/PyVertexvectors.py +73 -21
  5. wolfhece/__init__.py +5 -2
  6. wolfhece/apps/version.py +1 -1
  7. wolfhece/hydrology/Internal_variables.py +283 -0
  8. wolfhece/hydrology/Models_characteristics.py +223 -0
  9. wolfhece/hydrology/Optimisation.py +324 -14
  10. wolfhece/hydrology/SubBasin.py +112 -28
  11. wolfhece/hydrology/cst_exchanges.py +1 -0
  12. wolfhece/hydrometry/kiwis.py +8 -3
  13. wolfhece/lagrangian/particle_system_ui.py +1 -1
  14. wolfhece/lazviewer/processing/estimate_normals/estimate_normals.cp311-win_amd64.pyd +0 -0
  15. wolfhece/lazviewer/vfuncsdir/vfuncs.cp311-win_amd64.pyd +0 -0
  16. wolfhece/lazviewer/viewer/viewer.exe +0 -0
  17. wolfhece/lazviewer/viewer/viewer_310.exe +0 -0
  18. wolfhece/libs/WolfDll.dll +0 -0
  19. wolfhece/libs/get_infos.cp311-win_amd64.pyd +0 -0
  20. wolfhece/libs/verify_wolf.cp311-win_amd64.pyd +0 -0
  21. wolfhece/libs/wolfogl.cp311-win_amd64.pyd +0 -0
  22. wolfhece/pydike.py +1 -1
  23. wolfhece/pyviews.py +1 -1
  24. wolfhece/wolf_array.py +28 -6
  25. wolfhece-2.2.10.dist-info/METADATA +90 -0
  26. {wolfhece-2.2.8.dist-info → wolfhece-2.2.10.dist-info}/RECORD +33 -21
  27. {wolfhece-2.2.8.dist-info → wolfhece-2.2.10.dist-info}/WHEEL +1 -1
  28. {wolfhece-2.2.8.dist-info → wolfhece-2.3.0.dist-info}/METADATA +3 -3
  29. wolfhece-2.3.0.dist-info/WHEEL +5 -0
  30. wolfhece-2.3.0.dist-info/entry_points.txt +17 -0
  31. wolfhece-2.3.0.dist-info/top_level.txt +1 -0
  32. {wolfhece-2.2.8.dist-info → wolfhece-2.2.10.dist-info}/entry_points.txt +0 -0
  33. {wolfhece-2.2.8.dist-info → wolfhece-2.2.10.dist-info}/top_level.txt +0 -0
wolfhece/PyDraw.py CHANGED
@@ -2008,6 +2008,8 @@ class WolfMapViewer(wx.Frame):
2008
2008
  self.treewidth = treewidth
2009
2009
  super(WolfMapViewer, self).__init__(wxparent, title=title, size=(w + self.treewidth, h))
2010
2010
 
2011
+ self._tmp_vector_distance = None # distance computation the vector
2012
+
2011
2013
  self._wxlogging = wxlogging
2012
2014
  self.action = None # Action à entreprendre
2013
2015
  self.update_absolute_minmax = False # Force la MAJ de la palette
@@ -4086,7 +4088,7 @@ class WolfMapViewer(wx.Frame):
4086
4088
  if ret == wx.ID_NO:
4087
4089
  return
4088
4090
 
4089
- with wx.lib.busy.BusyInfo(_('Updating 2D model')):
4091
+ with wx.BusyInfo(_('Updating 2D model')):
4090
4092
  wait = wx.BusyCursor()
4091
4093
 
4092
4094
  sux,suy,cont,interior = self.active_array.suxsuy_contour(self.wolfparent.filenamegen,True)
@@ -4305,7 +4307,7 @@ class WolfMapViewer(wx.Frame):
4305
4307
  if self.active_cloud is not None and self.active_array is not None:
4306
4308
 
4307
4309
  keyvalue='z'
4308
- if self.active_cloud.header:
4310
+ if self.active_cloud.has_values:
4309
4311
  choices = list(self.active_cloud.myvertices[0].keys())
4310
4312
  dlg = wx.SingleChoiceDialog(None, "Pick the value to interpolate", "Choices", choices)
4311
4313
  ret = dlg.ShowModal()
@@ -5734,12 +5736,12 @@ class WolfMapViewer(wx.Frame):
5734
5736
  fnpos = self.link_params['gltf pos']
5735
5737
  break
5736
5738
 
5737
- with wx.lib.busy.BusyInfo(_('Importing gltf/glb')):
5739
+ with wx.BusyInfo(_('Importing gltf/glb')):
5738
5740
  wait = wx.BusyCursor()
5739
5741
  sourcenew.import_from_gltf(fn, fnpos, 'scipy')
5740
5742
  del wait
5741
5743
 
5742
- with wx.lib.busy.BusyInfo(_('Update plots')):
5744
+ with wx.BusyInfo(_('Update plots')):
5743
5745
  # Création du différentiel -- Les opérateurs mathématiques sont surchargés
5744
5746
  diff.array = (sourcenew - source).array
5745
5747
  grad.array = sourcenew.get_gradient_norm().array
@@ -8986,7 +8988,7 @@ class WolfMapViewer(wx.Frame):
8986
8988
  fn = dlg.GetPath()
8987
8989
  dlg.Destroy()
8988
8990
 
8989
- with wx.lib.busy.BusyInfo(_('Export to gltf/glb')):
8991
+ with wx.BusyInfo(_('Export to gltf/glb')):
8990
8992
  wait = wx.BusyCursor()
8991
8993
  curarray.export_to_gltf(mybounds, fn)
8992
8994
  del wait
@@ -9037,7 +9039,7 @@ class WolfMapViewer(wx.Frame):
9037
9039
  method = dlg.GetStringSelection()
9038
9040
  dlg.Destroy()
9039
9041
 
9040
- with wx.lib.busy.BusyInfo(_('Importing gltf/glb')):
9042
+ with wx.BusyInfo(_('Importing gltf/glb')):
9041
9043
  wait = wx.BusyCursor()
9042
9044
  try:
9043
9045
  curarray.import_from_gltf(fn, fnpos, method)
@@ -9199,7 +9201,7 @@ class WolfMapViewer(wx.Frame):
9199
9201
  dlg.Destroy()
9200
9202
  return
9201
9203
 
9202
- with wx.lib.busy.BusyInfo(_('Loading masks')):
9204
+ with wx.BusyInfo(_('Loading masks')):
9203
9205
  wait = wx.BusyCursor()
9204
9206
  curarray:WolfArray
9205
9207
  for curarray in self.myarrays:
@@ -9830,7 +9832,7 @@ class WolfMapViewer(wx.Frame):
9830
9832
  testobj.read_txt_header()
9831
9833
 
9832
9834
  if testobj.wolftype in WOLF_ARRAY_MB:
9833
- # with wx.lib.busy.BusyInfo(_('Importing array')):
9835
+ # with wx.BusyInfo(_('Importing array')):
9834
9836
  # wait = wx.BusyCursor()
9835
9837
  # newobj = WolfArrayMB(filename, mapviewer=self)
9836
9838
  # del wait
@@ -9839,7 +9841,7 @@ class WolfMapViewer(wx.Frame):
9839
9841
  if which.lower() == 'array_crop':
9840
9842
  newobj = WolfArray(filename, mapviewer=self, crop='newcrop')
9841
9843
  else:
9842
- # with wx.lib.busy.BusyInfo(_('Importing array')):
9844
+ # with wx.BusyInfo(_('Importing array')):
9843
9845
  # wait = wx.BusyCursor()
9844
9846
  # newobj = WolfArray(filename, mapviewer=self)
9845
9847
  # del wait
@@ -9945,7 +9947,7 @@ class WolfMapViewer(wx.Frame):
9945
9947
  curtree = self.myitemsvector
9946
9948
 
9947
9949
  if newobj is None:
9948
- with wx.lib.busy.BusyInfo(_('Importing files')):
9950
+ with wx.BusyInfo(_('Importing files')):
9949
9951
  wait = wx.BusyCursor()
9950
9952
  newobj = Bridges(filename, mapviewer=self)
9951
9953
  del wait
@@ -9961,7 +9963,7 @@ class WolfMapViewer(wx.Frame):
9961
9963
  curtree = self.myitemsvector
9962
9964
 
9963
9965
  if newobj is None:
9964
- with wx.lib.busy.BusyInfo(_('Importing files')):
9966
+ with wx.BusyInfo(_('Importing files')):
9965
9967
  wait = wx.BusyCursor()
9966
9968
  newobj = Weirs(filename, mapviewer=self)
9967
9969
  del wait
@@ -9996,7 +9998,7 @@ class WolfMapViewer(wx.Frame):
9996
9998
  dirname_comp = file.GetPath()
9997
9999
  file.Destroy()
9998
10000
 
9999
- with wx.lib.busy.BusyInfo(_('Importing files')):
10001
+ with wx.BusyInfo(_('Importing files')):
10000
10002
  wait = wx.BusyCursor()
10001
10003
  newobj = Tiles(filename, parent=self, linked_data_dir=dirname, mapviewer=self)
10002
10004
  del wait
@@ -10152,7 +10154,7 @@ class WolfMapViewer(wx.Frame):
10152
10154
  curtree = self.myitemsres2d
10153
10155
 
10154
10156
  if newobj is None:
10155
- with wx.lib.busy.BusyInfo(_('Importing 2D model')):
10157
+ with wx.BusyInfo(_('Importing 2D model')):
10156
10158
  wait = wx.BusyCursor()
10157
10159
  newobj = Wolfresults_2D(filename, mapviewer=self)
10158
10160
  del wait
@@ -10168,7 +10170,7 @@ class WolfMapViewer(wx.Frame):
10168
10170
  curtree = self.myitemsres2d
10169
10171
 
10170
10172
  if newobj is None:
10171
- with wx.lib.busy.BusyInfo(_('Importing 2D GPU model')):
10173
+ with wx.BusyInfo(_('Importing 2D GPU model')):
10172
10174
  wait = wx.BusyCursor()
10173
10175
  newobj = wolfres2DGPU(filename, mapviewer=self)
10174
10176
  del wait
@@ -10183,7 +10185,7 @@ class WolfMapViewer(wx.Frame):
10183
10185
  curdict = self.myvectors
10184
10186
  curtree = self.myitemsvector
10185
10187
  if newobj is None:
10186
- with wx.lib.busy.BusyInfo(_('Importing file')):
10188
+ with wx.BusyInfo(_('Importing file')):
10187
10189
  wait = wx.BusyCursor()
10188
10190
  newobj = Zones(filename, parent=self)
10189
10191
  del wait
@@ -10220,7 +10222,7 @@ class WolfMapViewer(wx.Frame):
10220
10222
  if ret == wx.ID_OK:
10221
10223
  dirlaz = dlg.GetPath()
10222
10224
 
10223
- with wx.lib.busy.BusyInfo(_('Importing cross sections')):
10225
+ with wx.BusyInfo(_('Importing cross sections')):
10224
10226
  wait = wx.BusyCursor()
10225
10227
  if curfilter == 1: # txt 2022
10226
10228
  newobj = crosssections(filename, format='2022', dirlaz=dirlaz, mapviewer=self)
@@ -10346,7 +10348,7 @@ class WolfMapViewer(wx.Frame):
10346
10348
  curdict = self.mytri
10347
10349
  curtree = self.myitemstri
10348
10350
  if newobj is None:
10349
- with wx.lib.busy.BusyInfo(_('Importing triangulation')):
10351
+ with wx.BusyInfo(_('Importing triangulation')):
10350
10352
  wait = wx.BusyCursor()
10351
10353
  newobj = Triangulation(filename, mapviewer=self)
10352
10354
  del wait
@@ -11584,6 +11586,11 @@ class WolfMapViewer(wx.Frame):
11584
11586
 
11585
11587
  self.rightdown = (x, y)
11586
11588
 
11589
+ elif self.action == 'distance along vector':
11590
+
11591
+ # add a vertex to the vector
11592
+ self._tmp_vector_distance.add_vertex(wolfvertex(x, y))
11593
+
11587
11594
  elif self.action == 'move triangles':
11588
11595
 
11589
11596
  if self.active_tri is None:
@@ -12342,6 +12349,39 @@ class WolfMapViewer(wx.Frame):
12342
12349
  type = Type_Param.String,
12343
12350
  comment = '')
12344
12351
 
12352
+ if self._tmp_vector_distance is not None:
12353
+ curgroup = _('Temporary vector')
12354
+ self.mytooltip.myparams[curgroup] = {}
12355
+ curpar = _('Length [m]')
12356
+ self._tmp_vector_distance.update_lengths()
12357
+ self.mytooltip.add_param(groupname = curgroup,
12358
+ name = curpar,
12359
+ value = '{:3f}'.format(self._tmp_vector_distance.length2D),
12360
+ type = Type_Param.Float,
12361
+ comment = '')
12362
+
12363
+ if self._tmp_vector_distance.nbvertices > 4:
12364
+ _polygon = self._tmp_vector_distance.asshapely_pol()
12365
+ _area = _polygon.area
12366
+ curpar = _('Area [m2]')
12367
+ self.mytooltip.add_param(groupname = curgroup,
12368
+ name = curpar,
12369
+ value = '{:3f}'.format(_area),
12370
+ type = Type_Param.Float,
12371
+ comment = '')
12372
+ curpar = _('Area [ha]')
12373
+ self.mytooltip.add_param(groupname = curgroup,
12374
+ name = curpar,
12375
+ value = '{:3f}'.format(_area / 10000.),
12376
+ type = Type_Param.Float,
12377
+ comment = '')
12378
+ curpar = _('Area [km2]')
12379
+ self.mytooltip.add_param(groupname = curgroup,
12380
+ name = curpar,
12381
+ value = '{:3f}'.format(_area / 1e6),
12382
+ type = Type_Param.Float,
12383
+ comment = '')
12384
+
12345
12385
  for locarray in self.myres2D:
12346
12386
  locarray:Wolfresults_2D
12347
12387
  curgroup = locarray.idx
@@ -12735,10 +12775,10 @@ class WolfMapViewer(wx.Frame):
12735
12775
 
12736
12776
  self.active_vertex.limit2bounds(self.active_vector.mylimits)
12737
12777
 
12738
- if self.action == 'dynamic parallel':
12778
+ elif self.action == 'dynamic parallel':
12739
12779
  self.active_zone.parallel_active(self.dynapar_dist)
12740
12780
 
12741
- if self.action == 'move vector':
12781
+ elif self.action == 'move vector':
12742
12782
  if self.active_vector is not None:
12743
12783
  if self.active_vector._move_start is not None:
12744
12784
 
@@ -12753,7 +12793,7 @@ class WolfMapViewer(wx.Frame):
12753
12793
 
12754
12794
  self.active_vector.move(delta_x, delta_y)
12755
12795
 
12756
- if self.action == 'move triangles':
12796
+ elif self.action == 'move triangles':
12757
12797
  if self.active_tri is not None:
12758
12798
  if self.active_tri._move_start is not None:
12759
12799
 
@@ -12770,18 +12810,18 @@ class WolfMapViewer(wx.Frame):
12770
12810
 
12771
12811
  self.active_tri.reset_plot()
12772
12812
 
12773
- if self.action == 'rotate vector':
12813
+ elif self.action == 'rotate vector':
12774
12814
  if self.active_vector is not None:
12775
12815
  if self.active_vector._rotation_center is not None:
12776
12816
  self.active_vector.rotate_xy(x, y)
12777
12817
 
12778
- if self.action == 'rotate triangles':
12818
+ elif self.action == 'rotate triangles':
12779
12819
  if self.active_tri is not None:
12780
12820
  if self.active_tri._rotation_center is not None:
12781
12821
  self.active_tri.rotate_xy(x, y)
12782
12822
  self.active_tri.reset_plot()
12783
12823
 
12784
- if self.action == 'move zone':
12824
+ elif self.action == 'move zone':
12785
12825
  if self.active_zone is not None:
12786
12826
  if self.active_zone._move_start is not None:
12787
12827
  delta_x = x - self.active_zone._move_start[0]
@@ -12795,11 +12835,20 @@ class WolfMapViewer(wx.Frame):
12795
12835
 
12796
12836
  self.active_zone.move(delta_x, delta_y)
12797
12837
 
12798
- if self.action == 'rotate zone':
12838
+ elif self.action == 'rotate zone':
12799
12839
  if self.active_zone is not None:
12800
12840
  if self.active_zone._rotation_center is not None:
12801
12841
  self.active_zone.rotate_xy(x, y)
12802
12842
 
12843
+ elif self.action == 'distance along vector':
12844
+ if self._tmp_vector_distance is not None:
12845
+ self._tmp_vector_distance.myvertices[-1].x = x
12846
+ self._tmp_vector_distance.myvertices[-1].y = y
12847
+
12848
+ if self._tmp_vector_distance.nbvertices ==2:
12849
+ self._tmp_vector_distance.myvertices[0].x = x
12850
+ self._tmp_vector_distance.myvertices[0].y = y
12851
+
12803
12852
  self.Paint()
12804
12853
 
12805
12854
  # Store the position of the mouse as last known position
@@ -12943,6 +12992,10 @@ class WolfMapViewer(wx.Frame):
12943
12992
  # we must reset the temporary vector
12944
12993
  self.active_vector.reset()
12945
12994
 
12995
+ if self.action == 'distance along vector':
12996
+
12997
+ self._tmp_vector_distance = None
12998
+
12946
12999
  elif self.action == 'pick landmap':
12947
13000
 
12948
13001
  self.end_action(_('End of landmap picking'))
@@ -13172,6 +13225,7 @@ class WolfMapViewer(wx.Frame):
13172
13225
  # r : reset de la sélection de la matrice courante\n \
13173
13226
  # R : reset de toutes les sélections de la matrice courante\n \
13174
13227
  # P : sélection de la section transversale par click souris\n \
13228
+ # D : calcule de distance le long d'un vecteur temporaire\n \
13175
13229
  # \n \
13176
13230
  # RETURN : end current action (cf aussi double clicks droit 'OnRDClick')\n \
13177
13231
  # DELETE : remove item\n \
@@ -13220,6 +13274,7 @@ class WolfMapViewer(wx.Frame):
13220
13274
  'ALT+C': _('Drawing : copy canvas to Clipboard as Matplotlib image'),
13221
13275
  'ALT+SHIFT+C': _('Drawing : copy canvas to Clipboard as Matplotlib image with axes - multiviewers'),
13222
13276
  'CTRL+ALT+SHIFT+C': _('Drawing : copy canvas to Clipboard as Matplotlib image with axes - all arrays one by one'),
13277
+ 'd or D': _('Drawing : calculate distance along a temporary vector'),
13223
13278
 
13224
13279
  'CTRL+o': _('Results : increase transparency of the current result'),
13225
13280
  'CTRL+O': _('Results : decrease transparency of the current result'),
@@ -13326,6 +13381,14 @@ class WolfMapViewer(wx.Frame):
13326
13381
  logging.info(_('ACTION : ') + _(message) if message != '' else _('ACTION : End of action') )
13327
13382
  self.msg_action(1)
13328
13383
 
13384
+ def distance_by_multiple_clicks(self):
13385
+ """ Distance between multiple clicks """
13386
+
13387
+ self.start_action('distance along vector', _('Distance by multiple clicks -- Select the points'))
13388
+ self._tmp_vector_distance = vector()
13389
+ self._tmp_vector_distance.add_vertex([wolfvertex(0., 0.),
13390
+ wolfvertex(0., 0.)])
13391
+
13329
13392
  def OnHotKey(self, e: wx.KeyEvent):
13330
13393
  """
13331
13394
  Gestion des touches clavier -- see print_shortcuts for more details
@@ -14001,6 +14064,9 @@ class WolfMapViewer(wx.Frame):
14001
14064
  if self.active_laz is not None:
14002
14065
  self.active_laz.add_pose_in_memory()
14003
14066
 
14067
+ elif key == ord('D'):
14068
+ self.distance_by_multiple_clicks()
14069
+
14004
14070
  def paste_values(self,fromarray:WolfArray):
14005
14071
  """ Paste selected values from a WolfArray to the active array """
14006
14072
 
@@ -14309,6 +14375,10 @@ class WolfMapViewer(wx.Frame):
14309
14375
  # Gestion des BC (si actif)
14310
14376
  if self.active_bc is not None:
14311
14377
  self.active_bc.plot()
14378
+
14379
+ if self._tmp_vector_distance is not None:
14380
+ self._tmp_vector_distance.plot()
14381
+
14312
14382
  # try:
14313
14383
  # if self.active_bc is not None:
14314
14384
  # self.active_bc.plot()
wolfhece/PyGui.py CHANGED
@@ -153,6 +153,7 @@ class GenMapManager(wx.Frame):
153
153
  return self._configuration
154
154
 
155
155
  class MapManager(GenMapManager):
156
+
156
157
  def __init__(self,*args, **kw):
157
158
  super().__init__(*args, **kw)
158
159
 
wolfhece/PyVertex.py CHANGED
@@ -549,7 +549,7 @@ class cloud_vertices(Element_To_Draw):
549
549
 
550
550
  self.loaded = False
551
551
 
552
- self.header = header
552
+ self._header = header
553
553
 
554
554
  self.gllist = 0
555
555
  self.forceupdategl = False
@@ -570,7 +570,7 @@ class cloud_vertices(Element_To_Draw):
570
570
  if Path(fname).suffix.lower() == '.dxf':
571
571
  self.import_from_dxf(self.filename, imported_elts=dxf_imported_elts)
572
572
  elif Path(fname).suffix.lower() == '.shp':
573
- self.import_shapefile(self.filename, bbox=bbox)
573
+ self.import_from_shapefile(self.filename, bbox=bbox)
574
574
  else:
575
575
  self.readfile(self.filename, header)
576
576
 
@@ -586,24 +586,53 @@ class cloud_vertices(Element_To_Draw):
586
586
  xyz = self.get_xyz()
587
587
  self.mytree = KDTree(xyz)
588
588
 
589
- def find_nearest(self, xy:np.ndarray, nb:int =1):
589
+ def find_nearest(self, xyz:np.ndarray | list, nb:int =1):
590
590
  """
591
- Find nearest neighbors from Scipy KDTree structure based on a copy of the vertices
591
+ Find nearest neighbors from Scipy KDTree structure based on a copy of the vertices.
592
592
 
593
- Return :
594
- - list of distances
595
- - list of "Wolfvertex"
596
- - list of elements stored in self.myvertices
593
+ See : https://docs.scipy.org/doc/scipy/reference/generated/scipy.spatial.KDTree.query.html
594
+
595
+ :param xyz: coordinates to find nearest neighbors -- shape (n, m) - where m is the number of coordinates (2 or 3)
596
+ :param nb: number of nearest neighbors to find
597
+ :return: list of distances, list of "Wolfvertex", list of elements stored in self.myvertices - or list of lists if xyz is a list of coordinates
597
598
  """
598
599
 
599
- keys = self.myvertices.keys()
600
- if self.mytree is None:
601
- self.create_kdtree()
600
+ if isinstance(xyz, list):
601
+ if len(xyz) > 0:
602
+ if isinstance(xyz[0], float | int):
603
+ logging.warning(_('xyz is a list of floats -- converting to a list of lists'))
604
+ xyz = [xyz]
605
+
606
+ xyz = np.array(xyz)
607
+
608
+ try:
609
+ keys = list(self.myvertices.keys())
610
+ if self.mytree is None:
611
+ self.create_kdtree()
612
+
613
+ if xyz.shape[1] != self.mytree.m:
614
+ logging.error(_('Error in find_nearest -- xyz must be a list or 2D array with {} columns').format(self.mytree.m))
615
+ return None, None, None
616
+
617
+ dist, ii = self.mytree.query(xyz, k=nb)
618
+
619
+ if xyz.shape[0] == 1:
620
+ if nb == 1:
621
+ return dist[0], self.myvertices[keys[ii[0]]]['vertex'], self.myvertices[keys[ii[0]]]
622
+ else:
623
+ return dist[0], [self.myvertices[keys[curi]]['vertex'] for curi in ii[0]], [self.myvertices[keys[curi]] for curi in ii[0]]
624
+ else:
625
+ if nb == 1:
626
+ return dist, [self.myvertices[keys[curi]]['vertex'] for curi in ii], [self.myvertices[keys[curi]] for curi in ii]
627
+ else:
628
+ return dist, [[self.myvertices[keys[curi]]['vertex'] for curi in curii] for curii in ii], [[self.myvertices[keys[curi]] for curi in curii] for curii in ii]
602
629
 
603
- dist, ii = self.mytree.query(xy, k=nb)
604
- return dist, [self.myvertices[keys[curi]]['vertex'] for curi in ii], [self.myvertices[keys[curi]] for curi in ii]
630
+ except Exception as e:
631
+ logging.error(_('Error in find_nearest -- {}').format(e))
632
+ logging.error(_('Check your input data -- it must be a list or 2D array with 3 columns'))
633
+ return None, None, None
605
634
 
606
- def init_from_nparray(self, array):
635
+ def init_from_nparray(self, array:np.ndarray):
607
636
  """
608
637
  Fill-in from nparray -- shape (n,3)
609
638
  """
@@ -624,7 +653,7 @@ class cloud_vertices(Element_To_Draw):
624
653
 
625
654
  self.plotted = True
626
655
  if not self.loaded:
627
- self.readfile(self.filename, self.header)
656
+ self.readfile(self.filename, self._header)
628
657
  self.loaded = True
629
658
 
630
659
  def uncheck_plot(self, unload=True):
@@ -703,11 +732,11 @@ class cloud_vertices(Element_To_Draw):
703
732
  nbcols = len(curval)
704
733
 
705
734
  if nbcols < 2:
706
- print('Not enough values on one line -- Retry !!')
735
+ logging.warning(_('Not enough values on one line -- Retry !!'))
707
736
  return
708
737
  elif nbcols > 3:
709
738
  if headers is None:
710
- print('No headers -- Retry !!')
739
+ logging.warning(_('No headers -- Retry !!'))
711
740
  return
712
741
  else:
713
742
  if headers[2].lower() == 'z':
@@ -847,7 +876,7 @@ class cloud_vertices(Element_To_Draw):
847
876
 
848
877
  return k
849
878
 
850
- def import_shapefile(self, fn:str='', targetcolumn:str = 'X1_Y1_Z1', bbox:Polygon = None):
879
+ def import_from_shapefile(self, fn:str='', targetcolumn:str = 'X1_Y1_Z1', bbox:Polygon = None):
851
880
  """
852
881
  Importing Shapefile using geopandas library
853
882
 
@@ -1010,6 +1039,8 @@ class cloud_vertices(Element_To_Draw):
1010
1039
  Plot OpenGL
1011
1040
 
1012
1041
  Legend is an image texture
1042
+
1043
+ FIXME : to be improved
1013
1044
  """
1014
1045
  if self.get_mapviewer() is None:
1015
1046
  logging.warning(_('No mapviewer available for legend plot'))
@@ -1235,7 +1266,7 @@ class cloud_vertices(Element_To_Draw):
1235
1266
 
1236
1267
  """
1237
1268
 
1238
- if self.header:
1269
+ if self._header:
1239
1270
  if key=='vertex':
1240
1271
  xyz = [[curvert['vertex'].x, curvert['vertex'].y, curvert['vertex'].z] for curvert in self.myvertices.values()]
1241
1272
  else:
@@ -1244,6 +1275,63 @@ class cloud_vertices(Element_To_Draw):
1244
1275
  xyz = [[curvert['vertex'].x, curvert['vertex'].y, curvert['vertex'].z] for curvert in self.myvertices.values()]
1245
1276
  return np.array(xyz)
1246
1277
 
1278
+ @property
1279
+ def has_values(self) -> bool:
1280
+ """
1281
+ Check if the cloud has values (other than X,Y,Z)
1282
+ """
1283
+
1284
+ if self._header:
1285
+ return len(self.myvertices[0]) > 1
1286
+ else:
1287
+ return False
1288
+
1289
+ @property
1290
+ def header(self) -> list[str]:
1291
+ """
1292
+ Return the headers of the cloud
1293
+ """
1294
+
1295
+ if self._header:
1296
+ return list(self.myvertices[0].keys())
1297
+ else:
1298
+ return []
1299
+
1300
+ def add_values_by_id_list(self, id:str, values:list[float]):
1301
+ """
1302
+ Add values to the cloud
1303
+
1304
+ :param id: use as key for the values
1305
+ :param values: list of values to be added - must be the same length as number of vertices
1306
+
1307
+ """
1308
+
1309
+ if len(values) != len(self.myvertices):
1310
+ logging.warning(_('Number of values does not match the number of vertices -- Retry !!'))
1311
+ logging.info(_(('Number of vertices : ')+str(len(self.myvertices))))
1312
+ return
1313
+
1314
+ for item, val in zip(self.myvertices.values(), values):
1315
+ item[id] = val
1316
+
1317
+ self._header = True
1318
+
1319
+ @property
1320
+ def xyz(self) -> np.ndarray:
1321
+ """
1322
+ Alias for get_xyz method
1323
+ """
1324
+
1325
+ return self.get_xyz(key='vertex')
1326
+
1327
+ @property
1328
+ def nbvertices(self) -> int:
1329
+ """
1330
+ Number of vertices in the cloud
1331
+ """
1332
+
1333
+ return len(self.myvertices)
1334
+
1247
1335
  def interp_on_array(self, myarray, key:str='vertex', method:Literal['linear', 'nearest', 'cubic'] = 'linear'):
1248
1336
  """
1249
1337
  Interpolation of the cloud on a 2D array
@@ -1319,3 +1407,23 @@ class cloud_vertices(Element_To_Draw):
1319
1407
  return newcloud
1320
1408
  else:
1321
1409
  return all_s, all_z
1410
+
1411
+ def plot_matplotlib(self, ax=None, **kwargs):
1412
+ """
1413
+ Plot the cloud using matplotlib
1414
+
1415
+ :param ax: axis to plot on -- if None, a new figure is created
1416
+ :param kwargs: additional arguments for matplotlib
1417
+
1418
+ """
1419
+ import matplotlib.pyplot as plt
1420
+
1421
+ if ax is None:
1422
+ fig, ax = plt.subplots()
1423
+
1424
+ x = [cur['vertex'].x for cur in self.myvertices.values()]
1425
+ y = [cur['vertex'].y for cur in self.myvertices.values()]
1426
+
1427
+ ax.scatter(x, y, **kwargs)
1428
+
1429
+ return fig, ax