Source code for wolfhece._laz_manager

"""
Companion manager for LAZ menu and actions.
Extracted from PyDraw.WolfMapViewer.
"""
from __future__ import annotations

import logging
from typing import TYPE_CHECKING

import wx

from ._action_kind import ActionKind
from .PyTranslate import _
from .PyVertex import cloud_vertices
from .PyVertexvectors import vector, wolfvertex
from .color_constants import getIfromRGB

if TYPE_CHECKING:
    from .PyDraw import WolfMapViewer


[docs] class LazManager: """Manages LAZ menu construction and menu actions.""" def __init__(self, viewer: "WolfMapViewer") -> None:
[docs] self._viewer = viewer
[docs] self._menu: wx.Menu | None = None
[docs] self._menu_data: wx.Menu | None = None
[docs] self._menu_grid: wx.Menu | None = None
[docs] def menu_build(self) -> None: v = self._viewer if self._menu is not None: return self._menu = wx.Menu() self._menu_data = wx.Menu() self._menu_grid = wx.Menu() v.menubar.Append(self._menu, _('&LAZ')) self._menu.AppendSubMenu(self._menu_data, _('LAZ data')) self._menu.AppendSubMenu(self._menu_grid, _('LAZ grid')) item = self._menu_data.Append(wx.ID_ANY, _('Initialize from laz, las or npz'), _('LAZ data from one specific file (type laz, las or npz)')) self._menu_data.Bind(wx.EVT_MENU, self._on_init_laz, item) item = self._menu_grid.Append(wx.ID_ANY, _('Initialize from directory'), _('LAZ GRID stored in a directory - subgridding of LAZ files'), kind=wx.ITEM_CHECK) self._menu_grid.Bind(wx.EVT_MENU, self._on_init_lazgrid, item) item = self._menu_grid.Append(wx.ID_ANY, _('Copy from current zoom'), _('Make a copy of the used files')) self._menu_grid.Bind(wx.EVT_MENU, self._on_copy_zoom, item) item = self._menu_grid.Append(wx.ID_ANY, _('Change colors - Classification'), _('Change color map associated to the current classification')) self._menu_grid.Bind(wx.EVT_MENU, self._on_change_colors, item) item = self._menu_data.Append(wx.ID_ANY, _('Create cloud points from bridges'), _('Extract bridge from LAZ data as cloud points (class 10)')) self._menu_data.Bind(wx.EVT_MENU, self._on_cloud_bridges, item) item = self._menu_data.Append(wx.ID_ANY, _('Create cloud points from buildings'), _('Extract buildings from LAZ data as cloud points (class 1)')) self._menu_data.Bind(wx.EVT_MENU, self._on_cloud_buildings, item) item = self._menu_data.Append(wx.ID_ANY, _('Create cloud points from specified classes'), _('Extract cloud points from LAZ data (class to specify)')) self._menu_data.Bind(wx.EVT_MENU, self._on_cloud_classes, item) item = self._menu.Append(wx.ID_ANY, _('Clip LAZ grid on current zoom'), _('Select LAZ data based on the visible screen extent')) self._menu.Bind(wx.EVT_MENU, self._on_clip_lazgrid, item) item = self._menu.Append(wx.ID_ANY, _('Create LAZ viewer'), _('Create a LAZ Viewer based on loaded data')) self._menu.Bind(wx.EVT_MENU, self._on_create_viewer, item) item = self._menu.Append(wx.ID_ANY, _('Filter data based on codes'), _('Filter LAZ data based on codes')) self._menu.Bind(wx.EVT_MENU, self._on_filter_codes, item) item = self._menu.Append(wx.ID_ANY, _('Descimate LAZ data'), _('Descimate LAZ data')) self._menu.Bind(wx.EVT_MENU, self._on_decimate, item) item = self._menu.Append(wx.ID_ANY, _('Plot LAZ around active vector'), _('Display a Matplotlib plot with the LAZ values around the active vector/polyline')) self._menu.Bind(wx.EVT_MENU, self._on_plot_active_vec, item) item = self._menu.Append(wx.ID_ANY, _('Plot LAZ around temporary vector'), _('Display a Matplotlib plot with the LAZ values around a temporary vector/polyline -- Right clicks to add points + Enter')) self._menu.Bind(wx.EVT_MENU, self._on_plot_tmp_vec, item) item = self._menu.Append(wx.ID_ANY, _('Fill active array from LAZ data'), _('Fill an array from the LAZ data')) self._menu.Bind(wx.EVT_MENU, self._on_fill_array, item) item = self._menu.Append(wx.ID_ANY, _('Select cells in array from LAZ data'), _('Select nodes in active array from the LAZ data')) self._menu.Bind(wx.EVT_MENU, self._on_select_cells, item) item = self._menu.Append(wx.ID_ANY, _('Count LAZ data in cells'), _('Count the number of LAZ data in each cell of the matrix')) self._menu.Bind(wx.EVT_MENU, self._on_count_cells, item)
# ------------------------------------------------------------------ # Individual menu handlers # ------------------------------------------------------------------
[docs] def _on_init_laz(self, event: wx.MenuEvent) -> None: self._viewer.init_laz_from_lazlasnpz()
[docs] def _on_init_lazgrid(self, event: wx.MenuEvent) -> None: self._viewer.init_laz_from_gridinfos()
[docs] def _on_copy_zoom(self, event: wx.MenuEvent) -> None: v = self._viewer if v.mylazgrid is None: logging.warning(_('No gridded LAZ data loaded !')) return dlg = wx.DirDialog(None, _('Choose a directory to copy the files'), _('Copy files'), style=wx.DD_DEFAULT_STYLE) if dlg.ShowModal() == wx.ID_CANCEL: dlg.Destroy() return dirout = dlg.GetPath() dlg.Destroy() curbounds = [[v.xmin, v.xmin + v.width], [v.ymin, v.ymin + v.height]] v.mylazgrid.copy_files_in_bounds(curbounds, dirout)
[docs] def _on_change_colors(self, event: wx.MenuEvent) -> None: v = self._viewer if v.mylazgrid is not None: v.mylazgrid.colors.interactive_update_colors()
[docs] def _on_cloud_bridges(self, event: wx.MenuEvent) -> None: v = self._viewer if v.active_laz is None: v.init_laz_from_lazlasnpz() if v.active_laz is None: return mybridges = v.active_laz.get_data_class(10) mycloud = cloud_vertices() mycloud.init_from_nparray(mybridges) mycloud.myprop.style = 2 mycloud.myprop.color = getIfromRGB([255, 102, 102]) mycloud.myprop.width = 0.5 if v.linked and v.linkedList is not None and len(v.linkedList) > 0: for curframe in v.linkedList: curframe.add_object('cloud', newobj=mycloud, ToCheck=True, id='Bridges') else: v.add_object('cloud', newobj=mycloud, ToCheck=True, id='Bridges')
[docs] def _on_cloud_buildings(self, event: wx.MenuEvent) -> None: v = self._viewer if v.active_laz is None: v.init_laz_from_lazlasnpz() if v.active_laz is None: return mybuildings = v.active_laz.get_data_class(1) mycloud = cloud_vertices() mycloud.init_from_nparray(mybuildings) mycloud.myprop.style = 2 mycloud.myprop.color = getIfromRGB([102, 102, 102]) mycloud.myprop.width = 0.5 if v.linked and v.linkedList is not None and len(v.linkedList) > 0: for curframe in v.linkedList: curframe.add_object('cloud', newobj=mycloud, ToCheck=True, id='Buildings') else: v.add_object('cloud', newobj=mycloud, ToCheck=True, id='Buildings')
[docs] def _on_cloud_classes(self, event: wx.MenuEvent) -> None: v = self._viewer if v.active_laz is None: v.init_laz_from_lazlasnpz() if v.active_laz is None: return codes = v.active_laz.codes_unique dlg = wx.MultiChoiceDialog(None, _('Choose the classes to plot'), _('Classes'), codes) if dlg.ShowModal() == wx.ID_CANCEL: dlg.Destroy() return selected = [codes[cur] for cur in dlg.GetSelections()] dlg.Destroy() for curcode in selected: mycloud = cloud_vertices() mydata = v.active_laz.get_data_class(curcode) mycloud.init_from_nparray(mydata) mycloud.myprop.style = 2 mycloud.myprop.color = getIfromRGB([102, 102, 102]) mycloud.myprop.width = 0.5 if v.linked and v.linkedList is not None and len(v.linkedList) > 0: for curframe in v.linkedList: curframe.add_object('cloud', newobj=mycloud, ToCheck=True, id='Class {}'.format(curcode)) else: v.add_object('cloud', newobj=mycloud, ToCheck=True, id='Class {}'.format(curcode))
[docs] def _on_clip_lazgrid(self, event: wx.MenuEvent) -> None: v = self._viewer if v.mylazgrid is None: logging.warning(_('No gridded LAZ data loaded !')) return v.clip_laz_gridded() if v.active_laz is None: logging.error(_('No data found')) return if v.active_laz.data.shape[0] > 100_000_000: dlg = wx.NumberEntryDialog( None, _('Your data selection is very large (>100 M)\nWould you like to descimate?\n\n{} points').format(v.active_laz.data.shape[0]), _('Descimate factor'), _('Decimation'), 0, 0, 100, ) ret = dlg.ShowModal() if ret == wx.ID_CANCEL: dlg.Destroy() return descimate_fact = dlg.GetValue() dlg.Destroy() if descimate_fact > 0: v.descimate_laz_data(descimate_fact)
[docs] def _on_create_viewer(self, event: wx.MenuEvent) -> None: v = self._viewer laz_source = v._select_laz_source() if laz_source is None: logging.warning(_('No LAZ data loaded !')) return if laz_source is v.mylazgrid: if v.mylazgrid is None: logging.warning(_('No gridded LAZ data loaded !')) return v.clip_laz_gridded() if v.active_laz.nb_points == 0: logging.warning(_('No points in the active LAZ object -- Aborting !')) return v.active_laz.create_viewer(v._choice_laz_colormap(), v.mylazgrid.colors) v.myviewerslaz.append(v.active_laz.viewer) v.active_viewerlaz = v.myviewerslaz[-1] else: if v.active_laz.nb_points == 0: logging.warning(_('No points in the active LAZ object -- Aborting !')) return v.active_laz.create_viewer() v.myviewerslaz.append(v.active_laz.viewer) v.active_viewerlaz = v.myviewerslaz[-1]
[docs] def _on_filter_codes(self, event: wx.MenuEvent) -> None: self._viewer.filter_active_laz()
[docs] def _on_decimate(self, event: wx.MenuEvent) -> None: v = self._viewer if v.active_laz is None: return dlg = wx.NumberEntryDialog( None, _('Your dataset contains {} points.\nWould you like to descimate?').format(v.active_laz.num_points), _('Decaimate factor'), _('Decimation'), 0, 0, 100, ) ret = dlg.ShowModal() if ret == wx.ID_CANCEL: dlg.Destroy() return descimate_fact = dlg.GetValue() dlg.Destroy() if descimate_fact > 0: v.active_laz.descimate(descimate_fact) logging.info(_('New count : {}').format(v.active_laz.num_points))
[docs] def _on_plot_active_vec(self, event: wx.MenuEvent) -> None: self._viewer.plot_laz_around_active_vec()
[docs] def _on_plot_tmp_vec(self, event: wx.MenuEvent) -> None: v = self._viewer v.active_vector = vector() v.active_vector.add_vertex(wolfvertex(0.0, 0.0)) v.mimicme() v.start_action(ActionKind.LAZ_TMP_VECTOR, _('LAZ tmp'))
[docs] def _on_fill_array(self, event: wx.MenuEvent) -> None: v = self._viewer if v.mylazgrid is None: logging.warning('') return if v.active_array is None: logging.warning(_('No active array -- select an array first and retry!')) return v.fill_active_array_from_laz(v.active_array)
[docs] def _on_select_cells(self, event: wx.MenuEvent) -> None: v = self._viewer if v.mylazgrid is None: logging.warning('') return if v.active_array is None: logging.warning(_('No active array -- select an array first and retry!')) return v.select_active_array_from_laz(v.active_array)
[docs] def _on_count_cells(self, event: wx.MenuEvent) -> None: v = self._viewer if v.mylazgrid is None: logging.warning('') return if v.active_array is None: logging.warning(_('No active array -- select an array first and retry!')) return v.count_active_array_from_laz(v.active_array)