15 #include "nav2_rviz_plugins/route_tool.hpp"
16 #include <QDesktopServices>
18 #include <sys/types.h>
19 #include <QFileDialog>
20 #include "rviz_common/display_context.hpp"
23 namespace nav2_rviz_plugins
26 : rviz_common::Panel(parent),
27 ui_(std::make_unique<Ui::route_tool>())
31 node_ = std::make_shared<nav2::LifecycleNode>(
"route_tool_node",
"", rclcpp::NodeOptions());
33 graph_vis_publisher_ = node_->create_publisher<visualization_msgs::msg::MarkerArray>(
34 "route_graph", rclcpp::QoS(rclcpp::KeepLast(1)).transient_local().reliable());
36 tf_ = std::make_shared<tf2_ros::Buffer>(node_->get_clock());
37 graph_loader_ = std::make_shared<nav2_route::GraphLoader>(node_, tf_,
"map");
38 graph_saver_ = std::make_shared<nav2_route::GraphSaver>(node_, tf_,
"map");
39 ui_->add_node_button->setChecked(
true);
40 ui_->edit_node_button->setChecked(
true);
41 ui_->remove_node_button->setChecked(
true);
47 void RouteTool::onInitialize(
void)
49 auto ros_node_abstraction = getDisplayContext()->getRosNodeAbstraction().lock();
50 if (!ros_node_abstraction) {
52 node_->get_logger(),
"Unable to get ROS node abstraction");
55 auto node = ros_node_abstraction->get_raw_node();
57 clicked_point_subscription_ = node->create_subscription<geometry_msgs::msg::PointStamped>(
58 "clicked_point", 1, [
this](
const geometry_msgs::msg::PointStamped::SharedPtr msg) {
59 ui_->add_field_1->setText(std::to_string(msg->point.x).c_str());
60 ui_->add_field_2->setText(std::to_string(msg->point.y).c_str());
61 ui_->edit_field_1->setText(std::to_string(msg->point.x).c_str());
62 ui_->edit_field_2->setText(std::to_string(msg->point.y).c_str());
66 void RouteTool::on_load_button_clicked(
void)
68 graph_to_id_map_.clear();
69 edge_to_node_map_.clear();
70 graph_to_incoming_edges_map_.clear();
72 QString filename = QFileDialog::getOpenFileName(
74 tr(
"Open Address Book"),
"",
75 tr(
"Address Book (*.geojson);;All Files (*)"));
76 graph_loader_->loadGraphFromFile(graph_, graph_to_id_map_, filename.toStdString());
77 unsigned int max_node_id = 0;
78 for (
const auto & node : graph_) {
79 max_node_id = std::max(node.nodeid, max_node_id);
80 for (
const auto & edge : node.neighbors) {
81 max_node_id = std::max(edge.edgeid, max_node_id);
82 edge_to_node_map_[edge.edgeid] = node.nodeid;
83 if (graph_to_incoming_edges_map_.find(edge.end->nodeid) !=
84 graph_to_incoming_edges_map_.end())
86 graph_to_incoming_edges_map_[edge.end->nodeid].push_back(edge.edgeid);
88 graph_to_incoming_edges_map_[edge.end->nodeid] = std::vector<unsigned int> {edge.edgeid};
92 next_node_id_ = max_node_id + 1;
96 void RouteTool::on_save_button_clicked(
void)
98 QString filename = QFileDialog::getSaveFileName(
100 tr(
"Open Address Book"),
"",
101 tr(
"Address Book (*.geojson);;All Files (*)"));
102 RCLCPP_INFO(node_->get_logger(),
"Save graph to: %s", filename.toStdString().c_str());
103 graph_saver_->saveGraphToFile(graph_, filename.toStdString());
106 void RouteTool::on_create_button_clicked(
void)
108 if (
ui_->add_field_1->toPlainText() ==
"" ||
ui_->add_field_2->toPlainText() ==
"") {
return;}
109 if (
ui_->add_node_button->isChecked()) {
110 auto longitude =
ui_->add_field_1->toPlainText().toFloat();
111 auto latitude =
ui_->add_field_2->toPlainText().toFloat();
113 new_node.nodeid = next_node_id_;
114 new_node.coords.x = longitude;
115 new_node.coords.y = latitude;
116 graph_.push_back(new_node);
117 graph_to_id_map_[next_node_id_++] = graph_.size() - 1;
118 RCLCPP_INFO(node_->get_logger(),
"Adding node at: (%f, %f)", longitude, latitude);
119 update_route_graph();
120 }
else if (
ui_->add_edge_button->isChecked()) {
121 auto start_node =
ui_->add_field_1->toPlainText().toInt();
122 auto end_node =
ui_->add_field_2->toPlainText().toInt();
124 graph_[graph_to_id_map_[start_node]].addEdge(
125 edge_cost, &(graph_[graph_to_id_map_[end_node]]),
127 if (graph_to_incoming_edges_map_.find(end_node) != graph_to_incoming_edges_map_.end()) {
128 graph_to_incoming_edges_map_[end_node].push_back(next_node_id_);
130 graph_to_incoming_edges_map_[end_node] = std::vector<unsigned int> {next_node_id_};
132 edge_to_node_map_[next_node_id_++] = start_node;
133 RCLCPP_INFO(node_->get_logger(),
"Adding edge from %d to %d", start_node, end_node);
134 update_route_graph();
136 ui_->add_field_1->setText(
"");
137 ui_->add_field_2->setText(
"");
140 void RouteTool::on_confirm_button_clicked(
void)
142 if (
ui_->edit_id->toPlainText() ==
"" ||
ui_->edit_field_1->toPlainText() ==
"" ||
143 ui_->edit_field_2->toPlainText() ==
"") {
return;}
144 if (
ui_->edit_node_button->isChecked()) {
145 auto node_id =
ui_->edit_id->toPlainText().toInt();
146 auto new_longitude =
ui_->edit_field_1->toPlainText().toFloat();
147 auto new_latitude =
ui_->edit_field_2->toPlainText().toFloat();
148 if (graph_to_id_map_.find(node_id) != graph_to_id_map_.end()) {
149 graph_[graph_to_id_map_[node_id]].coords.x = new_longitude;
150 graph_[graph_to_id_map_[node_id]].coords.y = new_latitude;
151 update_route_graph();
153 }
else if (
ui_->edit_edge_button->isChecked()) {
154 auto edge_id = (
unsigned int)
ui_->edit_id->toPlainText().toInt();
155 auto new_start =
ui_->edit_field_1->toPlainText().toInt();
156 auto new_end =
ui_->edit_field_2->toPlainText().toInt();
158 auto current_start_node = &graph_[graph_to_id_map_[edge_to_node_map_[edge_id]]];
159 for (
auto itr = current_start_node->neighbors.begin();
160 itr != current_start_node->neighbors.end(); itr++)
162 if (itr->edgeid == edge_id) {
163 current_start_node->neighbors.erase(itr);
169 graph_[graph_to_id_map_[new_start]].addEdge(
170 edge_cost, &(graph_[graph_to_id_map_[new_end]]),
172 edge_to_node_map_[edge_id] = new_start;
173 if (graph_to_incoming_edges_map_.find(new_end) != graph_to_incoming_edges_map_.end()) {
174 graph_to_incoming_edges_map_[new_end].push_back(edge_id);
176 graph_to_incoming_edges_map_[new_end] = std::vector<unsigned int> {edge_id};
178 update_route_graph();
180 ui_->edit_id->setText(
"");
181 ui_->edit_field_1->setText(
"");
182 ui_->edit_field_2->setText(
"");
185 void RouteTool::on_delete_button_clicked(
void)
187 if (
ui_->remove_id->toPlainText() ==
"") {
return;}
188 if (
ui_->remove_node_button->isChecked()) {
189 unsigned int node_id =
ui_->remove_id->toPlainText().toInt();
191 for (
auto edge_id : graph_to_incoming_edges_map_[node_id]) {
192 auto start_node = &graph_[graph_to_id_map_[edge_to_node_map_[edge_id]]];
193 for (
auto itr = start_node->neighbors.begin(); itr != start_node->neighbors.end(); itr++) {
194 if (itr->edgeid == edge_id) {
195 start_node->neighbors.erase(itr);
196 edge_to_node_map_.erase(edge_id);
201 if (graph_[graph_to_id_map_[node_id]].nodeid == node_id) {
203 graph_[graph_to_id_map_[node_id]].nodeid = std::numeric_limits<int>::max();
204 graph_to_id_map_.erase(node_id);
205 graph_to_incoming_edges_map_.erase(node_id);
206 RCLCPP_INFO(node_->get_logger(),
"Removed node %d", node_id);
208 update_route_graph();
209 }
else if (
ui_->remove_edge_button->isChecked()) {
210 auto edge_id = (
unsigned int)
ui_->remove_id->toPlainText().toInt();
211 auto start_node = &graph_[graph_to_id_map_[edge_to_node_map_[edge_id]]];
212 for (
auto itr = start_node->neighbors.begin(); itr != start_node->neighbors.end(); itr++) {
213 if (itr->edgeid == edge_id) {
214 RCLCPP_INFO(node_->get_logger(),
"Removed edge %d", edge_id);
215 start_node->neighbors.erase(itr);
216 edge_to_node_map_.erase(edge_id);
220 update_route_graph();
222 ui_->remove_id->setText(
"");
225 void RouteTool::on_add_node_button_toggled(
void)
227 if (
ui_->add_node_button->isChecked()) {
228 ui_->add_text->setText(
"Position:");
229 ui_->add_label_1->setText(
"X:");
230 ui_->add_label_2->setText(
"Y:");
232 ui_->add_text->setText(
"Connections:");
233 ui_->add_label_1->setText(
"Start Node ID:");
234 ui_->add_label_2->setText(
"End Node ID:");
238 void RouteTool::on_edit_node_button_toggled(
void)
240 if (
ui_->edit_node_button->isChecked()) {
241 ui_->edit_text->setText(
"Position:");
242 ui_->edit_label_1->setText(
"X:");
243 ui_->edit_label_2->setText(
"Y:");
245 ui_->edit_text->setText(
"Connections:");
246 ui_->edit_label_1->setText(
"Start Node ID:");
247 ui_->edit_label_2->setText(
"End Node ID:");
251 void RouteTool::update_route_graph(
void)
253 graph_vis_publisher_->publish(nav2_route::utils::toMsg(graph_,
"map", node_->now()));
258 rviz_common::Panel::save(config);
261 void RouteTool::load(
const rviz_common::Config & config)
263 rviz_common::Panel::load(config);
267 #include <pluginlib/class_list_macros.hpp>
An object to store edge cost or cost metadata for scoring.
An object to store the nodes in the graph file.