11std::string as_str_or_empty(
const nlohmann::ordered_json& obj,
const std::string&
key) {
12 auto it = obj.find(
key);
13 if (it == obj.end() || !it->is_string())
15 return it->get<std::string>();
24std::string require_string_field(
const nlohmann::ordered_json& obj,
const std::string&
key) {
25 auto it = obj.find(
key);
26 if (it == obj.end() || !it->is_string())
28 "pointOfContact." +
key +
" is required and must be a string");
29 return it->get<std::string>();
42std::optional<std::string> optional_string_field(
const nlohmann::ordered_json& obj,
43 const std::string&
key) {
44 auto it = obj.find(
key);
45 if (it == obj.end() || !it->is_string())
47 return it->get<std::string>();
55std::optional<std::string> address_member(
const nlohmann::ordered_json& address,
56 const std::string&
key) {
57 auto it = address.find(
key);
58 if (it == address.end() || it->is_null())
61 return it->get<std::string>();
65std::optional<std::string> address_either(
const nlohmann::ordered_json& address,
66 const std::string& a,
const std::string& b) {
67 if (
auto v = address_member(address, a))
69 return address_member(address, b);
72std::int32_t parse_i32_whole(std::string_view s) {
81 if (s.front() ==
'+') {
83 if (s.empty() || !(s.front() >=
'0' && s.front() <=
'9'))
86 std::int32_t value = 0;
87 auto [ptr, ec] = std::from_chars(s.data(), s.data() + s.size(), value);
88 if (ec != std::errc() || ptr != s.data() + s.size())
96 const auto& scale = transform.at(
"scale");
97 const auto& translate = transform.at(
"translate");
99 ::Vector(scale.at(0).get<
double>(), scale.at(1).get<
double>(), scale.at(2).get<
double>()),
100 ::Vector(translate.at(0).get<
double>(), translate.at(1).get<
double>(),
101 translate.at(2).get<
double>()));
105 static constexpr std::string_view kPrefixes[] = {
"http://www.opengis.net/def/crs/",
106 "https://www.opengis.net/def/crs/"};
107 for (
const auto& prefix : kPrefixes) {
108 if (url.compare(0, prefix.size(), prefix) != 0)
111 const std::string rest = url.substr(prefix.size());
112 std::vector<std::string> segments;
113 std::size_t start = 0;
115 const std::size_t pos = rest.find(
'/', start);
116 const std::size_t end = pos == std::string::npos ? rest.size() : pos;
117 segments.push_back(rest.substr(start, end - start));
118 if (pos == std::string::npos)
125 out.
version = segments.size() > 1 ? parse_i32_whole(segments[1]) : 0;
126 out.
code = segments.size() > 2 ? parse_i32_whole(segments[2]) : 0;
134 auto authority = fbb.CreateString(ref_sys.
authority);
135 return CreateReferenceSystem(fbb, authority, ref_sys.
version, ref_sys.
code, 0);
138::flatbuffers::Offset<::Extension>
to_extension(::flatbuffers::FlatBufferBuilder& fbb,
139 const std::string& name,
const std::string& url,
140 const std::string& version) {
141 auto name_off = fbb.CreateString(name);
142 auto url_off = fbb.CreateString(url);
143 auto version_off = fbb.CreateString(version);
144 return CreateExtension(fbb, name_off, 0, url_off, version_off);
147::flatbuffers::Offset<::flatbuffers::Vector<const ::DoubleVertex*>>
149 const nlohmann::ordered_json& vertices_templates) {
150 std::vector<::DoubleVertex> verts;
151 if (vertices_templates.is_array()) {
152 for (
const auto& v : vertices_templates) {
155 std::vector<double> coords;
156 for (
const auto& c : v)
158 coords.push_back(c.get<
double>());
159 if (coords.size() == 3)
160 verts.emplace_back(coords[0], coords[1], coords[2]);
163 return fbb.CreateVectorOfStructs(verts);
167 const nlohmann::ordered_json& poc) {
169 out.
contact_name = fbb.CreateString(require_string_field(poc,
"contactName"));
171 if (
auto v = optional_string_field(poc,
"contactType"))
173 if (
auto v = optional_string_field(poc,
"role"))
174 out.
role = fbb.CreateString(*v);
175 if (
auto v = optional_string_field(poc,
"phone"))
176 out.
phone = fbb.CreateString(*v);
177 out.
email = fbb.CreateString(require_string_field(poc,
"emailAddress"));
178 if (
auto v = optional_string_field(poc,
"website"))
179 out.
website = fbb.CreateString(*v);
186 if (
auto addr_it = poc.find(
"address"); addr_it != poc.end() && addr_it->is_object()) {
187 const nlohmann::ordered_json& address = *addr_it;
188 if (
auto v = address_member(address,
"thoroughfareNumber"))
190 if (
auto v = address_member(address,
"thoroughfareName"))
192 if (
auto v = address_member(address,
"locality"))
194 if (
auto v = address_either(address,
"postcode",
"postalCode"))
196 if (
auto v = address_member(address,
"country"))
202::flatbuffers::Offset<::Header>
203to_fcb_header(::flatbuffers::FlatBufferBuilder& fbb,
const nlohmann::ordered_json& cj,
206 const std::vector<AttributeIndexInfo>* attribute_indices_info) {
207 auto version = fbb.CreateString(cj.at(
"version").get<std::string>());
208 ::Transform transform =
to_transform(cj.at(
"transform"));
212 std::optional<::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset<::Column>>>>
214 if (semantic_attr_schema !=
nullptr)
215 semantic_columns =
to_columns(fbb, *semantic_attr_schema);
219 std::optional<::flatbuffers::Offset<::flatbuffers::Vector<const ::AttributeIndex*>>>
221 if (attribute_indices_info !=
nullptr) {
222 std::vector<::AttributeIndex> entries;
223 entries.reserve(attribute_indices_info->size());
224 for (
const auto& info : *attribute_indices_info)
225 entries.emplace_back(info.index, info.length, info.branching_factor,
226 info.num_unique_items);
227 attribute_index = fbb.CreateVectorOfStructs(entries);
230 std::optional<::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset<::Extension>>>>
232 if (
auto it = cj.find(
"extensions"); it != cj.end() && it->is_object()) {
233 std::vector<::flatbuffers::Offset<::Extension>> ext_offs;
234 for (
const auto& [name, ext] : it->items())
235 ext_offs.push_back(
to_extension(fbb, name, as_str_or_empty(ext,
"url"),
236 as_str_or_empty(ext,
"version")));
237 extensions = fbb.CreateVector(ext_offs);
240 std::optional<::GeographicalExtent> geographical_extent;
244 std::optional<::flatbuffers::Offset<::Appearance>> appearance;
245 if (
auto it = cj.find(
"appearance"); it != cj.end() && it->is_object())
248 std::optional<::flatbuffers::Offset<::flatbuffers::Vector<const ::DoubleVertex*>>>
250 std::optional<::flatbuffers::Offset<::flatbuffers::Vector<::flatbuffers::Offset<::Geometry>>>>
252 if (
auto gm_it = cj.find(
"geometry-templates"); gm_it != cj.end() && gm_it->is_object()) {
255 std::vector<::flatbuffers::Offset<::Geometry>> geom_offs;
256 for (
const auto& g : gm_it->at(
"templates"))
257 geom_offs.push_back(
to_geometry(fbb, g, semantic_attr_schema));
258 templates = fbb.CreateVector(geom_offs);
261 std::optional<::flatbuffers::Offset<::ReferenceSystem>> reference_system;
262 std::optional<::flatbuffers::Offset<::flatbuffers::String>> identifier;
263 std::optional<::flatbuffers::Offset<::flatbuffers::String>> reference_date;
264 std::optional<::flatbuffers::Offset<::flatbuffers::String>> title;
267 if (
auto meta_it = cj.find(
"metadata"); meta_it != cj.end() && meta_it->is_object()) {
268 const nlohmann::ordered_json& meta = *meta_it;
270 if (
auto rs_it = meta.find(
"referenceSystem"); rs_it != meta.end() && rs_it->is_string()) {
275 if (!geographical_extent) {
276 if (
auto ge_it = meta.find(
"geographicalExtent");
277 ge_it != meta.end() && ge_it->is_array() && ge_it->size() == 6) {
278 std::array<double, 6> extent{};
279 for (std::size_t i = 0; i < 6; ++i)
280 extent[i] = ge_it->at(i).get<
double>();
285 if (
auto v = optional_string_field(meta,
"identifier"))
286 identifier = fbb.CreateString(*v);
287 if (
auto v = optional_string_field(meta,
"referenceDate"))
288 reference_date = fbb.CreateString(*v);
289 if (
auto v = optional_string_field(meta,
"title"))
290 title = fbb.CreateString(*v);
292 if (
auto poc_it = meta.find(
"pointOfContact"); poc_it != meta.end() && poc_it->is_object())
310 fbb, &transform, appearance.value_or(0), columns, semantic_columns.value_or(0),
311 features_count, index_node_size, attribute_index.value_or(0),
312 geographical_extent ? &*geographical_extent :
nullptr, reference_system.value_or(0),
313 identifier.value_or(0), reference_date.value_or(0), title.value_or(0),
314 templates.value_or(0), templates_vertices.value_or(0), extensions.value_or(0),
::flatbuffers::Offset<::flatbuffers::Vector< const ::DoubleVertex * > > to_templates_vertices(::flatbuffers::FlatBufferBuilder &fbb, const nlohmann::ordered_json &vertices_templates)
Builds Header.templates_vertices from CityJSON's geometry-templates.vertices-templates (f64 precision...
::Transform to_transform(const nlohmann::ordered_json &transform)
Builds the Transform struct (scale + translate) from CityJSON's top-level transform member.
::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.
PocOffsets to_point_of_contact(::flatbuffers::FlatBufferBuilder &fbb, const nlohmann::ordered_json &poc)
::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).
::flatbuffers::Offset<::ReferenceSystem > to_reference_system(::flatbuffers::FlatBufferBuilder &fbb, const ParsedReferenceSystem &ref_sys)
Builds the ReferenceSystem table from a parsed metadata.referenceSystem URL.
::flatbuffers::Offset<::Extension > to_extension(::flatbuffers::FlatBufferBuilder &fbb, const std::string &name, const std::string &url, const std::string &version)
Builds one extensions entry: only name/url/version are written (the schema document itself is never f...
std::optional< ParsedReferenceSystem > parse_reference_system(const std::string &url)
Parses a referenceSystem URL (https://www.opengis.net/def/crs/{authority}/{version}/{code},...
::flatbuffers::Offset<::Header > to_fcb_header(::flatbuffers::FlatBufferBuilder &fbb, const nlohmann::ordered_json &cj, const HeaderWriterOptions &options, const AttributeSchema &attr_schema, const AttributeSchema *semantic_attr_schema, const std::vector< AttributeIndexInfo > *attribute_indices_info)
Builds the whole Header table from the CityJSON metadata line (the first line of a CityJSONSeq: type/...
A metadata.referenceSystem URL, parsed into its OGC three-element form.
Builds the pointOfContact fields of a header from CityJSON metadata.pointOfContact.
std::optional<::flatbuffers::Offset<::flatbuffers::String > > contact_name
std::optional<::flatbuffers::Offset<::flatbuffers::String > > role
std::optional<::flatbuffers::Offset<::flatbuffers::String > > phone
std::optional<::flatbuffers::Offset<::flatbuffers::String > > website
std::optional<::flatbuffers::Offset<::flatbuffers::String > > address_postcode
std::optional<::flatbuffers::Offset<::flatbuffers::String > > email
std::optional<::flatbuffers::Offset<::flatbuffers::String > > address_thoroughfare_number
std::optional<::flatbuffers::Offset<::flatbuffers::String > > address_country
std::optional<::flatbuffers::Offset<::flatbuffers::String > > address_thoroughfare_name
std::optional<::flatbuffers::Offset<::flatbuffers::String > > contact_type
std::optional<::flatbuffers::Offset<::flatbuffers::String > > address_locality