From 7c354b072259f10f796551923a59902263e2c199 Mon Sep 17 00:00:00 2001
From: Rainer Kartmann <rainer.kartmann@kit.edu>
Date: Mon, 23 Aug 2021 16:25:37 +0200
Subject: [PATCH] Add ArViz visu, improve change handling

---
 .../LocationGraphEditor/CMakeLists.txt        |   5 +
 .../LocationGraphEditor/GuiGraph.cpp          |  43 +-
 .../LocationGraphEditor/GuiGraph.h            |   3 +-
 .../LocationGraphEditorWidgetController.cpp   | 369 ++++++------------
 .../LocationGraphEditorWidgetController.h     |  55 +--
 .../gui-plugins/LocationGraphEditor/Visu.cpp  | 121 ++++++
 .../gui-plugins/LocationGraphEditor/Visu.h    |  88 +++++
 .../widgets/EdgeTableWidget.cpp               |  19 +-
 .../widgets/EdgeTableWidget.h                 |   5 +-
 .../widgets/VertexDataWidget.cpp              |  85 ++--
 .../widgets/VertexDataWidget.h                |  13 +-
 .../widgets/VertexTableWidget.cpp             |  53 ++-
 .../widgets/VertexTableWidget.h               |   4 +-
 .../widgets/default_colors.cpp                |   2 +-
 .../widgets/graph_scene/Scene.cpp             |   4 +-
 .../widgets/graph_scene/Scene.h               |   4 +-
 .../widgets/graph_scene/Widget.cpp            |  45 +++
 .../LocationGraphEditor/widgets/utils.cpp     |  53 +++
 .../LocationGraphEditor/widgets/utils.h       |  35 ++
 19 files changed, 631 insertions(+), 375 deletions(-)
 create mode 100644 source/armarx/navigation/gui-plugins/LocationGraphEditor/Visu.cpp
 create mode 100644 source/armarx/navigation/gui-plugins/LocationGraphEditor/Visu.h
 create mode 100644 source/armarx/navigation/gui-plugins/LocationGraphEditor/widgets/utils.cpp
 create mode 100644 source/armarx/navigation/gui-plugins/LocationGraphEditor/widgets/utils.h

diff --git a/source/armarx/navigation/gui-plugins/LocationGraphEditor/CMakeLists.txt b/source/armarx/navigation/gui-plugins/LocationGraphEditor/CMakeLists.txt
index d2661dba..0820ee1b 100644
--- a/source/armarx/navigation/gui-plugins/LocationGraphEditor/CMakeLists.txt
+++ b/source/armarx/navigation/gui-plugins/LocationGraphEditor/CMakeLists.txt
@@ -15,8 +15,10 @@ set(SOURCES
 
     FunctionalEventFilter.cpp
     GuiGraph.cpp
+    Visu.cpp
 
     widgets/default_colors.cpp
+    widgets/utils.cpp
     widgets/EdgeTableWidget.cpp
     widgets/VertexDataWidget.cpp
     widgets/VertexTableWidget.cpp
@@ -30,8 +32,10 @@ set(HEADERS
 
     FunctionalEventFilter.h
     GuiGraph.h
+    Visu.h
 
     widgets/default_colors.h
+    widgets/utils.h
     widgets/EdgeTableWidget.h
     widgets/VertexDataWidget.h
     widgets/VertexTableWidget.h
@@ -52,6 +56,7 @@ set(COMPONENT_LIBS
     SimpleConfigDialog
 
     # RobotAPI
+    ArViz
     armem
 
     Navigation::Location
diff --git a/source/armarx/navigation/gui-plugins/LocationGraphEditor/GuiGraph.cpp b/source/armarx/navigation/gui-plugins/LocationGraphEditor/GuiGraph.cpp
index f4fd3e1d..49361f44 100644
--- a/source/armarx/navigation/gui-plugins/LocationGraphEditor/GuiGraph.cpp
+++ b/source/armarx/navigation/gui-plugins/LocationGraphEditor/GuiGraph.cpp
@@ -59,24 +59,47 @@ namespace armarx::nav::locgrapheditor
         return map;
     }
 
+
+    std::map<QTableWidgetItem*, GuiGraph::Edge>
+    GuiGraph::getTableItemToEdgeMap()
+    {
+        std::map<QTableWidgetItem*, GuiGraph::Edge> map;
+
+        for (auto edge : edges())
+        {
+            if (edge.attrib().tableWidgetItem != nullptr)
+            {
+                map[edge.attrib().tableWidgetItem] = edge;
+            }
+        }
+
+        return map;
+    }
+
 }
 
 namespace armarx::nav
 {
 
-float locgrapheditor::getYawAngleDegree(const Eigen::Matrix4f& pose)
-{
-    Eigen::Vector3f rpy = simox::math::mat4f_to_rpy(pose);
-    return simox::math::rad_to_deg(rpy[2]);
-}
+    float locgrapheditor::getYawAngleDegree(const Eigen::Matrix4f& pose)
+    {
+        return simox::math::rad_to_deg(simox::math::mat4f_to_rpy(pose)(2));
+    }
 
-auto locgrapheditor::toGuiGraph(const graph::Graph& nav) -> GuiGraph
-{
-    GuiGraph gui;
-    for (auto v : nav.vertices())
+
+    double locgrapheditor::getYawAngleDegree(const Eigen::Matrix4d& pose)
     {
-        gui.addVertex(v.objectID(), { v.attrib() });
+        return simox::math::rad_to_deg(simox::math::mat4f_to_rpy(pose)(2));
     }
+
+
+    auto locgrapheditor::toGuiGraph(const graph::Graph& nav) -> GuiGraph
+    {
+        GuiGraph gui;
+        for (auto v : nav.vertices())
+        {
+            gui.addVertex(v.objectID(), { v.attrib() });
+        }
         for (auto e : nav.edges())
         {
             gui.addEdge(e.sourceObjectID(), e.targetObjectID(), { e.attrib() });
diff --git a/source/armarx/navigation/gui-plugins/LocationGraphEditor/GuiGraph.h b/source/armarx/navigation/gui-plugins/LocationGraphEditor/GuiGraph.h
index 87395a1f..33f4ddf5 100644
--- a/source/armarx/navigation/gui-plugins/LocationGraphEditor/GuiGraph.h
+++ b/source/armarx/navigation/gui-plugins/LocationGraphEditor/GuiGraph.h
@@ -86,7 +86,7 @@ namespace armarx::nav::locgrapheditor
         std::optional<Vertex> getVertexFromTableItem(QTableWidgetItem* item);
 
         std::map<QTableWidgetItem*, Vertex> getTableItemToVertexMap();
-
+        std::map<QTableWidgetItem*, Edge> getTableItemToEdgeMap();
     };
 
 
@@ -96,5 +96,6 @@ namespace armarx::nav::locgrapheditor
 
 
     float getYawAngleDegree(const Eigen::Matrix4f& pose);
+    double getYawAngleDegree(const Eigen::Matrix4d& pose);
 
 }
diff --git a/source/armarx/navigation/gui-plugins/LocationGraphEditor/LocationGraphEditorWidgetController.cpp b/source/armarx/navigation/gui-plugins/LocationGraphEditor/LocationGraphEditorWidgetController.cpp
index e23d6748..59de014f 100644
--- a/source/armarx/navigation/gui-plugins/LocationGraphEditor/LocationGraphEditorWidgetController.cpp
+++ b/source/armarx/navigation/gui-plugins/LocationGraphEditor/LocationGraphEditorWidgetController.cpp
@@ -20,7 +20,10 @@
  *             GNU General Public License
  */
 
+#include <VirtualRobot/VirtualRobot.h>
+
 #include "LocationGraphEditorWidgetController.h"
+#include "Visu.h"
 #include "widgets/EdgeTableWidget.h"
 #include "widgets/VertexDataWidget.h"
 #include "widgets/VertexTableWidget.h"
@@ -35,6 +38,7 @@
 #include <armarx/navigation/graph/aron/Graph.aron.generated.h>
 
 #include <RobotAPI/libraries/armem/client/MemoryNameSystem.h>
+#include <RobotAPI/components/ArViz/Client/Client.h>
 
 #include <ArmarXCore/core/exceptions/local/ExpressionException.h>
 
@@ -54,17 +58,6 @@
 #include <sstream>
 
 
-/**
- * @brief The increment used when a rotation button is pressed.
- *
- * A positive rotation is counter clockwise.
- * This value should be positive.
- *
- * rotation buttons: LocationGraphEditorWidgetController::widget.buttonRotateClock and
- * LocationGraphEditorWidgetController::widget.buttonRotateCounterClock
- */
-static const float VIEW_ROTATE_STEP_SIZE_CC = 45;
-
 static const QString SETTING_LAST_SCENE = "lastScene";
 
 
@@ -125,58 +118,36 @@ namespace armarx::nav::locgrapheditor
         view.graph = new GraphSceneWidget();
         widget.graphSceneLayout->addWidget(view.graph);
 
-#if 0
-        auto eventFilter = [this](QObject* obj, QEvent* event) -> bool
-        {
-            if (obj == this->view.view && event->type() == QEvent::MouseButtonPress)
-            {
-                QMouseEvent* me = static_cast<QMouseEvent*>(event);
-                if (me->button() == Qt::LeftButton)
-                {
-                    QPointF scenePoint = this->view.view->mapToScene(me->pos());
-                    scenePoint.setY(- scenePoint.y());  // not sure why
-
-                    float minDist = std::numeric_limits<float>::max();
-                    auto bestIt = this->vertices.cend();
-
-                    for (auto it = this->vertices.cbegin(); it != this->vertices.cend(); ++it)
-                    {
-                        float deltaX = it->second.pose->position->x - scenePoint.x();
-                        float deltaY = it->second.pose->position->y - scenePoint.y();
-                        float dist = std::sqrt(deltaX * deltaX + deltaY * deltaY);
-
-                        if (dist < minDist)
-                        {
-                            minDist = dist;
-                            bestIt = it;
-                        }
-                    }
-
-                    if (bestIt != this->vertices.cend())
-                    {
-                        this->highlightVertex(bestIt->first);
-                    }
-                }
-            }
-            else if (event->type() == QEvent::Resize)
-            {
-                this->adjustView();
-            }
-            else
-            {
-                return false;
-            }
-        };
-        view.view->installEventFilter(new simox::gui::FunctionalEventFilter(eventFilter));
-#endif
 
         // 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);
 
 
+        // Update views
+        connect(this, &This::graphChanged, this, &This::updateGraphView);
+        connect(view.vertexData, &VertexDataWidget::vertexDataChanged,
+                this, &This::updateGraphView);
+
+        // Selection
+        connect(view.vertexTable, &VertexTableWidget::currentItemChanged,
+                [this](QTableWidgetItem* current, QTableWidgetItem* previous)
+        {
+            (void) previous;
+            this->selectVertex(current);
+        });
+
+        connect(view.vertexTable, &VertexTableWidget::itemSelectionChanged,
+                this, &This::updateVertexHighlighting);
+        connect(view.edgeTable, &EdgeTableWidget::itemSelectionChanged,
+                this, &This::updateEdgeHighlighting);
+
+
 #if 0
         // Tables
         connect(view.vertexTable, &QTableWidget::cellDoubleClicked, this, &This::vertexTableDoubleClicked);
@@ -190,30 +161,6 @@ namespace armarx::nav::locgrapheditor
         connect(widget.btnAddEdgeEndStart, &QPushButton::pressed, this, &This::addNewEdgeEndStart);
         connect(widget.btnEdit, &QPushButton::pressed, this, &This::editGraphVertex);
 #endif
-
-        // View
-
-        // connect(view.graph->scene(), &GraphScene::vertexSelected, this, &This::toggleVertexSelected);
-
-        // This -> This
-
-        // Intra-connections.
-        connect(this, &This::connected, this, &This::queryGraphs);
-        connect(this, &This::memoryDataChanged, this, &This::updateGraphList);
-
-
-        connect(view.vertexData, &VertexDataWidget::vertexDataChanged,
-                this, &This::updateVertexView);
-
-        connect(view.vertexTable, &VertexTableWidget::currentItemChanged,
-                [this](QTableWidgetItem* current, QTableWidgetItem* previous)
-        {
-            (void) previous;
-            this->selectVertex(current);
-        });
-
-        connect(view.vertexTable, &VertexTableWidget::itemSelectionChanged,
-                this, &This::updateVertexHighlighting);
     }
 
 
@@ -299,6 +246,8 @@ namespace armarx::nav::locgrapheditor
 
         graphReader = memoryNameSystem.useReader(nav::graph::coreSegmentID);
         graphWriter = memoryNameSystem.useWriter(nav::graph::coreSegmentID);
+
+        arviz = std::make_unique<viz::Client>(viz::Client::createForGuiPlugin(parent));
     }
 
 
@@ -307,7 +256,7 @@ namespace armarx::nav::locgrapheditor
         armem::client::QueryResult result = remote.graphReader.getLatestSnapshotsIn(nav::graph::coreSegmentID);
         if (result.success)
         {
-            data.memory = std::move(result.memory);
+            model.memory = std::move(result.memory);
             emit memoryDataChanged();
         }
         else
@@ -322,7 +271,7 @@ namespace armarx::nav::locgrapheditor
         widget.graphsComboBox->clear();
 
         bool enable = false;
-        data.memory.forEachEntity([this, &enable](const armem::wm::Entity& entity)
+        model.memory.forEachEntity([this, &enable](const armem::wm::Entity& entity)
         {
             widget.graphsComboBox->addItem(QString::fromStdString(entity.id().str()));
             enable = true;
@@ -333,18 +282,8 @@ namespace armarx::nav::locgrapheditor
 
     void LocationGraphEditorWidgetController::loadGraph()
     {
-        // Store vertex highlighting.
-        std::vector<semrel::ShapeID> highlightedVertices;
-        for (auto vertex : data.graph.vertices())
-        {
-            if (vertex.attrib().highlighted)
-            {
-                highlightedVertices.push_back(vertex.objectID());
-            }
-        }
-
         const armem::MemoryID entityID = armem::MemoryID::fromString(widget.graphsComboBox->currentText().toStdString());
-        const armem::wm::EntityInstance* instance = data.memory.findLatestInstance(entityID);
+        const armem::wm::EntityInstance* instance = model.memory.findLatestInstance(entityID);
         if (not instance)
         {
             std::stringstream ss;
@@ -359,30 +298,39 @@ namespace armarx::nav::locgrapheditor
             fromAron(dto, nav);
         }
 
-        clearGraph();
+        setGraph(nav);
+    }
+
+
+    void LocationGraphEditorWidgetController::setGraph(graph::Graph& nav)
+    {
+        // Store vertex highlighting.
+        std::set<std::string> highlightedVertices;
+        for (auto vertex : model.graph.vertices())
+        {
+            if (vertex.attrib().highlighted)
+            {
+                highlightedVertices.insert(vertex.attrib().getName());
+            }
+        }
 
-        // Add vertices
+        // Build graph.
+        clearGraph();
         for (auto vertex : nav.vertices())
         {
             addVertex(vertex);
         }
-
-        // Add edges
         for (auto edge : nav.edges())
         {
             addEdge(edge);
         }
 
         // Restore vertex highlighting.
-        for (const semrel::ShapeID& vertexID : highlightedVertices)
+        for (auto vertex : model.graph.vertices())
         {
-            for (auto vertex : data.graph.vertices())
+            if (highlightedVertices.count(vertex.attrib().getName()))
             {
-                if (vertex.objectID() == vertexID)
-                {
-                    vertex.attrib().highlighted = true;
-                    updateVertexView(vertex);
-                }
+                vertex.attrib().highlighted = true;
             }
         }
 
@@ -391,42 +339,52 @@ namespace armarx::nav::locgrapheditor
 
 
     GuiGraph::Vertex
-    LocationGraphEditorWidgetController::addVertex(graph::Graph::Vertex vertex)
+    LocationGraphEditorWidgetController::addVertex(graph::Graph::ConstVertex vertex)
     {
-        ARMARX_CHECK(not data.graph.hasVertex(vertex.objectID()));
+        ARMARX_CHECK(not model.graph.hasVertex(vertex.objectID()));
 
         VertexData attrib { vertex.attrib() };
         attrib.graphicsItem = view.graph->scene()->addVertex(vertex);
         attrib.tableWidgetItem = view.vertexTable->addVertex(vertex);
-        auto guiVertex = data.graph.addVertex(vertex.objectID(), attrib);
-
-        updateVertexView(guiVertex);
-        return guiVertex;
+        return model.graph.addVertex(vertex.objectID(), attrib);
     }
 
 
     GuiGraph::Edge
-    LocationGraphEditorWidgetController::addEdge(graph::Graph::Edge edge)
+    LocationGraphEditorWidgetController::addEdge(graph::Graph::ConstEdge edge)
     {
-        ARMARX_CHECK(not data.graph.hasEdge(edge.sourceObjectID(), edge.targetObjectID()))
+        ARMARX_CHECK(not model.graph.hasEdge(edge.sourceObjectID(), edge.targetObjectID()))
                 << "Edge must not exist before being added: '"
                 << edge.source().attrib().getName() << "' -> '"
                 << edge.target().attrib().getName() << "'";
 
-        auto source = data.graph.vertex(edge.sourceObjectID());
-        auto target = data.graph.vertex(edge.targetObjectID());
+        auto source = model.graph.vertex(edge.sourceObjectID());
+        auto target = model.graph.vertex(edge.targetObjectID());
 
         EdgeData attrib { edge.attrib() };
         attrib.tableWidgetItem = view.edgeTable->addEdge(edge);
         attrib.graphicsItem = view.graph->scene()->addEdge(edge);
-        auto guiEdge = data.graph.addEdge(source, target, attrib);
+        return model.graph.addEdge(source, target, attrib);
+    }
+
 
-        updateEdgeView(guiEdge);
-        return guiEdge;
+
+    void LocationGraphEditorWidgetController::updateGraphView()
+    {
+        for (auto vertex : model.graph.vertices())
+        {
+            updateVertexView(vertex);
+        }
+        for (auto edge : model.graph.edges())
+        {
+            updateEdgeView(edge);
+        }
+        updateArViz();
     }
 
 
-    void LocationGraphEditorWidgetController::updateVertexView(GuiGraph::Vertex vertex)
+    void
+    LocationGraphEditorWidgetController::updateVertexView(GuiGraph::Vertex vertex)
     {
         view.graph->scene()->updateVertex(vertex);
         view.vertexTable->updateVertex(vertex);
@@ -435,7 +393,7 @@ namespace armarx::nav::locgrapheditor
         {
             // Highlight all edges between highlighted vertices
             std::set<semrel::ShapeID> highlightedVertices;
-            for (auto v : data.graph.vertices())
+            for (auto v : model.graph.vertices())
             {
                 if (v.attrib().highlighted)
                 {
@@ -443,7 +401,7 @@ namespace armarx::nav::locgrapheditor
                 }
             }
 
-            for (auto edge : data.graph.edges())
+            for (auto edge : model.graph.edges())
             {
                 bool verticesHighlighted =
                         highlightedVertices.count(edge.sourceObjectID())
@@ -454,54 +412,36 @@ namespace armarx::nav::locgrapheditor
                 if (edge.attrib().highlighted != verticesHighlighted)
                 {
                     edge.attrib().highlighted = verticesHighlighted;
-                    updateEdgeView(edge);
                 }
             }
         }
-
-        // ToDo: Update ArViz
-#if 0
-        float yaw = getYawAngleDegree(attrib.pose) / 180 * M_PI;
-        Eigen::AngleAxisf aa(yaw, Eigen::Vector3f(0, 0, 1));
-        Eigen::Vector3f dir {0, 1, 0};
-        dir = aa.toRotationMatrix() * dir;
-        debugDrawer->setArrowVisu(debugDrawerLayerName, iceName(id), attrib.pose->position,
-                                  new armarx::Vector3(dir),
-                                  armarx::DrawColor {0, 0, 1, 1},
-                                  100,
-                                  lineWidth);
-        debugDrawer->setTextVisu(debugDrawerLayerName, iceName(id) + "text", attrib.vertex->getName(),
-                                 attrib.pose->position, armarx::DrawColor {0, 0, 1, 1}, 10);
-#endif
     }
 
 
-    void LocationGraphEditorWidgetController::updateEdgeView(GuiGraph::Edge edge)
+    void
+    LocationGraphEditorWidgetController::updateEdgeView(GuiGraph::Edge edge)
     {
         view.graph->scene()->updateEdge(edge);
         view.edgeTable->updateEdge(edge);
+    }
 
-#if 0
-        armarx::Vector3Ptr posStart = new armarx::Vector3(armarx::Vector3Ptr::dynamicCast(vertices.at(id.first).pose->position)->toEigen());
-        posStart->z += posStart->z < 1 ? 10 : 0;
-        armarx::Vector3Ptr posEnd = new armarx::Vector3(armarx::Vector3Ptr::dynamicCast(vertices.at(id.attrib()).pose->position)->toEigen());
-        posEnd->z += posEnd->z < 1 ? 10 : 0;
-        // debug layer
-        // ToDo: Update ArViz
-        debugDrawer->setLineVisu(debugDrawerLayerName,
-                                 iceName(id),
-                                 posStart,
-                                 posEnd,
-                                 lineWidth,
-                                 color);
-#endif
+
+    void LocationGraphEditorWidgetController::updateArViz()
+    {
+        if (remote.arviz)
+        {
+            viz::Layer layer = remote.arviz->layer(widget.graphsComboBox->currentText().toStdString());
+            GraphVisu visu;
+            visu.draw(layer, model.graph);
+            remote.arviz->commit(layer);
+        }
     }
 
 
     void LocationGraphEditorWidgetController::clearEdges()
     {
         // Remove from graphics scene
-        for (auto edge : data.graph.edges())
+        for (auto edge : model.graph.edges())
         {
             view.graph->scene()->removeEdge(edge.attrib().graphicsItem);
         }
@@ -511,12 +451,12 @@ namespace armarx::nav::locgrapheditor
         view.edgeTable->setRowCount(0);
 
         // Clear data structure
-        while (data.graph.edges().begin() != data.graph.edges().end())
+        while (model.graph.edges().begin() != model.graph.edges().end())
         {
-            data.graph.removeEdge(*data.graph.edges().begin());
+            model.graph.removeEdge(*model.graph.edges().begin());
         }
 
-        // ToDo: Update ArViz
+        emit graphChanged();
     }
 
 
@@ -532,148 +472,71 @@ namespace armarx::nav::locgrapheditor
         view.vertexTable->setRowCount(0);
 
         // Clear data structure
-        data.graph.clear();
-    }
-
+        model.graph.clear();
 
-    void LocationGraphEditorWidgetController::toggleVertexSelected(const semrel::ShapeID& vertexID)
-    {
-        auto vertex = data.graph.vertex(vertexID);
-        vertex.attrib().highlighted ^= true;
-        updateVertexView(vertex);
+        emit graphChanged();
     }
 
 
     void LocationGraphEditorWidgetController::resetHighlighting()
     {
-        for (auto vertex : data.graph.vertices())
+        for (auto vertex : model.graph.vertices())
         {
             if (vertex.attrib().highlighted)
             {
                 vertex.attrib().highlighted = false;
-                updateVertexView(vertex);
             }
         }
-        for (auto edge : data.graph.edges())
+        for (auto edge : model.graph.edges())
         {
             if (edge.attrib().highlighted)
             {
                 edge.attrib().highlighted = false;
-                updateEdgeView(edge);
             }
         }
+
+        emit graphChanged();
     }
 
 
-    void LocationGraphEditorWidgetController::updateVertexHighlighting()
+    template <class T>
+    static
+    void updateElementHighlighting(
+            QList<QTableWidgetItem*> selectedItems,
+            std::map<QTableWidgetItem*, T>&& itemToElementMap)
     {
-        std::map<QTableWidgetItem*, GuiGraph::Vertex> map = data.graph.getTableItemToVertexMap();
-
-        for (QTableWidgetItem* selected : view.vertexTable->selectedEdgeItems())
+        for (QTableWidgetItem* selected : selectedItems)
         {
-            if (auto it = map.find(selected); it != map.end())
+            if (auto it = itemToElementMap.find(selected); it != itemToElementMap.end())
             {
                 it->second.attrib().highlighted = true;
-                updateVertexView(it->second);
-                map.erase(it);
+                itemToElementMap.erase(it);
             }
         }
-        for (auto& [_, unselected] : map)
+        for (auto& [_, unselected] : itemToElementMap)
         {
             unselected.attrib().highlighted = false;
-            updateVertexView(unselected);
         }
     }
 
 
-#if 0
-
-
-    void LocationGraphEditorWidgetController::highlightVertex(const std::string& vertexId, bool highlighted)
-    {
-        if (!hasVertex(vertexId))
-        {
-            ARMARX_WARNING << "No vertex: " << vertexId << " [pushBfile: " << __FILE__ << " | line: " << __LINE__ << " | function: " << __PRETTY_FUNCTION__ << "]";
-            return;
-        }
-
-        if (vertices.at(vertexId).highlighted != highlighted)
-        {
-            vertices.at(vertexId).highlighted = highlighted;
-            updateVertex(vertexId);
-        }
-    }
-
-
-    void LocationGraphEditorWidgetController::highlightEdge(const std::string& vertex1Id, const std::string& vertex2Id, bool highlighted)
-    {
-        if (!hasEdge(vertex1Id, vertex2Id))
-        {
-            ARMARX_WARNING << "No edge for: " << vertex1Id << " and " << vertex2Id << " [file: " << __FILE__ << " | line: " << __LINE__ << " | function: " << __PRETTY_FUNCTION__ << "]";
-            return;
-        }
-
-        EdgeId edge = toEdge(vertex1Id, vertex2Id);
-
-        if (edges.at(edge).highlighted != highlighted)
-        {
-            edges.at(edge).highlighted = highlighted;
-            updateEdge(edge);
-        }
-    }
-
-
-    void LocationGraphEditorWidgetController::vertexTableDoubleClicked(int row, int)
-    {
-        auto vertexIt = std::find_if(vertices.cbegin(), vertices.cend(), [&](const std::pair<std::string, VertexData>& d)
-        {
-            //        return d.attrib().vertex->getName() == view.vertexTable->item(row, 0)->text().toStdString();
-            return d.attrib().tableWidgetVerticesIndex == row;
-        });
-        auto vertexId = vertexIt->second.vertex->getId();
-        highlightVertex(vertexId);
-    }
-
-
-    void LocationGraphEditorWidgetController::edgeTableDoubleClicked(int row, int)
-    {
-        auto edgeIt = std::find_if(edges.cbegin(), edges.cend(), [&](const std::pair<EdgeId, EdgeData>& d)
-        {
-            return d.attrib().tableWidgetEdgesIndex == row;
-        });
-
-        edgeDoubleClicked(edgeIt->first);
-    }
-
-
-
-    void LocationGraphEditorWidgetController::edgeDoubleClicked(GuiGraph::Edge edge)
+    void LocationGraphEditorWidgetController::updateVertexHighlighting()
     {
-        //    edges.at(id).highlighted ^= true;
-        //    updateEdge(id);
-        bool highlight = !vertices.at(edge.first).highlighted && !vertices.at(edge.attrib()).highlighted;
-        vertices.at(edge.first).highlighted = highlight;
-        vertices.at(edge.attrib()).highlighted = highlight;
-        updateVertex(edge.first);
-        updateVertex(edge.attrib());
+        updateElementHighlighting(view.vertexTable->selectedVertexItems(), model.graph.getTableItemToVertexMap());
+        emit graphChanged();
     }
 
-#endif
 
-
-#if 0
-    void LocationGraphEditorWidgetController::refreshGraph()
+    void LocationGraphEditorWidgetController::updateEdgeHighlighting()
     {
-        clearGraph();
-        loadGraph();
+        updateElementHighlighting(view.edgeTable->selectedEdgeItems(), model.graph.getTableItemToEdgeMap());
+        emit graphChanged();
     }
 
-#endif
-
 
     void LocationGraphEditorWidgetController::selectVertex(QTableWidgetItem* vertexItem)
     {
-        if (auto vertex = data.graph.getVertexFromTableItem(vertexItem))
+        if (auto vertex = model.graph.getVertexFromTableItem(vertexItem))
         {
             selectVertex(vertex.value());
         }
diff --git a/source/armarx/navigation/gui-plugins/LocationGraphEditor/LocationGraphEditorWidgetController.h b/source/armarx/navigation/gui-plugins/LocationGraphEditor/LocationGraphEditorWidgetController.h
index ded79af1..fe161065 100644
--- a/source/armarx/navigation/gui-plugins/LocationGraphEditor/LocationGraphEditorWidgetController.h
+++ b/source/armarx/navigation/gui-plugins/LocationGraphEditor/LocationGraphEditorWidgetController.h
@@ -41,12 +41,17 @@
 #include <ArmarXCore/core/Component.h>
 #include <ArmarXCore/core/system/ImportExportComponent.h>
 
+#include <memory>
 #include <string>
 
 
 class QDialog;
 
 
+namespace armarx::viz
+{
+    class Client;
+}
 namespace armarx::nav::locgrapheditor
 {
     class EdgeTableWidget;
@@ -133,8 +138,9 @@ namespace armarx::nav::locgrapheditor
         void updateGraphList();
         void loadGraph();
 
-        GuiGraph::Vertex addVertex(graph::Graph::Vertex vertex);
-        GuiGraph::Edge addEdge(graph::Graph::Edge edge);
+        void setGraph(graph::Graph& nav);
+        GuiGraph::Vertex addVertex(graph::Graph::ConstVertex vertex);
+        GuiGraph::Edge addEdge(graph::Graph::ConstEdge edge);
 
         void clearEdges();
         void clearGraph();
@@ -142,8 +148,10 @@ namespace armarx::nav::locgrapheditor
 
         // View & Tables
 
+        void updateGraphView();
         void updateVertexView(GuiGraph::Vertex vertex);
         void updateEdgeView(GuiGraph::Edge edge);
+        void updateArViz();
 
 
         // Selection
@@ -153,7 +161,7 @@ namespace armarx::nav::locgrapheditor
 
         void resetHighlighting();
         void updateVertexHighlighting();
-        void toggleVertexSelected(const semrel::ShapeID& vertexID);
+        void updateEdgeHighlighting();
 
 
     private:
@@ -174,18 +182,19 @@ namespace armarx::nav::locgrapheditor
             armem::client::Reader graphReader;
             armem::client::Writer graphWriter;
 
+            std::unique_ptr<viz::Client> arviz;
 
             void connect(Component& parent);
         };
         Remote remote;
 
 
-        struct Data
+        struct Model
         {
             armem::wm::Memory memory;
             GuiGraph graph;
         };
-        Data data;
+        Model model;
 
 
         struct View
@@ -204,11 +213,6 @@ namespace armarx::nav::locgrapheditor
 #if 0
     private slots:
 
-        void highlightEdge(const std::string& vertex1Id, const std::string& vertex2Id, bool highlighted = true);
-        void highlightVertex(const std::string& vertexId, bool highlighted = true);
-
-        void refreshGraph();
-
         void tableWidgetVerticesCustomContextMenu(QPoint pos);
         void tableWidgetEdgesCustomContextMenu(QPoint pos);
 
@@ -218,37 +222,6 @@ namespace armarx::nav::locgrapheditor
         /// Add kitchen graph (H2T Armar3a robot kitchen)
         void addKitchenGraph();
 
-        /**
-         * @brief Toggles the double clicked vertex's selection state.
-         * @param row Identifies the vertex.
-         */
-        void vertexTableDoubleClicked(int row, int);
-        /**
-         * @brief Toggles the double clicked edge's selection state.
-         * @param row Identifies the edge.
-         */
-        void edgeTableDoubleClicked(int row, int);
-
-        /**
-         * @brief Toggles the double clicked edge's selection state.
-         * @param id Identifies the edge.
-         */
-        void edgeDoubleClicked(GuiGraph::Edge edge);
-
-        /**
-         * @brief Rotates the view clockwise.
-         *
-         * Stepsize set by VIEW_ROTATE_STEP_SIZE_CC in GraphVisualizerGuiPlugin.cpp
-         */
-        void viewRotatedClock();
-
-        /**
-         * @brief Rotates the view counter clockwise.
-         *
-         * Stepsize set by VIEW_ROTATE_STEP_SIZE_CC in GraphVisualizerGuiPlugin.cpp
-         */
-        void viewRotatedCounterClock();
-
         bool addNewEdge(const std::string& from, const std::string& to);
         void addNewEdgeBoth();
         void addNewEdgeStartEnd();
diff --git a/source/armarx/navigation/gui-plugins/LocationGraphEditor/Visu.cpp b/source/armarx/navigation/gui-plugins/LocationGraphEditor/Visu.cpp
new file mode 100644
index 00000000..d978ffb6
--- /dev/null
+++ b/source/armarx/navigation/gui-plugins/LocationGraphEditor/Visu.cpp
@@ -0,0 +1,121 @@
+/*
+ * 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 <VirtualRobot/VirtualRobot.h>
+
+#include "Visu.h"
+
+#include <RobotAPI/components/ArViz/Client/Client.h>
+
+#include <SimoxUtility/color/Color.h>
+#include <SimoxUtility/math/pose.h>
+
+
+namespace armarx::nav::locgrapheditor
+{
+
+    const simox::Color visu::defaultColorHighlighted = simox::Color::orange();
+
+
+    viz::Pose VertexVisu::Pose::draw(const VertexData& attribs) const
+    {
+        viz::Pose pose = nav::graph::VertexVisu::Pose::draw(attribs);
+        if (attribs.highlighted)
+        {
+            pose.scale(scale * scaleFactorHighlighted);
+        }
+        return pose;
+    }
+
+
+    viz::Arrow VertexVisu::ForwardArrow::draw(const VertexData& attribs) const
+    {
+        viz::Arrow arrow = nav::graph::VertexVisu::ForwardArrow::draw(attribs);
+        if (attribs.highlighted)
+        {
+            arrow.color(colorHighlighted);
+            arrow.width(width * 1.5f);
+        }
+        return arrow;
+    }
+
+
+    void VertexVisu::draw(viz::Layer& layer, const VertexData& attribs) const
+    {
+        if (pose.has_value())
+        {
+            layer.add(pose->draw(attribs));
+        }
+        if (forwardArrow.has_value())
+        {
+            layer.add(forwardArrow->draw(attribs));
+        }
+    }
+
+
+    viz::Arrow EdgeVisu::Arrow::draw(GuiGraph::ConstEdge edge) const
+    {
+        return draw(edge.attrib(), edge.source().attrib(), edge.target().attrib());
+    }
+
+
+    viz::Arrow EdgeVisu::Arrow::draw(
+            const EdgeData& edge,
+            const VertexData& source,
+            const VertexData& target) const
+    {
+        viz::Arrow arrow = nav::graph::EdgeVisu::Arrow::draw(edge, source, target);
+        if (edge.highlighted)
+        {
+            arrow.color(colorHighlighted);
+        }
+        return arrow;
+    }
+
+
+    void EdgeVisu::draw(viz::Layer& layer, GuiGraph::ConstEdge edge) const
+    {
+        if (arrow.has_value())
+        {
+            layer.add(arrow->draw(edge));
+        }
+    }
+
+
+    void GraphVisu::draw(viz::Layer& layer, const GuiGraph& graph)
+    {
+        if (vertex.has_value())
+        {
+            for (GuiGraph::ConstVertex v : graph.vertices())
+            {
+                vertex->draw(layer, v.attrib());
+            }
+        }
+        if (edge.has_value())
+        {
+            for (GuiGraph::ConstEdge e : graph.edges())
+            {
+                edge->draw(layer, e);
+            }
+        }
+    }
+
+}
diff --git a/source/armarx/navigation/gui-plugins/LocationGraphEditor/Visu.h b/source/armarx/navigation/gui-plugins/LocationGraphEditor/Visu.h
new file mode 100644
index 00000000..5e77c8be
--- /dev/null
+++ b/source/armarx/navigation/gui-plugins/LocationGraphEditor/Visu.h
@@ -0,0 +1,88 @@
+/*
+ * 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 "GuiGraph.h"
+
+#include <armarx/navigation/graph/Visu.h>
+
+#include <SimoxUtility/color/Color.h>
+
+#include <optional>
+
+
+namespace armarx::nav::locgrapheditor::visu
+{
+    extern const simox::Color defaultColorHighlighted;
+}
+namespace armarx::nav::locgrapheditor
+{
+
+    struct VertexVisu
+    {
+        struct Pose : public nav::graph::VertexVisu::Pose
+        {
+            float scaleFactorHighlighted = 1.5;
+
+            viz::Pose draw(const VertexData& attribs) const;
+        };
+        std::optional<Pose> pose = Pose {};
+
+        struct ForwardArrow : public nav::graph::VertexVisu::ForwardArrow
+        {
+            simox::Color colorHighlighted = visu::defaultColorHighlighted;
+
+            viz::Arrow draw(const VertexData& attribs) const;
+        };
+        std::optional<ForwardArrow> forwardArrow = ForwardArrow {};
+
+
+        void draw(viz::Layer& layer, const VertexData& attribs) const;
+    };
+
+
+    struct EdgeVisu
+    {
+        struct Arrow : public nav::graph::EdgeVisu::Arrow
+        {
+            simox::Color colorHighlighted = visu::defaultColorHighlighted;
+
+            viz::Arrow draw(GuiGraph::ConstEdge edge) const;
+            viz::Arrow draw(const EdgeData& edge, const VertexData& source, const VertexData& target) const;
+        };
+        std::optional<Arrow> arrow = Arrow {};
+
+
+        void draw(viz::Layer& layer, GuiGraph::ConstEdge edge) const;
+    };
+
+
+    struct GraphVisu
+    {
+        std::optional<VertexVisu> vertex = VertexVisu {};
+        std::optional<EdgeVisu> edge = EdgeVisu {};
+
+
+        void draw(viz::Layer& layer, const GuiGraph& graph);
+    };
+
+}
diff --git a/source/armarx/navigation/gui-plugins/LocationGraphEditor/widgets/EdgeTableWidget.cpp b/source/armarx/navigation/gui-plugins/LocationGraphEditor/widgets/EdgeTableWidget.cpp
index 09a1b2b1..6c60fac3 100644
--- a/source/armarx/navigation/gui-plugins/LocationGraphEditor/widgets/EdgeTableWidget.cpp
+++ b/source/armarx/navigation/gui-plugins/LocationGraphEditor/widgets/EdgeTableWidget.cpp
@@ -21,6 +21,10 @@
 
 #include "EdgeTableWidget.h"
 
+#include "utils.h"
+
+#include <ArmarXCore/core/logging/Logging.h>
+
 #include <QHeaderView>
 
 
@@ -33,17 +37,23 @@ namespace armarx::nav::locgrapheditor
         setColumnCount(columns.size());
         setHorizontalHeaderLabels(columns);
         horizontalHeader()->setResizeMode(0, QHeaderView::Stretch);
+        horizontalHeader()->setResizeMode(1, QHeaderView::Stretch);
         horizontalHeader()->setVisible(true);
 
         setEditTriggers(QAbstractItemView::NoEditTriggers);
         setSortingEnabled(true);
 
+        QString styleSheet = this->styleSheet();
+        ARMARX_IMPORTANT << VAROUT(styleSheet.toStdString());
+        styleSheet = styleSheet + "\n" + "selection-background-color: orange;";
+        this->setStyleSheet(styleSheet);
+
         setContextMenuPolicy(Qt::CustomContextMenu);
     }
 
 
     QTableWidgetItem*
-    EdgeTableWidget::addEdge(graph::Graph::Edge edge)
+    EdgeTableWidget::addEdge(graph::Graph::ConstEdge edge)
     {
         int row = rowCount();
         setRowCount(row + 1);
@@ -73,4 +83,11 @@ namespace armarx::nav::locgrapheditor
     }
 
 
+    QList<QTableWidgetItem*>
+    EdgeTableWidget::selectedEdgeItems()
+    {
+        return utils::getSelectedItemsOfColumn(this, 0);
+    }
+
+
 }
diff --git a/source/armarx/navigation/gui-plugins/LocationGraphEditor/widgets/EdgeTableWidget.h b/source/armarx/navigation/gui-plugins/LocationGraphEditor/widgets/EdgeTableWidget.h
index 0991665d..bf6a496f 100644
--- a/source/armarx/navigation/gui-plugins/LocationGraphEditor/widgets/EdgeTableWidget.h
+++ b/source/armarx/navigation/gui-plugins/LocationGraphEditor/widgets/EdgeTableWidget.h
@@ -42,7 +42,7 @@ namespace armarx::nav::locgrapheditor
         EdgeTableWidget();
 
 
-        QTableWidgetItem* addEdge(graph::Graph::Edge edge);
+        QTableWidgetItem* addEdge(graph::Graph::ConstEdge edge);
 
         void updateEdge(GuiGraph::Edge edge);
 
@@ -51,6 +51,9 @@ namespace armarx::nav::locgrapheditor
         void clearEdges();
 
 
+        QList<QTableWidgetItem*> selectedEdgeItems();
+
+
     private slots:
 
 
diff --git a/source/armarx/navigation/gui-plugins/LocationGraphEditor/widgets/VertexDataWidget.cpp b/source/armarx/navigation/gui-plugins/LocationGraphEditor/widgets/VertexDataWidget.cpp
index e79324ea..d34dab04 100644
--- a/source/armarx/navigation/gui-plugins/LocationGraphEditor/widgets/VertexDataWidget.cpp
+++ b/source/armarx/navigation/gui-plugins/LocationGraphEditor/widgets/VertexDataWidget.cpp
@@ -68,18 +68,11 @@ namespace armarx::nav::locgrapheditor
             pos->setSuffix(" mm");
             pos->setMinimum(-1e6);
             pos->setMaximum(+1e6);
-            pos->setSingleStep(10);
-            pos->setValue(0);
-        }
-        for (QDoubleSpinBox* angle : _angleSpinBoxes())
-        {
-            angle->setSuffix("");
-            angle->setMinimum(-360);
-            angle->setMaximum(+360);
-            angle->setValue(0);
+            pos->setSingleStep(50);
         }
         for (QDoubleSpinBox* spinBox : _allSpinBoxes())
         {
+            spinBox->setValue(0);
             spinBox->setAlignment(Qt::AlignRight);
         }
 
@@ -107,21 +100,38 @@ namespace armarx::nav::locgrapheditor
 
         for (QRadioButton* angleUnit : {angleUnitDeg, angleUnitRad})
         {
-            connect(angleUnit, &QRadioButton::toggled, this, &This::_updateAngleUnit);
+            connect(angleUnit, &QRadioButton::toggled,
+                    this, &This::_updateAngleUnit);
+        }
+
+        connect(frame, &QLineEdit::editingFinished,
+                this, &This::_updateVertexAttribs);
+        for (QDoubleSpinBox* spinBox : _allSpinBoxes())
+        {
+            connect(spinBox, QOverload<qreal>::of(&QDoubleSpinBox::valueChanged),
+                    this, &This::_updateVertexAttribs);
         }
 
+
         angleUnitDeg->click();
     }
 
 
+    std::optional<GuiGraph::Vertex> VertexDataWidget::vertex()
+    {
+        return _vertex;
+    }
+
+
     void VertexDataWidget::setVertex(GuiGraph::Vertex vertex)
     {
-        setFromVertex(vertex);
+        _vertex = vertex;
+        _setFromVertex(vertex);
         setEnabled(true);
     }
 
 
-    void VertexDataWidget::setFromVertex(const GuiGraph::Vertex& vertex)
+    void VertexDataWidget::_setFromVertex(const GuiGraph::Vertex& vertex)
     {
         const VertexData& attrib = vertex.attrib();
         const Eigen::Matrix4d pose = attrib.getPose().cast<qreal>();
@@ -133,7 +143,7 @@ namespace armarx::nav::locgrapheditor
     }
 
 
-    void VertexDataWidget::getToVertex(GuiGraph::Vertex& vertex)
+    void VertexDataWidget::_getToVertex(GuiGraph::Vertex& vertex)
     {
         VertexData& attrib = vertex.attrib();
 
@@ -181,33 +191,60 @@ namespace armarx::nav::locgrapheditor
 
     void VertexDataWidget::_updateAngleUnit()
     {
-        QString suffix;
-        if (angleUnitDeg->isChecked())
-        {
-            suffix = " \u00b0";
-        }
-        else if (angleUnitRad->isChecked())
+        std::function<double(double)> convertValue;
+        QString suffix = " \u00b0";
+        double min = -360;
+        double max = +360;
+        double step = 5.;
+        int decimals = 2;
+
+        if (angleUnitRad->isChecked())
         {
+            convertValue = [](double deg)
+            {
+                return simox::math::deg_to_rad(deg);
+            };
             suffix = " rad";
+            min = simox::math::deg_to_rad(min);
+            max = simox::math::deg_to_rad(max);
+            step = simox::math::deg_to_rad(step);
+            decimals = 3;
         }
         else
         {
-            return;
+            ARMARX_CHECK(angleUnitDeg->isChecked());
+            convertValue = [](double rad)
+            {
+                return simox::math::rad_to_deg(rad);
+            };
         }
         for (QDoubleSpinBox* angle : _angleSpinBoxes())
         {
+            angle->blockSignals(true);
+
             angle->setSuffix(suffix);
+            angle->setMinimum(min);
+            angle->setMaximum(max);
+            angle->setSingleStep(step);
+            angle->setDecimals(decimals);
+        }
+        if (_vertex.has_value())
+        {
+            _setRpyRad(simox::math::mat4f_to_rpy(_vertex->attrib().getPose().cast<qreal>()));
+        }
+        for (QDoubleSpinBox* angle : _angleSpinBoxes())
+        {
+            angle->blockSignals(false);
         }
     }
 
 
     void VertexDataWidget::_updateVertexAttribs()
     {
-        if (vertex.has_value())
+        if (_vertex.has_value())
         {
-            getToVertex(vertex.value());
-
-            emit vertexDataChanged(vertex.value());
+            _getToVertex(_vertex.value());
+            emit vertexDataChanged();
         }
     }
 
diff --git a/source/armarx/navigation/gui-plugins/LocationGraphEditor/widgets/VertexDataWidget.h b/source/armarx/navigation/gui-plugins/LocationGraphEditor/widgets/VertexDataWidget.h
index 75d17e33..64b7971e 100644
--- a/source/armarx/navigation/gui-plugins/LocationGraphEditor/widgets/VertexDataWidget.h
+++ b/source/armarx/navigation/gui-plugins/LocationGraphEditor/widgets/VertexDataWidget.h
@@ -46,11 +46,9 @@ namespace armarx::nav::locgrapheditor
 
         VertexDataWidget();
 
-        void setVertex(GuiGraph::Vertex vertex);
-
-        void setFromVertex(const GuiGraph::Vertex& vertex);
-        void getToVertex(GuiGraph::Vertex& vertex);
 
+        std::optional<GuiGraph::Vertex> vertex();
+        void setVertex(GuiGraph::Vertex vertex);
 
         Eigen::Vector3d xyz() const;
         Eigen::Vector3d rpyDeg() const;
@@ -59,7 +57,7 @@ namespace armarx::nav::locgrapheditor
 
     signals:
 
-        void vertexDataChanged(GuiGraph::Vertex vertex);
+        void vertexDataChanged();
 
 
     private slots:
@@ -70,6 +68,9 @@ namespace armarx::nav::locgrapheditor
 
     private:
 
+        void _setFromVertex(const GuiGraph::Vertex& vertex);
+        void _getToVertex(GuiGraph::Vertex& vertex);
+
         std::vector<QDoubleSpinBox*> _positionSpinBoxes();
         std::vector<QDoubleSpinBox*> _angleSpinBoxes();
         std::vector<QDoubleSpinBox*> _allSpinBoxes();
@@ -84,7 +85,7 @@ namespace armarx::nav::locgrapheditor
 
     private:
 
-        std::optional<GuiGraph::Vertex> vertex;
+        std::optional<GuiGraph::Vertex> _vertex;
 
 
         QLineEdit* locationID = nullptr;
diff --git a/source/armarx/navigation/gui-plugins/LocationGraphEditor/widgets/VertexTableWidget.cpp b/source/armarx/navigation/gui-plugins/LocationGraphEditor/widgets/VertexTableWidget.cpp
index 7df6d1b7..c06f36de 100644
--- a/source/armarx/navigation/gui-plugins/LocationGraphEditor/widgets/VertexTableWidget.cpp
+++ b/source/armarx/navigation/gui-plugins/LocationGraphEditor/widgets/VertexTableWidget.cpp
@@ -20,6 +20,7 @@
  */
 
 #include "VertexTableWidget.h"
+#include "utils.h"
 
 #include <QHeaderView>
 
@@ -29,7 +30,7 @@ namespace armarx::nav::locgrapheditor
 
     VertexTableWidget::VertexTableWidget()
     {
-        QStringList columns{"Name", "X", "Y", "Yaw"};
+        QStringList columns{"Name", "X [mm]", "Y [mm]", "Yaw [\u00b0]"};
         setColumnCount(columns.size());
         setHorizontalHeaderLabels(columns);
         horizontalHeader()->setResizeMode(0, QHeaderView::Stretch);
@@ -45,20 +46,18 @@ namespace armarx::nav::locgrapheditor
 
 
     QTableWidgetItem*
-    VertexTableWidget::addVertex(graph::Graph::Vertex vertex)
+    VertexTableWidget::addVertex(graph::Graph::ConstVertex vertex)
     {
-        Eigen::Matrix4f pose = vertex.attrib().getPose();
-
-        char format = 'f';
-        const int precision = 2;
+        (void) vertex;
 
         int row = rowCount();
         setRowCount(row + 1);
-        setItem(row, 0, new QTableWidgetItem {QString::fromStdString(vertex.attrib().getName())});
-        setItem(row, 1, new QTableWidgetItem {QString::number(qreal(pose(0, 3)), format, precision)});
-        setItem(row, 2, new QTableWidgetItem {QString::number(qreal(pose(1, 3)), format, precision)});
-        setItem(row, 3, new QTableWidgetItem {QString::number(qreal(getYawAngleDegree(pose)), format, precision)});
 
+        // Just fill with vanilla items, they will get values in the update.
+        for (int col = 0; col < 4; ++col)
+        {
+            setItem(row, col, new QTableWidgetItem {});
+        }
         for (int col = 1; col <= 3; ++col)
         {
             item(row, col)->setTextAlignment(Qt::AlignRight | Qt::AlignVCenter);
@@ -71,11 +70,20 @@ namespace armarx::nav::locgrapheditor
     void
     VertexTableWidget::updateVertex(GuiGraph::Vertex vertex)
     {
+        const Eigen::Matrix4d pose = vertex.attrib().getPose().cast<qreal>();
+        char format = 'f';
+        const int precision = 2;
+
+        int row = this->row(vertex.attrib().tableWidgetItem);
+        item(row, 0)->setText(QString::fromStdString(vertex.attrib().getName()));
+        item(row, 1)->setText(QString::number(pose(0, 3), format, precision));
+        item(row, 2)->setText(QString::number(pose(1, 3), format, precision));
+        item(row, 3)->setText(QString::number(getYawAngleDegree(pose), format, precision));
+
+
         QColor bgColor = vertex.attrib().highlighted ? bgColorSelected : bgColorDefault;
         QFont font;
         font.setBold(vertex.attrib().highlighted);
-
-        int row = this->row(vertex.attrib().tableWidgetItem);
         for (int col = 0; col < 4; ++col)
         {
             auto* item = this->item(row, col);
@@ -87,27 +95,10 @@ namespace armarx::nav::locgrapheditor
     }
 
 
-
     QList<QTableWidgetItem*>
-    VertexTableWidget::selectedEdgeItems()
+    VertexTableWidget::selectedVertexItems()
     {
-        // Only return items from the first column (and avoid duplicates).
-        std::set<QTableWidgetItem*> set;
-        for (QTableWidgetItem* selected : selectedItems())
-        {
-            if (column(selected) != 0)
-            {
-                selected = item(row(selected), 0);
-            }
-            set.insert(selected);
-        }
-
-        QList<QTableWidgetItem*> list;
-        for (auto i : set)
-        {
-            list.append(i);
-        }
-        return list;
+        return utils::getSelectedItemsOfColumn(this, 0);
     }
 
 }
diff --git a/source/armarx/navigation/gui-plugins/LocationGraphEditor/widgets/VertexTableWidget.h b/source/armarx/navigation/gui-plugins/LocationGraphEditor/widgets/VertexTableWidget.h
index 6a8f24a7..7fea8530 100644
--- a/source/armarx/navigation/gui-plugins/LocationGraphEditor/widgets/VertexTableWidget.h
+++ b/source/armarx/navigation/gui-plugins/LocationGraphEditor/widgets/VertexTableWidget.h
@@ -42,12 +42,12 @@ namespace armarx::nav::locgrapheditor
         VertexTableWidget();
 
 
-        QTableWidgetItem* addVertex(graph::Graph::Vertex vertex);
+        QTableWidgetItem* addVertex(graph::Graph::ConstVertex vertex);
 
         void updateVertex(GuiGraph::Vertex vertex);
 
 
-        QList<QTableWidgetItem*> selectedEdgeItems();
+        QList<QTableWidgetItem*> selectedVertexItems();
 
 
     public slots:
diff --git a/source/armarx/navigation/gui-plugins/LocationGraphEditor/widgets/default_colors.cpp b/source/armarx/navigation/gui-plugins/LocationGraphEditor/widgets/default_colors.cpp
index 059cb71c..fd026201 100644
--- a/source/armarx/navigation/gui-plugins/LocationGraphEditor/widgets/default_colors.cpp
+++ b/source/armarx/navigation/gui-plugins/LocationGraphEditor/widgets/default_colors.cpp
@@ -28,6 +28,6 @@ namespace armarx::nav::locgrapheditor
 {
 
     const QColor default_colors::tableBackgroundDefault = QColor::fromRgb(255, 255, 255);
-    const QColor default_colors::tableBackgroundSelected = QColor::fromRgb(128, 196, 255);
+    const QColor default_colors::tableBackgroundSelected = QColor::fromRgb(255, 210, 160);
 
 }
diff --git a/source/armarx/navigation/gui-plugins/LocationGraphEditor/widgets/graph_scene/Scene.cpp b/source/armarx/navigation/gui-plugins/LocationGraphEditor/widgets/graph_scene/Scene.cpp
index d66a24e5..32c33382 100644
--- a/source/armarx/navigation/gui-plugins/LocationGraphEditor/widgets/graph_scene/Scene.cpp
+++ b/source/armarx/navigation/gui-plugins/LocationGraphEditor/widgets/graph_scene/Scene.cpp
@@ -11,7 +11,7 @@ namespace armarx::nav::locgrapheditor::graph_scene
 {
 
     QGraphicsEllipseItem*
-    Scene::addVertex(const graph::Graph::Vertex& vertex)
+    Scene::addVertex(graph::Graph::ConstVertex vertex)
     {
         const Eigen::Matrix4d pose = vertex.attrib().getPose().cast<qreal>();
 
@@ -36,7 +36,7 @@ namespace armarx::nav::locgrapheditor::graph_scene
 
 
     QGraphicsLineItem*
-    Scene::addEdge(const graph::Graph::Edge& edge)
+    Scene::addEdge(graph::Graph::ConstEdge edge)
     {
         semrel::ShapeID sourceID = edge.sourceObjectID();
         semrel::ShapeID targetID = edge.targetObjectID();
diff --git a/source/armarx/navigation/gui-plugins/LocationGraphEditor/widgets/graph_scene/Scene.h b/source/armarx/navigation/gui-plugins/LocationGraphEditor/widgets/graph_scene/Scene.h
index f1b1ac37..95965d34 100644
--- a/source/armarx/navigation/gui-plugins/LocationGraphEditor/widgets/graph_scene/Scene.h
+++ b/source/armarx/navigation/gui-plugins/LocationGraphEditor/widgets/graph_scene/Scene.h
@@ -24,8 +24,8 @@ namespace armarx::nav::locgrapheditor::graph_scene
 
         using QGraphicsScene::QGraphicsScene;
 
-        QGraphicsEllipseItem* addVertex(const graph::Graph::Vertex& vertex);
-        QGraphicsLineItem* addEdge(const graph::Graph::Edge& Edge);
+        QGraphicsEllipseItem* addVertex(graph::Graph::ConstVertex vertex);
+        QGraphicsLineItem* addEdge(graph::Graph::ConstEdge Edge);
 
         void updateVertex(GuiGraph::Vertex& vertex);
         void updateEdge(GuiGraph::Edge& edge);
diff --git a/source/armarx/navigation/gui-plugins/LocationGraphEditor/widgets/graph_scene/Widget.cpp b/source/armarx/navigation/gui-plugins/LocationGraphEditor/widgets/graph_scene/Widget.cpp
index e05f7e6b..7cdcbcd8 100644
--- a/source/armarx/navigation/gui-plugins/LocationGraphEditor/widgets/graph_scene/Widget.cpp
+++ b/source/armarx/navigation/gui-plugins/LocationGraphEditor/widgets/graph_scene/Widget.cpp
@@ -48,6 +48,51 @@ namespace armarx::nav::locgrapheditor::graph_scene
 
         // Initial transform.
         transform();
+
+#if 0
+        auto eventFilter = [this](QObject* obj, QEvent* event) -> bool
+        {
+            if (obj == this->view.view && event->type() == QEvent::MouseButtonPress)
+            {
+                QMouseEvent* me = static_cast<QMouseEvent*>(event);
+                if (me->button() == Qt::LeftButton)
+                {
+                    QPointF scenePoint = this->view.view->mapToScene(me->pos());
+                    scenePoint.setY(- scenePoint.y());  // not sure why
+
+                    float minDist = std::numeric_limits<float>::max();
+                    auto bestIt = this->vertices.cend();
+
+                    for (auto it = this->vertices.cbegin(); it != this->vertices.cend(); ++it)
+                    {
+                        float deltaX = it->second.pose->position->x - scenePoint.x();
+                        float deltaY = it->second.pose->position->y - scenePoint.y();
+                        float dist = std::sqrt(deltaX * deltaX + deltaY * deltaY);
+
+                        if (dist < minDist)
+                        {
+                            minDist = dist;
+                            bestIt = it;
+                        }
+                    }
+
+                    if (bestIt != this->vertices.cend())
+                    {
+                        this->highlightVertex(bestIt->first);
+                    }
+                }
+            }
+            else if (event->type() == QEvent::Resize)
+            {
+                this->adjustView();
+            }
+            else
+            {
+                return false;
+            }
+        };
+        _view->installEventFilter(new simox::gui::FunctionalEventFilter(eventFilter));
+#endif
     }
 
 
diff --git a/source/armarx/navigation/gui-plugins/LocationGraphEditor/widgets/utils.cpp b/source/armarx/navigation/gui-plugins/LocationGraphEditor/widgets/utils.cpp
new file mode 100644
index 00000000..c81e5509
--- /dev/null
+++ b/source/armarx/navigation/gui-plugins/LocationGraphEditor/widgets/utils.cpp
@@ -0,0 +1,53 @@
+/*
+ * 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 "utils.h"
+
+#include <QTableWidget>
+
+#include <set>
+
+
+namespace armarx::nav::locgrapheditor
+{
+
+    QList<QTableWidgetItem*>
+    utils::getSelectedItemsOfColumn(QTableWidget* widget, int column)
+    {
+        std::set<QTableWidgetItem*> set;
+        for (QTableWidgetItem* selected : widget->selectedItems())
+        {
+            if (widget->column(selected) != column)
+            {
+                selected = widget->item(widget->row(selected), 0);
+            }
+            set.insert(selected);
+        }
+
+        QList<QTableWidgetItem*> list;
+        for (auto i : set)
+        {
+            list.append(i);
+        }
+        return list;
+    }
+
+}
diff --git a/source/armarx/navigation/gui-plugins/LocationGraphEditor/widgets/utils.h b/source/armarx/navigation/gui-plugins/LocationGraphEditor/widgets/utils.h
new file mode 100644
index 00000000..8c344f01
--- /dev/null
+++ b/source/armarx/navigation/gui-plugins/LocationGraphEditor/widgets/utils.h
@@ -0,0 +1,35 @@
+/*
+ * 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 <QList>
+
+class QTableWidget;
+class QTableWidgetItem;
+
+
+namespace armarx::nav::locgrapheditor::utils
+{
+
+    QList<QTableWidgetItem*> getSelectedItemsOfColumn(QTableWidget* widget, int column);
+
+}
-- 
GitLab