diff --git a/src/Model/OutputKpAdists/OutputKpAdists.py b/src/Model/OutputKpAdists/OutputKpAdists.py
index af1c6df0903cc54196a3b59954f8750271927efb..d8ab6f566b786ac4624ce43308f6861e2387bfd7 100644
--- a/src/Model/OutputKpAdists/OutputKpAdists.py
+++ b/src/Model/OutputKpAdists/OutputKpAdists.py
@@ -86,7 +86,7 @@ class OutputKpAdists(SQLSubModel):
           CREATE TABLE OutputKpAdists(
             id INTEGER NOT NULL PRIMARY KEY,
             reach INTEGER NOT NULL,
-            kp INTEGER NOT NULL,
+            kp REAL NOT NULL,
             title TEXT NOT NULL,
             FOREIGN KEY(edge) REFERENCES river_reach(id)
           )
@@ -130,9 +130,18 @@ class OutputKpAdists(SQLSubModel):
 
     def _db_save(self, execute, data=None):
 
+        print("print output kp unit")
+        print("id : ", self.id)
+        print("reach : ", self._reach)
+        print("kp : ", self._kp)
+        print("title : ", self._title)
+        print("title format : ", self._db_format(self._title))
+
+        execute(f"DELETE FROM OutputKpAdists WHERE id = {self.id}")
+
         sql = (
             "INSERT INTO " +
-            "OutputKpAdists(id, reach, kp, title " +
+            "OutputKpAdists(id, reach, kp, title) " +
             "VALUES (" +
             f"{self.id}, {self._reach}, " +
             f"{self._kp}, '{self._db_format(self._title)}'" +
diff --git a/src/Model/OutputKpAdists/OutputKpListAdists.py b/src/Model/OutputKpAdists/OutputKpListAdists.py
index d6560e6847c5357de3bc241f02fb293d57b95afe..741cb2291a920a1e367fe9b0d260e27c7f99d144 100644
--- a/src/Model/OutputKpAdists/OutputKpListAdists.py
+++ b/src/Model/OutputKpAdists/OutputKpListAdists.py
@@ -35,6 +35,7 @@ class OutputKpAdistsList(PamhyrModelList):
         return new
 
     def _db_save(self, execute, data=None):
+        print("save outputkpadists")
         ok = True
 
         # Delete previous data
diff --git a/src/Model/Pollutants/Pollutants.py b/src/Model/Pollutants/Pollutants.py
index f2ca9e60dfa20fbf1f2b0653b660e4b950c8b5e6..42d5698c1d8ca97a5eb854c52e479d8255b307b7 100644
--- a/src/Model/Pollutants/Pollutants.py
+++ b/src/Model/Pollutants/Pollutants.py
@@ -47,7 +47,7 @@ class Pollutants(SQLSubModel):
         self._name = str(name)
         self._enabled = True
 
-        self._characteristics = []
+        self._data = []
 
         Pollutants._id_cnt = max(
             Pollutants._id_cnt + 1, self.id)
@@ -61,6 +61,9 @@ class Pollutants(SQLSubModel):
         self._name = name
         self._status.modified()
 
+    @property
+    def data(self):
+        return self._data.copy()
 
     @classmethod
     def _db_create(cls, execute):
@@ -125,7 +128,7 @@ class Pollutants(SQLSubModel):
                     for t in table:
                         new_data.append(t)
 
-                new_pollutant._characteristics = new_data
+                new_pollutant._data.append(new_data)
 
                 new.append(new_pollutant)
 
@@ -133,6 +136,8 @@ class Pollutants(SQLSubModel):
 
     def _db_save(self, execute, data=None):
 
+        print("save in data base for pollutants")
+
         execute(f"DELETE FROM Pollutants WHERE id = {self.id}")
         execute(f"DELETE FROM Pollutants_characteristics WHERE pollutant = {self.id}")
 
@@ -147,13 +152,13 @@ class Pollutants(SQLSubModel):
 
         execute(sql)
 
-        for d in self._characteristics:
+        for d in self._data:
             sql = (
                 "INSERT INTO " +
                 "Pollutants_characteristics(type, diametre, rho, porosity, " +
-                "cdc_riv, cdc_cas, apd, ac, bc) " +
-                f"VALUES ({d[1]}, {d[2]}, {d[3]},{d[4]}, {d[5]}, "
-                f"{d[6]}, {d[7]}, {d[8]}, {d[9]}, {self.id})"
+                "cdc_riv, cdc_cas, apd, ac, bc, pollutant) " +
+                f"VALUES ({d[0]}, {d[1]}, {d[2]},{d[3]}, {d[4]}, "
+                f"{d[5]}, {d[6]}, {d[7]}, {d[8]}, {self.id})"
             )
             execute(sql)
 
@@ -168,6 +173,13 @@ class Pollutants(SQLSubModel):
         self._enabled = enabled
         self._status.modified()
 
+    def is_define(self):
+        return len(self._data) != 0
+
+    def __len__(self):
+        return len(self._data)
+
+
 
 
 
diff --git a/src/Model/Pollutants/PollutantsList.py b/src/Model/Pollutants/PollutantsList.py
index 2526329a76b0f356f803dee125218eb598aa695f..e2fa44043128f47f0b58b008bc8028b5ee66d2e0 100644
--- a/src/Model/Pollutants/PollutantsList.py
+++ b/src/Model/Pollutants/PollutantsList.py
@@ -35,6 +35,7 @@ class PollutantsList(PamhyrModelList):
         return new
 
     def _db_save(self, execute, data=None):
+        print("save pollutantsList")
         ok = True
 
         # Delete previous data
diff --git a/src/Model/River.py b/src/Model/River.py
index 066dbaaf79e96544fa9818a28025df65fd034c8d..d380220dbb85b42433e551c3816d368c8be81da3 100644
--- a/src/Model/River.py
+++ b/src/Model/River.py
@@ -232,6 +232,7 @@ class River(Graph, SQLSubModel):
         AddFileList,
         REPLineList,
         OutputKpAdistsList,
+        PollutantsList,
     ]
 
     def __init__(self, status=None):
diff --git a/src/View/Pollutants/BasicHydraulicStructures/Table.py b/src/View/Pollutants/BasicHydraulicStructures/Table.py
deleted file mode 100644
index f69a4099f458accc233412f1cfbc4fa6fcf361d4..0000000000000000000000000000000000000000
--- a/src/View/Pollutants/BasicHydraulicStructures/Table.py
+++ /dev/null
@@ -1,277 +0,0 @@
-# Table.py -- Pamhyr
-# Copyright (C) 2023-2024  INRAE
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program.  If not, see <https://www.gnu.org/licenses/>.
-
-# -*- coding: utf-8 -*-
-
-import logging
-import traceback
-
-from tools import trace, timer
-
-from PyQt5.QtCore import (
-    Qt, QVariant, QAbstractTableModel,
-    QCoreApplication, QModelIndex, pyqtSlot,
-    QRect,
-)
-
-from PyQt5.QtWidgets import (
-    QDialogButtonBox, QPushButton, QLineEdit,
-    QFileDialog, QTableView, QAbstractItemView,
-    QUndoStack, QShortcut, QAction, QItemDelegate,
-    QComboBox, QMessageBox,
-)
-
-from View.Tools.PamhyrTable import PamhyrTableModel
-
-from View.HydraulicStructures.BasicHydraulicStructures.UndoCommand import (
-    SetNameCommand, SetTypeCommand,
-    SetEnabledCommand, AddCommand, DelCommand,
-    SetValueCommand,
-)
-from Model.HydraulicStructures.Basic.Types import BHS_types
-
-logger = logging.getLogger()
-
-_translate = QCoreApplication.translate
-
-
-class ComboBoxDelegate(QItemDelegate):
-    def __init__(self, data=None, trad=None, parent=None):
-        super(ComboBoxDelegate, self).__init__(parent)
-
-        self._data = data
-        self._trad = trad
-
-        self._long_types = {}
-        if self._trad is not None:
-            self._long_types = self._trad.get_dict("long_types")
-
-    def createEditor(self, parent, option, index):
-        self.editor = QComboBox(parent)
-
-        lst = list(
-            map(
-                lambda k: self._long_types[k],
-                BHS_types.keys()
-            )
-        )
-        self.editor.addItems(lst)
-
-        self.editor.setCurrentText(index.data(Qt.DisplayRole))
-        return self.editor
-
-    def setEditorData(self, editor, index):
-        value = index.data(Qt.DisplayRole)
-        self.editor.currentTextChanged.connect(self.currentItemChanged)
-
-    def setModelData(self, editor, model, index):
-        text = str(editor.currentText())
-        model.setData(index, text)
-        editor.close()
-        editor.deleteLater()
-
-    def updateEditorGeometry(self, editor, option, index):
-        r = QRect(option.rect)
-        if self.editor.windowFlags() & Qt.Popup:
-            if editor.parent() is not None:
-                r.setTopLeft(self.editor.parent().mapToGlobal(r.topLeft()))
-        editor.setGeometry(r)
-
-    @pyqtSlot()
-    def currentItemChanged(self):
-        self.commitData.emit(self.sender())
-
-
-class TableModel(PamhyrTableModel):
-    def __init__(self, trad=None, **kwargs):
-        self._trad = trad
-        self._long_types = {}
-        if self._trad is not None:
-            self._long_types = self._trad.get_dict("long_types")
-
-        super(TableModel, self).__init__(trad=trad, **kwargs)
-
-    def rowCount(self, parent):
-        return len(self._lst)
-
-    def data(self, index, role):
-        if role != Qt.ItemDataRole.DisplayRole:
-            return QVariant()
-
-        row = index.row()
-        column = index.column()
-
-        if self._headers[column] == "name":
-            return self._data.basic_structure(row).name
-        elif self._headers[column] == "type":
-            return self._long_types[self._data.basic_structure(row).type]
-
-        return QVariant()
-
-    def setData(self, index, value, role=Qt.EditRole):
-        if not index.isValid() or role != Qt.EditRole:
-            return False
-
-        row = index.row()
-        column = index.column()
-
-        try:
-            if self._headers[column] == "name":
-                self._undo.push(
-                    SetNameCommand(
-                        self._data, row, value
-                    )
-                )
-            elif self._headers[column] == "type":
-                old_type = self._data.basic_structure(row).type
-
-                if old_type == "ND" or self._question_set_type():
-                    key = next(
-                        k for k, v in self._long_types.items()
-                        if v == value
-                    )
-
-                    self._undo.push(
-                        SetTypeCommand(
-                            self._data, row, BHS_types[key]
-                        )
-                    )
-        except Exception as e:
-            logger.error(e)
-            logger.debug(traceback.format_exc())
-
-        self.dataChanged.emit(index, index)
-        return True
-
-    def _question_set_type(self):
-        question = QMessageBox(self._parent)
-
-        question.setWindowTitle(self._trad['msg_type_change_title'])
-        question.setText(self._trad['msg_type_change_text'])
-        question.setStandardButtons(QMessageBox.Cancel | QMessageBox.Ok)
-        question.setIcon(QMessageBox.Question)
-
-        res = question.exec()
-        return res == QMessageBox.Ok
-
-    def add(self, row, parent=QModelIndex()):
-        self.beginInsertRows(parent, row, row - 1)
-
-        self._undo.push(
-            AddCommand(
-                self._data, row
-            )
-        )
-
-        self.endInsertRows()
-        self.layoutChanged.emit()
-
-    def delete(self, rows, parent=QModelIndex()):
-        self.beginRemoveRows(parent, rows[0], rows[-1])
-
-        self._undo.push(
-            DelCommand(
-                self._data, rows
-            )
-        )
-
-        self.endRemoveRows()
-        self.layoutChanged.emit()
-
-    def enabled(self, row, enabled, parent=QModelIndex()):
-        self._undo.push(
-            SetEnabledCommand(
-                self._lst, row, enabled
-            )
-        )
-        self.layoutChanged.emit()
-
-    def undo(self):
-        self._undo.undo()
-        self.layoutChanged.emit()
-
-    def redo(self):
-        self._undo.redo()
-        self.layoutChanged.emit()
-
-
-class ParametersTableModel(PamhyrTableModel):
-    def __init__(self, trad=None, **kwargs):
-        self._trad = trad
-        self._long_types = {}
-
-        if self._trad is not None:
-            self._long_types = self._trad.get_dict("long_types")
-
-        self._hs_index = None
-
-        super(ParametersTableModel, self).__init__(trad=trad, **kwargs)
-
-    def rowCount(self, parent):
-        if self._hs_index is None:
-            return 0
-
-        return len(
-            self._data.basic_structure(self._hs_index)
-        )
-
-    def data(self, index, role):
-        if role != Qt.ItemDataRole.DisplayRole:
-            return QVariant()
-
-        if self._hs_index is None:
-            return QVariant()
-
-        row = index.row()
-        column = index.column()
-
-        hs = self._data.basic_structure(self._hs_index)
-
-        if self._headers[column] == "name":
-            return self._trad[hs.parameters[row].name]
-        elif self._headers[column] == "value":
-            return str(hs.parameters[row].value)
-
-        return QVariant()
-
-    def setData(self, index, value, role=Qt.EditRole):
-        if not index.isValid() or role != Qt.EditRole:
-            return False
-
-        if self._hs_index is None:
-            return QVariant()
-
-        row = index.row()
-        column = index.column()
-
-        try:
-            if self._headers[column] == "value":
-                self._undo.push(
-                    SetValueCommand(
-                        self._data.basic_structure(self._hs_index),
-                        row, value
-                    )
-                )
-        except Exception as e:
-            logger.error(e)
-            logger.debug(traceback.format_exc())
-
-        self.dataChanged.emit(index, index)
-        return True
-
-    def update_hs_index(self, index):
-        self._hs_index = index
-        self.layoutChanged.emit()
diff --git a/src/View/Pollutants/BasicHydraulicStructures/Translate.py b/src/View/Pollutants/BasicHydraulicStructures/Translate.py
deleted file mode 100644
index f55fb2d778e212588520e94dafd133c101c38413..0000000000000000000000000000000000000000
--- a/src/View/Pollutants/BasicHydraulicStructures/Translate.py
+++ /dev/null
@@ -1,155 +0,0 @@
-# translate.py -- Pamhyr
-# Copyright (C) 2023-2024  INRAE
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program.  If not, see <https://www.gnu.org/licenses/>.
-
-# -*- coding: utf-8 -*-
-
-from PyQt5.QtCore import QCoreApplication
-
-from View.Translate import MainTranslate
-
-_translate = QCoreApplication.translate
-
-
-class BasicHydraulicStructuresTranslate(MainTranslate):
-    def __init__(self):
-        super(BasicHydraulicStructuresTranslate, self).__init__()
-
-        self._dict["Basic Hydraulic Structures"] = _translate(
-            "BasicHydraulicStructures", "Basic Hydraulic Structures"
-        )
-
-        self._dict['msg_type_change_title'] = _translate(
-            "BasicHydraulicStructures",
-            "Change hydraulic structure type"
-        )
-
-        self._dict['msg_type_change_text'] = _translate(
-            "BasicHydraulicStructures",
-            "Do you want to change the hydraulic structure type and reset \
-hydraulic structure values?"
-        )
-
-        # BHSValues translation
-
-        self._dict['width'] = self._dict["unit_width"]
-        self._dict['height'] = self._dict["unit_thickness"]
-        self._dict['elevation'] = self._dict["unit_elevation"]
-        self._dict['diameter'] = self._dict["unit_diameter"]
-        self._dict['discharge_coefficient'] = _translate(
-            "BasicHydraulicStructures", "Discharge coefficient"
-        )
-        self._dict['loading_elevation'] = _translate(
-            "BasicHydraulicStructures", "Upper elevation (m)"
-        )
-        self._dict['half-angle_tangent'] = _translate(
-            "BasicHydraulicStructures", "Half-angle tangent"
-        )
-        self._dict['maximal_loading_elevation'] = _translate(
-            "BasicHydraulicStructures", "Maximal loading elevation"
-        )
-        self._dict['siltation_height'] = _translate(
-            "BasicHydraulicStructures", "Siltation height (m)"
-        )
-        self._dict['top_of_the_vault'] = _translate(
-            "BasicHydraulicStructures", "Top of the vault (m)"
-        )
-        self._dict['bottom_of_the_vault'] = _translate(
-            "BasicHydraulicStructures", "Bottom of the vault (m)"
-        )
-        self._dict['opening'] = _translate(
-            "BasicHydraulicStructures", "Opening"
-        )
-        self._dict['maximal_opening'] = _translate(
-            "BasicHydraulicStructures", "Maximal opening"
-        )
-        self._dict['step_space'] = _translate(
-            "BasicHydraulicStructures", "Step space"
-        )
-        self._dict['weir'] = _translate(
-            "BasicHydraulicStructures", "Weir"
-        )
-        self._dict['coefficient'] = _translate(
-            "BasicHydraulicStructures", "Coefficient"
-        )
-
-        # Dummy parameters
-
-        self._dict['parameter_1'] = _translate(
-            "BasicHydraulicStructures", "Parameter 1"
-        )
-        self._dict['parameter_2'] = _translate(
-            "BasicHydraulicStructures", "Parameter 2"
-        )
-        self._dict['parameter_3'] = _translate(
-            "BasicHydraulicStructures", "Parameter 3"
-        )
-        self._dict['parameter_4'] = _translate(
-            "BasicHydraulicStructures", "Parameter 4"
-        )
-        self._dict['parameter_5'] = _translate(
-            "BasicHydraulicStructures", "Parameter 5"
-        )
-
-        # BHS types long names
-
-        self._sub_dict["long_types"] = {
-            "ND": self._dict["not_defined"],
-            "S1": _translate(
-                "BasicHydraulicStructures", "Discharge weir"
-            ),
-            "S2": _translate(
-                "BasicHydraulicStructures", "Trapezoidal weir"
-            ),
-            "S3": _translate(
-                "BasicHydraulicStructures", "Triangular weir"
-            ),
-            "OR": _translate(
-                "BasicHydraulicStructures", "Rectangular orifice"
-            ),
-            "OC": _translate(
-                "BasicHydraulicStructures", "Circular orifice"
-            ),
-            "OV": _translate(
-                "BasicHydraulicStructures", "Vaulted orifice"
-            ),
-            "V1": _translate(
-                "BasicHydraulicStructures", "Rectangular gate"
-            ),
-            "V2": _translate(
-                "BasicHydraulicStructures", "Simplified rectangular gate"
-            ),
-            "BO": _translate(
-                "BasicHydraulicStructures", "Borda-type head loss"
-            ),
-            "CV": _translate(
-                "BasicHydraulicStructures", "Check valve"
-            ),
-            "UD": _translate(
-                "BasicHydraulicStructures", "User defined"
-            ),
-        }
-
-        # Tables
-
-        self._sub_dict["table_headers"] = {
-            "name": self._dict["name"],
-            "type": self._dict["type"],
-        }
-
-        self._sub_dict["table_headers_parameters"] = {
-            "name": self._dict["name"],
-            "value": self._dict["value"],
-        }
diff --git a/src/View/Pollutants/BasicHydraulicStructures/UndoCommand.py b/src/View/Pollutants/BasicHydraulicStructures/UndoCommand.py
deleted file mode 100644
index 78fa3d62ea3f06c45c7d11d4c79b8b7c8b81e6d8..0000000000000000000000000000000000000000
--- a/src/View/Pollutants/BasicHydraulicStructures/UndoCommand.py
+++ /dev/null
@@ -1,153 +0,0 @@
-# UndoCommand.py -- Pamhyr
-# Copyright (C) 2023-2024  INRAE
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program.  If not, see <https://www.gnu.org/licenses/>.
-
-# -*- coding: utf-8 -*-
-
-from copy import deepcopy
-from tools import trace, timer
-
-from PyQt5.QtWidgets import (
-    QMessageBox, QUndoCommand, QUndoStack,
-)
-
-
-class SetNameCommand(QUndoCommand):
-    def __init__(self, hs, index, new_value):
-        QUndoCommand.__init__(self)
-
-        self._hs = hs
-        self._index = index
-        self._old = self._hs.basic_structure(self._index).name
-        self._new = str(new_value)
-
-    def undo(self):
-        self._hs.basic_structure(self._index).name = self._old
-
-    def redo(self):
-        self._hs.basic_structure(self._index).name = self._new
-
-
-class SetTypeCommand(QUndoCommand):
-    def __init__(self, hs, index, new_type):
-        QUndoCommand.__init__(self)
-
-        self._hs = hs
-        self._index = index
-        self._type = new_type
-        self._old = self._hs.basic_structure(self._index)
-        self._new = self._hs.basic_structure(self._index)\
-            .convert(self._type)
-
-    def undo(self):
-        self._hs.delete_i([self._index])
-        self._hs.insert(self._index, self._old)
-
-    def redo(self):
-        self._hs.delete_i([self._index])
-        self._hs.insert(self._index, self._new)
-
-
-class SetEnabledCommand(QUndoCommand):
-    def __init__(self, hs, index, enabled):
-        QUndoCommand.__init__(self)
-
-        self._hs = hs
-        self._index = index
-        self._old = not enabled
-        self._new = enabled
-
-    def undo(self):
-        self._hs.basic_structure(self._index).enabled = self._old
-
-    def redo(self):
-        self._hs.basic_structure(self._index).enabled = self._new
-
-
-class AddCommand(QUndoCommand):
-    def __init__(self, hs, index):
-        QUndoCommand.__init__(self)
-
-        self._hs = hs
-
-        self._index = index
-        self._new = None
-
-    def undo(self):
-        self._hs.delete_i([self._index])
-
-    def redo(self):
-        if self._new is None:
-            self._new = self._hs.add(self._index)
-        else:
-            self._hs.insert(self._index, self._new)
-
-
-class DelCommand(QUndoCommand):
-    def __init__(self, hs, rows):
-        QUndoCommand.__init__(self)
-
-        self._hs = hs
-
-        self._rows = rows
-
-        self._bhs = []
-        for row in rows:
-            self._bhs.append((row, self._hs.basic_structure(row)))
-
-    def undo(self):
-        for row, el in self._bhs:
-            self._hs.insert(row, el)
-
-    def redo(self):
-        self._hs.delete_i(self._rows)
-
-
-class PasteCommand(QUndoCommand):
-    def __init__(self, hs, row, h_s):
-        QUndoCommand.__init__(self)
-
-        self._hs = hs
-
-        self._row = row
-        self._bhs = deepcopy(h_s)
-        self._bhs.reverse()
-
-    def undo(self):
-        self._hs.delete_i(range(self._row, self._row + len(self._bhs)))
-
-    def redo(self):
-        for r in self._bhs:
-            self._hs.insert(self._row, r)
-
-####################################
-# Basic hydraulic structure values #
-####################################
-
-
-class SetValueCommand(QUndoCommand):
-    def __init__(self, bhs, index, value):
-        QUndoCommand.__init__(self)
-
-        self._bhs = bhs
-        self._index = index
-        self._old = self._bhs.parameters[self._index].value
-        self._new = self._bhs.parameters[self._index].type(value)
-
-    def undo(self):
-        self._bhs.parameters[self._index].value = self._old
-
-    def redo(self):
-        self._bhs.parameters[self._index].value = self._new
diff --git a/src/View/Pollutants/BasicHydraulicStructures/Window.py b/src/View/Pollutants/BasicHydraulicStructures/Window.py
deleted file mode 100644
index 9a39f49c64749b995b3ac3733038ee0e720003b5..0000000000000000000000000000000000000000
--- a/src/View/Pollutants/BasicHydraulicStructures/Window.py
+++ /dev/null
@@ -1,270 +0,0 @@
-# Window.py -- Pamhyr
-# Copyright (C) 2023-2024  INRAE
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program.  If not, see <https://www.gnu.org/licenses/>.
-
-# -*- coding: utf-8 -*-
-
-import logging
-
-from tools import timer, trace
-
-from View.Tools.PamhyrWindow import PamhyrWindow
-
-from PyQt5 import QtCore
-from PyQt5.QtCore import (
-    Qt, QVariant, QAbstractTableModel, QCoreApplication,
-    pyqtSlot, pyqtSignal, QItemSelectionModel,
-)
-
-from PyQt5.QtWidgets import (
-    QDialogButtonBox, QPushButton, QLineEdit,
-    QFileDialog, QTableView, QAbstractItemView,
-    QUndoStack, QShortcut, QAction, QItemDelegate,
-    QHeaderView, QDoubleSpinBox, QVBoxLayout, QCheckBox
-)
-
-from View.Tools.Plot.PamhyrCanvas import MplCanvas
-from View.Tools.Plot.PamhyrToolbar import PamhyrPlotToolbar
-
-from View.HydraulicStructures.PlotAC import PlotAC
-
-from View.HydraulicStructures.BasicHydraulicStructures.Table import (
-    ComboBoxDelegate, TableModel, ParametersTableModel,
-)
-
-from View.Network.GraphWidget import GraphWidget
-from View.HydraulicStructures.BasicHydraulicStructures.Translate import (
-    BasicHydraulicStructuresTranslate
-)
-
-_translate = QCoreApplication.translate
-
-logger = logging.getLogger()
-
-
-class BasicHydraulicStructuresWindow(PamhyrWindow):
-    _pamhyr_ui = "BasicHydraulicStructures"
-    _pamhyr_name = "Basic Hydraulic Structures"
-
-    def __init__(self, data=None, study=None, config=None, parent=None):
-        trad = BasicHydraulicStructuresTranslate()
-        name = " - ".join([
-            trad[self._pamhyr_name], data.name, study.name
-        ])
-
-        super(BasicHydraulicStructuresWindow, self).__init__(
-            title=name,
-            study=study,
-            config=config,
-            trad=trad,
-            parent=parent
-        )
-
-        self._hash_data.append(data)
-
-        self._hs = data
-
-        self.setup_table()
-        self.setup_checkbox()
-        self.setup_plot()
-        self.setup_connections()
-
-        self.update()
-
-    def setup_table(self):
-        self.setup_table_bhs()
-        self.setup_table_bhs_parameters()
-
-    def setup_table_bhs(self):
-        self._table = None
-
-        self._delegate_type = ComboBoxDelegate(
-            trad=self._trad,
-            parent=self
-        )
-
-        table = self.find(QTableView, f"tableView")
-        self._table = TableModel(
-            table_view=table,
-            table_headers=self._trad.get_dict("table_headers"),
-            editable_headers=["name", "type"],
-            delegates={
-                "type": self._delegate_type,
-            },
-            trad=self._trad,
-            data=self._hs,
-            undo=self._undo_stack,
-            parent=self,
-        )
-
-        selectionModel = table.selectionModel()
-        index = table.model().index(0, 0)
-
-        selectionModel.select(
-            index,
-            QItemSelectionModel.Rows |
-            QItemSelectionModel.ClearAndSelect |
-            QItemSelectionModel.Select
-        )
-        table.scrollTo(index)
-
-    def setup_table_bhs_parameters(self):
-        self._table_parameters = None
-
-        table = self.find(QTableView, f"tableView_2")
-        self._table_parameters = ParametersTableModel(
-            table_view=table,
-            table_headers=self._trad.get_dict("table_headers_parameters"),
-            editable_headers=["value"],
-            delegates={},
-            trad=self._trad,
-            data=self._hs,
-            undo=self._undo_stack,
-            parent=self,
-        )
-
-    def setup_checkbox(self):
-        self._checkbox = self.find(QCheckBox, f"checkBox")
-        self._set_checkbox_state()
-
-    def setup_plot(self):
-        self.canvas = MplCanvas(width=5, height=4, dpi=100)
-        self.canvas.setObjectName("canvas")
-        self.toolbar = PamhyrPlotToolbar(
-            self.canvas, self
-        )
-        self.plot_layout = self.find(QVBoxLayout, "verticalLayout")
-        self.plot_layout.addWidget(self.toolbar)
-        self.plot_layout.addWidget(self.canvas)
-
-        reach = self._hs.input_reach
-        profile_kp = self._hs.input_kp
-        if profile_kp is not None:
-            profiles = reach.reach.get_profiles_from_kp(float(profile_kp))
-        else:
-            profiles = None
-        if profiles is not None:
-            profile = profiles[0]
-        else:
-            profile = None
-
-        self.plot_ac = PlotAC(
-            canvas=self.canvas,
-            river=self._study.river,
-            reach=self._hs.input_reach,
-            profile=profile,
-            trad=self._trad,
-            toolbar=self.toolbar
-        )
-        self.plot_ac.draw()
-
-    def setup_connections(self):
-        self.find(QAction, "action_add").triggered.connect(self.add)
-        self.find(QAction, "action_delete").triggered.connect(self.delete)
-        self._checkbox.clicked.connect(self._set_basic_structure_state)
-
-        table = self.find(QTableView, "tableView")
-        table.selectionModel()\
-             .selectionChanged\
-             .connect(self.update)
-
-        self._table.dataChanged.connect(self.update)
-        self._table.layoutChanged.connect(self.update)
-
-    def index_selected(self):
-        table = self.find(QTableView, "tableView")
-        r = table.selectionModel().selectedRows()
-
-        if len(r) > 0:
-            return r[0]
-        else:
-            return None
-
-    def index_selected_row(self):
-        table = self.find(QTableView, "tableView")
-        r = table.selectionModel().selectedRows()
-
-        if len(r) > 0:
-            return r[0].row()
-        else:
-            return None
-
-    def index_selected_rows(self):
-        table = self.find(QTableView, "tableView")
-        return list(
-            # Delete duplicate
-            set(
-                map(
-                    lambda i: i.row(),
-                    table.selectedIndexes()
-                )
-            )
-        )
-
-    def add(self):
-        rows = self.index_selected_rows()
-
-        if len(self._hs) == 0 or len(rows) == 0:
-            self._table.add(0)
-        else:
-            self._table.add(rows[0])
-
-    def delete(self):
-        rows = self.index_selected_rows()
-
-        if len(rows) == 0:
-            return
-
-        self._table.delete(rows)
-
-    def _copy(self):
-        logger.info("TODO: copy")
-
-    def _paste(self):
-        logger.info("TODO: paste")
-
-    def _undo(self):
-        self._table.undo()
-
-    def _redo(self):
-        self._table.redo()
-
-    def _set_checkbox_state(self):
-        row = self.index_selected_row()
-
-        if row is None:
-            self._checkbox.setEnabled(False)
-            self._checkbox.setChecked(True)
-        else:
-            self._checkbox.setEnabled(True)
-            self._checkbox.setChecked(self._hs.basic_structure(row).enabled)
-
-    def _set_basic_structure_state(self):
-        rows = self.index_selected_rows()
-        if len(rows) != 0:
-            for row in rows:
-                if row is not None:
-                    self._table.enabled(
-                        row,
-                        self._checkbox.isChecked()
-                    )
-
-    def update(self):
-        self._set_checkbox_state()
-        self._update_parameters_table()
-
-    def _update_parameters_table(self):
-        row = self.index_selected_row()
-        self._table_parameters.update_hs_index(row)
diff --git a/src/View/Pollutants/Edit/Table.py b/src/View/Pollutants/Edit/Table.py
new file mode 100644
index 0000000000000000000000000000000000000000..aa3c919b0f0cd997b562efbfd8edec37d0319aa6
--- /dev/null
+++ b/src/View/Pollutants/Edit/Table.py
@@ -0,0 +1,106 @@
+# Table.py -- Pamhyr
+# Copyright (C) 2023-2024  INRAE
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+# -*- coding: utf-8 -*-
+
+import logging
+import traceback
+
+from datetime import date, time, datetime, timedelta
+
+from tools import trace, timer
+
+from View.Tools.PamhyrTable import PamhyrTableModel
+
+from PyQt5.QtCore import (
+    Qt, QVariant, QAbstractTableModel,
+    QCoreApplication, QModelIndex, pyqtSlot,
+    QRect, QTime, QDateTime,
+)
+
+from PyQt5.QtWidgets import (
+    QTableView, QAbstractItemView, QSpinBox, QItemDelegate,
+)
+
+from View.Pollutants.Edit.UndoCommand import (
+    SetDataCommand, PasteCommand,
+)
+
+_translate = QCoreApplication.translate
+
+logger = logging.getLogger()
+
+
+class TableModel(PamhyrTableModel):
+    def data(self, index, role):
+        if role == Qt.TextAlignmentRole:
+            return Qt.AlignHCenter | Qt.AlignVCenter
+
+        if role != Qt.ItemDataRole.DisplayRole:
+            return QVariant()
+
+        row = index.row()
+        column = index.column()
+
+        return self._data.data[row][column]
+
+    def setData(self, index, value, role=Qt.EditRole):
+        if not index.isValid() or role != Qt.EditRole:
+            return False
+
+        row = index.row()
+        column = index.column()
+
+        try:
+            if self._headers[column] == "type":
+                self._undo.push(
+                    SetDataCommand(
+                        self._data, row, column, int(value)
+                    )
+                )
+            else:
+                self._undo.push(
+                    SetDataCommand(
+                        self._data, row, column, float(value)
+                    )
+                )
+        except Exception as e:
+            logger.info(e)
+            logger.debug(traceback.format_exc())
+
+        self.dataChanged.emit(index, index)
+        return True
+
+    def paste(self, row, data):
+        if len(data) == 0:
+            return
+
+        self.layoutAboutToBeChanged.emit()
+
+        self._undo.push(
+            PasteCommand(
+                self._data, row,
+                list(
+                    map(
+                        lambda d: self._data.new_from_data(d),
+                        data
+                    )
+                )
+            )
+        )
+
+        self.layoutAboutToBeChanged.emit()
+        self.layoutChanged.emit()
diff --git a/src/View/Pollutants/Edit/Translate.py b/src/View/Pollutants/Edit/Translate.py
new file mode 100644
index 0000000000000000000000000000000000000000..acdcab31f3948eb3ee6fd7b847a235cfe0994482
--- /dev/null
+++ b/src/View/Pollutants/Edit/Translate.py
@@ -0,0 +1,46 @@
+# translate.py -- Pamhyr
+# Copyright (C) 2023-2024  INRAE
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+# -*- coding: utf-8 -*-
+
+from PyQt5.QtCore import QCoreApplication
+
+from View.Translate import MainTranslate
+
+from View.Pollutants.Translate import PollutantsTranslate
+
+_translate = QCoreApplication.translate
+
+
+class EditPollutantTranslate(PollutantsTranslate):
+    def __init__(self):
+        super(EditPollutantTranslate, self).__init__()
+
+        self._dict["Edit Pollutant"] = _translate(
+            "Pollutants", "Edit Pollutant"
+        )
+
+        self._sub_dict["table_headers"] = {
+            "type": self._dict["type"],
+            "diametre": self._dict["unit_area"],
+            "rho": self._dict["unit_rho"],
+            "porosity": self._dict["unit_porosity"],
+            "cdc_riv": self._dict["unit_cdc_riv"],
+            "cdc_cas": self._dict["unit_cdc_cas"],
+            "apd": self._dict["unit_apd"],
+            "ac": self._dict["unit_ac"],
+            "bc": self._dict["unit_bc"],
+        }
diff --git a/src/View/Pollutants/Edit/UndoCommand.py b/src/View/Pollutants/Edit/UndoCommand.py
new file mode 100644
index 0000000000000000000000000000000000000000..4a30340adfce2b5d88a5855d52c48ae5f5ae7e69
--- /dev/null
+++ b/src/View/Pollutants/Edit/UndoCommand.py
@@ -0,0 +1,65 @@
+# UndoCommand.py -- Pamhyr
+# Copyright (C) 2023-2024  INRAE
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+# -*- coding: utf-8 -*-
+
+import logging
+
+from copy import deepcopy
+from tools import trace, timer
+
+from PyQt5.QtWidgets import (
+    QMessageBox, QUndoCommand, QUndoStack,
+)
+
+from Model.Pollutants.Pollutants import Pollutants
+
+logger = logging.getLogger()
+
+
+class SetDataCommand(QUndoCommand):
+    def __init__(self, data, index, column, new_value):
+        QUndoCommand.__init__(self)
+
+        self._data = data
+        self._index = index
+        self._column = column
+        self._old = self._data.data[self._index][self._column]
+        self._new = new_value
+
+    def undo(self):
+        self._data.data[self._index][self._column] = self._old
+
+    def redo(self):
+        self._data.data[self._index][self._column] = self._new
+
+class PasteCommand(QUndoCommand):
+    def __init__(self, data, row, hs):
+        QUndoCommand.__init__(self)
+
+        self._data = data
+        self._row = row
+        self._h = hs
+        self._h.reverse()
+
+    def undo(self):
+        self._data.delete_i(
+            range(self._row, self._row + len(self._h))
+        )
+
+    def redo(self):
+        for h in self._h:
+            self._data.insert(self._row, h)
diff --git a/src/View/Pollutants/Edit/Window.py b/src/View/Pollutants/Edit/Window.py
new file mode 100644
index 0000000000000000000000000000000000000000..735d1b20ade9bc42b1d78a7f2f0df5178a919677
--- /dev/null
+++ b/src/View/Pollutants/Edit/Window.py
@@ -0,0 +1,149 @@
+# Window.py -- Pamhyr
+# Copyright (C) 2023-2024  INRAE
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+# -*- coding: utf-8 -*-
+
+import logging
+
+from tools import timer, trace
+
+from View.Tools.PamhyrWindow import PamhyrWindow
+from View.Tools.PamhyrWidget import PamhyrWidget
+
+from PyQt5.QtGui import (
+    QKeySequence,
+)
+
+from PyQt5 import QtCore
+from PyQt5.QtCore import (
+    Qt, QVariant, QAbstractTableModel, QCoreApplication,
+    pyqtSlot, pyqtSignal,
+)
+
+from PyQt5.QtWidgets import (
+    QDialogButtonBox, QPushButton, QLineEdit,
+    QFileDialog, QTableView, QAbstractItemView,
+    QUndoStack, QShortcut, QAction, QItemDelegate,
+    QHeaderView, QDoubleSpinBox, QVBoxLayout,
+)
+
+from View.Pollutants.Edit.Translate import EditPollutantTranslate
+from View.Pollutants.Edit.Table import TableModel
+
+_translate = QCoreApplication.translate
+
+logger = logging.getLogger()
+
+
+class EditPolluantWindow(PamhyrWindow):
+    _pamhyr_ui = "Pollutant"
+    _pamhyr_name = "Edit Pollutant"
+
+    def __init__(self, data=None, study=None, config=None, parent=None):
+        self._data = data
+        trad = EditPollutantTranslate()
+
+        name = trad[self._pamhyr_name]
+        print(name)
+        if self._data is not None:
+            name += (
+                f" - {study.name} " +
+                f" - {self._data.name}"
+            )
+
+        super(EditPolluantWindow, self).__init__(
+            title=name,
+            study=study,
+            config=config,
+            trad=trad,
+            parent=parent
+        )
+
+        self._hash_data.append(data)
+
+        self.setup_table()
+
+    def setup_table(self):
+        headers = {}
+        table_headers = self._trad.get_dict("table_headers")
+
+        table = self.find(QTableView, "tableView")
+        self._table = TableModel(
+            table_view=table,
+            table_headers=table_headers,
+            editable_headers=table_headers,
+            delegates={},
+            data=self._data,
+            undo=self._undo_stack,
+            opt_data=self._study.time_system
+        )
+
+        table.setModel(self._table)
+        table.setSelectionBehavior(QAbstractItemView.SelectRows)
+        table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
+        table.setAlternatingRowColors(True)
+
+    def index_selected_row(self):
+        table = self.find(QTableView, "tableView")
+        return table.selectionModel()\
+                    .selectedRows()[0]\
+                    .row()
+
+    def index_selected_rows(self):
+        table = self.find(QTableView, "tableView")
+        return list(
+            # Delete duplicate
+            set(
+                map(
+                    lambda i: i.row(),
+                    table.selectedIndexes()
+                )
+            )
+        )
+
+    def _copy(self):
+        rows = self.index_selected_rows()
+
+        table = []
+        # table.append(self._data.header)
+        table.append(self._trad.get_dict("table_headers"))
+
+        data = self._data.data
+        for row in rows:
+            table.append(list(data[row]))
+
+        self.copyTableIntoClipboard(table)
+
+    def _paste(self):
+        header, data = self.parseClipboardTable()
+
+        logger.debug(f"paste: h:{header}, d:{data}")
+
+        if len(data) == 0:
+            return
+
+        row = 0
+        rows = self.index_selected_rows()
+        if len(rows) != 0:
+            row = rows[0]
+
+        self._table.paste(row, data)
+
+    def _undo(self):
+        self._table.undo()
+
+    def _redo(self):
+        self._table.redo()
diff --git a/src/View/Pollutants/PlotAC.py b/src/View/Pollutants/PlotAC.py
deleted file mode 100644
index c63dc9bfdef5fb2e311b54eb90cf85e4937a4b1c..0000000000000000000000000000000000000000
--- a/src/View/Pollutants/PlotAC.py
+++ /dev/null
@@ -1,120 +0,0 @@
-# PlotAC.py -- Pamhyr
-# Copyright (C) 2023-2024  INRAE
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program.  If not, see <https://www.gnu.org/licenses/>.
-
-# -*- coding: utf-8 -*-
-
-from tools import timer
-from View.Tools.PamhyrPlot import PamhyrPlot
-from matplotlib import pyplot as plt
-
-
-class PlotAC(PamhyrPlot):
-    def __init__(self, canvas=None, trad=None, toolbar=None,
-                 river=None, reach=None, profile=None,
-                 parent=None):
-        super(PlotAC, self).__init__(
-            canvas=canvas,
-            trad=trad,
-            data=river,
-            toolbar=toolbar,
-            parent=parent
-        )
-
-        self._current_reach = reach
-        self._current_profile = profile
-
-        self.label_x = self._trad["x"]
-        self.label_y = self._trad["unit_elevation"]
-
-        self._isometric_axis = False
-
-        self._auto_relim_update = True
-        self._autoscale_update = True
-
-    @property
-    def river(self):
-        return self.data
-
-    @river.setter
-    def river(self, river):
-        self.data = river
-
-    @timer
-    def draw(self):
-        self.init_axes()
-
-        if self.data is None:
-            self.line_kp = None
-            return
-
-        if self._current_reach is None:
-            self.line_kp = None
-            return
-
-        self.draw_data()
-
-        self.idle()
-        self._init = True
-
-    def draw_data(self):
-        reach = self._current_reach
-
-        if self._current_profile is None:
-            self.line_kp = None
-        else:
-            profile = self._current_profile
-            x = profile.get_station()
-            z = profile.z()
-
-            self.line_kp, = self.canvas.axes.plot(
-                x, z,
-                color=self.color_plot_river_bottom,
-                **self.plot_default_kargs
-            )
-
-    def set_reach(self, reach):
-        self._current_reach = reach
-        self.update()
-
-    def set_profile(self, profile):
-        self._current_profile = profile
-        self.update()
-
-    def update(self):
-        if self.line_kp is None:
-            self.draw()
-            return
-
-        if self._current_reach is None or self._current_profile is None:
-            self.update_clear()
-        else:
-            self.update_data()
-
-        self.update_idle()
-
-    def update_data(self):
-        profile = self._current_profile
-        x = profile.get_station()
-        z = profile.z()
-
-        self.line_kp.set_data(x, z)
-
-    def clear(self):
-        self.update_clear()
-
-    def update_clear(self):
-        if self.line_kp is not None:
-            self.line_kp.set_data([], [])
diff --git a/src/View/Pollutants/PlotKPC.py b/src/View/Pollutants/PlotKPC.py
deleted file mode 100644
index 3327f6e627114ac8513dda1ca57670b0cdf722e8..0000000000000000000000000000000000000000
--- a/src/View/Pollutants/PlotKPC.py
+++ /dev/null
@@ -1,165 +0,0 @@
-# PlotKPC.py -- Pamhyr
-# Copyright (C) 2023-2024  INRAE
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program.  If not, see <https://www.gnu.org/licenses/>.
-
-# -*- coding: utf-8 -*-
-
-from tools import timer
-from View.Tools.PamhyrPlot import PamhyrPlot
-
-from PyQt5.QtCore import (
-    QCoreApplication
-)
-
-from matplotlib.collections import LineCollection
-
-_translate = QCoreApplication.translate
-
-
-class PlotKPC(PamhyrPlot):
-    def __init__(self, canvas=None, trad=None, toolbar=None,
-                 river=None, reach=None, profile=None,
-                 parent=None):
-        super(PlotKPC, self).__init__(
-            canvas=canvas,
-            trad=trad,
-            data=river,
-            toolbar=toolbar,
-            parent=parent
-        )
-
-        self._current_reach = reach
-        self._current_profile = profile
-
-        self.label_x = self._trad["unit_kp"]
-        self.label_y = self._trad["unit_elevation"]
-
-        self._isometric_axis = False
-
-        self._auto_relim_update = True
-        self._autoscale_update = True
-
-    @property
-    def river(self):
-        return self.data
-
-    @river.setter
-    def river(self, river):
-        self.data = river
-
-    @timer
-    def draw(self, highlight=None):
-        self.init_axes()
-
-        if self.data is None:
-            self.profile = None
-            self.line_kp_zmin_zmax = None
-            self.line_kp_zmin = None
-            return
-
-        if self._current_reach is None:
-            self.profile = None
-            self.line_kp_zmin_zmax = None
-            self.line_kp_zmin = None
-            return
-
-        self.draw_data()
-        self.draw_current()
-
-        self.idle()
-        self._init = True
-
-    def draw_data(self):
-        reach = self._current_reach
-
-        kp = reach.reach.get_kp()
-        z_min = reach.reach.get_z_min()
-        z_max = reach.reach.get_z_max()
-
-        self.line_kp_zmin, = self.canvas.axes.plot(
-            kp, z_min,
-            color=self.color_plot_river_bottom,
-            lw=1.
-        )
-
-        if len(kp) != 0:
-            self.line_kp_zmin_zmax = self.canvas.axes.vlines(
-                x=kp,
-                ymin=z_min, ymax=z_max,
-                color=self.color_plot,
-                lw=1.
-            )
-
-    def draw_current(self):
-        if self._current_profile is None:
-            self.profile = None
-        else:
-            kp = [self._current_profile.kp,
-                  self._current_profile.kp]
-            min_max = [self._current_profile.z_min(),
-                       self._current_profile.z_max()]
-
-            self.profile = self.canvas.axes.plot(
-                kp, min_max,
-                color=self.color_plot_current,
-                lw=1.
-            )
-
-    def set_reach(self, reach):
-        self._current_reach = reach
-        self._current_profile = None
-        self.update()
-
-    def set_profile(self, profile):
-        self._current_profile = profile
-        self.update_current_profile()
-
-    def update(self):
-        self.draw()
-
-    def update_current_profile(self):
-        reach = self._current_reach
-        kp = reach.reach.get_kp()
-        z_min = reach.reach.get_z_min()
-        z_max = reach.reach.get_z_max()
-
-        if self.profile is None:
-            self.draw()
-        else:
-            self.profile.set_data(
-                [self._current_profile.kp, self._current_profile.kp],
-                [self._current_profile.z_min(), self._current_profile.z_max()],
-            )
-
-            self.update_idle()
-
-    def clear(self):
-        if self.profile is not None:
-            self.profile[0].set_data([], [])
-
-        if self.line_kp_zmin_zmax is not None:
-            self.line_kp_zmin_zmax.remove()
-            self.line_kp_zmin_zmax = None
-
-        if self.line_kp_zmin is not None:
-            self.line_kp_zmin.set_data([], [])
-
-        self.canvas.figure.canvas.draw_idle()
-
-    def clear_profile(self):
-        if self.profile is not None:
-            self.profile.set_data([], [])
-
-        self.canvas.figure.canvas.draw_idle()
diff --git a/src/View/Pollutants/UndoCommand.py b/src/View/Pollutants/UndoCommand.py
index 213bf0207bec8dc1d142f85c949fa9f404425be8..843db085c3217c2b9d569d2ce8108701cee085d9 100644
--- a/src/View/Pollutants/UndoCommand.py
+++ b/src/View/Pollutants/UndoCommand.py
@@ -74,6 +74,7 @@ class AddCommand(QUndoCommand):
     def redo(self):
         if self._new is None:
             self._new = self._pollutants_lst.new(self._pollutants_lst, self._index)
+            self._new._data = [[0, 0., 0., 0., 0., 0., 0., 0., 0.]]
         else:
             self._pollutants_lst.insert(self._index, self._new)
 
diff --git a/src/View/Pollutants/Window.py b/src/View/Pollutants/Window.py
index 430061c9e10a2ba9428963e255d9d0ba91c5f0b0..a9dc2059c224fd10c85df7c3adad41ad9b503707 100644
--- a/src/View/Pollutants/Window.py
+++ b/src/View/Pollutants/Window.py
@@ -35,22 +35,13 @@ from PyQt5.QtWidgets import (
     QHeaderView, QDoubleSpinBox, QVBoxLayout, QCheckBox
 )
 
-from View.Tools.Plot.PamhyrCanvas import MplCanvas
-from View.Tools.Plot.PamhyrToolbar import PamhyrPlotToolbar
-
-from View.Pollutants.PlotAC import PlotAC
-from View.Pollutants.PlotKPC import PlotKPC
-
 from View.Pollutants.Table import (
     TableModel
 )
 
-from View.Network.GraphWidget import GraphWidget
 from View.Pollutants.Translate import PollutantsTranslate
 
-from View.Pollutants.BasicHydraulicStructures.Window import (
-    BasicHydraulicStructuresWindow
-)
+from View.Pollutants.Edit.Window import EditPolluantWindow
 
 logger = logging.getLogger()
 
@@ -183,12 +174,12 @@ class PollutantsWindow(PamhyrWindow):
             data = self._pollutants_lst.get(row)
 
             if self.sub_window_exists(
-                BasicHydraulicStructuresWindow,
+                EditPolluantWindow,
                 data=[self._study, None, data]
             ):
                 continue
 
-            win = BasicHydraulicStructuresWindow(
+            win = EditPolluantWindow(
                 data=data,
                 study=self._study,
                 parent=self
diff --git a/src/View/Translate.py b/src/View/Translate.py
index 7d22a04269e9e6f0923f5a0c2ba2270495a2a469..bc809481f789f6a954b50bab3e9412064b8094af 100644
--- a/src/View/Translate.py
+++ b/src/View/Translate.py
@@ -72,6 +72,14 @@ class UnitTranslate(CommonWordTranslate):
         self._dict["unit_date_s"] = _translate("Unit", "Date (sec)")
         self._dict["unit_date_iso"] = _translate("Unit", "Date (ISO format)")
 
+        self._dict["unit_area"] = _translate("Unit", "Area")
+        self._dict["unit_rho"] = _translate("Unit", "Rho")
+        self._dict["unit_porosity"] = _translate("Unit", "Porosity")
+        self._dict["unit_cdc_riv"] = _translate("Unit", "CDC_RIV")
+        self._dict["unit_cdc_cas"] = _translate("Unit", "CDC_CAS")
+        self._dict["unit_apd"] = _translate("Unit", "APD")
+        self._dict["unit_ac"] = _translate("Unit", "AC")
+        self._dict["unit_bc"] = _translate("Unit", "BC")
 
 class MainTranslate(UnitTranslate):
     def __init__(self):
diff --git a/src/View/ui/Pollutant.ui b/src/View/ui/Pollutant.ui
new file mode 100644
index 0000000000000000000000000000000000000000..e842bacae69422f03322464664570f854efa5503
--- /dev/null
+++ b/src/View/ui/Pollutant.ui
@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>MainWindow</class>
+ <widget class="QMainWindow" name="MainWindow">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>800</width>
+    <height>600</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>MainWindow</string>
+  </property>
+  <widget class="QWidget" name="centralwidget">
+   <layout class="QGridLayout" name="gridLayout">
+    <item row="0" column="0">
+     <widget class="QSplitter" name="splitter">
+      <property name="orientation">
+       <enum>Qt::Horizontal</enum>
+      </property>
+      <widget class="QTableView" name="tableView">
+       <property name="minimumSize">
+        <size>
+         <width>300</width>
+         <height>0</height>
+        </size>
+       </property>
+       <property name="baseSize">
+        <size>
+         <width>0</width>
+         <height>0</height>
+        </size>
+       </property>
+      </widget>
+      <widget class="QWidget" name="verticalLayoutWidget">
+       <layout class="QVBoxLayout" name="verticalLayout"/>
+      </widget>
+     </widget>
+    </item>
+   </layout>
+  </widget>
+  <widget class="QMenuBar" name="menubar">
+   <property name="geometry">
+    <rect>
+     <x>0</x>
+     <y>0</y>
+     <width>800</width>
+     <height>22</height>
+    </rect>
+   </property>
+  </widget>
+  <widget class="QStatusBar" name="statusbar"/>
+  <widget class="QToolBar" name="toolBar">
+   <property name="windowTitle">
+    <string>toolBar</string>
+   </property>
+   <attribute name="toolBarArea">
+    <enum>TopToolBarArea</enum>
+   </attribute>
+   <attribute name="toolBarBreak">
+    <bool>false</bool>
+   </attribute>
+  </widget>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/src/View/ui/Pollutants.ui b/src/View/ui/Pollutants.ui
index 213d583eecdea8caec6d9605815f40b1b63d06e1..16960d920a52b5b61ee239aab37aed4f6198d81c 100644
--- a/src/View/ui/Pollutants.ui
+++ b/src/View/ui/Pollutants.ui
@@ -122,12 +122,8 @@
    </property>
   </action>
   <action name="action_edit">
-   <property name="icon">
-    <iconset>
-     <normaloff>ressources/edit.png</normaloff>ressources/edit.png</iconset>
-   </property>
    <property name="text">
-    <string>Edit</string>
+    <string>Characteristics</string>
    </property>
    <property name="toolTip">
     <string>Edit selected hydraulic structure</string>
diff --git a/tests_cases/Ardeche/Ardeche.pamhyr b/tests_cases/Ardeche/Ardeche.pamhyr
index d0c55c708be0c428de5ad820953f73592f5b09ba..0951c307eaddec127fdc2ffe676ce37bcf6d6f17 100644
Binary files a/tests_cases/Ardeche/Ardeche.pamhyr and b/tests_cases/Ardeche/Ardeche.pamhyr differ