15constexpr std::uint32_t kNull = std::numeric_limits<std::uint32_t>::max();
17void push_ring(
const nlohmann::ordered_json&
ring, GMBoundaries& b) {
18 b.strings.push_back(
static_cast<std::uint32_t
>(
ring.size()));
19 for (
const auto& idx :
ring)
20 b.indices.push_back(idx.get<std::uint32_t>());
23void push_surface(
const nlohmann::ordered_json&
surface, GMBoundaries& b) {
26 b.surfaces.push_back(
static_cast<std::uint32_t
>(
surface.size()));
29void push_shell(
const nlohmann::ordered_json&
shell, GMBoundaries& b) {
32 b.shells.push_back(
static_cast<std::uint32_t
>(
shell.size()));
35void push_solid(
const nlohmann::ordered_json& solid, GMBoundaries& b) {
36 for (
const auto&
shell : solid)
38 b.solids.push_back(
static_cast<std::uint32_t
>(solid.size()));
41std::uint32_t semantics_index(
const nlohmann::ordered_json& v) {
42 return v.is_null() ? kNull : v.get<std::uint32_t>();
48void push_semantics_shell(
const nlohmann::ordered_json*
shell,
const GMBoundaries& boundaries,
49 std::size_t& shell_cursor, std::vector<std::uint32_t>& flattened) {
50 const std::uint32_t surface_count =
51 shell_cursor < boundaries.shells.size() ? boundaries.shells[shell_cursor] : 0;
53 if (
shell ==
nullptr) {
54 flattened.insert(flattened.end(), surface_count, kNull);
56 for (
const auto& v : *
shell)
57 flattened.push_back(semantics_index(v));
61std::uint32_t material_index(
const nlohmann::ordered_json& v) {
62 return v.is_null() ? kNull : v.get<std::uint32_t>();
68void push_material_shell(
const nlohmann::ordered_json*
shell, MaterialMapping& mv) {
69 if (
shell ==
nullptr) {
70 mv.shells.push_back(kNull);
72 mv.shells.push_back(
static_cast<std::uint32_t
>(
shell->size()));
73 for (
const auto& v : *
shell)
74 mv.
vertices.push_back(material_index(v));
78void push_textured_ring(
const nlohmann::ordered_json&
ring, TextureMapping& m) {
79 m.strings.push_back(
static_cast<std::uint32_t
>(
ring.size()));
80 for (
const auto& v :
ring)
81 m.
vertices.push_back(material_index(v));
84void push_textured_surface(
const nlohmann::ordered_json&
surface, TextureMapping& m) {
86 push_textured_ring(
ring, m);
87 m.surfaces.push_back(
static_cast<std::uint32_t
>(
surface.size()));
90void push_textured_shell(
const nlohmann::ordered_json&
shell, TextureMapping& m) {
92 push_textured_surface(
surface, m);
93 m.shells.push_back(
static_cast<std::uint32_t
>(
shell.size()));
126bool fits_nullable_rank(
const nlohmann::ordered_json& arr,
int rank) {
129 for (
const auto& el : arr) {
135 }
else if (!fits_nullable_rank(el, rank - 1)) {
145int sniff_nullable_rank(
const nlohmann::ordered_json& arr) {
146 for (
int rank = 1; rank <= 3; ++rank)
147 if (fits_nullable_rank(arr, rank))
156bool is_texture_ring(
const nlohmann::ordered_json& v) {
159 for (
const auto& el : v)
160 if (!el.is_null() && el.is_array())
168bool fits_texture_depth(
const nlohmann::ordered_json& arr,
int depth) {
170 return is_texture_ring(arr);
173 for (
const auto& el : arr)
174 if (!fits_texture_depth(el, depth - 1))
186int sniff_texture_depth(
const nlohmann::ordered_json& arr) {
187 for (
int rank = 1; rank <= 3; ++rank)
188 if (fits_texture_depth(arr, rank + 1))
191 "texture values array nests deeper than a Solid permits");
197 if (name ==
"MultiPoint")
199 if (name ==
"MultiLineString")
201 if (name ==
"MultiSurface")
203 if (name ==
"CompositeSurface")
207 if (name ==
"MultiSolid")
209 if (name ==
"CompositeSolid")
211 if (name ==
"GeometryInstance")
220 push_ring(boundaries, b);
223 for (
const auto&
ring : boundaries)
225 b.
surfaces.push_back(
static_cast<std::uint32_t
>(boundaries.size()));
229 for (
const auto&
surface : boundaries)
231 b.
shells.push_back(
static_cast<std::uint32_t
>(boundaries.size()));
234 push_solid(boundaries, b);
238 for (
const auto& solid : boundaries)
239 push_solid(solid, b);
251 result.
surfaces = semantics.value(
"surfaces", nlohmann::ordered_json::array());
253 auto values_it = semantics.find(
"values");
254 if (values_it == semantics.end() || values_it->is_null())
257 std::vector<std::uint32_t> flattened;
258 switch (sniff_nullable_rank(*values_it)) {
260 for (
const auto& v : *values_it)
261 flattened.push_back(semantics_index(v));
265 std::size_t shell_cursor = 0;
266 for (
const auto&
shell : *values_it)
267 push_semantics_shell(
shell.is_null() ? nullptr : &
shell, boundaries, shell_cursor,
273 std::size_t shell_cursor = 0;
274 std::size_t solid_i = 0;
275 for (
const auto& solid : *values_it) {
276 const std::uint32_t shell_count =
277 solid_i < boundaries.
solids.size() ? boundaries.
solids[solid_i] : 0;
278 if (solid.is_null()) {
279 for (std::uint32_t k = 0; k < shell_count; ++k)
280 push_semantics_shell(
nullptr, boundaries, shell_cursor, flattened);
282 for (
const auto&
shell : solid)
283 push_semantics_shell(
shell.is_null() ? nullptr : &
shell, boundaries,
284 shell_cursor, flattened);
291 result.
values = std::move(flattened);
296 std::vector<MaterialMapping> out;
297 if (!material.is_object())
307 std::vector<std::string> themes;
308 for (
const auto& [theme, unused] : material.items())
309 themes.push_back(theme);
310 std::sort(themes.begin(), themes.end());
312 for (
const auto& theme : themes) {
313 const auto& m = material.at(theme);
314 auto value_it = m.find(
"value");
315 if (value_it != m.end() && !value_it->is_null()) {
318 mapping.
theme = theme;
319 mapping.
value = value_it->get<std::uint32_t>();
320 out.push_back(std::move(mapping));
324 auto values_it = m.find(
"values");
325 if (values_it == m.end())
327 if (values_it->is_null()) {
330 mapping.
theme = theme;
331 out.push_back(std::move(mapping));
337 mapping.
theme = theme;
338 switch (sniff_nullable_rank(*values_it)) {
341 for (
const auto& v : *values_it)
342 mapping.
vertices.push_back(material_index(v));
347 mapping.
solids.push_back(
static_cast<std::uint32_t
>(values_it->size()));
348 for (
const auto&
shell : *values_it)
349 push_material_shell(
shell.is_null() ? nullptr : &
shell, mapping);
353 for (
const auto& solid : *values_it) {
354 if (solid.is_null()) {
355 mapping.
solids.push_back(kNull);
357 mapping.
solids.push_back(
static_cast<std::uint32_t
>(solid.size()));
358 for (
const auto&
shell : solid)
359 push_material_shell(
shell.is_null() ? nullptr : &
shell, mapping);
364 out.push_back(std::move(mapping));
369std::vector<TextureMapping>
encode_texture(
const nlohmann::ordered_json& texture) {
370 std::vector<TextureMapping> out;
371 if (!texture.is_object())
377 std::vector<std::string> themes;
378 for (
const auto& [theme, unused] : texture.items())
379 themes.push_back(theme);
380 std::sort(themes.begin(), themes.end());
382 for (
const auto& theme : themes) {
383 const auto& t = texture.at(theme);
385 mapping.
theme = theme;
387 auto values_it = t.find(
"values");
388 if (values_it != t.end() && !values_it->is_null()) {
390 switch (sniff_texture_depth(*values_it)) {
392 for (
const auto&
surface : *values_it)
393 push_textured_surface(
surface, mapping);
394 mapping.
shells.push_back(
static_cast<std::uint32_t
>(values_it->size()));
397 for (
const auto&
shell : *values_it)
398 push_textured_shell(
shell, mapping);
399 mapping.
solids.push_back(
static_cast<std::uint32_t
>(values_it->size()));
402 for (
const auto& solid : *values_it) {
403 for (
const auto&
shell : solid)
404 push_textured_shell(
shell, mapping);
405 mapping.
solids.push_back(
static_cast<std::uint32_t
>(solid.size()));
410 out.push_back(std::move(mapping));
419 auto boundaries_it = geometry.find(
"boundaries");
421 kind, boundaries_it != geometry.end() ? *boundaries_it : nlohmann::ordered_json::array());
423 auto semantics_it = geometry.find(
"semantics");
424 if (semantics_it != geometry.end() && semantics_it->is_object())
427 auto material_it = geometry.find(
"material");
428 if (material_it != geometry.end() && material_it->is_object())
431 auto texture_it = geometry.find(
"texture");
432 if (texture_it != geometry.end() && texture_it->is_object())
Every failure the library reports is one of these.
GeometryKind geometry_kind_from_name(const std::string &name)
Maps a CityJSON geometry type string to GeometryKind.
GMBoundaries encode_boundaries(GeometryKind kind, const nlohmann::ordered_json &boundaries)
Flattens boundaries (nested CityJSON boundary arrays, straight off the JSON as parsed – no intermedia...
GMSemantics encode_semantics(const nlohmann::ordered_json &semantics, const GMBoundaries &boundaries)
Flattens semantics (the CityJSON semantics object: surfaces plus values), using boundaries's shell/so...
std::vector< MaterialMapping > encode_material(const nlohmann::ordered_json &material)
Flattens material (the CityJSON geometry.material object: theme name -> {"value": N} / {"values": [....
EncodedGeometry encode(const nlohmann::ordered_json &geometry)
Flattens one CityJSON geometry object – its boundaries and whatever semantics, material and texture i...
GeometryKind
The FlatBuffers GeometryType enumerators, mirrored here so a caller can name a geometry type without ...
std::vector< TextureMapping > encode_texture(const nlohmann::ordered_json &texture)
Flattens texture (the CityJSON geometry.texture object).
Everything one CityJSON geometry object flattens to.
std::optional< std::vector< TextureMapping > > textures
std::optional< GMSemantics > semantics
std::optional< std::vector< MaterialMapping > > materials
Flattened geometry boundaries: a flat vertex-index list plus one count array per level of the dimensi...
std::vector< std::uint32_t > shells
std::vector< std::uint32_t > solids
std::vector< std::uint32_t > surfaces
Flattened semantics: surfaces is the CityJSON semantics.surfaces array, passed through verbatim (Flat...
nlohmann::ordered_json surfaces
std::optional< std::vector< std::uint32_t > > values
One theme's material mapping.
std::vector< std::uint32_t > vertices
std::vector< std::uint32_t > solids
One theme's texture mapping.
std::vector< std::uint32_t > solids
std::vector< std::uint32_t > shells