17const char*
const kCityObjectTypeNames[] = {
21 "BridgeConstructiveElement",
26 "BuildingInstallation",
27 "BuildingConstructiveElement",
38 "SolitaryVegetationObject",
47 "TunnelConstructiveElement",
52constexpr std::size_t kCityObjectTypeCount =
53 sizeof(kCityObjectTypeNames) /
sizeof(kCityObjectTypeNames[0]);
55const char*
const kSemanticSurfaceTypeNames[] = {
56 "RoofSurface",
"GroundSurface",
"WallSurface",
"ClosureSurface",
57 "OuterCeilingSurface",
"OuterFloorSurface",
"Window",
"Door",
58 "InteriorWallSurface",
"CeilingSurface",
"FloorSurface",
"WaterSurface",
59 "WaterGroundSurface",
"WaterClosureSurface",
"TrafficArea",
"AuxiliaryTrafficArea",
60 "TransportationMarking",
"TransportationHole",
62constexpr std::size_t kSemanticSurfaceTypeCount =
63 sizeof(kSemanticSurfaceTypeNames) /
sizeof(kSemanticSurfaceTypeNames[0]);
65std::optional<::flatbuffers::Offset<::flatbuffers::Vector<double>>>
66to_color(::flatbuffers::FlatBufferBuilder& fbb,
const nlohmann::ordered_json& obj,
68 auto it = obj.find(
key);
69 if (it == obj.end() || it->is_null())
71 std::vector<double> v;
72 for (
const auto& c : *it)
73 v.push_back(c.get<double>());
74 return fbb.CreateVector(v);
83::flatbuffers::Offset<::SemanticObject>
84to_semantic_object(::flatbuffers::FlatBufferBuilder& fbb,
const nlohmann::ordered_json&
surface,
86 auto [type_, extension_type_name] =
95 std::optional<::flatbuffers::Offset<::flatbuffers::Vector<std::uint32_t>>> children;
96 if (
auto it =
surface.find(
"children"); it !=
surface.end() && it->is_array()) {
97 std::vector<std::uint32_t> c;
98 for (
const auto& v : *it)
99 c.push_back(v.get<std::uint32_t>());
100 children = fbb.CreateVector(c);
103 auto extension_type =
104 extension_type_name ? std::optional(fbb.CreateString(*extension_type_name)) : std::nullopt;
106 ::flatbuffers::Optional<std::uint32_t> parent = ::flatbuffers::nullopt;
107 if (
auto it =
surface.find(
"parent"); it !=
surface.end() && !it->is_null())
108 parent = it->get<std::uint32_t>();
110 nlohmann::ordered_json other = nlohmann::ordered_json::object();
111 for (
const auto& [
key, val] :
surface.items())
112 if (
key !=
"type" &&
key !=
"parent" &&
key !=
"children")
115 std::optional<::flatbuffers::Offset<::flatbuffers::Vector<std::uint8_t>>> attributes;
116 if (!other.empty() && semantic_attr_schema !=
nullptr) {
120 return CreateSemanticObject(fbb, type_, attributes.value_or(0), children.value_or(0), parent,
121 extension_type.value_or(0));
127 for (std::size_t i = 0; i < kCityObjectTypeCount; ++i) {
128 if (name == kCityObjectTypeNames[i])
129 return {
static_cast<::CityObjectType
>(i), std::nullopt};
131 return {::CityObjectType::ExtensionObject, name};
135 for (std::size_t i = 0; i < kSemanticSurfaceTypeCount; ++i) {
136 if (name == kSemanticSurfaceTypeNames[i])
137 return {
static_cast<::SemanticSurfaceType
>(i), std::nullopt};
139 return {::SemanticSurfaceType::ExtraSemanticSurface, name};
142::flatbuffers::Offset<::Appearance>
to_appearance(::flatbuffers::FlatBufferBuilder& fbb,
143 const nlohmann::ordered_json& appearance) {
144 std::optional<::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset<::Material>>>>
146 if (
auto it = appearance.find(
"materials"); it != appearance.end() && it->is_array()) {
147 std::vector<::flatbuffers::Offset<::Material>> materials;
148 for (
const auto& m : *it) {
156 auto name = fbb.CreateString(m.at(
"name").get<std::string>());
157 auto diffuse_color = to_color(fbb, m,
"diffuseColor");
158 auto emissive_color = to_color(fbb, m,
"emissiveColor");
159 auto specular_color = to_color(fbb, m,
"specularColor");
161 auto ambient = m.find(
"ambientIntensity");
162 auto shininess = m.find(
"shininess");
163 auto transparency = m.find(
"transparency");
164 auto is_smooth = m.find(
"isSmooth");
165 materials.push_back(CreateMaterial(
167 ambient != m.end() && !ambient->is_null()
168 ? ::flatbuffers::Optional<double>(ambient->get<
double>())
169 : ::flatbuffers::nullopt,
170 diffuse_color.value_or(0), emissive_color.value_or(0), specular_color.value_or(0),
171 shininess != m.end() && !shininess->is_null()
172 ? ::flatbuffers::Optional<double>(shininess->get<
double>())
173 : ::flatbuffers::nullopt,
174 transparency != m.end() && !transparency->is_null()
175 ? ::flatbuffers::Optional<double>(transparency->get<
double>())
176 : ::flatbuffers::nullopt,
177 is_smooth != m.end() && !is_smooth->is_null()
178 ? ::flatbuffers::Optional<bool>(is_smooth->get<
bool>())
179 : ::flatbuffers::nullopt));
181 materials_off = fbb.CreateVector(materials);
184 std::optional<::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset<::Texture>>>>
186 if (
auto it = appearance.find(
"textures"); it != appearance.end() && it->is_array()) {
187 std::vector<::flatbuffers::Offset<::Texture>> textures;
188 for (
const auto& t : *it) {
191 auto type_it = t.find(
"type");
192 const ::TextureFormat format = (type_it != t.end() && *type_it ==
"JPG")
193 ? ::TextureFormat::JPG
194 : ::TextureFormat::PNG;
195 auto image = fbb.CreateString(t.value(
"image", std::string()));
197 ::flatbuffers::Optional<::WrapMode> wrap_mode = ::flatbuffers::nullopt;
198 if (
auto w = t.find(
"wrapMode"); w != t.end() && !w->is_null()) {
199 const std::string& s = w->get_ref<
const std::string&>();
201 wrap_mode = ::WrapMode::None;
202 else if (s ==
"wrap")
203 wrap_mode = ::WrapMode::Wrap;
204 else if (s ==
"mirror")
205 wrap_mode = ::WrapMode::Mirror;
206 else if (s ==
"clamp")
207 wrap_mode = ::WrapMode::Clamp;
208 else if (s ==
"border")
209 wrap_mode = ::WrapMode::Border;
212 ::flatbuffers::Optional<::TextureType> texture_type = ::flatbuffers::nullopt;
213 if (
auto tt = t.find(
"textureType"); tt != t.end() && !tt->is_null()) {
214 const std::string& s = tt->get_ref<
const std::string&>();
216 texture_type = ::TextureType::Unknown;
217 else if (s ==
"specific")
218 texture_type = ::TextureType::Specific;
219 else if (s ==
"typical")
220 texture_type = ::TextureType::Typical;
227 auto border_color = to_color(fbb, t,
"borderColor");
228 textures.push_back(CreateTexture(fbb, format, image, wrap_mode, texture_type,
229 border_color.value_or(0)));
231 textures_off = fbb.CreateVector(textures);
234 std::optional<::flatbuffers::Offset<::flatbuffers::Vector<const ::Vec2*>>> vertices_texture_off;
235 if (
auto it = appearance.find(
"vertices-texture"); it != appearance.end() && it->is_array()) {
236 std::vector<::Vec2> uvs;
237 for (
const auto& v : *it)
238 uvs.push_back(::Vec2(v.at(0).get<
double>(), v.at(1).get<
double>()));
239 vertices_texture_off = fbb.CreateVectorOfStructs(uvs);
242 std::optional<::flatbuffers::Offset<::flatbuffers::String>> default_theme_texture_off;
243 if (
auto it = appearance.find(
"default-theme-texture");
244 it != appearance.end() && it->is_string())
245 default_theme_texture_off = fbb.CreateString(it->get<std::string>());
247 std::optional<::flatbuffers::Offset<::flatbuffers::String>> default_theme_material_off;
248 if (
auto it = appearance.find(
"default-theme-material");
249 it != appearance.end() && it->is_string())
250 default_theme_material_off = fbb.CreateString(it->get<std::string>());
252 return CreateAppearance(fbb, materials_off.value_or(0), textures_off.value_or(0),
253 vertices_texture_off.value_or(0), default_theme_texture_off.value_or(0),
254 default_theme_material_off.value_or(0));
257::flatbuffers::Offset<::Geometry>
to_geometry(::flatbuffers::FlatBufferBuilder& fbb,
258 const nlohmann::ordered_json& geometry,
261 const auto type_ =
static_cast<::GeometryType
>(
kind);
262 auto lod = geometry.find(
"lod");
263 auto lod_off = (lod != geometry.end() && lod->is_string())
264 ? std::optional(fbb.CreateString(lod->get<std::string>()))
274 std::optional<::flatbuffers::Offset<::flatbuffers::Vector<std::uint32_t>>> semantics_values_off;
276 ::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset<::SemanticObject>>>>
277 semantics_objects_off;
279 std::vector<::flatbuffers::Offset<::SemanticObject>> objects;
281 objects.push_back(to_semantic_object(fbb,
surface, semantic_attr_schema));
282 semantics_objects_off = fbb.CreateVector(objects);
284 semantics_values_off = fbb.CreateVector(*encoded.
semantics->values);
288 ::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset<::MaterialMapping>>>>
291 std::vector<::flatbuffers::Offset<::MaterialMapping>> mappings;
292 for (
const auto& m : *encoded.
materials) {
293 auto theme = fbb.CreateString(m.theme);
296 mappings.push_back(CreateMaterialMapping(
297 fbb, theme, 0, 0, 0, ::flatbuffers::Optional<std::uint32_t>(m.value)));
308 auto solids = fbb.CreateVector(m.solids);
309 auto shells = fbb.CreateVector(m.shells);
310 auto vertices = fbb.CreateVector(m.vertices);
311 mappings.push_back(CreateMaterialMapping(fbb, theme, solids,
shells,
vertices,
312 ::flatbuffers::nullopt));
317 CreateMaterialMapping(fbb, theme, 0, 0, 0, ::flatbuffers::nullopt));
321 material_off = fbb.CreateVector(mappings);
325 ::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset<::TextureMapping>>>>
328 std::vector<::flatbuffers::Offset<::TextureMapping>> mappings;
329 for (
const auto& t : *encoded.
textures) {
330 auto theme = fbb.CreateString(t.theme);
337 auto solids = fbb.CreateVector(t.solids);
338 auto shells = fbb.CreateVector(t.shells);
339 auto surfaces = fbb.CreateVector(t.surfaces);
340 auto strings = fbb.CreateVector(t.strings);
341 auto vertices = fbb.CreateVector(t.vertices);
345 mappings.push_back(CreateTextureMapping(fbb, theme, 0, 0, 0, 0, 0));
348 texture_off = fbb.CreateVector(mappings);
351 return CreateGeometry(fbb, type_, lod_off.value_or(0), solids_off, shells_off, surfaces_off,
352 strings_off, boundaries_off, semantics_values_off.value_or(0),
353 semantics_objects_off.value_or(0), material_off.value_or(0),
354 texture_off.value_or(0));
357::flatbuffers::Offset<::GeometryInstance>
359 const nlohmann::ordered_json& geometry) {
360 if (geometry.at(
"type").get<std::string>() !=
"GeometryInstance") {
362 "to_geometry_instance called on a non-GeometryInstance geometry");
365 const std::uint32_t template_ = geometry.at(
"template").get<std::uint32_t>();
367 std::vector<std::uint32_t> indices;
368 for (
const auto& v : geometry.at(
"boundaries"))
369 indices.push_back(v.get<std::uint32_t>());
370 auto boundaries_off = fbb.CreateVector(indices);
372 const auto& m = geometry.at(
"transformationMatrix");
373 ::TransformationMatrix matrix(
374 m.at(0).get<
double>(), m.at(1).get<
double>(), m.at(2).get<
double>(), m.at(3).get<
double>(),
375 m.at(4).get<
double>(), m.at(5).get<
double>(), m.at(6).get<
double>(), m.at(7).get<
double>(),
376 m.at(8).get<
double>(), m.at(9).get<
double>(), m.at(10).get<
double>(),
377 m.at(11).get<
double>(), m.at(12).get<
double>(), m.at(13).get<
double>(),
378 m.at(14).get<
double>(), m.at(15).get<
double>());
380 return CreateGeometryInstance(fbb, &matrix, template_, boundaries_off);
384 return ::GeographicalExtent(::Vector(extent[0], extent[1], extent[2]),
385 ::Vector(extent[3], extent[4], extent[5]));
394 ::flatbuffers::Offset<::flatbuffers::Vector<std::uint8_t>>
attr_offset;
398FcbAttribute to_fcb_attribute(::flatbuffers::FlatBufferBuilder& fbb,
400 bool is_own_schema =
false;
401 for (
const auto& [
key, val] : attr.items()) {
402 if (schema.find(
key) == schema.end()) {
403 is_own_schema =
true;
411 return {fbb.CreateVector(encoded), std::move(
own_schema)};
414 return {fbb.CreateVector(encoded), std::nullopt};
419::flatbuffers::Offset<::CityObject>
to_city_object(::flatbuffers::FlatBufferBuilder& fbb,
420 const std::string&
id,
421 const nlohmann::ordered_json& co,
424 auto id_off = fbb.CreateString(
id);
425 auto [type_, extension_type_name] =
427 auto extension_type_off =
428 extension_type_name ? std::optional(fbb.CreateString(*extension_type_name)) : std::nullopt;
430 std::optional<::GeographicalExtent> extent;
431 if (
auto it = co.find(
"geographicalExtent");
432 it != co.end() && it->is_array() && it->size() == 6) {
434 it->at(2).get<
double>(), it->at(3).get<
double>(),
435 it->at(4).get<
double>(), it->at(5).get<
double>()});
438 std::optional<::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset<::Geometry>>>>
441 ::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset<::GeometryInstance>>>>
442 geometry_instances_off;
443 if (
auto git = co.find(
"geometry"); git != co.end() && git->is_array()) {
463 std::vector<::flatbuffers::Offset<::Geometry>> geoms;
464 for (
const auto& g : *git)
465 if (g.at(
"type").get<std::string>() !=
"GeometryInstance")
466 geoms.push_back(
to_geometry(fbb, g, semantic_attr_schema));
467 geometry_off = fbb.CreateVector(geoms);
469 std::vector<::flatbuffers::Offset<::GeometryInstance>> instances;
470 for (
const auto& g : *git)
471 if (g.at(
"type").get<std::string>() ==
"GeometryInstance")
477 geometry_instances_off = fbb.CreateVector(instances);
480 std::optional<::flatbuffers::Offset<::flatbuffers::Vector<std::uint8_t>>> attributes_off;
481 std::optional<::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset<::Column>>>>
483 if (
auto ait = co.find(
"attributes"); ait != co.end() && ait->is_object()) {
484 FcbAttribute fcb_attr = to_fcb_attribute(fbb, *ait, attr_schema);
485 attributes_off = fcb_attr.attr_offset;
486 if (fcb_attr.own_schema)
487 columns_off =
to_columns(fbb, *fcb_attr.own_schema);
491 ::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset<::flatbuffers::String>>>>
493 if (
auto it = co.find(
"children"); it != co.end() && it->is_array()) {
494 std::vector<::flatbuffers::Offset<::flatbuffers::String>> c;
495 for (
const auto& s : *it)
496 c.push_back(fbb.CreateString(s.get<std::string>()));
497 children_off = fbb.CreateVector(c);
509 ::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset<::flatbuffers::String>>>>
511 if (
auto it = co.find(
"children_roles"); it != co.end() && it->is_array()) {
512 std::vector<::flatbuffers::Offset<::flatbuffers::String>> r;
513 for (
const auto& role : *it)
515 fbb.CreateString(role.is_string() ? role.get<std::string>() : std::string()));
516 children_roles_off = fbb.CreateVector(r);
520 ::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset<::flatbuffers::String>>>>
522 if (
auto it = co.find(
"parents"); it != co.end() && it->is_array()) {
523 std::vector<::flatbuffers::Offset<::flatbuffers::String>> p;
524 for (
const auto& s : *it)
525 p.push_back(fbb.CreateString(s.get<std::string>()));
526 parents_off = fbb.CreateVector(p);
529 return CreateCityObject(fbb, type_, extension_type_off.value_or(0), id_off,
530 extent ? &*extent :
nullptr, geometry_off.value_or(0),
531 geometry_instances_off.value_or(0), attributes_off.value_or(0),
532 columns_off.value_or(0), children_off.value_or(0),
533 children_roles_off.value_or(0), parents_off.value_or(0));
536std::pair<::flatbuffers::Offset<::CityFeature>, NodeItem>
538 const nlohmann::ordered_json& city_feature,
const AttributeSchema& attr_schema,
540 auto id_off = fbb.CreateString(
id);
545 static const nlohmann::ordered_json kEmptyObjects = nlohmann::ordered_json::object();
546 auto co_it = city_feature.find(
"CityObjects");
547 const nlohmann::ordered_json& city_objects =
548 (co_it != city_feature.end() && co_it->is_object()) ? *co_it : kEmptyObjects;
550 std::vector<std::string> object_ids;
551 object_ids.reserve(city_objects.size());
552 for (
const auto& [oid, unused] : city_objects.items())
553 object_ids.push_back(oid);
554 std::sort(object_ids.begin(), object_ids.end());
556 std::vector<::flatbuffers::Offset<::CityObject>> objects;
557 objects.reserve(object_ids.size());
558 for (
const auto& oid : object_ids)
560 to_city_object(fbb, oid, city_objects.at(oid), attr_schema, semantic_attr_schema));
561 auto objects_off = fbb.CreateVector(objects);
563 std::vector<::Vertex> fb_vertices;
564 double min_x = 0, min_y = 0, max_x = 0, max_y = 0;
566 if (
auto it = city_feature.find(
"vertices"); it != city_feature.end()) {
567 fb_vertices.reserve(it->size());
568 for (
const auto& v : *it) {
569 const double x = v.at(0).get<
double>();
570 const double y = v.at(1).get<
double>();
571 fb_vertices.emplace_back(v.at(0).get<std::int32_t>(), v.at(1).get<std::int32_t>(),
572 v.at(2).get<std::int32_t>());
578 min_x = std::min(min_x, x);
579 max_x = std::max(max_x, x);
580 min_y = std::min(min_y, y);
581 max_y = std::max(max_y, y);
585 auto vertices_off = fbb.CreateVectorOfStructs(fb_vertices);
587 std::optional<::flatbuffers::Offset<::Appearance>> appearance_off;
588 if (
auto it = city_feature.find(
"appearance"); it != city_feature.end() && it->is_object())
591 NodeItem bbox{min_x, min_y, max_x, max_y, 0};
593 CreateCityFeature(fbb, id_off, objects_off, vertices_off, appearance_off.value_or(0));
594 return {feature_off, bbox};
Every failure the library reports is one of these.
::flatbuffers::Offset<::flatbuffers::Vector< std::uint8_t > > attr_offset
std::optional< AttributeSchema > own_schema
GeometryKind geometry_kind_from_name(const std::string &name)
Maps a CityJSON geometry type string to GeometryKind.
std::vector< std::uint8_t > encode_attributes_with_schema(const nlohmann::ordered_json &attr, const AttributeSchema &schema)
Encodes attr (a CityJSON attributes object) against schema: repeated [u16 LE column index][value] rec...
::flatbuffers::Offset<::CityObject > to_city_object(::flatbuffers::FlatBufferBuilder &fbb, const std::string &id, const nlohmann::ordered_json &co, const AttributeSchema &attr_schema, const AttributeSchema *semantic_attr_schema)
Builds one CityObject table: type, geographical extent, geometry (split into non-instance and Geometr...
::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset<::Column > > > to_columns(::flatbuffers::FlatBufferBuilder &fbb, const AttributeSchema &schema)
Builds the Column vector for Header.columns or CityObject.columns, in ascending column-index order.
EncodedGeometry encode(const nlohmann::ordered_json &geometry)
Flattens one CityJSON geometry object – its boundaries and whatever semantics, material and texture i...
::flatbuffers::Offset<::GeometryInstance > to_geometry_instance(::flatbuffers::FlatBufferBuilder &fbb, const nlohmann::ordered_json &geometry)
Builds a GeometryInstance table: the template index, the 4x4 transformation matrix,...
GeometryKind
The FlatBuffers GeometryType enumerators, mirrored here so a caller can name a geometry type without ...
void add_attributes(AttributeSchema &schema, const nlohmann::ordered_json &attrs)
Adds every member of a JSON object to schema, assigning each new, non-null name the next free column ...
CoType city_object_type_from_name(const std::string &name)
Maps a CityJSON CityObject type string to the FlatBuffers tag.
::flatbuffers::Offset<::Appearance > to_appearance(::flatbuffers::FlatBufferBuilder &fbb, const nlohmann::ordered_json &appearance)
Builds the Appearance table (materials, textures, UV vertices, default themes) from a CityJSON appear...
::flatbuffers::Offset<::Geometry > to_geometry(::flatbuffers::FlatBufferBuilder &fbb, const nlohmann::ordered_json &geometry, const AttributeSchema *semantic_attr_schema)
Builds one Geometry table – boundaries, and whatever semantics, material and texture it carries – fro...
::GeographicalExtent to_geographical_extent(const std::array< double, 6 > &extent)
Builds a GeographicalExtent struct from a 6-element [minx,miny,minz,maxx,maxy,maxz] array.
std::map< std::string, std::pair< std::uint16_t, ::ColumnType > > AttributeSchema
Attribute schema: name -> (column index, column type).
std::pair<::flatbuffers::Offset<::CityFeature >, NodeItem > to_fcb_city_feature(::flatbuffers::FlatBufferBuilder &fbb, const std::string &id, const nlohmann::ordered_json &city_feature, const AttributeSchema &attr_schema, const AttributeSchema *semantic_attr_schema)
Builds one CityFeature table – its CityObjects (visited in ascending id order), vertices,...
SurfaceType semantic_surface_type_from_name(const std::string &name)
Maps a CityJSON semantic surface type string to the FlatBuffers tag.
A CityObjectType tag, plus the verbatim CityJSON name when the tag is ExtensionObject (which has no s...
Everything one CityJSON geometry object flattens to.
std::optional< std::vector< TextureMapping > > textures
std::optional< GMSemantics > semantics
std::optional< std::vector< MaterialMapping > > materials
std::vector< std::uint32_t > strings
std::vector< std::uint32_t > shells
std::vector< std::uint32_t > solids
std::vector< std::uint32_t > surfaces
std::vector< std::uint32_t > indices
One R-tree node entry: 4 doubles then a u64, all little-endian, 40 bytes with no padding (packed_rtre...
A SemanticSurfaceType tag, plus the verbatim CityJSON name when the tag is ExtraSemanticSurface.