"""
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.
"""
import numpy as np
import logging
from ..drawing_obj import Element_To_Draw
try:
# Trying to import the hydrometry_hece module from the hydrometry_hece package
# containing the KEY access to the SPW server
from ..hydrometry_hece.kiwis_hece import hydrometry_hece as hydrometry
except:
# If the hydrometry_hece module is not found, we import the hydrometry module from the hydrometry package
from .kiwis import hydrometry
from .kiwis_gui import hydrometry_gui
from ..PyVertex import cloud_vertices, wolfvertex, Cloud_Styles, Cloud_OGLRenderer
from ..color_constants import getRGBfromI, getIfromRGB
[docs]
class hydrometry_wolfgui(Element_To_Draw):
def __init__(self, idx:str = '', plotted:bool = True, mapviewer = None, need_for_wx:bool = False, dir:str = '', **kwargs) -> None:
"""
Constructor of the class
:param idx: identifier
:param plotted: boolean if plotting action must be processed
:param mapviewer: WolfMapViewer instance attached to the object
:param need_for_wx: test if wx App is running. If not, raise an error
"""
Element_To_Draw.__init__(self, idx, plotted, mapviewer, need_for_wx)
[docs]
self.hydrometry = hydrometry(dir=dir)
[docs]
self.cloud_stations_real = cloud_vertices(idx = 'real stations', mapviewer = mapviewer)
self.cloud_stations_real.myprop.style = Cloud_Styles.CIRCLE.value
self.cloud_stations_real.myprop.width = 10
self.cloud_stations_real.myprop.color = getIfromRGB([0,0,255])
self.cloud_stations_real.myprop.filled = True
self.cloud_stations_real.myprop.legendvisible = True
self.cloud_stations_real.myprop.legendfontsize = 14
self.cloud_stations_real.myprop.legendrelpos = 8
self.cloud_stations_real.myprop.renderingmode = Cloud_OGLRenderer.SHADER.value
# Adaptive marker sizing in screen pixels (converted to world units
# each frame) keeps stations visible at regional scale while limiting
# oversized symbols during deep zoom.
[docs]
self.station_marker_auto_size = True
[docs]
self.station_marker_px_target = 8.0
[docs]
self.station_marker_px_min = 4.0
[docs]
self.station_marker_px_max = 14.0
self._init_cloud()
self.find_minmax(True)
[docs]
self.gui_hydrometry = None
@staticmethod
[docs]
def _compute_adaptive_world_marker_width(
xmin: float,
xmax: float,
ymin: float,
ymax: float,
viewport_w_px: int,
viewport_h_px: int,
target_px: float,
min_px: float,
max_px: float,
) -> float | None:
"""Convert a target marker size in pixels to world units.
:return: width in world units, or ``None`` when inputs are invalid.
"""
if viewport_w_px <= 0 or viewport_h_px <= 0:
return None
dx = abs(float(xmax) - float(xmin))
dy = abs(float(ymax) - float(ymin))
if dx <= 1e-12 or dy <= 1e-12:
return None
px = max(float(min_px), min(float(target_px), float(max_px)))
world_per_px_x = dx / float(viewport_w_px)
world_per_px_y = dy / float(viewport_h_px)
# Use the larger world-per-pixel to avoid symbols collapsing on one axis.
world_per_px = max(world_per_px_x, world_per_px_y)
return px * world_per_px
[docs]
def _update_station_marker_width_from_view(self, **kwargs):
"""Update station marker width from current view/canvas dimensions."""
if not self.station_marker_auto_size:
return
xmin = kwargs.get('xmin')
ymin = kwargs.get('ymin')
xmax = kwargs.get('xmax')
ymax = kwargs.get('ymax')
mapviewer = self.get_mapviewer()
if mapviewer is None:
return
# Fallback to mapviewer current bounds when plot kwargs are absent.
if None in (xmin, ymin, xmax, ymax):
xmin = getattr(mapviewer, 'xmin', None)
ymin = getattr(mapviewer, 'ymin', None)
xmax = getattr(mapviewer, 'xmax', None)
ymax = getattr(mapviewer, 'ymax', None)
if None in (xmin, ymin, xmax, ymax):
return
try:
canvas = mapviewer.canvas
viewport_w_px, viewport_h_px = canvas.GetSize()
except Exception:
return
width_world = self._compute_adaptive_world_marker_width(
xmin=xmin,
xmax=xmax,
ymin=ymin,
ymax=ymax,
viewport_w_px=int(viewport_w_px),
viewport_h_px=int(viewport_h_px),
target_px=self.station_marker_px_target,
min_px=self.station_marker_px_min,
max_px=self.station_marker_px_max,
)
if width_world is not None:
new_width = float(width_world)
old_width = float(self.cloud_stations_real.myprop.width)
# Rebuild display list only when width meaningfully changes.
if abs(new_width - old_width) > max(1e-9, 1e-6 * max(abs(old_width), 1.0)):
self.cloud_stations_real.myprop.width = new_width
if hasattr(self.cloud_stations_real, 'reset_listogl'):
self.cloud_stations_real.reset_listogl()
[docs]
def _init_cloud(self):
"""
Initialize the cloud of stations
"""
real = self.hydrometry.get_names_xy()
real_dict = {}
for i in range(len(real[0])):
if not np.isnan(real[1][i]) and not np.isnan(real[2][i]):
real_dict[real[0][i]] = {'vertex' : wolfvertex(real[1][i], real[2][i])}
else:
logging.debug(f'Real station {real[0][i]} has no coordinates')
self.cloud_stations_real.add_vertex(cloud = real_dict)
[docs]
def show_properties(self):
"""
Show the properties of the object
"""
if self.gui_hydrometry is None:
self.gui_hydrometry = hydrometry_gui(self.hydrometry.credential, dir = self.hydrometry.dir)
else:
try:
self.gui_hydrometry.Show()
except:
self.gui_hydrometry = hydrometry_gui(self.hydrometry.credential, dir = self.hydrometry.dir)
self.cloud_stations_real.show_properties()
[docs]
def plot(self, **kwargs):
"""
Plot the object
"""
self._update_station_marker_width_from_view(**kwargs)
self.cloud_stations_real.plot(**kwargs)
[docs]
def plot_legend(self, **kwargs):
"""
Plot the legend of the object
"""
self.cloud_stations_real.plot_legend(**kwargs)
[docs]
def find_minmax(self, update=False):
"""
Find the minimum and maximum values of the object
"""
if update:
real = self.hydrometry.get_names_xy()
if len(real[0]) > 0:
x_real = real[1][np.isnan(real[1]) == False]
y_real = real[2][np.isnan(real[2]) == False]
self.xmin = float(np.min(x_real))
self.ymin = float(np.min(y_real))
self.xmax = float(np.max(x_real))
self.ymax = float(np.max(y_real))