wolfhece.wolf_array._base_gui ============================= .. py:module:: wolfhece.wolf_array._base_gui .. autoapi-nested-parse:: GUI-enabled WolfArray with OpenGL rendering and wxPython integration. This module defines :class:`WolfArray`, which inherits from :class:`WolfArrayModel` (data operations) and :class:`Element_To_Draw` (OpenGL / MapViewer integration). It adds: - Multiple 2D rendering back-ends (display-list via Cython, per-cell shader, shared-resource shader). - LOD (Level-Of-Detail) tiling for large grids. - Interactive wx dialogs for crop, band selection, volume estimation, etc. - Palette / colormap management with automatic isopop. - 3D preview support. Author: HECE - University of Liege, Pierre Archambeau Date: 2024 Copyright (c) 2024 University of Liege. All rights reserved. This script and its content are protected by copyright law. Unauthorized copying or distribution of this file, via any medium, is strictly prohibited. Module Contents --------------- .. py:data:: msg :value: 'Error importing OpenGL library' .. py:class:: WolfArray(fname: str = None, mold: wolfhece.wolf_array._base.WolfArrayModel = None, masknull: bool = True, crop: list[list[float], list[float]] = None, whichtype=WOLF_ARRAY_FULL_SINGLE, preload: bool = True, create: bool = False, mapviewer=None, nullvalue: float = 0.0, srcheader: wolfhece.wolf_array._header_wolf.header_wolf = None, idx: str = '', plotted: bool = False, need_for_wx: bool = False, mask_source: numpy.ndarray = None, np_source: numpy.ndarray = None) Bases: :py:obj:`wolfhece.wolf_array._base.WolfArrayModel`, :py:obj:`wolfhece.drawing_obj.Element_To_Draw` .. autoapi-inheritance-diagram:: wolfhece.wolf_array._base_gui.WolfArray :parts: 1 :private-bases: GUI-enabled WolfArray with OpenGL rendering and wxPython integration. Inherits all data operations from :class:`WolfArrayModel` and adds: - OpenGL 2D rendering via display-lists (Cython ``wolfogl``) or GLSL shaders (:class:`WolfArrayShader2D`). - LOD tiling: the grid is divided into tiles whose resolution adapts to the current zoom level. - Palette / colormap management with automatic ``isopop`` adjustment. - Interactive wx dialogs for crop, band selection, volume estimation, file saving, and error reporting. - ``Ops_Array`` integration for the GUI operations panel. - 3D preview via :class:`WolfArrayPlotShader`. .. py:attribute:: myops :type: wolfhece.ui.wolf_array_ops.Ops_Array .. py:attribute:: _clip_zones :type: list[wolfhece.wolf_array._clipping.WolfArrayClipZone] :value: [] .. py:attribute:: _clip_show_bars :type: bool :value: True .. py:property:: rendering_machine :type: wolfhece.wolf_array._base.OGLRenderer Current OpenGL rendering back-end (:class:`OGLRenderer`). .. py:property:: clip_zones :type: list[wolfhece.wolf_array._clipping.WolfArrayClipZone] Active clip zones restricting OpenGL rendering. .. py:method:: add_clip_zone(clip: wolfhece.wolf_array._clipping.WolfArrayClipZone) -> wolfhece.wolf_array._clipping.WolfArrayClipZone Add a clip zone to restrict rendering. :param clip: The clip zone to add. :return: The same clip zone (for chaining). .. py:method:: remove_clip_zone(clip: wolfhece.wolf_array._clipping.WolfArrayClipZone) Remove a previously added clip zone. .. py:method:: clear_clip_zones() Remove all clip zones (rendering becomes unrestricted). .. py:method:: add_clip_zone_from_bounds(xmin: float, ymin: float, xmax: float, ymax: float, active: bool = True) -> wolfhece.wolf_array._clipping.WolfArrayClipZone Create and add a clip zone from world-coordinate bounds. :return: The newly created clip zone. .. py:method:: add_clip_zone_from_header(h: wolfhece.wolf_array._header_wolf.header_wolf, active: bool = True) -> wolfhece.wolf_array._clipping.WolfArrayClipZone Create and add a clip zone from a :class:`header_wolf`. :return: The newly created clip zone. .. py:method:: setup_curtain_clip(edge: wolfhece.wolf_array._clipping.ClipSliderEdge = ClipSliderEdge.RIGHT, **slider_kwargs) -> tuple[wolfhece.wolf_array._clipping.WolfArrayClipZone, wolfhece.wolf_array._clipping.ClipSlider] Set up a "curtain" clip with a draggable slider. Creates a clip zone covering the full array extent and attaches a slider on the given *edge*. This is the recommended entry point for the "swipe to compare" use case. :param edge: Which edge the slider bar controls. :param slider_kwargs: Extra keyword arguments forwarded to :class:`ClipSlider` (e.g. *color*, *line_width*). :return: ``(clip_zone, slider)`` tuple. .. py:method:: setup_vertical_band(**slider_kwargs) -> tuple[wolfhece.wolf_array._clipping.WolfArrayClipZone, list[wolfhece.wolf_array._clipping.ClipSlider]] Set up a vertical band with left and right sliders. The left slider hides everything to its left, the right slider hides everything to its right. :return: ``(clip_zone, [left_slider, right_slider])`` tuple. .. py:method:: setup_horizontal_band(**slider_kwargs) -> tuple[wolfhece.wolf_array._clipping.WolfArrayClipZone, list[wolfhece.wolf_array._clipping.ClipSlider]] Set up a horizontal band with bottom and top sliders. The bottom slider hides everything below, the top slider hides everything above. :return: ``(clip_zone, [bottom_slider, top_slider])`` tuple. .. py:method:: setup_clip_from_view(xmin: float, ymin: float, xmax: float, ymax: float, invert: bool = False) -> wolfhece.wolf_array._clipping.WolfArrayClipZone Create a clip zone matching the given viewport bounds. :param invert: If True, the *exterior* of the zone is drawn. :return: The created clip zone. .. py:method:: snapshot_clip_config(name: str) -> wolfhece.wolf_array._clipping.ClipConfig Create a :class:`ClipConfig` snapshot of the current clip zones. .. py:method:: apply_clip_config(cfg: wolfhece.wolf_array._clipping.ClipConfig) Apply a :class:`ClipConfig` to this array, replacing current clips. .. py:property:: epsg_parent :type: int EPSG code of the parent map viewer. Falls back to the array's own EPSG when no viewer is attached. .. py:method:: _check_epsg_coherence() Check whether the array and map viewer share the same EPSG code. :return: ``True`` if both codes match, ``False`` otherwise. :rtype: bool .. py:property:: usemask Whether the operations panel requests masked-value filtering. Delegates to ``self.myops.usemask``; returns ``False`` if no operations panel is attached. .. py:method:: set_opacity(alpha: float) Set the global transparency of this array. *alpha* is clamped to [0, 1]. If an ``Ops_Array`` panel is attached, the corresponding slider is synchronised. :param alpha: Opacity value (0 = fully transparent, 1 = opaque). :return: The clamped alpha value actually applied. :rtype: float .. py:method:: extract_selection() Create a new WolfArray from the current selection and add it to the map viewer with suffix ``'_extracted'``. .. py:method:: prepare_3D() Build a :class:`WolfArrayPlotShader` for 3D rendering. Converts the data to a GPU-ready z-texture (C-contiguous float32, masked cells set to ``array.min()``) and creates a per-cell shader object stored in ``self._array3d``. .. py:method:: show_properties() Show the ``Ops_Array`` properties window (wxPython). .. py:method:: hide_properties() Hide the ``Ops_Array`` properties window. .. py:property:: Operations :type: wolfhece.ui.wolf_array_ops.Ops_Array The :class:`Ops_Array` instance managing GUI operations. .. py:method:: add_ops_sel() Attach an ``Ops_Array`` panel and a selection manager. - Creates an ``Ops_Array`` if a map viewer is available. - Creates a :class:`SelectionData` (or ``SelectionDataMB`` for multi-block arrays) if none exists yet. .. py:method:: change_gui(newparentgui) Re-parent this array to a different :class:`WolfMapViewer`. If no viewer was previously attached, creates a new ``Ops_Array`` panel. Otherwise, migrates the existing one. :param newparentgui: Target :class:`WolfMapViewer` instance. :raises AssertionError: If *newparentgui* is not a :class:`WolfMapViewer`. .. py:method:: init_from_new(dlg: wolfhece.ui.wolf_array_ui.NewArray) Initialise grid geometry from a completed :class:`NewArray` dialog. Reads *dx*, *dy*, *nbx*, *nby*, *origx*, *origy* from the dialog widgets and creates a ones-filled masked array. :param dlg: The :class:`NewArray` dialog that has been validated. .. py:method:: _prompt_crop(dx: float, dy: float) -> list | None Show a :class:`CropDialog` and return crop bounds. :param dx: Cell size in X (pre-filled, read-only). :param dy: Cell size in Y (pre-filled, read-only). :return: ``[[xmin, xmax], [ymin, ymax]]`` or ``None`` if cancelled. .. py:method:: _prompt_band_selection(band_names: list[str]) -> int | None Show a single-choice dialog for raster band selection. :param band_names: List of human-readable band names. :return: 1-based band index, or ``None`` if cancelled. .. py:method:: _prompt_save_file(message: str, wildcard: str) -> str | None Show a file-save dialog. :param message: Dialog title / prompt. :param wildcard: File-type filter string (wx format). :return: Selected file path, or ``None`` if cancelled. .. py:method:: _show_error_dialog(message: str) Log an error and optionally show a wx error dialog. :param message: Error message text. .. py:method:: volume_estimation(axs=None) Interactive volume estimation driven by wx dialogs. Prompts the user for Z-max, number of slices, and labelling method, then delegates to :meth:`WolfArrayModel.volume_estimation`. :param axs: Optional matplotlib axes for plotting results. :return: The result of ``WolfArrayModel.volume_estimation()``. .. py:method:: check_plot() Enable plotting and lazy-load data if needed. If the array has not been loaded yet (and a filename exists), reads all data from disk and applies the null-value mask. Updates the palette so that colour stops match the current data range. .. py:method:: uncheck_plot(unload: bool = True, forceresetOGL: bool = False, askquestion: bool = True) Disable plotting and optionally unload data from memory. :param unload: If ``True`` (default), free the array data and OpenGL resources after prompting the user. :param forceresetOGL: If ``True``, delete OpenGL lists without asking. :param askquestion: If ``True`` (default) and a wx App is running, prompt the user before unloading or resetting. .. py:method:: reset_plot(whichpal=0, mimic=True) Reset OpenGL resources and rebuild the palette. Deletes cached display lists / shader objects, clears any 2D/3D array caches, recomputes the palette, and propagates the reset to linked arrays when *mimic* is ``True``. :param whichpal: Palette index passed to :meth:`updatepalette`. :param mimic: If ``True``, also reset linked arrays sharing the same palette. .. py:method:: updatepalette(which: int = 0, onzoom=[]) Recompute palette colours for the current data range. When ``which == 0`` and ``VERSION_RGB >= 2``, only the float32 temporary buffer is prepared (palette look-up is done on the GPU). When ``which == 1`` or ``VERSION_RGB == 1``, a full RGBA buffer (``self.rgb``) is computed on the CPU. :param which: ``0`` for normal update, ``1`` to force CPU-side RGBA computation (used by hillshade overlay). :param onzoom: Optional ``[xmin, xmax, ymin, ymax]`` window; if given, the iso-population palette is computed over this sub-extent only. .. py:method:: find_minmax(update=False) Refresh the bounding-box cache. This override of :class:`Element_To_Draw` updates ``xmin``/``xmax``/``ymin``/``ymax`` from the grid header. :param update: If ``True``, recompute bounds from the header; otherwise leave the existing values untouched. .. py:method:: _get_LOD(sx: float, sy: float, xmin: float, ymin: float, xmax: float, ymax: float) Determine the level-of-detail factor for the current viewport. The LOD is the smallest power of two such that each cell occupies at least 0.5 screen pixels. A return value of 1 means every cell is drawn; higher values skip cells. :param sx: Horizontal scale (pixels per world-unit). :param sy: Vertical scale (pixels per world-unit). :param xmin: Viewport lower-left X. :param ymin: Viewport lower-left Y. :param xmax: Viewport upper-right X. :param ymax: Viewport upper-right Y. :return: LOD factor (>= 1), or ``-1`` if nothing is visible. .. py:method:: _get_dxdy_LOD(lod: int) Return the effective cell sizes ``(dx, dy)`` at a given LOD. :param lod: Level-of-detail factor. :return: ``(dx * lod, dy * lod)``. .. py:method:: _get_scale_step_LOD(lod: int) Return the tile step ``_gridsize * lod``. :param lod: Level-of-detail factor. .. py:method:: _get_grid_shape_LOD(lod: int) Return ``(nbx, nby)`` of LOD tiles covering the full grid. :param lod: Level-of-detail factor. :return: Tuple ``(ceil(nbx / step), ceil(nby / step))``. .. py:method:: _get_grid_LOD(lod: int, shader: bool = False) Return (or create) the tile cache for a given LOD. On first call for a given *lod*, allocates OpenGL display-list IDs (legacy path) or a ``WolfArrayPlotShader`` grid (shader path). :param lod: Level-of-detail factor. :param shader: If ``True``, allocate shader objects instead of GL display-list IDs. :return: ``dict`` with keys ``'nbx'``, ``'nby'``, ``'cache'``, ``'done'``. .. py:method:: _get_part_to_plot_LOD(lod: int, xmin: float, ymin: float, xmax: float, ymax: float) Compute the tile index range visible in the current viewport. :param lod: Level-of-detail factor. :param xmin: Viewport lower-left X. :param ymin: Viewport lower-left Y. :param xmax: Viewport upper-right X. :param ymax: Viewport upper-right Y. :return: ``((istart, iend), (jstart, jend))`` tile indices (inclusive). .. py:method:: _draw_2d_lists(sx: float, sy: float, xmin: float, ymin: float, xmax: float, ymax: float) Draw the array using legacy OpenGL display lists. :param sx: Horizontal scale (pixels per world-unit). :param sy: Vertical scale (pixels per world-unit). :param xmin: Viewport lower-left X. :param ymin: Viewport lower-left Y. :param xmax: Viewport upper-right X. :param ymax: Viewport upper-right Y. .. py:method:: _fill_ogllist_for_one_grid_cell(lod, loci, locj, force=False) Build the OpenGL display list for one LOD tile. Delegates actual vertex generation to the Cython ``wolfogl`` helpers depending on ``VERSION_RGB`` and the array dtype. :param lod: Level-of-detail factor. :param loci: Tile column index. :param locj: Tile row index. :param force: If ``True``, rebuild even if already built. .. py:method:: _get_xy_centers_LOD(lod: int, loci: int, locj: int, usenap: bool = True) Compute cell-centre coordinates for one LOD tile. Returns a flat ``float32`` array of ``(x, y)`` pairs suitable for upload as a VBO. :param lod: Level-of-detail factor. :param loci: Tile column index. :param locj: Tile row index. :param usenap: If ``True`` (default), exclude masked (null) cells. :return: 1-D ``np.float32`` array ``[x0, y0, x1, y1, …]``. .. py:method:: _fill_oglshader_for_one_grid_cell(lod, loci, locj, force=False) Prepare one LOD tile for the per-cell shader pipeline. Creates a :class:`WolfArrayPlotShader` object containing the vertex buffer and z-texture for the tile. :param lod: Level-of-detail factor. :param loci: Tile column index. :param locj: Tile row index. :param force: If ``True``, rebuild even if already built. .. py:method:: _draw_2d_shader(sx: float = None, sy: float = None, xmin: float = None, ymin: float = None, xmax: float = None, ymax: float = None) Delegate 2D shader rendering to :class:`WolfArrayShader2D`. .. py:method:: _cleanup_shader_2d_resources() Release all shared GL resources for 2D shader rendering. .. py:method:: plot(sx: float = None, sy: float = None, xmin: float = None, ymin: float = None, xmax: float = None, ymax: float = None, size: float = None) Draw the array in the current OpenGL context. Dispatches to :meth:`_draw_2d_shader` or :meth:`_draw_2d_lists` depending on the active rendering backend. Also draws the current selection overlay and attached zones. When :attr:`clip_zones` contains active zones, rendering is restricted to those regions via ``GL_SCISSOR_TEST``. Each active zone triggers a separate rendering pass so that the union of all zones is visible. Attached :class:`ClipSlider` bars are drawn afterwards (outside the scissor) for visual feedback. :param sx: Horizontal scale (pixels per world-unit). :param sy: Vertical scale (pixels per world-unit). :param xmin: Viewport lower-left X. :param ymin: Viewport lower-left Y. :param xmax: Viewport upper-right X. :param ymax: Viewport upper-right Y. :param size: Unused — kept for API compatibility with :class:`Element_To_Draw`. .. py:method:: _plot_array_core(sx, sy, xmin, ymin, xmax, ymax) Internal: dispatch to the active 2D rendering backend. .. py:method:: delete_lists() Release all cached OpenGL display lists and shader objects.