wolfhece 2.1.117__py3-none-any.whl → 2.1.118__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 +359 -79
- wolfhece/apps/version.py +1 -1
- wolfhece/lazviewer/laz_viewer.py +647 -10
- wolfhece/lazviewer/viewer/viewer.py +2 -2
- wolfhece/wolf_array.py +278 -5
- wolfhece/wolf_vrt.py +18 -17
- {wolfhece-2.1.117.dist-info → wolfhece-2.1.118.dist-info}/METADATA +1 -1
- {wolfhece-2.1.117.dist-info → wolfhece-2.1.118.dist-info}/RECORD +12 -12
- {wolfhece-2.1.117.dist-info → wolfhece-2.1.118.dist-info}/WHEEL +0 -0
- {wolfhece-2.1.117.dist-info → wolfhece-2.1.118.dist-info}/entry_points.txt +0 -0
- {wolfhece-2.1.117.dist-info → wolfhece-2.1.118.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,10 @@ 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
|
25
|
+
from ..PyParams import Wolf_Param, Type_Param
|
26
26
|
from ..matplotlib_fig import Matplotlib_Figure as mplfig
|
27
|
+
from ..drawing_obj import Element_To_Draw
|
28
|
+
from ..CpGrid import CpGrid
|
27
29
|
|
28
30
|
"""
|
29
31
|
Importation et visualisation de données LAS et LAZ
|
@@ -49,6 +51,7 @@ class Classification_LAZ():
|
|
49
51
|
self.test_wx()
|
50
52
|
|
51
53
|
self._choose_colors = None
|
54
|
+
self._viewer = None
|
52
55
|
|
53
56
|
def test_wx(self):
|
54
57
|
self.wx_exists = wx.App.Get() is not None
|
@@ -76,6 +79,7 @@ class Classification_LAZ():
|
|
76
79
|
9 : ['Eau', 'Points de la surface d\’eau brute mesurés par le scanner 2',Colors.rgb_withalpha_float('royalblue',.3)],
|
77
80
|
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
81
|
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.)],
|
82
|
+
13 : ['Inconnu', 'A vérifier auSPW', Colors.rgb_withalpha_float('lightslategray',.2)],
|
79
83
|
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
84
|
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
85
|
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 +150,7 @@ class xyz_laz():
|
|
146
150
|
self.data = []
|
147
151
|
return
|
148
152
|
|
149
|
-
last_part =
|
153
|
+
last_part = Path(fn).name
|
150
154
|
parts = last_part.split('_')
|
151
155
|
|
152
156
|
# L'origine est codée dans le nom de fichier
|
@@ -157,7 +161,7 @@ class xyz_laz():
|
|
157
161
|
dy = 3500.
|
158
162
|
|
159
163
|
# Récupération du lien vers le fichier GridInfo.txt
|
160
|
-
gridinfo=join(
|
164
|
+
gridinfo=join(Path(fn).parent,'gridinfo.txt')
|
161
165
|
|
162
166
|
if exists(gridinfo):
|
163
167
|
with open(gridinfo,'r') as f:
|
@@ -244,8 +248,8 @@ class xyz_laz():
|
|
244
248
|
if exists(fn+'.zip'):
|
245
249
|
with zipfile.ZipFile(fn+'.zip','r') as zip_file:
|
246
250
|
fn = zip_file.namelist()[0]
|
247
|
-
zip_file.extract(fn, path=
|
248
|
-
fn = join(
|
251
|
+
zip_file.extract(fn, path=Path(fn).parent)
|
252
|
+
fn = join(Path(fn).parent, fn)
|
249
253
|
elif not exists(fn):
|
250
254
|
return
|
251
255
|
|
@@ -395,7 +399,7 @@ class xyz_laz_grid():
|
|
395
399
|
xloc = np.linspace(xbounds[0], xbounds[1], gridsize[0]+1)
|
396
400
|
yloc = np.linspace(ybounds[0], ybounds[1], gridsize[1]+1)
|
397
401
|
|
398
|
-
dirout =
|
402
|
+
dirout = Path(fn_out).parent
|
399
403
|
fn=join(dirout,'gridinfo.txt')
|
400
404
|
|
401
405
|
if exists(fn):
|
@@ -405,7 +409,7 @@ class xyz_laz_grid():
|
|
405
409
|
f.write(str(int(xbounds[0]))+','+str(int(xbounds[1]))+'\n')
|
406
410
|
f.write(str(int(ybounds[0]))+','+str(int(ybounds[1]))+'\n')
|
407
411
|
f.write(str(int(gridsize[0]))+','+str(int(gridsize[1]))+'\n')
|
408
|
-
f.write(
|
412
|
+
f.write(Path(fn_out).name+'_'+'x1'+'_'+'y1'+'_xyz.bin'+'\n')
|
409
413
|
if force_format == np.float64:
|
410
414
|
f.write('xy_float64')
|
411
415
|
else:
|
@@ -481,7 +485,7 @@ class xyz_laz_grid():
|
|
481
485
|
if force_format == np.float64:
|
482
486
|
fnzip = fn + '.zip'
|
483
487
|
with zipfile.ZipFile(fnzip,'w',zipfile.ZIP_DEFLATED) as zip_file:
|
484
|
-
zip_file.write(fn,
|
488
|
+
zip_file.write(fn, Path(fn).name)
|
485
489
|
remove(fn)
|
486
490
|
|
487
491
|
|
@@ -682,6 +686,639 @@ class xyz_laz_grids():
|
|
682
686
|
gridsize=[max(int(dx/ds),1), max(int(dy/ds),1)],
|
683
687
|
force_format=force_format))
|
684
688
|
|
689
|
+
|
690
|
+
class Base_LAZ_Data(Element_To_Draw):
|
691
|
+
|
692
|
+
def __init__(self, idx:str = '', plotted:bool = False, mapviewer = None, need_for_wx:bool = False) -> None:
|
693
|
+
|
694
|
+
self._bounds = [[0.,0.],[0.,0.]] # [[xmin,xmax],[ymin,ymax]]
|
695
|
+
super().__init__(idx, plotted, mapviewer, need_for_wx)
|
696
|
+
|
697
|
+
self.lazfile:Path = None
|
698
|
+
self._data:np.ndarray = None
|
699
|
+
self.classification = Classification_LAZ()
|
700
|
+
|
701
|
+
self.associated_color:int = Colors_Lazviewer.CODE_2023.value
|
702
|
+
|
703
|
+
self.viewer:viewer = None
|
704
|
+
self._colors:np.ndarray = None
|
705
|
+
|
706
|
+
self._point_size = .05
|
707
|
+
|
708
|
+
self._memory = []
|
709
|
+
|
710
|
+
self._myprops = None
|
711
|
+
|
712
|
+
self._bg_color = [0,0,0,255]
|
713
|
+
self._bg_color_top = [0,0,0,255]
|
714
|
+
self._bg_color_bottom = [0,0,0,255]
|
715
|
+
|
716
|
+
self._floor_level = 0.
|
717
|
+
self._floor_color = [0,0,0,255]
|
718
|
+
|
719
|
+
self._show_grid = True
|
720
|
+
self._show_axis = True
|
721
|
+
self._show_info = True
|
722
|
+
|
723
|
+
self._select_only_codes = []
|
724
|
+
|
725
|
+
self._xls:CpGrid = None
|
726
|
+
self._xlsFrame:wx.Frame = None
|
727
|
+
|
728
|
+
# self._auto_update_properties = False
|
729
|
+
# self._auto_update_interval = 100 # ms
|
730
|
+
# self._timer = None
|
731
|
+
|
732
|
+
def filter_data(self, codes:list[int]):
|
733
|
+
self._data = self._data[np.isin(self._data[:,3],codes)]
|
734
|
+
|
735
|
+
def bg_color(self, value):
|
736
|
+
if self.viewer is not None:
|
737
|
+
return self.viewer.set(bg_color = value)
|
738
|
+
|
739
|
+
def bg_color_top(self, value):
|
740
|
+
if self.viewer is not None:
|
741
|
+
return self.viewer.set(bg_color_top = value)
|
742
|
+
|
743
|
+
def bg_color_bottom(self, value):
|
744
|
+
if self.viewer is not None:
|
745
|
+
return self.viewer.set(bg_color_bottom = value)
|
746
|
+
|
747
|
+
def floor_level(self, value):
|
748
|
+
if self.viewer is not None:
|
749
|
+
return self.viewer.set(floor_level = value)
|
750
|
+
|
751
|
+
def floor_color(self, value):
|
752
|
+
if self.viewer is not None:
|
753
|
+
return self.viewer.set(floor_color = value)
|
754
|
+
|
755
|
+
def show_grid(self, value:bool):
|
756
|
+
if self.viewer is not None:
|
757
|
+
return self.viewer.set(show_grid = value)
|
758
|
+
|
759
|
+
def show_axis(self, value:bool):
|
760
|
+
if self.viewer is not None:
|
761
|
+
return self.viewer.set(show_axis = value)
|
762
|
+
|
763
|
+
def show_info(self, value:bool):
|
764
|
+
if self.viewer is not None:
|
765
|
+
return self.viewer.set(show_info = value)
|
766
|
+
|
767
|
+
@property
|
768
|
+
def selected(self):
|
769
|
+
if self.viewer is None:
|
770
|
+
return None
|
771
|
+
return self.viewer.get('selected')
|
772
|
+
|
773
|
+
@property
|
774
|
+
def xyz_selected(self) -> np.ndarray:
|
775
|
+
if self.viewer is None:
|
776
|
+
return None
|
777
|
+
|
778
|
+
if self.selected.shape[0] == 0:
|
779
|
+
return np.ndarray((0,3))
|
780
|
+
|
781
|
+
if len(self._select_only_codes)>0:
|
782
|
+
return self.data[self.selected,:3][np.isin(self.data[self.selected,3],self._select_only_codes)]
|
783
|
+
else:
|
784
|
+
return self.data[self.selected,:3]
|
785
|
+
|
786
|
+
@property
|
787
|
+
def code_selected(self) -> np.ndarray:
|
788
|
+
if self.viewer is None:
|
789
|
+
return None
|
790
|
+
|
791
|
+
if self.selected.shape[0] == 0:
|
792
|
+
return np.ndarray((0,1))
|
793
|
+
|
794
|
+
if len(self._select_only_codes)>0:
|
795
|
+
return self.data[self.selected,3][np.isin(self.data[self.selected,3],self._select_only_codes)]
|
796
|
+
else:
|
797
|
+
return self.data[self.selected,3]
|
798
|
+
|
799
|
+
@property
|
800
|
+
def num_points(self):
|
801
|
+
""" Number of points """
|
802
|
+
nb1 = self.data.shape[0]
|
803
|
+
if self.viewer is not None:
|
804
|
+
nb2 = self.viewer.get('num_points')[0]
|
805
|
+
assert nb1 == nb2, _('Incoherent number of points')
|
806
|
+
|
807
|
+
return nb1
|
808
|
+
|
809
|
+
@property
|
810
|
+
def nb_points(self):
|
811
|
+
""" Number of points """
|
812
|
+
return self.num_points
|
813
|
+
|
814
|
+
@property
|
815
|
+
def nb(self):
|
816
|
+
""" Number of points """
|
817
|
+
return self.num_points
|
818
|
+
|
819
|
+
@property
|
820
|
+
def right(self):
|
821
|
+
"""Camera Right vector """
|
822
|
+
return self.viewer.get('right')
|
823
|
+
|
824
|
+
@property
|
825
|
+
def mvp(self):
|
826
|
+
if self.viewer is None:
|
827
|
+
return None
|
828
|
+
return self.viewer.get('mvp')
|
829
|
+
|
830
|
+
@property
|
831
|
+
def eye(self):
|
832
|
+
if self.viewer is None:
|
833
|
+
return None
|
834
|
+
return self.viewer.get('eye')
|
835
|
+
|
836
|
+
@property
|
837
|
+
def lookat(self):
|
838
|
+
if self.viewer is None:
|
839
|
+
return None
|
840
|
+
return self.viewer.get('lookat')
|
841
|
+
|
842
|
+
@property
|
843
|
+
def phi(self):
|
844
|
+
if self.viewer is None:
|
845
|
+
return None
|
846
|
+
return self.viewer.get('phi')[0]
|
847
|
+
|
848
|
+
@property
|
849
|
+
def theta(self):
|
850
|
+
if self.viewer is None:
|
851
|
+
return None
|
852
|
+
return self.viewer.get('theta')[0]
|
853
|
+
|
854
|
+
@property
|
855
|
+
def r(self):
|
856
|
+
if self.viewer is None:
|
857
|
+
return None
|
858
|
+
return self.viewer.get('r')[0]
|
859
|
+
|
860
|
+
@phi.setter
|
861
|
+
def phi(self, value):
|
862
|
+
if self.viewer is None:
|
863
|
+
return None
|
864
|
+
self.viewer.set(phi=value)
|
865
|
+
|
866
|
+
@theta.setter
|
867
|
+
def theta(self, value):
|
868
|
+
if self.viewer is None:
|
869
|
+
return None
|
870
|
+
self.viewer.set(theta=value)
|
871
|
+
|
872
|
+
@r.setter
|
873
|
+
def r(self, value):
|
874
|
+
if self.viewer is None:
|
875
|
+
return None
|
876
|
+
self.viewer.set(r=value)
|
877
|
+
|
878
|
+
@lookat.setter
|
879
|
+
def lookat(self, value):
|
880
|
+
if self.viewer is None:
|
881
|
+
return None
|
882
|
+
self.viewer.set(lookat=value)
|
883
|
+
|
884
|
+
@eye.setter
|
885
|
+
def eye(self, value):
|
886
|
+
if self.viewer is None:
|
887
|
+
return None
|
888
|
+
x,y,z = value
|
889
|
+
lx, ly, lz = self.lookat
|
890
|
+
right = self.right
|
891
|
+
|
892
|
+
# Compute phi, theta, r based on eye and lookat and right vector
|
893
|
+
r = np.sqrt((x-lx)**2 + (y-ly)**2 + (z-lz)**2)
|
894
|
+
self.r =r
|
895
|
+
self.phi = np.arccos((z-lz)/r)
|
896
|
+
self.theta = np.arctan2((y-ly),(x-lx))
|
897
|
+
|
898
|
+
@property
|
899
|
+
def point_size(self):
|
900
|
+
return self._point_size
|
901
|
+
|
902
|
+
@point_size.setter
|
903
|
+
def point_size(self, value):
|
904
|
+
self._point_size = value
|
905
|
+
if self.viewer is not None:
|
906
|
+
self.viewer.set(point_size=value)
|
907
|
+
|
908
|
+
@property
|
909
|
+
def xyz(self):
|
910
|
+
return self.data[:,:3]
|
911
|
+
|
912
|
+
@property
|
913
|
+
def codes(self):
|
914
|
+
return self.data[:,3]
|
915
|
+
|
916
|
+
def codes_unique(self):
|
917
|
+
return list(np.unique(self.codes).astype(int))
|
918
|
+
|
919
|
+
def create_viewer(self, color_code:Colors_Lazviewer = None, classification:Classification_LAZ = None):
|
920
|
+
""" Create a viewer for las data """
|
921
|
+
|
922
|
+
if color_code is not None:
|
923
|
+
self.associated_color = color_code
|
924
|
+
|
925
|
+
if classification is not None:
|
926
|
+
self.classification = classification
|
927
|
+
|
928
|
+
self._colors = get_colors(self.data, self.associated_color, palette_classif= self.classification)
|
929
|
+
|
930
|
+
self.viewer = viewer(self.xyz, self._colors)
|
931
|
+
self.viewer.set(point_size= self._point_size)
|
932
|
+
|
933
|
+
return self.viewer
|
934
|
+
|
935
|
+
def interactive_update_colors(self):
|
936
|
+
|
937
|
+
self.classification.interactive_update_colors()
|
938
|
+
|
939
|
+
self.classification._choose_colors.SetTitle(_('Colors of ') + self.idx)
|
940
|
+
|
941
|
+
def new_callback_colors():
|
942
|
+
self.classification.callback_colors()
|
943
|
+
self.set_colors()
|
944
|
+
|
945
|
+
self.classification._choose_colors.callback = new_callback_colors
|
946
|
+
|
947
|
+
def set_colors(self):
|
948
|
+
if self.viewer is not None:
|
949
|
+
self._colors = get_colors(self.data, self.associated_color, palette_classif= self.classification)
|
950
|
+
self.viewer.attributes(self._colors)
|
951
|
+
|
952
|
+
def set_classification(self, classification:str = None):
|
953
|
+
|
954
|
+
if classification is None:
|
955
|
+
logging.warning(_('No classification chosen - Abort !'))
|
956
|
+
elif classification == 'SPW 2013-2014':
|
957
|
+
self.classification.init_2013()
|
958
|
+
else:
|
959
|
+
self.classification.init_2023()
|
960
|
+
|
961
|
+
def find_minmax(self, update=False):
|
962
|
+
if self.data is not None:
|
963
|
+
self.bounds = [[np.min(self.data[:,0]), np.max(self.data[:,0])],[np.min(self.data[:,1]), np.max(self.data[:,1])]]
|
964
|
+
|
965
|
+
@property
|
966
|
+
def xmin(self):
|
967
|
+
return self.bounds[0][0]
|
968
|
+
|
969
|
+
@xmin.setter
|
970
|
+
def xmin(self, value):
|
971
|
+
self._bounds[0][0] = value
|
972
|
+
|
973
|
+
@property
|
974
|
+
def xmax(self):
|
975
|
+
return self.bounds[0][1]
|
976
|
+
|
977
|
+
@xmax.setter
|
978
|
+
def xmax(self, value):
|
979
|
+
self._bounds[0][1] = value
|
980
|
+
|
981
|
+
@property
|
982
|
+
def ymin(self):
|
983
|
+
return self.bounds[1][0]
|
984
|
+
|
985
|
+
@ymin.setter
|
986
|
+
def ymin(self, value):
|
987
|
+
self._bounds[1][0] = value
|
988
|
+
|
989
|
+
@property
|
990
|
+
def ymax(self):
|
991
|
+
return self.bounds[1][1]
|
992
|
+
|
993
|
+
@ymax.setter
|
994
|
+
def ymax(self, value):
|
995
|
+
self._bounds[1][1] = value
|
996
|
+
|
997
|
+
@property
|
998
|
+
def data(self):
|
999
|
+
return self._data
|
1000
|
+
|
1001
|
+
@data.setter
|
1002
|
+
def data(self, value):
|
1003
|
+
self._data = value
|
1004
|
+
|
1005
|
+
@property
|
1006
|
+
def bounds(self):
|
1007
|
+
return self._bounds
|
1008
|
+
|
1009
|
+
@bounds.setter
|
1010
|
+
def bounds(self, value):
|
1011
|
+
self._bounds = value
|
1012
|
+
|
1013
|
+
def from_grid(self, grid:xyz_laz_grid, bounds:Union[tuple[tuple[float,float],tuple[float,float]], list[list[float, float],list[float, float]]]):
|
1014
|
+
|
1015
|
+
self.bounds = bounds
|
1016
|
+
self.data = grid.scan(bounds)
|
1017
|
+
|
1018
|
+
def from_file(self, fn:str):
|
1019
|
+
|
1020
|
+
self.data = read_laz(fn)
|
1021
|
+
self.bounds = [[np.min(self.data[:,0]), np.max(self.data[:,0])],[np.min(self.data[:,1]), np.max(self.data[:,1])]]
|
1022
|
+
|
1023
|
+
def decimate(self, step:int):
|
1024
|
+
self.data = self.data[::step]
|
1025
|
+
|
1026
|
+
def get_data_class(self, key:int):
|
1027
|
+
return self.data[self.data[:,3] == key]
|
1028
|
+
|
1029
|
+
def add_pose_in_memory(self, key_time:float = 1.):
|
1030
|
+
if self.viewer is not None:
|
1031
|
+
lookat = self.lookat
|
1032
|
+
new_pose = (lookat[0], lookat[1], lookat[2], self.phi, self.theta, self.r)
|
1033
|
+
self._memory.append((new_pose, key_time))
|
1034
|
+
|
1035
|
+
def play_flight(self, tlim=[-np.inf, np.inf], repeat=False, interp='cubic_natural'):
|
1036
|
+
|
1037
|
+
if self.viewer is not None:
|
1038
|
+
if len(self._memory)>0:
|
1039
|
+
poses = [cur[0] for cur in self._memory]
|
1040
|
+
times = [0.]
|
1041
|
+
for i in range(1,len(self._memory)):
|
1042
|
+
times.append(times[-1]+self._memory[i][1])
|
1043
|
+
self.viewer.play(poses, times, tlim, repeat, interp)
|
1044
|
+
|
1045
|
+
def set_times(self, times:np.ndarray):
|
1046
|
+
if len(self._memory)>0:
|
1047
|
+
self._memory = [(self._memory[i][0], times[i]) for i in range(len(self._memory))]
|
1048
|
+
|
1049
|
+
def set_times_increment(self, increment:float):
|
1050
|
+
if len(self._memory)>0:
|
1051
|
+
self._memory = [(self._memory[i][0], increment*i) for i in range(len(self._memory))]
|
1052
|
+
|
1053
|
+
def get_times(self):
|
1054
|
+
return np.asarray([cur[1] for cur in self._memory])
|
1055
|
+
|
1056
|
+
def record_flight(self, dirout:str, tlim=[-np.inf, np.inf], interp='cubic_natural', fps=24, prefix:str = 'laz_', ext:str = '.png'):
|
1057
|
+
if self.viewer is not None:
|
1058
|
+
if len(self._memory)>0:
|
1059
|
+
poses = [cur[0] for cur in self._memory]
|
1060
|
+
times = [0.]
|
1061
|
+
for i in range(1,len(self._memory)):
|
1062
|
+
times.append(times[-1]+self._memory[i][1])
|
1063
|
+
self.viewer.record(dirout, poses, times, tlim, interp, fps=fps, prefix=prefix, ext=ext)
|
1064
|
+
|
1065
|
+
def save_flight(self, fn:str):
|
1066
|
+
""" Write memory to file JSON """
|
1067
|
+
import json
|
1068
|
+
|
1069
|
+
if len(self._memory)>0:
|
1070
|
+
with open(fn,'w') as f:
|
1071
|
+
json.dump(self._memory, f, indent=2)
|
1072
|
+
|
1073
|
+
def load_flight(self, fn:str):
|
1074
|
+
""" Load memory from file JSON """
|
1075
|
+
import json
|
1076
|
+
|
1077
|
+
if exists(fn):
|
1078
|
+
with open(fn,'r') as f:
|
1079
|
+
self._memory = json.load(f)
|
1080
|
+
|
1081
|
+
|
1082
|
+
def _callback_props(self):
|
1083
|
+
|
1084
|
+
self._update_props()
|
1085
|
+
|
1086
|
+
def _callback_destroy_props(self):
|
1087
|
+
|
1088
|
+
if self._myprops is not None:
|
1089
|
+
self._callback_props()
|
1090
|
+
self._myprops.Destroy()
|
1091
|
+
self._myprops = None
|
1092
|
+
|
1093
|
+
def _create_props(self):
|
1094
|
+
|
1095
|
+
if self._myprops is not None:
|
1096
|
+
return
|
1097
|
+
|
1098
|
+
self._myprops = Wolf_Param(None, title=_('Properties of ') + self.idx,
|
1099
|
+
to_read=False, force_even_if_same_default= True)
|
1100
|
+
|
1101
|
+
props = self._myprops
|
1102
|
+
|
1103
|
+
props.set_callbacks(self._callback_props, self._callback_destroy_props)
|
1104
|
+
props.hide_selected_buttons()
|
1105
|
+
|
1106
|
+
|
1107
|
+
ret = props.addparam('Camera', 'X', self.eye[0], Type_Param.Float, 'eye_x')
|
1108
|
+
ret = props.addparam('Camera', 'Y', self.eye[1], Type_Param.Float, 'eye_y')
|
1109
|
+
ret = props.addparam('Camera', 'Z', self.eye[2], Type_Param.Float, 'eye_z')
|
1110
|
+
|
1111
|
+
ret = props.addparam('Look at', 'X', self.lookat[0], Type_Param.Float, 'lookat_x')
|
1112
|
+
ret = props.addparam('Look at', 'Y', self.lookat[1], Type_Param.Float, 'lookat_y')
|
1113
|
+
ret = props.addparam('Look at', 'Z', self.lookat[2], Type_Param.Float, 'lookat_z')
|
1114
|
+
|
1115
|
+
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.')
|
1116
|
+
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.')
|
1117
|
+
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.')
|
1118
|
+
|
1119
|
+
ret = props.addparam('Background', 'Color', self._bg_color, Type_Param.Color, 'Background color')
|
1120
|
+
ret = props.addparam('Background', 'Top Color', self._bg_color_top, Type_Param.Color, 'Top Background color')
|
1121
|
+
ret = props.addparam('Background', 'Bottom Color', self._bg_color_bottom, Type_Param.Color, 'Bottom Background color')
|
1122
|
+
|
1123
|
+
ret = props.addparam('Floor', 'Level', self._floor_level, Type_Param.Float, 'Floor level')
|
1124
|
+
ret = props.addparam('Floor', 'Color', self._floor_color, Type_Param.Color, 'Floor color')
|
1125
|
+
|
1126
|
+
ret = props.addparam('Infos', 'Grid', self._show_grid, Type_Param.Logical, 'Show grid')
|
1127
|
+
ret = props.addparam('Infos', 'Axis', self._show_axis, Type_Param.Logical, 'Show axis')
|
1128
|
+
ret = props.addparam('Infos', 'values', self._show_info, Type_Param.Logical, 'Show info')
|
1129
|
+
|
1130
|
+
ret = props.addparam('Points', 'Size', self._point_size, Type_Param.Float, 'Point size')
|
1131
|
+
|
1132
|
+
codes_sel = ''
|
1133
|
+
for curcode in self._select_only_codes:
|
1134
|
+
codes_sel += str(curcode) + ','
|
1135
|
+
ret = props.addparam('Selection', 'Codes', codes_sel, Type_Param.String, 'Codes to select')
|
1136
|
+
|
1137
|
+
# ret = props.addparam('Auto Update', 'Active', self._auto_update_properties, Type_Param.Logical, 'Auto update properties')
|
1138
|
+
# ret = props.addparam('Auto Update', 'Interval', self._auto_update_interval, Type_Param.Integer, 'Auto update interval (ms)')
|
1139
|
+
|
1140
|
+
props.Populate()
|
1141
|
+
|
1142
|
+
updatebutton = wx.Button(props, label=_('Update from viewer'))
|
1143
|
+
props.sizerbut.Add(updatebutton,1,wx.EXPAND)
|
1144
|
+
props.Bind(wx.EVT_BUTTON, self._fill_props, updatebutton)
|
1145
|
+
|
1146
|
+
getselection = wx.Button(props, label=_('Get selection'))
|
1147
|
+
props.sizerbut.Add(getselection,1,wx.EXPAND)
|
1148
|
+
props.Bind(wx.EVT_BUTTON, self._get_selection, getselection)
|
1149
|
+
|
1150
|
+
props.Layout()
|
1151
|
+
|
1152
|
+
def _get_selection(self, event):
|
1153
|
+
|
1154
|
+
if self.viewer is None:
|
1155
|
+
logging.warning(_('No viewer'))
|
1156
|
+
return
|
1157
|
+
|
1158
|
+
xyz = self.xyz_selected
|
1159
|
+
|
1160
|
+
if xyz.shape[0]==0:
|
1161
|
+
logging.warning(_('No points selected'))
|
1162
|
+
return
|
1163
|
+
|
1164
|
+
if self._xls is None:
|
1165
|
+
self._xlsFrame = wx.Frame(None, wx.ID_ANY, _('Selected points - ') + self.idx)
|
1166
|
+
self._xls = CpGrid(self._xlsFrame, wx.ID_ANY, style = wx.WANTS_CHARS)
|
1167
|
+
|
1168
|
+
sizer = wx.BoxSizer(wx.VERTICAL)
|
1169
|
+
sizer.Add(self._xls, 1, wx.EXPAND)
|
1170
|
+
|
1171
|
+
nbclass = len(self.classification.classification)
|
1172
|
+
self._xls.CreateGrid(xyz.shape[0] + nbclass +1, 4)
|
1173
|
+
|
1174
|
+
self._xlsFrame.SetSizer(sizer)
|
1175
|
+
self._xlsFrame.Layout()
|
1176
|
+
self._xlsFrame.Show()
|
1177
|
+
|
1178
|
+
else:
|
1179
|
+
self._xls.ClearGrid()
|
1180
|
+
if self._xls.NumberRows < xyz.shape[0]:
|
1181
|
+
self._xls.AppendRows(xyz.shape[0]-self._xls.NumberRows)
|
1182
|
+
|
1183
|
+
self._xls.SetColLabelValue(0, 'X')
|
1184
|
+
self._xls.SetColLabelValue(1, 'Y')
|
1185
|
+
self._xls.SetColLabelValue(2, 'Z')
|
1186
|
+
self._xls.SetColLabelValue(3, 'Code')
|
1187
|
+
|
1188
|
+
codes = self.code_selected
|
1189
|
+
|
1190
|
+
for i in range(xyz.shape[0]):
|
1191
|
+
self._xls.SetCellValue(i,0,str(xyz[i,0]))
|
1192
|
+
self._xls.SetCellValue(i,1,str(xyz[i,1]))
|
1193
|
+
self._xls.SetCellValue(i,2,str(xyz[i,2]))
|
1194
|
+
self._xls.SetCellValue(i,3,str(codes[i]))
|
1195
|
+
|
1196
|
+
# Copy classification under the values
|
1197
|
+
for i, (key, val) in enumerate(self.classification.classification.items()):
|
1198
|
+
self._xls.SetCellValue(xyz.shape[0]+i+1,0,str(key))
|
1199
|
+
self._xls.SetCellValue(xyz.shape[0]+i+1,1,val[0])
|
1200
|
+
self._xls.SetCellValue(xyz.shape[0]+i+1,2,val[1])
|
1201
|
+
self._xls.SetCellValue(xyz.shape[0]+i+1,3,str(key))
|
1202
|
+
|
1203
|
+
self._xlsFrame.Show()
|
1204
|
+
|
1205
|
+
# Mettre la fenêtre au premier plan et centrée
|
1206
|
+
self._xlsFrame.Raise()
|
1207
|
+
self._xlsFrame.Center()
|
1208
|
+
|
1209
|
+
def _update_props(self):
|
1210
|
+
|
1211
|
+
if self._myprops is None:
|
1212
|
+
return
|
1213
|
+
|
1214
|
+
if self.viewer is None:
|
1215
|
+
return
|
1216
|
+
|
1217
|
+
props = self._myprops
|
1218
|
+
|
1219
|
+
self.lookat = (props[('Look at', 'X')], props[('Look at', 'Y')], props[('Look at', 'Z')])
|
1220
|
+
self.eye = (props[('Camera', 'X')], props[('Camera', 'Y')], props[('Camera', 'Z')])
|
1221
|
+
|
1222
|
+
color = np.asarray(props[('Background', 'Color')])
|
1223
|
+
self.bg_color(color / 255.)
|
1224
|
+
color = np.asarray(props[('Background', 'Top Color')])
|
1225
|
+
self.bg_color_top(color / 255.)
|
1226
|
+
color = np.asarray(props[('Background', 'Bottom Color')])
|
1227
|
+
self.bg_color_bottom(color / 255.)
|
1228
|
+
|
1229
|
+
self.floor_level(props[('Floor', 'Level')])
|
1230
|
+
color = np.asarray(props[('Floor', 'Color')])
|
1231
|
+
self.floor_color(color / 255.)
|
1232
|
+
|
1233
|
+
self.show_grid(props[('Infos', 'Grid')])
|
1234
|
+
self.show_axis(props[('Infos', 'Axis')])
|
1235
|
+
self.show_info(props[('Infos', 'values')])
|
1236
|
+
|
1237
|
+
self.point_size = props[('Points', 'Size')]
|
1238
|
+
|
1239
|
+
codes_sel = props[('Selection', 'Codes')]
|
1240
|
+
codes_sel = codes_sel.split(',')
|
1241
|
+
try:
|
1242
|
+
self._select_only_codes = list(set([int(curcode) for curcode in codes_sel]))
|
1243
|
+
except Exception as e:
|
1244
|
+
logging.error(e)
|
1245
|
+
logging.warning(_('Nullify selection filter - Check your input'))
|
1246
|
+
self._select_only_codes = []
|
1247
|
+
|
1248
|
+
# self._auto_update_interval = props[('Auto Update', 'Interval')]
|
1249
|
+
# self._auto_update_properties = props[('Auto Update', 'Active')]
|
1250
|
+
|
1251
|
+
# if self._auto_update_properties:
|
1252
|
+
# self.SetTimer(self._auto_update_interval)
|
1253
|
+
# else:
|
1254
|
+
# if self._timer is not None:
|
1255
|
+
# self._timer.Stop()
|
1256
|
+
|
1257
|
+
def _fill_props(self, full:bool = False):
|
1258
|
+
|
1259
|
+
if self._myprops is None:
|
1260
|
+
return
|
1261
|
+
|
1262
|
+
props = self._myprops
|
1263
|
+
|
1264
|
+
props[('Look at', 'X')] = self.lookat[0]
|
1265
|
+
props[('Look at', 'Y')] = self.lookat[1]
|
1266
|
+
props[('Look at', 'Z')] = self.lookat[2]
|
1267
|
+
|
1268
|
+
props[('Camera', 'X')] = self.eye[0]
|
1269
|
+
props[('Camera', 'Y')] = self.eye[1]
|
1270
|
+
props[('Camera', 'Z')] = self.eye[2]
|
1271
|
+
|
1272
|
+
props[('Relative Position', 'Phi')] = self.phi
|
1273
|
+
props[('Relative Position', 'Theta')] = self.theta
|
1274
|
+
props[('Relative Position', 'R')] = self.r
|
1275
|
+
|
1276
|
+
if full:
|
1277
|
+
|
1278
|
+
props[('Background', 'Color')] = self._bg_color
|
1279
|
+
props[('Background', 'Top Color')] = self._bg_color_top
|
1280
|
+
props[('Background', 'Bottom Color')] = self._bg_color_bottom
|
1281
|
+
|
1282
|
+
props[('Floor', 'Level')] = self._floor_level
|
1283
|
+
props[('Floor', 'Color')] = self._floor_color
|
1284
|
+
|
1285
|
+
props[('Infos', 'Grid')] = self._show_grid
|
1286
|
+
props[('Infos', 'Axis')] = self._show_axis
|
1287
|
+
props[('Infos', 'values')] = self._show_info
|
1288
|
+
|
1289
|
+
props[('Points', 'Size')] = self._point_size
|
1290
|
+
|
1291
|
+
# props[('Auto Update', 'Interval')] = self._auto_update_interval
|
1292
|
+
# props[('Auto Update', 'Active')] = self._auto_update_properties
|
1293
|
+
|
1294
|
+
props.Populate()
|
1295
|
+
|
1296
|
+
# def _fill_handler(self, event):
|
1297
|
+
|
1298
|
+
# if self.viewer is None:
|
1299
|
+
# return
|
1300
|
+
|
1301
|
+
# self._fill_props(full = False)
|
1302
|
+
|
1303
|
+
# def SetTimer(self, interval:int):
|
1304
|
+
|
1305
|
+
# if self.viewer is None:
|
1306
|
+
# return
|
1307
|
+
|
1308
|
+
# if self._timer is None:
|
1309
|
+
# self._timer = wx.Timer(self._myprops)
|
1310
|
+
# self._myprops.Bind(wx.EVT_TIMER, self._fill_handler, self._timer)
|
1311
|
+
|
1312
|
+
# self._timer.Start(interval)
|
1313
|
+
|
1314
|
+
def show_properties(self):
|
1315
|
+
|
1316
|
+
if self.viewer is None:
|
1317
|
+
return
|
1318
|
+
|
1319
|
+
self._create_props()
|
1320
|
+
self._myprops.Show()
|
1321
|
+
|
685
1322
|
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
1323
|
|
687
1324
|
xb=bounds[0]
|
@@ -1096,7 +1733,7 @@ def myviewer(las:Union[np.ndarray, list[laspy.LasData], laspy.LasData], which_co
|
|
1096
1733
|
|
1097
1734
|
colors = get_colors(las, which_colors, fname=fname, palette_classif= palette_classif)
|
1098
1735
|
|
1099
|
-
v=viewer(xyz,colors)
|
1736
|
+
v = viewer(xyz,colors)
|
1100
1737
|
v.set(point_size=.05)
|
1101
1738
|
return v
|
1102
1739
|
|