diff --git a/src/View/Results/PlotHEnsemble.py b/src/View/Results/PlotHEnsemble.py new file mode 100644 index 00000000..40b37e80 --- /dev/null +++ b/src/View/Results/PlotHEnsemble.py @@ -0,0 +1,237 @@ +# PlotHEnsemble.py -- Pamhyr +# Copyright (C) 2023-2026 INRAE +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# -*- coding: utf-8 -*- + +import logging + +from functools import reduce +from datetime import datetime + +from tools import timer, trace, logger_exception +from View.Tools.PamhyrPlot import PamhyrPlot + +from PyQt5.QtCore import ( + QCoreApplication +) + +_translate = QCoreApplication.translate + +logger = logging.getLogger() + + +class PlotHEnsemble(PamhyrPlot): + def __init__(self, canvas=None, trad=None, toolbar=None, + results=None, reach_id=0, profile_id=[0], res_id=[0], + parent=None): + super(PlotHEnsemble, self).__init__( + canvas=canvas, + trad=trad, + data=results, + toolbar=toolbar, + parent=parent + ) + + self._mode = "time" + + self._parent = parent + self._current_reach_id = reach_id + self._current_profile_id = profile_id + self._current_res_id = res_id + self._timestamps = parent._timestamps + self._current_timestamp = max(self._timestamps) + + self.label_x = self._trad["unit_time_s"] + self.label_y = self._trad["unit_discharge"] + + self.label_discharge = _translate("Results", "Cross-section discharge") + self.label_discharge_max = _translate("Results", "Max discharge") + self.label_timestamp = _translate("Results", "Current timestamp") + + self._isometric_axis = False + + # self._auto_relim_update = False + # self._autoscale_update = False + + self._line_max = [] + self._line = [] + self._add_data_lines = [] + + @property + def results(self): + return self.data + + @results.setter + def results(self, results): + self.data = results + self._current_timestamp = max(self._timestamps) + + @timer + def draw(self, highlight=None): + self.canvas.axes.cla() + self.canvas.axes.grid(color='grey', linestyle='--', linewidth=0.5) + + if self.results is None: + return + + if len(self._current_res_id) < 1: + return + + for res_id in self._current_res_id: + if self.results[res_id] is None: + continue + + results = self.results[res_id] + reach = results.river.reach(self._current_reach_id) + + if reach.geometry.number_profiles == 0: + self._init = False + return + + self.draw_data(res_id) + + self.canvas.axes.set_xlabel( + self._trad["unit_time_s"], + color='black', fontsize=10 + ) + + self.canvas.axes.set_ylabel( + self._trad["unit_discharge"], + color='black', fontsize=10 + ) + + self.set_ticks_time_formater() + + self.draw_additional_data(0) + + self.enable_legend() + + self.canvas.draw_idle() + self.update_idle() + + self.draw_current() + self._init = True + + @timer + def draw_data(self, res_id): + results = self.results[res_id] + reach = results.river.reach(self._current_reach_id) + + colors = [ + ("q_min", self.color_plot_ensemble_min), + ("q_mean", self.color_plot_ensemble_mean), + ("q_max", self.color_plot_ensemble_max), + ] + + for t, c in colors: + q = results.get("table")[t] + profile = reach.profile(self._current_profile_id[0]) + + x = self._timestamps + y = q[:, profile.global_index] + # y = profile.get_key("Q") + + if res_id == 2: + label = f"Δ {self.label_discharge} {t} {profile.name}" + else: + label = f"{self.label_discharge} {t} {profile.name}" + + line, = self.canvas.axes.plot( + x, y, + label=label, + color=c, + # linestyle=self.linestyle[i % len(self.linestyle)], + **self.plot_default_kargs + ) + + self._line.append(line) + + def draw_additional_data(self, res_id): + results = self.results[res_id] + + self._add_data_lines = [] + + for data in results.get("additional_data"): + data = data._data + x, y = data['x'], data['y'] + legend = data['legend'] + unit = data['unit'] + + if data['type_x'] == 'time' and data['type_y'] == 'discharge': + line, = self.canvas.axes.plot( + x, y, marker="+", + label=legend + ' ' + unit + ) + + self._add_data_lines.append(line) + + self.enable_legend() + self.canvas.draw_idle() + + def draw_current(self): + self._current, = self.canvas.axes.plot( + [self._current_timestamp, self._current_timestamp], + self.canvas.axes.get_ylim(), + # label=self.label_timestamp, + color="grey", + linestyle="dashed", + lw=1., + ) + + def set_reach(self, reach_id): + self._current_reach_id = reach_id + self._current_profile_id = [0] + self.draw() + + def set_profile(self, profile_id): + self._current_profile_id = profile_id + # self.update() + self.draw() + + def set_result(self, res_id): + self._current_res_id = res_id + self.draw() + + def set_timestamp(self, timestamp): + self._current_timestamp = timestamp + self.update_current() + self.update_idle() + + def update(self): + if not self._init: + self.draw() + + self.update_current() + self.update_idle() + + def update_additional_data(self): + self.draw_additional_data(0) + self.update_idle() + + def update_all(self): + self._current_reach_id = self._parent._get_current_reach() + self._current_profile_id = self._parent._get_current_profiles_list() + self._current_res_id = self._parent._get_current_results() + self._current_timestamp = self._parent._get_current_timestamp() + self._init = False + self.update() + + def update_current(self): + y = self._current.get_ydata() + self._current.set_data( + [self._current_timestamp, self._current_timestamp], + y + ) diff --git a/src/View/Results/Window.py b/src/View/Results/Window.py index df554549..619f832e 100644 --- a/src/View/Results/Window.py +++ b/src/View/Results/Window.py @@ -73,6 +73,7 @@ from View.Results.PlotH import PlotH from View.Results.PlotSedReach import PlotSedReach from View.Results.PlotSedProfile import PlotSedProfile from View.Results.PlotRKCEnsemble import PlotRKCEnsemble +from View.Results.PlotHEnsemble import PlotHEnsemble from View.Results.CustomPlot.Plot import CustomPlot from View.Results.CustomPlot.CustomPlotValuesSelectionDialog import ( @@ -1510,7 +1511,7 @@ class ResultsEnsembleWindow(ResultsWindow): self.plot_layout_2.addWidget(self.toolbar_2) self.plot_layout_2.addWidget(self.canvas_2) - self.plot_h = PlotH( + self.plot_h = PlotHEnsemble( canvas=self.canvas_2, results=self._results, reach_id=0,