Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Pymem relaxjson #831

Merged
merged 4 commits into from
Aug 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -421,7 +421,7 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${g2o_CXX_FLAGS}")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${g2o_C_FLAGS}")

find_package(Eigen3 3.3 REQUIRED)
find_package(nlohmann_json 3.2.0)
find_package(nlohmann_json 3.3.0)

# Generate config.h
set(G2O_OPENGL_FOUND ${OPENGL_FOUND})
Expand Down
23 changes: 15 additions & 8 deletions g2o/apps/g2o_cli/g2o.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
#include <iostream>
#include <sstream>
#include <string>
#include <string_view>

#include "dl_wrapper.h"
#include "g2o/config.h"
Expand Down Expand Up @@ -65,6 +66,15 @@ void sigquit_handler(int sig) {
}
}
}

g2o::io::Format guessIOFormat(const std::string_view filename) {
const std::string file_extension = g2o::getFileExtension(filename);
if (file_extension.empty()) {
return g2o::io::Format::kG2O;
}
return g2o::io::formatForFileExtension(file_extension)
.value_or(g2o::io::Format::kG2O);
}
} // namespace

using g2o::HyperGraph;
Expand Down Expand Up @@ -274,13 +284,15 @@ int main(int argc, char** argv) {
return 2;
}
} else {
cerr << "Read input from " << inputFilename << '\n';
const g2o::io::Format input_format = guessIOFormat(inputFilename);
cerr << "Read input from " << inputFilename << " as "
<< g2o::io::to_string(input_format) << '\n';
std::ifstream ifs(inputFilename.c_str());
if (!ifs) {
cerr << "Failed to open file\n";
return 1;
}
if (!optimizer.load(ifs)) {
if (!optimizer.load(ifs, input_format)) {
cerr << "Error loading graph\n";
return 2;
}
Expand Down Expand Up @@ -693,12 +705,7 @@ int main(int argc, char** argv) {
cerr << "saving to stdout";
optimizer.save(cout);
} else {
const std::string file_extension = g2o::getFileExtension(outputfilename);
g2o::io::Format output_format = g2o::io::Format::kG2O;
if (!file_extension.empty()) {
output_format = g2o::io::formatForFileExtension(file_extension)
.value_or(output_format);
}
const g2o::io::Format output_format = guessIOFormat(outputfilename);
cerr << "saving " << outputfilename << " in "
<< g2o::io::to_string(output_format) << " ... ";
optimizer.save(outputfilename.c_str(), output_format);
Expand Down
2 changes: 2 additions & 0 deletions g2o/core/io/io_json.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
#include "io_json.h"

#include <exception>
#include <iomanip>
#include <optional>

#include "g2o/config.h"
Expand Down Expand Up @@ -56,6 +57,7 @@ std::optional<AbstractGraph> IoJson::load(std::istream& input) {

bool IoJson::save(std::ostream& output, const AbstractGraph& graph) {
try {
output << std::setw(2);
output << json::toJson(graph);
} catch (const std::exception& e) {
G2O_ERROR("Exception while saving: {}", e.what());
Expand Down
86 changes: 73 additions & 13 deletions g2o/core/io/io_wrapper_json.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,14 +35,77 @@
#include "g2o/core/abstract_graph.h"

namespace g2o {
namespace internal {
/**
* @brief Get the object from the JSON if key exists
*
* @tparam T type of the target value
* @param j a JSON
* @param key key for retrieving the value
* @param target Where to store the value
*/
template <typename T>
void get_to_if_exists(const nlohmann::json& j, const char* key, T& target) {
auto it = j.find(key);
if (it == j.end()) return;
it->get_to(target);
}

/**
* @brief Store a container into the JSON if not empty
*
* @tparam T type of the container
* @param j a JSON
* @param key key for storing the container
* @param value the container to store
*/
template <typename T>
void store_if_not_empty(nlohmann::json& j, const char* key, const T& value) {
if (value.empty()) return;
j[key] = value;
}
} // namespace internal

NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(AbstractGraph::AbstractParameter, tag, id,
value);
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(AbstractGraph::AbstractData, tag, data);
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(AbstractGraph::AbstractVertex, tag, id,
estimate, data);
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(AbstractGraph::AbstractEdge, tag, ids,
param_ids, measurement, information, data);

// VERTEX
inline void to_json(nlohmann::json& j,
const AbstractGraph::AbstractVertex& vertex) {
j = nlohmann::json{
{"tag", vertex.tag}, {"id", vertex.id}, {"estimate", vertex.estimate}};
internal::store_if_not_empty(j, "data", vertex.data);
}

inline void from_json(const nlohmann::json& j,
AbstractGraph::AbstractVertex& vertex) {
j.at("tag").get_to(vertex.tag);
j.at("id").get_to(vertex.id);
j.at("estimate").get_to(vertex.estimate);
internal::get_to_if_exists(j, "data", vertex.data);
}

// EDGE
inline void to_json(nlohmann::json& j,
const AbstractGraph::AbstractEdge& edge) {
j = nlohmann::json{{"tag", edge.tag},
{"ids", edge.ids},
{"measurement", edge.measurement},
{"information", edge.information}};
internal::store_if_not_empty(j, "data", edge.data);
internal::store_if_not_empty(j, "param_ids", edge.param_ids);
}

inline void from_json(const nlohmann::json& j,
AbstractGraph::AbstractEdge& edge) {
j.at("tag").get_to(edge.tag);
j.at("ids").get_to(edge.ids);
j.at("measurement").get_to(edge.measurement);
j.at("information").get_to(edge.information);
internal::get_to_if_exists(j, "data", edge.data);
internal::get_to_if_exists(j, "param_ids", edge.param_ids);
}

namespace json {

Expand All @@ -61,23 +124,20 @@ enum class IoVersions {
inline AbstractGraph fromJson(const nlohmann::json& json) {
const nlohmann::json& json_graph = json["graph"];
AbstractGraph graph;
graph.fixed() = json_graph["fixed"].get<std::vector<int>>();
graph.parameters() =
json_graph["params"].get<std::vector<AbstractGraph::AbstractParameter>>();
graph.vertices() =
json_graph["vertices"].get<std::vector<AbstractGraph::AbstractVertex>>();
graph.edges() =
json_graph["edges"].get<std::vector<AbstractGraph::AbstractEdge>>();
json_graph["vertices"].get_to(graph.vertices());
json_graph["edges"].get_to(graph.edges());
internal::get_to_if_exists(json_graph, "fixed", graph.fixed());
internal::get_to_if_exists(json_graph, "params", graph.parameters());
return graph;
}

inline nlohmann::json toJson(const AbstractGraph& graph) {
nlohmann::json json;
nlohmann::json& json_graph = json["graph"];
json_graph["fixed"] = graph.fixed();
json_graph["params"] = graph.parameters();
json_graph["vertices"] = graph.vertices();
json_graph["edges"] = graph.edges();
g2o::internal::store_if_not_empty(json_graph, "fixed", graph.fixed());
g2o::internal::store_if_not_empty(json_graph, "params", graph.parameters());
return json;
}

Expand Down
54 changes: 54 additions & 0 deletions python/examples/validate_graph_json.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
#!/usr/bin/env python3

import argparse
import errno
import json
import sys
from typing import Any, Optional

from jsonschema import ValidationError, validate


def read_json(filename) -> Optional[Any]:
"""Reads the content of filename as JSON and returns the dict

Args:
filename: Filename of the file to read

Returns:
Optional[Any]: The JSON dict stored in the file, None in case of Exception.
"""
try:
with open(filename) as schema_file:
content = json.load(schema_file)
return content
except Exception:
return None


def main():
parser = argparse.ArgumentParser()
parser.add_argument("schema", type=str, help="JSON schema")
parser.add_argument("input", type=str, help="input JSON g2o graph")
args = parser.parse_args()

schema = read_json(args.schema)
if schema is None:
print("Error while reading schema")
sys.exit(errno.EIO)

json_data = read_json(args.input)
if json_data is None:
print("Error while graph input for validation")
sys.exit(errno.EIO)

try:
validate(instance=json_data, schema=schema)
print("Success")
except ValidationError as ex:
print("Validation failed")
print(ex)


if __name__ == "__main__":
main()
Loading