Source code for wolfhece._alaro_manager

"""Alaro forecasts companion for WolfMapViewer.

All Alaro-related menu and command logic lives here.
``WolfMapViewer`` holds a single instance as ``self._alaro`` and exposes
one-line delegators so external callers remain unaffected.
"""
from __future__ import annotations

import logging
import os
import sys
from datetime import datetime
from pathlib import Path
from typing import TYPE_CHECKING

import wx

from .PyTranslate import _
from .irm_alaro import IRM_Alaro, GribFiles, _convert_col2date_str

if TYPE_CHECKING:
    from .PyDraw import WolfMapViewer

__all__ = ['AlaroManager']


[docs] class AlaroManager: """Companion object that owns Alaro menu state and actions.""" def __init__(self, viewer: 'WolfMapViewer') -> None:
[docs] self._viewer = viewer
[docs] self.active: IRM_Alaro | None = None
[docs] self._menu: wx.Menu | None = None
[docs] self._menu_ftp: wx.Menu | None = None
[docs] self._menu_videos: wx.Menu | None = None
[docs] def menu_build(self) -> None: """Create and append the Alaro menu to the viewer menubar.""" if self._menu is not None: return v = self._viewer self._menu = wx.Menu() v.menubar.Append(self._menu, _('Alaro forecasts')) self._menu_ftp = wx.Menu() self._menu.AppendSubMenu(self._menu_ftp, _('FTP server')) item_show_grid = self._menu.Append(wx.ID_ANY, _("Show grid"), _("Show the Alaro grid on the viewer")) item_list_ftp = self._menu_ftp.Append(wx.ID_ANY, _("List runs on FTP server"), _("List available Alaro runs on the FTP server")) item_dl = self._menu_ftp.Append(wx.ID_ANY, _("Download runs"), _("Download Alaro runs")) item_dl_rt = self._menu_ftp.Append(wx.ID_ANY, _("Download runs - Only Rain and Temperature"), _("Download Alaro runs")) item_load_run = self._menu.Append(wx.ID_ANY, _("Load run"), _("Load an available Alaro run")) item_add_fc = self._menu.Append(wx.ID_ANY, _("Add forecast(s) as array(s)"), _("Show Alaro forecasts on the viewer as WolfArray - Total precipitation [mm]")) item_add_all = self._menu.Append(wx.ID_ANY, _("Add all forecasts as arrays"), _("Show all available Alaro runs on the viewer as WolfArrays - Total precipitation [mm]")) item_plot_xy = self._menu.Append(wx.ID_ANY, _("Plot for XY"), _("Plot the next selected point")) self._menu_videos = wx.Menu() self._menu.AppendSubMenu(self._menu_videos, _('Videos')) item_one_run = self._menu_videos.Append(wx.ID_ANY, _("Total precip. [mm] - One run"), _("Create a video of the Total Precipitation forecasts for a specific run")) item_multi_run = self._menu_videos.Append(wx.ID_ANY, _("Total precip. [mm] - Multiple runs"), _("Create videos of the Total Precipitation forecasts for all runs")) item_compare = self._menu_videos.Append(wx.ID_ANY, _("Rain intensity [mm/h] - Comparison"), _("Create a video comparing the Rain Intensity forecasts of different runs")) self._menu.Bind(wx.EVT_MENU, self._on_show_grid, item_show_grid) self._menu_ftp.Bind(wx.EVT_MENU, self._on_list_ftp, item_list_ftp) self._menu_ftp.Bind(wx.EVT_MENU, self._on_download, item_dl) self._menu_ftp.Bind(wx.EVT_MENU, self._on_download_rain_temp, item_dl_rt) self._menu.Bind(wx.EVT_MENU, self._on_load_run, item_load_run) self._menu.Bind(wx.EVT_MENU, self._on_add_forecasts, item_add_fc) self._menu.Bind(wx.EVT_MENU, self._on_add_all_forecasts, item_add_all) self._menu.Bind(wx.EVT_MENU, self._on_plot_xy, item_plot_xy) self._menu_videos.Bind(wx.EVT_MENU, self._on_one_run_video, item_one_run) self._menu_videos.Bind(wx.EVT_MENU, self._on_multi_run_video, item_multi_run) self._menu_videos.Bind(wx.EVT_MENU, self._on_compare_video, item_compare)
[docs] def _ensure_active(self) -> None: if self.active is None: self.active = IRM_Alaro()
[docs] def _on_list_ftp(self, event: wx.MenuEvent) -> None: v = self._viewer self._ensure_active() runs = self.active.run_dates_str for run in runs: logging.info(f'Available Alaro run: {run}') dlg = wx.MessageDialog(v, _('Available Alaro runs:\n\n') + '\n'.join(runs), _('Alaro runs'), wx.OK | wx.ICON_INFORMATION) dlg.ShowModal()
[docs] def _on_compare_video(self, event: wx.MenuEvent) -> None: """Rain intensity [mm/h] - Comparison video.""" from datetime import timezone as tz v = self._viewer self._ensure_active() data_dir = self.active.data_directory dates = self.active.list_run_dates_cached() dates_str = [datetime.strptime(date, '%Y%m%d%H').replace(tzinfo=tz.utc) for date in dates] dates_str = [date.strftime('%Y-%m-%d %H+00') for date in dates_str] dlg = wx.MultiChoiceDialog(v, _('Choose one or multiple Alaro runs to treat'), _('Choose Alaro runs'), choices=dates_str) ret = dlg.ShowModal() if ret == wx.ID_CANCEL: dlg.Destroy() return choices = dlg.GetSelections() dlg.Destroy() if len(choices) == 0: logging.warning(_('No Alaro run selected')) return dates = [dates[choice] for choice in choices] dlg = wx.FileDialog(v, _('Choose output video filename'), wildcard='mp4 (*.mp4)|*.mp4', defaultDir=str(data_dir), defaultFile='Alaro_RainIntensity_Comparison.mp4', style=wx.FD_SAVE) ret = dlg.ShowModal() if ret == wx.ID_OK: output_dir = dlg.GetPath() dlg.Destroy() self.active.reset_gdf() pgbar = wx.ProgressDialog(_('Loading Alaro runs'), _('Loading data...'), maximum=100, parent=v, style=wx.PD_APP_MODAL | wx.PD_AUTO_HIDE) pgbar.Pulse(_('Creating video...')) outfile = self.active.video_gradient_cumulated_rain_compare(Path(output_dir), run_dates=dates) pgbar.Update(100, _('Video created')) pgbar.Destroy() logging.info(f'Videos: {dates}') dlg = wx.MessageDialog(v, _('Video created: {}\n\nDo you want to launch it ?').format(outfile), _('Video created'), wx.YES_NO | wx.ICON_QUESTION) ret = dlg.ShowModal() if ret == wx.ID_YES: if sys.platform == 'win32': os.startfile(outfile) dlg.Destroy()
[docs] def _on_one_run_video(self, event: wx.MenuEvent) -> None: """Total precip. [mm] - One run video.""" from datetime import timezone as tz v = self._viewer self._ensure_active() data_dir = self.active.data_directory dates = self.active.list_run_dates_cached() dates_str = [datetime.strptime(date, '%Y%m%d%H').replace(tzinfo=tz.utc) for date in dates] dates_str = [date.strftime('%Y-%m-%d %H+00') for date in dates_str] dlg = wx.SingleChoiceDialog(v, _('Choose one Alaro run to treat'), _('Choose Alaro run'), choices=dates_str) ret = dlg.ShowModal() if ret == wx.ID_CANCEL: dlg.Destroy() return choice = dlg.GetSelection() dlg.Destroy() if choice == wx.NOT_FOUND: logging.warning(_('No Alaro run selected')) return dates = [dates[choice]] dlg = wx.FileDialog(v, _('Choose output video filename'), wildcard='mp4 (*.mp4)|*.mp4', defaultDir=str(data_dir), defaultFile=f"Alaro_cumulated_rain_{dates[0]}.mp4", style=wx.FD_SAVE) ret = dlg.ShowModal() if ret == wx.ID_OK: output_dir = dlg.GetPath() dlg.Destroy() self.active.reset_gdf() pgbar = wx.ProgressDialog(_('Loading Alaro runs'), _('Loading data...'), maximum=100, parent=v, style=wx.PD_APP_MODAL | wx.PD_AUTO_HIDE) pgbar.Pulse(_('Creating video...')) outfile = self.active.video_cumulated_rain(dates[0], Path(output_dir)) pgbar.Update(100, _('Video created')) pgbar.Destroy() dlg = wx.MessageDialog(v, _('Video created: {}\n\nDo you want to launch it ?').format(outfile), _('Video created'), wx.YES_NO | wx.ICON_QUESTION) ret = dlg.ShowModal() if ret == wx.ID_YES: if sys.platform == 'win32': os.startfile(outfile) dlg.Destroy()
[docs] def _on_multi_run_video(self, event: wx.MenuEvent) -> None: """Total precip. [mm] - Multiple runs video.""" from datetime import timezone as tz v = self._viewer self._ensure_active() data_dir = self.active.data_directory dates = self.active.list_run_dates_cached() dates_str = [datetime.strptime(date, '%Y%m%d%H').replace(tzinfo=tz.utc) for date in dates] dates_str = [date.strftime('%Y-%m-%d %H+00') for date in dates_str] dlg = wx.MultiChoiceDialog(v, _('Choose multiple Alaro runs to treat'), _('Choose Alaro runs'), choices=dates_str) ret = dlg.ShowModal() if ret == wx.ID_CANCEL: dlg.Destroy() return choices = dlg.GetSelections() dlg.Destroy() if len(choices) == 0: logging.warning(_('No Alaro run selected')) return dates = [dates[choice] for choice in choices] dlg = wx.DirDialog(v, _('Choose output video directory'), defaultPath=str(data_dir), style=wx.DD_DEFAULT_STYLE | wx.DD_DIR_MUST_EXIST) ret = dlg.ShowModal() if ret == wx.ID_OK: output_dir = dlg.GetPath() dlg.Destroy() self.active.reset_gdf() pgbar = wx.ProgressDialog(_('Loading Alaro runs'), _('Loading data...'), maximum=100, parent=v, style=wx.PD_APP_MODAL | wx.PD_AUTO_HIDE) pgbar.Pulse(_('Creating video...')) self.active.videos_cumulated_rain_allforecasts(output_dir, run_dates=dates) pgbar.Update(100, _('Video created')) pgbar.Destroy()
[docs] def _on_download(self, event: wx.MenuEvent) -> None: v = self._viewer self._ensure_active() pgbar = wx.ProgressDialog(_('Downloading Alaro runs'), _('Downloading data...'), maximum=100, parent=v, style=wx.PD_APP_MODAL | wx.PD_AUTO_HIDE) pgbar.Pulse(_('Downloading Alaro runs...')) ret = self.active.download_all_available_files() pgbar.Update(100, _('Alaro runs downloaded')) pgbar.Destroy() for run_id in ret: logging.info(f'Downloaded Alaro run: {run_id}')
[docs] def _on_download_rain_temp(self, event: wx.MenuEvent) -> None: v = self._viewer self._ensure_active() pgbar = wx.ProgressDialog(_('Downloading Alaro runs'), _('Downloading data...'), maximum=100, parent=v, style=wx.PD_APP_MODAL | wx.PD_AUTO_HIDE) pgbar.Pulse(_('Downloading Alaro runs...')) ret = self.active.download_TotalPrecipitations_available_files() pgbar.Update(100, _('Alaro runs downloaded')) pgbar.Destroy() for run_id in ret: logging.info(f'Downloaded Alaro run: {run_id}')
[docs] def _on_load_run(self, event: wx.MenuEvent) -> None: from datetime import timezone as tz v = self._viewer self._ensure_active() dates = self.active.list_run_dates_cached() dates_str = [datetime.strptime(date, '%Y%m%d%H').replace(tzinfo=tz.utc) for date in dates] dates_str = [date.strftime('%Y-%m-%d %H+00') for date in dates_str] dlg = wx.MultiChoiceDialog(v, _('Choose one or multiple Alaro runs to load'), _('Choose Alaro runs'), choices=dates_str) ret = dlg.ShowModal() if ret == wx.ID_CANCEL: dlg.Destroy() return choices = dlg.GetSelections() dlg.Destroy() if len(choices) == 0: logging.warning(_('No Alaro run selected')) return dates = [dates[choice] for choice in choices] self.active.reset_gdf() pgbar = wx.ProgressDialog(_('Loading Alaro runs'), _('Loading data...'), maximum=100, parent=v, style=wx.PD_APP_MODAL | wx.PD_AUTO_HIDE) pgbar.Pulse(_('Loading Alaro runs...')) self.active.load_grib_data_to_gdf(GribFiles.FILE_TotPrecip, dates) pgbar.Update(100, _('Alaro runs loaded')) pgbar.Destroy() logging.info(f'Loaded Alaro run: {dates}')
[docs] def _on_add_forecasts(self, event: wx.MenuEvent) -> None: v = self._viewer self._ensure_active() if self.active._gdf is None: logging.warning(_('No Alaro run loaded -- Please load a run first')) return columns = self.active.get_forecast_columns() columns_str = [_convert_col2date_str(col) for col in columns] dlg = wx.MultiChoiceDialog(v, _('Choose one or multiple Alaro forecast to show'), _('Choose Alaro forecasts'), choices=columns_str) ret = dlg.ShowModal() if ret == wx.ID_CANCEL: dlg.Destroy() return choices = dlg.GetSelections() dlg.Destroy() if len(choices) == 0: logging.warning(_('No Alaro forecast selected')) return columns = [columns[choice] for choice in choices] pgbar = wx.ProgressDialog(_('Loading Alaro forecasts'), _('Loading data...'), maximum=100, parent=v, style=wx.PD_APP_MODAL | wx.PD_AUTO_HIDE) pgbar.Pulse(_('Loading Alaro forecasts...')) arrays = self.active.forecasts_to_arrays(columns) pgbar.Update(100, _('Alaro forecasts loaded')) pgbar.Destroy() if len(arrays) == 0: logging.warning(_('No Alaro forecast to show')) return for array in arrays: array.array *= 1000.0 v.add_object('array', newobj=array, id=array.idx) v.Refresh() logging.info(_('Alaro forecasts added to the viewer'))
[docs] def _on_show_grid(self, event: wx.MenuEvent) -> None: v = self._viewer self._ensure_active() if self.active._zones is None: self.active._prepare_Zones_from_grib(GribFiles.FILE_TotPrecip, self.active.run_dates[0]) self.active._zones.prep_listogl() v.add_object('vector', newobj=self.active._zones, id='Alaro_grid') v.Refresh()
[docs] def _on_add_all_forecasts(self, event: wx.MenuEvent) -> None: v = self._viewer self._ensure_active() if self.active._gdf is None: logging.warning(_('No Alaro run loaded -- Please load a run first')) return pgbar = wx.ProgressDialog(_('Loading Alaro forecasts'), _('Loading data...'), maximum=100, parent=v, style=wx.PD_APP_MODAL | wx.PD_AUTO_HIDE) pgbar.Pulse(_('Loading Alaro forecasts...')) arrays = self.active.forecasts_to_arrays() pgbar.Update(100, _('Alaro forecasts loaded')) pgbar.Destroy() if len(arrays) == 0: logging.warning(_('No Alaro forecast to show')) return for array in arrays: array.array *= 1000.0 v.add_object('array', newobj=array, id=array.idx, ToCheck=False) v.Refresh() logging.info(_('Alaro forecasts added to the viewer'))
[docs] def _on_plot_xy(self, event: wx.MenuEvent) -> None: v = self._viewer self._ensure_active() if self.active._gdf is None: logging.warning(_('No Alaro run loaded -- Please load a run first')) return v.start_action('plot alaro xy', _('Plot Alaro forecasts for a specific point -- Right click to select points'))