From cd69d56c7441e27e2367a544303a9f1d42234025 Mon Sep 17 00:00:00 2001
From: Pierre-Antoine Rouby <pierre-antoine.rouby@inrae.fr>
Date: Wed, 12 Apr 2023 15:42:21 +0200
Subject: [PATCH] geometry: Guidelines computation.

---
 src/Model/Geometry/Profile.py       |  10 +++
 src/Model/Geometry/Reach.py         | 106 +++++++++++++++++++++++++++-
 src/View/Geometry/GeometryWindow.py |  77 ++++++++++----------
 src/tools.py                        |  19 +++++
 4 files changed, 172 insertions(+), 40 deletions(-)
 create mode 100644 src/tools.py

diff --git a/src/Model/Geometry/Profile.py b/src/Model/Geometry/Profile.py
index 1d9677bb..477e6ef5 100644
--- a/src/Model/Geometry/Profile.py
+++ b/src/Model/Geometry/Profile.py
@@ -91,3 +91,13 @@ class Profile(object):
     @profile_type.setter
     def profile_type(self, value: str):
         self._profile_type = value
+
+
+    def named_points(self):
+        """List of named point
+
+        Returns:
+            The list of named point
+        """
+        return [point for point in self._points
+                if point.point_is_named()]
diff --git a/src/Model/Geometry/Reach.py b/src/Model/Geometry/Reach.py
index c7492c12..6e2f0d39 100644
--- a/src/Model/Geometry/Reach.py
+++ b/src/Model/Geometry/Reach.py
@@ -7,6 +7,8 @@ from typing import List
 from copy import deepcopy
 from operator import itemgetter
 
+from tools import flatten
+
 from Model.Geometry.Profile import Profile
 from Model.Geometry.ProfileXYZ import ProfileXYZ
 
@@ -17,6 +19,9 @@ class Reach:
         self._parent = parent
         self._profiles: List[Profile] = []
 
+        self._guidelines_is_valid = False
+        self._guidelines = {}
+
         # Copy/Paste
         self.__list_copied_profiles = []
 
@@ -138,7 +143,16 @@ class Reach:
             reverse=is_reversed
         )
 
-    def z_min(self):
+    def get_x(self):
+        return [profile.x() for profile in self.profiles]
+
+    def get_y(self):
+        return [profile.y() for profile in self.profiles]
+
+    def get_z(self):
+        return [profile.z() for profile in self.profiles]
+
+    def get_z_min(self):
         """List of z min for each profile
 
         Returns:
@@ -146,7 +160,7 @@ class Reach:
         """
         return [profile.z_min() for profile in self._data.profiles]
 
-    def z_max(self):
+    def get_z_max(self):
         """List of z max for each profile
 
         Returns:
@@ -154,7 +168,7 @@ class Reach:
         """
         return [profile.z_max() for profile in self._data.profiles]
 
-    def kp(self):
+    def get_kp(self):
         """List of profiles kp
 
         Returns:
@@ -162,6 +176,92 @@ class Reach:
         """
         return [profile.kp for profile in self._data.profiles]
 
+    ##############
+    # GUIDELINES #
+    ##############
+
+    def _compute_guidelines_cache(self, guide_set, named_points):
+        # Reset guide lines cache
+        self._guidelines = {}
+
+        # Make a list of point for each guideline
+        for guide in guide_set:
+            self._guidelines[guide] = flatten(
+                map(
+                    lambda l: list(
+                        # Filter point with name (we assume we have
+                        # only one point by profile)
+                        filter(
+                            lambda p: p.name == guide,
+                            l
+                        )
+                    ),
+                    named_points
+                )
+            )
+
+    def compute_guidelines(self):
+        """Compute reach guideline and check if is valid for all profiles
+
+        Returns:
+            True if all guide line is valid
+        """
+        # Get all point contains into a guideline
+        named_points = [profile.named_points() for profile in self._profiles]
+        points_name = list(
+            map(
+                lambda lst: list(map(lambda p: p.name, lst)),
+                named_points
+            )
+        )
+
+        # Get all guide line name
+        guide_set = reduce(
+            lambda acc, x: set(x).union(acc),
+            points_name
+        )
+
+        # All guide line is valid
+        is_ok = reduce(
+            bool.__and__,
+            map(
+                lambda l: len(set(l).symmetric_difference(guide_set)) == 0,
+                points_name
+            )
+        )
+        self._guidelines_is_valid = is_ok
+
+        # Compute guideline and put data in cache
+        self._compute_guidelines_cache(guide_set, named_points)
+
+        return is_ok
+
+    def _map_guidelines_points(self, func):
+        return list(
+            # Map for each guideline
+            map(
+                lambda k: list(
+                    # Apply function FUNC on each point of guideline
+                    map(
+                        func,
+                        self._guidelines[k],
+                    )
+                ),
+                self._guidelines
+            )
+        )
+
+    def get_guidelines_x(self):
+        return self._map_guidelines_points(lambda p: p.x)
+
+    def get_guidelines_y(self):
+        return self._map_guidelines_points(lambda p: p.y)
+
+    def get_guidelines_z(self):
+        return self._map_guidelines_points(lambda p: p.z)
+
+    # Sort
+
     def sort_ascending(self):
         """Sort profiles by increasing KP
 
diff --git a/src/View/Geometry/GeometryWindow.py b/src/View/Geometry/GeometryWindow.py
index 4282d7ac..a28e1f84 100644
--- a/src/View/Geometry/GeometryWindow.py
+++ b/src/View/Geometry/GeometryWindow.py
@@ -149,17 +149,17 @@ class GeometryWindow(QMainWindow, WindowToolKit):
         for selected_row in list_selected_row[:5]:
             selected_row = int(selected_row)
             profile_identifier = self._reach.get_profile_selected_identifier(selected_row)
-            Pk = self._reach.get_pk_i(selected_row)
+            Kp = self._reach.get_kp_i(selected_row)
             profile_name = self._reach.get_profile_name(selected_row)
 
             if len(self.list_second_window) == 0:
                 self.second_window = window_profileXYZ.View(
                     selected_row + 1,
                     self._reach.get_profile_via_identifier(profile_identifier),
-                    pk=Pk, profile_name="", parent=self
+                    kp=Kp, profile_name="", parent=self
                 )
                 self.second_window.window_title(
-                    pk=Pk,
+                    kp=Kp,
                     profile_name=profile_name,
                     profile_selected_num=selected_row
                 )
@@ -172,7 +172,7 @@ class GeometryWindow(QMainWindow, WindowToolKit):
                 if profile_identifier in self.list_row:
                     self.list_second_window[self.list_row.index(profile_identifier)]\
                         .window_title(
-                            pk=Pk, profile_name=profile_name,
+                            kp=Kp, profile_name=profile_name,
                             profile_selected_num=selected_row
                         )
 
@@ -187,10 +187,10 @@ class GeometryWindow(QMainWindow, WindowToolKit):
                     second_window1 = window_profileXYZ.View(
                         selected_row + 1,
                         self._reach.get_profile_via_identifier(profile_identifier),
-                        pk=Pk, profile_name="", parent=self
+                        kp=Kp, profile_name="", parent=self
                     )
                     second_window1.window_title(
-                        pk=Pk, profile_name=profile_name,
+                        kp=Kp, profile_name=profile_name,
                         profile_selected_num=selected_row
                     )
 
@@ -253,14 +253,16 @@ class GeometryWindow(QMainWindow, WindowToolKit):
 
         self.plot_selected_1, = self.ui.canvas_1.axes.plot(
             self._reach.get_x_profile_i(0),
-            self._reach.get_y_profile_i(0), lw=1., markersize=3,
+            self._reach.get_y_profile_i(0),
+            lw=1., markersize=3,
             marker='+', color="b"
         )
 
         self.plot_selected_1.set_visible(False)
         self.before_plot_selected_1, = self.ui.canvas_1.axes.plot(
             self._reach.get_x_profile_i(0),
-            self._reach.get_y_profile_i(0), lw=1., markersize=3,
+            self._reach.get_y_profile_i(0),
+            lw=1., markersize=3,
             marker='+', color="k", linestyle="--"
         )
 
@@ -268,7 +270,8 @@ class GeometryWindow(QMainWindow, WindowToolKit):
 
         self.after_plot_selected_1, = self.ui.canvas_1.axes.plot(
             self._reach.get_x_profile_i(0),
-            self._reach.get_y_profile_i(0), lw=1., markersize=3,
+            self._reach.get_y_profile_i(0),
+            lw=1., markersize=3,
             marker='+', color="m", linestyle='--'
         )
 
@@ -284,54 +287,54 @@ class GeometryWindow(QMainWindow, WindowToolKit):
         self.ui.canvas_2.axes.cla()
         self.ui.canvas_2.axes.grid(color='grey', linestyle='--', linewidth=0.5)
         self.ui.canvas_2.axes.set_xlabel(
-            _translate("MainWindow_reach", "Pk (m)"), color='green', fontsize=12
+            _translate("MainWindow_reach", "Kp (m)"), color='green', fontsize=12
         )
         self.ui.canvas_2.axes.set_ylabel(
             _translate("MainWindow_reach", "Cote (m)"), color='green', fontsize=12
         )
 
-        self.get_pk = self._reach.get_pk()
+        self.get_kp = self._reach.get_kp()
         self.get_z_min = self._reach.get_z_min()
         self.get_z_max = self._reach.get_z_max()
 
-        self.line_pk_zmin_zmax = self.ui.canvas_2.axes.vlines(
-            x=self.get_pk,
+        self.line_kp_zmin_zmax = self.ui.canvas_2.axes.vlines(
+            x=self.get_kp,
             ymin=self.get_z_min, ymax=self.get_z_max,
             color='r', lw=1.
         )
 
         self.plot_selected_2, = self.ui.canvas_2.axes.plot(
-            (self.get_pk[0], self.get_pk[0]),
+            (self.get_kp[0], self.get_kp[0]),
             (self.get_z_min[0], self.get_z_max[0]),
             color='b', lw=1.8
         )
         self.plot_selected_2.set_visible(False)
 
         self.before_plot_selected_2, = self.ui.canvas_2.axes.plot(
-            (self.get_pk[0], self.get_pk[0]),
+            (self.get_kp[0], self.get_kp[0]),
             (self.get_z_min[0], self.get_z_max[0]),
             color='k', lw=1.6, linestyle='--'
         )
         self.before_plot_selected_2.set_visible(False)
 
         self.after_plot_selected_2, = self.ui.canvas_2.axes.plot(
-            (self.get_pk[0], self.get_pk[0]),
+            (self.get_kp[0], self.get_kp[0]),
             (self.get_z_min[0], self.get_z_max[0]),
             color='m', lw=1.6, linestyle='--'
         )
         self.after_plot_selected_2.set_visible(False)
 
         try:
-            self.line_pk_zld = [
+            self.line_kp_zld = [
                 self.ui.canvas_2.axes.plot(
-                    self._reach.get_pk(), i, lw=1.
+                    self._reach.get_kp(), i, lw=1.
                 ) for i in self.model.z_complete_guideline()
             ]
         except:
             print("TODO")
 
-        self.line_pk_zmin, = self.ui.canvas_2.axes.plot(
-            self.get_pk, self.get_z_min,
+        self.line_kp_zmin, = self.ui.canvas_2.axes.plot(
+            self.get_kp, self.get_z_min,
             linestyle=":", lw=1.8,
             color='lightgrey'
         )
@@ -363,23 +366,23 @@ class GeometryWindow(QMainWindow, WindowToolKit):
     def update_graphic_2(self):
         self.tableView.model().blockSignals(True)
 
-        get_pk = self._reach.get_pk()
+        get_kp = self._reach.get_kp()
         get_z_min = self._reach.get_z_min()
         get_z_max = self._reach.get_z_max()
 
-        self.line_pk_zmin.set_data(get_pk, get_z_min)
+        self.line_kp_zmin.set_data(get_kp, get_z_min)
 
-        self.line_pk_zmin_zmax.remove()
-        self.line_pk_zmin_zmax = self.ui.canvas_2.axes.vlines(
-            x=get_pk,
+        self.line_kp_zmin_zmax.remove()
+        self.line_kp_zmin_zmax = self.ui.canvas_2.axes.vlines(
+            x=get_kp,
             ymin=get_z_min, ymax=get_z_max,
             color='r', lw=1.
         )
 
         try:
-            for i in range(len(self.line_pk_zld)):
-                self.line_pk_zld[i][0].set_data(
-                    get_pk, self.model.z_complete_guideline()[i]
+            for i in range(len(self.line_kp_zld)):
+                self.line_kp_zld[i][0].set_data(
+                    get_kp, self.model.z_complete_guideline()[i]
                 )
         except:
             print("TODO")
@@ -618,12 +621,12 @@ class GeometryWindow(QMainWindow, WindowToolKit):
         self.tableView.model().blockSignals(False)
 
     def select_plot_graphic_2(self, ind: int):
-        get_pk_i = self.get_pk_i(ind)
+        get_kp_i = self.get_kp_i(ind)
         get_z_min_i = self.get_z_min_i(ind)
         get_z_max_i = self.get_z_max_i(ind)
 
         if 0 <= ind < self.model.rowCount():
-            self.plot_selected_2.set_data((get_pk_i, get_pk_i),
+            self.plot_selected_2.set_data((get_kp_i, get_kp_i),
                                           (get_z_min_i, get_z_max_i))
             self.plot_selected_2.set_visible(True)
 
@@ -651,12 +654,12 @@ class GeometryWindow(QMainWindow, WindowToolKit):
     def select_before_plot_selected_2(self, ind: int):
         if 0 <= ind < self.model.rowCount():
             t0 = time.time()
-            get_pk_i = self.get_pk_i(ind)
+            get_kp_i = self.get_kp_i(ind)
             get_z_min_i = self.get_z_min_i(ind)
             get_z_max_i = self.get_z_max_i(ind)
 
             self.before_plot_selected_2.set_data(
-                (get_pk_i, get_pk_i),
+                (get_kp_i, get_kp_i),
                 (get_z_min_i, get_z_max_i)
             )
 
@@ -666,12 +669,12 @@ class GeometryWindow(QMainWindow, WindowToolKit):
     def select_after_plot_selected_2(self, ind: int):
         if 0 <= ind < self.model.rowCount():
             t0 = time.time()
-            get_pk_i = self.get_pk_i(ind)
+            get_kp_i = self.get_kp_i(ind)
             get_z_min_i = self.get_z_min_i(ind)
             get_z_max_i = self.get_z_max_i(ind)
 
             self.after_plot_selected_2.set_data(
-                (get_pk_i, get_pk_i),
+                (get_kp_i, get_kp_i),
                 (get_z_min_i, get_z_max_i)
             )
 
@@ -730,11 +733,11 @@ class GeometryWindow(QMainWindow, WindowToolKit):
             self.ui.verticalSlider.setMaximum(self.model.rowCount() - 1)
 
             slider_value = self.ui.verticalSlider.value()
-            pk = self._reach.get_pk_profile_i(slider_value)
+            kp = self._reach.get_kp_profile_i(slider_value)
 
             self.ui.vertical_slider_label.setText(
-                _translate("MainWindow_reach", "Pk : ") +
-                f"{pk}" + "\n" +
+                _translate("MainWindow_reach", "Kp : ") +
+                f"{kp}" + "\n" +
                 _translate("MainWindow_reach",
                            "Profil N° : ") +
                 f"{slider_value + 1}"
diff --git a/src/tools.py b/src/tools.py
new file mode 100644
index 00000000..34805ed4
--- /dev/null
+++ b/src/tools.py
@@ -0,0 +1,19 @@
+# -*- coding: utf-8 -*-
+
+from functools import (
+    reduce, partial
+)
+
+def flatten(lst):
+    """Flatten list of list
+
+    Args:
+        lst: A list of list
+
+    Returns:
+        returns a list of element
+    """
+    if not lst:
+        return []
+
+    return reduce(list.__add__, lst)
-- 
GitLab