6# include <fcb/generated/feature_generated.h>
7# include <fcb/generated/header_generated.h>
52const char*
const kCityObjectTypeNames[] = {
56 "BridgeConstructiveElement",
61 "BuildingInstallation",
62 "BuildingConstructiveElement",
73 "SolitaryVegetationObject",
82 "TunnelConstructiveElement",
89constexpr const char* kUnknownCityObjectName =
"+UnknownCityObject";
91UIntView as_uint_view(
const flatbuffers::Vector<std::uint32_t>* v) {
94 return UIntView(v->data(), v->size());
97const char*
const kSemanticSurfaceTypeNames[] = {
98 "RoofSurface",
"GroundSurface",
"WallSurface",
"ClosureSurface",
99 "OuterCeilingSurface",
"OuterFloorSurface",
"Window",
"Door",
100 "InteriorWallSurface",
"CeilingSurface",
"FloorSurface",
"WaterSurface",
101 "WaterGroundSurface",
"WaterClosureSurface",
"TrafficArea",
"AuxiliaryTrafficArea",
102 "TransportationMarking",
"TransportationHole",
106constexpr const char* kGenericSurfaceName =
"+GenericSurface";
109 return static_cast<GeometryKind>(
static_cast<std::uint8_t
>(g->type()));
118nlohmann::json color_to_json(
const flatbuffers::Vector<double>* c) {
119 if (c ==
nullptr || c->size() != 3)
121 auto out = nlohmann::json::array();
130nlohmann::json border_color_to_json(
const flatbuffers::Vector<double>* c) {
131 if (c ==
nullptr || (c->size() != 3 && c->size() != 4))
133 auto out = nlohmann::json::array();
147[[noreturn]]
void unknown_enum_tag(
const char* member,
int tag) {
149 std::string(
"unknown ") + member +
" tag " + std::to_string(tag));
155const char* texture_format_name(::TextureFormat f) {
157 case ::TextureFormat::PNG:
159 case ::TextureFormat::JPG:
162 unknown_enum_tag(
"type",
static_cast<int>(f));
165const char* wrap_mode_name(::WrapMode w) {
167 case ::WrapMode::None:
169 case ::WrapMode::Wrap:
171 case ::WrapMode::Mirror:
173 case ::WrapMode::Clamp:
175 case ::WrapMode::Border:
178 unknown_enum_tag(
"wrapMode",
static_cast<int>(w));
181const char* texture_type_name(::TextureType t) {
183 case ::TextureType::Unknown:
185 case ::TextureType::Specific:
187 case ::TextureType::Typical:
190 unknown_enum_tag(
"textureType",
static_cast<int>(t));
196nlohmann::json appearance_to_json(const ::Appearance* a) {
197 nlohmann::json out = nlohmann::json::object();
199 if (a->materials() !=
nullptr) {
200 auto materials = nlohmann::json::array();
201 for (
const auto* m : *a->materials()) {
204 nlohmann::json j = nlohmann::json::object();
205 j[
"name"] = (m->name() !=
nullptr) ? m->name()->str() :
"";
208 if (
const auto v = m->ambient_intensity())
209 j[
"ambientIntensity"] = *v;
210 if (
auto c = color_to_json(m->diffuse_color()); !c.is_null())
211 j[
"diffuseColor"] = c;
212 if (
auto c = color_to_json(m->emissive_color()); !c.is_null())
213 j[
"emissiveColor"] = c;
214 if (
auto c = color_to_json(m->specular_color()); !c.is_null())
215 j[
"specularColor"] = c;
216 if (
const auto v = m->shininess())
218 if (
const auto v = m->transparency())
219 j[
"transparency"] = *v;
220 if (
const auto v = m->is_smooth())
222 materials.push_back(std::move(j));
224 out[
"materials"] = std::move(materials);
227 if (a->textures() !=
nullptr) {
228 auto textures = nlohmann::json::array();
229 for (
const auto* t : *a->textures()) {
232 nlohmann::json j = nlohmann::json::object();
233 j[
"type"] = texture_format_name(t->type());
241 if (t->image() !=
nullptr && t->image()->size() > 0) {
242 j[
"image"] = t->image()->str();
244 if (
const auto w = t->wrap_mode())
245 j[
"wrapMode"] = wrap_mode_name(*w);
246 if (
const auto tt = t->texture_type())
247 j[
"textureType"] = texture_type_name(*tt);
248 if (
auto c = border_color_to_json(t->border_color()); !c.is_null()) {
249 j[
"borderColor"] = c;
251 textures.push_back(std::move(j));
253 out[
"textures"] = std::move(textures);
258 if (a->vertices_texture() !=
nullptr) {
259 auto uvs = nlohmann::json::array();
260 for (
const auto* v : *a->vertices_texture()) {
263 std::memcpy(&u,
reinterpret_cast<const std::uint8_t*
>(v),
sizeof(
double));
264 std::memcpy(&w,
reinterpret_cast<const std::uint8_t*
>(v) +
sizeof(
double),
266 uvs.push_back(nlohmann::json::array({u, w}));
268 out[
"vertices-texture"] = std::move(uvs);
271 if (a->default_theme_texture() !=
nullptr) {
272 out[
"default-theme-texture"] = a->default_theme_texture()->str();
274 if (a->default_theme_material() !=
nullptr) {
275 out[
"default-theme-material"] = a->default_theme_material()->str();
291materials_to_json(
const flatbuffers::Vector<flatbuffers::Offset<::MaterialMapping>>* mappings,
293 nlohmann::json out = nlohmann::json::object();
294 for (
const auto* m : *mappings) {
297 const std::string theme = (m->theme() !=
nullptr) ? m->theme()->str() :
"theme";
299 if (
const auto v = m->value()) {
300 out[theme] = {{
"value", *v}};
303 if (m->vertices() ==
nullptr) {
304 out[theme] = {{
"values",
nullptr}};
308 as_uint_view(m->shells()),
309 as_uint_view(m->vertices()))}};
322textures_to_json(
const flatbuffers::Vector<flatbuffers::Offset<::TextureMapping>>* mappings,
324 nlohmann::json out = nlohmann::json::object();
325 for (
const auto* m : *mappings) {
328 const std::string theme = (m->theme() !=
nullptr) ? m->theme()->str() :
"theme";
329 if (m->vertices() ==
nullptr) {
330 out[theme] = nlohmann::json::object();
334 type, as_uint_view(m->solids()), as_uint_view(m->shells()),
335 as_uint_view(m->surfaces()), as_uint_view(m->strings()),
336 as_uint_view(m->vertices()))}};
341nlohmann::json geometry_instance_to_json(const ::GeometryInstance* gi) {
342 nlohmann::json out = nlohmann::json::object();
343 out[
"type"] =
"GeometryInstance";
344 out[
"template"] = gi->template_();
348 auto b = nlohmann::json::array();
349 if (gi->boundaries() !=
nullptr) {
350 for (std::uint32_t v : *gi->boundaries())
353 out[
"boundaries"] = std::move(b);
357 if (
const auto* m = gi->transformation()) {
358 auto mat = nlohmann::json::array();
359 for (std::size_t i = 0; i < 16; ++i) {
361 std::memcpy(&d,
reinterpret_cast<const std::uint8_t*
>(m) + i *
sizeof(
double),
365 out[
"transformationMatrix"] = std::move(mat);
370nlohmann::json geometry_to_json(const ::Geometry* g,
371 const std::vector<ColumnInfo>& semantic_columns) {
372 nlohmann::json out = nlohmann::json::object();
374 if (g->lod() !=
nullptr)
375 out[
"lod"] = g->lod()->str();
378 kind_of(g), as_uint_view(g->solids()), as_uint_view(g->shells()),
379 as_uint_view(g->surfaces()), as_uint_view(g->strings()), as_uint_view(g->boundaries()));
388 if (g->semantics_objects() !=
nullptr) {
389 auto surfaces = nlohmann::json::array();
390 for (
const auto* so : *g->semantics_objects()) {
393 nlohmann::json s = nlohmann::json::object();
394 s[
"type"] = (so->extension_type() !=
nullptr)
395 ? so->extension_type()->str()
401 if (
const auto p = so->parent())
406 if (so->attributes() !=
nullptr && so->attributes()->size() > 0) {
408 bytes_view(so->attributes()->data(), so->attributes()->size()),
410 for (
auto& [k, v] : attrs.items())
413 if (so->children() !=
nullptr && so->children()->size() > 0) {
414 auto kids = nlohmann::json::array();
415 for (std::uint32_t c : *so->children())
417 s[
"children"] = std::move(kids);
422 nlohmann::json sem = nlohmann::json::object();
423 sem[
"surfaces"] = std::move(
surfaces);
428 if (g->semantics() ==
nullptr) {
429 sem[
"values"] =
nullptr;
433 as_uint_view(g->shells()), as_uint_view(g->semantics()));
435 out[
"semantics"] = std::move(sem);
449 if (g->material() !=
nullptr && g->material()->size() > 0) {
450 out[
"material"] = materials_to_json(g->material(), kind_of(g));
452 if (g->texture() !=
nullptr && g->texture()->size() > 0) {
453 out[
"texture"] = textures_to_json(g->texture(), kind_of(g));
467nlohmann::json point_of_contact_address_to_json(
const FileInfo& info) {
468 nlohmann::json addr = nlohmann::json::object();
469 const auto insert = [&addr](
const char*
key,
const std::string& value) {
473 insert(
"thoroughfareNumber", info.poc_address_thoroughfare_number);
474 insert(
"thoroughfareName", info.poc_address_thoroughfare_name);
475 insert(
"locality", info.poc_address_locality);
476 insert(
"postcode", info.poc_address_postcode);
477 insert(
"country", info.poc_address_country);
509nlohmann::json point_of_contact_to_json(
const FileInfo& info) {
510 if (!info.poc_contact_name.has_value())
512 if (!info.poc_email.has_value()) {
516 nlohmann::json poc = nlohmann::json::object();
517 poc[
"contactName"] = *info.poc_contact_name;
518 if (info.poc_contact_type.has_value())
519 poc[
"contactType"] = *info.poc_contact_type;
520 if (info.poc_role.has_value())
521 poc[
"role"] = *info.poc_role;
522 if (info.poc_phone.has_value())
523 poc[
"phone"] = *info.poc_phone;
524 poc[
"emailAddress"] = *info.poc_email;
525 if (info.poc_website.has_value())
526 poc[
"website"] = *info.poc_website;
527 if (
auto addr = point_of_contact_address_to_json(info); !addr.is_null()) {
528 poc[
"address"] = std::move(addr);
541nlohmann::json extensions_to_json(const ::Header* hdr) {
542 if (hdr ==
nullptr || hdr->extensions() ==
nullptr)
545 nlohmann::json out = nlohmann::json::object();
546 for (
const auto* e : *hdr->extensions()) {
547 if (e ==
nullptr || e->name() ==
nullptr)
549 nlohmann::json entry = nlohmann::json::object();
550 entry[
"url"] = (e->url() !=
nullptr) ? e->url()->str() :
"";
551 entry[
"version"] = (e->version() !=
nullptr) ? e->version()->str() :
"";
552 out[e->name()->str()] = std::move(entry);
554 return out.empty() ? nlohmann::json(
nullptr) : out;
560 constexpr std::size_t kCount =
sizeof(kCityObjectTypeNames) /
sizeof(kCityObjectTypeNames[0]);
568 return kUnknownCityObjectName;
569 return kCityObjectTypeNames[type];
573 constexpr std::size_t kCount =
574 sizeof(kSemanticSurfaceTypeNames) /
sizeof(kSemanticSurfaceTypeNames[0]);
581 return kGenericSurfaceName;
582 return kSemanticSurfaceTypeNames[type];
586 const auto& info = header.
info();
587 nlohmann::json cj = nlohmann::json::object();
589 cj[
"type"] =
"CityJSON";
590 cj[
"version"] = info.cityjson_version;
600 {
"scale", info.has_transform ? info.scale : std::array<double, 3>{1.0, 1.0, 1.0}},
601 {
"translate", info.has_transform ? info.translate : std::array<double, 3>{0.0, 0.0, 0.0}}};
614 nlohmann::json meta = nlohmann::json::object();
615 meta[
"geographicalExtent"] =
616 info.has_extent ? info.geographical_extent : std::array<double, 6>{};
617 if (!info.crs.empty()) {
618 meta[
"referenceSystem"] =
"https://www.opengis.net/def/crs/" +
619 info.crs.substr(0, info.crs.find(
':')) +
"/0/" +
620 info.crs.substr(info.crs.find(
':') + 1);
622 if (info.identifier.has_value())
623 meta[
"identifier"] = *info.identifier;
624 if (
auto poc = point_of_contact_to_json(info); !poc.is_null()) {
625 meta[
"pointOfContact"] = std::move(poc);
627 if (info.reference_date.has_value())
628 meta[
"referenceDate"] = *info.reference_date;
629 if (info.title.has_value())
630 meta[
"title"] = *info.title;
631 cj[
"metadata"] = std::move(meta);
634 cj[
"CityObjects"] = nlohmann::json::object();
635 cj[
"vertices"] = nlohmann::json::array();
642 if (
auto ext = extensions_to_json(hdr); !ext.is_null()) {
643 cj[
"extensions"] = std::move(ext);
651 if (hdr !=
nullptr && hdr->appearance() !=
nullptr) {
652 cj[
"appearance"] = appearance_to_json(hdr->appearance());
655 if (hdr !=
nullptr && hdr->templates() !=
nullptr && hdr->templates_vertices() !=
nullptr) {
656 auto templates = nlohmann::json::array();
657 for (
const auto* t : *hdr->templates()) {
659 templates.push_back(geometry_to_json(t, info.semantic_columns));
665 auto verts = nlohmann::json::array();
666 for (
const auto* v : *hdr->templates_vertices()) {
667 std::array<double, 3> xyz{};
668 for (std::size_t i = 0; i < 3; ++i) {
669 std::memcpy(&xyz[i],
reinterpret_cast<const std::uint8_t*
>(v) + i *
sizeof(
double),
672 verts.push_back(nlohmann::json::array({xyz[0], xyz[1], xyz[2]}));
675 cj[
"geometry-templates"] = {{
"templates", std::move(templates)},
676 {
"vertices-templates", std::move(verts)}};
688 nlohmann::json out = nlohmann::json::object();
689 out[
"type"] =
"CityJSONFeature";
690 out[
"id"] = feature.
id();
692 nlohmann::json objects = nlohmann::json::object();
694 for (std::size_t i = 0; i < n; ++i) {
695 const auto* obj = cf->objects()->Get(
static_cast<flatbuffers::uoffset_t
>(i));
699 nlohmann::json co = nlohmann::json::object();
700 co[
"type"] = (obj->extension_type() !=
nullptr)
701 ? obj->extension_type()->str()
717 std::array<double, 6> extent{};
719 co[
"geographicalExtent"] = extent;
722 auto geoms = nlohmann::json::array();
723 if (obj->geometry() !=
nullptr) {
724 for (
const auto* g : *obj->geometry()) {
733 if (obj->geometry_instances() !=
nullptr) {
734 for (
const auto* gi : *obj->geometry_instances()) {
736 geoms.push_back(geometry_instance_to_json(gi));
740 co[
"geometry"] = std::move(geoms);
742 if (obj->children() !=
nullptr && obj->children()->size() > 0) {
743 auto kids = nlohmann::json::array();
744 for (
const auto* c : *obj->children()) {
746 kids.push_back(c->str());
748 co[
"children"] = std::move(kids);
750 if (obj->parents() !=
nullptr && obj->parents()->size() > 0) {
751 auto ps = nlohmann::json::array();
752 for (
const auto* p : *obj->parents()) {
754 ps.push_back(p->str());
756 co[
"parents"] = std::move(ps);
759 objects[feature.
object_id(i)] = std::move(co);
761 out[
"CityObjects"] = std::move(objects);
765 auto verts = nlohmann::json::array();
766 if (cf->vertices() !=
nullptr) {
767 for (
const auto* v : *cf->vertices()) {
768 verts.push_back(nlohmann::json::array({v->x(), v->y(), v->z()}));
771 out[
"vertices"] = std::move(verts);
776 if (cf->appearance() !=
nullptr) {
777 out[
"appearance"] = appearance_to_json(cf->appearance());
Every failure the library reports is one of these.
One decoded feature that OWNS the bytes it points into.
bool object_extent(std::size_t i, std::array< double, 6 > &out) const
CityObject i's own bounding box, if it declares one.
bool object_has_attributes(std::size_t i) const
Whether CityObject i declares an attributes vector at all.
bytes_view object_attributes(std::size_t i) const
Raw attribute blob of CityObject i, or empty if it has none.
std::string object_id(std::size_t i) const
CityObject i's id.
bool object_has_columns(std::size_t i) const
CityObject i's own column schema, if it declares one.
std::string id() const
The feature's CityJSON id. Empty when empty().
std::vector< ColumnInfo > object_columns(std::size_t i) const
std::size_t city_object_count() const
How many CityObjects this feature carries. Zero when empty().
nlohmann::json decode_boundaries(GeometryKind type, UIntView solids, UIntView shells, UIntView surfaces, UIntView strings, UIntView indices)
Rebuild CityJSON's nested boundaries from the five flattened arrays, at the depth type implies.
nlohmann::json decode_texture_values(GeometryKind type, UIntView solids, UIntView shells, UIntView surfaces, UIntView strings, UIntView vertices)
Rebuild the values array of one texture theme from a TextureMapping.
span< const std::uint8_t > bytes_view
The workhorse alias: a read-only view over bytes.
nlohmann::json attributes_to_json(bytes_view blob, const std::vector< ColumnInfo > &schema)
Same decode, rendered as a JSON object for CityJSON emission.
span< const std::uint32_t > UIntView
nlohmann::json to_cityjson_feature(const Feature &feature, const HeaderView &header)
One feature as a CityJSONFeature object.
nlohmann::json decode_material_values(GeometryKind type, UIntView solids, UIntView shells, UIntView vertices)
Rebuild the values array of one material theme from a MaterialMapping.
std::string semantic_surface_type_name(std::uint8_t type)
CityJSON name for a SemanticSurfaceType enumerator, e.g.
nlohmann::json decode_semantics_values(GeometryKind type, UIntView solids, UIntView shells, UIntView values)
Rebuild the nested semantics values array from the flat run of semantic indices.
GeometryKind
The FlatBuffers GeometryType enumerators, mirrored here so a caller can name a geometry type without ...
nlohmann::json to_cityjson_metadata(const HeaderView &header)
The CityJSON metadata envelope: type, version, transform, extent, CRS.
std::string geometry_type_name(std::uint8_t type)
CityJSON name for a GeometryType enumerator, e.g.
std::string city_object_type_name(std::uint8_t type)
CityJSON name for a CityObjectType enumerator, e.g.
std::vector< ColumnInfo > columns
std::vector< ColumnInfo > semantic_columns
Schema for SemanticObject.attributes, which is separate from the feature attribute schema (Header....
static const ::CityFeature * get(const Feature &f)