Nav2 Navigation Stack - kilted  kilted
ROS 2 Navigation Stack
geojson_graph_file_saver.cpp
1 // Copyright (c) 2024 John Chrosniak
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include <memory>
16 #include <string>
17 #include <vector>
18 #include <fstream>
19 
20 #include "nav2_route/plugins/graph_file_savers/geojson_graph_file_saver.hpp"
21 
22 namespace nav2_route
23 {
24 
26  const rclcpp_lifecycle::LifecycleNode::SharedPtr node)
27 {
28  RCLCPP_INFO(node->get_logger(), "Configuring geojson graph file saver");
29  logger_ = node->get_logger();
30 }
31 
33  Graph & graph, std::string filepath)
34 {
35  if (filepath.empty()) {
36  RCLCPP_ERROR(logger_, "File path is empty");
37  return false;
38  }
39  Json json_graph, json_crs, json_properties;
40  json_graph["type"] = "FeatureCollection";
41  json_graph["name"] = "graph";
42  json_properties["name"] = "urn:ogc:def:crs:EPSG::3857";
43  json_crs["type"] = "name";
44  json_crs["properties"] = json_properties;
45  json_graph["crs"] = json_crs;
46  std::vector<Json> json_features;
47  try {
48  loadNodesFromGraph(graph, json_features);
49  loadEdgesFromGraph(graph, json_features);
50  json_graph["features"] = json_features;
51  std::ofstream file(filepath);
52  file << json_graph.dump(4) << std::endl;
53  file.close();
54  } catch (const std::exception & e) {
55  RCLCPP_ERROR(logger_, "An error occurred: %s", e.what());
56  return false;
57  }
58  return true;
59 }
60 
62  Graph & graph, std::vector<Json> & json_features)
63 {
64  for (const auto & node : graph) {
65  if (node.nodeid == std::numeric_limits<int>::max()) { // Skip "deleted" nodes
66  continue;
67  }
68  Json json_feature, json_properties, json_geometry, json_metadata, json_operations;
69  json_geometry["type"] = "Point";
70  json_geometry["coordinates"] = std::vector<float>{node.coords.x, node.coords.y};
71  json_feature["geometry"] = json_geometry;
72  json_properties["id"] = node.nodeid;
73  json_properties["frame"] = node.coords.frame_id;
74  convertMetaDataToJson(node.metadata, json_metadata);
75  if (json_metadata.size() > 0) {
76  json_properties["metadata"] = json_metadata;
77  }
78  convertOperationsToJson(node.operations, json_operations);
79  if (json_operations.size() > 0) {
80  json_properties["operations"] = json_operations;
81  }
82  json_feature["properties"] = json_properties;
83  json_feature["type"] = "Feature";
84  json_features.push_back(json_feature);
85  }
86 }
87 
89  Graph & graph, std::vector<Json> & json_edges)
90 {
91  for (const auto & node : graph) {
92  for (const auto & edge : node.neighbors) {
93  Json json_edge, json_properties, json_geometry, json_metadata, json_operations;
94  json_geometry["type"] = "MultiLineString";
95  json_edge["geometry"] = json_geometry;
96  json_properties["id"] = edge.edgeid;
97  json_properties["startid"] = edge.start->nodeid;
98  json_properties["endid"] = edge.end->nodeid;
99  convertMetaDataToJson(edge.metadata, json_metadata);
100  if (json_metadata.size() > 0) {
101  json_properties["metadata"] = json_metadata;
102  }
103  convertOperationsToJson(edge.operations, json_operations);
104  if (json_operations.size() > 0) {
105  json_properties["operations"] = json_operations;
106  }
107  json_properties["cost"] = edge.edge_cost.cost;
108  json_properties["overridable"] = edge.edge_cost.overridable;
109  json_edge["properties"] = json_properties;
110  json_edge["type"] = "Feature";
111  json_edges.push_back(json_edge);
112  }
113  }
114 }
115 
117  const Metadata & metadata, Json & json_metadata)
118 {
119  /* Function partially created using GPT */
120  for (auto itr = metadata.data.begin(); itr != metadata.data.end(); itr++) {
121  if (itr->second.type() == typeid(std::string)) {
122  json_metadata[itr->first] = std::any_cast<std::string>(itr->second);
123  } else if (itr->second.type() == typeid(int)) {
124  json_metadata[itr->first] = std::any_cast<int>(itr->second);
125  } else if (itr->second.type() == typeid(unsigned int)) {
126  json_metadata[itr->first] = std::any_cast<unsigned int>(itr->second);
127  } else if (itr->second.type() == typeid(float)) {
128  json_metadata[itr->first] = std::any_cast<float>(itr->second);
129  } else if (itr->second.type() == typeid(bool)) {
130  json_metadata[itr->first] = std::any_cast<bool>(itr->second);
131  } else if (itr->second.type() == typeid(Metadata)) {
132  // If the itr->second is another Metadata, recursively convert it to JSON
133  Json nested_metadata_json;
134  convertMetaDataToJson(std::any_cast<Metadata>(itr->second), nested_metadata_json);
135  json_metadata[itr->first] = nested_metadata_json;
136  } else if (itr->second.type() == typeid(std::vector<std::any>)) {
137  // If the itr->second is a vector, convert each element
138  std::vector<Json> arrayJson;
139  for (const auto & element : std::any_cast<std::vector<std::any>>(itr->second)) {
140  if (element.type() == typeid(std::string)) {
141  arrayJson.push_back(std::any_cast<std::string>(element));
142  } else if (element.type() == typeid(int)) {
143  arrayJson.push_back(std::any_cast<int>(element));
144  } else if (element.type() == typeid(unsigned int)) {
145  arrayJson.push_back(std::any_cast<unsigned int>(element));
146  } else if (element.type() == typeid(float)) {
147  arrayJson.push_back(std::any_cast<float>(element));
148  } else if (element.type() == typeid(bool)) {
149  arrayJson.push_back(std::any_cast<bool>(element));
150  }
151  }
152  json_metadata[itr->first] = arrayJson;
153  } else {
154  // If we have an unknown type, handle as needed
155  json_metadata[itr->first] = itr->second.type().name();
156  }
157  }
158 }
159 
161  const Operations & operations, Json & json_operations)
162 {
163  for (const auto & operation : operations) {
164  Json json_operation, json_metadata;
165  json_operation["type"] = operation.type;
166  json_operation["trigger"] = operation.trigger;
167  convertMetaDataToJson(operation.metadata, json_metadata);
168  if (json_metadata.size()) {
169  json_operation["metadata"] = json_metadata;
170  }
171  json_operations[operation.type] = json_operation;
172  }
173 }
174 } // namespace nav2_route
175 
176 #include "pluginlib/class_list_macros.hpp"
A GraphFileSaver plugin to save a geojson graph representation.
void convertOperationsToJson(const Operations &operations, Json &json_operations)
Convert graph operation to Json.
void configure(const rclcpp_lifecycle::LifecycleNode::SharedPtr node) override
Configure, but do not store the node.
bool saveGraphToFile(Graph &graph, std::string filepath) override
Saves the graph to a geojson file.
void loadNodesFromGraph(Graph &graph, std::vector< Json > &json_features)
Add nodes into the graph.
void loadEdgesFromGraph(Graph &graph, std::vector< Json > &json_edges)
Add edges into the graph.
void convertMetaDataToJson(const Metadata &metadata, Json &json_metadata)
Convert graph metadata to Json.
A plugin interface to parse a file into the graph.
An object to store arbitrary metadata regarding nodes from the graph file.
Definition: types.hpp:35