VIPRA Documentation
Loading...
Searching...
No Matches
json.hpp
1#pragma once
2
3#include <algorithm>
4#include <filesystem>
5#include <fstream>
6#include <functional>
7#include <iostream>
8#include <optional>
9#include <string>
10#include <type_traits>
11
12#include <nlohmann/json.hpp>
13
14#include "vipra/geometry/f3d.hpp"
15
16#include "vipra/modules/map_input.hpp"
17#include "vipra/modules/param_reader.hpp"
18#include "vipra/modules/pedestrian_input.hpp"
19#include "vipra/modules/serializable.hpp"
20
21#include "vipra/geometry/polygon.hpp"
22
23#include "vipra/macros/errors.hpp"
24#include "vipra/util/is_map.hpp"
25#include "vipra/util/template_specialization.hpp"
26
27namespace VIPRA::Input {
33class JSON : public VIPRA::Modules::ParamReader<JSON>,
37 using json_cref = std::reference_wrapper<const nlohmann::json>;
38
39 public:
40 static constexpr auto module_name() -> const char* { return "JSON"; }
41
42 void load(std::string const& filepath) override;
43
44 template <typename data_t>
45 [[nodiscard]] auto get(std::vector<std::string> const& keys) const
46 -> std::optional<data_t>;
47
48 [[nodiscard]] auto serialize() -> std::string override { return _json.dump(); }
49
50 [[nodiscard]] auto get_pedestrians() const -> std::optional<VIPRA::f3dVec> override;
51 [[nodiscard]] auto get_obstacles() const
52 -> std::optional<std::vector<VIPRA::Geometry::Polygon>> override;
53 [[nodiscard]] auto get_spawns() const
54 -> std::optional<std::vector<VIPRA::Geometry::Polygon>> override;
55 [[nodiscard]] auto get_objectives() const
56 -> std::optional<
57 std::map<std::string, std::vector<VIPRA::Geometry::Polygon>>> override;
58 [[nodiscard]] auto get_areas() const
59 -> std::optional<std::map<std::string, VIPRA::Geometry::Polygon>> override;
60
61 private:
62 nlohmann::json _json;
63
64 template <typename data_t>
65 [[nodiscard]] auto get_value(json_cref const& value) const -> std::optional<data_t>;
66
67 [[nodiscard]] static auto is_f3d(nlohmann::json const& value) -> bool;
68 [[nodiscard]] static auto is_f2d(nlohmann::json const& value) -> bool;
69 [[nodiscard]] static auto get_f3d(json_cref const& value) -> std::optional<VIPRA::f3d>;
70 [[nodiscard]] static auto get_f3d_vec(json_cref const& value)
71 -> std::optional<VIPRA::f3dVec>;
72
73 template <typename data_t>
74 [[nodiscard]] auto get_vector(json_cref const& value) const
75 -> std::optional<std::vector<data_t>>;
76
77 template <typename data_t>
78 [[nodiscard]] auto get_map(json_cref const& value) const
79 -> std::optional<std::map<std::string, data_t>>;
80
81 [[nodiscard]] static auto get_polygons(json_cref const& value)
82 -> std::optional<std::vector<VIPRA::Geometry::Polygon>>;
83
84 [[nodiscard]] auto get_value_at_key(std::vector<std::string> const& keys) const
85 -> std::optional<json_cref>
86 {
87 auto value = std::cref(_json);
88 bool found = true;
89
90 std::for_each(keys.begin(), keys.end(), [&](std::string const& key) {
91 if ( ! found ) return;
92
93 if ( ! value.get().contains(key) ) {
94 found = false;
95 return;
96 }
97
98 value = std::cref(value.get().at(key));
99 });
100
101 if ( ! found ) return std::nullopt;
102
103 return value;
104 }
105
106 public:
107 void parse(std::string const& data) override
108 {
109 try {
110 _json = nlohmann::json::parse(data);
111 }
112 catch ( nlohmann::json::parse_error const& e ) {
113 VIPRA_MODULE_ERROR("Could not parse JSON data\n");
114 }
115 }
116};
117} // namespace VIPRA::Input
118
119// ---------------------------------------------------- IMPLEMENTATION ---------------------------------------------------- //
120
129template <typename data_t>
130auto VIPRA::Input::JSON::get(std::vector<std::string> const& keys) const
131 -> std::optional<data_t>
132{
133 auto value = get_value_at_key(keys);
134 if ( ! value ) return std::nullopt;
135
136 return get_value<data_t>(value.value());
137}
138
146template <typename data_t>
147[[nodiscard]] auto VIPRA::Input::JSON::get_vector(json_cref const& value) const
148 -> std::optional<std::vector<data_t>>
149{
150 if ( ! value.get().is_array() ) return std::nullopt;
151
152 std::vector<data_t> vec;
153 for ( auto const& element : value.get() ) {
154 auto elementValue = get_value<data_t>(std::cref(element));
155 if ( elementValue ) vec.emplace_back(*elementValue);
156 }
157
158 return vec;
159}
160
167[[nodiscard]] inline auto VIPRA::Input::JSON::get_f3d_vec(json_cref const& value)
168 -> std::optional<VIPRA::f3dVec>
169{
170 if ( ! value.get().is_array() ) return std::nullopt;
171
172 VIPRA::f3dVec f3dVec;
173 for ( auto const& f3d : value.get() ) {
174 if ( ! is_f3d(f3d) ) {
175 if ( ! is_f2d(f3d) ) return std::nullopt;
176
177 f3dVec.emplace_back(f3d.at("x").get<VIPRA::f_pnt>(),
178 f3d.at("y").get<VIPRA::f_pnt>(), 0);
179 continue;
180 }
181
182 f3dVec.emplace_back(f3d.at("x").get<VIPRA::f_pnt>(), f3d.at("y").get<VIPRA::f_pnt>(),
183 f3d.at("z").get<VIPRA::f_pnt>());
184 }
185
186 return f3dVec;
187}
188
195[[nodiscard]] inline auto VIPRA::Input::JSON::get_f3d(json_cref const& value)
196 -> std::optional<VIPRA::f3d>
197{
198 if ( ! is_f3d(value.get()) ) return std::nullopt;
199
200 return VIPRA::f3d(value.get().at("x").get<VIPRA::f_pnt>(),
201 value.get().at("y").get<VIPRA::f_pnt>(),
202 value.get().at("z").get<VIPRA::f_pnt>());
203}
204
210inline void VIPRA::Input::JSON::load(std::string const& filepath)
211{
212 if ( ! std::filesystem::exists(filepath) )
213 VIPRA_MODULE_ERROR("File does not exist at: {}", filepath);
214
215 if ( ! std::filesystem::is_regular_file(filepath) )
216 VIPRA_MODULE_ERROR("{} is not a file", filepath);
217
218 std::ifstream file(filepath, std::ios::in);
219 if ( ! file.is_open() ) VIPRA_MODULE_ERROR("Could not open file at: ", filepath);
220
221 try {
222 _json = nlohmann::json::parse(file);
223 }
224 catch ( nlohmann::json::parse_error const& e ) {
225 VIPRA_MODULE_ERROR("Could not parse JSON file at: {}\nnlohmann json error: {}",
226 filepath, e.what());
227 }
228
229 file.close();
230}
231
239template <typename data_t>
240[[nodiscard]] auto VIPRA::Input::JSON::get_value(json_cref const& value) const
241 -> std::optional<data_t>
242{
243 if constexpr ( std::is_same_v<data_t, VIPRA::f3d> ) {
244 return get_f3d(value);
245 }
246
247 else if constexpr ( std::is_same_v<data_t, VIPRA::f3dVec> ) {
248 return get_f3d_vec(value);
249 }
250
251 else if constexpr ( std::is_same_v<data_t, std::vector<VIPRA::Geometry::Polygon>> ) {
252 return get_polygons(value);
253 }
254
255 else if constexpr ( VIPRA::Util::is_specialization<data_t, std::vector>::value ) {
256 using value_t = typename VIPRA::Util::get_specialization_internal<data_t>::type;
257 return get_vector<value_t>(value);
258 }
259
260 else if constexpr ( VIPRA::Util::is_map_v<data_t> ) {
261 using value_t = typename VIPRA::Util::get_map_specialization<data_t>::value_t;
262 return get_map<value_t>(value);
263 }
264
265 else {
266 try {
267 return value.get().get<data_t>();
268 }
269 catch ( nlohmann::json::type_error const& e ) {
270 return std::nullopt;
271 }
272 }
273
274 return std::nullopt;
275}
276
277template <typename data_t>
278auto VIPRA::Input::JSON::get_map(json_cref const& value) const
279 -> std::optional<std::map<std::string, data_t>>
280{
281 try {
282 return value.get().get<std::map<std::string, data_t>>();
283 }
284 catch ( nlohmann::json::type_error const& e ) {
285 return std::nullopt;
286 }
287}
288
296[[nodiscard]] inline auto VIPRA::Input::JSON::is_f3d(nlohmann::json const& value) -> bool
297{
298 if ( ! value.is_object() ) return false;
299 if ( ! value.contains("x") || ! value.contains("y") || ! value.contains("z") )
300 return false;
301 if ( ! value.at("x").is_number() || ! value.at("y").is_number() ||
302 ! value.at("z").is_number() )
303 return false;
304 return true;
305}
306
314[[nodiscard]] inline auto VIPRA::Input::JSON::is_f2d(nlohmann::json const& value) -> bool
315{
316 if ( ! value.is_object() ) return false;
317 if ( ! value.contains("x") || ! value.contains("y") ) return false;
318 if ( ! value.at("x").is_number() || ! value.at("y").is_number() ) return false;
319 return true;
320}
Parameter and Polygon qualified JSON input module.
Definition json.hpp:36
auto get_objectives() const -> std::optional< std::map< std::string, std::vector< VIPRA::Geometry::Polygon > > > override
Returns polygons as loaded from the input module, std::nullopt if incorrect type / doesn't exist.
Definition json.cpp:54
auto get(std::vector< std::string > const &keys) const -> std::optional< data_t >
Returns the value of the given key from the JSON file.
Definition json.hpp:130
void load(std::string const &filepath) override
Loads the JSON file from the given path.
Definition json.hpp:210
auto get_spawns() const -> std::optional< std::vector< VIPRA::Geometry::Polygon > > override
Returns polygons as loaded from the input module, std::nullopt if incorrect type / doesn't exist.
Definition json.cpp:48
auto get_areas() const -> std::optional< std::map< std::string, VIPRA::Geometry::Polygon > > override
Returns polygons as loaded from the input module, std::nullopt if incorrect type / doesn't exist.
Definition json.cpp:82
auto get_obstacles() const -> std::optional< std::vector< VIPRA::Geometry::Polygon > > override
Returns polygons as loaded from the input module, std::nullopt if incorrect type / doesn't exist.
Definition json.cpp:42
Base MapInput Module.
Definition map_input.hpp:15
Base ParamReader Module.
Definition param_reader.hpp:21
Base PedestrianInput Module.
Definition pedestrian_input.hpp:13
Definition serializable.hpp:7
Definition f3d.hpp:27