PlotKPZ.py 7.93 KiB
# PlotKPC.py -- Pamhyr
# Copyright (C) 2023-2024  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 <https://www.gnu.org/licenses/>.

# -*- coding: utf-8 -*-

import logging

from tools import timer
from View.Tools.PamhyrPlot import PamhyrPlot

from PyQt5.QtCore import (
    QCoreApplication
)

_translate = QCoreApplication.translate

logger = logging.getLogger()


class PlotKPZ(PamhyrPlot):
    def __init__(self, canvas=None, trad=None, data=None, toolbar=None,
                 parent=None):
        super(PlotKPZ, self).__init__(
            canvas=canvas,
            trad=trad,
            data=data,
            toolbar=toolbar,
            parent=parent
        )

        self._isometric_axis = False

        self.line_kp_zgl = []
        self.line_kp_zmin = None
        self.line_kp_zmin_zmax = None
        self.line_kp_zmin_zmax_highlight = None

        self.label_x = _translate("MainWindow_reach", "Kp (m)")
        self.label_y = _translate("MainWindow_reach", "Height (m)")

        self.before_plot_selected = None
        self.plot_selected = None
        self.after_plot_selected = None

    @timer
    def draw(self, highlight=None):
        self.init_axes()

        if self.data is None:
            return

        if len(self.data.profiles) == 0:
            return

        profiles_defined = any(
            filter(
                lambda profile: len(profile) > 0,
                self.data.profiles
            )
        )

        if not profiles_defined:
            self._init = False
            return

        self.draw_z_line()
        self.draw_z_line_highlight()
        self.draw_current()
        self.draw_gl()
        self.draw_bottom()

        self.idle()
        self._init = True

    def draw_z_line(self):
        kp = self.data.get_kp_complete_profiles()
        z_min = self.data.get_z_min()
        z_max = self.data.get_z_max()

        self.line_kp_zmin_zmax = self.canvas.axes.vlines(
            x=kp,
            ymin=z_min, ymax=z_max,
            color=self.color_plot,
            lw=1.
        )

    def draw_z_line_highlight(self):
        if self._highlight_data is not None:
            kp = self.data.get_kp_complete_profiles()
            z_min = self.data.get_z_min()
            z_max = self.data.get_z_max()

            kp_min, kp_max = highlight

            indexes = list(
                map(
                    lambda x: x[0],
                    filter(
                        lambda x: kp_min <= x[1] <= kp_max,
                        enumerate(kp)
                    )
                )
            )

            def indexes_filter(data): return list(
                map(
                    lambda x: x[1],
                    filter(
                        lambda x: x[0] in indexes,
                        enumerate(data)
                    )
                )
            )

            ikp = indexes_filter(kp)
            imin = indexes_filter(z_min)
            imax = indexes_filter(z_max)

            self.line_kp_zmin_zmax_highlight = self.canvas.axes.vlines(
                x=ikp,
                ymin=imin, ymax=imax,
                color=self.color_plot_highlight,
                lw=1.
            )

    def draw_current(self):
        kp = self.data.get_kp_complete_profiles()
        z_min = self.data.get_z_min()
        z_max = self.data.get_z_max()

        self.plot_selected, = self.canvas.axes.plot(
            (kp[0], kp[0]),
            (z_min[0], z_max[0]),
            color=self.color_plot_current, lw=1.5
        )
        self.plot_selected.set_visible(False)

        self.before_plot_selected, = self.canvas.axes.plot(
            (kp[0], kp[0]),
            (z_min[0], z_max[0]),
            color=self.color_plot_previous, lw=1.5, linestyle='--'
        )
        self.before_plot_selected.set_visible(False)

        self.after_plot_selected, = self.canvas.axes.plot(
            (kp[0], kp[0]),
            (z_min[0], z_max[0]),
            color=self.color_plot_next, lw=1.5, linestyle='--'
        )
        self.after_plot_selected.set_visible(False)

    def draw_gl(self):
        kp = self.data.get_kp_complete_profiles()

        ind = 0
        self.line_kp_zgl = []
        for z in self.data.get_guidelines_z():
            # Is incomplete guideline?
            if len(z) != len(kp):
                continue

            self.line_kp_zgl.append(
                self.canvas.axes.plot(
                    kp, z, lw=1., color=self.colors[ind]
                )
            )
            ind += 1

    def draw_bottom(self):
        kp = self.data.get_kp_complete_profiles()
        z_min = self.data.get_z_min()

        self.line_kp_zmin, = self.canvas.axes.plot(
            kp, z_min,
            linestyle=":", lw=1.5,
            color='lightgrey'
        )

    @timer
    def update(self):
        if not self._init:
            self.draw()
            return

        self.update_gl()
        self.update_current()

        self.update_idle()

    def update_current(self):
        if self._current_data_update:
            ind = self._current_data
            before = ind - 1
            after = ind + 1

            self.before_plot_selected.set_visible(False)
            self.plot_selected.set_visible(False)
            self.after_plot_selected.set_visible(False)

            if 0 <= before < self.data.number_profiles:
                profile = self.data.profile(before)
                if len(profile) > 0:
                    kp_i = profile.kp
                    z_min_i = profile.z_min()
                    z_max_i = profile.z_max()

                    self.before_plot_selected.set_data(
                        (kp_i, kp_i),
                        (z_min_i, z_max_i)
                    )
                    self.before_plot_selected.set_visible(True)

            if 0 <= ind < self.data.number_profiles:
                profile = self.data.profile(ind)
                if len(profile) > 0:
                    kp_i = profile.kp
                    z_min_i = profile.z_min()
                    z_max_i = profile.z_max()

                    self.plot_selected.set_data(
                        (kp_i, kp_i),
                        (z_min_i, z_max_i)
                    )
                    self.plot_selected.set_visible(True)

            if 0 <= after < self.data.number_profiles:
                profile = self.data.profile(after)
                if len(profile) > 0:
                    kp_i = profile.kp
                    z_min_i = profile.z_min()
                    z_max_i = profile.z_max()

                    self.after_plot_selected.set_data(
                        (kp_i, kp_i),
                        (z_min_i, z_max_i)
                    )
                    self.after_plot_selected.set_visible(True)

    def update_gl(self):
        if self._current_data_update:
            return

        kp = self.data.get_kp_complete_profiles()
        z_min = self.data.get_z_min()
        z_max = self.data.get_z_max()

        self.line_kp_zmin.set_data(kp, z_min)

        self.line_kp_zmin_zmax.remove()
        self.line_kp_zmin_zmax = self.canvas.axes.vlines(
            x=kp,
            ymin=z_min, ymax=z_max,
            color='r', lw=1.
        )

        z_complete = self.data.get_guidelines_z()
        try:
            for i in range(len(self.line_kp_zgl)):
                self.line_kp_zgl[i][0].set_data(
                    kp, z_complete[i]
                )
        except Exception as e:
            logger.warning(f"Failed to update graphic KPZ: {e}")