From 1dd5dc6e25ef8c10a23904578dc8d80d8f62ff86 Mon Sep 17 00:00:00 2001
From: Rainer Kartmann <rainer.kartmann@kit.edu>
Date: Wed, 25 Aug 2021 15:12:05 +0200
Subject: [PATCH] Implement adding vertices and creating new graphs; remove
 robot pose from graph aron

---
 .../GraphImportExport/GraphImportExport.cpp   |   3 +-
 source/armarx/navigation/graph/Graph.cpp      |  34 +-
 source/armarx/navigation/graph/Graph.h        |  11 +
 source/armarx/navigation/graph/aron/Graph.xml |   5 +-
 .../LocationGraphEditor/CMakeLists.txt        |   2 +
 .../LocationGraphEditorWidget.ui              |  28 +-
 .../LocationGraphEditorWidgetController.cpp   | 579 +++++++++++-------
 .../LocationGraphEditorWidgetController.h     | 104 ++--
 .../widgets/EdgeTableWidget.cpp               |  13 +-
 .../widgets/NewEntityIdDialog.cpp             | 129 ++++
 .../widgets/NewEntityIdDialog.h               |  64 ++
 .../widgets/VertexTableWidget.cpp             |  58 +-
 .../widgets/VertexTableWidget.h               |  10 +-
 .../LocationGraphEditor/widgets/utils.cpp     |   1 +
 .../LocationGraphEditor/widgets/utils.h       |  12 +-
 15 files changed, 748 insertions(+), 305 deletions(-)
 create mode 100644 source/armarx/navigation/gui-plugins/LocationGraphEditor/widgets/NewEntityIdDialog.cpp
 create mode 100644 source/armarx/navigation/gui-plugins/LocationGraphEditor/widgets/NewEntityIdDialog.h

diff --git a/source/armarx/navigation/components/GraphImportExport/GraphImportExport.cpp b/source/armarx/navigation/components/GraphImportExport/GraphImportExport.cpp
index 96a69c23..36936fee 100644
--- a/source/armarx/navigation/components/GraphImportExport/GraphImportExport.cpp
+++ b/source/armarx/navigation/components/GraphImportExport/GraphImportExport.cpp
@@ -26,7 +26,6 @@
 
 #include <armarx/navigation/location/aron/Location.aron.generated.h>
 #include <armarx/navigation/location/constants.h>
-#include <armarx/navigation/graph/Writer.h>
 
 #include <armarx/navigation/graph/aron/Graph.aron.generated.h>
 #include <armarx/navigation/graph/constants.h>
@@ -373,7 +372,7 @@ namespace armarx::nav
                 nav::graph::Graph::Vertex& vertex = vertexMap.emplace(name, graph.addVertex(nextVertexID)).first->second;
                 vertex.attrib().aron.vertexID = static_cast<long>(nextVertexID);
                 toAron(vertex.attrib().aron.locationID, getLocationProviderSegmentID().withEntityName(name));
-                vertex.attrib().aron.globalRobotPose = globalNodePose->toEigen();
+                vertex.attrib().setPose(globalNodePose->toEigen());
 
                 nextVertexID++;
             }
diff --git a/source/armarx/navigation/graph/Graph.cpp b/source/armarx/navigation/graph/Graph.cpp
index 48c0f4dd..f34cb169 100644
--- a/source/armarx/navigation/graph/Graph.cpp
+++ b/source/armarx/navigation/graph/Graph.cpp
@@ -22,8 +22,14 @@
 
 #include "Graph.h"
 
+#include <armarx/navigation/location/aron/Location.aron.generated.h>
+
 #include <ArmarXCore/core/exceptions/local/ExpressionException.h>
 
+#include <RobotAPI/libraries/armem/core/aron_conversions.h>
+#include <RobotAPI/libraries/armem/core/wm/memory_definitions.h>
+
+
 
 namespace armarx::nav::graph
 {
@@ -33,14 +39,20 @@ namespace armarx::nav::graph
         return aron.locationID.providerSegmentName + "/" + aron.locationID.entityName;
     }
 
+    bool VertexAttribs::hasPose() const
+    {
+        return _pose.has_value();
+    }
+
     Eigen::Matrix4f VertexAttribs::getPose() const
     {
-        return aron.globalRobotPose;
+        ARMARX_CHECK(_pose.has_value());
+        return _pose.value();
     }
 
     void VertexAttribs::setPose(const Eigen::Matrix4f& pose)
     {
-        this->aron.globalRobotPose = pose;
+        this->_pose = pose;
     }
 
 }
@@ -87,4 +99,22 @@ namespace armarx::nav
         ARMARX_CHECK_EQUAL(bo.numEdges(), dto.edges.size());
     }
 
+
+    void graph::resolveLocations(Graph& bo, armem::wm::Memory& locationMemory)
+    {
+        for (graph::Graph::Vertex vertex : bo.vertices())
+        {
+            armem::MemoryID locationID;
+            fromAron(vertex.attrib().aron.locationID, locationID);
+
+            armem::wm::EntityInstance* instance = locationMemory.findLatestInstance(locationID);
+            if (instance)
+            {
+                nav::loc::arondto::Location dto;
+                dto.fromAron(instance->data());
+                vertex.attrib().setPose(dto.globalRobotPose);
+            }
+        }
+    }
+
 }
diff --git a/source/armarx/navigation/graph/Graph.h b/source/armarx/navigation/graph/Graph.h
index d895dd63..aa216d69 100644
--- a/source/armarx/navigation/graph/Graph.h
+++ b/source/armarx/navigation/graph/Graph.h
@@ -24,6 +24,8 @@
 
 #include <armarx/navigation/graph/aron/Graph.aron.generated.h>
 
+#include <RobotAPI/libraries/armem/core/forward_declarations.h>
+
 #include <SemanticObjectRelations/RelationGraph/RelationGraph.h>
 
 
@@ -36,13 +38,21 @@ namespace armarx::nav::graph
 
         std::string getName() const;
 
+        bool hasPose() const;
         Eigen::Matrix4f getPose() const;
         void setPose(const Eigen::Matrix4f& pose);
+
+    private:
+
+        std::optional<Eigen::Matrix4f> _pose;
+
     };
+
     struct EdgeAttribs
     {
         armarx::nav::graph::arondto::Edge aron;
     };
+
     struct GraphAttribs
     {
     };
@@ -52,5 +62,6 @@ namespace armarx::nav::graph
 
     void toAron(arondto::Graph& dto, const Graph& bo);
     void fromAron(const arondto::Graph& dto, Graph& bo);
+    void resolveLocations(Graph& bo, armem::wm::Memory& locationMemory);
 
 }
diff --git a/source/armarx/navigation/graph/aron/Graph.xml b/source/armarx/navigation/graph/aron/Graph.xml
index 18e1c847..fe09dcc0 100644
--- a/source/armarx/navigation/graph/aron/Graph.xml
+++ b/source/armarx/navigation/graph/aron/Graph.xml
@@ -19,9 +19,10 @@
                 <armarx::armem::arondto::MemoryID />
             </ObjectChild>
 
-            <ObjectChild key='globalRobotPose'>
+            <!-- Only stored in Location -->
+            <!--ObjectChild key='globalRobotPose'>
                 <Pose />
-            </ObjectChild>
+            </ObjectChild-->
 
         </Object>
 
diff --git a/source/armarx/navigation/gui-plugins/LocationGraphEditor/CMakeLists.txt b/source/armarx/navigation/gui-plugins/LocationGraphEditor/CMakeLists.txt
index 0820ee1b..e345f39d 100644
--- a/source/armarx/navigation/gui-plugins/LocationGraphEditor/CMakeLists.txt
+++ b/source/armarx/navigation/gui-plugins/LocationGraphEditor/CMakeLists.txt
@@ -20,6 +20,7 @@ set(SOURCES
     widgets/default_colors.cpp
     widgets/utils.cpp
     widgets/EdgeTableWidget.cpp
+    widgets/NewEntityIdDialog.cpp
     widgets/VertexDataWidget.cpp
     widgets/VertexTableWidget.cpp
 
@@ -37,6 +38,7 @@ set(HEADERS
     widgets/default_colors.h
     widgets/utils.h
     widgets/EdgeTableWidget.h
+    widgets/NewEntityIdDialog.h
     widgets/VertexDataWidget.h
     widgets/VertexTableWidget.h
 
diff --git a/source/armarx/navigation/gui-plugins/LocationGraphEditor/LocationGraphEditorWidget.ui b/source/armarx/navigation/gui-plugins/LocationGraphEditor/LocationGraphEditorWidget.ui
index 161687fa..88fa0f4c 100644
--- a/source/armarx/navigation/gui-plugins/LocationGraphEditor/LocationGraphEditorWidget.ui
+++ b/source/armarx/navigation/gui-plugins/LocationGraphEditor/LocationGraphEditorWidget.ui
@@ -15,18 +15,28 @@
   </property>
   <layout class="QVBoxLayout" name="verticalLayout_2">
    <item>
-    <widget class="QGroupBox" name="sceneGroupBox">
+    <widget class="QGroupBox" name="graphGroupBox">
      <property name="title">
       <string>Navigation Graphs from Navigation Graph Segment</string>
      </property>
      <layout class="QHBoxLayout" name="horizontalLayout_4">
       <item>
-       <widget class="QPushButton" name="refreshGraphsButton">
+       <widget class="QPushButton" name="createGraphButton">
         <property name="toolTip">
          <string>Reloads the list of scenes from the memory</string>
         </property>
         <property name="text">
-         <string>Refresh List</string>
+         <string>Create New Graph</string>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <widget class="QPushButton" name="queryGraphsButton">
+        <property name="toolTip">
+         <string>Reloads the list of scenes from the memory</string>
+        </property>
+        <property name="text">
+         <string>Query</string>
         </property>
        </widget>
       </item>
@@ -110,6 +120,14 @@
              </layout>
             </widget>
            </item>
+           <item>
+            <widget class="QGroupBox" name="robotVisuGroupBox">
+             <property name="title">
+              <string>Robot Visu</string>
+             </property>
+             <layout class="QVBoxLayout" name="verticalLayout_5"/>
+            </widget>
+           </item>
           </layout>
          </widget>
          <widget class="QWidget" name="verticalLayout_13Widget">
@@ -117,7 +135,7 @@
            <item>
             <widget class="QGroupBox" name="locationsTableGroupBox">
              <property name="title">
-              <string>Locations (Graph Vertices)</string>
+              <string>Locations (right click for options)</string>
              </property>
              <layout class="QVBoxLayout" name="verticalLayout_7">
               <property name="leftMargin">
@@ -142,7 +160,7 @@
            <item>
             <widget class="QGroupBox" name="edgesTableGroupBox">
              <property name="title">
-              <string>Graph Edges</string>
+              <string>Graph Edges (right click for options)</string>
              </property>
              <layout class="QVBoxLayout" name="verticalLayout_8">
               <property name="leftMargin">
diff --git a/source/armarx/navigation/gui-plugins/LocationGraphEditor/LocationGraphEditorWidgetController.cpp b/source/armarx/navigation/gui-plugins/LocationGraphEditor/LocationGraphEditorWidgetController.cpp
index 3f4a7113..abe9388f 100644
--- a/source/armarx/navigation/gui-plugins/LocationGraphEditor/LocationGraphEditorWidgetController.cpp
+++ b/source/armarx/navigation/gui-plugins/LocationGraphEditor/LocationGraphEditorWidgetController.cpp
@@ -23,8 +23,9 @@
 #include <VirtualRobot/VirtualRobot.h>
 
 #include "LocationGraphEditorWidgetController.h"
-#include "Visu.h"
+#include "widgets/utils.h"
 #include "widgets/EdgeTableWidget.h"
+#include "widgets/NewEntityIdDialog.h"
 #include "widgets/VertexDataWidget.h"
 #include "widgets/VertexTableWidget.h"
 #include "widgets/graph_scene/Scene.h"
@@ -42,8 +43,7 @@
 #include <RobotAPI/components/ArViz/Client/Client.h>
 
 #include <ArmarXCore/core/exceptions/local/ExpressionException.h>
-
-#include <SimoxUtility/color/Color.h>
+#include <ArmarXCore/core/logging/Logging.h>
 
 // Qt headers
 #include <QDialog>
@@ -67,30 +67,6 @@ static const QString SETTING_LAST_SCENE = "lastScene";
 namespace armarx::nav::locgrapheditor
 {
 
-
-    /**
-     * @brief Returns the name used on the debug layer.
-     * @param edge The edge.
-     * @return The name used on the debug layer.
-     */
-    std::string iceName(const GuiGraph::Edge& edge)
-    {
-        std::stringstream ss;
-        ss << "edge " << std::to_string(edge.sourceObjectID()) << " -> " << std::to_string(edge.targetObjectID());
-        return ss.str();
-    }
-
-    /**
-     * @brief iceName Returns the name used on the debug layer.
-     * @param vertexName The vertex.
-     * @return The name used on the debug layer.
-     */
-    std::string iceName(const GuiGraph::Vertex& vertex)
-    {
-        return std::to_string(vertex.objectID());
-    }
-
-
     QString LocationGraphEditorWidgetController::GetWidgetName()
     {
         return "Navigation.LocationGraphEditor";
@@ -110,31 +86,49 @@ namespace armarx::nav::locgrapheditor
 
         widget.loadGraphButton->setEnabled(false);
 
-        view.edgeTable = new EdgeTableWidget();
-        view.vertexTable = new VertexTableWidget();
         view.vertexData = new VertexDataWidget();
         view.vertexData->setEnabled(false);  // Enable on first selection of vertex.
-        widget.edgesTableGroupBox->layout()->addWidget(view.edgeTable);
-        widget.locationsTableGroupBox->layout()->addWidget(view.vertexTable);
+        view.vertexTable = new VertexTableWidget();
+        view.edgeTable = new EdgeTableWidget();
+
         widget.locationDataGroupBox->layout()->addWidget(view.vertexData);
+        if (QBoxLayout* layout = dynamic_cast<QBoxLayout*>(widget.locationDataGroupBox->layout()))
+        {
+            layout->addStretch();
+        }
+
+        widget.locationsTableGroupBox->layout()->addWidget(view.vertexTable);
+        widget.edgesTableGroupBox->layout()->addWidget(view.edgeTable);
 
         view.graph = new GraphSceneWidget();
         widget.graphSceneLayout->addWidget(view.graph);
 
 
-        // Widgets -> This
-
-        // Memory Access
         connect(this, &This::connected, this, &This::queryGraphs);
-        connect(this, &This::memoryDataChanged, this, &This::updateGraphList);
-        connect(widget.refreshGraphsButton, &QPushButton::pressed, this, &This::updateGraphList);
 
-        connect(widget.loadGraphButton, &QPushButton::pressed, this, &This::loadGraph);
-        connect(widget.commitGraphButton, &QPushButton::pressed, this, &This::commit);
+        connect(widget.createGraphButton, &QPushButton::pressed,
+                this, &This::createGraphDialog);
+
+        connect(widget.queryGraphsButton, &QPushButton::pressed,
+                this, &This::queryGraphs);
 
+        connect(this, &This::locationMemoryChanged,
+                this, &This::memoryChanged);
+        connect(this, &This::graphMemoryChanged,
+                this, &This::memoryChanged);
 
-        // Update views
-        connect(this, &This::graphChanged, this, &This::updateGraphView);
+        connect(this, &This::graphMemoryChanged,
+                this, &This::updateGraphList);
+
+        connect(widget.loadGraphButton, &QPushButton::pressed,
+                this, &This::loadGraph);
+        connect(widget.commitGraphButton, &QPushButton::pressed,
+                this, &This::commit);
+
+
+        // View updates
+        connect(this, &This::graphChanged,
+                this, &This::updateGraphView);
         connect(view.vertexData, &VertexDataWidget::vertexDataChanged,
                 this, &This::updateGraphView);
 
@@ -151,8 +145,15 @@ namespace armarx::nav::locgrapheditor
         connect(view.edgeTable, &EdgeTableWidget::itemSelectionChanged,
                 this, &This::updateEdgeHighlighting);
 
+        // Graph modification
+
+        connect(view.vertexTable, &VertexTableWidget::newVertexRequested,
+                this, &This::createVertexDialog);
         connect(view.vertexTable, &VertexTableWidget::newEdgesRequested,
                 this, &This::addEdges);
+        connect(view.vertexTable, &VertexTableWidget::edgeRemovalRequested,
+                this, &This::removeEdgesOfVertex);
+
         connect(view.edgeTable, &EdgeTableWidget::edgeRemovalRequested,
                 this, &This::removeEdges);
     }
@@ -166,18 +167,18 @@ namespace armarx::nav::locgrapheditor
 
     QPointer<QDialog> LocationGraphEditorWidgetController::getConfigDialog(QWidget* parent)
     {
-        if (not dialog)
+        if (not configDialog)
         {
-            dialog = new SimpleConfigDialog(parent);
-            dialog->addProxyFinder<armem::mns::MemoryNameSystemInterfacePrx>("MemoryNameSystem", "Memory Name System", remote.memoryNameSystemName);
+            configDialog = new SimpleConfigDialog(parent);
+            configDialog->addProxyFinder<armem::mns::MemoryNameSystemInterfacePrx>("MemoryNameSystem", "Memory Name System", remote.memoryNameSystemName);
         }
-        return qobject_cast<SimpleConfigDialog*>(dialog);
+        return qobject_cast<SimpleConfigDialog*>(configDialog);
     }
 
 
     void LocationGraphEditorWidgetController::configured()
     {
-        remote.memoryNameSystemName = dialog->getProxyName("MemoryNameSystem");
+        remote.memoryNameSystemName = configDialog->getProxyName("MemoryNameSystem");
     }
 
 
@@ -217,8 +218,8 @@ namespace armarx::nav::locgrapheditor
         {
             std::stringstream ss;
             ss << "Navigation Graphs (Entities from Core Segment " << nav::graph::coreSegmentID << ")";
-            widget.sceneGroupBox->setTitle(QString::fromStdString(ss.str()));
-            widget.sceneGroupBox->setEnabled(true);
+            widget.graphGroupBox->setTitle(QString::fromStdString(ss.str()));
+            widget.graphGroupBox->setEnabled(true);
         }
 
         emit connected();
@@ -240,18 +241,49 @@ namespace armarx::nav::locgrapheditor
     }
 
 
-    void LocationGraphEditorWidgetController::queryGraphs()
+    void LocationGraphEditorWidgetController::queryMemory()
     {
-        armem::client::QueryResult result = remote.graphReader.getLatestSnapshotsIn(nav::graph::coreSegmentID);
+        armem::client::QueryResult locResult = queryLocations();
+        armem::client::QueryResult graphResult = queryGraphs();
+
+        if (not (locResult.success and graphResult.success))
+        {
+            QStringList status;
+            std::stringstream ss;
+            if (not locResult.success)
+            {
+                status.append(QString::fromStdString(locResult.errorMessage));
+            }
+            if (not graphResult.success)
+            {
+                status.append(QString::fromStdString(graphResult.errorMessage));
+            }
+            widget.statusLabel->setText(status.join("\n"));
+        }
+    }
+
+
+    armem::client::QueryResult LocationGraphEditorWidgetController::queryLocations()
+    {
+        armem::client::QueryResult result = remote.locationReader.getLatestSnapshotsIn(nav::loc::coreSegmentID);
         if (result.success)
         {
-            model.memory = std::move(result.memory);
-            emit memoryDataChanged();
+            model.locationsMemory = std::move(result.memory);
+            emit locationMemoryChanged();
         }
-        else
+        return result;
+    }
+
+
+    armem::client::QueryResult LocationGraphEditorWidgetController::queryGraphs()
+    {
+        armem::client::QueryResult result = remote.graphReader.getLatestSnapshotsIn(nav::graph::coreSegmentID);
+        if (result.success)
         {
-            widget.statusLabel->setText(QString::fromStdString(result.errorMessage));
+            model.graphMemory = std::move(result.memory);
+            emit graphMemoryChanged();
         }
+        return result;
     }
 
 
@@ -262,7 +294,7 @@ namespace armarx::nav::locgrapheditor
 
         int i = 0;
         int previousIndex = -1;  // To keep selection.
-        model.memory.forEachEntity([&](const armem::wm::Entity& entity)
+        model.graphMemory.forEachEntity([&](const armem::wm::Entity& entity)
         {
             bool hasChanged = (entity.id() == model.graphEntityID
                                ? model.graph.hasChanged()
@@ -289,34 +321,9 @@ namespace armarx::nav::locgrapheditor
     {
         if (model.graph.hasChanged())
         {
-            QMessageBox msgBox;
-            msgBox.setText("The current graph and/or locations have uncommitted changes. ");
-            msgBox.setInformativeText("Do you want to discard them?");
-            QStringList detailLines;
-            if (model.graph.attrib().edgesChanged)
-            {
-                detailLines.append("Graph edges have changed.");
-            }
-            for (auto vertex : model.graph.vertices())
-            {
-                if (vertex.attrib().changed)
-                {
-                    detailLines.append("Location " + QString::fromStdString(vertex.attrib().getName()) + " has changed.");
-                }
-            }
-            msgBox.setDetailedText(detailLines.join("\n"));
-            msgBox.setStandardButtons(QMessageBox::Discard | QMessageBox::Cancel);
-            msgBox.setDefaultButton(QMessageBox::Cancel);
-
-            int ret = msgBox.exec();
-            switch (ret)
+            if (not loadGraphDialog())
             {
-              case QMessageBox::Discard:
-                  // Ok go.
-                  break;
-              case QMessageBox::Cancel:
-                  // Abort loading.
-                  return;
+                return;
             }
         }
 
@@ -324,9 +331,9 @@ namespace armarx::nav::locgrapheditor
                     widget.graphsComboBox->currentData().toString().toStdString());
 
         // Refresh local memory.
-        queryGraphs();
+        queryMemory();
 
-        const armem::wm::EntityInstance* instance = model.memory.findLatestInstance(entityID);
+        const armem::wm::EntityInstance* instance = model.graphMemory.findLatestInstance(entityID);
         if (instance)
         {
             widget.statusLabel->setText(QString::fromStdString("Loaded snapshot " + instance->id().getEntitySnapshotID().str()));
@@ -341,9 +348,32 @@ namespace armarx::nav::locgrapheditor
         nav::graph::Graph nav;
         {
             nav::graph::arondto::Graph dto;
+            ARMARX_CHECK_NOT_NULL(instance->data());
             dto.fromAron(instance->data());
             fromAron(dto, nav);
         }
+        // Resolve locations and remove vertices which could not be resolved.
+        {
+            resolveLocations(nav, model.locationsMemory);
+            std::vector<nav::graph::Graph::Vertex> remove;
+            for (nav::graph::Graph::Vertex vertex : nav.vertices())
+            {
+                if (not vertex.attrib().hasPose())
+                {
+                    remove.push_back(vertex);
+                }
+            }
+            if (not remove.empty())
+            {
+                for (nav::graph::Graph::Vertex vertex : remove)
+                {
+                    nav.removeVertex(vertex.objectID());
+                }
+                std::stringstream ss;
+                ss << "Dropped " << remove.size() << " locations which could not be resolved.";
+                widget.statusLabel->setText(QString::fromStdString(ss.str()));
+            }
+        }
 
         model.graphEntityID = entityID;
         ARMARX_VERBOSE << "Loading graph " << nav.str();
@@ -351,6 +381,42 @@ namespace armarx::nav::locgrapheditor
     }
 
 
+
+    bool LocationGraphEditorWidgetController::loadGraphDialog()
+    {
+        ARMARX_CHECK(model.graph.hasChanged());
+
+        QMessageBox msgBox;
+        msgBox.setText("The current graph and/or locations have uncommitted changes. ");
+        msgBox.setInformativeText("Do you want to discard them?");
+        QStringList detailLines;
+        if (model.graph.attrib().edgesChanged)
+        {
+            detailLines.append("Graph edges have changed.");
+        }
+        for (auto vertex : model.graph.vertices())
+        {
+            if (vertex.attrib().changed)
+            {
+                detailLines.append("Location " + QString::fromStdString(vertex.attrib().getName()) + " has changed.");
+            }
+        }
+        msgBox.setDetailedText(detailLines.join("\n"));
+        msgBox.setStandardButtons(QMessageBox::Discard | QMessageBox::Cancel);
+        msgBox.setDefaultButton(QMessageBox::Cancel);
+
+        switch (msgBox.exec())
+        {
+        case QMessageBox::Discard:
+            return true;   // Ok go.
+        case QMessageBox::Cancel:
+            return false;  // Abort loading.
+        default:
+            return false;  // Something went wrong.
+        }
+    }
+
+
     void
     LocationGraphEditorWidgetController::commit()
     {
@@ -460,7 +526,7 @@ namespace armarx::nav::locgrapheditor
     }
 
 
-    void LocationGraphEditorWidgetController::setGraph(graph::Graph& nav)
+    void LocationGraphEditorWidgetController::setGraph(const graph::Graph& nav)
     {
         // Build the gui graph (model).
         {
@@ -471,11 +537,14 @@ namespace armarx::nav::locgrapheditor
 
             for (auto vertex : nav.vertices())
             {
-                addVertex(vertex);
+                ARMARX_CHECK(vertex.attrib().hasPose());
+                addVertex(vertex.objectID(), { vertex.attrib() });
             }
             for (auto edge : nav.edges())
             {
-                addEdge(edge);
+                addEdge(model.graph.vertex(edge.sourceObjectID()),
+                        model.graph.vertex(edge.targetObjectID()),
+                        { edge.attrib() });
             }
         }
 
@@ -484,25 +553,19 @@ namespace armarx::nav::locgrapheditor
     }
 
 
-    GuiGraph::Vertex
-    LocationGraphEditorWidgetController::addVertex(graph::Graph::ConstVertex vertex)
+    GuiGraph::Vertex LocationGraphEditorWidgetController::addVertex(
+            semrel::ShapeID vertexID,
+            const VertexData& defaultAttribs)
     {
-        ARMARX_CHECK(not model.graph.hasVertex(vertex.objectID()));
+        ARMARX_CHECK(not model.graph.hasVertex(vertexID));
 
-        VertexData attrib { vertex.attrib() };
-        attrib.graphicsItem = view.graph->scene()->addVertex(vertex);
-        attrib.tableWidgetItem = view.vertexTable->addVertex(vertex);
-        return model.graph.addVertex(vertex.objectID(), attrib);
-    }
+        GuiGraph::Vertex vertex = model.graph.addVertex(vertexID, defaultAttribs);
+        vertex.attrib().graphicsItem = view.graph->scene()->addVertex(vertex);
+        vertex.attrib().tableWidgetItem = view.vertexTable->addVertex();
 
+        emit graphChanged();
 
-    GuiGraph::Edge
-    LocationGraphEditorWidgetController::addEdge(graph::Graph::ConstEdge edge)
-    {
-        auto source = model.graph.vertex(edge.sourceObjectID());
-        auto target = model.graph.vertex(edge.targetObjectID());
-
-        return addEdge(source, target, { edge.attrib() });
+        return vertex;
     }
 
 
@@ -520,68 +583,10 @@ namespace armarx::nav::locgrapheditor
         GuiGraph::Edge edge = model.graph.addEdge(source, target, defaultAttribs);
         edge.attrib().tableWidgetItem = view.edgeTable->addEdge(edge);
         edge.attrib().graphicsItem = view.graph->scene()->addEdge(edge);
-        return edge;
-    }
-
-
-    void LocationGraphEditorWidgetController::addEdges(
-            QList<QPair<QTableWidgetItem*, QTableWidgetItem*>> vertexItems)
-    {
-        if (/* DISABLES CODE */ (false))
-        {
-            std::stringstream ss;
-            ss << "Adding edges ...";
-            for (const auto& [source, target] : vertexItems)
-            {
-                ss << "\n- " << source->text() << " to " << target->text();
-            }
-            ARMARX_IMPORTANT << ss.str();
-        }
-
-        const auto itemToVertexMap = model.graph.getTableItemToVertexMap();
-        for (const auto& [sourceItem, targetItem] : vertexItems)
-        {
-            GuiGraph::Vertex source = itemToVertexMap.at(sourceItem);
-            GuiGraph::Vertex target = itemToVertexMap.at(targetItem);
-            if (not model.graph.hasEdge(source, target))
-            {
-                addEdge(source, target, {});
-            }
-        }
-
-        model.graph.attrib().edgesChanged = true;
 
         emit graphChanged();
-    }
-
-
-    void LocationGraphEditorWidgetController::removeEdges(
-            QList<QTableWidgetItem*> edgeItems)
-    {
-        if (/* DISABLES CODE */ (false))
-        {
-            std::stringstream ss;
-            ss << "Remoiving edges ...";
-            for (const auto& edgeItem : edgeItems)
-            {
-                ss << "\n- " << edgeItem->text();
-            }
-            ARMARX_IMPORTANT << ss.str();
-        }
-        {
-            QSignalBlocker blocker(this);
-
-            const auto itemToEdgeMap = model.graph.getTableItemToEdgeMap();
-            for (const auto& edgeItem : edgeItems)
-            {
-                GuiGraph::Edge edge = itemToEdgeMap.at(edgeItem);
-                removeEdge(edge);
-            }
-        }
 
-        model.graph.attrib().edgesChanged = true;
-
-        emit graphChanged();
+        return edge;
     }
 
 
@@ -606,8 +611,7 @@ namespace armarx::nav::locgrapheditor
     }
 
 
-    void
-    LocationGraphEditorWidgetController::updateVertexView(GuiGraph::Vertex vertex)
+    void LocationGraphEditorWidgetController::updateVertexView(GuiGraph::Vertex vertex)
     {
         view.graph->scene()->updateVertex(vertex);
         view.vertexTable->updateVertex(vertex);
@@ -653,14 +657,68 @@ namespace armarx::nav::locgrapheditor
     {
         if (remote.arviz)
         {
-            viz::Layer layer = remote.arviz->layer(widget.graphsComboBox->currentText().toStdString());
-            GraphVisu visu;
-            visu.draw(layer, model.graph);
+            const QString layerName = getGraphDisplayName(model.graphEntityID, false);
+            viz::Layer layer = remote.arviz->layer(layerName.toStdString());
+            model.visu.draw(layer, model.graph);
             remote.arviz->commit(layer);
         }
     }
 
 
+    template <class T>
+    static
+    void updateElementHighlighting(
+            QList<QTableWidgetItem*> selectedItems,
+            std::map<QTableWidgetItem*, T>&& itemToElementMap)
+    {
+        for (QTableWidgetItem* selected : selectedItems)
+        {
+            if (auto it = itemToElementMap.find(selected); it != itemToElementMap.end())
+            {
+                it->second.attrib().highlighted = true;
+                itemToElementMap.erase(it);
+            }
+        }
+        for (auto& [_, unselected] : itemToElementMap)
+        {
+            unselected.attrib().highlighted = false;
+        }
+    }
+
+
+    void LocationGraphEditorWidgetController::updateVertexHighlighting()
+    {
+        updateElementHighlighting(view.vertexTable->selectedVertexItems(), model.graph.getTableItemToVertexMap());
+        emit graphChanged();
+    }
+
+
+    void LocationGraphEditorWidgetController::updateEdgeHighlighting()
+    {
+        updateElementHighlighting(view.edgeTable->selectedEdgeItems(), model.graph.getTableItemToEdgeMap());
+        emit graphChanged();
+    }
+
+
+    void LocationGraphEditorWidgetController::selectVertex(QTableWidgetItem* vertexItem)
+    {
+        if (vertexItem == nullptr)
+        {
+            view.vertexData->clearVertex();
+        }
+        if (auto vertex = model.graph.getVertexFromTableItem(vertexItem))
+        {
+            selectVertex(vertex.value());
+        }
+    }
+
+
+    void LocationGraphEditorWidgetController::selectVertex(GuiGraph::Vertex vertex)
+    {
+        view.vertexData->setVertex(vertex);
+    }
+
+
     void LocationGraphEditorWidgetController::clearGraph()
     {
         {
@@ -681,10 +739,13 @@ namespace armarx::nav::locgrapheditor
     {
         // Remove in reverse order to be more array-friendly.
         std::vector<GuiGraph::Edge> edges { model.graph.edges().begin(), model.graph.edges().end() };
-        for (auto it = edges.rbegin(); it != edges.rend(); ++it)
         {
-            GuiGraph::Edge edge = *it;
-            removeEdge(edge);
+            QSignalBlocker blocker(this);
+            for (auto it = edges.rbegin(); it != edges.rend(); ++it)
+            {
+                GuiGraph::Edge edge = *it;
+                removeEdge(edge);
+            }
         }
 
         ARMARX_CHECK_EQUAL(view.edgeTable->rowCount(), 0);
@@ -701,10 +762,13 @@ namespace armarx::nav::locgrapheditor
 
         // Remove in reverse order to be more array-friendly.
         std::vector<GuiGraph::Vertex> vertices { model.graph.vertices().begin(), model.graph.vertices().end() };
-        for (auto it = vertices.rbegin(); it != vertices.rend(); ++it)
         {
-            GuiGraph::Vertex vertex = *it;
-            removeVertex(vertex);
+            QSignalBlocker blocker(this);
+            for (auto it = vertices.rbegin(); it != vertices.rend(); ++it)
+            {
+                GuiGraph::Vertex vertex = *it;
+                removeVertex(vertex);
+            }
         }
 
         ARMARX_CHECK_EQUAL(view.vertexTable->rowCount(), 0);
@@ -720,30 +784,35 @@ namespace armarx::nav::locgrapheditor
                 << "Cannot remove edge that does not exist.";
 
         // Remove view elements
+        {
+            QSignalBlocker blocker(view.edgeTable);
+            view.edgeTable->removeEdge(edge);
+        }
         view.graph->scene()->removeEdge(edge.attrib().graphicsItem);
 
-        const int rows = view.edgeTable->rowCount();
-        view.edgeTable->removeEdge(edge);
-        ARMARX_CHECK_EQUAL(view.edgeTable->rowCount(), rows - 1);
-
         // Remove from model
         model.graph.removeEdge(edge);
 
         ARMARX_CHECK(not model.graph.hasEdge(edge.sourceDescriptor(), edge.targetDescriptor()))
                 << edge.sourceDescriptor() << " -> " << edge.targetDescriptor();
+
+        emit graphChanged();
     }
 
 
     void LocationGraphEditorWidgetController::removeVertex(GuiGraph::Vertex& vertex)
     {
         ARMARX_CHECK_EQUAL(vertex.inDegree(), 0)
-                << "A vertex may not have any edges before being removed. " << vertex.attrib().getName();
+                << "A vertex must not have any edges before being removed. " << vertex.attrib().getName();
         ARMARX_CHECK_EQUAL(vertex.outDegree(), 0)
-                << "A vertex may not have any edges before being removed. "  << vertex.attrib().getName();
+                << "A vertex must not have any edges before being removed. " << vertex.attrib().getName();
 
         // Remove view elements
+        {
+            QSignalBlocker blocker(view.vertexTable);
+            view.vertexTable->removeVertex(vertex);
+        }
         view.graph->scene()->removeVertex(vertex.attrib().graphicsItem);
-        view.vertexTable->removeVertex(vertex);
         if (view.vertexData->vertex().has_value() and view.vertexData->vertex().value() == vertex)
         {
             view.vertexData->clearVertex();
@@ -751,81 +820,159 @@ namespace armarx::nav::locgrapheditor
 
         // Remove from model
         model.graph.removeVertex(vertex);
+
+        emit graphChanged();
     }
 
 
-    void LocationGraphEditorWidgetController::resetHighlighting()
+    void LocationGraphEditorWidgetController::createVertexDialog()
     {
-        for (auto vertex : model.graph.vertices())
+        NewEntityIdDialog dialog(nav::loc::coreSegmentID, nullptr);
+        switch (dialog.exec())
         {
-            if (vertex.attrib().highlighted)
-            {
-                vertex.attrib().highlighted = false;
-            }
+          case QDialog::Accepted:
+              // Ok go.
+              break;
+          case QDialog::Rejected:
+              // Abort creation.
+              return;
         }
-        for (auto edge : model.graph.edges())
+
+        // Find free vertex ID
+        semrel::ShapeID vertexID { 0 };
+        while (model.graph.hasVertex(vertexID))
         {
-            if (edge.attrib().highlighted)
-            {
-                edge.attrib().highlighted = false;
-            }
+            ++vertexID;
         }
 
-        emit graphChanged();
+        // Initiaize attributes
+        VertexData attribs;
+        toAron(attribs.aron.locationID, dialog.entityID());
+        attribs.setPose(Eigen::Matrix4f::Identity());
+        attribs.changed = true;
+
+        addVertex(vertexID, attribs);
     }
 
 
-    template <class T>
-    static
-    void updateElementHighlighting(
-            QList<QTableWidgetItem*> selectedItems,
-            std::map<QTableWidgetItem*, T>&& itemToElementMap)
+    void LocationGraphEditorWidgetController::addEdges(
+            QList<QPair<QTableWidgetItem*, QTableWidgetItem*>> vertexItems)
     {
-        for (QTableWidgetItem* selected : selectedItems)
+        const auto itemToVertexMap = model.graph.getTableItemToVertexMap();
         {
-            if (auto it = itemToElementMap.find(selected); it != itemToElementMap.end())
+            QSignalBlocker blocker(this);
+
+            for (const auto& [sourceItem, targetItem] : vertexItems)
             {
-                it->second.attrib().highlighted = true;
-                itemToElementMap.erase(it);
+                GuiGraph::Vertex source = itemToVertexMap.at(sourceItem);
+                GuiGraph::Vertex target = itemToVertexMap.at(targetItem);
+                if (not model.graph.hasEdge(source, target))
+                {
+                    addEdge(source, target, {});
+                }
             }
         }
-        for (auto& [_, unselected] : itemToElementMap)
-        {
-            unselected.attrib().highlighted = false;
-        }
+
+        model.graph.attrib().edgesChanged = true;
+        emit graphChanged();
     }
 
 
-    void LocationGraphEditorWidgetController::updateVertexHighlighting()
+    void LocationGraphEditorWidgetController::removeEdges(
+            QList<QTableWidgetItem*> edgeItems)
     {
-        updateElementHighlighting(view.vertexTable->selectedVertexItems(), model.graph.getTableItemToVertexMap());
+        const auto itemToEdgeMap = model.graph.getTableItemToEdgeMap();
+        {
+            QSignalBlocker blocker(this);
+
+            for (const auto& edgeItem : edgeItems)
+            {
+                GuiGraph::Edge edge = itemToEdgeMap.at(edgeItem);
+                removeEdge(edge);
+            }
+        }
+
+        model.graph.attrib().edgesChanged = true;
         emit graphChanged();
     }
 
 
-    void LocationGraphEditorWidgetController::updateEdgeHighlighting()
+    void LocationGraphEditorWidgetController::removeEdgesOfVertex(
+            QList<QTableWidgetItem*> vertexItems,
+            utils::EdgeDirection direction)
     {
-        updateElementHighlighting(view.edgeTable->selectedEdgeItems(), model.graph.getTableItemToEdgeMap());
+        auto removeEdges = [this](auto&& range)
+        {
+            for (auto edge : range)
+            {
+                removeEdge(edge);
+            }
+        };
+        {
+            QSignalBlocker blocker(this);
+
+            const auto itemToVertexMap = model.graph.getTableItemToVertexMap();
+            for (const auto& vertexItem : vertexItems)
+            {
+                GuiGraph::Vertex vertex = itemToVertexMap.at(vertexItem);
+                switch (direction)
+                {
+                case utils::EdgeDirection::To:
+                    removeEdges(vertex.inEdges());
+                    break;
+
+                case utils::EdgeDirection::From:
+                    removeEdges(vertex.outEdges());
+                    break;
+
+                case utils::EdgeDirection::Bidirectional:
+                    removeEdges(vertex.inEdges());
+                    removeEdges(vertex.outEdges());
+                    break;
+                }
+            }
+        }
+
+        model.graph.attrib().edgesChanged = true;
         emit graphChanged();
     }
 
 
-    void LocationGraphEditorWidgetController::selectVertex(QTableWidgetItem* vertexItem)
+    void LocationGraphEditorWidgetController::createGraphDialog()
     {
-        if (vertexItem == nullptr)
+        NewEntityIdDialog dialog(nav::graph::coreSegmentID, nullptr);
+        switch (dialog.exec())
         {
-            view.vertexData->clearVertex();
+          case QDialog::Accepted:
+              break;
+          case QDialog::Rejected:
+              return;
         }
-        if (auto vertex = model.graph.getVertexFromTableItem(vertexItem))
+
+        // Find free vertex ID
+        semrel::ShapeID vertexID { 0 };
+        while (model.graph.hasVertex(vertexID))
         {
-            selectVertex(vertex.value());
+            ++vertexID;
         }
-    }
 
+        const armem::MemoryID entityID = dialog.entityID();
 
-    void LocationGraphEditorWidgetController::selectVertex(GuiGraph::Vertex vertex)
-    {
-        view.vertexData->setVertex(vertex);
+        const bool hasChanged = true;
+        QString text = getGraphDisplayName(entityID, hasChanged);
+        QString data = QString::fromStdString(entityID.str());
+        widget.graphsComboBox->addItem(text, data);
+        widget.graphsComboBox->setCurrentIndex(widget.graphsComboBox->count() - 1);
+
+        {
+            QSignalBlocker blocker(this);
+            clearEdges();
+            // clearVertices();
+        }
+        model.graph.attrib().edgesChanged = true;
+        model.graphEntityID = entityID;
+
+        emit graphChanged();
     }
 
 
diff --git a/source/armarx/navigation/gui-plugins/LocationGraphEditor/LocationGraphEditorWidgetController.h b/source/armarx/navigation/gui-plugins/LocationGraphEditor/LocationGraphEditorWidgetController.h
index e4de7136..32ae0ccd 100644
--- a/source/armarx/navigation/gui-plugins/LocationGraphEditor/LocationGraphEditorWidgetController.h
+++ b/source/armarx/navigation/gui-plugins/LocationGraphEditor/LocationGraphEditorWidgetController.h
@@ -21,8 +21,8 @@
  */
 #pragma once
 
-#include "FunctionalEventFilter.h"
 #include "GuiGraph.h"
+#include "Visu.h"
 #include "widgets/graph_scene.h"
 
 #include <armarx/navigation/graph/Graph.h>
@@ -52,6 +52,10 @@ namespace armarx::viz
 {
     class Client;
 }
+namespace armarx::nav::locgrapheditor::utils
+{
+    enum class EdgeDirection;
+}
 namespace armarx::nav::locgrapheditor
 {
     class EdgeTableWidget;
@@ -127,59 +131,73 @@ namespace armarx::nav::locgrapheditor
     signals:
 
         void connected();
-        void memoryDataChanged();
+
+        void locationMemoryChanged();
+        void graphMemoryChanged();
+        void memoryChanged();
+
         void graphChanged();
 
 
     private slots:
 
-        // Model
+        // Load
+        void queryMemory();
+        armem::client::QueryResult queryLocations();
+        armem::client::QueryResult queryGraphs();
 
-        void queryGraphs();
-        void updateGraphList();
         void loadGraph();
+        bool loadGraphDialog();
 
-        void setGraph(graph::Graph& nav);
-        GuiGraph::Vertex addVertex(graph::Graph::ConstVertex vertex);
-        GuiGraph::Edge addEdge(graph::Graph::ConstEdge edge);
-        GuiGraph::Edge addEdge(GuiGraph::ConstVertex source, GuiGraph::ConstVertex target,
-                               const EdgeData& defaultAttribs);
-
-        void clearGraph();
-        void clearEdges();
-        void clearVertices();
-
-        void removeEdge(GuiGraph::Edge& edge);
-        void removeVertex(GuiGraph::Vertex& vertex);
-
-        void addEdges(QList<QPair<QTableWidgetItem*, QTableWidgetItem*>> vertexItems);
-        void removeEdges(QList<QTableWidgetItem*> edgeItems);
-
-
+        // Commit
         void commit();
         armem::CommitResult commitLocations();
         armem::EntityUpdateResult commitGraph();
 
-        // View & Tables
 
+        // View & Tables
+        void updateGraphList();
         void updateGraphView();
         void updateVertexView(GuiGraph::Vertex vertex);
         void updateEdgeView(GuiGraph::Edge edge);
         void updateArViz();
 
 
-        // Selection
-
+        // Selection & Highlighting
         void selectVertex(QTableWidgetItem* vertexItem);
         void selectVertex(GuiGraph::Vertex vertex);
-
-        void resetHighlighting();
         void updateVertexHighlighting();
         void updateEdgeHighlighting();
 
 
+        // Graph modification triggered by user interaction
+        void createVertexDialog();
+
+        void addEdges(QList<QPair<QTableWidgetItem*, QTableWidgetItem*>> vertexItems);
+        void removeEdges(QList<QTableWidgetItem*> edgeItems);
+        void removeEdgesOfVertex(QList<QTableWidgetItem*> vertexItems, utils::EdgeDirection direction);
+
+        void createGraphDialog();
+
+
     private:
 
+        void setGraph(const graph::Graph& nav);
+
+        GuiGraph::Vertex
+        addVertex(semrel::ShapeID vertexID, const VertexData& defaultAttribs);
+
+        GuiGraph::Edge
+        addEdge(GuiGraph::ConstVertex source, GuiGraph::ConstVertex target, const EdgeData& defaultAttribs);
+
+        void removeVertex(GuiGraph::Vertex& vertex);
+        void removeEdge(GuiGraph::Edge& edge);
+
+        void clearGraph();
+        void clearEdges();
+        void clearVertices();
+
+
         QString getGraphDisplayName(const armem::MemoryID& entityID, bool changed = false) const;
 
 
@@ -187,7 +205,7 @@ namespace armarx::nav::locgrapheditor
 
         /// Widget Form
         Ui::LocationGraphEditorWidget widget;
-        QPointer<SimpleConfigDialog> dialog;
+        QPointer<SimpleConfigDialog> configDialog;
 
 
         struct Remote
@@ -210,9 +228,13 @@ namespace armarx::nav::locgrapheditor
 
         struct Model
         {
-            armem::wm::Memory memory;
+            armem::wm::Memory locationsMemory;
+
+            armem::wm::Memory graphMemory;
             armem::MemoryID graphEntityID;
             GuiGraph graph;
+
+            GraphVisu visu;
         };
         Model model;
 
@@ -228,30 +250,6 @@ namespace armarx::nav::locgrapheditor
         View view;
 
 
-    // Non-refactored
-
-#if 0
-    private slots:
-
-        void tableWidgetVerticesCustomContextMenu(QPoint pos);
-        void tableWidgetEdgesCustomContextMenu(QPoint pos);
-
-
-    private slots:
-
-        /// Add kitchen graph (H2T Armar3a robot kitchen)
-        void addKitchenGraph();
-
-        bool addNewEdge(const std::string& from, const std::string& to);
-        void addNewEdgeBoth();
-        void addNewEdgeStartEnd();
-        void addNewEdgeEndStart();
-
-        void addNewGraphVertex();
-        void editGraphVertex();
-#endif
-
-
     private:
 
         QSettings settings;
diff --git a/source/armarx/navigation/gui-plugins/LocationGraphEditor/widgets/EdgeTableWidget.cpp b/source/armarx/navigation/gui-plugins/LocationGraphEditor/widgets/EdgeTableWidget.cpp
index adecbca4..93d87392 100644
--- a/source/armarx/navigation/gui-plugins/LocationGraphEditor/widgets/EdgeTableWidget.cpp
+++ b/source/armarx/navigation/gui-plugins/LocationGraphEditor/widgets/EdgeTableWidget.cpp
@@ -100,9 +100,15 @@ namespace armarx::nav::locgrapheditor
     }
 
 
-    void EdgeTableWidget::removeEdge(GuiGraph::Edge& edge)
+    void
+    EdgeTableWidget::removeEdge(GuiGraph::Edge& edge)
     {
-        this->removeRow(row(edge.attrib().tableWidgetItem));
+        if (currentItem() == edge.attrib().tableWidgetItem)
+        {
+            setCurrentItem(nullptr);
+        }
+
+        removeRow(row(edge.attrib().tableWidgetItem));
         edge.attrib().tableWidgetItem = nullptr;
     }
 
@@ -114,7 +120,8 @@ namespace armarx::nav::locgrapheditor
     }
 
 
-    void EdgeTableWidget::makeContextMenu(QPoint pos)
+    void
+    EdgeTableWidget::makeContextMenu(QPoint pos)
     {
         QList<QTableWidgetItem*> items = selectedEdgeItems();
 
diff --git a/source/armarx/navigation/gui-plugins/LocationGraphEditor/widgets/NewEntityIdDialog.cpp b/source/armarx/navigation/gui-plugins/LocationGraphEditor/widgets/NewEntityIdDialog.cpp
new file mode 100644
index 00000000..381556b4
--- /dev/null
+++ b/source/armarx/navigation/gui-plugins/LocationGraphEditor/widgets/NewEntityIdDialog.cpp
@@ -0,0 +1,129 @@
+/*
+ * This file is part of ArmarX.
+ *
+ * ArmarX is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * ArmarX 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 <http://www.gnu.org/licenses/>.
+ *
+ * @author     Rainer Kartmann ( rainer dot kartmann at kit dot edu )
+ * @date       2021
+ * @copyright  http://www.gnu.org/licenses/gpl-2.0.txt
+ *             GNU General Public License
+ */
+
+#include "NewEntityIdDialog.h"
+
+#include <RobotAPI/libraries/armem/core/MemoryID.h>
+
+#include <ArmarXCore/core/exceptions/local/ExpressionException.h>
+
+#include <QDialogButtonBox>
+#include <QGridLayout>
+#include <QLineEdit>
+#include <QLabel>
+#include <QPushButton>
+#include <QVBoxLayout>
+
+
+namespace armarx::nav::locgrapheditor
+{
+
+    NewEntityIdDialog::NewEntityIdDialog(const armem::MemoryID& coreSegmentID, QWidget* parent) :
+        QDialog(parent),
+        coreSegmentID(std::make_unique<armem::MemoryID>(coreSegmentID))
+    {
+        _providerSegmentName = new QLineEdit(this);
+        _entityName = new QLineEdit(this);
+
+        _buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok
+                                         | QDialogButtonBox::Cancel, this);
+
+        QLabel* instruction = new QLabel("Enter provider segment name and entity name for new entity:");
+        QFont font = instruction->font();
+        font.setBold(true);
+        instruction->setFont(font);
+
+
+        QGridLayout* grid = new QGridLayout();
+        int col = 0;
+
+        grid->addWidget(new QLabel("Core Segment ID"), 0, col);
+        grid->addWidget(new QLabel(QString::fromStdString(coreSegmentID.str()) + "/"), 1, col);
+        ++col;
+
+        grid->addWidget(new QLabel("Provider Segment Name"), 0, col);
+        grid->addWidget(_providerSegmentName, 1, col);
+        ++col;
+
+        grid->addWidget(new QLabel("/"), 1, col);
+        ++col;
+
+        grid->addWidget(new QLabel("Entity Name"), 0, col);
+        grid->addWidget(_entityName, 1, col);
+        ++col;
+
+
+        QVBoxLayout* layout = new QVBoxLayout();
+        setLayout(layout);
+        layout->addWidget(instruction);
+        layout->addLayout(grid);
+        layout->addWidget(_buttonBox);
+
+
+        connect(_buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
+        connect(_buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
+
+        auto enableOkIfReady = [this]()
+        {
+            _buttonBox->button(QDialogButtonBox::Ok)
+                    ->setEnabled(_providerSegmentName->text().size() > 0 and _entityName->text().size() > 0);
+        };
+        connect(_providerSegmentName, &QLineEdit::textChanged, this, enableOkIfReady);
+        connect(_entityName, &QLineEdit::textChanged, this, enableOkIfReady);
+
+        enableOkIfReady();
+    }
+
+
+    NewEntityIdDialog::~NewEntityIdDialog()
+    {
+    }
+
+
+    QString NewEntityIdDialog::providerSegmentName() const
+    {
+        return _providerSegmentName->text();
+    }
+
+
+    QString NewEntityIdDialog::entityName() const
+    {
+        return _entityName->text();
+    }
+
+
+    armem::MemoryID NewEntityIdDialog::entityIDWithoutCoreSegmentID() const
+    {
+        armem::MemoryID id;
+        id.providerSegmentName = providerSegmentName().toStdString();
+        id.entityName = entityName().toStdString();
+        return id;
+    }
+
+    armem::MemoryID NewEntityIdDialog::entityID() const
+    {
+        armem::MemoryID entityID = entityIDWithoutCoreSegmentID();
+        ARMARX_CHECK_NOT_NULL(coreSegmentID);
+        entityID.setCoreSegmentID(*coreSegmentID);
+        return entityID;
+    }
+
+}
diff --git a/source/armarx/navigation/gui-plugins/LocationGraphEditor/widgets/NewEntityIdDialog.h b/source/armarx/navigation/gui-plugins/LocationGraphEditor/widgets/NewEntityIdDialog.h
new file mode 100644
index 00000000..18659cda
--- /dev/null
+++ b/source/armarx/navigation/gui-plugins/LocationGraphEditor/widgets/NewEntityIdDialog.h
@@ -0,0 +1,64 @@
+/*
+ * This file is part of ArmarX.
+ *
+ * ArmarX is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * ArmarX 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 <http://www.gnu.org/licenses/>.
+ *
+ * @author     Rainer Kartmann ( rainer dot kartmann at kit dot edu )
+ * @date       2021
+ * @copyright  http://www.gnu.org/licenses/gpl-2.0.txt
+ *             GNU General Public License
+ */
+
+#pragma once
+
+#include <RobotAPI/libraries/armem/core/forward_declarations.h>
+
+#include <QDialog>
+
+#include <memory>
+
+
+class QLineEdit;
+class QDialogButtonBox;
+
+
+namespace armarx::nav::locgrapheditor
+{
+    class NewEntityIdDialog : public QDialog
+    {
+    public:
+
+        NewEntityIdDialog(const armem::MemoryID& coreSegmentID, QWidget* parent = nullptr);
+        virtual ~NewEntityIdDialog() override;
+
+        QString providerSegmentName() const;
+        QString entityName() const;
+
+
+        armem::MemoryID entityIDWithoutCoreSegmentID() const;
+        armem::MemoryID entityID() const;
+
+
+    private:
+
+        QLineEdit* _providerSegmentName = nullptr;
+        QLineEdit* _entityName = nullptr;
+
+        QDialogButtonBox* _buttonBox = nullptr;
+
+
+        std::unique_ptr<armem::MemoryID> coreSegmentID;
+
+    };
+
+}
diff --git a/source/armarx/navigation/gui-plugins/LocationGraphEditor/widgets/VertexTableWidget.cpp b/source/armarx/navigation/gui-plugins/LocationGraphEditor/widgets/VertexTableWidget.cpp
index 908ea14f..b1a5ceed 100644
--- a/source/armarx/navigation/gui-plugins/LocationGraphEditor/widgets/VertexTableWidget.cpp
+++ b/source/armarx/navigation/gui-plugins/LocationGraphEditor/widgets/VertexTableWidget.cpp
@@ -57,10 +57,8 @@ namespace armarx::nav::locgrapheditor
 
 
     QTableWidgetItem*
-    VertexTableWidget::addVertex(graph::Graph::ConstVertex vertex)
+    VertexTableWidget::addVertex()
     {
-        (void) vertex;
-
         // We need to disable sorting to prevent the new row from being moved away.
         setSortingEnabled(false);
 
@@ -142,12 +140,8 @@ namespace armarx::nav::locgrapheditor
             setCurrentItem(nullptr);
         }
 
-        const int numRows = rowCount();
-        int row = this->row(vertex.attrib().tableWidgetItem);
-        removeRow(row);
+        removeRow(row(vertex.attrib().tableWidgetItem));
         vertex.attrib().tableWidgetItem = nullptr;
-
-        ARMARX_CHECK_EQUAL(rowCount(), numRows - 1);
     }
 
 
@@ -158,20 +152,22 @@ namespace armarx::nav::locgrapheditor
     }
 
 
-    QString VertexTableWidget::_nameOf(QTableWidgetItem* item)
+    QString
+    VertexTableWidget::_nameOf(QTableWidgetItem* item)
     {
         return item->data(Qt::UserRole).toString();
     }
 
 
-    void VertexTableWidget::makeContextMenu(QPoint pos)
+    void
+    VertexTableWidget::makeContextMenu(QPoint pos)
     {
         QList<QTableWidgetItem*> items = selectedVertexItems();
 
         QMenu menu;
         if (items.size() == 0)
         {
-            QAction* action = menu.addAction("No locations selected");
+            QAction* action = menu.addSection("No locations selected");
             action->setEnabled(false);
         }
 
@@ -229,7 +225,7 @@ namespace armarx::nav::locgrapheditor
             using ListOfEdges = QList<QPair<Item*, Item*>>;
             using AppendFunc = std::function<void(ListOfEdges& edges, Item* selected, Item* action)>;
 
-            auto addBulkActions = [this, &items](QMenu* submenu, AppendFunc appendFunc)
+            auto addBulkAddEdgeActions = [this, &items](QMenu* submenu, AppendFunc appendFunc)
             {
                 if (items.size() == rowCount())
                 {
@@ -242,8 +238,8 @@ namespace armarx::nav::locgrapheditor
                     if (items.count(action) == 0)  // Do no generate self-edges
                     {
                         QAction* a = submenu->addAction(_nameOf(action));
-                        connect(a, &QAction::triggered,
-                                [this, &items, action, appendFunc]()
+                        connect(a, &QAction::triggered, this,
+                                [this, items, action, appendFunc]()
                         {
                             QList<QPair<QTableWidgetItem*, QTableWidgetItem*>> edges;
                             for (auto* selected : items)
@@ -256,24 +252,52 @@ namespace armarx::nav::locgrapheditor
                 }
             };
 
-            addBulkActions(menu.addMenu("Add " + edges + " " + desc + " " + utils::arrowRight + " ..."),
+            addBulkAddEdgeActions(menu.addMenu("Add " + edges + " " + desc + " " + utils::arrowRight + " ..."),
                            [](ListOfEdges& edges, Item* selected, Item* action)
             {
                 edges.append({selected, action});
             });
-            addBulkActions(menu.addMenu("Add " + edges + " " + desc + " " + utils::arrowLeft + " ..."),
+            addBulkAddEdgeActions(menu.addMenu("Add " + edges + " " + desc + " " + utils::arrowLeft + " ..."),
                            [](ListOfEdges& edges, Item* selected, Item* action)
             {
                 edges.append({action, selected});
             });
-            addBulkActions(menu.addMenu("Add " + edges + " " + desc + " " + utils::arrowBoth + " ..."),
+            addBulkAddEdgeActions(menu.addMenu("Add " + edges + " " + desc + " " + utils::arrowBoth + " ..."),
                            [](ListOfEdges& edges, Item* selected, Item* action)
             {
                 edges.append({selected, action});
                 edges.append({action, selected});
             });
+
+
+            auto connectBulkRemoveEdgeAction = [this, &items](
+                    QAction* action, utils::EdgeDirection edgeDirection)
+            {
+                connect(action, &QAction::triggered, this, [this, items, edgeDirection]()
+                {
+                    emit edgeRemovalRequested(items, edgeDirection);
+                });
+            };
+
+            connectBulkRemoveEdgeAction(
+                        menu.addAction("Remove all edges " + utils::arrowRight + " " + desc),
+                        utils::EdgeDirection::To);
+            connectBulkRemoveEdgeAction(
+                        menu.addAction("Remove all edges " + utils::arrowLeft + " " + desc),
+                        utils::EdgeDirection::From);
+            connectBulkRemoveEdgeAction(
+                        menu.addAction("Remove all edges " + utils::arrowBoth + " " + desc),
+                        utils::EdgeDirection::Bidirectional);
         }
 
+
+        menu.addSection("Manage Locations");
+        connect(menu.addAction("Create new location ..."), &QAction::triggered, this,
+                [this]()
+        {
+            emit newVertexRequested();
+        });
+
         menu.exec(mapToGlobal(pos));
     }
 
diff --git a/source/armarx/navigation/gui-plugins/LocationGraphEditor/widgets/VertexTableWidget.h b/source/armarx/navigation/gui-plugins/LocationGraphEditor/widgets/VertexTableWidget.h
index e782867f..9224c249 100644
--- a/source/armarx/navigation/gui-plugins/LocationGraphEditor/widgets/VertexTableWidget.h
+++ b/source/armarx/navigation/gui-plugins/LocationGraphEditor/widgets/VertexTableWidget.h
@@ -30,9 +30,12 @@
 #include <QTableWidget>
 
 
+namespace armarx::nav::locgrapheditor::utils
+{
+    enum class EdgeDirection;
+}
 namespace armarx::nav::locgrapheditor
 {
-
     class VertexTableWidget : public QTableWidget
     {
         Q_OBJECT
@@ -44,7 +47,7 @@ namespace armarx::nav::locgrapheditor
         VertexTableWidget();
 
 
-        QTableWidgetItem* addVertex(graph::Graph::ConstVertex vertex);
+        QTableWidgetItem* addVertex();
 
         void updateVertex(GuiGraph::Vertex vertex);
 
@@ -56,7 +59,10 @@ namespace armarx::nav::locgrapheditor
 
     signals:
 
+        void newVertexRequested();
+
         void newEdgesRequested(QList<QPair<QTableWidgetItem*, QTableWidgetItem*>> edges);
+        void edgeRemovalRequested(QList<QTableWidgetItem*> vertexItems, utils::EdgeDirection direction);
 
 
     public slots:
diff --git a/source/armarx/navigation/gui-plugins/LocationGraphEditor/widgets/utils.cpp b/source/armarx/navigation/gui-plugins/LocationGraphEditor/widgets/utils.cpp
index 1a101fff..80b75d16 100644
--- a/source/armarx/navigation/gui-plugins/LocationGraphEditor/widgets/utils.cpp
+++ b/source/armarx/navigation/gui-plugins/LocationGraphEditor/widgets/utils.cpp
@@ -37,6 +37,7 @@ namespace armarx::nav::locgrapheditor
     const QString utils::arrowBoth = QStringLiteral("\u21C4");
 
 
+
     QList<QTableWidgetItem*>
     utils::getSelectedItemsOfColumn(QTableWidget* widget, int column)
     {
diff --git a/source/armarx/navigation/gui-plugins/LocationGraphEditor/widgets/utils.h b/source/armarx/navigation/gui-plugins/LocationGraphEditor/widgets/utils.h
index ad866a66..bd1358b3 100644
--- a/source/armarx/navigation/gui-plugins/LocationGraphEditor/widgets/utils.h
+++ b/source/armarx/navigation/gui-plugins/LocationGraphEditor/widgets/utils.h
@@ -22,6 +22,7 @@
 #pragma once
 
 #include <QList>
+#include <QString>
 
 class QTableWidget;
 class QTableWidgetItem;
@@ -30,9 +31,6 @@ class QTableWidgetItem;
 namespace armarx::nav::locgrapheditor::utils
 {
 
-    QList<QTableWidgetItem*> getSelectedItemsOfColumn(QTableWidget* widget, int column);
-
-
     /// <-
     extern const QString arrowLeft;
     /// ->
@@ -41,4 +39,12 @@ namespace armarx::nav::locgrapheditor::utils
     extern const QString arrowBoth;
 
 
+    enum class EdgeDirection
+    {
+        From, To, Bidirectional
+    };
+
+
+    QList<QTableWidgetItem*> getSelectedItemsOfColumn(QTableWidget* widget, int column);
+
 }
-- 
GitLab