1use std::{collections::HashMap, mem::size_of};
2
3use crate::{
4 error::Error,
5 fb::*,
6 geom_decoder::{
7 decode_materials, decode_points, decode_rings, decode_semantics, decode_shells,
8 decode_solids, decode_surfaces, decode_textures,
9 },
10};
11use byteorder::{ByteOrder, LittleEndian};
12use cjseq::{
13 Address as CjAddress, Appearance as CjAppearance, BorderColor as CjBorderColor, CityJSON,
14 CityJSONFeature, CityObject as CjCityObject, CityObjectType as CjCityObjectType,
15 Color as CjColor, Geometry as CjGeometry, GeometryCommon as CjGeometryCommon,
16 GeometryTemplates as CjGeometryTemplates, MaterialObject as CjMaterialObject,
17 Metadata as CjMetadata, PointOfContact as CjPointOfContact,
18 ReferenceSystem as CjReferenceSystem, Semantics as CjSemantics,
19 TextureFormat as CjTextureFormat, TextureObject as CjTextureObject,
20 TextureType as CjTextureType, Transform as CjTransform, WrapMode as CjWrapMode,
21};
22use serde_json::{json, Value};
23
24use super::meta::{Column as MetaColumn, ColumnType as MetaColumnType, Meta};
25
26pub fn to_cj_metadata(header: &Header) -> Result<CityJSON, Error> {
27 let mut cj = CityJSON::new();
28 let semantic_attr_schema = header.semantic_columns();
29 if let Some(transform) = header.transform() {
30 let (scale, translate) = (transform.scale(), transform.translate());
31 cj.transform = CjTransform {
32 scale: vec![scale.x(), scale.y(), scale.z()],
33 translate: vec![translate.x(), translate.y(), translate.z()],
34 };
35 }
36
37 if let Some(extensions_vec) = header.extensions() {
39 let mut extensions_map = serde_json::Map::new();
40 for extension in extensions_vec.iter() {
41 if let Some(name) = extension.name() {
42 extensions_map.insert(
43 name.to_string(),
44 json!({
45 "url": extension.url().unwrap_or_default(),
46 "version": extension.version().unwrap_or_default(),
47 }),
48 );
49 }
50 }
51
52 if !extensions_map.is_empty() {
53 cj.extensions = Some(Value::Object(extensions_map));
54 }
55 }
56
57 let reference_system = header.reference_system().map(|rs| {
58 CjReferenceSystem::new(
59 None,
60 rs.authority().unwrap_or_default().to_string(),
61 rs.version().to_string(),
62 rs.code().to_string(),
63 )
64 });
65 cj.version = header.version().to_string();
66
67 let geographical_extent = header
68 .geographical_extent()
69 .map(|extent| {
70 [
71 extent.min().x(),
72 extent.min().y(),
73 extent.min().z(),
74 extent.max().x(),
75 extent.max().y(),
76 extent.max().z(),
77 ]
78 })
79 .unwrap_or_default();
80
81 let point_of_contact = match header.poc_contact_name() {
82 Some(_) => Some(to_cj_point_of_contact(header)?),
83 None => None,
84 };
85
86 cj.metadata = Some(CjMetadata {
87 geographical_extent: Some(geographical_extent),
88 identifier: header.identifier().map(|i| i.to_string()),
89 point_of_contact,
90 reference_date: header.reference_date().map(|r| r.to_string()),
91 reference_system,
92 title: header.title().map(|t| t.to_string()),
93 other: HashMap::new(),
94 });
95
96 cj.appearance = header.appearance().map(to_cj_appearance).transpose()?;
101
102 if let (Some(fb_templates), Some(fb_vertices)) =
104 (header.templates(), header.templates_vertices())
105 {
106 let templates = fb_templates
107 .iter()
108 .map(|g| decode_geometry(g, semantic_attr_schema)) .collect::<Result<Vec<_>, _>>()?;
110
111 let vertices_templates = Value::Array(
112 fb_vertices
113 .iter()
114 .map(|v| json!([v.x(), v.y(), v.z()]))
115 .collect(),
116 );
117
118 cj.geometry_templates = Some(CjGeometryTemplates {
119 templates,
120 vertices_templates,
121 });
122 }
123
124 Ok(cj)
125}
126
127pub(crate) fn to_meta(header: Header) -> Result<Meta, Error> {
128 let columns = header.columns().map(|c| {
129 c.iter()
130 .map(|c| {
131 let i = c.index();
132 MetaColumn {
133 index: i,
134 name: c.name().to_string(),
135 _type: match c.type_() {
136 ColumnType::Int => MetaColumnType::Int,
137 ColumnType::UInt => MetaColumnType::UInt,
138 ColumnType::Bool => MetaColumnType::Bool,
139 ColumnType::Float => MetaColumnType::Float,
140 ColumnType::Double => MetaColumnType::Double,
141 ColumnType::String => MetaColumnType::String,
142 ColumnType::DateTime => MetaColumnType::DateTime,
143 ColumnType::Json => MetaColumnType::Json,
144 ColumnType::Binary => MetaColumnType::Binary,
145 ColumnType::Short => MetaColumnType::Short,
146 ColumnType::UShort => MetaColumnType::UShort,
147 ColumnType::Long => MetaColumnType::Long,
148 ColumnType::ULong => MetaColumnType::ULong,
149 _ => unreachable!(),
150 },
151 title: c.title().map(|t| t.to_string()),
152 description: c.description().map(|d| d.to_string()),
153 precision: Some(c.precision()),
154 scale: Some(c.scale()),
155 nullable: Some(c.nullable()),
156 unique: Some(c.unique()),
157 primary_key: Some(c.primary_key()),
158 metadata: c.metadata().map(|m| m.to_string()),
159 attr_index: Some(
160 header
161 .attribute_index()
162 .map(|attr_indices| attr_indices.iter().any(|i| i.index() == c.index()))
163 .unwrap_or(false),
164 ),
165 }
166 })
167 .collect::<Vec<_>>()
168 });
169 if columns.is_none() {
170 return Err(Error::MissingRequiredField("columns".to_string()));
171 }
172 Ok(Meta {
173 columns: columns.unwrap(),
174 feature_count: header.features_count(),
175 })
176}
177
178pub(crate) fn to_cj_point_of_contact(header: &Header) -> Result<CjPointOfContact, Error> {
179 Ok(CjPointOfContact {
180 contact_name: header
181 .poc_contact_name()
182 .ok_or(Error::MissingRequiredField("contact_name".to_string()))?
183 .to_string(),
184 contact_type: header.poc_contact_type().map(|ct| ct.to_string()),
185 role: header.poc_role().map(|r| r.to_string()),
186 phone: header.poc_phone().map(|p| p.to_string()),
187 email_address: header
188 .poc_email()
189 .ok_or(Error::MissingRequiredField("email_address".to_string()))?
190 .to_string(),
191 website: header.poc_website().map(|w| w.to_string()),
192 organization: None,
193 address: to_cj_address(header),
194 other: HashMap::new(),
195 })
196}
197
198pub(crate) fn to_cj_address(header: &Header) -> Option<CjAddress> {
202 let mut members: HashMap<String, Value> = HashMap::new();
203 let mut insert = |key: &str, value: Option<&str>| {
204 if let Some(value) = value.filter(|v| !v.is_empty()) {
205 members.insert(key.to_string(), Value::String(value.to_string()));
206 }
207 };
208 insert(
209 "thoroughfareNumber",
210 header.poc_address_thoroughfare_number(),
211 );
212 insert("thoroughfareName", header.poc_address_thoroughfare_name());
213 insert("locality", header.poc_address_locality());
214 insert("postcode", header.poc_address_postcode());
215 insert("country", header.poc_address_country());
216
217 if members.is_empty() {
218 None
219 } else {
220 Some(CjAddress { members })
221 }
222}
223
224pub(crate) fn to_cj_co_type(
239 co_type: CityObjectType,
240 extension_type: Option<&str>,
241) -> CjCityObjectType {
242 match co_type {
243 CityObjectType::Bridge => CjCityObjectType::Bridge,
244 CityObjectType::BridgePart => CjCityObjectType::BridgePart,
245 CityObjectType::BridgeInstallation => CjCityObjectType::BridgeInstallation,
246 CityObjectType::BridgeConstructiveElement => CjCityObjectType::BridgeConstructiveElement,
247 CityObjectType::BridgeRoom => CjCityObjectType::BridgeRoom,
248 CityObjectType::BridgeFurniture => CjCityObjectType::BridgeFurniture,
249 CityObjectType::Building => CjCityObjectType::Building,
250 CityObjectType::BuildingPart => CjCityObjectType::BuildingPart,
251 CityObjectType::BuildingInstallation => CjCityObjectType::BuildingInstallation,
252 CityObjectType::BuildingConstructiveElement => {
253 CjCityObjectType::BuildingConstructiveElement
254 }
255 CityObjectType::BuildingFurniture => CjCityObjectType::BuildingFurniture,
256 CityObjectType::BuildingStorey => CjCityObjectType::BuildingStorey,
257 CityObjectType::BuildingRoom => CjCityObjectType::BuildingRoom,
258 CityObjectType::BuildingUnit => CjCityObjectType::BuildingUnit,
259 CityObjectType::CityFurniture => CjCityObjectType::CityFurniture,
260 CityObjectType::CityObjectGroup => CjCityObjectType::CityObjectGroup,
261 CityObjectType::GenericCityObject => CjCityObjectType::GenericCityObject,
262 CityObjectType::LandUse => CjCityObjectType::LandUse,
263 CityObjectType::OtherConstruction => CjCityObjectType::OtherConstruction,
264 CityObjectType::PlantCover => CjCityObjectType::PlantCover,
265 CityObjectType::SolitaryVegetationObject => CjCityObjectType::SolitaryVegetationObject,
266 CityObjectType::TINRelief => CjCityObjectType::TINRelief,
267 CityObjectType::Road => CjCityObjectType::Road,
268 CityObjectType::Railway => CjCityObjectType::Railway,
269 CityObjectType::Waterway => CjCityObjectType::Waterway,
270 CityObjectType::TransportSquare => CjCityObjectType::TransportSquare,
271 CityObjectType::Tunnel => CjCityObjectType::Tunnel,
272 CityObjectType::TunnelPart => CjCityObjectType::TunnelPart,
273 CityObjectType::TunnelInstallation => CjCityObjectType::TunnelInstallation,
274 CityObjectType::TunnelConstructiveElement => CjCityObjectType::TunnelConstructiveElement,
275 CityObjectType::TunnelHollowSpace => CjCityObjectType::TunnelHollowSpace,
276 CityObjectType::TunnelFurniture => CjCityObjectType::TunnelFurniture,
277 CityObjectType::WaterBody => CjCityObjectType::WaterBody,
278 _ => {
280 CjCityObjectType::Extension(extension_type.unwrap_or("+UnknownCityObject").to_string())
281 }
282 }
283}
284
285pub fn decode_attributes(
286 columns: &flatbuffers::Vector<'_, flatbuffers::ForwardsUOffset<Column<'_>>>,
287 attributes: flatbuffers::Vector<'_, u8>,
288) -> serde_json::Value {
289 if attributes.is_empty() {
290 return serde_json::Value::Object(serde_json::Map::new());
291 }
292
293 let mut map = serde_json::Map::new();
294 let bytes = attributes.bytes();
295 let mut offset = 0;
296 while offset < bytes.len() {
297 let col_index = LittleEndian::read_u16(&bytes[offset..offset + size_of::<u16>()]) as u16;
298 offset += size_of::<u16>();
299 if col_index >= columns.len() as u16 {
300 panic!("column index out of range"); }
302 let column = columns.iter().find(|c| c.index() == col_index);
303 if column.is_none() {
304 panic!("column not found"); }
306 let column = column.unwrap();
307 match column.type_() {
308 ColumnType::Int => {
309 map.insert(
310 column.name().to_string(),
311 serde_json::Value::Number(serde_json::Number::from(LittleEndian::read_i32(
312 &bytes[offset..offset + size_of::<i32>()],
313 ))),
314 );
315 offset += size_of::<i32>();
316 }
317 ColumnType::UInt => {
318 map.insert(
319 column.name().to_string(),
320 serde_json::Value::Number(serde_json::Number::from(LittleEndian::read_u32(
321 &bytes[offset..offset + size_of::<u32>()],
322 ))),
323 );
324 offset += size_of::<u32>();
325 }
326 ColumnType::Bool => {
327 map.insert(
328 column.name().to_string(),
329 serde_json::Value::Bool(bytes[offset] != 0),
330 );
331 offset += size_of::<u8>();
332 }
333 ColumnType::Short => {
334 map.insert(
335 column.name().to_string(),
336 serde_json::Value::Number(serde_json::Number::from(LittleEndian::read_i16(
337 &bytes[offset..offset + size_of::<i16>()],
338 ))),
339 );
340 offset += size_of::<i16>();
341 }
342 ColumnType::UShort => {
343 map.insert(
344 column.name().to_string(),
345 serde_json::Value::Number(serde_json::Number::from(LittleEndian::read_u16(
346 &bytes[offset..offset + size_of::<u16>()],
347 ))),
348 );
349 offset += size_of::<u16>();
350 }
351 ColumnType::Long => {
352 map.insert(
353 column.name().to_string(),
354 serde_json::Value::Number(serde_json::Number::from(LittleEndian::read_i64(
355 &bytes[offset..offset + size_of::<i64>()],
356 ))),
357 );
358 offset += size_of::<i64>();
359 }
360 ColumnType::ULong => {
361 map.insert(
362 column.name().to_string(),
363 serde_json::Value::Number(serde_json::Number::from(LittleEndian::read_u64(
364 &bytes[offset..offset + size_of::<u64>()],
365 ))),
366 );
367 offset += size_of::<u64>();
368 }
369 ColumnType::Float => {
370 let f = LittleEndian::read_f32(&bytes[offset..offset + size_of::<f32>()]);
371 if let Some(num) = serde_json::Number::from_f64(f as f64) {
372 map.insert(column.name().to_string(), serde_json::Value::Number(num));
373 }
374 offset += size_of::<f32>();
375 }
376 ColumnType::Double => {
377 let f = LittleEndian::read_f64(&bytes[offset..offset + size_of::<f64>()]);
378 if let Some(num) = serde_json::Number::from_f64(f) {
379 map.insert(column.name().to_string(), serde_json::Value::Number(num));
380 }
381 offset += size_of::<f64>();
382 }
383 ColumnType::String => {
384 let len = LittleEndian::read_u32(&bytes[offset..offset + size_of::<u32>()]);
385 offset += size_of::<u32>();
386 let s = String::from_utf8(bytes[offset..offset + len as usize].to_vec())
387 .unwrap_or_default();
388 map.insert(column.name().to_string(), serde_json::Value::String(s));
389 offset += len as usize;
390 }
391 ColumnType::DateTime => {
392 let len = LittleEndian::read_u32(&bytes[offset..offset + size_of::<u32>()]);
393 offset += size_of::<u32>();
394 let s = String::from_utf8(bytes[offset..offset + len as usize].to_vec())
395 .unwrap_or_default();
396 map.insert(column.name().to_string(), serde_json::Value::String(s));
397 offset += len as usize;
398 }
399 ColumnType::Json => {
400 let len = LittleEndian::read_u32(&bytes[offset..offset + size_of::<u32>()]);
401 offset += size_of::<u32>();
402 let s = String::from_utf8(bytes[offset..offset + len as usize].to_vec())
403 .unwrap_or_default();
404 map.insert(column.name().to_string(), serde_json::from_str(&s).unwrap());
405 offset += len as usize;
406 }
407
408 ColumnType::Byte => {
416 map.insert(
417 column.name().to_string(),
418 serde_json::Value::Number(serde_json::Number::from(bytes[offset])),
419 );
420 offset += size_of::<u8>();
421 }
422 ColumnType::UByte => {
423 map.insert(
424 column.name().to_string(),
425 serde_json::Value::Number(serde_json::Number::from(bytes[offset])),
426 );
427 offset += size_of::<u8>();
428 }
429 ColumnType::Binary => {
430 let len = LittleEndian::read_u32(&bytes[offset..offset + size_of::<u32>()]);
431 offset += size_of::<u32>();
432 let raw = &bytes[offset..offset + len as usize];
433 map.insert(
434 column.name().to_string(),
435 serde_json::Value::Array(
436 raw.iter()
437 .map(|b| serde_json::Value::Number(serde_json::Number::from(*b)))
438 .collect(),
439 ),
440 );
441 offset += len as usize;
442 }
443 _ => {
446 return serde_json::Value::Object(map);
447 }
448 }
449 }
450
451 serde_json::Value::Object(map)
452}
453
454fn to_color(color: Option<flatbuffers::Vector<'_, f64>>) -> Option<CjColor> {
459 let values: Vec<f64> = color?.iter().collect();
460 values.try_into().ok()
461}
462
463fn to_border_color(color: Option<flatbuffers::Vector<'_, f64>>) -> Option<CjBorderColor> {
467 let values: Vec<f64> = color?.iter().collect();
468 match values.len() {
469 3 => values.try_into().ok().map(CjBorderColor::Rgb),
470 4 => values.try_into().ok().map(CjBorderColor::Rgba),
471 _ => None,
472 }
473}
474
475fn cj_wrap_mode(tag: WrapMode) -> Result<CjWrapMode, Error> {
485 Ok(match tag {
486 WrapMode::None => CjWrapMode::None,
487 WrapMode::Wrap => CjWrapMode::Wrap,
488 WrapMode::Mirror => CjWrapMode::Mirror,
489 WrapMode::Clamp => CjWrapMode::Clamp,
490 WrapMode::Border => CjWrapMode::Border,
491 _ => return Err(Error::UnknownEnumTag("wrapMode", format!("{tag:?}"))),
492 })
493}
494
495fn cj_texture_type(tag: TextureType) -> Result<CjTextureType, Error> {
498 Ok(match tag {
499 TextureType::Unknown => CjTextureType::Unknown,
500 TextureType::Specific => CjTextureType::Specific,
501 TextureType::Typical => CjTextureType::Typical,
502 _ => return Err(Error::UnknownEnumTag("textureType", format!("{tag:?}"))),
503 })
504}
505
506fn cj_texture_format(tag: TextureFormat) -> Result<CjTextureFormat, Error> {
509 Ok(match tag {
510 TextureFormat::PNG => CjTextureFormat::PNG,
511 TextureFormat::JPG => CjTextureFormat::JPG,
512 _ => return Err(Error::UnknownEnumTag("type", format!("{tag:?}"))),
513 })
514}
515
516pub fn to_cj_feature(
517 feature: CityFeature,
518 root_attr_schema: Option<flatbuffers::Vector<'_, flatbuffers::ForwardsUOffset<Column<'_>>>>,
519 semantic_attr_schema: Option<flatbuffers::Vector<'_, flatbuffers::ForwardsUOffset<Column<'_>>>>,
520) -> Result<CityJSONFeature, Error> {
521 let mut cj = CityJSONFeature::new();
523 cj.id = feature.id().to_string();
524
525 if let Some(objects) = feature.objects() {
526 let city_objects_result: Result<HashMap<String, CjCityObject>, Error> = objects
527 .iter()
528 .map(|co| {
529 let geographical_extent = co.geographical_extent().map(|extent| {
530 [
531 extent.min().x(),
532 extent.min().y(),
533 extent.min().z(),
534 extent.max().x(),
535 extent.max().y(),
536 extent.max().z(),
537 ]
538 });
539
540 let mut all_geometries: Vec<cjseq::Geometry> = Vec::new();
541
542 if let Some(standard_geometries) = co.geometry() {
544 let decoded_standard = standard_geometries
545 .iter()
546 .map(|g| decode_geometry(g, semantic_attr_schema)) .collect::<Result<Vec<_>, _>>()?; all_geometries.extend(decoded_standard);
549 }
550
551 if let Some(instances) = co.geometry_instances() {
553 let decoded_instances = instances
554 .iter()
555 .map(|inst| decode_geometry_instance(&inst)) .collect::<Result<Vec<_>, _>>()?; all_geometries.extend(decoded_instances);
558 }
559
560 let final_geometries = if all_geometries.is_empty() {
561 None
562 } else {
563 Some(all_geometries)
564 };
565
566 let attributes = if root_attr_schema.is_none() && co.columns().is_none() {
567 None
568 } else {
569 co.attributes().map(|a| {
570 decode_attributes(&co.columns().unwrap_or(root_attr_schema.unwrap()), a)
571 })
572 };
573
574 let children_roles = co.children_roles().map(|c| {
577 c.iter()
578 .map(|s| (!s.is_empty()).then(|| s.to_string()))
579 .collect()
580 });
581
582 let mut cjco = CjCityObject::new(to_cj_co_type(co.type_(), co.extension_type()));
583 cjco.geographical_extent = geographical_extent;
584 cjco.attributes = attributes;
585 cjco.geometry = final_geometries;
586 cjco.children = co
587 .children()
588 .map(|c| c.iter().map(|s| s.to_string()).collect());
589 cjco.children_roles = children_roles;
590 cjco.parents = co
591 .parents()
592 .map(|p| p.iter().map(|s| s.to_string()).collect());
593 Ok((co.id().to_string(), cjco)) })
595 .collect(); let city_objects = city_objects_result?;
598 cj.city_objects = city_objects;
599 }
600
601 cj.vertices = feature
602 .vertices()
603 .map_or(Vec::new(), |v| to_cj_vertices(v.iter().collect()));
604
605 cj.appearance = feature.appearance().map(to_cj_appearance).transpose()?;
607
608 Ok(cj) }
610
611pub(crate) fn to_cj_appearance(appearance: Appearance) -> Result<CjAppearance, Error> {
619 let mut cj_appearance = CjAppearance {
620 materials: None,
621 textures: None,
622 vertices_texture: None,
623 default_theme_texture: None,
624 default_theme_material: None,
625 };
626
627 {
628 if let Some(materials) = appearance.materials() {
632 let cj_materials = materials
633 .iter()
634 .map(|m| CjMaterialObject {
635 name: m.name().to_string(),
636 ambient_intensity: m.ambient_intensity(),
637 diffuse_color: to_color(m.diffuse_color()),
638 emissive_color: to_color(m.emissive_color()),
639 specular_color: to_color(m.specular_color()),
640 shininess: m.shininess(),
641 transparency: m.transparency(),
642 is_smooth: m.is_smooth(),
643 })
644 .collect();
645
646 cj_appearance.materials = Some(cj_materials);
647 }
648
649 if let Some(textures) = appearance.textures() {
651 let cj_textures = textures
652 .iter()
653 .map(|t| {
654 Ok(CjTextureObject {
655 thetype: Some(cj_texture_format(t.type_())?),
656 image: Some(t.image())
667 .filter(|i| !i.is_empty())
668 .map(str::to_string),
669 wrap_mode: t.wrap_mode().map(cj_wrap_mode).transpose()?,
670 texture_type: t.texture_type().map(cj_texture_type).transpose()?,
671 border_color: to_border_color(t.border_color()),
672 })
673 })
674 .collect::<Result<Vec<_>, Error>>()?;
675
676 cj_appearance.textures = Some(cj_textures);
677 }
678
679 if let Some(vertices_texture) = appearance.vertices_texture() {
681 cj_appearance.vertices_texture = Some(
682 vertices_texture
683 .iter()
684 .map(|v| [v.u(), v.v()])
685 .collect::<Vec<_>>(),
686 );
687 }
688
689 if let Some(default_theme_texture) = appearance.default_theme_texture() {
691 cj_appearance.default_theme_texture = Some(default_theme_texture.to_string());
692 }
693
694 if let Some(default_theme_material) = appearance.default_theme_material() {
695 cj_appearance.default_theme_material = Some(default_theme_material.to_string());
696 }
697 }
698
699 Ok(cj_appearance)
700}
701
702pub(crate) fn decode_geometry(
703 g: Geometry,
704 semantic_attr_schema: Option<flatbuffers::Vector<'_, flatbuffers::ForwardsUOffset<Column<'_>>>>,
705) -> Result<CjGeometry, Error> {
706 let solids = g
707 .solids()
708 .map(|v| v.iter().collect::<Vec<_>>())
709 .unwrap_or_default();
710 let shells = g
711 .shells()
712 .map(|v| v.iter().collect::<Vec<_>>())
713 .unwrap_or_default();
714 let surfaces = g
715 .surfaces()
716 .map(|v| v.iter().collect::<Vec<_>>())
717 .unwrap_or_default();
718 let strings = g
719 .strings()
720 .map(|v| v.iter().collect::<Vec<_>>())
721 .unwrap_or_default();
722 let indices = g
723 .boundaries()
724 .map(|v| v.iter().collect::<Vec<_>>())
725 .unwrap_or_default();
726 let geometry_type = g.type_();
727
728 let semantics: Option<CjSemantics> = g.semantics_objects().map(|semantics_objects| {
732 let semantics_objects = semantics_objects.iter().collect::<Vec<_>>();
733 let semantics_values = g.semantics().map(|v| v.iter().collect::<Vec<_>>());
734 decode_semantics(
735 &solids,
736 &shells,
737 geometry_type,
738 semantics_objects,
739 semantics_values,
740 semantic_attr_schema,
741 )
742 });
743
744 let material = if let Some(material_mappings) = g.material() {
746 decode_materials(geometry_type, &material_mappings.iter().collect::<Vec<_>>())
747 } else {
748 None
749 };
750
751 let texture = if let Some(texture_mappings) = g.texture() {
753 decode_textures(geometry_type, &texture_mappings.iter().collect::<Vec<_>>())
754 } else {
755 None
756 };
757
758 let common = CjGeometryCommon {
759 semantics,
760 material,
761 texture,
762 };
763 let lod = g.lod().map(|v| v.to_string());
764
765 Ok(match geometry_type {
768 GeometryType::MultiPoint => CjGeometry::MultiPoint {
769 lod,
770 boundaries: decode_points(&indices),
771 common,
772 },
773 GeometryType::MultiLineString => CjGeometry::MultiLineString {
774 lod,
775 boundaries: decode_rings(&strings, &indices),
776 common,
777 },
778 GeometryType::MultiSurface => CjGeometry::MultiSurface {
779 lod,
780 boundaries: decode_surfaces(&surfaces, &strings, &indices),
781 common,
782 },
783 GeometryType::CompositeSurface => CjGeometry::CompositeSurface {
784 lod,
785 boundaries: decode_surfaces(&surfaces, &strings, &indices),
786 common,
787 },
788 GeometryType::MultiSolid => CjGeometry::MultiSolid {
789 lod,
790 boundaries: decode_solids(&solids, &shells, &surfaces, &strings, &indices),
791 common,
792 },
793 GeometryType::CompositeSolid => CjGeometry::CompositeSolid {
794 lod,
795 boundaries: decode_solids(&solids, &shells, &surfaces, &strings, &indices),
796 common,
797 },
798 GeometryType::Solid => CjGeometry::Solid {
799 lod,
800 boundaries: decode_shells(&shells, &surfaces, &strings, &indices),
801 common,
802 },
803 other => return Err(Error::UnknownEnumTag("GeometryType", format!("{other:?}"))),
810 })
811}
812
813pub(crate) fn decode_geometry_instance(instance: &GeometryInstance) -> Result<CjGeometry, Error> {
824 let template_index = instance.template();
825
826 let boundaries = match instance.boundaries() {
827 Some(fb_boundaries) => {
828 if fb_boundaries.len() != 1 {
829 return Err(Error::InvalidAttributeValue {
830 msg: format!("geometryinstance boundaries should contain exactly one vertex index, found {}", fb_boundaries.len())
831 });
832 }
833 vec![fb_boundaries.get(0) as usize]
834 }
835 None => {
836 return Err(Error::MissingRequiredField(
837 "geometryinstance boundaries".to_string(),
838 ));
839 }
840 };
841
842 let fb_matrix = instance.transformation().ok_or_else(|| {
843 Error::MissingRequiredField("geometryinstance transformation field".to_string())
844 })?;
845
846 let transformation_matrix_array = [
848 fb_matrix.m00(),
849 fb_matrix.m01(),
850 fb_matrix.m02(),
851 fb_matrix.m03(),
852 fb_matrix.m10(),
853 fb_matrix.m11(),
854 fb_matrix.m12(),
855 fb_matrix.m13(),
856 fb_matrix.m20(),
857 fb_matrix.m21(),
858 fb_matrix.m22(),
859 fb_matrix.m23(),
860 fb_matrix.m30(),
861 fb_matrix.m31(),
862 fb_matrix.m32(),
863 fb_matrix.m33(),
864 ];
865
866 Ok(CjGeometry::GeometryInstance {
870 boundaries,
871 template: template_index as usize,
872 transformation_matrix: transformation_matrix_array,
873 })
874}
875
876pub(crate) fn to_cj_vertices(vertices: Vec<&Vertex>) -> Vec<Vec<i64>> {
877 vertices
878 .iter()
879 .map(|v| vec![v.x() as i64, v.y() as i64, v.z() as i64])
880 .collect()
881}
882
883#[cfg(test)]
884mod tests {
885 use super::*;
886 use anyhow::Result;
887
888 use flatbuffers::FlatBufferBuilder;
889 #[test]
890 fn test_decode_geometry_instance() -> Result<()> {
891 let mut fbb = FlatBufferBuilder::new();
892
893 let transformation = TransformationMatrix::new(
895 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 10.0, 10.0, 10.0,
896 1.0, );
898
899 let boundaries_vec = vec![42u32]; let boundaries = fbb.create_vector(&boundaries_vec);
902
903 let geometry_instance = GeometryInstance::create(
905 &mut fbb,
906 &crate::fb::GeometryInstanceArgs {
907 template: 5, transformation: Some(&transformation),
909 boundaries: Some(boundaries),
910 },
911 );
912
913 fbb.finish(geometry_instance, None);
914 let buf = fbb.finished_data();
915
916 let geometry_instance = flatbuffers::root::<GeometryInstance>(buf).unwrap();
918
919 let cj_geometry = decode_geometry_instance(&geometry_instance)?;
921
922 let CjGeometry::GeometryInstance {
926 boundaries,
927 template,
928 transformation_matrix: matrix,
929 } = cj_geometry
930 else {
931 panic!("expected a GeometryInstance");
932 };
933 assert_eq!(template, 5);
934 assert_eq!(boundaries, vec![42]);
935 assert_eq!(matrix[0], 1.0); assert_eq!(matrix[12], 10.0); assert_eq!(matrix[13], 10.0); assert_eq!(matrix[14], 10.0); Ok(())
941 }
942
943 #[test]
944 fn test_decode_geometry_instance_missing_boundaries() -> Result<()> {
945 let mut fbb = FlatBufferBuilder::new();
946
947 let transformation = TransformationMatrix::new(
949 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0,
950 );
951
952 let geometry_instance = GeometryInstance::create(
954 &mut fbb,
955 &crate::fb::GeometryInstanceArgs {
956 template: 5,
957 transformation: Some(&transformation),
958 boundaries: None, },
960 );
961
962 fbb.finish(geometry_instance, None);
963 let buf = fbb.finished_data();
964 let geometry_instance = flatbuffers::root::<GeometryInstance>(buf).unwrap();
965
966 let result = decode_geometry_instance(&geometry_instance);
968 assert!(result.is_err());
969 match result.err().unwrap() {
970 Error::MissingRequiredField(field) => {
971 assert!(field.contains("geometryinstance boundaries"));
972 }
973 _ => panic!("Expected MissingRequiredField error"),
974 }
975
976 Ok(())
977 }
978
979 #[test]
980 fn test_decode_geometry_instance_missing_transformation() -> Result<()> {
981 let mut fbb = FlatBufferBuilder::new();
982
983 let boundaries_vec = vec![42u32];
985 let boundaries = fbb.create_vector(&boundaries_vec);
986
987 let geometry_instance = GeometryInstance::create(
989 &mut fbb,
990 &crate::fb::GeometryInstanceArgs {
991 template: 5,
992 transformation: None, boundaries: Some(boundaries),
994 },
995 );
996
997 fbb.finish(geometry_instance, None);
998 let buf = fbb.finished_data();
999 let geometry_instance = flatbuffers::root::<GeometryInstance>(buf).unwrap();
1000
1001 let result = decode_geometry_instance(&geometry_instance);
1003 assert!(result.is_err());
1004 match result.err().unwrap() {
1005 Error::MissingRequiredField(field) => {
1006 assert!(field.contains("geometryinstance transformation field"));
1007 }
1008 _ => panic!("Expected MissingRequiredField error"),
1009 }
1010
1011 Ok(())
1012 }
1013
1014 #[test]
1015 fn test_decode_geometry_instance_invalid_boundaries() -> Result<()> {
1016 let mut fbb = FlatBufferBuilder::new();
1017
1018 let transformation = TransformationMatrix::new(
1020 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0,
1021 );
1022
1023 let boundaries_vec_zero: Vec<u32> = vec![];
1025 let boundaries_zero = fbb.create_vector(&boundaries_vec_zero);
1026 let geometry_instance_zero = GeometryInstance::create(
1027 &mut fbb,
1028 &crate::fb::GeometryInstanceArgs {
1029 template: 5,
1030 transformation: Some(&transformation),
1031 boundaries: Some(boundaries_zero),
1032 },
1033 );
1034 fbb.finish(geometry_instance_zero, None);
1035 let buf_zero = fbb.finished_data();
1036 let instance_zero = flatbuffers::root::<GeometryInstance>(buf_zero).unwrap();
1037
1038 let result_zero = decode_geometry_instance(&instance_zero);
1039 assert!(result_zero.is_err());
1040 match result_zero.err().unwrap() {
1041 Error::InvalidAttributeValue { msg } => {
1042 assert!(msg.contains("should contain exactly one vertex index, found 0"));
1043 }
1044 _ => panic!("Expected InvalidAttributeValue error for zero boundaries"),
1045 }
1046
1047 fbb.reset(); let boundaries_vec_multi = vec![42u32, 43u32]; let boundaries_multi = fbb.create_vector(&boundaries_vec_multi);
1051 let transformation_multi = TransformationMatrix::new(
1053 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0,
1054 );
1055 let geometry_instance_multi = GeometryInstance::create(
1056 &mut fbb,
1057 &crate::fb::GeometryInstanceArgs {
1058 template: 5,
1059 transformation: Some(&transformation_multi),
1060 boundaries: Some(boundaries_multi),
1061 },
1062 );
1063 fbb.finish(geometry_instance_multi, None);
1064 let buf_multi = fbb.finished_data();
1065 let instance_multi = flatbuffers::root::<GeometryInstance>(buf_multi).unwrap();
1066
1067 let result_multi = decode_geometry_instance(&instance_multi);
1068 assert!(result_multi.is_err());
1069 match result_multi.err().unwrap() {
1070 Error::InvalidAttributeValue { msg } => {
1071 assert!(msg.contains("should contain exactly one vertex index, found 2"));
1072 }
1073 _ => panic!("Expected InvalidAttributeValue error for multiple boundaries"),
1074 }
1075
1076 Ok(())
1077 }
1078
1079 #[test]
1087 fn test_decode_attributes_byte_ubyte_binary() -> Result<()> {
1088 let mut fbb = FlatBufferBuilder::new();
1089
1090 let byte_name = fbb.create_string("b");
1091 let ubyte_name = fbb.create_string("ub");
1092 let binary_name = fbb.create_string("bin");
1093 let byte_col = Column::create(
1094 &mut fbb,
1095 &ColumnArgs {
1096 index: 0,
1097 name: Some(byte_name),
1098 type_: ColumnType::Byte,
1099 ..Default::default()
1100 },
1101 );
1102 let ubyte_col = Column::create(
1103 &mut fbb,
1104 &ColumnArgs {
1105 index: 1,
1106 name: Some(ubyte_name),
1107 type_: ColumnType::UByte,
1108 ..Default::default()
1109 },
1110 );
1111 let binary_col = Column::create(
1112 &mut fbb,
1113 &ColumnArgs {
1114 index: 2,
1115 name: Some(binary_name),
1116 type_: ColumnType::Binary,
1117 ..Default::default()
1118 },
1119 );
1120 let columns = fbb.create_vector(&[byte_col, ubyte_col, binary_col]);
1121
1122 let mut blob: Vec<u8> = Vec::new();
1124 blob.extend_from_slice(&0u16.to_le_bytes()); blob.push(200);
1126 blob.extend_from_slice(&1u16.to_le_bytes()); blob.push(200);
1128 blob.extend_from_slice(&2u16.to_le_bytes()); blob.extend_from_slice(&2u32.to_le_bytes());
1130 blob.extend_from_slice(&[1, 255]);
1131 let attributes = fbb.create_vector(&blob);
1132
1133 let id = fbb.create_string("obj-1");
1134 let city_object = CityObject::create(
1135 &mut fbb,
1136 &CityObjectArgs {
1137 id: Some(id),
1138 attributes: Some(attributes),
1139 columns: Some(columns),
1140 ..Default::default()
1141 },
1142 );
1143 fbb.finish(city_object, None);
1144
1145 let city_object = flatbuffers::root::<CityObject>(fbb.finished_data()).unwrap();
1146 let columns = city_object.columns().unwrap();
1147 let decoded = decode_attributes(&columns, city_object.attributes().unwrap());
1148
1149 assert_eq!(decoded, json!({"b": 200, "ub": 200, "bin": [1, 255]}));
1150
1151 Ok(())
1152 }
1153}