wolfhece 2.1.117__py3-none-any.whl → 2.1.119__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/CpGrid.py +1 -1
- wolfhece/PyDraw.py +519 -101
- wolfhece/PyVertexvectors.py +5 -0
- wolfhece/apps/version.py +1 -1
- wolfhece/lazviewer/laz_viewer.py +884 -12
- wolfhece/lazviewer/viewer/viewer.py +14 -4
- wolfhece/opengl/py3d.py +10 -4
- wolfhece/wolf_array.py +278 -5
- wolfhece/wolf_vrt.py +18 -17
- {wolfhece-2.1.117.dist-info → wolfhece-2.1.119.dist-info}/METADATA +1 -1
- {wolfhece-2.1.117.dist-info → wolfhece-2.1.119.dist-info}/RECORD +14 -14
- {wolfhece-2.1.117.dist-info → wolfhece-2.1.119.dist-info}/WHEEL +0 -0
- {wolfhece-2.1.117.dist-info → wolfhece-2.1.119.dist-info}/entry_points.txt +0 -0
- {wolfhece-2.1.117.dist-info → wolfhece-2.1.119.dist-info}/top_level.txt +0 -0
wolfhece/lazviewer/laz_viewer.py
CHANGED
@@ -5,7 +5,7 @@ from PIL import Image
|
|
5
5
|
from os.path import exists,join
|
6
6
|
import matplotlib.pyplot as plt
|
7
7
|
from os import stat,remove,listdir,scandir, makedirs
|
8
|
-
import
|
8
|
+
from pathlib import Path
|
9
9
|
import math
|
10
10
|
from time import sleep
|
11
11
|
from typing import Literal, Union
|
@@ -22,8 +22,11 @@ from . import viewer
|
|
22
22
|
from ..PyWMS import getWalonmap
|
23
23
|
from ..PyTranslate import _
|
24
24
|
from ..color_constants import Colors
|
25
|
-
from ..PyParams import Wolf_Param
|
26
|
-
from ..matplotlib_fig import Matplotlib_Figure as
|
25
|
+
from ..PyParams import Wolf_Param, Type_Param
|
26
|
+
from ..matplotlib_fig import Matplotlib_Figure as MplFig
|
27
|
+
from ..drawing_obj import Element_To_Draw
|
28
|
+
from ..CpGrid import CpGrid
|
29
|
+
from ..PyVertexvectors import vector, Zones, zone, wolfvertex
|
27
30
|
|
28
31
|
"""
|
29
32
|
Importation et visualisation de données LAS et LAZ
|
@@ -49,6 +52,7 @@ class Classification_LAZ():
|
|
49
52
|
self.test_wx()
|
50
53
|
|
51
54
|
self._choose_colors = None
|
55
|
+
self._viewer = None
|
52
56
|
|
53
57
|
def test_wx(self):
|
54
58
|
self.wx_exists = wx.App.Get() is not None
|
@@ -76,6 +80,7 @@ class Classification_LAZ():
|
|
76
80
|
9 : ['Eau', 'Points de la surface d\’eau brute mesurés par le scanner 2',Colors.rgb_withalpha_float('royalblue',.3)],
|
77
81
|
10 : ['Ponts', 'Les ponts ont été classés à part pour améliorer la définition du MNT. Ils ont été ouverts grâce',Colors.rgb_withalpha_float('lightyellow1',1.)],
|
78
82
|
11 : ['Mur de berges', 'Mur et muret en berge de la Vesdre dépassant le sol à des vocation de réaliser une modélisation 3D hydraulique avec ces obstacles.',Colors.rgb_withalpha_float('red1',1.)],
|
83
|
+
13 : ['Inconnu', 'A vérifier auSPW', Colors.rgb_withalpha_float('lightslategray',.2)],
|
79
84
|
15 : ['Tranche d\'eau', 'Echo intermédiaire dans l\’eau n\’appartenant ni à la surface d\’eau ni au fond du lit', Colors.rgb_withalpha_float('lightblue',.2)],
|
80
85
|
16 : ['Surface bathymétrique', 'Fond du lit de la Vesdre et de ses affluents et des autres surfaces d\’eau mesurées à partir du scanner 3 FWF discrétisé',Colors.rgb_withalpha_float('sandybrown',1.)],
|
81
86
|
17 : ['Surface bathymétrique incertaine', 'Surface bathymétrique sur les zones peu profondes principalement sous végétation où les intensités des échos sont parfois trop faibles pour avoir la certitude qu\’ils représentent le fond de rivière. La classe 17 est néanmoins plus certaine que la classe 18. Elle est utilisée dans la génération des MNT par défaut.',Colors.rgb_withalpha_float('rosybrown',.5)],
|
@@ -146,7 +151,7 @@ class xyz_laz():
|
|
146
151
|
self.data = []
|
147
152
|
return
|
148
153
|
|
149
|
-
last_part =
|
154
|
+
last_part = Path(fn).name
|
150
155
|
parts = last_part.split('_')
|
151
156
|
|
152
157
|
# L'origine est codée dans le nom de fichier
|
@@ -157,7 +162,7 @@ class xyz_laz():
|
|
157
162
|
dy = 3500.
|
158
163
|
|
159
164
|
# Récupération du lien vers le fichier GridInfo.txt
|
160
|
-
gridinfo=join(
|
165
|
+
gridinfo=join(Path(fn).parent,'gridinfo.txt')
|
161
166
|
|
162
167
|
if exists(gridinfo):
|
163
168
|
with open(gridinfo,'r') as f:
|
@@ -244,8 +249,8 @@ class xyz_laz():
|
|
244
249
|
if exists(fn+'.zip'):
|
245
250
|
with zipfile.ZipFile(fn+'.zip','r') as zip_file:
|
246
251
|
fn = zip_file.namelist()[0]
|
247
|
-
zip_file.extract(fn, path=
|
248
|
-
fn = join(
|
252
|
+
zip_file.extract(fn, path=Path(fn).parent)
|
253
|
+
fn = join(Path(fn).parent, fn)
|
249
254
|
elif not exists(fn):
|
250
255
|
return
|
251
256
|
|
@@ -395,7 +400,7 @@ class xyz_laz_grid():
|
|
395
400
|
xloc = np.linspace(xbounds[0], xbounds[1], gridsize[0]+1)
|
396
401
|
yloc = np.linspace(ybounds[0], ybounds[1], gridsize[1]+1)
|
397
402
|
|
398
|
-
dirout =
|
403
|
+
dirout = Path(fn_out).parent
|
399
404
|
fn=join(dirout,'gridinfo.txt')
|
400
405
|
|
401
406
|
if exists(fn):
|
@@ -405,7 +410,7 @@ class xyz_laz_grid():
|
|
405
410
|
f.write(str(int(xbounds[0]))+','+str(int(xbounds[1]))+'\n')
|
406
411
|
f.write(str(int(ybounds[0]))+','+str(int(ybounds[1]))+'\n')
|
407
412
|
f.write(str(int(gridsize[0]))+','+str(int(gridsize[1]))+'\n')
|
408
|
-
f.write(
|
413
|
+
f.write(Path(fn_out).name+'_'+'x1'+'_'+'y1'+'_xyz.bin'+'\n')
|
409
414
|
if force_format == np.float64:
|
410
415
|
f.write('xy_float64')
|
411
416
|
else:
|
@@ -481,7 +486,7 @@ class xyz_laz_grid():
|
|
481
486
|
if force_format == np.float64:
|
482
487
|
fnzip = fn + '.zip'
|
483
488
|
with zipfile.ZipFile(fnzip,'w',zipfile.ZIP_DEFLATED) as zip_file:
|
484
|
-
zip_file.write(fn,
|
489
|
+
zip_file.write(fn, Path(fn).name)
|
485
490
|
remove(fn)
|
486
491
|
|
487
492
|
|
@@ -632,7 +637,7 @@ class xyz_laz_grids():
|
|
632
637
|
|
633
638
|
(up_s, up_z, up_color), (down_s, down_z, down_color) = self.scan_around(xy, length_buffer)
|
634
639
|
|
635
|
-
figmpl =
|
640
|
+
figmpl = MplFig()
|
636
641
|
figmpl.presets()
|
637
642
|
fig = figmpl.fig
|
638
643
|
ax = figmpl.cur_ax
|
@@ -682,6 +687,873 @@ class xyz_laz_grids():
|
|
682
687
|
gridsize=[max(int(dx/ds),1), max(int(dy/ds),1)],
|
683
688
|
force_format=force_format))
|
684
689
|
|
690
|
+
|
691
|
+
class Wolf_LAZ_Data(Element_To_Draw):
|
692
|
+
""" Base class for LAZ data which can be imported in Pydraw.Mapviewer.
|
693
|
+
"""
|
694
|
+
|
695
|
+
def __init__(self, idx:str = '', plotted:bool = False, mapviewer = None, need_for_wx:bool = False) -> None:
|
696
|
+
|
697
|
+
self._filename:Path = Path('') # filename TODO : improve serialization
|
698
|
+
self._lazfile:Path = None # Not used
|
699
|
+
|
700
|
+
# Bounds must be set before calling super().__init__() because
|
701
|
+
# xmin, xmax, ymin, ymax properties are used and depend on _bounds
|
702
|
+
self._bounds = [[0.,0.],[0.,0.]] # [[xmin,xmax],[ymin,ymax]]
|
703
|
+
|
704
|
+
super().__init__(idx, plotted, mapviewer, need_for_wx)
|
705
|
+
|
706
|
+
self._data:np.ndarray = None # Numpy data array -- to be plotted
|
707
|
+
self._colors:np.ndarray = None # NumPy array of colors for each point --> see viewer attributes for details
|
708
|
+
self.classification = Classification_LAZ() # Classification of LAZ data --> defining colors if codification is used
|
709
|
+
|
710
|
+
self._associated_color:int = Colors_Lazviewer.CODE_2023.value # Associated color type for LAZ data
|
711
|
+
|
712
|
+
self.viewer:viewer = None # PPTK viewer
|
713
|
+
|
714
|
+
self._point_size = .05 # Point size in viewer -- in user units
|
715
|
+
|
716
|
+
self._flight_memory = [] # Flight position
|
717
|
+
|
718
|
+
self._myprops = None # Properties based on Wolf_Param class
|
719
|
+
|
720
|
+
self._bg_color = [int(0.23*255), int(0.23*255), int(.44*255), 255] # Background color
|
721
|
+
self._bg_color_top = [0, 0, 0, 255] # Top background color
|
722
|
+
self._bg_color_bottom = [int(0.23*255), int(0.23*255), int(.44*255), 255] # Bottom background color
|
723
|
+
|
724
|
+
self._floor_level = 0. # Floor level -- user units
|
725
|
+
self._floor_color = [int(0.3*255), int(0.3*255), int(.3*255), 127] # Floor color
|
726
|
+
|
727
|
+
self._show_grid = True # Show grid in viewer
|
728
|
+
self._show_axis = True # Show axis in viewer
|
729
|
+
self._show_info = True # Show info in viewer
|
730
|
+
|
731
|
+
self._select_only_codes = [] # Codes to be selected -- Defined by user
|
732
|
+
|
733
|
+
self._xls:CpGrid = None # Excel grid for selected data
|
734
|
+
self._xlsFrame:wx.Frame = None # Frame containing the xls grid
|
735
|
+
|
736
|
+
def serialize(self):
|
737
|
+
""" Serialize class : data and attributes """
|
738
|
+
return {'bounds':self._bounds, 'data':str(self._filename) + '.npz', 'associated_color':self._associated_color,
|
739
|
+
'point_size':self._point_size, 'bg_color':self._bg_color, 'bg_color_top':self._bg_color_top,
|
740
|
+
'bg_color_bottom':self._bg_color_bottom, 'floor_level':self._floor_level, 'floor_color':self._floor_color,
|
741
|
+
'show_grid':self._show_grid, 'show_axis':self._show_axis, 'show_info':self._show_info}
|
742
|
+
|
743
|
+
def deserialize(self, data:dict):
|
744
|
+
""" Deserialize class : data and attributes """
|
745
|
+
self._bounds = data['bounds']
|
746
|
+
self._filename = Path(data['data'])
|
747
|
+
self._associated_color = data['associated_color']
|
748
|
+
self._point_size = data['point_size']
|
749
|
+
self._bg_color = data['bg_color']
|
750
|
+
self._bg_color_top = data['bg_color_top']
|
751
|
+
self._bg_color_bottom = data['bg_color_bottom']
|
752
|
+
self._floor_level = data['floor_level']
|
753
|
+
self._floor_color = data['floor_color']
|
754
|
+
self._show_grid = data['show_grid']
|
755
|
+
self._show_axis = data['show_axis']
|
756
|
+
self._show_info = data['show_info']
|
757
|
+
|
758
|
+
self.data = np.load(self._filename)['data']
|
759
|
+
|
760
|
+
def saveas(self, fn:str):
|
761
|
+
""" Save class : data and attributes """
|
762
|
+
import pickle
|
763
|
+
|
764
|
+
self._filename = Path(fn)
|
765
|
+
|
766
|
+
with open(fn,'wb') as f:
|
767
|
+
pickle.dump(self.serialize(),f)
|
768
|
+
|
769
|
+
# save data by numpy
|
770
|
+
np.savez(str(fn) + '.npz', data=self._data)
|
771
|
+
|
772
|
+
def load(self, fn:str):
|
773
|
+
""" Load class : data and attributes """
|
774
|
+
import pickle
|
775
|
+
|
776
|
+
with open(fn,'rb') as f:
|
777
|
+
self.deserialize(pickle.load(f))
|
778
|
+
|
779
|
+
@property
|
780
|
+
def associated_color(self):
|
781
|
+
return self._associated_color
|
782
|
+
|
783
|
+
@associated_color.setter
|
784
|
+
def associated_color(self, value:int):
|
785
|
+
self._associated_color = value
|
786
|
+
self.set_colors()
|
787
|
+
|
788
|
+
def merge(self, other:"Wolf_LAZ_Data"):
|
789
|
+
""" Merge two Wolf_LAZ_Data objects """
|
790
|
+
|
791
|
+
if self._data is None:
|
792
|
+
self._data = other._data
|
793
|
+
else:
|
794
|
+
self._data = np.concatenate((self._data, other._data))
|
795
|
+
|
796
|
+
self.bounds = [[min(self.bounds[0][0],other.bounds[0][0]),max(self.bounds[0][1],other.bounds[0][1])],
|
797
|
+
[min(self.bounds[1][0],other.bounds[1][0]),max(self.bounds[1][1],other.bounds[1][1])]]
|
798
|
+
|
799
|
+
def filter_data(self, codes:list[int]):
|
800
|
+
""" Filter data by codes """
|
801
|
+
self._data = self._data[np.isin(self._data[:,3],codes)]
|
802
|
+
|
803
|
+
def bg_color(self, value):
|
804
|
+
if self.viewer is not None:
|
805
|
+
return self.viewer.set(bg_color = value)
|
806
|
+
|
807
|
+
def bg_color_top(self, value):
|
808
|
+
if self.viewer is not None:
|
809
|
+
return self.viewer.set(bg_color_top = value)
|
810
|
+
|
811
|
+
def bg_color_bottom(self, value):
|
812
|
+
if self.viewer is not None:
|
813
|
+
return self.viewer.set(bg_color_bottom = value)
|
814
|
+
|
815
|
+
def floor_level(self, value):
|
816
|
+
if self.viewer is not None:
|
817
|
+
return self.viewer.set(floor_level = float(value))
|
818
|
+
|
819
|
+
def floor_color(self, value):
|
820
|
+
if self.viewer is not None:
|
821
|
+
return self.viewer.set(floor_color = value)
|
822
|
+
|
823
|
+
def show_grid(self, value:bool):
|
824
|
+
if self.viewer is not None:
|
825
|
+
return self.viewer.set(show_grid = bool(value))
|
826
|
+
|
827
|
+
def show_axis(self, value:bool):
|
828
|
+
if self.viewer is not None:
|
829
|
+
return self.viewer.set(show_axis = bool(value))
|
830
|
+
|
831
|
+
def show_info(self, value:bool):
|
832
|
+
if self.viewer is not None:
|
833
|
+
return self.viewer.set(show_info = bool(value))
|
834
|
+
|
835
|
+
def force_view(self, x, y, z = -1):
|
836
|
+
""" Force lookat position """
|
837
|
+
if z == -1:
|
838
|
+
curx,cury,curz = self.lookat
|
839
|
+
self.lookat = [x,y,curz]
|
840
|
+
else:
|
841
|
+
self.lookat = [x,y,z]
|
842
|
+
# self.eye = self._eye_pos()
|
843
|
+
|
844
|
+
@property
|
845
|
+
def selected(self):
|
846
|
+
if self.viewer is None:
|
847
|
+
return None
|
848
|
+
return self.viewer.get('selected')
|
849
|
+
|
850
|
+
@property
|
851
|
+
def xyz_selected(self) -> np.ndarray:
|
852
|
+
""" Extract the selected points from the viewer.
|
853
|
+
|
854
|
+
Filter the selected points by codes if _select_only_codes is not empty."""
|
855
|
+
|
856
|
+
if self.viewer is None:
|
857
|
+
return None
|
858
|
+
|
859
|
+
if self.selected.shape[0] == 0:
|
860
|
+
return np.ndarray((0,3))
|
861
|
+
|
862
|
+
if len(self._select_only_codes)>0:
|
863
|
+
return self.data[self.selected,:3][np.isin(self.data[self.selected,3],self._select_only_codes)]
|
864
|
+
else:
|
865
|
+
return self.data[self.selected,:3]
|
866
|
+
|
867
|
+
@property
|
868
|
+
def code_selected(self) -> np.ndarray:
|
869
|
+
if self.viewer is None:
|
870
|
+
return None
|
871
|
+
|
872
|
+
if self.selected.shape[0] == 0:
|
873
|
+
return np.ndarray((0,1))
|
874
|
+
|
875
|
+
if len(self._select_only_codes)>0:
|
876
|
+
return self.data[self.selected,3][np.isin(self.data[self.selected,3],self._select_only_codes)]
|
877
|
+
else:
|
878
|
+
return self.data[self.selected,3]
|
879
|
+
|
880
|
+
@property
|
881
|
+
def num_points(self):
|
882
|
+
""" Number of points """
|
883
|
+
|
884
|
+
nb1 = self.data.shape[0]
|
885
|
+
if self.viewer is not None:
|
886
|
+
nb2 = self.viewer.get('num_points')[0]
|
887
|
+
assert nb1 == nb2, _('Incoherent number of points')
|
888
|
+
|
889
|
+
return nb1
|
890
|
+
|
891
|
+
@property
|
892
|
+
def nb_points(self):
|
893
|
+
""" Number of points - alias of num_points """
|
894
|
+
return self.num_points
|
895
|
+
|
896
|
+
@property
|
897
|
+
def nb(self):
|
898
|
+
""" Number of points - alias of num_points """
|
899
|
+
return self.num_points
|
900
|
+
|
901
|
+
@property
|
902
|
+
def right(self):
|
903
|
+
"""Camera Right vector """
|
904
|
+
return self.viewer.get('right')
|
905
|
+
|
906
|
+
@property
|
907
|
+
def mvp(self):
|
908
|
+
""" Model View Projection matrix
|
909
|
+
|
910
|
+
See https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93projection_matrix for details
|
911
|
+
"""
|
912
|
+
|
913
|
+
if self.viewer is None:
|
914
|
+
return None
|
915
|
+
return self.viewer.get('mvp')
|
916
|
+
|
917
|
+
@property
|
918
|
+
def eye(self):
|
919
|
+
""" eye/camera position """
|
920
|
+
if self.viewer is None:
|
921
|
+
return None
|
922
|
+
return self.viewer.get('eye')
|
923
|
+
|
924
|
+
@property
|
925
|
+
def lookat(self):
|
926
|
+
""" Lookat position """
|
927
|
+
if self.viewer is None:
|
928
|
+
return None
|
929
|
+
return self.viewer.get('lookat')
|
930
|
+
|
931
|
+
@property
|
932
|
+
def phi(self):
|
933
|
+
""" Azimuth angle (radians) """
|
934
|
+
if self.viewer is None:
|
935
|
+
return None
|
936
|
+
return self.viewer.get('phi')[0]
|
937
|
+
|
938
|
+
@property
|
939
|
+
def theta(self):
|
940
|
+
""" Elevation angle (radians) """
|
941
|
+
if self.viewer is None:
|
942
|
+
return None
|
943
|
+
return self.viewer.get('theta')[0]
|
944
|
+
|
945
|
+
@property
|
946
|
+
def r(self):
|
947
|
+
""" Distance from lookat """
|
948
|
+
if self.viewer is None:
|
949
|
+
return None
|
950
|
+
return self.viewer.get('r')[0]
|
951
|
+
|
952
|
+
@phi.setter
|
953
|
+
def phi(self, value):
|
954
|
+
if self.viewer is None:
|
955
|
+
return None
|
956
|
+
self.viewer.set(phi=value)
|
957
|
+
self._fill_props()
|
958
|
+
|
959
|
+
@theta.setter
|
960
|
+
def theta(self, value):
|
961
|
+
if self.viewer is None:
|
962
|
+
return None
|
963
|
+
self.viewer.set(theta=value)
|
964
|
+
self._fill_props()
|
965
|
+
|
966
|
+
@r.setter
|
967
|
+
def r(self, value):
|
968
|
+
if self.viewer is None:
|
969
|
+
return None
|
970
|
+
self.viewer.set(r=value)
|
971
|
+
self._fill_props()
|
972
|
+
|
973
|
+
@lookat.setter
|
974
|
+
def lookat(self, value):
|
975
|
+
if self.viewer is None:
|
976
|
+
return None
|
977
|
+
self.viewer.set(lookat=value)
|
978
|
+
# self.viewer.set(lookat=value, phi=self.phi, theta=self.theta, r=self.r)
|
979
|
+
self._fill_props()
|
980
|
+
|
981
|
+
def _eye_pos(self):
|
982
|
+
""" Compute eye pos from lookat and r, phi, theta.
|
983
|
+
|
984
|
+
phi is the azimuth angle (radians)
|
985
|
+
theta is the elevation angle (radians)
|
986
|
+
r is the distance from lookat
|
987
|
+
"""
|
988
|
+
|
989
|
+
lx, ly, lz = self.lookat
|
990
|
+
r = self.r
|
991
|
+
phi = self.phi
|
992
|
+
theta = self.theta
|
993
|
+
|
994
|
+
x = lx + r*np.sin(phi)*np.cos(theta)
|
995
|
+
y = ly + r*np.cos(phi)*np.cos(theta)
|
996
|
+
z = lz + r*np.sin(theta)
|
997
|
+
|
998
|
+
return [x,y,z]
|
999
|
+
|
1000
|
+
@eye.setter
|
1001
|
+
def eye(self, value):
|
1002
|
+
if self.viewer is None:
|
1003
|
+
return None
|
1004
|
+
x,y,z = value
|
1005
|
+
lx, ly, lz = self.lookat
|
1006
|
+
right = self.right
|
1007
|
+
|
1008
|
+
# Compute phi, theta, r based on eye and lookat and right vector
|
1009
|
+
r = np.sqrt((x-lx)**2 + (y-ly)**2 + (z-lz)**2)
|
1010
|
+
self.r =r
|
1011
|
+
self.theta = np.arcsin((z-lz)/r)
|
1012
|
+
self.phi = -np.arctan2((x-lx),(y-ly))
|
1013
|
+
self._fill_props()
|
1014
|
+
|
1015
|
+
@property
|
1016
|
+
def point_size(self):
|
1017
|
+
""" Point size in viewer -- user units """
|
1018
|
+
return self._point_size
|
1019
|
+
|
1020
|
+
@point_size.setter
|
1021
|
+
def point_size(self, value):
|
1022
|
+
self._point_size = value
|
1023
|
+
if self.viewer is not None:
|
1024
|
+
self.viewer.set(point_size=value)
|
1025
|
+
self._fill_props()
|
1026
|
+
|
1027
|
+
@property
|
1028
|
+
def xyz(self):
|
1029
|
+
return self.data[:,:3]
|
1030
|
+
|
1031
|
+
@property
|
1032
|
+
def codes(self):
|
1033
|
+
return self.data[:,3]
|
1034
|
+
|
1035
|
+
def codes_unique(self):
|
1036
|
+
""" Only unique codes """
|
1037
|
+
return list(np.unique(self.codes).astype(int))
|
1038
|
+
|
1039
|
+
def create_viewer(self, color_code:Colors_Lazviewer = None, classification:Classification_LAZ = None):
|
1040
|
+
""" Create a viewer for las data """
|
1041
|
+
|
1042
|
+
if classification is not None:
|
1043
|
+
self.classification = classification
|
1044
|
+
|
1045
|
+
if color_code is not None:
|
1046
|
+
self.associated_color = color_code
|
1047
|
+
|
1048
|
+
self._colors = get_colors(self.data, self.associated_color, palette_classif= self.classification)
|
1049
|
+
|
1050
|
+
self.viewer = viewer(self.xyz, self._colors)
|
1051
|
+
self.viewer.set(point_size= self._point_size)
|
1052
|
+
|
1053
|
+
return self.viewer
|
1054
|
+
|
1055
|
+
def interactive_update_colors(self):
|
1056
|
+
""" Create a frame to interactively update colors """
|
1057
|
+
|
1058
|
+
self.classification.interactive_update_colors()
|
1059
|
+
|
1060
|
+
self.classification._choose_colors.SetTitle(_('Colors of ') + self.idx)
|
1061
|
+
|
1062
|
+
def new_callback_colors():
|
1063
|
+
self.classification.callback_colors()
|
1064
|
+
self.set_colors()
|
1065
|
+
|
1066
|
+
self.classification._choose_colors.callback = new_callback_colors
|
1067
|
+
|
1068
|
+
def set_colors(self):
|
1069
|
+
""" Set colors in viewer --> using attributes method (not colormap) """
|
1070
|
+
|
1071
|
+
if self.viewer is not None:
|
1072
|
+
self._colors = get_colors(self.data, self.associated_color, palette_classif= self.classification)
|
1073
|
+
self.viewer.attributes(self._colors)
|
1074
|
+
|
1075
|
+
def set_classification(self, classification:str = None):
|
1076
|
+
""" Set classification of LAZ data
|
1077
|
+
|
1078
|
+
TODO : Check if 2020-2022 SPW campaign is the same classification as 2013
|
1079
|
+
"""
|
1080
|
+
if classification is None:
|
1081
|
+
logging.warning(_('No classification chosen - Abort !'))
|
1082
|
+
elif classification == 'SPW 2013-2014':
|
1083
|
+
self.classification.init_2013()
|
1084
|
+
else:
|
1085
|
+
self.classification.init_2023()
|
1086
|
+
|
1087
|
+
def find_minmax(self, update=False):
|
1088
|
+
""" Find min and max of data """
|
1089
|
+
if self.data is not None:
|
1090
|
+
self.bounds = [[np.min(self.data[:,0]), np.max(self.data[:,0])],[np.min(self.data[:,1]), np.max(self.data[:,1])]]
|
1091
|
+
|
1092
|
+
@property
|
1093
|
+
def xmin(self):
|
1094
|
+
return self.bounds[0][0]
|
1095
|
+
|
1096
|
+
@xmin.setter
|
1097
|
+
def xmin(self, value):
|
1098
|
+
self._bounds[0][0] = value
|
1099
|
+
|
1100
|
+
@property
|
1101
|
+
def xmax(self):
|
1102
|
+
return self.bounds[0][1]
|
1103
|
+
|
1104
|
+
@xmax.setter
|
1105
|
+
def xmax(self, value):
|
1106
|
+
self._bounds[0][1] = value
|
1107
|
+
|
1108
|
+
@property
|
1109
|
+
def ymin(self):
|
1110
|
+
return self.bounds[1][0]
|
1111
|
+
|
1112
|
+
@ymin.setter
|
1113
|
+
def ymin(self, value):
|
1114
|
+
self._bounds[1][0] = value
|
1115
|
+
|
1116
|
+
@property
|
1117
|
+
def ymax(self):
|
1118
|
+
return self.bounds[1][1]
|
1119
|
+
|
1120
|
+
@ymax.setter
|
1121
|
+
def ymax(self, value):
|
1122
|
+
self._bounds[1][1] = value
|
1123
|
+
|
1124
|
+
@property
|
1125
|
+
def data(self):
|
1126
|
+
""" Full data array (x,y,z,code) """
|
1127
|
+
return self._data
|
1128
|
+
|
1129
|
+
@data.setter
|
1130
|
+
def data(self, value):
|
1131
|
+
self._data = value
|
1132
|
+
|
1133
|
+
@property
|
1134
|
+
def bounds(self):
|
1135
|
+
return self._bounds
|
1136
|
+
|
1137
|
+
@bounds.setter
|
1138
|
+
def bounds(self, value):
|
1139
|
+
self._bounds = value
|
1140
|
+
|
1141
|
+
def from_grid(self, grid:xyz_laz_grid, bounds:Union[tuple[tuple[float,float],tuple[float,float]], list[list[float, float],list[float, float]]]):
|
1142
|
+
""" Create data from grid LAZ """
|
1143
|
+
self.bounds = bounds
|
1144
|
+
self.data = grid.scan(bounds)
|
1145
|
+
|
1146
|
+
def from_file(self, fn:str):
|
1147
|
+
""" Create data from LAZ file """
|
1148
|
+
self.data = read_laz(fn)
|
1149
|
+
self.bounds = [[np.min(self.data[:,0]), np.max(self.data[:,0])],[np.min(self.data[:,1]), np.max(self.data[:,1])]]
|
1150
|
+
|
1151
|
+
def descimate(self, step:int):
|
1152
|
+
""" Descimate data.
|
1153
|
+
|
1154
|
+
Conserve only one point every 'step' points.
|
1155
|
+
|
1156
|
+
:param step: step of descimation
|
1157
|
+
"""
|
1158
|
+
self.data = self.data[::step]
|
1159
|
+
|
1160
|
+
def get_data_class(self, key:int):
|
1161
|
+
""" Get data with a specific code """
|
1162
|
+
|
1163
|
+
assert isinstance(key, int), _('Key must be an integer')
|
1164
|
+
|
1165
|
+
return self.data[self.data[:,3] == key]
|
1166
|
+
|
1167
|
+
def add_pose_in_memory(self, key_time:float = 1.):
|
1168
|
+
""" Add current pose in flight memory """
|
1169
|
+
|
1170
|
+
if self.viewer is not None:
|
1171
|
+
lookat = self.lookat
|
1172
|
+
new_pose = (lookat[0], lookat[1], lookat[2], self.phi, self.theta, self.r)
|
1173
|
+
self._flight_memory.append((new_pose, key_time))
|
1174
|
+
|
1175
|
+
def play_flight(self, tlim=[-np.inf, np.inf], repeat=False, interp='cubic_natural'):
|
1176
|
+
""" Play flight memory """
|
1177
|
+
if self.viewer is not None:
|
1178
|
+
if len(self._flight_memory)>0:
|
1179
|
+
poses = [cur[0] for cur in self._flight_memory]
|
1180
|
+
times = [0.]
|
1181
|
+
for i in range(1,len(self._flight_memory)):
|
1182
|
+
times.append(times[-1]+self._flight_memory[i][1])
|
1183
|
+
self.viewer.play(poses, times, tlim, repeat, interp)
|
1184
|
+
|
1185
|
+
def set_times(self, times:np.ndarray):
|
1186
|
+
""" Set times for flight memory """
|
1187
|
+
if len(self._flight_memory)>0:
|
1188
|
+
self._flight_memory = [(self._flight_memory[i][0], times[i]) for i in range(len(self._flight_memory))]
|
1189
|
+
|
1190
|
+
def set_times_increment(self, increment:float):
|
1191
|
+
if len(self._flight_memory)>0:
|
1192
|
+
self._flight_memory = [(self._flight_memory[i][0], increment*i) for i in range(len(self._flight_memory))]
|
1193
|
+
|
1194
|
+
def get_times(self):
|
1195
|
+
return np.asarray([cur[1] for cur in self._flight_memory])
|
1196
|
+
|
1197
|
+
def record_flight(self, dirout:str, tlim=[-np.inf, np.inf], interp='cubic_natural', fps=24, prefix:str = 'laz_', ext:str = 'png'):
|
1198
|
+
""" Record flight memory in multiple images
|
1199
|
+
|
1200
|
+
FIXME : FREEZE the app --> to debug
|
1201
|
+
"""
|
1202
|
+
if self.viewer is not None:
|
1203
|
+
if len(self._flight_memory)>0:
|
1204
|
+
poses = [cur[0] for cur in self._flight_memory]
|
1205
|
+
times = [0.]
|
1206
|
+
for i in range(1,len(self._flight_memory)):
|
1207
|
+
times.append(times[-1]+self._flight_memory[i][1])
|
1208
|
+
self.viewer.record(dirout, poses, times, tlim, interp, fps=fps, prefix=prefix, ext=ext)
|
1209
|
+
|
1210
|
+
def save_flight(self, fn:str):
|
1211
|
+
""" Write flight memory to file JSON """
|
1212
|
+
|
1213
|
+
import json
|
1214
|
+
|
1215
|
+
if len(self._flight_memory)>0:
|
1216
|
+
with open(fn,'w') as f:
|
1217
|
+
json.dump(self._flight_memory, f, indent=2)
|
1218
|
+
|
1219
|
+
def load_flight(self, fn:str):
|
1220
|
+
""" Load flight memory from file JSON """
|
1221
|
+
import json
|
1222
|
+
|
1223
|
+
if exists(fn):
|
1224
|
+
with open(fn,'r') as f:
|
1225
|
+
self._flight_memory = json.load(f)
|
1226
|
+
|
1227
|
+
|
1228
|
+
def _callback_props(self):
|
1229
|
+
|
1230
|
+
self._update_viewer()
|
1231
|
+
|
1232
|
+
def _callback_destroy_props(self):
|
1233
|
+
|
1234
|
+
if self._myprops is not None:
|
1235
|
+
# self._callback_props()
|
1236
|
+
self._myprops.Destroy()
|
1237
|
+
self._myprops = None
|
1238
|
+
|
1239
|
+
def _create_props(self):
|
1240
|
+
""" Create properties Wolf_Param for LAZ data """
|
1241
|
+
|
1242
|
+
if self._myprops is not None:
|
1243
|
+
return
|
1244
|
+
|
1245
|
+
self._myprops = Wolf_Param(None, title=_('Properties of ') + self.idx,
|
1246
|
+
to_read=False, force_even_if_same_default= True)
|
1247
|
+
|
1248
|
+
props = self._myprops
|
1249
|
+
|
1250
|
+
props.set_callbacks(self._callback_props, self._callback_destroy_props)
|
1251
|
+
props.hide_selected_buttons()
|
1252
|
+
|
1253
|
+
|
1254
|
+
ret = props.addparam('Camera', 'X', self.eye[0], Type_Param.Float, 'eye_x')
|
1255
|
+
ret = props.addparam('Camera', 'Y', self.eye[1], Type_Param.Float, 'eye_y')
|
1256
|
+
ret = props.addparam('Camera', 'Z', self.eye[2], Type_Param.Float, 'eye_z')
|
1257
|
+
|
1258
|
+
ret = props.addparam('Look at', 'X', self.lookat[0], Type_Param.Float, 'lookat_x')
|
1259
|
+
ret = props.addparam('Look at', 'Y', self.lookat[1], Type_Param.Float, 'lookat_y')
|
1260
|
+
ret = props.addparam('Look at', 'Z', self.lookat[2], Type_Param.Float, 'lookat_z')
|
1261
|
+
|
1262
|
+
ret = props.addparam('Relative Position', 'Phi', self.phi, Type_Param.Float, 'azimuthal angle (radians) - (phi, theta, r) are spherical coordinates specifying camera position relative to the look at position.')
|
1263
|
+
ret = props.addparam('Relative Position', 'Theta', self.theta, Type_Param.Float, 'elevation angle (radians) - (phi, theta, r) are spherical coordinates specifying camera position relative to the look at position.')
|
1264
|
+
ret = props.addparam('Relative Position', 'R', self.r, Type_Param.Float, 'distance to look-at point - (phi, theta, r) are spherical coordinates specifying camera position relative to the look at position.')
|
1265
|
+
|
1266
|
+
ret = props.addparam('Background', 'Color', self._bg_color, Type_Param.Color, 'Background color')
|
1267
|
+
ret = props.addparam('Background', 'Top Color', self._bg_color_top, Type_Param.Color, 'Top Background color')
|
1268
|
+
ret = props.addparam('Background', 'Bottom Color', self._bg_color_bottom, Type_Param.Color, 'Bottom Background color')
|
1269
|
+
|
1270
|
+
ret = props.addparam('Floor', 'Level', self._floor_level, Type_Param.Float, 'Floor level')
|
1271
|
+
ret = props.addparam('Floor', 'Color', self._floor_color, Type_Param.Color, 'Floor color')
|
1272
|
+
|
1273
|
+
ret = props.addparam('Infos', 'Grid', self._show_grid, Type_Param.Logical, 'Show grid')
|
1274
|
+
ret = props.addparam('Infos', 'Axis', self._show_axis, Type_Param.Logical, 'Show axis')
|
1275
|
+
ret = props.addparam('Infos', 'values', self._show_info, Type_Param.Logical, 'Show info')
|
1276
|
+
|
1277
|
+
ret = props.addparam('Points', 'Size', self._point_size, Type_Param.Float, 'Point size')
|
1278
|
+
|
1279
|
+
codes_sel = ''
|
1280
|
+
for curcode in self._select_only_codes:
|
1281
|
+
codes_sel += str(curcode) + ','
|
1282
|
+
ret = props.addparam('Selection', 'Codes', codes_sel, Type_Param.String, 'Codes to select')
|
1283
|
+
|
1284
|
+
props.Populate()
|
1285
|
+
|
1286
|
+
updatebutton = wx.Button(props, label=_('Get from viewer'))
|
1287
|
+
props.sizerbut.Add(updatebutton,1,wx.EXPAND)
|
1288
|
+
updatebutton.Bind(wx.EVT_BUTTON, self._fill_props)
|
1289
|
+
|
1290
|
+
getselection = wx.Button(props, label=_('Edit selection'))
|
1291
|
+
props.sizerbut.Add(getselection,1,wx.EXPAND)
|
1292
|
+
getselection.Bind(wx.EVT_BUTTON, self._OnEdit_Selection)
|
1293
|
+
|
1294
|
+
props.Layout()
|
1295
|
+
|
1296
|
+
def _set_new_xls(self):
|
1297
|
+
""" Create a new Excel grid for selected data """
|
1298
|
+
|
1299
|
+
self._xlsFrame = wx.Frame(None, wx.ID_ANY, _('Selected points - ') + self.idx)
|
1300
|
+
self._xls = CpGrid(self._xlsFrame, wx.ID_ANY, style = wx.WANTS_CHARS)
|
1301
|
+
|
1302
|
+
sizer = wx.BoxSizer(wx.VERTICAL)
|
1303
|
+
sizer.Add(self._xls, 1, wx.EXPAND)
|
1304
|
+
|
1305
|
+
nbclass = len(self.classification.classification)
|
1306
|
+
self._xls.CreateGrid(10, 4)
|
1307
|
+
|
1308
|
+
# Add a button to plot a histogram
|
1309
|
+
but_sizer = wx.BoxSizer(wx.HORIZONTAL)
|
1310
|
+
plotbutton = wx.Button(self._xlsFrame, label=_('Plot histogram (All data)'))
|
1311
|
+
but_sizer.Add(plotbutton, 1, wx.EXPAND)
|
1312
|
+
plotbutton.Bind(wx.EVT_BUTTON, self.OnPlot_histogram)
|
1313
|
+
|
1314
|
+
plotbutton2 = wx.Button(self._xlsFrame, label=_('Plot histogram (Grid data)'))
|
1315
|
+
but_sizer.Add(plotbutton2, 1, wx.EXPAND)
|
1316
|
+
plotbutton2.Bind(wx.EVT_BUTTON, self.OnPlot_histogram_grid)
|
1317
|
+
|
1318
|
+
sizer.Add(but_sizer, 0, wx.EXPAND)
|
1319
|
+
|
1320
|
+
self._xlsFrame.SetSizer(sizer)
|
1321
|
+
self._xlsFrame.Layout()
|
1322
|
+
self._xlsFrame.Show()
|
1323
|
+
|
1324
|
+
icon = wx.Icon()
|
1325
|
+
icon_path = Path(__file__).parent.parent / "apps/wolf_logo2.bmp"
|
1326
|
+
icon.CopyFromBitmap(wx.Bitmap(str(icon_path), wx.BITMAP_TYPE_ANY))
|
1327
|
+
self._xlsFrame.SetIcon(icon)
|
1328
|
+
|
1329
|
+
def OnPlot_histogram(self, event:wx.MouseEvent):
|
1330
|
+
""" Plot histogram of selected data """
|
1331
|
+
self._plot_histogram()
|
1332
|
+
|
1333
|
+
def OnPlot_histogram_grid(self, event:wx.MouseEvent):
|
1334
|
+
""" Plot histogram of selected data """
|
1335
|
+
self._plot_histogram_grid()
|
1336
|
+
|
1337
|
+
def _plot_histogram_grid(self):
|
1338
|
+
""" Histogram ONLY of selected data in grid.
|
1339
|
+
|
1340
|
+
The data are extracted based on the first column of the grid
|
1341
|
+
untile an empty cell is found.
|
1342
|
+
"""
|
1343
|
+
|
1344
|
+
xls = self._xls
|
1345
|
+
|
1346
|
+
if xls is None:
|
1347
|
+
logging.warning(_('No Excel grid'))
|
1348
|
+
return
|
1349
|
+
|
1350
|
+
# Find not null cells
|
1351
|
+
nbrows = 1
|
1352
|
+
|
1353
|
+
while xls.GetCellValue(nbrows,0) != '' and nbrows < xls.NumberRows:
|
1354
|
+
nbrows += 1
|
1355
|
+
|
1356
|
+
if nbrows == 1:
|
1357
|
+
logging.warning(_('Nt enough points selected'))
|
1358
|
+
return
|
1359
|
+
|
1360
|
+
xyz = np.zeros((nbrows,3))
|
1361
|
+
codes = np.zeros(nbrows)
|
1362
|
+
|
1363
|
+
try:
|
1364
|
+
for i in range(nbrows):
|
1365
|
+
xyz[i,0] = float(xls.GetCellValue(i,0))
|
1366
|
+
xyz[i,1] = float(xls.GetCellValue(i,1))
|
1367
|
+
xyz[i,2] = float(xls.GetCellValue(i,2))
|
1368
|
+
codes[i] = int(xls.GetCellValue(i,3))
|
1369
|
+
except Exception as e:
|
1370
|
+
logging.error(e)
|
1371
|
+
logging.warning(_('Bad values in grid - Check your input'))
|
1372
|
+
return
|
1373
|
+
|
1374
|
+
fig = plt.figure()
|
1375
|
+
|
1376
|
+
ax = fig.add_subplot(111)
|
1377
|
+
|
1378
|
+
ax.hist(xyz[:,2], bins=256)
|
1379
|
+
|
1380
|
+
fig.show()
|
1381
|
+
|
1382
|
+
def _plot_histogram(self):
|
1383
|
+
""" """
|
1384
|
+
|
1385
|
+
xyz = self.xyz_selected
|
1386
|
+
|
1387
|
+
if xyz.shape[0]==0:
|
1388
|
+
logging.warning(_('No points selected'))
|
1389
|
+
return
|
1390
|
+
|
1391
|
+
fig = plt.figure()
|
1392
|
+
|
1393
|
+
ax = fig.add_subplot(111)
|
1394
|
+
|
1395
|
+
ax.hist(xyz[:,2], bins=256)
|
1396
|
+
|
1397
|
+
fig.show()
|
1398
|
+
|
1399
|
+
def _selection2vector(self):
|
1400
|
+
""" FIXME: must use RANSAC to compute a segment from the selected points """
|
1401
|
+
|
1402
|
+
if self.viewer is None:
|
1403
|
+
logging.warning(_('No viewer'))
|
1404
|
+
return
|
1405
|
+
|
1406
|
+
xyz = self.xyz_selected
|
1407
|
+
|
1408
|
+
if xyz.shape[0]==0:
|
1409
|
+
logging.warning(_('No points selected'))
|
1410
|
+
return
|
1411
|
+
|
1412
|
+
vect = vector(name = self.idx + '_selection', fromnumpy=xyz)
|
1413
|
+
|
1414
|
+
return vect
|
1415
|
+
|
1416
|
+
|
1417
|
+
def _OnEdit_Selection(self, event:wx.MouseEvent):
|
1418
|
+
""" Get selection from viewer and create a XLS grid """
|
1419
|
+
self._edit_selection()
|
1420
|
+
|
1421
|
+
def _edit_selection(self):
|
1422
|
+
|
1423
|
+
if self.viewer is None:
|
1424
|
+
logging.warning(_('No viewer'))
|
1425
|
+
return
|
1426
|
+
|
1427
|
+
xyz = self.xyz_selected
|
1428
|
+
|
1429
|
+
if xyz.shape[0]==0:
|
1430
|
+
logging.warning(_('No points selected'))
|
1431
|
+
return
|
1432
|
+
|
1433
|
+
if self._xls is None:
|
1434
|
+
self._set_new_xls()
|
1435
|
+
else:
|
1436
|
+
try:
|
1437
|
+
self._xls.ClearGrid()
|
1438
|
+
except:
|
1439
|
+
#Useful if the grid is already destroyed
|
1440
|
+
self._set_new_xls()
|
1441
|
+
|
1442
|
+
nbclass = len(self.classification.classification)
|
1443
|
+
min_rows = xyz.shape[0] + nbclass +1
|
1444
|
+
if self._xls.NumberRows < min_rows:
|
1445
|
+
self._xls.AppendRows(min_rows - self._xls.NumberRows)
|
1446
|
+
|
1447
|
+
self._xls.SetColLabelValue(0, 'X')
|
1448
|
+
self._xls.SetColLabelValue(1, 'Y')
|
1449
|
+
self._xls.SetColLabelValue(2, 'Z')
|
1450
|
+
self._xls.SetColLabelValue(3, 'Code')
|
1451
|
+
|
1452
|
+
codes = self.code_selected
|
1453
|
+
|
1454
|
+
for i in range(xyz.shape[0]):
|
1455
|
+
self._xls.SetCellValue(i,0,str(xyz[i,0]))
|
1456
|
+
self._xls.SetCellValue(i,1,str(xyz[i,1]))
|
1457
|
+
self._xls.SetCellValue(i,2,str(xyz[i,2]))
|
1458
|
+
self._xls.SetCellValue(i,3,str(codes[i]))
|
1459
|
+
|
1460
|
+
# Copy classification under the values
|
1461
|
+
for i, (key, val) in enumerate(self.classification.classification.items()):
|
1462
|
+
self._xls.SetCellValue(xyz.shape[0]+i+1,0,str(key))
|
1463
|
+
self._xls.SetCellValue(xyz.shape[0]+i+1,1,val[0])
|
1464
|
+
self._xls.SetCellValue(xyz.shape[0]+i+1,2,val[1])
|
1465
|
+
self._xls.SetCellValue(xyz.shape[0]+i+1,3,str(key))
|
1466
|
+
|
1467
|
+
self._xlsFrame.Show()
|
1468
|
+
|
1469
|
+
# Mettre la fenêtre au premier plan et centrée
|
1470
|
+
self._xlsFrame.Raise()
|
1471
|
+
self._xlsFrame.Center()
|
1472
|
+
|
1473
|
+
def _update_viewer(self):
|
1474
|
+
""" Update the viewer with properties """
|
1475
|
+
|
1476
|
+
if self._myprops is None:
|
1477
|
+
return
|
1478
|
+
|
1479
|
+
if self.viewer is None:
|
1480
|
+
return
|
1481
|
+
|
1482
|
+
props = self._myprops
|
1483
|
+
|
1484
|
+
self.lookat = (props[('Look at', 'X')], props[('Look at', 'Y')], props[('Look at', 'Z')])
|
1485
|
+
self.eye = (props[('Camera', 'X')], props[('Camera', 'Y')], props[('Camera', 'Z')])
|
1486
|
+
|
1487
|
+
color = np.asarray(props[('Background', 'Color')])
|
1488
|
+
self.bg_color(color / 255.)
|
1489
|
+
color = np.asarray(props[('Background', 'Top Color')])
|
1490
|
+
self.bg_color_top(color / 255.)
|
1491
|
+
color = np.asarray(props[('Background', 'Bottom Color')])
|
1492
|
+
self.bg_color_bottom(color / 255.)
|
1493
|
+
|
1494
|
+
self.floor_level(props[('Floor', 'Level')])
|
1495
|
+
color = np.asarray(props[('Floor', 'Color')])
|
1496
|
+
self.floor_color(color / 255.)
|
1497
|
+
|
1498
|
+
self.show_grid(props[('Infos', 'Grid')])
|
1499
|
+
self.show_axis(props[('Infos', 'Axis')])
|
1500
|
+
self.show_info(props[('Infos', 'values')])
|
1501
|
+
|
1502
|
+
self.point_size = props[('Points', 'Size')]
|
1503
|
+
|
1504
|
+
codes_sel = props[('Selection', 'Codes')]
|
1505
|
+
codes_sel = codes_sel.split(',')
|
1506
|
+
try:
|
1507
|
+
self._select_only_codes = list(set([int(curcode) for curcode in codes_sel]))
|
1508
|
+
except Exception as e:
|
1509
|
+
logging.error(e)
|
1510
|
+
logging.warning(_('Nullify selection filter - Check your input'))
|
1511
|
+
self._select_only_codes = []
|
1512
|
+
|
1513
|
+
def _fill_props(self, full:bool = False):
|
1514
|
+
""" Fill properties from attributes """
|
1515
|
+
if self._myprops is None:
|
1516
|
+
return
|
1517
|
+
|
1518
|
+
props = self._myprops
|
1519
|
+
|
1520
|
+
props[('Look at', 'X')] = self.lookat[0]
|
1521
|
+
props[('Look at', 'Y')] = self.lookat[1]
|
1522
|
+
props[('Look at', 'Z')] = self.lookat[2]
|
1523
|
+
|
1524
|
+
props[('Camera', 'X')] = self.eye[0]
|
1525
|
+
props[('Camera', 'Y')] = self.eye[1]
|
1526
|
+
props[('Camera', 'Z')] = self.eye[2]
|
1527
|
+
|
1528
|
+
props[('Relative Position', 'Phi')] = self.phi
|
1529
|
+
props[('Relative Position', 'Theta')] = self.theta
|
1530
|
+
props[('Relative Position', 'R')] = self.r
|
1531
|
+
|
1532
|
+
if full:
|
1533
|
+
|
1534
|
+
props[('Background', 'Color')] = self._bg_color
|
1535
|
+
props[('Background', 'Top Color')] = self._bg_color_top
|
1536
|
+
props[('Background', 'Bottom Color')] = self._bg_color_bottom
|
1537
|
+
|
1538
|
+
props[('Floor', 'Level')] = self._floor_level
|
1539
|
+
props[('Floor', 'Color')] = self._floor_color
|
1540
|
+
|
1541
|
+
props[('Infos', 'Grid')] = self._show_grid
|
1542
|
+
props[('Infos', 'Axis')] = self._show_axis
|
1543
|
+
props[('Infos', 'values')] = self._show_info
|
1544
|
+
|
1545
|
+
props[('Points', 'Size')] = self._point_size
|
1546
|
+
|
1547
|
+
props.Populate()
|
1548
|
+
|
1549
|
+
def show_properties(self):
|
1550
|
+
""" Surcharged method (see Element_To_Draw) to show properties from MapViewer"""
|
1551
|
+
if self.viewer is None:
|
1552
|
+
return
|
1553
|
+
|
1554
|
+
self._create_props()
|
1555
|
+
self._myprops.Show()
|
1556
|
+
|
685
1557
|
def find_pointsXYZ(xyz:np.ndarray, bounds:Union[tuple[tuple[float,float],tuple[float,float]], list[list[float, float],list[float, float]]]) -> np.ndarray:
|
686
1558
|
|
687
1559
|
xb=bounds[0]
|
@@ -1096,7 +1968,7 @@ def myviewer(las:Union[np.ndarray, list[laspy.LasData], laspy.LasData], which_co
|
|
1096
1968
|
|
1097
1969
|
colors = get_colors(las, which_colors, fname=fname, palette_classif= palette_classif)
|
1098
1970
|
|
1099
|
-
v=viewer(xyz,colors)
|
1971
|
+
v = viewer(xyz,colors)
|
1100
1972
|
v.set(point_size=.05)
|
1101
1973
|
return v
|
1102
1974
|
|