diff --git a/src/Model/Geometry/Reach.py b/src/Model/Geometry/Reach.py
index eca5a529e4d82f4bdaae5b048d02aa1f19a7cd77..46decef0188014a79d2571663dbf0a26e1cb3d1d 100644
--- a/src/Model/Geometry/Reach.py
+++ b/src/Model/Geometry/Reach.py
@@ -134,6 +134,7 @@ class Reach:
     def copy(self, index_list: List[int]):
         self.__list_copied_profiles.clear()
         index_list = list(set(index_list))  # delete duplicate index
+
         for index in index_list:
             try:
                 self.__list_copied_profiles.append(deepcopy(self.get_profile_i(index)))
diff --git a/src/Model/Geometry/projection_pointXYZ.py b/src/Model/Geometry/projection_pointXYZ.py
new file mode 100644
index 0000000000000000000000000000000000000000..439d4f4cd513923fa6c5797d7b3f53a437b3ebd7
--- /dev/null
+++ b/src/Model/Geometry/projection_pointXYZ.py
@@ -0,0 +1,108 @@
+#!/usr/bin/python3
+# -*- coding: utf-8 -*-
+import copy
+
+import numpy as np
+import pandas as pd
+from time import time
+
+from Model.Geometry import PointXYZ
+from Model.Geometry.ProfileXYZ import ProfileXYZ
+from Model.Geometry.vector_1d import Vector1d
+
+
+def update_station(list_header, list_point_xyz):
+    profile = ProfileXYZ(list_header, list_point_xyz)
+    if profile.nb_points >= 3:
+        return get_station(profile)
+    else:
+        pass
+
+
+def get_station(profile: ProfileXYZ) -> np.ndarray:
+    """Projection of the points of the profile on a plane.
+
+    Args:
+        profile: The profile
+
+    Returns:
+        Projection of the points of the profile on a plane.
+    """
+    if profile.nb_points >= 3:
+        first_named_point = None
+        index_first_named_point = None
+        last_named_point = None
+
+        for index, point in enumerate(profile.points):
+            if point.point_is_named():
+                index_first_named_point = index
+                first_named_point = point
+                break
+
+        for point in profile.points[::-1]:
+            if point.point_is_named():
+                last_named_point = point
+                break
+
+        station = []  # abscisse en travers
+        constant = 0.0  # constante pour décaler l'origine de l'ascisse en travers caluculée.
+
+        if (first_named_point is not None) and (last_named_point is not None):
+            if first_named_point != last_named_point and first_named_point.x != last_named_point.x:
+                vector = Vector1d(first_named_point, last_named_point)
+                normalized_direction_vec = vector.normalized_direction_vector()
+            else:
+                vector = Vector1d(_first_point_not_nan(profile), _last_point_not_nan(profile))
+                normalized_direction_vec = vector.normalized_direction_vector()
+
+            for point in profile.points:
+                xi = point.x - first_named_point.x
+                yi = point.y - first_named_point.y
+                station_i = normalized_direction_vec[0] * xi + normalized_direction_vec[1] * yi
+                station.append(station_i)
+
+            station = np.array(station)
+
+            constant = station[index_first_named_point]  # pour placer l'origine au premier point nomme.
+        elif first_named_point is None:
+            vector = Vector1d(_first_point_not_nan(profile), _last_point_not_nan(profile))
+            normalized_direction_vec = vector.normalized_direction_vector()
+
+            for point in profile.points:
+                xi = point.x - _first_point_not_nan(profile).x
+                yi = point.y - _first_point_not_nan(profile).y
+                station_i = normalized_direction_vec[0] * xi + normalized_direction_vec[1] * yi
+                station.append(station_i)
+
+            station = np.array(station)
+            index_profile_z_min = np.where(np.array(profile.z) == profile.z_min)[0][0]
+            constant = station[index_profile_z_min]  # pour placer l'origine au fond ie à la cote minimale
+            # z_min du profil.
+        station = station - constant
+
+        return station
+    else:
+        return np.array(np.nan)
+
+def _point_is_nan(point: PointXYZ):
+    return pd.isna(point.x) or pd.isna(point.y) or pd.isna(point.z)
+
+def _first_point_not_nan(profile: ProfileXYZ):
+    first_point_not_nan = profile.points[0]
+
+    for point in profile.points:
+        if not _point_is_nan(point):
+            first_point_not_nan = point
+            break
+
+    return first_point_not_nan
+
+def _last_point_not_nan(profile: ProfileXYZ):
+    last_point = profile.points[-1]
+
+    for point in profile.points[::-1]:
+        if not _point_is_nan(point):
+            last_point = point
+            break
+
+    return last_point
diff --git a/src/Model/Geometry/vector_1d.py b/src/Model/Geometry/vector_1d.py
new file mode 100644
index 0000000000000000000000000000000000000000..b82b86b03d5d353b5f165ff66308a53161fa0337
--- /dev/null
+++ b/src/Model/Geometry/vector_1d.py
@@ -0,0 +1,40 @@
+# -*- coding: utf-8 -*-
+
+import numpy as np
+from Model.Geometry.PointXYZ import PointXYZ
+
+
+class Vector1d:
+    def __init__(self, a: PointXYZ, b: PointXYZ):
+        self.A = a
+        self.B = b
+
+    def __repr__(self):
+        return "vecteur AB = ({}, {}, {})".format(
+            self.B.x - self.A.x,
+            self.B.y - self.A.y,
+            self.B.z - self.A.z
+        )
+
+    def vector1d(self):
+        return np.array([
+            self.B.x - self.A.x,
+            self.B.y - self.A.y,
+            self.B.z - self.A.z
+        ])
+
+    def direction_vector(self):
+        return np.array([
+            self.B.x - self.A.x,
+            self.B.y - self.A.y,
+            0
+        ])
+
+    def norm_vector(self):
+        return np.linalg.norm(self.vector1d())
+
+    def norm_direction_vector(self):
+        return np.linalg.norm(self.direction_vector())
+
+    def normalized_direction_vector(self):
+        return self.direction_vector() / self.norm_direction_vector()
diff --git a/src/View/ASubWindow.py b/src/View/ASubWindow.py
index cbb49ae13782a01efa98c4196496053d181ece4f..6d90eebf0fc056f869722f02882a2077600999ad 100644
--- a/src/View/ASubWindow.py
+++ b/src/View/ASubWindow.py
@@ -2,6 +2,8 @@
 
 import os
 
+from PyQt5.QtCore import Qt
+
 from PyQt5.QtWidgets import (
     QMainWindow, QApplication, QDesktopWidget,
     QMdiArea, QMdiSubWindow, QDialog,
@@ -25,12 +27,12 @@ class ASubWindow(QDialog):
         self.name = name
         self.parent = parent
         self.parent.sub_win_add(name, self)
-        self.fixed_size()
+        # self.fixed_size()
 
     def fixed_size(self):
         width = self.frameGeometry().width()
         height = self.frameGeometry().height()
-        # self.setFixedSize(width, height)
+        self.setFixedSize(width, height)
 
     def closeEvent(self, event):
         if not self.parent is None:
diff --git a/src/View/Geometry/mainwindow_ui_profile.py b/src/View/Geometry/mainwindow_ui_profile.py
new file mode 100644
index 0000000000000000000000000000000000000000..7aeb1bd160f47576ba5e6e00fa5550b5f832debe
--- /dev/null
+++ b/src/View/Geometry/mainwindow_ui_profile.py
@@ -0,0 +1,330 @@
+# -*- coding: utf-8 -*-
+
+import os.path
+
+from PyQt5 import QtCore, QtGui, QtWidgets
+from PyQt5.QtCore import Qt, QSize
+from PyQt5.QtWidgets import (
+    QTableView, QAbstractItemView, QHeaderView
+)
+
+from View.Plot import MplCanvas, navigation_toolbar_2qt
+
+file_path = os.path.abspath(os.path.dirname(__file__))
+icon_path = f"{os.path.dirname(os.path.dirname(file_path))}/../../ui/ressources/"
+
+class Ui_MainWindow(object):
+    def setupUi(self, MainWindowProfile):
+        MainWindowProfile.setObjectName("MainWindowProfile")
+        MainWindowProfile.resize(1089, 699)
+        self.centralwidget = QtWidgets.QWidget(MainWindowProfile)
+        self.centralwidget.setObjectName("centralwidget")
+        self.horizontalLayout_2 = QtWidgets.QHBoxLayout(self.centralwidget)
+        self.horizontalLayout_2.setObjectName("horizontalLayout_2")
+        self.splitter = QtWidgets.QSplitter(self.centralwidget)
+        self.splitter.setOrientation(Qt.Horizontal)
+        self.splitter.setObjectName("splitter")
+        self.widget = QtWidgets.QWidget(self.splitter)
+        self.widget.setObjectName("widget")
+        self.verticalLayout_left = QtWidgets.QVBoxLayout(self.widget)
+        self.verticalLayout_left.setContentsMargins(0, 0, 0, 0)
+        self.verticalLayout_left.setObjectName("verticalLayout_left")
+
+        self.horizontalLayout = QtWidgets.QHBoxLayout()
+        self.horizontalLayout.setObjectName("horizontalLayout")
+
+        btn_size = QSize(30, 30)
+
+        self.btn_add = QtWidgets.QPushButton(self.widget)
+        self.btn_add.setMaximumSize(QtCore.QSize(30, 30))
+        self.btn_add.setText("")
+        self.btn_add.setObjectName("btn_add")
+        icon_btn_add = QtGui.QIcon()
+        icon_btn_add.addPixmap(
+            QtGui.QPixmap(f"{icon_path}gtk-add.png"))
+        self.btn_add.setIcon(icon_btn_add)
+        self.btn_add.setIconSize(btn_size)
+        self.horizontalLayout.addWidget(self.btn_add)
+
+        self.btn_delete = QtWidgets.QPushButton(self.widget)
+        self.btn_delete.setMaximumSize(QtCore.QSize(30, 30))
+        self.btn_delete.setText("")
+        self.btn_delete.setObjectName("btn_delete")
+        icon_btn_delete = QtGui.QIcon()
+        icon_btn_delete.addPixmap(
+            QtGui.QPixmap(f"{icon_path}gtk-remove.png"))
+        self.btn_delete.setIcon(icon_btn_delete)
+        self.btn_delete.setIconSize(btn_size)
+        self.horizontalLayout.addWidget(self.btn_delete)
+
+        self.btn_sort_asc_x = QtWidgets.QPushButton(self.widget)
+        self.btn_sort_asc_x.setMaximumSize(QtCore.QSize(30, 30))
+        self.btn_sort_asc_x.setText("")
+        self.btn_sort_asc_x.setObjectName("btn_sort_asc_x")
+        icon_btn_sort_asc_x = QtGui.QIcon()
+        icon_btn_sort_asc_x.addPixmap(
+            QtGui.QPixmap(f"{icon_path}gtk-sort-ascending.png"))
+        self.btn_sort_asc_x.setIcon(icon_btn_sort_asc_x)
+        self.btn_sort_asc_x.setIconSize(btn_size)
+        self.horizontalLayout.addWidget(self.btn_sort_asc_x)
+
+        self.btn_sort_desc_x = QtWidgets.QPushButton(self.widget)
+        self.btn_sort_desc_x.setMaximumSize(QtCore.QSize(30, 30))
+        self.btn_sort_desc_x.setText("")
+        self.btn_sort_desc_x.setObjectName("btn_sort_desc_x")
+        icon_btn_sort_desc_x = QtGui.QIcon()
+        icon_btn_sort_desc_x.addPixmap(
+            QtGui.QPixmap(f"{icon_path}gtk-sort-descending.png"))
+        self.btn_sort_desc_x.setIcon(icon_btn_sort_desc_x)
+        self.btn_sort_desc_x.setIconSize(btn_size)
+        self.horizontalLayout.addWidget(self.btn_sort_desc_x)
+
+        self.btn_sort_asc_y = QtWidgets.QPushButton(self.widget)
+        self.btn_sort_asc_y.setMaximumSize(QtCore.QSize(30, 30))
+        self.btn_sort_asc_y.setText("")
+        self.btn_sort_asc_y.setObjectName("btn_sort_asc_y")
+        icon_btn_sort_asc_y = QtGui.QIcon()
+        icon_btn_sort_asc_y.addPixmap(
+            QtGui.QPixmap(f"{icon_path}gtk-sort-ascending.png"))
+        self.btn_sort_asc_y.setIcon(icon_btn_sort_asc_y)
+        self.btn_sort_asc_y.setIconSize(btn_size)
+        self.horizontalLayout.addWidget(self.btn_sort_asc_y)
+
+        self.btn_sort_desc_y = QtWidgets.QPushButton(self.widget)
+        self.btn_sort_desc_y.setMaximumSize(QtCore.QSize(30, 30))
+        self.btn_sort_desc_y.setText("")
+        self.btn_sort_desc_y.setObjectName("btn_sort_desc_y")
+        icon_btn_sort_desc_y = QtGui.QIcon()
+        icon_btn_sort_desc_y.addPixmap(
+            QtGui.QPixmap(f"{icon_path}gtk-sort-descending.png"))
+        self.btn_sort_desc_y.setIcon(icon_btn_sort_desc_y)
+        self.btn_sort_desc_y.setIconSize(btn_size)
+        self.horizontalLayout.addWidget(self.btn_sort_desc_y)
+
+        self.btn_move_up = QtWidgets.QPushButton(self.widget)
+        self.btn_move_up.setMaximumSize(QtCore.QSize(30, 30))
+        self.btn_move_up.setText("")
+        self.btn_move_up.setObjectName("btn_move_up")
+        icon_btn_move_up = QtGui.QIcon()
+        icon_btn_move_up.addPixmap(
+            QtGui.QPixmap(f"{icon_path}up.png"))
+        self.btn_move_up.setIcon(icon_btn_move_up)
+        self.btn_move_up.setIconSize(btn_size)
+        self.horizontalLayout.addWidget(self.btn_move_up)
+
+        self.btn_move_down = QtWidgets.QPushButton(self.widget)
+        self.btn_move_down.setMaximumSize(QtCore.QSize(30, 30))
+        self.btn_move_down.setText("")
+        self.btn_move_down.setObjectName("btn_move_down")
+        icon_btn_move_down = QtGui.QIcon()
+        icon_btn_move_down.addPixmap(
+            QtGui.QPixmap(f"{icon_path}down.png"))
+        self.btn_move_down.setIcon(icon_btn_move_down)
+        self.btn_move_down.setIconSize(btn_size)
+        self.horizontalLayout.addWidget(self.btn_move_down)
+
+        self.btn_export = QtWidgets.QPushButton(self.widget)
+        self.btn_export.setMaximumSize(QtCore.QSize(30, 30))
+        self.btn_export.setText("")
+        self.btn_export.setObjectName("btn_export")
+        icon_btn_export = QtGui.QIcon()
+        icon_btn_export.addPixmap(
+            QtGui.QPixmap(f"{icon_path}export.png"))
+        self.btn_export.setIcon(icon_btn_export)
+        self.btn_export.setIconSize(btn_size)
+        self.horizontalLayout.addWidget(self.btn_export)
+
+        self.btn_copy = QtWidgets.QPushButton(self.widget)
+        self.btn_copy.setMaximumSize(QtCore.QSize(30, 30))
+        self.btn_copy.setText("")
+        self.btn_copy.setObjectName("btn_copy")
+        icon_btn_copy = QtGui.QIcon()
+        icon_btn_copy.addPixmap(
+            QtGui.QPixmap(f"{icon_path}gtk-copy.png"))
+        self.btn_copy.setIcon(icon_btn_copy)
+        self.btn_copy.setIconSize(btn_size)
+        self.horizontalLayout.addWidget(self.btn_copy)
+
+        self.btn_paste = QtWidgets.QPushButton(self.widget)
+        self.btn_paste.setMaximumSize(QtCore.QSize(30, 30))
+        self.btn_paste.setText("")
+        self.btn_paste.setObjectName("btn_paste")
+        icon_btn_paste = QtGui.QIcon()
+        icon_btn_paste.addPixmap(
+            QtGui.QPixmap(f"{icon_path}gtk-paste.png"))
+        self.btn_paste.setIcon(icon_btn_paste)
+        self.btn_paste.setIconSize(btn_size)
+        self.horizontalLayout.addWidget(self.btn_paste)
+
+        self.btn_check = QtWidgets.QPushButton(self.widget)
+        self.btn_check.setMaximumSize(QtCore.QSize(30, 30))
+        self.btn_check.setText("")
+        self.btn_check.setObjectName("btn_check")
+        icon_btn_check = QtGui.QIcon()
+        icon_btn_check.addPixmap(
+            QtGui.QPixmap(f"{icon_path}gtk-apply.png"))
+        self.btn_check.setIcon(icon_btn_check)
+        self.btn_check.setIconSize(btn_size)
+        self.horizontalLayout.addWidget(self.btn_check)
+
+        self.btn_go_back = QtWidgets.QPushButton(self.widget)
+        self.btn_go_back.setMaximumSize(QtCore.QSize(30, 30))
+        self.btn_go_back.setText("")
+        self.btn_go_back.setObjectName("btn_go_back")
+        icon_btn_go_back = QtGui.QIcon()
+        icon_btn_go_back.addPixmap(
+            QtGui.QPixmap(f"{icon_path}gtk-undo.png"))
+        self.btn_go_back.setIcon(icon_btn_go_back)
+        self.btn_go_back.setIconSize(btn_size)
+        self.horizontalLayout.addWidget(self.btn_go_back)
+
+        self.btn_reset = QtWidgets.QPushButton(self.widget)
+        self.btn_reset.setMaximumSize(QtCore.QSize(30, 30))
+        self.btn_reset.setText("")
+        self.btn_reset.setObjectName("btn_reset")
+        icon_btn_reset = QtGui.QIcon()
+        icon_btn_reset.addPixmap(
+            QtGui.QPixmap(f"{icon_path}cancel.png"))
+        self.btn_reset.setIcon(icon_btn_reset)
+        self.btn_reset.setIconSize(btn_size)
+        self.horizontalLayout.addWidget(self.btn_reset)
+
+        spacerItem = QtWidgets.QSpacerItem(30, 30, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
+        self.horizontalLayout.addItem(spacerItem)
+
+        self.verticalLayout_left.addLayout(self.horizontalLayout)
+
+        self.tableView = QTableView(self.widget)
+        self.tableView.setStyleSheet(" QTableView { border: 1px solid black;\n"
+                                     "                                gridline-color: blue;\n"
+                                     "                                border-radius: 2px;\n"
+                                     "                                border-style: solid;\n"
+                                     "                                background-color: #EEF6FC; \n"
+                                     "                                selection-background-color: #218ede;\n"
+                                     "                                font-size: 11.5px;\n"
+                                     "                                font-family: Helvetica\n"
+                                     "\n"
+                                     "                   }")
+        # self.tableView.setSelectionMode(QtWidgets.QAbstractItemView.MultiSelection)
+        # self.tableView.setGridStyle(Qt.SolidLine)
+        self.tableView.setObjectName("tableView")
+        self.tableView.setSelectionBehavior(QAbstractItemView.SelectRows)  # permet de sélectionner une ligne entière
+        # self.tableView.verticalHeader().hide()  # hide vertical/row headers
+        self.tableView.setSizeAdjustPolicy(QtWidgets.QAbstractScrollArea.AdjustToContents)
+        # self.tableView.setSelectionMode(QAbstractItemView.MultiSelection)
+        # self.tableView.horizontalHeader().setStretchLastSection(True)
+        # self.tableView.setVisible(False)
+        self.tableView.resizeColumnsToContents()
+        self.tableView.resizeRowsToContents()
+        # self.tableView.setVisible(True)
+        # self.tableView.verticalHeader().setSectionResizeMode(QHeaderView.Stretch) # pour que le tableau occupe
+        # toute la zone
+        self.tableView.setAlternatingRowColors(True)  # colorie une ligne sur 2
+        self.tableView.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)  # ajuster la largeur du tableau
+        # self.tableView.setEditTriggers(QAbstractItemView.DoubleClicked)  # met en mode édition en cliquant sur une
+        # ligne https://doc.qt.io/qt-5/qabstractitemview.html#EditTrigger-enum NoEditTriggers, DoubleClicked,
+        # SelectedClicked, CurrentChanged
+        self.verticalLayout_left.addWidget(self.tableView)
+
+        self.widget1 = QtWidgets.QWidget(self.splitter)
+        self.widget1.setObjectName("widget1")
+
+        self.verticalLayout_right = QtWidgets.QVBoxLayout(self.widget1)
+        self.verticalLayout_right.setContentsMargins(0, 0, 0, 0)
+        self.verticalLayout_right.setObjectName("verticalLayout_right")
+
+        self.label_graphic_help = QtWidgets.QLabel(self.widget1)
+        self.label_graphic_help.setMaximumSize(QtCore.QSize(16777215, 25))
+        self.label_graphic_help.setBaseSize(QtCore.QSize(0, 30))
+        self.label_graphic_help.setStyleSheet(
+            "QLabel { border: 1px solid blue;\n"
+            "                                gridline-color: blue;\n"
+            "                                border-radius: 3px;\n"
+            "                                border-style: solid;\n"
+            "                                background-color: #EEF6FC; \n"
+            "                                selection-background-color: black;\n"
+            "                                font-size: 11px;\n"
+            "                                font-family: Helvetica\n"
+            "}"
+        )
+        self.label_graphic_help.setObjectName("label_graphic_help")
+        self.verticalLayout_right.addWidget(self.label_graphic_help)
+
+        # self.graphicsView = QtWidgets.QGraphicsView(self.widget1)
+        # self.graphicsView.setObjectName("graphicsView")
+        # self.verticalLayout_right.addWidget(self.graphicsView)
+
+        self.my_canvas = MplCanvas.MplCanvas(width=5, height=4,
+                                             dpi=100)
+        self.my_canvas.setObjectName("my_canvas")
+
+        self.my_toolbar = navigation_toolbar_2qt.PamHyrNavigationToolbar2QT(self.my_canvas, self.widget1)
+        self.my_toolbar.setStyleSheet(
+            "QToolBar{ border: 1px solid darkGray;\n"
+            "                                gridline-color: blue;\n"
+            "                                border-radius: 4px;\n"
+            "                                border-style: solid;\n"
+            "                                background-color: #EEF6FC; \n"
+            "                                selection-background-color: #218ede;\n"
+            "                                font-size: 12px;\n"
+            "                                font-family: Helvetica\n"
+            "\n"
+            "                   }"
+        )
+
+        self.verticalLayout_right.addWidget(self.my_toolbar)
+        self.verticalLayout_right.addWidget(self.my_canvas)
+        self.horizontalLayout_2.addWidget(self.splitter)
+
+        MainWindowProfile.setCentralWidget(self.centralwidget)
+
+        self.menubar = QtWidgets.QMenuBar(MainWindowProfile)
+        self.menubar.setGeometry(QtCore.QRect(0, 0, 1089, 20))
+        self.menubar.setObjectName("menubar")
+        MainWindowProfile.setMenuBar(self.menubar)
+        self.statusbar = QtWidgets.QStatusBar(MainWindowProfile)
+        self.statusbar.setObjectName("statusbar")
+        MainWindowProfile.setStatusBar(self.statusbar)
+
+        self.retranslateUi(MainWindowProfile)
+        QtCore.QMetaObject.connectSlotsByName(MainWindowProfile)
+
+    def retranslateUi(self, MainWindow):
+        _translate = QtCore.QCoreApplication.translate
+        MainWindow.setWindowTitle(_translate("MainWindowProfile", "MainWindowProfile"))
+        self.btn_add.setToolTip(_translate("MainWindowProfile", "Insérer un point"))
+        self.btn_delete.setToolTip(
+            _translate("MainWindowProfile", "Supprimer le/les point(s) sélectionnés"))
+        self.btn_sort_asc_x.setToolTip(
+            _translate("MainWindowProfile", "Trier les points par ordre croissant de X"))
+        self.btn_sort_desc_x.setToolTip(
+            _translate("MainWindowProfile", "Trier les points par ordre décroissant de X"))
+        self.btn_sort_asc_y.setToolTip(_translate("MainWindowProfile",
+                                                  "Trier les points par ordre croissant de Y"
+                                                  ))
+        self.btn_sort_desc_y.setToolTip(_translate("MainWindowProfile",
+                                                   "Trier les points par ordre décroissant de Y"))
+        self.btn_move_up.setToolTip(_translate("MainWindowProfile",
+                                               "Décaler le point sélectionné vers le haut"))
+        self.btn_move_down.setToolTip(_translate("MainWindowProfile",
+                                                 "Décaler le point sélectionné vers le bas"))
+        self.btn_export.setToolTip(_translate("MainWindowProfile",
+                                              "Exporter (dans un fichier) les points du profil au format tabulé"))
+        self.btn_copy.setToolTip(
+            _translate("MainWindowProfile", "Copier la sélection au format tabulé"))
+        self.btn_paste.setToolTip(_translate("MainWindowProfile",
+                                             "Coller la sélection depuis le presse-papier au format tabulé"))
+        self.btn_check.setToolTip(_translate("MainWindowProfile",
+                                             "Vérifier la validité de la saisie et garder ou pas les modifications "
+                                             "apportées"))
+        self.btn_go_back.setToolTip(_translate("MainWindowProfile",
+                                               "Annuler toutes les modifications depuis la dernière validation"))
+        self.btn_reset.setToolTip(_translate("MainWindowProfile",
+                                             "Annuler toutes les modifications et revenir à l\'état initial"))
+        self.label_graphic_help.setToolTip(_translate("MainWindowProfile",
+                                                      "Ligne d'eau : \n \tZ : Cote (m) \n \tA : Aire mouillée ("
+                                                      "m\u00B2) \n \tp : "
+                                                      "Périmètre mouillé (m) \n \tL : Largeur au miroir (m)"))
+        self.label_graphic_help.setText(
+            _translate("MainWindowProfile", "\'Maj + Clic\' : Ligne d\'eau & \'Ctrl + Clic\' : "
+                                            "Sélectionner des points"))
diff --git a/src/View/Geometry/mainwindow_ui_reach.py b/src/View/Geometry/mainwindow_ui_reach.py
new file mode 100644
index 0000000000000000000000000000000000000000..dfecd2d9620c79ffc498b723ef400e4ec3faf051
--- /dev/null
+++ b/src/View/Geometry/mainwindow_ui_reach.py
@@ -0,0 +1,425 @@
+# -*- coding: utf-8 -*-
+
+import sys
+import os.path
+
+from PyQt5 import QtCore, QtGui, QtWidgets
+from PyQt5.QtCore import QSize, Qt
+from PyQt5.QtWidgets import (
+    QAbstractItemView, QHeaderView
+)
+
+from View.Plot import (
+    MplCanvas, navigation_toolbar_2qt
+)
+
+file_path = os.path.abspath(os.path.dirname(__file__))
+icon_path = f"{os.path.dirname(os.path.dirname(file_path))}/../../ui/resources/"
+
+class Ui_MainWindow(object):
+    def setupUi(self, MainWindow_reach):
+        MainWindow_reach.setObjectName("MainWindow_reach")
+        MainWindow_reach.resize(1400, 800)
+        self.centralwidget = QtWidgets.QWidget(MainWindow_reach)
+        self.centralwidget.setObjectName("centralwidget")
+        self.horizontalLayout_2 = QtWidgets.QHBoxLayout(self.centralwidget)
+        self.horizontalLayout_2.setObjectName("horizontalLayout_2")
+        self.splitter_3 = QtWidgets.QSplitter(self.centralwidget)
+        self.splitter_3.setOrientation(Qt.Horizontal)
+        self.splitter_3.setObjectName("splitter_3")
+        self.widget = QtWidgets.QWidget(self.splitter_3)
+        self.widget.setObjectName("widget")
+        self.verticalLayout = QtWidgets.QVBoxLayout(self.widget)
+        self.verticalLayout.setContentsMargins(0, 0, 0, 0)
+        self.verticalLayout.setObjectName("verticalLayout")
+        self.horizontalLayout = QtWidgets.QHBoxLayout()
+        self.horizontalLayout.setObjectName("horizontalLayout")
+
+        self.label_section_order = QtWidgets.QLabel(self.widget)
+        self.label_section_order.setLayoutDirection(Qt.LeftToRight)
+        self.label_section_order.setObjectName("label_section_order")
+        self.label_section_order.setStyleSheet(
+            "QLabel{ border: 1px solid darkGray;\n"
+            "                                gridline-color: gray;\n"
+            "                                border-radius: 1px;\n"
+            "                                border-style: solid;\n"
+            "                                background-color: ; \n"
+            "                                selection-background-color: #218ede;\n"
+            "                                font-size: 12px;\n"
+            "                                font-family: Helvetica\n"
+            "\n"
+            "                   }"
+        )
+        self.verticalLayout.addWidget(self.label_section_order)
+        btn_size = QSize(30, 30)
+        self.btn_open = QtWidgets.QPushButton(self.widget)
+        self.btn_open.setMaximumSize(QtCore.QSize(30, 30))
+        self.btn_open.setObjectName("btn_open")
+        icon_btn_open = QtGui.QIcon()
+        icon_btn_open.addPixmap(
+            QtGui.QPixmap(f"{icon_path}open.png"))
+        self.btn_open.setIcon(icon_btn_open)
+        self.btn_open.setIconSize(btn_size)
+        self.horizontalLayout.addWidget(self.btn_open)
+
+        self.btn_add = QtWidgets.QPushButton(self.widget)
+        self.btn_add.setMaximumSize(QtCore.QSize(30, 30))
+        self.btn_add.setObjectName("btn_add")
+        icon_btn_add = QtGui.QIcon()
+        icon_btn_add.addPixmap(
+            QtGui.QPixmap(f"{icon_path}gtk_add.png"))
+        self.btn_add.setIcon(icon_btn_add)
+        self.btn_add.setIconSize(btn_size)
+        self.horizontalLayout.addWidget(self.btn_add)
+
+        self.btn_delete = QtWidgets.QPushButton(self.widget)
+        self.btn_delete.setMaximumSize(QtCore.QSize(30, 30))
+        self.btn_delete.setObjectName("btn_delete")
+        icon_btn_delete = QtGui.QIcon()
+        icon_btn_delete.addPixmap(
+            QtGui.QPixmap(f"{icon_path}gtk-remove.png"))
+        self.btn_delete.setIcon(icon_btn_delete)
+        self.btn_delete.setIconSize(btn_size)
+        self.horizontalLayout.addWidget(self.btn_delete)
+
+        self.btn_edit = QtWidgets.QPushButton(self.widget)
+        self.btn_edit.setMaximumSize(QtCore.QSize(30, 30))
+        self.btn_edit.setObjectName("btn_edit")
+        icon_btn_edit = QtGui.QIcon()
+        icon_btn_edit.addPixmap(
+            QtGui.QPixmap(f"{icon_path}gnome-stock-edit.png"))
+        self.btn_edit.setIcon(icon_btn_edit)
+        self.btn_edit.setIconSize(btn_size)
+        self.horizontalLayout.addWidget(self.btn_edit)
+
+        self.btn_copy_selected_profile = QtWidgets.QPushButton(self.widget)
+        self.btn_copy_selected_profile.setMaximumSize(QtCore.QSize(30, 30))
+        self.btn_copy_selected_profile.setObjectName("btn_copy_selected_profile")
+        icon_btn_copy_selected_profile = QtGui.QIcon()
+        icon_btn_copy_selected_profile.addPixmap(
+            QtGui.QPixmap(f"{icon_path}gtk-copy.png"))
+        self.btn_copy_selected_profile.setIcon(icon_btn_copy_selected_profile)
+        self.btn_copy_selected_profile.setIconSize(btn_size)
+        self.horizontalLayout.addWidget(self.btn_copy_selected_profile)
+
+        self.btn_paste_selected_profile = QtWidgets.QPushButton(self.widget)
+        self.btn_paste_selected_profile.setMaximumSize(QtCore.QSize(30, 30))
+        self.btn_paste_selected_profile.setObjectName("btn_paste_selected_profile")
+        icon_btn_paste_selected_profile = QtGui.QIcon()
+        icon_btn_paste_selected_profile.addPixmap(
+            QtGui.QPixmap(f"{icon_path}gtk-paste.png"))
+        self.btn_paste_selected_profile.setIcon(icon_btn_paste_selected_profile)
+        self.btn_paste_selected_profile.setIconSize(btn_size)
+        self.horizontalLayout.addWidget(self.btn_paste_selected_profile)
+
+        self.btn_duplicate_selected_profile = QtWidgets.QPushButton(self.widget)
+        self.btn_duplicate_selected_profile.setMaximumSize(QtCore.QSize(30, 30))
+        self.btn_duplicate_selected_profile.setObjectName("btn_duplicate_selected_profile")
+        icon_btn_duplicate_selected_profile = QtGui.QIcon()
+        icon_btn_duplicate_selected_profile.addPixmap(
+            QtGui.QPixmap(f"{icon_path}gtk-dnd-multiple.png"))
+        self.btn_duplicate_selected_profile.setIcon(icon_btn_duplicate_selected_profile)
+        self.btn_duplicate_selected_profile.setIconSize(btn_size)
+        self.horizontalLayout.addWidget(self.btn_duplicate_selected_profile)
+
+        self.btn_sort_asc = QtWidgets.QPushButton(self.widget)
+        self.btn_sort_asc.setMaximumSize(QtCore.QSize(30, 30))
+        self.btn_sort_asc.setText("")
+        self.btn_sort_asc.setObjectName("btn_sort_asc")
+        icon_btn_sort_asc = QtGui.QIcon()
+        icon_btn_sort_asc.addPixmap(
+            QtGui.QPixmap(f"{icon_path}gtk-sort-ascending.png"))
+        self.btn_sort_asc.setIcon(icon_btn_sort_asc)
+        self.btn_sort_asc.setIconSize(btn_size)
+        self.horizontalLayout.addWidget(self.btn_sort_asc)
+
+        self.btn_sort_desc = QtWidgets.QPushButton(self.widget)
+        self.btn_sort_desc.setMaximumSize(QtCore.QSize(30, 30))
+        self.btn_sort_desc.setText("")
+        self.btn_sort_desc.setObjectName("btn_sort_desc")
+        icon_btn_sort_desc = QtGui.QIcon()
+        icon_btn_sort_desc.addPixmap(
+            QtGui.QPixmap(f"{icon_path}gtk-sort-descending.png"))
+        self.btn_sort_desc.setIcon(icon_btn_sort_desc)
+        self.btn_sort_desc.setIconSize(btn_size)
+        self.horizontalLayout.addWidget(self.btn_sort_desc)
+
+        self.btn_move_up = QtWidgets.QPushButton(self.widget)
+        self.btn_move_up.setMaximumSize(QtCore.QSize(30, 30))
+        self.btn_move_up.setText("")
+        self.btn_move_up.setObjectName("btn_move_up")
+        icon_btn_move_up = QtGui.QIcon()
+        icon_btn_move_up.addPixmap(
+            QtGui.QPixmap(f"{icon_path}up.png"))
+        self.btn_move_up.setIcon(icon_btn_move_up)
+        self.btn_move_up.setIconSize(btn_size)
+        self.horizontalLayout.addWidget(self.btn_move_up)
+
+        self.btn_move_down = QtWidgets.QPushButton(self.widget)
+        self.btn_move_down.setMaximumSize(QtCore.QSize(30, 30))
+        self.btn_move_down.setText("")
+        self.btn_move_down.setObjectName("btn_move_down")
+        icon_btn_move_down = QtGui.QIcon()
+        icon_btn_move_down.addPixmap(
+            QtGui.QPixmap(f"{icon_path}down.png"))
+        self.btn_move_down.setIcon(icon_btn_move_down)
+        self.btn_move_down.setIconSize(btn_size)
+        self.horizontalLayout.addWidget(self.btn_move_down)
+
+        self.btn_end_editing = QtWidgets.QPushButton(self.widget)
+        self.btn_end_editing.setMaximumSize(QtCore.QSize(30, 30))
+        self.btn_end_editing.setText("")
+        self.btn_end_editing.setObjectName("btn_end_editing")
+        icon_btn_save = QtGui.QIcon()
+        icon_btn_save.addPixmap(
+            QtGui.QPixmap(f"{icon_path}gtk-apply.png"))
+        self.btn_end_editing.setIcon(icon_btn_save)
+        self.btn_end_editing.setIconSize(btn_size)
+        self.horizontalLayout.addWidget(self.btn_end_editing)
+
+        spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
+        self.horizontalLayout.addItem(spacerItem)
+        self.verticalLayout.addLayout(self.horizontalLayout)
+        self.tableView = QtWidgets.QTableView(self.widget)
+        self.tableView.setObjectName("tableView")
+        self.verticalLayout.addWidget(self.tableView)
+        self.tableView.setSelectionBehavior(QAbstractItemView.SelectRows)  # permet de sélectionner une ligne entière
+        self.tableView.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)  # ajuster la largeur du tableau
+        self.tableView.setAlternatingRowColors(True)  # colorie une ligne sur 2
+        self.tableView.verticalHeader().hide()  # hide vertical/row headers
+        self.tableView.setStyleSheet(
+            " QTableView { border: 1px solid black;\n"
+            "                                gridline-color: blue;\n"
+            "                                border-radius: 2px;\n"
+            "                                border-style: solid;\n"
+            "                                background-color: #EEF6FC; \n"
+            "                                selection-background-color: #218ede;\n"
+            "                                font-size: 12px;\n"
+            "                                font-family: Helvetica\n"
+            "\n"
+            "                   }"
+        )
+        self.tableView.resizeColumnsToContents()
+        self.tableView.resizeRowsToContents()
+
+        self.widget1 = QtWidgets.QWidget(self.splitter_3)
+        self.widget1.setObjectName("widget1")
+        self.gridLayout = QtWidgets.QGridLayout(self.widget1)
+        self.gridLayout.setContentsMargins(0, 0, 0, 0)
+        self.gridLayout.setObjectName("gridLayout")
+        self.splitter_2 = QtWidgets.QSplitter(self.widget1)
+        self.splitter_2.setOrientation(Qt.Vertical)
+        self.splitter_2.setObjectName("splitter_2")
+        self.splitter = QtWidgets.QSplitter(self.splitter_2)
+        self.splitter.setOrientation(Qt.Horizontal)
+        self.splitter.setObjectName("splitter")
+        self.verticalLayoutWidget_3 = QtWidgets.QWidget(self.splitter)
+        self.verticalLayoutWidget_3.setObjectName("verticalLayoutWidget_3")
+        self.verticalLayout_canvas_1 = QtWidgets.QVBoxLayout(self.verticalLayoutWidget_3)
+        self.verticalLayout_canvas_1.setContentsMargins(0, 0, 0, 0)
+        self.verticalLayout_canvas_1.setObjectName("verticalLayout_canvas_1")
+        self.canvas_1 = MplCanvas.MplCanvas(width=5, height=4,
+                                            dpi=100)  # QtWidgets.QGraphicsView(self.verticalLayoutWidget_3)
+
+        self.canvas_1.setObjectName("canvas_1")
+
+        self.toolbar_1 = navigation_toolbar_2qt.PamHyrNavigationToolbar2QT(self.canvas_1, self.centralwidget)
+        self.toolbar_1.setStyleSheet("QToolBar{ border: 1px solid darkGray;\n"
+                                     "                                gridline-color: blue;\n"
+                                     "                                border-radius: 4px;\n"
+                                     "                                border-style: solid;\n"
+                                     "                                background-color: #EEF6FC; \n"
+                                     "                                selection-background-color: #218ede;\n"
+                                     "                                font-size: 12px;\n"
+                                     "                                font-family: Helvetica\n"
+                                     "\n"
+                                     "                   }")
+
+        self.verticalLayout_canvas_1.addWidget(self.toolbar_1)
+
+        self.verticalLayout_canvas_1.addWidget(self.canvas_1)
+        self.verticalLayoutWidget_2 = QtWidgets.QWidget(self.splitter)
+        self.verticalLayoutWidget_2.setObjectName("verticalLayoutWidget_2")
+        self.verticalLayout_canvas_2 = QtWidgets.QVBoxLayout(self.verticalLayoutWidget_2)
+        self.verticalLayout_canvas_2.setContentsMargins(0, 0, 0, 0)
+        self.verticalLayout_canvas_2.setObjectName("verticalLayout_canvas_2")
+        self.canvas_2 = MplCanvas.MplCanvas(width=5, height=4,
+                                            dpi=100)  # QtWidgets.QGraphicsView(self.verticalLayoutWidget_2)
+        #  self.canvas_2.setMouseTracking(False)
+        self.canvas_2.setObjectName("canvas_2")
+
+        self.toolbar_2 = navigation_toolbar_2qt.PamHyrNavigationToolbar2QT(self.canvas_2, self.centralwidget)
+        self.toolbar_2.setStyleSheet(
+            "QToolBar{ border: 1px solid darkGray;\n"
+            "                                gridline-color: blue;\n"
+            "                                border-radius: 4px;\n"
+            "                                border-style: solid;\n"
+            "                                background-color: #EEF6FC; \n"
+            "                                selection-background-color: #218ede;\n"
+            "                                font-size: 12px;\n"
+            "                                font-family: Helvetica\n"
+            "\n"
+            "                   }"
+        )
+        #  self.frame_toolbar.addWidget(self.toolbar)
+        self.verticalLayout_canvas_2.addWidget(self.toolbar_2)
+        self.verticalLayout_canvas_2.addWidget(self.canvas_2)
+        self.verticalLayoutWidget = QtWidgets.QWidget(self.splitter_2)
+        self.verticalLayoutWidget.setObjectName("verticalLayoutWidget")
+        self.verticalLayout_canvas_3 = QtWidgets.QVBoxLayout(self.verticalLayoutWidget)
+        self.verticalLayout_canvas_3.setContentsMargins(0, 0, 0, 0)
+        self.verticalLayout_canvas_3.setObjectName("verticalLayout_canvas_3")
+        self.canvas_3 = MplCanvas.MplCanvas(width=5, height=4,
+                                            dpi=100)  # QtWidgets.QGraphicsView(self.verticalLayoutWidget)
+        self.canvas_3.setObjectName("canvas_3")
+
+        self.toolbar_3 = navigation_toolbar_2qt.PamHyrNavigationToolbar2QT(self.canvas_3, self.centralwidget)
+        self.toolbar_3.setStyleSheet(
+            "QToolBar{ border: 1px solid darkGray;\n"
+            "                                gridline-color: blue;\n"
+            "                                border-radius: 4px;\n"
+            "                                border-style: solid;\n"
+            "                                background-color: #EEF6FC; \n"
+            "                                selection-background-color: #218ede;\n"
+            "                                font-size: 12px;\n"
+            "                                font-family: Helvetica\n"
+            "\n"
+            "                   }"
+        )
+        #  self.frame_toolbar.addWidget(self.toolbar)
+        self.verticalLayout_canvas_3.addWidget(self.toolbar_3)
+        # self.verticalLayout_canvas_3.addWidget(self.canvas_3)
+        # from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
+        # import matplotlib.pyplot as plt
+        # self.figure = plt.figure()
+        # self.canvas_3 = FigureCanvas(self.figure)
+        self.verticalLayout_canvas_3.addWidget(self.canvas_3)
+
+        self.gridLayout.addWidget(self.splitter_2, 0, 0, 3, 3)
+        self.verticalSlider = QtWidgets.QSlider(self.widget1)
+        self.verticalSlider.setMinimumSize(QtCore.QSize(30, 0))
+        self.verticalSlider.setOrientation(Qt.Vertical)
+        self.verticalSlider.setInvertedAppearance(True)
+        self.verticalSlider.setInvertedControls(False)
+        self.verticalSlider.setObjectName("verticalSlider")
+        self.gridLayout.addWidget(self.verticalSlider, 0, 3, 1, 1)
+
+        self.btn_slider_up = QtWidgets.QPushButton(self.widget1)
+        self.btn_slider_up.setMaximumSize(QtCore.QSize(30, 30))
+        self.btn_slider_up.setObjectName("btn_slider_up")
+        icon_btn_slider_up = QtGui.QIcon()
+        icon_btn_slider_up.addPixmap(
+            QtGui.QPixmap(f"{icon_path}go-up2.png"))
+        self.btn_slider_up.setIcon(icon_btn_slider_up)
+        self.btn_slider_up.setIconSize(btn_size)
+        self.gridLayout.addWidget(self.btn_slider_up, 1, 3, 1, 1)
+
+        self.btn_slider_down = QtWidgets.QPushButton(self.widget1)
+        self.btn_slider_down.setMaximumSize(QtCore.QSize(30, 30))
+        self.btn_slider_down.setObjectName("btn_slider_down")
+        icon_btn_slider_down = QtGui.QIcon()
+        icon_btn_slider_down.addPixmap(
+            QtGui.QPixmap(f"{icon_path}go-down1.png"))
+        self.btn_slider_down.setIcon(icon_btn_slider_down)
+        self.btn_slider_down.setIconSize(btn_size)
+        self.gridLayout.addWidget(self.btn_slider_down, 2, 3, 1, 1)
+
+        self.vertical_slider_label = QtWidgets.QLabel(self.widget1)
+        # self.vertical_slider_label.setLayoutDirection(Qt.RightToLeft)
+        # self.vertical_slider_label.setLayoutDirection(Qt.LeftToRight)
+        self.vertical_slider_label.setObjectName("vertical_slider_label")
+        self.gridLayout.addWidget(self.vertical_slider_label, 3, 2, 2, 1)
+        self.vertical_slider_label.setAlignment(Qt.AlignRight)
+
+        self.btn_slider_go_back = QtWidgets.QPushButton(self.widget1)
+        self.btn_slider_go_back.setMaximumSize(QtCore.QSize(30, 30))
+        self.btn_slider_go_back.setObjectName("btn_slider_go_back")
+        icon_btn_slider_go_back = QtGui.QIcon()
+        icon_btn_slider_go_back.addPixmap(
+            QtGui.QPixmap(f"{icon_path}gtk-go-back.png"))
+        self.btn_slider_go_back.setIcon(icon_btn_slider_go_back)
+        self.btn_slider_go_back.setIconSize(btn_size)
+        self.gridLayout.addWidget(self.btn_slider_go_back, 5, 0, 1, 1)
+        self.btn_slider_go_back.setEnabled(False)
+
+        self.btn_slider_go_forward = QtWidgets.QPushButton(self.widget1)
+        self.btn_slider_go_forward.setMaximumSize(QtCore.QSize(30, 30))
+        self.btn_slider_go_forward.setObjectName("btn_slider_go_forward")
+        icon_btn_slider_go_forward = QtGui.QIcon()
+        icon_btn_slider_go_forward.addPixmap(
+            QtGui.QPixmap(f"{icon_path}gtk-go-forward.png"))
+        self.btn_slider_go_forward.setIcon(icon_btn_slider_go_forward)
+        self.btn_slider_go_forward.setIconSize(btn_size)
+        self.gridLayout.addWidget(self.btn_slider_go_forward, 5, 1, 1, 1)
+        self.btn_slider_go_forward.setEnabled(False)
+
+        self.horizontalSlider = QtWidgets.QSlider(self.widget1)
+        self.horizontalSlider.setMinimumSize(QtCore.QSize(0, 30))
+        self.horizontalSlider.setOrientation(Qt.Horizontal)
+        self.horizontalSlider.setObjectName("horizontalSlider")
+        self.gridLayout.addWidget(self.horizontalSlider, 5, 2, 1, 1)
+        self.horizontalSlider.setEnabled(False)
+
+        self.horizontalLayout_2.addWidget(self.splitter_3)
+        MainWindow_reach.setCentralWidget(self.centralwidget)
+        self.menubar = QtWidgets.QMenuBar(MainWindow_reach)
+        self.menubar.setGeometry(QtCore.QRect(0, 0, 1123, 20))
+        self.menubar.setObjectName("menubar")
+        MainWindow_reach.setMenuBar(self.menubar)
+        self.statusbar = QtWidgets.QStatusBar(MainWindow_reach)
+        self.statusbar.setObjectName("statusbar")
+        MainWindow_reach.setStatusBar(self.statusbar)
+
+        self.retranslateUi(MainWindow_reach)
+        QtCore.QMetaObject.connectSlotsByName(MainWindow_reach)
+
+    def retranslateUi(self, MainWindow_reach):
+        _translate = QtCore.QCoreApplication.translate
+        self.name_reach = ""
+        self.mainwindow_title = _translate("MainWindow_reach", "Jeu de sections du Bief")
+        MainWindow_reach.setWindowTitle(self.mainwindow_title + f"{self.name_reach}")
+        self.text_section_order = _translate("MainWindow_reach", "\nOrdre des sections : "
+                                                                 "Amont --> Aval")
+        self.label_section_order.setText(f"{self.text_section_order}")
+        self.vertical_slider_value = ""
+        self.vertical_slider_label.setText(_translate("MainWindow_reach", "Pk = ") + f"{self.vertical_slider_value}")
+        self.btn_open.setToolTip(_translate("MainWindow_reach", " Importer une géométrie"))
+        self.btn_add.setToolTip(_translate("MainWindow_reach", " Nouveau profil"))
+        self.btn_delete.setToolTip(_translate("MainWindow_reach", " Supprimer le profil sélectionné"))
+        self.btn_edit.setToolTip(_translate("MainWindow_reach", " Éditer le profil sélectionné"))
+        self.btn_copy_selected_profile.setToolTip(_translate("MainWindow_reach", " Copier le profil sélectionné"))
+        self.btn_paste_selected_profile.setToolTip(_translate("MainWindow_reach", "Coller le profil en fin de liste ("
+                                                                                  "penser à modifier le Pk avant de "
+                                                                                  "trier)"))
+        self.btn_duplicate_selected_profile.setToolTip(
+            _translate("MainWindow_reach", " Dupliquer la section sélectionnée"))
+        self.btn_sort_asc.setToolTip(_translate("MainWindow_reach", " Trier les profils par ordre croissant des Pk"))
+        self.btn_sort_desc.setToolTip(_translate("MainWindow_reach", " Trier les profils par ordre décroissant des Pk"))
+        self.btn_move_up.setToolTip(_translate("MainWindow_reach", " Changer l'ordre des profils (en décalant le "
+                                                                   "profil sélectionné vers le haut)"))
+        self.btn_move_down.setToolTip(_translate("MainWindow_reach", " Changer l'ordre des profils (en décalant le "
+                                                                     "profil sélectionné vers le bas)"))
+        self.btn_end_editing.setToolTip(_translate("MainWindow_reach", " Terminer l'édition"))
+        self.tableView_header = [_translate("MainWindow_reach", "Nom"), _translate("MainWindow_reach", "Pk (m)"),
+                                 _translate("MainWindow_reach", "Type")]
+
+        self.toolbar_1._actions["isometric_view"].setShortcut(_translate("MainWindow_reach", "Alt+Z"))
+        self.toolbar_1._actions["isometric_view"].setToolTip(_translate("MainWindow_reach", "Vue isométrique (Alt+Z)"))
+
+        self.toolbar_2._actions["isometric_view"].setShortcut(_translate("MainWindow_reach", "Alt+E"))
+        self.toolbar_2._actions["isometric_view"].setToolTip(_translate("MainWindow_reach", "Vue isométrique (Alt+E)"))
+
+        self.toolbar_3._actions["isometric_view"].setShortcut(_translate("MainWindow_reach", "Alt+R"))
+        self.toolbar_3._actions["isometric_view"].setToolTip(_translate("MainWindow_reach", "Vue isométrique (Alt+R)"))
+
+        self.toolbar_1._actions["non_isometric_view"].setShortcut("Alt+S")
+        self.toolbar_1._actions["non_isometric_view"].setToolTip(
+            _translate("MainWindow_reach", "Vue globale automatique (Alt+S)"))
+
+        self.toolbar_2._actions["non_isometric_view"].setShortcut("Alt+D")
+        self.toolbar_2._actions["non_isometric_view"].setToolTip(
+            _translate("MainWindow_reach", "Vue globale automatique (Alt+D)"))
+
+        self.toolbar_3._actions["non_isometric_view"].setShortcut("Alt+F")
+        self.toolbar_3._actions["non_isometric_view"].setToolTip(
+            _translate("MainWindow_reach", "Vue globale automatique (Alt+F)"))
diff --git a/src/View/Geometry/qtableview_profile.py b/src/View/Geometry/qtableview_profile.py
new file mode 100644
index 0000000000000000000000000000000000000000..7f59e1bf36a9da2f91622a18fe08fa92a4fc3460
--- /dev/null
+++ b/src/View/Geometry/qtableview_profile.py
@@ -0,0 +1,422 @@
+import numpy as np
+import pandas as pd
+from PyQt5.QtGui import QFont
+
+from PyQt5.QtWidgets import QMessageBox
+from PyQt5 import QtWidgets, QtGui
+from PyQt5.QtCore import QModelIndex, Qt, QAbstractTableModel, QVariant, QCoreApplication
+from ProfileXYZ import ProfileXYZ
+import projection_pointXYZ
+
+_translate = QCoreApplication.translate
+
+
+class PandasModelEditable(QAbstractTableModel):
+    def __init__(self, profile: ProfileXYZ, table_header=None):
+        QAbstractTableModel.__init__(self)
+
+        if table_header is None:
+            self.header = ["X (m)", "Y (m)", "Z (m)", _translate("MainWindowProfile", "Nom"),
+                           _translate("MainWindowProfile", "Abs en travers (m)")]
+        else:
+            self.header = table_header
+
+        self.profile = profile
+
+        data = pd.DataFrame({
+            self.header[0]: profile.x,
+            self.header[1]: profile.y,
+            self.header[2]: profile.z,
+            self.header[3]: profile.ld,
+            self.header[4]: projection_pointXYZ.get_station(profile)
+        })
+        self._data = data
+
+    def rowCount(self, parent=QModelIndex()):
+        return self._data.shape[0]
+
+    def columnCount(self, parent=QModelIndex()):
+        return self._data.shape[1]
+
+    def data(self, index, role=Qt.DisplayRole):
+        value = self._data.iloc[index.row()][index.column()]
+        if index.isValid():
+            if role == Qt.DisplayRole:
+                if index.column() != 4:
+                    if isinstance(value, float):
+                        return "%.4f" % value
+                else:
+                    if isinstance(value, float):
+                        return "%.3f" % value
+
+                return str(self._data.iloc[index.row(), index.column()])
+
+            if role == Qt.TextAlignmentRole:
+                return Qt.AlignHCenter | Qt.AlignVCenter
+
+            if index.column() == 2:
+                if role == Qt.ForegroundRole:
+                    if value == min(self._data.iloc[:, index.column()]):
+                        return QtGui.QColor("red")
+                    elif value == max(self._data.iloc[:, index.column()]):
+                        return QtGui.QColor("Blue")
+
+                if role == Qt.ToolTipRole:
+                    if value == min(self._data.iloc[:, index.column()]):
+                        return _translate("MainWindowProfile", "La cote du fond", "Z minimale")
+                    elif value == max(self._data.iloc[:, index.column()]):
+                        return _translate("MainWindowProfile", "La cote maximale", "Z maximale")
+
+            if index.column() == 3:
+                if value.strip().upper() in ["RG", "RD"]:
+                    if role == Qt.FontRole:
+                        font = QFont()
+                        font.setBold(True)
+                        return font
+
+                    if role == Qt.ForegroundRole:
+                        return QtGui.QColor("darkRed")
+
+                    if role == Qt.ToolTipRole:
+                        if value.strip().upper() == "RG":
+                            return _translate("MainWindowProfile", "Rive gauche")
+                        else:
+                            return _translate("MainWindowProfile", "Rive droite")
+
+            if index.column() == 4:
+                if role == Qt.FontRole:
+                    font = QFont()
+                    font.setBold(True)
+                    return font
+
+                if role == Qt.BackgroundRole:
+                    return QtGui.QColor("#ededee")
+
+        return QVariant()
+
+    def headerData(self, section, orientation, role=Qt.DisplayRole):
+        if orientation == Qt.Horizontal and role == Qt.DisplayRole:
+            return self.header[section]
+
+        if role == Qt.ToolTipRole and section == 4:
+            return _translate(
+                "MainWindowProfile",
+                "Abscisse en travers calculée en projétant les points"
+                " \nsur le plan défini par les deux points nommés extrêmes "
+            )
+
+        if orientation == Qt.Vertical and role == Qt.DisplayRole:
+            return self._data.index[section] + 1
+
+        return None
+
+    def setData(self, index, value, role=Qt.EditRole):
+        if role == Qt.EditRole:
+            try:
+                if index.column() == 3:
+                    self._data.iat[index.row(), index.column()] = str(value)
+                elif index.column() == 0:
+                    self._data.iat[index.row(), index.column()] = float(value)
+                elif index.column() == 1:
+                    self._data.iat[index.row(), index.column()] = float(value)
+                elif index.column() == 2:
+                    self._data.iat[index.row(), index.column()] = float(value)
+
+                self._data.iloc[:, 4] = projection_pointXYZ.update_station(
+                    self.header,
+                    self._data.values.tolist()
+                )
+                self.dataChanged.emit(index, index)
+            except:
+                print('TODO')
+                self.QMessageBoxCritical(value)
+
+            return True
+
+        self.dataChanged.emit(index, index)
+        self.layoutChanged.emit()
+
+        return False
+
+    @staticmethod
+    def QMessageBoxCritical(value):
+        msg = QMessageBox()
+        msg.setIcon(QMessageBox.Warning)
+        msg.setText("{} : Valeur saisie incorrecte ".format(value))
+        msg.setInformativeText("Seules les valeurs numériques sont autorisées.")
+        msg.setWindowTitle("Warning ")
+        msg.setStyleSheet("QLabel{min-width:150 px; font-size: 13px;} QPushButton{ width:20px; font-size: 12px};"
+                          "background-color: Ligthgray ; color : gray;font-size: 8pt; color: #888a80;")
+        msg.exec_()
+
+    def index(self, row, column, parent=QModelIndex()):
+        if not self.hasIndex(row, column, parent):
+            return QModelIndex()
+        return self.createIndex(row, column, QModelIndex())
+
+    def flags(self, index):
+        return Qt.ItemIsEditable | Qt.ItemIsSelectable | Qt.ItemIsEnabled
+
+    # @QtCore.pyqtSlot()
+    def insertRows(self, row, count, parent=QModelIndex()):
+        self.beginInsertRows(parent, row, row + count - 1)
+        indexes = [str(self.rowCount() + i) for i in range(count)]
+        left = self._data[0:row]
+        mid = pd.DataFrame(index=indexes, columns=self._data.columns)
+        right = self._data[row + count - 1:self.rowCount()]
+
+        self._data = pd.concat([left, mid, right])
+
+        for i in [3]:
+            self._data.iloc[:, i].replace(np.nan, '', inplace=True)
+
+        self._data.reset_index(drop=True, inplace=True)
+
+        try:
+            self._data.iloc[:, 4] = projection_pointXYZ.update_station(
+                self.header,
+                self._data.values.tolist()
+            )
+        except:
+            print("TODO")
+
+        self.endInsertRows()
+        self.layoutChanged.emit()
+
+    # @QtCore.pyqtSlot()
+    def removeRows(self, row, count, parent=QModelIndex()):
+        self.beginRemoveRows(parent, row, row + count + 1)
+        self._data.drop(self._data.index[row], inplace=True)
+        self._data.iloc[:, 4] = projection_pointXYZ.update_station(
+            self.header,
+            self._data.values.tolist()
+        )
+        self.endRemoveRows()
+        self.layoutChanged.emit()
+
+    def remove_rows1(self, row, count, parent=QModelIndex()):
+        self.beginRemoveRows(parent, row, row + count - 1)
+        left = self._data.iloc[0:row]
+        right = self._data.iloc[row + count:self.rowCount()]
+
+        self._data = pd.concat([left, right], axis=0, ignore_index=True)
+        self._data.iloc[:, 4] = projection_pointXYZ.update_station(
+            self.header,
+            self._data.values.tolist()
+        )
+        self.endRemoveRows()
+        self.layoutChanged.emit()
+
+    def remove_rows(self, list_row_selected, parent=QModelIndex()):
+        self.beginRemoveRows(parent, list_row_selected[0], list_row_selected[-1])
+
+        try:
+            self._data.drop(self._data.index[list_row_selected], inplace=True)
+            self._data.reset_index(drop=True, inplace=True)
+        except:
+            print('TODO')
+        try:
+            self._data.iloc[:, 4] = projection_pointXYZ.update_station(
+                self.header,
+                self._data.values.tolist()
+            )
+        except:
+            print("TODO")
+
+        self.endRemoveRows()
+        self.layoutChanged.emit()
+
+    def sort(self, column, order=Qt.AscendingOrder):
+        self.layoutAboutToBeChanged.emit()
+        colname = self._data.columns.tolist()[column]
+        self._data.sort_values(colname, ascending=order == Qt.AscendingOrder, inplace=True)
+        self._data.reset_index(inplace=True, drop=True)
+
+        self._data.iloc[:, 4] = projection_pointXYZ.update_station(
+            self.header,
+            self._data.values.tolist()
+        )
+        self.layoutChanged.emit()
+
+    def moveRowDown(self, row_to_move, parent=QModelIndex()):
+        target = row_to_move + 2
+        self.beginMoveRows(parent, row_to_move, row_to_move, parent, target)
+        block_before_row = self._data.iloc[0:row_to_move]
+        selected_row = self._data.iloc[row_to_move:row_to_move + 1]
+        after_selcted_row = self._data.iloc[row_to_move + 1:row_to_move + 2]
+        block_after_row = self._data.iloc[row_to_move + 2:self.rowCount()]
+
+        self._data = pd.concat([block_before_row, after_selcted_row, selected_row, block_after_row], axis=0)
+        self._data.reset_index(inplace=True, drop=True)
+
+        self.endMoveRows()
+        self.layoutChanged.emit()
+
+    def moveRowUp(self, row_to_move, parent=QModelIndex()):
+        target = row_to_move + 1
+        self.beginMoveRows(parent, row_to_move - 1, row_to_move - 1, parent, target)
+        block_before_row = self._data.iloc[0:row_to_move - 1]
+        before_selected_row = self._data.iloc[row_to_move - 1:row_to_move]
+        selected_row = self._data.iloc[row_to_move:row_to_move + 1]
+        block_after_row = self._data.iloc[row_to_move + 1:self.rowCount()]
+
+        self._data = pd.concat([block_before_row, selected_row, before_selected_row, block_after_row], axis=0)
+        self._data.reset_index(inplace=True, drop=True)
+
+        self.endMoveRows()
+        self.layoutChanged.emit()
+
+    def copyTable(self, start_selection, end_selection):
+        end_selection = self.rowCount()
+
+        self._data.loc[start_selection:end_selection]\
+                  .to_clipboard(header=None, index=False, excel=True, sep='\t')
+
+    def insert_df_to_idx(self, idx, df, df_insert):
+        """
+        Args:
+            idx: is the index position in df where you want to insert new dataframe (df_insert)
+            df: dataframe
+            df_insert: dataframe to insert
+        Returns:
+            The dataframe df with df_insert inserted at index idx.
+        """
+        return df.iloc[:idx, ].append(df_insert).append(df.iloc[idx:, ]).reset_index(drop=True)
+
+    def pasteTable(self, insertion_index):
+        self.layoutAboutToBeChanged.emit()
+        df = pd.read_clipboard(header=None, skip_blank_lines=True,
+                               sep="\t", names=self.header)
+        self._data = self.insert_df_to_idx(insertion_index, self._data, df)
+
+        for i in [3]:
+            self._data.iloc[:, i].replace(np.nan, '', inplace=True)
+
+        self.layoutChanged.emit()
+        self._data.iloc[:, 4] = projection_pointXYZ.update_station(
+            self.header,
+            self._data.values.tolist()
+        )
+
+    @property
+    def model_data(self):
+        return self._data
+
+    @model_data.setter
+    def model_data(self, new_data):
+        self._data = new_data
+        self.layoutChanged.emit()
+
+    @property
+    def x(self):
+        return self._data.iloc[:, 0].tolist()
+
+    @property
+    def y(self):
+        return self._data.iloc[:, 1].tolist()
+
+    @property
+    def z(self):
+        return self._data.iloc[:, 2].tolist()
+
+    @property
+    def name(self):
+        return self._data.iloc[:, 3].tolist()
+
+    def get_data(self):
+        return self._data
+
+    @property
+    def station(self):
+        return self._data.iloc[:, 4].tolist()
+
+    def remove_duplicates_names(self):
+        counter_list = []
+        list_deleted_names = []
+        ind_ind = []
+
+        for ind, name_point in enumerate(self.name):
+            if name_point not in counter_list:
+                counter_list.append(name_point)
+            elif len(name_point.strip()) > 0 and name_point in counter_list:
+                ind_ind.append(ind)
+
+                if name_point not in list_deleted_names:
+                    list_deleted_names.append(name_point)
+
+        for ind in ind_ind:
+            self._data.iat[ind, 3] = ""
+
+    def data_contains_nan(self) -> bool:
+        """
+        Returns:
+            Returns True if the QTableView() contains np.nan
+        """
+        return self._data.isnull().values.any()
+
+    def delete_empty_rows(self):
+        self.layoutAboutToBeChanged.emit()
+
+        self._data.dropna(inplace=True)
+        self._data.reset_index(drop=True, inplace=True)
+
+        self.layoutChanged.emit()
+
+    def valide_all_changes(self):
+        self.profile.x = self._data.iloc[:, 0]
+        self.profile.y = self._data.iloc[:, 1]
+        self.profile.z = self._data.iloc[:, 2]
+        self.profile.ld = self._data.iloc[:, 3]
+
+
+class Delegate(QtWidgets.QStyledItemDelegate):
+    def __init__(self, parent=None, setModelDataEvent=None):
+        super(Delegate, self).__init__(parent)
+        self.setModelDataEvent = setModelDataEvent
+
+    def createEditor(self, parent, option, index):
+        """
+        Args:
+            parent:
+            option:
+            index:
+        Returns:
+            Le widget (éditeur) pour éditer l'item se trouvant à l'index index.
+        """
+        index.model().data(index, Qt.DisplayRole)
+        return QtWidgets.QLineEdit(parent)
+
+    def setEditorData(self, editor, index):
+        """
+        Args:
+            editor: l'éditeur
+            index: l'index
+        Returns: permet de transmettre à l'éditeur editor les données à afficher à partir du modèle se trouvant
+                à l'index index.
+        """
+        value = index.model().data(index, Qt.DisplayRole)
+        editor.setText(str(value))
+
+    def setModelData(self, editor, model, index):
+        """
+        Args:
+            editor: l'éditeur
+            model: le modèle
+            index: l'index
+        Returns: permet de récupérer les données de l'éditeur et de les stocker à l'intérieur du modèle, à l'index
+                identifié par le paramètre index
+        """
+        model.setData(index, editor.text())
+
+        if not self.setModelDataEvent is None:
+            self.setModelDataEvent()
+
+    def updateEditorGeometry(self, editor, option, index):
+        """
+        Args:
+            editor: l'éditeur
+            option:
+            index: l'index
+        Returns: Permet de redimensionner l'éditeur à la bonne taille lorsque la taille de la vue change
+        """
+        editor.setGeometry(option.rect)
diff --git a/src/View/Geometry/qtableview_reach.py b/src/View/Geometry/qtableview_reach.py
new file mode 100644
index 0000000000000000000000000000000000000000..3c892051d15fd5015eea2a61003ec9d3c402abfe
--- /dev/null
+++ b/src/View/Geometry/qtableview_reach.py
@@ -0,0 +1,1038 @@
+# -*- coding: utf-8 -*-
+
+import time
+from functools import reduce
+from itertools import groupby
+from operator import itemgetter
+
+import numpy as np
+import pandas as pd
+
+from PyQt5 import (
+    QtGui, QtWidgets
+)
+from PyQt5.QtCore import (
+    Qt, QAbstractTableModel, QModelIndex, QVariant, pyqtSlot, QCoreApplication
+)
+from PyQt5.QtWidgets import (
+    QMessageBox
+)
+
+from Model.Geometry import Reach
+
+_translate = QCoreApplication.translate
+
+class PandasModelEditable(QAbstractTableModel):
+    def __init__(self, reach, header=None, profiles_type=None):
+        QAbstractTableModel.__init__(self)
+        data_list = []
+
+        self._data = reach
+
+        if header is None:
+            self.headers = [
+                _translate("MainWindow_reach", "Nom"),
+                _translate("MainWindow_reach", "Pk"),
+                _translate("MainWindow_reach", "Type")
+            ]
+        else:
+            self.headers = header
+
+        if profiles_type is None:
+            self.profiles_type = [
+                "XYZ", "AC",
+                # "LC", "R", "T", "RT"
+            ]
+
+    def rowCount(self, parent=QModelIndex()):
+        return len(self._data.profile)
+
+    def columnCount(self, parent=QModelIndex()):
+        return len(self.headers)
+
+    def data(self, index, role=Qt.DisplayRole):
+        if index.isValid():
+            if role == Qt.DisplayRole and index.column() == 0:
+                value = self._data.profile[index.row()].name
+                return self._data.profile[index.row()].name
+
+            if role == Qt.DisplayRole and index.column() == 1:
+                return "%.4f" % float(self._data.profile[index.row()].pk)
+
+            if role == Qt.DisplayRole and index.column() == 2:
+                return self._data.profile[index.row()].profile_type
+
+            column_count = self.columnCount()
+            for column in range(0, column_count):
+                if index.column() == column and role == Qt.TextAlignmentRole:
+                    return Qt.AlignHCenter | Qt.AlignVCenter
+
+            if role == Qt.BackgroundRole and index.column() == 1:
+                return QtGui.QColor(Qt.lightGray)
+
+            if role == Qt.BackgroundRole and index.column() == 2:
+                color = QtGui.QColor()
+                data_i_profile_type = self._data.profile[index.row()].profile_type
+
+                if data_i_profile_type == self.profiles_type[1]:
+                    color = QtGui.QColor("magenta")
+                elif data_i_profile_type == self.profiles_type[2]:
+                    color = QtGui.QColor("red")
+                else:
+                    color = QtGui.QColor("lightgreen")
+
+                return color
+            if role == Qt.ForegroundRole and index.column() == 0:
+                if self._data.profile[index.row()].name == "Amont":
+                    return QtGui.QColor("Green")
+                elif self._data.profile[index.row()].name == "Aval":
+                    return QtGui.QColor("Red")
+
+        return QVariant()
+
+    def headerData(self, section, orientation, role=Qt.DisplayRole):
+        if role == Qt.DisplayRole:
+            if orientation == Qt.Horizontal:
+                if section < len(self.headers):
+                    return self.headers[section]
+                else:
+                    return "not implemented"
+            else:
+                return "%d" % (section + 1)
+
+        if orientation == Qt.Vertical and role == Qt.DisplayRole:
+            return self.headers[section]
+
+        return None
+
+    def setData(self, index, value, role=Qt.EditRole):
+        row = index.row()
+        column = index.column()
+
+        if role == Qt.EditRole and index.column() != 2:
+            try:
+                if role == Qt.EditRole and index.column() == 0:
+                    self._data.profile[index.row()].name = value
+
+                if role == Qt.EditRole and index.column() == 1:
+                    self._data.profile[index.row()].pk = float(value)
+
+                self.dataChanged.emit(index, index)
+            except:
+                self.QMessageBoxCritical(value)
+
+            return True
+
+        self.dataChanged.emit(index, index)
+        self.layoutChanged.emit()
+
+        return False
+
+    @staticmethod
+    def QMessageBoxCritical(value):
+        msg = QMessageBox()
+        msg.setIcon(QMessageBox.Warning)
+        msg.setText("{} : Valeur saisie incorrecte".format(value))
+        msg.setInformativeText("Seules les valeurs numériques sont autorisées.")
+        msg.setWindowTitle("Warning ")
+        msg.setStyleSheet("QLabel{min-width:250 px; font-size: 13px;} QPushButton{ width:20px; font-size: 12px};"
+                          "background-color: Ligthgray ; color : gray;font-size: 8pt; color: #888a80;")
+        msg.exec_()
+
+    def index(self, row, column, parent=QModelIndex()):
+        if not self.hasIndex(row, column, parent):
+            return QModelIndex()
+
+        return self.createIndex(row, column, QModelIndex())
+
+    def flags(self, index):
+        if index.column() == 2:
+            return Qt.ItemIsEnabled | Qt.ItemIsSelectable
+        else:
+            return Qt.ItemIsEditable | Qt.ItemIsSelectable | Qt.ItemIsEnabled
+
+    # @QtCore.pyqtSlot()
+    def insertRows(self, row, count, parent=QModelIndex()):
+        self.beginInsertRows(parent, row, row + count - 1)
+
+        self._data.add_profile(row)
+        self._data.profile[row].profile_type = "XYZ"
+
+        self.endInsertRows()
+        self.layoutChanged.emit()
+
+    def insertRows_(self, row, rowt=1, parent=QModelIndex()):
+        self.beginInsertRows(parent, row, rowt)
+        self._data.add_profile(row)
+        self.endInsertRows()
+        self.layoutChanged.emit()
+
+    def remove_rows(self, list_row_selected, parent=QModelIndex()):
+        self.beginRemoveRows(parent, list_row_selected[0], list_row_selected[-1])
+
+        if len(list_row_selected) >= 1:
+            if len(list_row_selected) == 1:
+                self._data.delete_profile(list_row_selected[0])
+            elif len(list_row_selected) > 1:
+                self._data.delete_profile_rows(list_row_selected)
+
+        self.endRemoveRows()
+        self.layoutChanged.emit()
+
+    def removeRows_(self, position, rows, parent=QModelIndex()):
+        self.beginRemoveRows(QModelIndex(), position, position + 1)
+        self._data.delete_profile(position)
+        self.endRemoveRows()
+        self.layoutChanged.emit()
+
+    def export_reach(self, filename):
+        with open(f"{filename}", "w") as file_st:
+            for index_pro in range(len(self._data.profile)):
+                file_st.write("    ")
+                for head in self._data.headers[index_pro]:
+                    file_st.write(str(head))
+                    file_st.write("    ")
+                file_st.write("\n")
+
+                for point in self._data.profile[index_pro].points:
+                    print(point)
+
+                    for i in [point.x, point.y, point.z, point.name]:
+                        if isinstance(i, float):
+                            if i >= 0:
+                                file_st.write("    " + str("%.4f" % i))
+                            else:
+                                file_st.write("   " + str("%.4f" % i))
+                        else:
+                            file_st.write("    " + str(i))
+
+                        file_st.write("\n")
+                file_st.write("    999.9990     999.9990     999.9990")
+                file_st.write("\n")
+
+    def sort_data(self, _reverse):
+        self.layoutAboutToBeChanged.emit()
+        self._data.sort_descending(_reverse)
+        self.layoutAboutToBeChanged.emit()
+        self.layoutChanged.emit()
+
+    def moveRowDown(self, row_to_move, parent=QModelIndex()):
+        target = row_to_move + 2
+        self.beginMoveRows(parent, row_to_move, row_to_move, parent, target)
+        self._data.move_down_profile(row_to_move)
+        self.endMoveRows()
+        self.layoutChanged.emit()
+
+    def moveRowUp(self, row_to_move, parent=QModelIndex()):
+        target = row_to_move + 1
+        self.beginMoveRows(parent, row_to_move - 1, row_to_move - 1, parent, target)
+        self._data.move_up_profile(row_to_move)
+        self.endMoveRows()
+        self.layoutChanged.emit()
+
+    def graphique(self, ax):
+        self.layoutChanged.emit()
+        self.ax = ax
+        for i in range(len(self._data)):
+            self.ax.plot(self._data[i][1].iloc[:, 0],
+                         self._data[i][1].iloc[:, 1],
+                         color='r', lw=1., markersize=3, marker='+')
+
+    def get_profile_selected(self, index):
+        return self._data.profile[index]
+
+    def get_profile_selected_identifier(self, index):
+        return self._data.profile[index].headers[0]
+
+    def get_profile_name(self, index):
+        return self._data.profile[index].name
+
+    def get_profile_via_identifier(self, identifier):
+        profile, = [
+            profile for profile in self._data.profile
+            if profile.headers[0] == str(identifier)
+        ]
+        return profile
+
+    def get_z_min(self):
+        return self._data.get_bief_list_z_min()
+
+    def get_z_min_i(self, index):
+        return self._data.profile[index].z_min
+
+    def get_z_max(self):
+        """
+        Returns: liste des z_max du profile
+        """
+        return self._data.get_bief_list_z_max()
+
+    def get_z_max_i(self, index):
+        return self._data.profile[index].z_max
+
+    def get_pk(self):
+        """
+        Returns: liste des Pk
+        """
+        return self._data.get_bief_pk()
+
+    def get_pk_i(self, index):
+        return self._data.profile[index].pk
+
+    def get_x(self):
+        return self._data.get_bief_x()
+
+    def get_y(self):
+        return self._data.get_bief_y()
+
+    def get_x_complete_list_ld(self):
+        return self._data.get_bief_x_complete_ld()
+
+    def get_y_complete_list_ld(self):
+        return self._data.get_bief_y_complete_ld()
+
+    def get_z_complete_list_ld(self):
+        return self._data.get_bief_z_complete_ld()
+
+    def get_pk_complete_list_ld(self):
+        return self._data.get_bief_pk_complete_guideline()
+
+    def get_x_incomplete_list_ld(self):
+        pass
+
+    def x_complete_guideline(self):
+        return [
+            [
+                profile.x_all_points_complete_guideline(name)
+                for profile in self._data.profile
+            ] for name in self._data.complete_guideline()
+        ]
+
+    def y_complete_guideline(self):
+        return [
+            [
+                profile.y_all_points_complete_guideline(name)
+                for profile in self._data.profile
+            ] for name in self._data.complete_guideline()
+        ]
+
+    def z_complete_guideline(self):
+        return [
+            [
+                profile.z_all_points_complete_guideline(name)
+                for profile in self._data.profile
+            ] for name in self._data.complete_guideline()
+        ]
+
+    def get_station(self, index):
+        return self._data.get_station(index)
+
+    def get_z(self):
+        return self._data.get_bief_z()
+
+    def get_x_profile_i(self, index):
+        return self._data.profile[index].x
+
+    def get_y_profile_i(self, index):
+        return self._data.profile[index].y
+
+    def get_z_profile_i(self, index):
+        return self._data.profile[index].z
+
+    def get_ld_profile_i(self, index):
+        return self._data.profile[index].ld
+
+    def get_pk_profile_i(self, index):
+        return self._data.profile[index].pk
+
+    def get_complete_list_ld(self):
+        return self._data.complete_guideline()
+
+    def get_incomplete_list_ld(self):
+        return self._data.incomplete_guideline()
+
+    def model_data(self):
+        return self._data
+
+    def add_ld(self, *args):
+        lst = []
+        for i in args:
+            list_i = []
+            for j in range(len(i)):
+                l = i[1]
+                list_i.append(l)
+            lst.extend(list_i)
+        return lst, len(lst)
+
+    def size_list_named_points(self):
+        return len(self.get_all_maned_points())
+
+
+class Delegate(QtWidgets.QStyledItemDelegate):
+    def __init__(self, parent=None, setModelDataEvent=None):
+        super(Delegate, self).__init__(parent)
+        self.setModelDataEvent = setModelDataEvent
+
+    def createEditor(self, parent, option, index):
+        """retourne le widget (éditeur) pour éditer l'item se trouvant à l'index index."""
+        index.model().data(index, Qt.DisplayRole)
+        return QtWidgets.QLineEdit(parent)
+
+    def setEditorData(self, editor, index):
+        """permet de transmettre à l'éditeur editor les données à afficher à partir du modèle se trouvant à l'index
+        index. """
+        value = index.model().data(index, Qt.DisplayRole)  # DisplayRole
+        editor.setText(str(value))  # récupère la valeur de la cellule applique la méthode définie dans setData
+        print('Donnée éditée dans la case [{},{}] :'.format(index.row(), index.column()), value)
+
+    def setModelData(self, editor, model, index):
+        """permet de récupérer les données de l'éditeur et de les stocker à l'intérieur du modèle, à l'index identifié
+        par le paramètre index """
+        model.setData(index, editor.currentItem().text())
+
+        if not self.setModelDataEvent is None:
+            self.setModelDataEvent()
+
+    def updateEditorGeometry(self, editor, option, index):
+        """permet de redimensionner l'éditeur à la bonne taille lorsque la taille de la vue change"""
+        editor.setGeometry(option.rect)
+
+
+class Delegate11(QtWidgets.QItemDelegate):
+    def __init__(self, owner, choices):
+        super().__init__(owner)
+        self.items = choices
+
+    def createEditor(self, parent, option, index):
+        self.editor = QtWidgets.QComboBox(parent)
+        self.editor.currentIndexChanged.connect(self.commit_editor)
+        self.editor.addItems(self.items)
+        return self.editor
+
+    def paint(self, painter, option, index):
+        value = index.data(Qt.DisplayRole)
+        style = QtWidgets.QApplication.style()
+        opt = QtWidgets.QStyleOptionComboBox()
+        opt.text = str(value)
+        opt.rect = option.rect
+        style.drawComplexControl(QtWidgets.QStyle.CC_ComboBox, opt, painter)
+        QtWidgets.QItemDelegate.paint(self, painter, option, index)
+
+    def commit_editor(self):
+        editor = self.sender()
+        color = QtGui.QColor()
+        if editor.currentText() == "ProfilXYZ":
+            color = QtGui.QColor("lightgreen")
+        elif editor.currentText() == "ProfilAC":
+            color = QtGui.QColor("red")
+        qss = """QComboBox{background-color : %s;}""" % (color.name(),)
+        editor.setStyleSheet(qss)
+        self.commitData.emit(editor)
+
+    def setEditorData(self, editor, index):
+        editor.blockSignals(True)
+        editor.setCurrentIndex(index.row())  # replacement
+        editor.blockSignals(False)
+
+    @pyqtSlot()
+    def currentIndexChanged(self):
+        self.commit_editor.emit(self.sender())
+
+    def setModelData(self, editor, model, index):
+        value = editor.currentText()
+        model.setData(index, Qt.DisplayRole, QVariant(value))
+
+    def updateEditorGeometry(self, editor, option, index):
+        editor.setGeometry(option.rect)
+
+
+class Delegate1(QtWidgets.QStyledItemDelegate):
+    def __init__(self, owner, choices):
+        super().__init__(owner)
+        self.items = choices
+
+    def paint(self, painter, option, index):
+        if isinstance(self.parent(), QtWidgets.QAbstractItemView):
+            self.parent().openPersistentEditor(index)
+        super(Delegate1, self).paint(painter, option, index)
+
+    def createEditor(self, parent, option, index):
+        editor = QtWidgets.QComboBox(parent)
+        editor.currentIndexChanged.connect(self.commit_editor)
+        editor.addItems(self.items)
+        return editor
+
+    def commit_editor(self):
+        editor = self.sender()
+        color = QtGui.QColor()
+
+        if editor.currentText() == "ProfilXYZ":
+            color = QtGui.QColor("lightgreen")
+        elif editor.currentText() == "ProfilAC":
+            color = QtGui.QColor("red")
+
+        qss = """QComboBox{background-color : %s;}""" % (color.name(),)
+        editor.setStyleSheet(qss)
+        self.commitData.emit(editor)
+
+    def setEditorData(self, editor, index):
+        editor.blockSignals(True)
+        text = index.model().data(index, Qt.DisplayRole)
+        try:
+            i = self.items.index(text)
+        except ValueError:
+            i = 0
+        editor.setCurrentIndex(i)
+        editor.blockSignals(False)
+
+    def setModelData(self, editor, model, index):
+        model.setData(index, editor.currentText(), Qt.DisplayRole)
+
+    def updateEditorGeometry(self, editor, option, index):
+        editor.setGeometry(option.rect)
+
+    @pyqtSlot()
+    def currentIndexChanged(self):
+        self.commitData.emit(self.sender())
+
+
+class PandasModelEditableCreateReach(QAbstractTableModel):
+    # TODO : Cette calsse permet juste d'éviter que l'application plante lorsque 'on essaye d'éditer le tablueau. Le
+    #  problème se trouve au niveau du délégué QtWidgets.QStyledItemDelegate. Il faudra prendre en compte le cas où
+    #  on part de zero pour creer un bief. Pour l'instant, le prototype ne gère que l'importation d'une géométrie.
+    #
+    def __init__(self, data, headers):
+        QAbstractTableModel.__init__(self)
+        data_list = []
+        self._data = data
+        self.headers = headers
+
+    def rowCount(self, parent=QModelIndex()):  # parent=None):
+        return len(self._data)
+
+    def columnCount(self, parent=QModelIndex()):  # parent=None):
+        return len(self.headers)
+
+    def data(self, index, role=Qt.DisplayRole):
+        if index.isValid():
+            if role == Qt.DisplayRole and index.column() == 0:
+                value = self._data[index.row()][0][5]
+                return self._data[index.row()][0][5]
+
+            if role == Qt.DisplayRole and index.column() == 1:
+                return "%.4f" % float(self._data[index.row()][0][4])
+
+            column_count = self.columnCount()
+            for column in range(0, column_count):
+                if index.column() == column and role == Qt.TextAlignmentRole:
+                    return Qt.AlignHCenter | Qt.AlignVCenter
+
+            if role == Qt.BackgroundRole and index.column() == 1:
+                return QtGui.QColor(Qt.lightGray)
+
+        return QVariant()  # None
+
+    def headerData(self, section, orientation, role=Qt.DisplayRole):
+        if role == Qt.DisplayRole:
+            if orientation == Qt.Horizontal:
+                if section < len(self.headers):
+                    return self.headers[section]
+                else:
+                    return "not implemented"
+            else:
+                return "%d" % (section + 1)
+
+        if orientation == Qt.Vertical and role == Qt.DisplayRole:
+            return self._data[section]
+
+        return None
+
+    def setData(self, index, value, role=Qt.EditRole):
+        row = index.row()
+        column = index.column()
+
+        if role == Qt.EditRole and index.column() != 2:
+            try:
+                if role == Qt.EditRole and index.column() == 0:
+                    self._data[index.row()][0][5] = value
+
+                if role == Qt.EditRole and index.column() == 1:
+                    self._data[index.row()][0][4] = float(value)
+                self.dataChanged.emit(index, index)
+            except:
+                print('TODO')
+            return True
+
+        self.layoutChanged.emit()
+        return False
+
+    def index(self, row, column, parent=QModelIndex()):
+        if not self.hasIndex(row, column, parent):
+            return QModelIndex()
+        return self.createIndex(row, column, QModelIndex())
+
+    def flags(self, index):
+        if index.column() == 2:
+            return Qt.ItemIsEnabled
+        else:
+            return Qt.ItemIsEditable | Qt.ItemIsSelectable | Qt.ItemIsEnabled
+
+    # @QtCore.pyqtSlot()
+    def insertRows(self, row, count, parent=QModelIndex()):
+        self.beginInsertRows(parent, row, row + count - 1)
+        indexes = [str(self.rowCount() + i) for i in range(count)]
+        self._data.insert(row, [[self.rowCount() + 1, 0, 0, 0, np.nan, ''],
+                                pd.DataFrame({'x': [0], 'y': [0], 'z': [0], 'ld': ['']})])
+        self.endInsertRows()
+        self.layoutChanged.emit()
+
+    # @QtCore.pyqtSlot()
+    def removeRows1(self, row, count, parent=QModelIndex()):
+        self.beginRemoveRows(parent, row, row)
+        self._data.pop(row)
+        self.endRemoveRows()
+        self.layoutChanged.emit()
+
+    def removeRows(self, position, rows, parent=QModelIndex()):
+        self.beginRemoveRows(QModelIndex(), position, position + rows - 1)
+
+        for i in range(rows):
+            del (self._data[position])
+
+        self.endRemoveRows()
+        self.layoutChanged.emit()
+
+        return True
+
+    # # @QtCore.pyqtSlot()
+    def sort_data(self, _reverse):
+        self.layoutAboutToBeChanged.emit()
+
+        if not _reverse:
+            self._data = sorted(self._data, key=lambda x: float(x[0][4]), reverse=False)
+        else:
+            self._data = sorted(self._data, key=lambda x: float(x[0][4]), reverse=True)
+
+        self.layoutAboutToBeChanged.emit()
+        self.layoutChanged.emit()
+
+    def moveRowDown(self, row_to_move, parent=QModelIndex()):
+        target = row_to_move + 2
+        self.beginMoveRows(parent, row_to_move, row_to_move, parent, target)
+        self._data[row_to_move], self._data[row_to_move + 1] = self._data[row_to_move + 1], self._data[row_to_move]
+        self.endMoveRows()
+        self.layoutChanged.emit()
+
+    def moveRowUp(self, row_to_move, parent=QModelIndex()):
+        target = row_to_move + 1
+
+        self.beginMoveRows(parent, row_to_move - 1, row_to_move - 1, parent, target)
+        self._data[row_to_move], self._data[row_to_move - 1] = self._data[row_to_move - 1], self._data[row_to_move]
+        self.endMoveRows()
+        self.layoutChanged.emit()
+
+    def graphique(self, ax):
+        self.layoutChanged.emit()
+        self.ax = ax
+
+        for i in range(len(self._data)):
+            self.ax.plot(self._data[i][1].iloc[:, 0],
+                         self._data[i][1].iloc[:, 1],
+                         color='r', lw=1., markersize=3, marker='+')
+
+    def get_z_min(self):
+        z_min = []
+
+        for row in range(self.rowCount()):
+            z_min.append(min(self._data[row][1].iloc[:, 2].tolist()))
+
+        return z_min
+
+    def get_z_max(self):
+        """
+        Returns: liste des z_max du profile
+        """
+        z_max = []
+
+        for row in range(self.rowCount()):
+            z_max.append(max(self._data[row][1].iloc[:, 2].tolist()))
+
+        return z_max
+
+    def get_pk(self):
+        """
+        Returns: liste des Pk
+        """
+        Pk = [
+            float(self._data[row][0][4]) for row in range(self.rowCount())
+        ]
+
+        return Pk
+
+    def get_x(self):
+        d = time.time()
+        x = []
+
+        for row in range(self.rowCount()):
+            lst = self._data[row][1].iloc[:, 0]  # .tolist()
+            x.append(lst)
+
+        return x
+
+    def get_y(self):
+        y = []
+
+        for row in range(self.rowCount()):
+            lst = self._data[row][1].iloc[:, 1]  # .tolist()
+            y.append(lst)
+
+        return y
+
+    def get_z(self):
+        z = []
+
+        for row in range(self.rowCount()):
+            z.append(float(self._data[row][1].iloc[:, 2]))
+
+        return z
+
+    def add_ld(self, *args):
+        lst = []
+
+        for i in args:
+            list_i = []
+            for j in range(len(i)):
+                l = i[1]
+                list_i.append(l)
+            lst.extend(list_i)
+
+        return lst, len(lst)
+
+    def get_all_maned_points(self):
+        """
+            Returns: la liste qui est l'union disjointe des listes de nom de points de tous les profils du profile
+        """
+        # je considère une union disjointe au lieu d'une intersection pour prendre en compte d'éventuelles lignes
+        # directrices qui ne sont définies qu'à une partie du profile ie qui ne relient pas les deux deux profils
+        # extrêmes.
+        # NB : la liste retournée est "alphanumériquement" ordonnée ?!
+        liste = [data[1].iloc[:, 3].tolist() for data in self._data]
+        disjoint_union_ld = reduce(np.union1d, liste)
+        disjoint_union_ld_list = [
+            element for element in disjoint_union_ld.tolist() if len(element.strip()) != 0
+        ]
+
+        return disjoint_union_ld_list
+
+    def size_list_named_points(self):
+        return len(self.get_all_maned_points())
+
+    def get_complete_list_ld(self):
+        liste = [data[1].iloc[:, 3] for data in self._data]
+        intersection_ld = reduce(np.intersect1d, liste)
+        intersection_ld_list = [el for el in intersection_ld.tolist() if el != ""]
+        return intersection_ld_list
+
+    def get_incomplete_list_ld(self):
+        """
+            Returns : liste de noms de points nommés incomplets, c'est-à-dire, des points nommés définis que
+            sur un ou quelques profils seulement. Il s'agit de la liste des lignes directrices "incomplètes".
+        """
+        liste = [data[1].iloc[:, 3] for data in self._data]
+        disjoint_union_ld = reduce(np.union1d, liste)
+        disjoint_union_ld_list = [el for el in disjoint_union_ld.tolist() if el != ""]
+        diff_union_instersect_ld = list(
+            set(disjoint_union_ld_list).symmetric_difference(set(self.get_complete_list_ld())))
+        return diff_union_instersect_ld
+
+    def get_x_y_named_points(self):
+        """
+            Returns : les coordonnées (x,y) des points nommés "complets" et (éventuellement) "incomplets"
+        """
+        list_x_y_ld = []
+        list_x_y_ld_incomplete = []
+
+        liste = [data[1].iloc[:, 3] for data in self._data]
+        intersection_ld = reduce(np.intersect1d, liste)
+        intersection_ld_list = [el for el in intersection_ld.tolist() if el != ""]
+        disjoint_union_ld = reduce(np.union1d, liste)
+        disjoint_union_ld_list = [el for el in disjoint_union_ld.tolist() if el != ""]
+        diff_union_instersect_ld = list(set(disjoint_union_ld_list).symmetric_difference(set(intersection_ld_list)))
+
+        if len(intersection_ld_list) != 0:
+            for data in self._data:
+                df = data[1]  # .iloc[row, 3]
+                col_name = df.columns[3]  # colonne (Name) des lignes directrices
+                col_x = df.columns[0]
+                col_y = df.columns[1]
+                list_name = df[col_name].to_list()  # liste de noms de lignes directrices
+                list_x = df[col_x].to_list()
+                list_y = df[col_y].to_list()
+                list_ld = [[list_x[list_name.index(name)], list_y[list_name.index(name)], name]
+                           for name in intersection_ld_list]
+                list_x_y_ld.append(list_ld)
+                if len(diff_union_instersect_ld) != 0:
+                    list_ld_incomlete = [
+                        [list_x[list_name.index(name)], list_y[list_name.index(name)], name]
+                        for name in diff_union_instersect_ld
+                        if name in list_name
+                    ]
+
+                    if len(list_ld_incomlete) != 0:
+                        [list_x_y_ld_incomplete.append(i) for i in list_ld_incomlete]
+
+            x_y_list_named_pt_i = [
+                [listeld[i] for listeld in list_x_y_ld]
+                for i in range(len(intersection_ld_list))
+            ]
+
+            liste_x_ld = [[element[0] for element in x_y_list_named_pt_ii]
+                          for x_y_list_named_pt_ii in x_y_list_named_pt_i]
+
+            liste_y_ld = [[element[1] for element in x_y_list_named_pt_ii]
+                          for x_y_list_named_pt_ii in x_y_list_named_pt_i]
+
+            x_y_ld = [np.array([liste_x_ld[i], liste_y_ld[i]]) for i in range(len(x_y_list_named_pt_i))]
+
+            if len(diff_union_instersect_ld) != 0:
+                x_y_list_named_pt_i_incomplete = [
+                    list(v) for _, v in
+                    groupby(sorted(list_x_y_ld_incomplete, key=itemgetter(2)),
+                            key=itemgetter(2))
+                ]
+
+                liste_x_ld_incomplete = [[element[0] for element in x_y_list_named_pt_ii] for x_y_list_named_pt_ii in
+                                         x_y_list_named_pt_i_incomplete]
+                liste_y_ld_incomplete = [[element[1] for element in x_y_list_named_pt_ii] for x_y_list_named_pt_ii in
+                                         x_y_list_named_pt_i_incomplete]
+                x_y_ld_incomplete = [np.array([liste_x_ld_incomplete[i], liste_y_ld_incomplete[i]]) for i in
+                                     range(len(x_y_list_named_pt_i_incomplete))]
+            else:
+                x_y_ld_incomplete = []
+            return x_y_ld, x_y_ld_incomplete
+        else:
+            print("TODO")
+            return []
+
+    def get_pk_z_named_points(self):
+        list_pk_z_ld = []
+
+        for row in range(self.rowCount()):
+            df = self._data[row][1]  # .iloc[row, 3]
+            col_name = df.columns[3]  # colonne (Name) des lignes directrices
+            col_z = df.columns[2]
+            list_name = df[col_name].to_list()  # liste de noms de lignes directrices
+            list_z = df[col_z].to_list()
+            Pk_i = float(self._data[row][0][4])  # le ième Pk
+            list_ld = [
+                [Pk_i, list_z[list_name.index(name)], name]
+                for name in self.get_all_maned_points()
+            ]
+
+            if len(list_ld) > 0:
+                list_pk_z_ld.append(list_ld)
+
+        mon_list = [
+            [listeld[i] for listeld in list_pk_z_ld]
+            for i in range(len(self.get_all_maned_points()))
+        ]
+
+        liste_pk_ld = [
+            [element[0] for element in mon_list[i]]
+            for i in range(len(mon_list))
+        ]
+        liste_z_ld = [
+            [element[1] for element in mon_list[i]]
+            for i in range(len(mon_list))
+        ]
+        pk_z_ld = [
+            np.array([liste_pk_ld[i], liste_z_ld[i]])
+            for i in range(len(mon_list))
+        ]
+
+        return pk_z_ld
+
+    def get_pk_z_ld(self):
+        """
+        Returns: liste de [Pk, z, ld] par profil
+                liste de listes contenant les z des points nommés (lignes directrices) par profil
+        """
+
+        list_pk_z_ld = []
+        list_pk_z_ld_incomplete = []
+
+        start = time.time()
+        liste = [data[1].iloc[:, 3].tolist() for data in self._data]
+
+        intersection_ld = reduce(np.intersect1d, liste)
+        intersection_ld_list = [el for el in intersection_ld.tolist() if el != ""]
+        disjoint_union_ld = reduce(np.union1d, liste)
+        disjoint_union_ld_list = [el for el in disjoint_union_ld.tolist() if el != ""]
+        diff_union_instersect_ld = list(set(disjoint_union_ld_list).symmetric_difference(set(intersection_ld_list)))
+
+        for row in range(self.rowCount()):
+            df = self._data[row][1]  # .iloc[row, 3]
+            col_name = df.columns[3]  # colonne (Name) des lignes directrices
+            col_z = df.columns[2]
+            list_name = df[col_name].to_list()  # liste de noms de lignes directrices
+            list_z = df[col_z].to_list()
+            Pk_i = float(self._data[row][0][4])  # le ième Pk
+
+            list_ld = [[Pk_i, list_z[list_name.index(name)], name] for name in
+                       intersection_ld_list]  # disjoint_union_ld_list]
+
+            if len(diff_union_instersect_ld) != 0:
+                list_ld_incomlete = [[Pk_i, list_z[list_name.index(name)], name] for name in diff_union_instersect_ld if
+                                     name in list_name]
+                list_pk_z_ld_incomplete.append(list_ld_incomlete)
+
+            if len(list_ld) > 0:
+                list_pk_z_ld.append(list_ld)
+
+        my_list = []
+        ll = []
+
+        for listeld in list_pk_z_ld:
+            ll.append(listeld[1])
+
+        ll0 = [listeld[1] for listeld in list_pk_z_ld]
+        mon_list = [[listeld[i] for listeld in list_pk_z_ld] for i in range(len(intersection_ld_list))]
+        liste_z_ld = [[el[1] for el in mon_list[i]] for i in range(len(mon_list))]
+        liste_pk_ld = [[el[0] for el in mon_list[i]] for i in range(len(mon_list))]
+        pk_z_ld = [np.array([liste_pk_ld[i], liste_z_ld[i]]) for i in range(len(mon_list))]
+
+        for j in range(len(list_pk_z_ld[0])):
+            lst = [el[j][1] for el in list_pk_z_ld]
+            my_list.append(lst)
+
+        return my_list  # liste de listes contenant les z des points nommés (lignes directrices) par profil
+
+    def model_data(self):
+        return self._data
+
+
+class Delegate(QtWidgets.QStyledItemDelegate):
+    # Lorsque l'on souhaite uniquement personnaliser l'édition des éléments dans une vue et non le rendu,
+    # on doit redéfinir quatre méthodes
+    def __init__(self, parent=None, setModelDataEvent=None):
+        super(Delegate, self).__init__(parent)
+        self.setModelDataEvent = setModelDataEvent
+
+    def createEditor(self, parent, option, index):
+        """retourne le widget (éditeur) pour éditer l'item se trouvant à l'index index."""
+        index.model().data(index, Qt.DisplayRole)
+        return QtWidgets.QLineEdit(parent)
+
+    def setEditorData(self, editor, index):
+        """permet de transmettre à l'éditeur editor les données à afficher à partir du modèle se trouvant à l'index
+        index. """
+        value = index.model().data(index, Qt.DisplayRole)  # DisplayRole
+        editor.setText(str(value))  # récupère la valeur de la cellule applique la méthode définie dans setData
+        print('Donnée éditée dans la case [{},{}] :'.format(index.row(), index.column()), value)
+
+    def setModelData(self, editor, model, index):
+        """permet de récupérer les données de l'éditeur et de les stocker à l'intérieur du modèle, à l'index identifié
+        par le paramètre index """
+        model.setData(index, editor.text())
+        if not self.setModelDataEvent is None:
+            self.setModelDataEvent()
+
+    def updateEditorGeometry(self, editor, option, index):
+        """permet de redimensionner l'éditeur à la bonne taille lorsque la taille de la vue change"""
+        editor.setGeometry(option.rect)
+
+
+class Delegate11(QtWidgets.QItemDelegate):
+    def __init__(self, owner, choices):
+        super().__init__(owner)
+        self.items = choices
+
+    def createEditor(self, parent, option, index):
+        self.editor = QtWidgets.QComboBox(parent)
+        self.editor.currentIndexChanged.connect(self.commit_editor)
+        self.editor.addItems(self.items)
+        return self.editor
+
+    def paint(self, painter, option, index):
+        value = index.data(Qt.DisplayRole)
+        style = QtWidgets.QApplication.style()
+        opt = QtWidgets.QStyleOptionComboBox()
+        opt.text = str(value)
+        opt.rect = option.rect
+        style.drawComplexControl(QtWidgets.QStyle.CC_ComboBox, opt, painter)
+        QtWidgets.QItemDelegate.paint(self, painter, option, index)
+
+
+    def commit_editor(self):
+        editor = self.sender()
+        color = QtGui.QColor()
+        if editor.currentText() == "ProfilXYZ":
+            color = QtGui.QColor("lightgreen")
+        elif editor.currentText() == "ProfilAC":
+            color = QtGui.QColor("red")
+        qss = """QComboBox{background-color : %s;}""" % (color.name(),)
+        editor.setStyleSheet(qss)
+        self.commitData.emit(editor)
+
+    def setEditorData(self, editor, index):
+        editor.blockSignals(True)
+        editor.setCurrentIndex(index.row())  # replacement
+        editor.blockSignals(False)
+
+    @pyqtSlot()
+    def currentIndexChanged(self):
+        self.commit_editor.emit(self.sender())
+
+    def setModelData(self, editor, model, index):
+        value = editor.currentText()
+        model.setData(index, Qt.DisplayRole, QVariant(value))
+
+    def updateEditorGeometry(self, editor, option, index):
+        editor.setGeometry(option.rect)
+
+
+class Delegate1(QtWidgets.QStyledItemDelegate):
+    def __init__(self, owner, choices):
+        super().__init__(owner)
+        self.items = choices
+
+    def paint(self, painter, option, index):
+        if isinstance(self.parent(), QtWidgets.QAbstractItemView):
+            self.parent().openPersistentEditor(index)
+        super(Delegate1, self).paint(painter, option, index)
+
+    def createEditor(self, parent, option, index):
+        editor = QtWidgets.QComboBox(parent)
+        editor.currentIndexChanged.connect(self.commit_editor)
+        editor.addItems(self.items)
+        return editor
+
+    def commit_editor(self):
+        editor = self.sender()
+        color = QtGui.QColor()
+        if editor.currentText() == "ProfilXYZ":
+            color = QtGui.QColor("lightgreen")
+        elif editor.currentText() == "ProfilAC":
+            color = QtGui.QColor("red")
+        qss = """QComboBox{background-color : %s;}""" % (color.name(),)
+        editor.setStyleSheet(qss)
+        self.commitData.emit(editor)
+
+    def setEditorData(self, editor, index):
+        editor.blockSignals(True)
+        text = index.model().data(index, Qt.DisplayRole)
+        try:
+            i = self.items.index(text)
+        except ValueError:
+            i = 0
+        editor.setCurrentIndex(i)
+        editor.blockSignals(False)
+
+    def setModelData(self, editor, model, index):
+        model.setData(index, editor.currentText(), Qt.DisplayRole)
+
+    def updateEditorGeometry(self, editor, option, index):
+        editor.setGeometry(option.rect)
+
+    @pyqtSlot()
+    def currentIndexChanged(self):
+        self.commitData.emit(self.sender())
diff --git a/src/View/GeometryWindow.py b/src/View/GeometryWindow.py
new file mode 100644
index 0000000000000000000000000000000000000000..ed58635d6c51c5742186f3c0cc876445e6347941
--- /dev/null
+++ b/src/View/GeometryWindow.py
@@ -0,0 +1,934 @@
+import os
+import pathlib
+import sys
+import csv
+import time
+
+from PyQt5 import QtWidgets
+from PyQt5.QtCore import (
+    QModelIndex, Qt, QSettings, pyqtSlot,
+    QItemSelectionModel, QCoreApplication, QSize
+)
+from PyQt5.QtWidgets import (
+    QApplication, QMainWindow, QFileDialog, QCheckBox
+)
+
+from View.Geometry.mainwindow_ui_reach import Ui_MainWindow
+from View.Geometry import qtableview_reach
+from View.Geometry import window_profileXYZ
+
+_translate = QCoreApplication.translate
+
+class GeomatryWindow(ASubWindow):
+    def __init__(self, parent=None):
+        self.parent = parent
+        super(MainReach, self).__init__()
+
+        self.ui = Ui_MainWindow()
+        self.ui.setupUi(self)
+        self.tableView = self.ui.tableView
+        self.tableView_header = self.ui.tableView_header
+        self.model = None
+        self.filename = None
+        self.setup_model()
+        self.setup_connections()
+
+    def setup_connections(self):
+        self.ui.btn_open.clicked.connect(self.open_file_dialog)
+        self.ui.btn_sort_asc.clicked.connect(self.sort_ascending)
+        self.ui.btn_sort_desc.clicked.connect(self.sort_descending)
+        self.ui.btn_move_up.clicked.connect(self.move_row_up)
+        self.ui.btn_move_down.clicked.connect(self.move_row_down)
+        self.ui.btn_end_editing.clicked.connect(self.handleSave)
+        self.ui.btn_add.clicked.connect(self.insert_row)
+        self.ui.btn_delete.clicked.connect(self.delete_row)
+        self.ui.btn_edit.clicked.connect(self.edit_profile)
+        self.ui.verticalSlider.valueChanged.connect(self.changed_slider_value)
+        self.tableView.selectionModel().selectionChanged.connect(self.changed_slider_value)
+        self.ui.btn_slider_up.clicked.connect(self.decrement_value_slider)
+        self.ui.btn_slider_down.clicked.connect(self.increment_value_slider)
+        self.ui.btn_move_up.clicked.connect(self.changed_profile_slot)
+
+    def setup_model(self):
+        self.model = qtableview_reach.PandasModelEditableCreateReach([], self.ui.tableView_header)
+        self.tableView.setModel(self.model)
+        self.tableView.setItemDelegate(qtableview_reach.Delegate())
+
+    def open_file_dialog(self):
+        options = QFileDialog.Options()
+        DEFAULT_DIRECTORY = '/home/'
+        settings = QSettings(QSettings.IniFormat, QSettings.UserScope, 'MyOrg', )  # application='MyApp', )
+        current_dir = settings.value('current_directory', DEFAULT_DIRECTORY, type=str)
+        options |= QFileDialog.DontUseNativeDialog
+        self.filename, _ = QtWidgets.QFileDialog.getOpenFileName(
+            self,
+            _translate("MainWindow_reach", "Ouvrir un fichier"),
+            current_dir,
+            _translate("MainWindow_reach",
+                       "Fichiers .ST (*.ST)") + ";;" +
+            _translate("MainWindow_reach",
+                       "Fichiers textes (*.txt)") + ";; " +
+            _translate("MainWindow_reach",
+                       "Tous les fichiers (*)"),
+            options=options
+        )
+
+        current_dir = os.path.split(self.filename)[0] or DEFAULT_DIRECTORY
+        settings.setValue('current_directory', current_dir)
+        if self.filename != "":
+            size = os.stat(self.filename).st_size
+
+            self.bief_name = pathlib.Path(self.filename).stem
+            self.setWindowTitle(f"{self.ui.mainwindow_title} {self.bief_name}")
+            self.model = qtableview_reach.PandasModelEditable(self.filename, self.tableView_header)
+            self.tableView.setModel(self.model)
+            self.update_text_label()
+
+            self.update_profile_windows()
+            self.graphic_1()
+            self.graphic_2()
+            self.graphic_3()
+            self.fichier_ouvert = True
+
+            # Profile selection when line change in table
+            self.tableView.selectionModel().selectionChanged.connect(
+                self.select_current_profile
+            )
+
+            # Update plot when profile data change
+            self.model.dataChanged.connect(self.update_graphic_1)
+            self.model.dataChanged.connect(self.update_graphic_2)
+            self.tableView.selectionModel().selectionChanged.connect(self.update_graphic_1)
+            self.tableView.selectionModel().selectionChanged.connect(self.update_graphic_2)
+
+
+    def update_text_label(self):
+        """
+        Returns: Cette méthode met à jour le texte (en haut à gauche) indiquant le nombre de profils et
+        l'ordre des sections.
+        """
+        if self.filename:
+            if self.model.rowCount() > 1:
+                self.ui.label_section_order.setText(
+                    f"{self.model.rowCount()}" + " " +
+                    _translate("MainWindow_reach", "profils") +
+                    f"{self.ui.text_section_order}"
+                )
+            else:
+                self.ui.label_section_order.setText(
+                    f"{self.model.rowCount()}" + " " +
+                    _translate("MainWindow_reach", "profil") +
+                    f"{self.ui.text_section_order}"
+                )
+
+    def messagebox_profile_editing(self):
+        msg_box = QtWidgets.QMessageBox()
+        msg_box.setIcon(QtWidgets.QMessageBox.Information)
+        msg_box.setWindowTitle(_translate("MainWindow_reach",
+                                          "Édition des profils sélectionnés"))
+        msg_box.setText(_translate("MainWindow_reach",
+                                   "Vous avez sélectionné plus de 5 profils."
+                                   " \nSeuls les 5 premiers seront édités."))
+        msg_box.setStandardButtons(QtWidgets.QMessageBox.Ok)
+
+        return_value = msg_box.exec()
+        if return_value == QtWidgets.QMessageBox.Ok:
+            print('OK clicked')
+
+    def edit_profile(self):
+        list_selected_row = list(
+            set([index.row() for index in self.tableView.selectedIndexes()])
+        )
+
+        self.tableView.model().blockSignals(True)
+
+        if len(list_selected_row) > 5:
+            self.messagebox_profile_editing()
+
+        for selected_row in list_selected_row[:5]:
+            selected_row = int(selected_row)
+            profile_identifier = self.model.get_profile_selected_identifier(selected_row)
+            Pk = self.model.get_pk_i(selected_row)
+            profile_name = self.model.get_profile_name(selected_row)
+
+            if len(self.list_second_window) == 0:
+                self.second_window = window_profileXYZ.View(
+                    selected_row + 1,
+                    self.model.get_profile_via_identifier(profile_identifier),
+                    pk=Pk, profile_name="", parent=self
+                )
+                self.second_window.window_title(
+                    pk=Pk,
+                    profile_name=profile_name,
+                    profile_selected_num=selected_row
+                )
+                self.second_window.setWindowFlags(Qt.Window | Qt.WindowStaysOnTopHint)
+                self.list_second_window.append(self.second_window)
+                self.second_window.show()
+                self.list_row.append(profile_identifier)
+
+            else:
+                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,
+                            profile_selected_num=selected_row
+                        )
+
+                    self.list_second_window[
+                        self.list_row.index(profile_identifier)
+                    ].setWindowFlags(Qt.Window | Qt.WindowStaysOnTopHint)
+
+                    self.list_second_window[
+                        self.list_row.index(profile_identifier)
+                    ].show()
+                else:
+                    second_window1 = window_profileXYZ.View(
+                        selected_row + 1,
+                        self.model.get_profile_via_identifier(profile_identifier),
+                        pk=Pk, profile_name="", parent=self
+                    )
+                    second_window1.window_title(
+                        pk=Pk, profile_name=profile_name,
+                        profile_selected_num=selected_row
+                    )
+
+                    second_window1.setWindowFlags(Qt.Window | Qt.WindowStaysOnTopHint)
+                    second_window1.show()
+                    self.list_row.append(profile_identifier)
+                    self.list_second_window.append(second_window1)
+
+        widgetList = QApplication.topLevelWidgets()
+        numWindows = len(widgetList)
+        self.tableView.model().blockSignals(False)
+
+    def wind_profile_changed(self):
+        self.second_window\
+            .datachanged_signal[bool]\
+            .connect(self.changed_profile_slot)
+
+    pyqtSlot(bool)
+
+    def changed_profile_slot(self, status):
+        print("status du changement ", status)
+        self.update_view1 = status
+        print(self.update_view1)
+
+    def update_graphic_1_profile(self):
+        if self.update_view1:
+            self.update_graphic_1()
+            print("update_graphic_1_profile")
+
+    def print_data(self):
+        print(self.model_data.profile)
+
+    def update_profile_windows(self):
+        self.list_second_window = []
+        self.list_row = []
+
+    def graphic_1(self):
+        self.ui.canvas_1.axes.cla()
+        self.ui.canvas_1.axes.grid(color='grey', linestyle='--', linewidth=0.5)
+        self.ui.canvas_1.axes.set_xlabel(
+            _translate("MainWindow_reach", "X (m)"), color='green', fontsize=12
+        )
+        self.ui.canvas_1.axes.set_ylabel(
+            _translate("MainWindow_reach", "Y (m)"), color='green', fontsize=12
+        )
+
+        self.get_x = self.model.get_x()
+        self.get_y = self.model.get_y()
+        self.line_xy = [
+            self.ui.canvas_1.axes.plot(x, y, color='r', lw=1., markersize=3, marker='+')
+            for x, y in zip(self.get_x, self.get_y)
+        ]
+
+        self.get_x_complete_list_ld = self.model.get_x_complete_list_ld()
+        self.get_y_complete_list_ld = self.model.get_y_complete_list_ld()
+        self.line_ld_1 = self.ui.canvas_1.axes.plot(
+            self.get_x_complete_list_ld,
+            self.get_y_complete_list_ld
+        )
+
+        self.plot_selected_1, = self.ui.canvas_1.axes.plot(
+            self.model.get_x_profile_i(0),
+            self.model.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.model.get_x_profile_i(0),
+            self.model.get_y_profile_i(0), lw=1., markersize=3,
+            marker='+', color="k", linestyle="--"
+        )
+
+        self.before_plot_selected_1.set_visible(False)
+
+        self.after_plot_selected_1, = self.ui.canvas_1.axes.plot(
+            self.model.get_x_profile_i(0),
+            self.model.get_y_profile_i(0), lw=1., markersize=3,
+            marker='+', color="m", linestyle='--'
+        )
+
+        self.after_plot_selected_1.set_visible(False)
+
+        self.ui.canvas_1.figure.tight_layout()
+        self.ui.canvas_1.figure.canvas.draw_idle()
+        self.ui.toolbar_1.update()
+
+    def graphic_2(self):
+        self.tableView.model().blockSignals(True)
+
+        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
+        )
+        self.ui.canvas_2.axes.set_ylabel(
+            _translate("MainWindow_reach", "Cote (m)"), color='green', fontsize=12
+        )
+
+        self.get_pk = self.model.get_pk()
+        self.get_z_min = self.model.get_z_min()
+        self.get_z_max = self.model.get_z_max()
+
+        self.line_pk_zmin_zmax = self.ui.canvas_2.axes.vlines(
+            x=self.get_pk,
+            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_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_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_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.ui.canvas_2.axes.plot(
+                    self.model.get_pk(), 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,
+            linestyle=":", lw=1.8,
+            color='lightgrey'
+        )
+
+        self.tableView.model().blockSignals(False)
+
+        self.ui.canvas_2.figure.tight_layout()
+        self.ui.canvas_2.figure.canvas.draw_idle()
+        self.ui.toolbar_2.update()
+
+    def update_graphic_1(self):
+        self.tableView.model().blockSignals(True)
+
+        for ind in range(self.model.rowCount()):
+            self.line_xy[ind][0].set_data(
+                self.model.get_x_profile_i(ind),
+                self.model.get_y_profile_i(ind)
+            )
+
+        for i in range(len(self.line_ld_1)):
+            self.line_ld_1[i].set_data(
+                [x[i] for x in self.get_x_complete_list_ld],
+                [y[i] for y in self.get_y_complete_list_ld]
+            )
+
+        self.tableView.model().blockSignals(False)
+        self.ui.canvas_1.figure.canvas.draw_idle()
+
+    def update_graphic_2(self):
+        self.tableView.model().blockSignals(True)
+
+        get_pk = self.model.get_pk()
+        get_z_min = self.model.get_z_min()
+        get_z_max = self.model.get_z_max()
+
+        self.line_pk_zmin.set_data(get_pk, get_z_min)
+
+        self.line_pk_zmin_zmax.remove()
+        self.line_pk_zmin_zmax = self.ui.canvas_2.axes.vlines(
+            x=get_pk,
+            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]
+                )
+        except:
+            print("TODO")
+
+        self.tableView.model().blockSignals(False)
+        self.ui.canvas_2.axes.autoscale_view(True, True, True)
+        self.ui.canvas_2.figure.canvas.draw_idle()
+
+    def graphic_3(self):
+        self.tableView.model().blockSignals(True)
+
+        selected_profile = 0
+        station = self.model.get_station(selected_profile)  # L'abscisse en travers
+        station_plus_1 = self.model.get_station(selected_profile + 1)
+        elevation = self.model.get_z_profile_i(selected_profile)
+        elevation_i_plus_1 = self.model.get_z_profile_i(selected_profile + 1)
+        ld = self.model.get_ld_profile_i(selected_profile)
+
+        self.ui.canvas_3.axes.cla()
+        self.ui.canvas_3.axes.grid(color='grey', linestyle='--', linewidth=0.5)
+        self.ui.canvas_3.axes.set_xlabel(
+            _translate("MainWindow_reach", "Abscisse en travers (m)"),
+            color='green', fontsize=12
+        )
+        self.ui.canvas_3.axes.set_ylabel(
+            _translate("MainWindow_reach", "Cote (m)"),
+            color='green', fontsize=12
+        )
+        self.ui.canvas_3.figure.tight_layout()
+
+        label_profile_i_minus_1 = _translate("MainWindow_reach", "Profil précédent")
+        label_profile_i = _translate("MainWindow_reach", "Profil sélectionné")
+        label_profile_i_plus_1 = _translate("MainWindow_reach", "Profil suivant")
+        color_profile_i_minus_1 = "k"  # 'grey'
+        color_profile_i = 'b'
+        color_profile_i_plus_1 = 'm'
+
+        self.profile_i_minus_1, = self.ui.canvas_3.axes.plot(
+            [], [], label=label_profile_i_minus_1, lw=1.8,
+            linestyle='--', color=color_profile_i_minus_1
+        )
+        self.profile_i, = self.ui.canvas_3.axes.plot(
+            station, elevation, label=label_profile_i,
+            color=color_profile_i, lw=1.8
+        )
+
+        self.profile_i_plus_1, = self.ui.canvas_3.axes.plot(
+            station_plus_1, elevation_i_plus_1,
+            label=label_profile_i_plus_1,
+            color=color_profile_i_plus_1, lw=1.6, linestyle='--'
+        )
+        self.annotation_3 = []
+        self.complete_list_ld = self.model.get_complete_list_ld()
+        self.incomplete_list_ld = self.model.get_incomplete_list_ld()
+
+        line_2d = [[line_2D] for line_2D in self.line_ld_1]
+        self.color_complete_ld = self.get_line_ld_colors(line_2d)
+        self.color_incomplete_ld = 2 * ["#000000"]
+
+        x_ld_complete = []
+        y_ld_complete = []
+        color_scat_complete_ld = []
+        x_ld_incomplete = []
+        y_ld_incomplete = []
+        color_scat_incomplete_ld = []
+
+        for i, txt in enumerate(list(ld)):
+            if txt.strip() in self.complete_list_ld:
+                annotation_3 = self.ui.canvas_3.axes.annotate(
+                    txt, (station[i], elevation[i]),
+                    horizontalalignment='left',
+                    verticalalignment='top', annotation_clip=True,
+                    fontsize=10,
+                    color=self.color_complete_ld[
+                        self.complete_list_ld.index(txt)
+                    ]
+                )
+
+                annotation_3.set_position((station[i] + 0., elevation[i] + 0.))
+                self.annotation_3.append(annotation_3)
+
+                x_ld_complete.append(station[i])
+                y_ld_complete.append(elevation[i])
+                color_scat_complete_ld.append(self.color_complete_ld[self.complete_list_ld.index(txt)])
+            elif txt.strip() in self.incomplete_list_ld:
+                annotate = self.ui.canvas_3.axes.annotate(
+                    txt, (station[i], elevation[i]), horizontalalignment='left',
+                    verticalalignment='top', annotation_clip=True, fontsize=11,
+                    color=self.color_incomplete_ld[
+                        self.incomplete_list_ld.index(txt)
+                    ],
+                    size=10
+                )
+
+                self.annotation_3.append(annotate)
+
+                x_ld_incomplete.append(station[i])
+                y_ld_incomplete.append(elevation[i])
+                color_scat_incomplete_ld.append(
+                    self.color_incomplete_ld[self.incomplete_list_ld.index(txt)]
+                )
+
+        self.tableView.model().blockSignals(False)
+
+        self.ui.canvas_3.axes.legend(fancybox=True, shadow=True, fontsize=8)
+        self.ui.canvas_3.figure.tight_layout()
+
+        self.ui.canvas_3.figure.canvas.draw_idle()
+        self.ui.toolbar_3.update()
+
+    def update_annotate_3(self, ind: int):
+        self.tableView.model().blockSignals(True)
+
+        for a in self.annotation_3:
+            a.remove()
+
+        self.annotation_3[:] = []
+
+        x = self.get_station(ind)
+        y = self.get_elevation(ind)
+        ld = self.model.get_ld_profile_i(ind)
+        get_complete_list_ld = self.model.get_complete_list_ld()
+        get_incomplete_list_ld = self.model.get_incomplete_list_ld()
+
+        try:
+            x_ld_complete = []
+            color_scat_complete_ld = []
+            x_ld_incomplete = []
+            color_scat_incomplete_ld = []
+            for i, txt in enumerate(list(ld)):
+                if txt in get_complete_list_ld:
+                    annotate = self.ui.canvas_3.axes.annotate(
+                        txt, (x[i], y[i]), horizontalalignment='left',
+                        verticalalignment='top', annotation_clip=True,
+                        fontsize=11,
+                        color=self.color_complete_ld[
+                            get_complete_list_ld.index(txt)
+                        ],
+                        size=10
+                    )
+                    self.annotation_3.append(annotate)
+                    x_ld_complete.append([x[i], y[i]])
+                    color_scat_complete_ld.append(
+                        self.color_complete_ld[self.complete_list_ld.index(txt)]
+                    )
+                elif txt in get_incomplete_list_ld:
+                    annotate = self.ui.canvas_3.axes.annotate(
+                        txt, (x[i], y[i]), horizontalalignment='left',
+                        verticalalignment='top', annotation_clip=True,
+                        fontsize=11,
+                        color=self.color_incomplete_ld[
+                            get_incomplete_list_ld.index(txt)
+                        ],
+                        size=10
+                    )
+                    self.annotation_3.append(annotate)
+                    x_ld_incomplete.append([x[i], y[i]])
+                    color_scat_incomplete_ld.append(
+                        self.color_incomplete_ld[get_incomplete_list_ld.index(txt)]
+                    )
+        except:
+            print("FIXME")
+
+        self.tableView.model().blockSignals(False)
+        self.ui.canvas_3.figure.canvas.draw_idle()
+
+    def get_line_ld_colors(self, line_2d):
+        colors = []
+
+        for line in line_2d:
+            colors.append(line[0].get_color())
+
+        return colors
+
+    def get_station(self, ind: int):
+        return self.model.get_station(ind)
+
+    def get_elevation(self, ind: int):
+        return self.model.get_z_profile_i(ind)
+
+    def update_graphic_3(self, ind: int):
+        self.tableView.model().blockSignals(True)
+
+        selected_profile = ind
+        print(selected_profile)
+
+        if selected_profile == 0:
+            self.profile_i_minus_1.set_data([], [])
+            self.profile_i.set_data(
+                self.get_station(selected_profile),
+                self.get_elevation(selected_profile)
+            )
+            self.profile_i_plus_1.set_data(
+                self.get_station(selected_profile + 1),
+                self.get_elevation(selected_profile + 1)
+            )
+        elif selected_profile == self.model.rowCount() - 1:
+            self.profile_i_minus_1.set_data(
+                self.get_station(selected_profile - 1),
+                self.get_elevation(selected_profile - 1)
+            )
+            self.profile_i.set_data(
+                self.get_station(selected_profile),
+                self.get_elevation(selected_profile)
+            )
+            self.profile_i_plus_1.set_data([], [])
+        elif 0 < selected_profile < self.model.rowCount() - 1:
+            self.profile_i_minus_1.set_data(
+                self.get_station(selected_profile - 1),
+                self.get_elevation(selected_profile - 1)
+            )
+            self.profile_i.set_data(
+                self.get_station(selected_profile),
+                self.get_elevation(selected_profile)
+            )
+            self.profile_i_plus_1.set_data(
+                self.get_station(selected_profile + 1),
+                self.get_elevation(selected_profile + 1)
+            )
+
+        self.tableView.model().blockSignals(False)
+
+        self.update_annotate_3(selected_profile)
+
+        self.ui.canvas_3.axes.relim()
+        self.ui.canvas_3.axes.autoscale_view()
+        self.ui.canvas_3.figure.canvas.draw_idle()
+
+    def select_plot_graphic_1(self, ind: int):
+        self.tableView.model().blockSignals(True)
+
+        if 0 <= ind < self.model.rowCount():
+            self.plot_selected_1.set_data(self.model.get_x_profile_i(ind),
+                                          self.model.get_y_profile_i(ind))
+            self.plot_selected_1.set_visible(True)
+        self.tableView.model().blockSignals(False)
+
+    def select_plot_graphic_2(self, ind: int):
+        get_pk_i = self.get_pk_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),
+                                          (get_z_min_i, get_z_max_i))
+            self.plot_selected_2.set_visible(True)
+
+    def select_before_plot_selected_1(self, ind: int):
+        if 0 <= ind < self.model.rowCount():
+            t0 = time.time()
+
+            self.before_plot_selected_1.set_data(
+                self.model.get_x_profile_i(ind),
+                self.model.get_y_profile_i(ind)
+            )
+
+            self.before_plot_selected_1.set_visible(True)
+            self.ui.canvas_1.figure.canvas.draw_idle()
+
+    def select_after_plot_selected_1(self, ind: int):
+        if 0 <= ind < self.model.rowCount():
+            self.after_plot_selected_1.set_data(
+                self.model.get_x_profile_i(ind),
+                self.model.get_y_profile_i(ind)
+            )
+            self.after_plot_selected_1.set_visible(True)
+            self.ui.canvas_1.figure.canvas.draw_idle()
+
+    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_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_z_min_i, get_z_max_i)
+            )
+
+            self.before_plot_selected_2.set_visible(True)
+            self.ui.canvas_2.figure.canvas.draw_idle()
+
+    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_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_z_min_i, get_z_max_i)
+            )
+            self.after_plot_selected_2.set_visible(True)
+            self.ui.canvas_2.figure.canvas.draw_idle()
+
+    def select_row_profile_slider(self, ind: int = 0):
+        if self.tableView is not None:
+            selectionModel = self.tableView.selectionModel()
+            index = self.tableView.model().index(ind, 0)
+
+            selectionModel.select(
+                index,
+                QItemSelectionModel.Rows |
+                QItemSelectionModel.ClearAndSelect |
+                QItemSelectionModel.Select
+            )
+
+            self.tableView.scrollTo(index)
+
+    def select_current_profile(self):
+        self.tableView.model().blockSignals(True)
+
+        if len(self.tableView.selectedIndexes()) > 0:
+            row = self.index_selected_row
+
+            self.ui.verticalSlider.setValue(row)
+            self.select_plot_graphic_1(row)
+            self.select_plot_graphic_2(row)
+
+            if row == 0:
+                self.before_plot_selected_1.set_visible(False)
+                self.select_after_plot_selected_1(row + 1)
+                self.before_plot_selected_2.set_visible(False)
+                self.select_after_plot_selected_2(row + 1)
+            elif 0 < row < self.model.rowCount() - 1:
+                self.select_before_plot_selected_1(row - 1)
+                self.select_after_plot_selected_1(row + 1)
+                self.select_before_plot_selected_2(row - 1)
+                self.select_after_plot_selected_2(row + 1)
+            elif row == self.model.rowCount() - 1:
+                self.after_plot_selected_1.set_visible(False)
+                self.select_before_plot_selected_1(row - 1)
+                self.after_plot_selected_2.set_visible(False)
+                self.select_before_plot_selected_2(row - 1)
+            self.tableView.model().blockSignals(False)
+
+            self.update_graphic_3(row)
+
+            self.ui.canvas_1.figure.canvas.draw_idle()
+            self.ui.canvas_2.figure.canvas.draw_idle()
+
+    def changed_slider_value(self):
+        self.tableView.model().blockSignals(True)
+
+        if self.filename is not None:
+            self.ui.verticalSlider.setMaximum(self.model.rowCount() - 1)
+
+            slider_value = self.ui.verticalSlider.value()
+
+            pk = self.model.get_pk_profile_i(slider_value)
+            self.ui.vertical_slider_label.setText(
+                _translate("MainWindow_reach", "Pk : ") +
+                f"{pk}" + "\n" +
+                _translate("MainWindow_reach",
+                           "Profil N° : ") +
+                f"{slider_value + 1}"
+            )
+
+            self.select_plot_graphic_1(slider_value)
+            self.select_plot_graphic_2(slider_value)
+            self.select_row_profile_slider(slider_value)
+
+            self.tableView.model().blockSignals(False)
+
+    def increment_value_slider(self):
+        if 0 <= self.ui.verticalSlider.value() < self.model.rowCount() - 1:
+            self.ui.verticalSlider.setValue(self.ui.verticalSlider.value() + 1)
+
+    def decrement_value_slider(self):
+        if 0 < self.ui.verticalSlider.value() < self.model.rowCount():
+            self.ui.verticalSlider.setValue(self.ui.verticalSlider.value() - 1)
+
+    def insert_row(self):
+        if len(self.tableView.selectedIndexes()) == 0:
+            self.model.insertRows(self.model.rowCount(), 1)
+        else:
+            row = self.index_selected_row
+            self.model.insertRows(row + 1, 1)
+
+        if self.filename is not None:
+            self.graphic_1()
+            self.graphic_2()
+            self.select_current_profile()
+
+    def delete_row(self):
+        rows = list(set(
+            [index.row() for index in self.tableView.selectedIndexes()]
+        ))
+
+        if len(rows) > 0:
+            self.model.remove_rows(rows)
+
+        self.update_graphic_1()
+        self.select_current_profile()
+
+        self.graphic_2()
+        self.changed_slider_value()
+
+    @property
+    def index_selected_row(self):
+        return [index.row() for index in self.tableView.selectedIndexes()][0]
+
+    def sort_ascending(self):
+        self.model.sort_data(True)
+        self.select_current_profile()
+        self.changed_slider_value()
+
+        self.update_graphic_2()
+        self.update_graphic_3(self.index_selected_row)
+
+    def sort_descending(self):
+        self.model.sort_data(False)
+
+        self.select_current_profile()
+        self.changed_slider_value()
+
+        self.update_graphic_2()
+        self.update_graphic_3(self.index_selected_row)
+
+    def move_row_down(self):
+        row = self.index_selected_row
+
+        if row < self.model.rowCount() - 1:
+            self.model.moveRowDown(row)
+            self.update_graphic_3(row + 1)
+            self.ui.canvas_3.axes.relim()
+            self.ui.canvas_3.axes.autoscale_view()
+
+            if row < self.model.rowCount() - 2:
+                self.select_before_plot_selected_1(row)
+                self.select_after_plot_selected_1(row + 2)
+                self.select_before_plot_selected_2(row)
+                self.select_after_plot_selected_2(row + 2)
+
+            if row == self.model.rowCount() - 2:
+                self.select_before_plot_selected_1(self.model.rowCount() - 2)
+                self.after_plot_selected_1.set_visible(False)
+                self.select_before_plot_selected_2(self.model.rowCount() - 2)
+                self.after_plot_selected_2.set_visible(False)
+
+            self.update_graphic_2()
+
+    def move_row_up(self):
+        row = self.index_selected_row
+
+        if 0 < row <= self.model.rowCount() - 1:
+            self.model.moveRowUp(row)
+
+            if row == 1:
+                self.select_after_plot_selected_1(row)
+                self.before_plot_selected_1.set_visible(False)
+                self.select_after_plot_selected_2(row)
+                self.before_plot_selected_2.set_visible(False)
+
+            elif row == self.model.rowCount() - 1:
+                self.select_before_plot_selected_1(row - 2)
+                self.select_after_plot_selected_1(row)
+                self.select_before_plot_selected_2(row - 2)
+                self.select_after_plot_selected_2(row)
+
+            else:
+                self.select_before_plot_selected_1(row - 2)
+                self.select_after_plot_selected_1(row)
+                self.select_before_plot_selected_2(row - 2)
+                self.select_after_plot_selected_2(row)
+
+            self.update_graphic_2()
+            self.update_graphic_3(row - 1)
+            self.ui.canvas_3.axes.relim()
+            self.ui.canvas_3.axes.autoscale_view()
+
+    def handleSave(self):
+        options = QFileDialog.Options()
+        DEFAULT_DIRECTORY = '/home/'
+        settings = QSettings(QSettings.IniFormat, QSettings.UserScope, 'MyOrg', )
+        current_dir = settings.value('current_directory', DEFAULT_DIRECTORY, type=str)
+        options |= QFileDialog.DontUseNativeDialog
+
+        filename, self.filters = QFileDialog.getSaveFileName(
+            self,
+            filter=_translate("MainWindow_reach",
+                              "Fichiers .ST(*.ST ou *.st)")
+            + ";; " +
+            _translate("MainWindow_reach", "Tous les fichiers "
+                       "(*)"),
+            options=options
+        )
+
+        current_dir = os.path.split(filename)[0] or DEFAULT_DIRECTORY
+
+        if filename != '':
+            self.model.export_reach(filename)
+
+    def handleOpen(self):
+        self.filename, self.filterName = QFileDialog.getOpenFileName(self)
+
+        if self.filename != '':
+            with open(self.filename, 'r') as f:
+                reader = csv.reader(f, delimiter='\t')
+                header = next(reader)
+                buf = []
+                for row in reader:
+                    row[0] = QCheckBox("-")
+                    buf.append(row)
+
+                self.model = None
+                self.model = qtableview_reach.PandasModelEditable(buf)
+                self.tableView.setModel(self.model)
+                self.filename = ''
+
+    def get_lignes_directrices(self):
+        liste_lignes_directrices = [
+            data[1].iloc[:, 3].tolist() for data in self.model._data
+        ]
+        return liste_lignes_directrices
+
+    def get_x(self):
+        return self.model.get_x()
+
+    def get_y(self):
+        return self.model.get_y()
+
+    def get_z(self):
+        return self.model.get_z()
+
+    def get_z_min(self):
+        return self.model.get_z_min()
+
+    def get_z_min_i(self, index):
+        return self.model.get_z_min_i(index)
+
+    def get_z_max_i(self, index):
+        return self.model.get_z_max_i(index)
+
+    def get_z_max(self):
+        return self.model.get_z_max()
+
+    def get_pk(self):
+        return self.model.get_pk()
+
+    def get_pk_i(self, index):
+        return self.model.get_pk_i(index)
+
+    def get_pk_z_ld(self):
+        return self.model.get_pk_z_ld()
+
+    def get_pk_z_ld_bis(self):
+        return self.model.get_pk_z_ld_bis()
+
+    @property
+    def model_data(self):
+        return self.model.model_data()
diff --git a/src/View/Plot/MplCanvas.py b/src/View/Plot/MplCanvas.py
new file mode 100644
index 0000000000000000000000000000000000000000..fa4c3ffffd6a2929ac54d9d31dac1cf33d2bb480
--- /dev/null
+++ b/src/View/Plot/MplCanvas.py
@@ -0,0 +1,34 @@
+from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg
+from matplotlib.figure import Figure
+
+
+class MplCanvas(FigureCanvasQTAgg):
+
+    def __init__(self, width=5, height=4, dpi=100):
+        fig = Figure(figsize=(width, height), dpi=dpi)
+        super(MplCanvas, self).__init__(fig)
+
+        self.axes = fig.add_subplot(111)
+        self.axes.format_coord = lambda x, y: '(x = ' + format(x, '1.4f') + ', \t' + ' y = ' + format(y, '1.4f') + ')'
+        self.axes.grid(color='green', linestyle='--', linewidth=0.5)
+        self.axes.yaxis.tick_left()
+        self.axes.xaxis.tick_bottom()
+        self.axes.spines[['top', 'right']].set_color('none')
+        self.figure.tight_layout()
+        self.add_arrows()
+
+    def add_arrows(self):
+        al = 8.
+        arrowprops = dict(
+            clip_on=True,
+            # frac=1.,
+            headwidth=5.,
+            facecolor='k'
+        )
+        kwargs = dict(
+            xycoords='axes fraction',
+            textcoords='offset points',
+            arrowprops=arrowprops,
+        )
+        self.axes.annotate("", (1, 0), xytext=(-al, 0), **kwargs)
+        self.axes.annotate("", (0, 1), xytext=(0, -al), **kwargs)
diff --git a/src/View/Plot/mpl_canvas_onpick_event.py b/src/View/Plot/mpl_canvas_onpick_event.py
new file mode 100644
index 0000000000000000000000000000000000000000..6a7e3f12a865b2422dbcdc8786d8ed923a5d998c
--- /dev/null
+++ b/src/View/Plot/mpl_canvas_onpick_event.py
@@ -0,0 +1,488 @@
+from time import time
+
+import numpy as np
+from PyQt5 import QtWidgets
+from PyQt5.QtCore import QItemSelectionModel, Qt
+from PyQt5.QtWidgets import QApplication
+from shapely.geometry.polygon import Polygon as ShapelyPolygon
+
+
+class OnpickEvent(object):
+    def __init__(self, ax, x, y, x_carto, y_carto, tableview=None):
+        """
+        Args:
+            ax: objet Axes.
+            x: abscisse x1 du graphe (f(x1) = y1) à tracer.
+            y: ordonnée y1 du graphe (f(x1) = y1) à tracer.
+            x_carto: (vecteur) abscisse des points (X,Y,Z) du profil.
+            y_carto: (vecteur) abscisse des points (X,Y,Z) du profil
+            tableview: tableau (de type QtableView) 'associé' au grahique.
+        """
+        self.ax = ax
+        self.x = x
+        self.y = y
+        self.x_carto = x_carto
+        self.y_carto = y_carto
+        self.tableView = tableview
+        self.counter_onclick = 0
+        # self.select_point_plot()
+        self.count = 0
+        self.annotation_onclick = self.ax.annotate("", xytext=(np.mean(self.x), np.mean(self.y)),
+                                                   xy=(np.mean(self.x), np.mean(self.y)),
+                                                   horizontalalignment='center',
+                                                   fontsize=8,
+                                                   # fontstyle='italic',
+                                                   fontweight='bold',
+                                                   alpha=0.7
+
+                                                   )
+        self.annotation_onclick.set_visible(False)
+        self.pos_x = 0
+        self.zomm_xmin_xmax = self.ax.get_xlim()
+        self.plot_selec()
+        # self.select_point_plot()
+        self._largeur_miroir, = self.ax.plot(self.x[1], self.y[1],
+                                             color='blue', lw=1.2, ls=":")
+
+        self.pt = []
+        self.tableView.selectionModel().selectionChanged.connect(self.update_select_point_point)
+
+    def select_row_pt_clicked(self, ind: int = 0):
+        """
+        Args:
+             ind: Indice de la ligne où se trouve le point le plus proche 'visé'.
+        Returns: Sélectionne la ligne (du tableau) correspondant au point le plus proche 'visé' à la suite de l'événement onpick.
+        """
+        if self.tableView is not None:
+            selectionModel = self.tableView.selectionModel()
+            index = self.tableView.model().index(ind, 0)
+            selectionModel.select(index,
+                                  QItemSelectionModel.Rows | QItemSelectionModel.ClearAndSelect |
+                                  QItemSelectionModel.Select)
+            self.tableView.scrollTo(index)
+
+    def select_qtableview_row(self, event):
+        if self.tableView is not None:
+            self.tableView.setFocus()
+            ind = self.indice_points_onpick(event)
+            dataidx_ecran = self.index_pt_plus_proche_ecran(event)
+            self.select_row_pt_clicked(ind[dataidx_ecran])
+
+    def select_point_plot(self):
+        """
+        Returns: sélectionne le(s) point(s) du graphique correspondant à la/aux ligne(s) sélectionnée(s) dans le tableau.
+        """
+        if self.tableView is not None:
+            rows = list(set([index.row() for index in self.tableView.selectedIndexes()]))
+            for row in rows:
+                pass
+
+    def update_select_point_point(self):
+        if self.tableView is not None:
+            rows = list(set([index.row() for index in self.tableView.selectedIndexes()]))
+            if len(rows) > 1:
+                for row in rows:
+                    self.pt1 = self.ax.plot(self.x[row], self.y[row], '+', c='Blue', markersize=7)
+                    self.pt.append(self.pt1)
+                    self.update_select_point_point_bis(self.x[row], self.y[row])
+
+            elif len(rows) == 1:
+                for row in rows:
+                    try:
+                        [pl[0].set_data([], []) for pl in self.pt if len(self.pt) > 1]
+                    except:
+                        print("Probleme  de mise à jour ... update_select_point_point()")
+
+                    try:
+                        self.update_select_point_point_bis(self.x[row], self.y[row])
+                    except:
+                        print("index introuvable pour la mise à jour de l'affichage de la sélection du point."
+                              "Editer les cases en 'nan'.")
+
+            self.ax.figure.canvas.draw_idle()
+
+    def plot_selec(self):
+        self.point_selec, = self.ax.plot(self.x[0], self.y[0], '+', c='Blue', markersize=7)
+        self.point_selec.set_visible(False)
+
+    def update_select_point_point_bis(self, x_ind, y_ind):
+        self.point_selec.set_data(x_ind, y_ind)
+        self.point_selec.set_visible(True)
+        self.ax.figure.canvas.draw_idle()
+
+    def plot_selection_point(self, x, y):
+        """
+        Args:
+            x: abscisse
+            y: ordonnée
+        Returns: sélectionne le point du graphique correspond à la ligne sélectionnée dans le tableau.
+        """
+        if self.tableView is not None:
+            self.select_point, = self.ax.plot(x, y, '+', c='Blue', markersize=7)
+        else:
+            self.select_point, = self.ax.plot([], [])
+
+    def geometrie_sans_rebord(self):
+        rebord = True
+        z_sans_rebord = [i for i in self.y]
+        x_sans_rebord = [i for i in self.x]
+
+        while rebord:
+            if z_sans_rebord[1] >= z_sans_rebord[0]:
+                z_sans_rebord.pop(0)
+                x_sans_rebord.pop(0)
+            else:
+                rebord = False
+
+        rebord = True
+        while rebord:
+            if z_sans_rebord[-1] <= z_sans_rebord[-2]:
+                z_sans_rebord.pop()
+                x_sans_rebord.pop()
+            else:
+                rebord = False
+
+        z_berge_basse = min(z_sans_rebord[0], z_sans_rebord[-1])
+
+        return z_berge_basse, z_sans_rebord, x_sans_rebord
+
+    @property
+    def z_berge_basse(self):
+        return self.geometrie_sans_rebord()[0]
+
+    @property
+    def z_sans_rebord(self):
+        return self.geometrie_sans_rebord()[1]
+
+    @property
+    def x_sans_rebord(self):
+        return self.geometrie_sans_rebord()[2]
+
+    @property
+    def z_fond(self):
+        return np.array(self.z_sans_rebord)
+
+    @property
+    def z_point_bas(self):
+        """
+        Returns: la cote (Zmin) du point le plus bas.
+        """
+        return min(self.y)
+
+    @property
+    def delta_x(self):
+        """"
+        Returns: la longueur entre les limites de la vue sur l'axe des x, c'est-à-dire |x_max_visible - x_min_visible|.
+        """
+        xgauche, xdroite = self.ax.get_xlim()
+        delta_x = abs(xdroite - xgauche)
+        return delta_x
+
+    @property
+    def delta_y(self):
+        """
+        Returns: la longueur entre les limites de la vue sur l'axe des y, c'est à dire |y_max_visible - y_min_visible|.
+        """
+        ybas, yhaut = self.ax.get_ylim()
+        delta_y = abs(yhaut - ybas)
+        return delta_y
+
+    @staticmethod
+    def indice_points_onpick(event):
+        """
+        Args: event
+        Returns: le(s) indexe(s) du/des point(s) (plus précisement les coordonnées de points) capturé(s)
+                 par l'événement onpick (voir picker)
+        """
+        return event.ind
+
+    def points_onpick(self, event):
+        """
+        Args:
+            event:
+        Returns: une array contenant les coordonées des points qui se trouvent dans la zone définie par l'événement
+        onpick (voir picker)
+
+        """
+        thisline = event.artist
+        xdata = thisline.get_xdata()
+        ydata = thisline.get_ydata()
+        points_onpick = np.array([(xdata[i], ydata[i]) for i in self.indice_points_onpick(event)])
+        return points_onpick
+
+    def distance_normee(self, event):
+        """
+        Args:
+            event:
+        Returns: la liste des distances normées (en m) entre les points situés dans la région définie par l'événement
+         onpick (voir picker).
+        """
+        ind = event.ind
+        thisline = event.artist
+        xdata = thisline.get_xdata()
+        ydata = thisline.get_ydata()
+        points_onpick = np.array([(xdata[i], ydata[i]) for i in ind])
+        distances_normees = [
+            (((x - event.mouseevent.xdata) / self.delta_x) ** 2 +
+             ((y - event.mouseevent.ydata) / self.delta_y) ** 2) ** (1 / 2)
+            for (x, y) in points_onpick
+        ]
+        return distances_normees
+
+    def position_souris(self, event):
+        """
+        Args:
+            event:
+        Returns: la position de la souris
+        """
+        self.pos_souris = [(event.mouseevent.xdata, event.mouseevent.ydata)]
+        return self.pos_souris
+
+    def distance_ecran(self, event):
+        """
+        Args:
+            event:
+        Returns: la liste des distances 'visuelles' entre les points situés dans la région définie par l'événement
+        onpick (voir picker).
+        """
+        bbox = self.ax.get_window_extent()\
+                      .transformed(self.ax.figure.dpi_scale_trans.inverted())
+        ratio_w_sur_h = bbox.width / bbox.height
+        distances_ecran = [
+            (((x - event.mouseevent.xdata) / (self.delta_x * ratio_w_sur_h)) ** 2 +
+             ((y - event.mouseevent.ydata) / self.delta_y) ** 2) ** (1 / 2)
+            for (x, y) in self.points_onpick(event)
+        ]
+
+        return distances_ecran
+
+    def distances(self, event):
+        """
+        Args: event:
+        Returns: la liste des distances entre la position de la souris et tous les points se trouvant dans
+        la zone définie par l'événement onpick ( voir picker)
+        """
+        distances = np.linalg.norm(
+            self.points_onpick(event) - self.position_souris(event),
+            axis=1
+        )
+
+        return distances
+
+    def index_pt_plus_proche_ecran(self, event):
+        """
+         Args:
+             event:
+         Returns: indice du point le plus proche visuellement de la position du click.
+         """
+        dataidx_ecran = np.argmin(self.distance_ecran(event))
+        return dataidx_ecran
+
+    def point_plus_proche_ecran(self, event):
+        point_onpick = self.points_onpick(event)
+        datapos_ecran = point_onpick[self.index_pt_plus_proche_ecran(event)]
+
+        return self.points_onpick(event)[self.index_pt_plus_proche_ecran(event)]
+
+    def index_pt_plus_proche(self, event):
+        """
+        Args:
+            event:
+        Returns: indice du point le plus proche de la position du click.
+        """
+        dataidx = np.argmin(self.distances(event))
+
+        return dataidx
+
+    def point_plus_proche(self, event):
+        """
+        Args:
+            event:
+        Returns: point le plus proche de la position du click
+        """
+        point_onpick = self.points_onpick(event)
+        datapos = point_onpick[self.index_pt_plus_proche(event)]
+
+        return datapos
+
+    def annotate_onpick(self, x, y):
+        """
+        Args:
+            x: abscisse du point à annoter.
+            y: ordonnée du point à annoter.
+        Returns: annote le point xy avec du texte text = xytext.
+        """
+        return self.ax.annotate(
+            "X", xytext=(x, y),
+            xy=(x, y), fontsize=9,
+            bbox=dict(
+                boxstyle='round,pad=0.8', fc='yellow', alpha=0.75
+            ),
+            arrowprops=dict(
+                arrowstyle='->',
+                connectionstyle='arc3,rad=0.',
+                color='blue'
+            )
+        )
+
+    def on_ylims_change(self, event_ax):
+        return event_ax.get_ylim()
+
+    def annotate_onclick(self, event):
+        if self.z_point_bas <= event.ydata:
+            self.count += 1
+
+            if event.ydata <= self.z_berge_basse:
+                A, p, L = self.calcul_ligne_eau(event.ydata)
+            else:
+                event.ydata = self.z_berge_basse
+                A, p, L = self.calcul_ligne_eau(event.ydata)
+
+            etiq = f"Z = {event.ydata:.3f} m, A = {A:.3f} m\u00B2, p = {p:.3f} m, L = {L:.3f} m"
+            self.annotation_onclick.set_text(etiq)
+            x_min, x_max = self.ax.get_xlim()
+            self.pos_x_annotation = x_min + ((x_max - x_min) / 2)
+
+            percent = 0
+            y_ecran_lim = ((max(self.ax.set_ylim()) - min(self.ax.set_ylim())) / 2)
+
+            if abs(y_ecran_lim) > 4:
+                percent = 0.05
+            elif 4 < abs(y_ecran_lim) < 1.5:
+                percent = 0.01
+            elif 0.5 < abs(y_ecran_lim) < 1.5:
+                percent = 0.05
+            elif 0.25 < abs(y_ecran_lim) < 0.5:
+                percent = 0.08
+            elif 0 < abs(y_ecran_lim) < 0.25:
+                percent = 0.25
+            else:
+                percent = 0.1
+
+            cte = 0.
+            if abs(event.ydata) < 100:
+                cte = 0.05
+            else:
+                cte = event.y * 0.1 / 100
+
+            self.y_pos_text_param_hydrau = event.ydata + cte
+            self.annotation_onclick.set_position((self.pos_x_annotation, self.y_pos_text_param_hydrau))
+
+            self.ax.callbacks.connect('ylim_changed', self.on_ylims_change)
+
+            self.annotation_onclick.set_color("DarkBlue")
+            self.annotation_onclick.set_visible(True)
+            self.annotation_onclick.set_horizontalalignment('center')
+
+            self.ax.figure.canvas.draw_idle()
+
+            return self.annotation_onclick
+
+    def largeur_au_miroir(self, event):
+        if event.ydata <= self.z_berge_basse:
+            self._largeur_miroir.set_data(
+                [min(self.x), max(self.x)],
+                [event.ydata, event.ydata]
+            )
+        else:
+            self._largeur_miroir.set_data(
+                [min(self.x), max(self.x)],
+                [self.z_berge_basse, self.z_berge_basse]
+            )
+
+        return self._largeur_miroir
+
+    def onpick(self, event):
+        modifiers = QApplication.keyboardModifiers()
+
+        if modifiers == Qt.ControlModifier:
+            if event.mouseevent.inaxes == self.ax:
+                self.select_qtableview_row(event)
+                x_proche, y_proche = self.point_plus_proche_ecran(event)
+                self.update_select_point_point_bis(x_proche, y_proche)
+
+            self.ax.figure.canvas.draw_idle()
+
+    def onclick(self, event):
+        modifiers = QtWidgets.QApplication.keyboardModifiers()
+        if modifiers == Qt.ShiftModifier:
+            if event.inaxes == self.ax:
+                if self.z_point_bas < event.ydata:
+                    try:
+                        self.poly_col_bis.remove()
+                        self.largeur_au_miroir(event)
+                    except:
+                        self.largeur_au_miroir(event)
+
+                self.annotate_onclick(event)
+                self.ax.figure.canvas.draw_idle()
+
+    def remplir_zone_mouillee(self, x, y1, y2):
+        """
+        Args:
+            x: Les coordonnées x des nœuds définissant la courbe.
+            y1: points définisant le polygone à déssiner.
+            y2: points définisant le polygone à déssiner.
+        Returns: dessine et colorie la région définie par le polygone.
+        """
+        return self.ax.fill_between(
+            x, y1=y1, y2=y2, where=y1 > y2, interpolate=True,
+            facecolor='skyblue', alpha=0.7
+        )
+
+    def calcul_ligne_eau(self, val: float) -> (float, float, float):
+        """
+        Args:
+            val: Valeur de la cote Z à laquelle on veut caluler A , p et L.
+        Returns: la valeur de la section mouillée A, du périmètre mouillé p et de la largeur au miroir L.
+        """
+        largeur_miroir = 0.
+        section_mouillee_totale = 0.
+        perimetre_mouille_total = 0.
+
+        if self.z_point_bas < val <= self.z_berge_basse:
+            z_eau = np.array([val] * (len(self.z_sans_rebord)))
+            self.poly_col_bis = self.remplir_zone_mouillee(self.x_sans_rebord, z_eau, self.z_sans_rebord)
+            liste_chemins = self.poly_col_bis.get_paths()
+            couleurs = ['crimson', 'pink'] * len(liste_chemins)
+            aire_calculee_shapely = None
+            perimetre_mouille_total_shapely = None
+            perimetre_shapely = 0.
+            perim_calc = 0.
+
+            for polyg, coul in zip(liste_chemins, couleurs[0:len(liste_chemins)]):
+                points_polygone = polyg.vertices
+                xs = points_polygone[:, 0]
+                ys = points_polygone[:, 1]
+
+                liste_points_miroir = [
+                    x for (x, y) in zip(xs, ys) if np.isclose(y, val)
+                ]
+                largeur_miroir_polygone = liste_points_miroir[-2] - liste_points_miroir[0]
+                largeur_miroir += largeur_miroir_polygone
+
+                polygone_shapely = ShapelyPolygon(points_polygone)
+                aire_calculee_shapely = polygone_shapely.area
+                perimetre_shapely = polygone_shapely.length
+                perimetre_mouille_total_shapely = polygone_shapely.length - largeur_miroir
+                liste_points_fond = [
+                    (x, y) for (x, y) in zip(xs, ys) if not np.isclose(y, val)
+                ]
+                x_pt_prec, y_pt_prec = max(liste_points_miroir), val
+
+                perimetre = 0
+                aire = 0
+
+                for un_point in liste_points_fond + [(min(liste_points_miroir), val)]:
+                    x_pt_suivant, y_pt_suivant = un_point
+                    perimetre += ((x_pt_prec - x_pt_suivant) ** 2 +
+                                  (y_pt_prec - y_pt_suivant) ** 2) ** (1 / 2)
+                    aire += (((val - y_pt_prec) + (val - y_pt_suivant)) *
+                             abs(x_pt_suivant - x_pt_prec) / 2)
+                    x_pt_prec, y_pt_prec = x_pt_suivant, y_pt_suivant
+                    perim_calc = perimetre
+
+            perimetre_mouille_total = perimetre_shapely - largeur_miroir
+            section_mouillee_totale = aire_calculee_shapely
+
+        return section_mouillee_totale, perimetre_mouille_total, largeur_miroir
diff --git a/src/View/Plot/navigation_toolbar_2qt.py b/src/View/Plot/navigation_toolbar_2qt.py
new file mode 100644
index 0000000000000000000000000000000000000000..44d86f72633b7e66e4933e85607396dd2493f36f
--- /dev/null
+++ b/src/View/Plot/navigation_toolbar_2qt.py
@@ -0,0 +1,162 @@
+# -*- coding: utf-8 -*-
+
+import os
+import matplotlib as mpl
+from matplotlib.backends import qt_compat
+from PyQt5 import QtCore, QtGui, QtWidgets
+from PyQt5.QtWidgets import QAction
+from matplotlib.backends.backend_qt5 import NavigationToolbar2QT
+from PyQt5.QtCore import pyqtSignal, QSize
+
+_translate = QtCore.QCoreApplication.translate
+
+file_path = os.path.abspath(os.path.dirname(__file__))
+
+
+class PamHyrNavigationToolbar2QT(NavigationToolbar2QT):
+    """
+    Cette classe est une personnalisation de la toolbar (NavigationToolbar2QT). Elle permet l'internationnalisation et
+    l'ajout d'autres boutons à la toolbar initiale
+    """
+    isometric_signal = pyqtSignal(str)
+
+    def __init__(self, canvas, parent):
+        """
+        Args:
+            canvas: canvas de matplotlib
+            parent: parent du canvas
+        """
+        self.my_canvas = canvas
+        self.toolitems = [
+            (None, None, None, None),
+
+            ('Home', _translate("Toolbar", 'Vue originale'), 'home', 'home'),
+            (None, None, None, None),
+
+            ('Back', _translate("Toolbar", 'Retour à la vue précédente'), 'back', 'back'),
+            ('Forward', _translate("Toolbar", 'Passer à la vue suivante'), 'forward', 'forward'),
+            (None, None, None, None),
+
+            ('Pan', _translate(
+                "Toolbar",
+                'Panoramique des axes avec la souris gauche, zoom avec la droite'
+            ), 'move', 'pan'),
+            (None, None, None, None),
+
+            ('Zoom', _translate("Toolbar", 'Zoom'), 'zoom_to_rect', 'zoom'),
+            (None, None, None, None),
+
+            ('Isometric_view', _translate(
+                "Toolbar", 'Vue isométrique (Shift+W)'
+            ), '', 'isometric_view'),
+            (None, None, None, None),
+
+            ('GlobalView', _translate(
+                "Toolbar", 'Vue globale automatique (Shift+X)'
+            ), '', 'non_isometric_view'),
+
+            (None, None, None, None),
+
+            ('Save', _translate(
+                "Toolbar", 'Enregistrer la figure'
+            ), 'filesave', 'save_figure'),
+            (None, None, None, None),
+        ]
+
+        NavigationToolbar2QT.__init__(self, canvas, parent)
+        btn_size = QSize(30, 30)
+
+        icon_zoom = QtGui.QIcon()
+        icon_zoom.addPixmap(QtGui.QPixmap(
+            f"{os.path.dirname(os.path.dirname(file_path))}/resources/icons/zoom.png"
+        ))
+        icon_btn_isometric_view = QtGui.QIcon()
+        icon_btn_isometric_view.addPixmap(
+            QtGui.QPixmap(f"{os.path.dirname(os.path.dirname(file_path))}/resources"
+                          f"/icons/zoom_fit_11.png"))
+
+        icon_btn_global_view = QtGui.QIcon()
+        icon_btn_global_view.addPixmap(
+            QtGui.QPixmap(f"{os.path.dirname(os.path.dirname(file_path))}/resources/icons"
+                          f"/zoom_fit.png"))
+
+        actions = self.findChildren(QAction)
+        self._actions["zoom"].setIcon(icon_zoom)
+        self._actions["isometric_view"].setIcon(icon_btn_isometric_view)
+        self._actions["non_isometric_view"].setIcon(icon_btn_global_view)
+
+        self.addSeparator()
+        self.set_style_sheet()
+
+    def save_figure(self, *args):
+        filetypes = self.canvas.get_supported_filetypes_grouped()
+        sorted_filetypes = sorted(filetypes.items())
+        default_filetype = self.canvas.get_default_filetype()
+
+        startpath = os.path.expanduser(mpl.rcParams['savefig.directory'])
+        start = os.path.join(startpath, self.canvas.get_default_filename())
+        filters = []
+        selectedFilter = None
+        for name, exts in sorted_filetypes:
+            exts_list = " ".join(['*.%s' % ext for ext in exts])
+            filter = '%s (%s)' % (name, exts_list)
+            if default_filetype in exts:
+                selectedFilter = filter
+            filters.append(filter)
+        filters = ';;'.join(filters)
+
+        fname, filter = qt_compat._getSaveFileName(
+            self.canvas.parent(),
+            _translate("MainWindow_reach", "Choisissez un nom de fichier à sauvegarder"),
+            start,
+            filters, selectedFilter)
+
+        if fname:
+            if startpath != "":
+                mpl.rcParams['savefig.directory'] = os.path.dirname(fname)
+
+            try:
+                self.canvas.figure.savefig(fname)
+            except Exception as e:
+                QtWidgets.QMessageBox.critical(
+                    self, "Error saving file", str(e),
+                    QtWidgets.QMessageBox.Ok, QtWidgets.QMessageBox.NoButton
+                )
+
+    def cursor1(self):
+        print("TODO")
+
+    def isometric_view(self):
+        self.my_canvas.axes.axis("equal")
+        self.my_canvas.figure.canvas.draw_idle()
+        self.isometric_signal[str].emit("vue iso")
+
+    def non_isometric_view(self):
+        self.my_canvas.axes.axis("tight")
+        self.my_canvas.toolbar.update()
+        self.my_canvas.figure.canvas.draw_idle()
+
+    def toolitems_translation(self):
+        self._actions['home'].setToolTip(_translate("Toolbar", "Vue originale"))
+        self._actions['back'].setToolTip(_translate("Toolbar", "Retour à la vue précédente"))
+        self._actions['forward'].setToolTip(_translate("Toolbar", "Passer à la vue suivante"))
+        self._actions['pan'].setToolTip(_translate("Toolbar", "Panoramique des axes avec la "
+                                                              "souris gauche, zoom avec la droite"))
+        self._actions['zoom'].setToolTip(_translate("Toolbar", "Zoom"))
+        self._actions['save_figure'].setToolTip(_translate("Toolbar", "Enregistrer la figure"))
+        self.action_isometric_view.setToolTip(_translate("Toolbar", "Vue isométrique (Shift+W)"))
+        self.action_auto_global_view.setToolTip(_translate("Toolbar", "Vue globale automatique (Shift+X)"))
+
+    def set_style_sheet(self):
+        self.setStyleSheet(
+            "QToolBar{ border: 1px solid darkGray;\n"
+            "                                gridline-color: blue;\n"
+            "                                border-radius: 4px;\n"
+            "                                border-style: solid;\n"
+            "                                background-color: #EEF6FC; \n"
+            "                                selection-background-color: #218ede;\n"
+            "                                font-size: 12px;\n"
+            "                                font-family: Helvetica\n"
+            "\n"
+            "                   }"
+        )