Skip to main content

cityjson_types/
relational.rs

1use crate::cityjson::core::appearance::ThemeName;
2use crate::error::{Error, Result};
3use crate::raw::{CityModelRawAccessor, DenseIndexRemap};
4use crate::resources::storage::OwnedStringStorage;
5use crate::symbols::SymbolStorageOptions;
6use crate::v2_0::appearance::material::Material;
7use crate::v2_0::appearance::texture::Texture;
8use crate::v2_0::attributes::{AttributeValue, Attributes};
9use crate::v2_0::boundary::Boundary;
10use crate::v2_0::cityobject::{CityObject, CityObjectType};
11use crate::v2_0::geometry::semantic::{Semantic, SemanticType};
12use crate::v2_0::geometry::{
13    AffineTransform3D, Geometry, GeometryType, LoD, StoredGeometryInstance, StoredGeometryParts,
14};
15use crate::v2_0::metadata::{BBox, CRS, CityModelIdentifier, Contact, ContactRole, ContactType};
16use crate::v2_0::vertex::VertexIndex;
17use crate::v2_0::{
18    CityModelCapacities, Extension, MaterialMap, Metadata, OwnedCityModel, RealWorldCoordinate,
19    SemanticMap, UVCoordinate, WrapMode,
20};
21use crate::{CityModelType, resources::mapping::textures::TextureMap as PublicTextureMap};
22use std::collections::HashMap;
23use std::str::FromStr;
24
25#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
26pub struct SymbolId(pub u32);
27
28#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
29pub struct CityObjectId(pub u32);
30
31#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
32pub struct GeometryId(pub u32);
33
34#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
35pub struct GeometryTemplateId(pub u32);
36
37#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
38pub struct SemanticId(pub u32);
39
40#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
41pub struct MaterialId(pub u32);
42
43#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
44pub struct TextureId(pub u32);
45
46#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
47pub struct VertexId(pub u32);
48
49#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
50pub struct UvVertexId(pub u32);
51
52#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
53pub struct AttributeNodeId(pub u32);
54
55macro_rules! impl_dense_id {
56    ($name:ident) => {
57        impl $name {
58            #[allow(dead_code)]
59            fn index(self) -> usize {
60                usize::try_from(self.0).unwrap_or(usize::MAX)
61            }
62        }
63    };
64}
65
66impl_dense_id!(SymbolId);
67impl_dense_id!(CityObjectId);
68impl_dense_id!(GeometryId);
69impl_dense_id!(GeometryTemplateId);
70impl_dense_id!(SemanticId);
71impl_dense_id!(MaterialId);
72impl_dense_id!(TextureId);
73impl_dense_id!(VertexId);
74impl_dense_id!(UvVertexId);
75impl_dense_id!(AttributeNodeId);
76
77#[derive(Debug, Clone, Copy, PartialEq, Eq)]
78pub enum AttributeNodeType {
79    Null,
80    Bool,
81    Unsigned,
82    Integer,
83    Float,
84    String,
85    Array,
86    Object,
87    GeometryRef,
88}
89
90#[derive(Debug, Clone, Default)]
91pub struct SymbolTableOwned {
92    pub values: Vec<String>,
93}
94
95#[derive(Debug, Clone, Default)]
96pub struct VertexTableOwned {
97    pub x: Vec<f64>,
98    pub y: Vec<f64>,
99    pub z: Vec<f64>,
100}
101
102impl VertexTableOwned {
103    #[must_use]
104    pub fn len(&self) -> usize {
105        self.x.len()
106    }
107
108    #[must_use]
109    pub fn is_empty(&self) -> bool {
110        self.x.is_empty()
111    }
112}
113
114#[derive(Debug, Clone, Default)]
115pub struct UvVertexTableOwned {
116    pub u: Vec<f64>,
117    pub v: Vec<f64>,
118}
119
120impl UvVertexTableOwned {
121    #[must_use]
122    pub fn len(&self) -> usize {
123        self.u.len()
124    }
125
126    #[must_use]
127    pub fn is_empty(&self) -> bool {
128        self.u.is_empty()
129    }
130}
131
132#[derive(Debug, Clone, Default)]
133pub struct CityObjectTableOwned {
134    pub ids: Vec<CityObjectId>,
135    pub external_id_symbols: Vec<SymbolId>,
136    pub object_type_symbols: Vec<SymbolId>,
137    pub parent_start: Vec<u32>,
138    pub parent_len: Vec<u32>,
139    pub parents: Vec<CityObjectId>,
140    pub child_start: Vec<u32>,
141    pub child_len: Vec<u32>,
142    pub children: Vec<CityObjectId>,
143    pub geometry_start: Vec<u32>,
144    pub geometry_len: Vec<u32>,
145    pub geometries: Vec<GeometryId>,
146    pub attribute_root: Vec<Option<AttributeNodeId>>,
147    pub bbox_min_x: Vec<Option<f64>>,
148    pub bbox_min_y: Vec<Option<f64>>,
149    pub bbox_min_z: Vec<Option<f64>>,
150    pub bbox_max_x: Vec<Option<f64>>,
151    pub bbox_max_y: Vec<Option<f64>>,
152    pub bbox_max_z: Vec<Option<f64>>,
153}
154
155impl CityObjectTableOwned {
156    #[must_use]
157    pub fn len(&self) -> usize {
158        self.ids.len()
159    }
160
161    #[must_use]
162    pub fn is_empty(&self) -> bool {
163        self.ids.is_empty()
164    }
165}
166
167#[derive(Debug, Clone, Default)]
168pub struct SemanticTableOwned {
169    pub ids: Vec<SemanticId>,
170    pub semantic_type_symbols: Vec<SymbolId>,
171    pub parent: Vec<Option<SemanticId>>,
172    pub child_start: Vec<u32>,
173    pub child_len: Vec<u32>,
174    pub children: Vec<SemanticId>,
175    pub attribute_root: Vec<Option<AttributeNodeId>>,
176}
177
178impl SemanticTableOwned {
179    #[must_use]
180    pub fn len(&self) -> usize {
181        self.ids.len()
182    }
183
184    #[must_use]
185    pub fn is_empty(&self) -> bool {
186        self.ids.is_empty()
187    }
188}
189
190#[derive(Debug, Clone, Default)]
191pub struct MaterialTableOwned {
192    pub ids: Vec<MaterialId>,
193    pub name_symbols: Vec<SymbolId>,
194    pub ambient_intensity: Vec<Option<f32>>,
195    pub diffuse_color: Vec<Option<[f32; 3]>>,
196    pub emissive_color: Vec<Option<[f32; 3]>>,
197    pub specular_color: Vec<Option<[f32; 3]>>,
198    pub shininess: Vec<Option<f32>>,
199    pub transparency: Vec<Option<f32>>,
200    pub is_smooth: Vec<Option<bool>>,
201}
202
203impl MaterialTableOwned {
204    #[must_use]
205    pub fn len(&self) -> usize {
206        self.ids.len()
207    }
208
209    #[must_use]
210    pub fn is_empty(&self) -> bool {
211        self.ids.is_empty()
212    }
213}
214
215#[derive(Debug, Clone, Default)]
216pub struct TextureTableOwned {
217    pub ids: Vec<TextureId>,
218    pub image_uri_symbols: Vec<SymbolId>,
219    pub texture_type_symbols: Vec<Option<SymbolId>>,
220    pub wrap_mode_symbols: Vec<Option<SymbolId>>,
221    pub image_type_symbols: Vec<SymbolId>,
222    pub border_color: Vec<Option<[f32; 4]>>,
223}
224
225impl TextureTableOwned {
226    #[must_use]
227    pub fn len(&self) -> usize {
228        self.ids.len()
229    }
230
231    #[must_use]
232    pub fn is_empty(&self) -> bool {
233        self.ids.is_empty()
234    }
235}
236
237#[derive(Debug, Clone, Default)]
238pub struct GeometryMaterialThemeOwned {
239    pub geometry: GeometryId,
240    pub theme_symbol: SymbolId,
241    pub point_start: u32,
242    pub point_len: u32,
243    pub linestring_start: u32,
244    pub linestring_len: u32,
245    pub surface_start: u32,
246    pub surface_len: u32,
247}
248
249#[derive(Debug, Clone, Default)]
250pub struct GeometryTextureThemeOwned {
251    pub geometry: GeometryId,
252    pub theme_symbol: SymbolId,
253    pub vertex_start: u32,
254    pub vertex_len: u32,
255    pub ring_start: u32,
256    pub ring_len: u32,
257    pub ring_texture_start: u32,
258    pub ring_texture_len: u32,
259}
260
261#[derive(Debug, Clone, Default)]
262pub struct GeometryTableOwned {
263    pub ids: Vec<GeometryId>,
264    pub geometry_type_symbols: Vec<SymbolId>,
265    pub lod_symbols: Vec<Option<SymbolId>>,
266    pub boundary_vertex_start: Vec<u32>,
267    pub boundary_vertex_len: Vec<u32>,
268    pub boundary_ring_start: Vec<u32>,
269    pub boundary_ring_len: Vec<u32>,
270    pub boundary_surface_start: Vec<u32>,
271    pub boundary_surface_len: Vec<u32>,
272    pub boundary_shell_start: Vec<u32>,
273    pub boundary_shell_len: Vec<u32>,
274    pub boundary_solid_start: Vec<u32>,
275    pub boundary_solid_len: Vec<u32>,
276    pub semantic_point_start: Vec<u32>,
277    pub semantic_point_len: Vec<u32>,
278    pub semantic_linestring_start: Vec<u32>,
279    pub semantic_linestring_len: Vec<u32>,
280    pub semantic_surface_start: Vec<u32>,
281    pub semantic_surface_len: Vec<u32>,
282    pub material_theme_start: Vec<u32>,
283    pub material_theme_len: Vec<u32>,
284    pub texture_theme_start: Vec<u32>,
285    pub texture_theme_len: Vec<u32>,
286    pub template_ref: Vec<Option<GeometryTemplateId>>,
287    pub reference_point: Vec<Option<VertexId>>,
288    pub transform_matrix: Vec<[f64; 16]>,
289    pub boundary_vertices: Vec<VertexId>,
290    pub boundary_rings: Vec<u32>,
291    pub boundary_surfaces: Vec<u32>,
292    pub boundary_shells: Vec<u32>,
293    pub boundary_solids: Vec<u32>,
294    pub semantic_points: Vec<Option<SemanticId>>,
295    pub semantic_linestrings: Vec<Option<SemanticId>>,
296    pub semantic_surfaces: Vec<Option<SemanticId>>,
297    pub material_themes: Vec<GeometryMaterialThemeOwned>,
298    pub material_points: Vec<Option<MaterialId>>,
299    pub material_linestrings: Vec<Option<MaterialId>>,
300    pub material_surfaces: Vec<Option<MaterialId>>,
301    pub texture_themes: Vec<GeometryTextureThemeOwned>,
302    pub texture_vertex_refs: Vec<Option<VertexId>>,
303    pub texture_rings: Vec<u32>,
304    pub texture_ring_textures: Vec<Option<TextureId>>,
305}
306
307impl GeometryTableOwned {
308    #[must_use]
309    pub fn len(&self) -> usize {
310        self.ids.len()
311    }
312
313    #[must_use]
314    pub fn is_empty(&self) -> bool {
315        self.ids.is_empty()
316    }
317}
318
319#[derive(Debug, Clone, Default)]
320pub struct AttributeArenaOwned {
321    pub node_type: Vec<AttributeNodeType>,
322    pub key_symbol: Vec<Option<SymbolId>>,
323    pub string_value_symbol: Vec<Option<SymbolId>>,
324    pub bool_value: Vec<Option<bool>>,
325    pub unsigned_value: Vec<Option<u64>>,
326    pub int_value: Vec<Option<i64>>,
327    pub float_value: Vec<Option<f64>>,
328    pub geometry_value: Vec<Option<GeometryId>>,
329    pub first_child_offset: Vec<u32>,
330    pub child_len: Vec<u32>,
331    pub child_nodes: Vec<AttributeNodeId>,
332}
333
334impl AttributeArenaOwned {
335    #[must_use]
336    pub fn len(&self) -> usize {
337        self.node_type.len()
338    }
339
340    #[must_use]
341    pub fn is_empty(&self) -> bool {
342        self.node_type.is_empty()
343    }
344}
345
346#[derive(Debug, Clone, Default)]
347pub struct ExtensionTableOwned {
348    pub name_symbols: Vec<SymbolId>,
349    pub url_symbols: Vec<SymbolId>,
350    pub version_symbols: Vec<SymbolId>,
351}
352
353impl ExtensionTableOwned {
354    #[must_use]
355    pub fn len(&self) -> usize {
356        self.name_symbols.len()
357    }
358
359    #[must_use]
360    pub fn is_empty(&self) -> bool {
361        self.name_symbols.is_empty()
362    }
363}
364
365#[derive(Debug, Clone, Default)]
366pub struct ContactOwned {
367    pub name_symbol: SymbolId,
368    pub email_symbol: SymbolId,
369    pub role_symbol: Option<SymbolId>,
370    pub website_symbol: Option<SymbolId>,
371    pub contact_type_symbol: Option<SymbolId>,
372    pub address_root: Option<AttributeNodeId>,
373    pub phone_symbol: Option<SymbolId>,
374    pub organization_symbol: Option<SymbolId>,
375}
376
377#[derive(Debug, Clone, Default)]
378pub struct MetadataOwned {
379    pub geographical_extent: Option<[f64; 6]>,
380    pub identifier_symbol: Option<SymbolId>,
381    pub reference_date_symbol: Option<SymbolId>,
382    pub reference_system_symbol: Option<SymbolId>,
383    pub title_symbol: Option<SymbolId>,
384    pub extra_root: Option<AttributeNodeId>,
385    pub point_of_contact: Option<ContactOwned>,
386}
387
388#[derive(Debug, Clone, Copy, Default)]
389pub struct TransformOwned {
390    pub scale: [f64; 3],
391    pub translate: [f64; 3],
392}
393
394#[derive(Debug, Clone, Default)]
395pub struct DefaultThemeOwned {
396    pub material_theme_symbol: Option<SymbolId>,
397    pub texture_theme_symbol: Option<SymbolId>,
398}
399
400pub struct SymbolTableView<'a> {
401    pub values: &'a [String],
402}
403
404impl SymbolTableView<'_> {
405    #[must_use]
406    pub fn len(&self) -> usize {
407        self.values.len()
408    }
409
410    #[must_use]
411    pub fn is_empty(&self) -> bool {
412        self.values.is_empty()
413    }
414}
415
416pub struct VertexTableView<'a> {
417    pub x: &'a [f64],
418    pub y: &'a [f64],
419    pub z: &'a [f64],
420}
421
422impl VertexTableView<'_> {
423    #[must_use]
424    pub fn len(&self) -> usize {
425        self.x.len()
426    }
427
428    #[must_use]
429    pub fn is_empty(&self) -> bool {
430        self.x.is_empty()
431    }
432}
433
434pub struct UvVertexTableView<'a> {
435    pub u: &'a [f64],
436    pub v: &'a [f64],
437}
438
439impl UvVertexTableView<'_> {
440    #[must_use]
441    pub fn len(&self) -> usize {
442        self.u.len()
443    }
444
445    #[must_use]
446    pub fn is_empty(&self) -> bool {
447        self.u.is_empty()
448    }
449}
450
451pub type CityObjectTableView<'a> = &'a CityObjectTableOwned;
452pub type GeometryTableView<'a> = &'a GeometryTableOwned;
453pub type SemanticTableView<'a> = &'a SemanticTableOwned;
454pub type MaterialTableView<'a> = &'a MaterialTableOwned;
455pub type TextureTableView<'a> = &'a TextureTableOwned;
456pub type AttributeArenaView<'a> = &'a AttributeArenaOwned;
457pub type ExtensionTableView<'a> = &'a ExtensionTableOwned;
458pub type MetadataView<'a> = &'a MetadataOwned;
459pub type TransformView<'a> = &'a TransformOwned;
460pub type DefaultThemeView<'a> = &'a DefaultThemeOwned;
461
462#[derive(Debug, Clone)]
463pub struct ModelRelationalView<'a> {
464    model: &'a OwnedCityModel,
465    cityobject_remap: DenseIndexRemap,
466    geometry_remap: DenseIndexRemap,
467    geometry_template_remap: DenseIndexRemap,
468    semantic_remap: DenseIndexRemap,
469    material_remap: DenseIndexRemap,
470    texture_remap: DenseIndexRemap,
471}
472
473impl<'a> ModelRelationalView<'a> {
474    #[must_use]
475    pub const fn model(&self) -> &'a OwnedCityModel {
476        self.model
477    }
478
479    #[must_use]
480    pub fn raw(&self) -> CityModelRawAccessor<'a, u32, OwnedStringStorage> {
481        self.model.raw()
482    }
483
484    #[must_use]
485    pub fn cityobjects(&self) -> &'a crate::v2_0::OwnedCityObjects {
486        self.model.cityobjects()
487    }
488
489    #[must_use]
490    pub fn cityobject_remap(&self) -> &DenseIndexRemap {
491        &self.cityobject_remap
492    }
493
494    #[must_use]
495    pub fn geometry_remap(&self) -> &DenseIndexRemap {
496        &self.geometry_remap
497    }
498
499    #[must_use]
500    pub fn geometry_template_remap(&self) -> &DenseIndexRemap {
501        &self.geometry_template_remap
502    }
503
504    #[must_use]
505    pub fn semantic_remap(&self) -> &DenseIndexRemap {
506        &self.semantic_remap
507    }
508
509    #[must_use]
510    pub fn material_remap(&self) -> &DenseIndexRemap {
511        &self.material_remap
512    }
513
514    #[must_use]
515    pub fn texture_remap(&self) -> &DenseIndexRemap {
516        &self.texture_remap
517    }
518
519    #[must_use]
520    pub fn feature_root(&self) -> Option<CityObjectId> {
521        self.model
522            .id()
523            .and_then(|handle| {
524                self.cityobject_remap
525                    .get(usize::try_from(slot(handle)).unwrap_or(usize::MAX))
526            })
527            .and_then(|dense| u32::try_from(dense).ok())
528            .map(CityObjectId)
529    }
530
531    #[must_use]
532    pub fn snapshot(&self) -> OwnedRelationalSnapshot {
533        build_relational_snapshot(self.model)
534    }
535}
536
537#[derive(Debug, Clone, Default)]
538pub struct OwnedRelationalSnapshot {
539    symbols: SymbolTableOwned,
540    vertices: VertexTableOwned,
541    template_vertices: VertexTableOwned,
542    uv_vertices: UvVertexTableOwned,
543    cityobjects: CityObjectTableOwned,
544    geometries: GeometryTableOwned,
545    geometry_templates: GeometryTableOwned,
546    semantics: SemanticTableOwned,
547    materials: MaterialTableOwned,
548    textures: TextureTableOwned,
549    attributes: AttributeArenaOwned,
550    metadata: Option<MetadataOwned>,
551    transform: Option<TransformOwned>,
552    defaults: DefaultThemeOwned,
553    extensions: ExtensionTableOwned,
554    feature_root: Option<CityObjectId>,
555}
556
557impl OwnedRelationalSnapshot {
558    #[must_use]
559    pub fn symbol_table(&self) -> &SymbolTableOwned {
560        &self.symbols
561    }
562
563    #[must_use]
564    pub fn symbols(&self) -> SymbolTableView<'_> {
565        SymbolTableView {
566            values: &self.symbols.values,
567        }
568    }
569
570    #[must_use]
571    pub fn vertex_table(&self) -> &VertexTableOwned {
572        &self.vertices
573    }
574
575    #[must_use]
576    pub fn vertices(&self) -> VertexTableView<'_> {
577        VertexTableView {
578            x: &self.vertices.x,
579            y: &self.vertices.y,
580            z: &self.vertices.z,
581        }
582    }
583
584    #[must_use]
585    pub fn template_vertices(&self) -> VertexTableView<'_> {
586        VertexTableView {
587            x: &self.template_vertices.x,
588            y: &self.template_vertices.y,
589            z: &self.template_vertices.z,
590        }
591    }
592
593    #[must_use]
594    pub fn template_vertex_table(&self) -> &VertexTableOwned {
595        &self.template_vertices
596    }
597
598    #[must_use]
599    pub fn uv_vertex_table(&self) -> &UvVertexTableOwned {
600        &self.uv_vertices
601    }
602
603    #[must_use]
604    pub fn uv_vertices(&self) -> UvVertexTableView<'_> {
605        UvVertexTableView {
606            u: &self.uv_vertices.u,
607            v: &self.uv_vertices.v,
608        }
609    }
610
611    #[must_use]
612    pub fn cityobjects(&self) -> CityObjectTableView<'_> {
613        &self.cityobjects
614    }
615
616    #[must_use]
617    pub fn geometries(&self) -> GeometryTableView<'_> {
618        &self.geometries
619    }
620
621    #[must_use]
622    pub fn geometry_templates(&self) -> GeometryTableView<'_> {
623        &self.geometry_templates
624    }
625
626    #[must_use]
627    pub fn semantics(&self) -> SemanticTableView<'_> {
628        &self.semantics
629    }
630
631    #[must_use]
632    pub fn materials(&self) -> MaterialTableView<'_> {
633        &self.materials
634    }
635
636    #[must_use]
637    pub fn textures(&self) -> TextureTableView<'_> {
638        &self.textures
639    }
640
641    #[must_use]
642    pub fn attributes(&self) -> AttributeArenaView<'_> {
643        &self.attributes
644    }
645
646    #[must_use]
647    pub fn metadata(&self) -> Option<MetadataView<'_>> {
648        self.metadata.as_ref()
649    }
650
651    #[must_use]
652    pub fn transform(&self) -> Option<TransformView<'_>> {
653        self.transform.as_ref()
654    }
655
656    #[must_use]
657    pub fn defaults(&self) -> DefaultThemeView<'_> {
658        &self.defaults
659    }
660
661    #[must_use]
662    pub fn extensions(&self) -> ExtensionTableView<'_> {
663        &self.extensions
664    }
665
666    #[must_use]
667    pub fn metadata_owned(&self) -> Option<&MetadataOwned> {
668        self.metadata.as_ref()
669    }
670
671    #[must_use]
672    pub fn transform_owned(&self) -> Option<&TransformOwned> {
673        self.transform.as_ref()
674    }
675
676    #[must_use]
677    pub fn defaults_owned(&self) -> &DefaultThemeOwned {
678        &self.defaults
679    }
680
681    #[must_use]
682    pub fn feature_root(&self) -> Option<CityObjectId> {
683        self.feature_root
684    }
685
686    fn symbol(&self, id: SymbolId) -> Result<&str> {
687        self.symbols
688            .values
689            .get(id.index())
690            .map(String::as_str)
691            .ok_or_else(|| Error::Import(format!("missing symbol {}", id.0)))
692    }
693}
694
695pub trait RelationalAccess {
696    fn relational(&self) -> ModelRelationalView<'_>;
697    fn relational_snapshot(&self) -> OwnedRelationalSnapshot;
698}
699
700impl RelationalAccess for OwnedCityModel {
701    fn relational(&self) -> ModelRelationalView<'_> {
702        ModelRelationalView {
703            model: self,
704            cityobject_remap: dense_cityobject_remap(self),
705            geometry_remap: self.raw().geometries().dense_index_remap(),
706            geometry_template_remap: dense_geometry_template_remap(self),
707            semantic_remap: self.raw().semantics().dense_index_remap(),
708            material_remap: self.raw().materials().dense_index_remap(),
709            texture_remap: self.raw().textures().dense_index_remap(),
710        }
711    }
712
713    fn relational_snapshot(&self) -> OwnedRelationalSnapshot {
714        build_relational_snapshot(self)
715    }
716}
717
718#[derive(Debug, Clone)]
719pub struct RelationalImportOptions {
720    pub symbol_storage: SymbolStorageOptions,
721    pub validate_default_themes: bool,
722    pub validate_references: bool,
723}
724
725impl Default for RelationalImportOptions {
726    fn default() -> Self {
727        Self {
728            symbol_storage: SymbolStorageOptions::default(),
729            validate_default_themes: true,
730            validate_references: true,
731        }
732    }
733}
734
735#[derive(Debug, Clone, Default)]
736pub struct RelationalCapacities {
737    pub symbols: usize,
738    pub cityobjects: usize,
739    pub geometries: usize,
740    pub geometry_templates: usize,
741    pub vertices: usize,
742    pub template_vertices: usize,
743    pub uv_vertices: usize,
744    pub semantics: usize,
745    pub materials: usize,
746    pub textures: usize,
747    pub attribute_nodes: usize,
748    pub extensions: usize,
749}
750
751#[derive(Debug, Default)]
752pub struct RelationalModelBuilder {
753    model_type: CityModelType,
754    options: RelationalImportOptions,
755    symbols: Option<SymbolTableOwned>,
756    cityobjects: Option<CityObjectTableOwned>,
757    geometries: Option<GeometryTableOwned>,
758    geometry_templates: Option<GeometryTableOwned>,
759    vertices: Option<VertexTableOwned>,
760    template_vertices: Option<VertexTableOwned>,
761    uv_vertices: Option<UvVertexTableOwned>,
762    semantics: Option<SemanticTableOwned>,
763    materials: Option<MaterialTableOwned>,
764    textures: Option<TextureTableOwned>,
765    attributes: Option<AttributeArenaOwned>,
766    metadata: Option<MetadataOwned>,
767    transform: Option<TransformOwned>,
768    defaults: Option<DefaultThemeOwned>,
769    extensions: Option<ExtensionTableOwned>,
770    feature_root: Option<CityObjectId>,
771}
772
773#[allow(clippy::missing_errors_doc)]
774impl RelationalModelBuilder {
775    #[must_use]
776    pub fn new(model_type: CityModelType, options: RelationalImportOptions) -> Self {
777        Self {
778            model_type,
779            options,
780            ..Self::default()
781        }
782    }
783
784    pub fn reserve(&mut self, _capacities: RelationalCapacities) -> Result<()> {
785        Ok(())
786    }
787
788    pub fn push_symbols(&mut self, table: SymbolTableOwned) -> Result<()> {
789        self.symbols = Some(table);
790        Ok(())
791    }
792
793    pub fn push_vertices(&mut self, table: VertexTableOwned) -> Result<()> {
794        self.vertices = Some(table);
795        Ok(())
796    }
797
798    pub fn push_template_vertices(&mut self, table: VertexTableOwned) -> Result<()> {
799        self.template_vertices = Some(table);
800        Ok(())
801    }
802
803    pub fn push_uv_vertices(&mut self, table: UvVertexTableOwned) -> Result<()> {
804        self.uv_vertices = Some(table);
805        Ok(())
806    }
807
808    pub fn push_semantics(&mut self, table: SemanticTableOwned) -> Result<()> {
809        self.semantics = Some(table);
810        Ok(())
811    }
812
813    pub fn push_materials(&mut self, table: MaterialTableOwned) -> Result<()> {
814        self.materials = Some(table);
815        Ok(())
816    }
817
818    pub fn push_textures(&mut self, table: TextureTableOwned) -> Result<()> {
819        self.textures = Some(table);
820        Ok(())
821    }
822
823    pub fn push_attributes(&mut self, table: AttributeArenaOwned) -> Result<()> {
824        self.attributes = Some(table);
825        Ok(())
826    }
827
828    pub fn push_cityobjects(&mut self, table: CityObjectTableOwned) -> Result<()> {
829        self.cityobjects = Some(table);
830        Ok(())
831    }
832
833    pub fn push_geometries(&mut self, table: GeometryTableOwned) -> Result<()> {
834        self.geometries = Some(table);
835        Ok(())
836    }
837
838    pub fn push_geometry_templates(&mut self, table: GeometryTableOwned) -> Result<()> {
839        self.geometry_templates = Some(table);
840        Ok(())
841    }
842
843    pub fn push_metadata(&mut self, view: Option<MetadataOwned>) -> Result<()> {
844        self.metadata = view;
845        Ok(())
846    }
847
848    pub fn push_transform(&mut self, view: Option<TransformOwned>) -> Result<()> {
849        self.transform = view;
850        Ok(())
851    }
852
853    pub fn push_defaults(&mut self, view: DefaultThemeOwned) -> Result<()> {
854        self.defaults = Some(view);
855        Ok(())
856    }
857
858    pub fn push_extensions(&mut self, table: ExtensionTableOwned) -> Result<()> {
859        self.extensions = Some(table);
860        Ok(())
861    }
862
863    pub fn push_feature_root(&mut self, feature_root: Option<CityObjectId>) -> Result<()> {
864        self.feature_root = feature_root;
865        Ok(())
866    }
867
868    pub fn finish(self) -> Result<OwnedCityModel> {
869        let view = OwnedRelationalSnapshot {
870            symbols: self
871                .symbols
872                .ok_or_else(|| Error::Import("missing symbol table".to_string()))?,
873            vertices: self.vertices.unwrap_or_default(),
874            template_vertices: self.template_vertices.unwrap_or_default(),
875            uv_vertices: self.uv_vertices.unwrap_or_default(),
876            cityobjects: self.cityobjects.unwrap_or_default(),
877            geometries: self.geometries.unwrap_or_default(),
878            geometry_templates: self.geometry_templates.unwrap_or_default(),
879            semantics: self.semantics.unwrap_or_default(),
880            materials: self.materials.unwrap_or_default(),
881            textures: self.textures.unwrap_or_default(),
882            attributes: self.attributes.unwrap_or_default(),
883            metadata: self.metadata,
884            transform: self.transform,
885            defaults: self.defaults.unwrap_or_default(),
886            extensions: self.extensions.unwrap_or_default(),
887            feature_root: self.feature_root,
888        };
889
890        build_model_from_relational(self.model_type, &self.options, &view)
891    }
892}
893
894#[derive(Default)]
895struct SymbolCollector {
896    ids_by_value: HashMap<String, SymbolId>,
897    values: Vec<String>,
898}
899
900impl SymbolCollector {
901    fn intern(&mut self, value: &str) -> SymbolId {
902        if let Some(id) = self.ids_by_value.get(value) {
903            return *id;
904        }
905
906        let id = SymbolId(u32::try_from(self.values.len()).unwrap_or(u32::MAX));
907        let owned = value.to_string();
908        self.values.push(owned.clone());
909        self.ids_by_value.insert(owned, id);
910        id
911    }
912}
913
914fn build_relational_snapshot(model: &OwnedCityModel) -> OwnedRelationalSnapshot {
915    let mut symbols = SymbolCollector::default();
916    let mut attributes = AttributeArenaOwned::default();
917
918    let cityobject_ids = dense_cityobject_ids(model);
919    let geometry_ids = dense_geometry_ids(model);
920    let geometry_template_ids = dense_geometry_template_ids(model);
921    let semantic_ids = dense_semantic_ids(model);
922    let material_ids = dense_material_ids(model);
923    let texture_ids = dense_texture_ids(model);
924
925    let vertices = encode_vertex_table(model.vertices().as_slice());
926    let template_vertices = encode_vertex_table(model.template_vertices().as_slice());
927    let uv_vertices = encode_uv_table(model.vertices_texture().as_slice());
928
929    let cityobjects = encode_cityobjects(
930        model,
931        &cityobject_ids,
932        &geometry_ids,
933        &mut symbols,
934        &mut attributes,
935    );
936    let semantics = encode_semantics(model, &semantic_ids, &mut symbols, &mut attributes);
937    let materials = encode_materials(model, &material_ids, &mut symbols);
938    let textures = encode_textures(model, &texture_ids, &mut symbols);
939    let geometries = encode_geometries(
940        model
941            .iter_geometries()
942            .map(|(id, geometry)| (GeometryId(geometry_ids[&slot(id)]), geometry)),
943        &semantic_ids,
944        &material_ids,
945        &texture_ids,
946        &geometry_template_ids,
947        &mut symbols,
948    );
949    let geometry_templates = encode_geometries(
950        model
951            .iter_geometry_templates()
952            .map(|(id, geometry)| (GeometryId(geometry_template_ids[&slot(id)]), geometry)),
953        &semantic_ids,
954        &material_ids,
955        &texture_ids,
956        &geometry_template_ids,
957        &mut symbols,
958    );
959    let metadata = encode_metadata(
960        model.metadata(),
961        &geometry_ids,
962        &mut symbols,
963        &mut attributes,
964    );
965    let transform = model.transform().map(|transform| TransformOwned {
966        scale: transform.scale(),
967        translate: transform.translate(),
968    });
969    let defaults = DefaultThemeOwned {
970        material_theme_symbol: model
971            .default_material_theme()
972            .map(|theme| symbols.intern(theme.as_ref())),
973        texture_theme_symbol: model
974            .default_texture_theme()
975            .map(|theme| symbols.intern(theme.as_ref())),
976    };
977    let extensions = encode_extensions(model.extensions(), &mut symbols);
978    let feature_root = model.id().map(|id| CityObjectId(cityobject_ids[&slot(id)]));
979
980    OwnedRelationalSnapshot {
981        symbols: SymbolTableOwned {
982            values: symbols.values,
983        },
984        vertices,
985        template_vertices,
986        uv_vertices,
987        cityobjects,
988        geometries,
989        geometry_templates,
990        semantics,
991        materials,
992        textures,
993        attributes,
994        metadata,
995        transform,
996        defaults,
997        extensions,
998        feature_root,
999    }
1000}
1001
1002#[allow(clippy::cast_possible_truncation, clippy::too_many_lines)]
1003fn build_model_from_relational(
1004    model_type: CityModelType,
1005    options: &RelationalImportOptions,
1006    relational: &OwnedRelationalSnapshot,
1007) -> Result<OwnedCityModel> {
1008    let capacities = CityModelCapacities {
1009        cityobjects: relational.cityobjects.len(),
1010        vertices: relational.vertices.len(),
1011        semantics: relational.semantics.len(),
1012        materials: relational.materials.len(),
1013        textures: relational.textures.len(),
1014        geometries: relational.geometries.len(),
1015        template_vertices: relational.template_vertices.len(),
1016        template_geometries: relational.geometry_templates.len(),
1017        uv_coordinates: relational.uv_vertices.len(),
1018    };
1019
1020    let mut model = OwnedCityModel::with_capacities(model_type, capacities);
1021
1022    for i in 0..relational.vertices.len() {
1023        model.add_vertex(RealWorldCoordinate::new(
1024            relational.vertices.x[i],
1025            relational.vertices.y[i],
1026            relational.vertices.z[i],
1027        ))?;
1028    }
1029
1030    for i in 0..relational.template_vertices.len() {
1031        model.add_template_vertex(RealWorldCoordinate::new(
1032            relational.template_vertices.x[i],
1033            relational.template_vertices.y[i],
1034            relational.template_vertices.z[i],
1035        ))?;
1036    }
1037
1038    for i in 0..relational.uv_vertices.len() {
1039        model.add_uv_coordinate(UVCoordinate::new(
1040            relational.uv_vertices.u[i] as f32,
1041            relational.uv_vertices.v[i] as f32,
1042        ))?;
1043    }
1044
1045    let mut semantic_handles = Vec::with_capacity(relational.semantics.len());
1046    for i in 0..relational.semantics.len() {
1047        let semantic = Semantic::new(parse_semantic_type(
1048            relational.symbol(relational.semantics.semantic_type_symbols[i])?,
1049        ));
1050        semantic_handles.push(model.add_semantic(semantic)?);
1051    }
1052    for i in 0..relational.semantics.len() {
1053        if let Some(root) = relational.semantics.attribute_root[i] {
1054            let attrs = decode_attributes(
1055                &relational.attributes,
1056                &relational.symbols.values,
1057                root,
1058                None,
1059            )?
1060            .ok_or_else(|| {
1061                Error::Import("semantic attribute root was not an object".to_string())
1062            })?;
1063            model
1064                .get_semantic_mut(semantic_handles[i])
1065                .ok_or_else(|| Error::Import("missing semantic during import".to_string()))?
1066                .attributes_mut()
1067                .clone_from(&attrs);
1068        }
1069        if let Some(parent) = relational.semantics.parent[i] {
1070            model
1071                .get_semantic_mut(semantic_handles[i])
1072                .ok_or_else(|| Error::Import("missing semantic parent target".to_string()))?
1073                .set_parent(semantic_handles[parent.index()]);
1074        }
1075        let start = usize::try_from(relational.semantics.child_start[i]).unwrap_or(usize::MAX);
1076        let len = usize::try_from(relational.semantics.child_len[i]).unwrap_or(usize::MAX);
1077        for child in &relational.semantics.children[start..start + len] {
1078            model
1079                .get_semantic_mut(semantic_handles[i])
1080                .ok_or_else(|| Error::Import("missing semantic child target".to_string()))?
1081                .children_mut()
1082                .push(semantic_handles[child.index()]);
1083        }
1084    }
1085
1086    let mut material_handles = Vec::with_capacity(relational.materials.len());
1087    for i in 0..relational.materials.len() {
1088        let mut material = Material::new(
1089            relational
1090                .symbol(relational.materials.name_symbols[i])?
1091                .to_string(),
1092        );
1093        material.set_ambient_intensity(relational.materials.ambient_intensity[i]);
1094        material.set_diffuse_color(relational.materials.diffuse_color[i].map(Into::into));
1095        material.set_emissive_color(relational.materials.emissive_color[i].map(Into::into));
1096        material.set_specular_color(relational.materials.specular_color[i].map(Into::into));
1097        material.set_shininess(relational.materials.shininess[i]);
1098        material.set_transparency(relational.materials.transparency[i]);
1099        material.set_is_smooth(relational.materials.is_smooth[i]);
1100        material_handles.push(model.add_material(material)?);
1101    }
1102
1103    let mut texture_handles = Vec::with_capacity(relational.textures.len());
1104    for i in 0..relational.textures.len() {
1105        let mut texture = Texture::new(
1106            relational
1107                .symbol(relational.textures.image_uri_symbols[i])?
1108                .to_string(),
1109            parse_image_type(relational.symbol(relational.textures.image_type_symbols[i])?),
1110        );
1111        texture.set_texture_type(
1112            relational.textures.texture_type_symbols[i]
1113                .map(|id| parse_texture_type(relational.symbol(id).unwrap_or("unknown"))),
1114        );
1115        texture.set_wrap_mode(
1116            relational.textures.wrap_mode_symbols[i]
1117                .map(|id| parse_wrap_mode(relational.symbol(id).unwrap_or("none"))),
1118        );
1119        texture.set_border_color(relational.textures.border_color[i].map(Into::into));
1120        texture_handles.push(model.add_texture(texture)?);
1121    }
1122
1123    let mut template_handles = Vec::with_capacity(relational.geometry_templates.len());
1124    for i in 0..relational.geometry_templates.len() {
1125        let geometry = decode_geometry(
1126            &relational.geometry_templates,
1127            i,
1128            &template_handles,
1129            &semantic_handles,
1130            &material_handles,
1131            &texture_handles,
1132            true,
1133            relational,
1134        )?;
1135        template_handles.push(model.add_geometry_template_unchecked(geometry)?);
1136    }
1137
1138    let mut geometry_handles = Vec::with_capacity(relational.geometries.len());
1139    for i in 0..relational.geometries.len() {
1140        let geometry = decode_geometry(
1141            &relational.geometries,
1142            i,
1143            &template_handles,
1144            &semantic_handles,
1145            &material_handles,
1146            &texture_handles,
1147            false,
1148            relational,
1149        )?;
1150        geometry_handles.push(model.add_geometry_unchecked(geometry)?);
1151    }
1152
1153    let mut cityobject_handles = Vec::with_capacity(relational.cityobjects.len());
1154    for i in 0..relational.cityobjects.len() {
1155        let mut object = CityObject::new(
1156            crate::v2_0::CityObjectIdentifier::new(
1157                relational
1158                    .symbol(relational.cityobjects.external_id_symbols[i])?
1159                    .to_string(),
1160            ),
1161            parse_cityobject_type(
1162                relational.symbol(relational.cityobjects.object_type_symbols[i])?,
1163            )?,
1164        );
1165        if let Some(root) = relational.cityobjects.attribute_root[i] {
1166            let attrs = decode_attributes(
1167                &relational.attributes,
1168                &relational.symbols.values,
1169                root,
1170                Some(&geometry_handles),
1171            )?
1172            .ok_or_else(|| {
1173                Error::Import("cityobject attribute root was not an object".to_string())
1174            })?;
1175            *object.attributes_mut() = attrs;
1176        }
1177        if let Some(min_x) = relational.cityobjects.bbox_min_x[i] {
1178            object.set_geographical_extent(Some(BBox::new(
1179                min_x,
1180                relational.cityobjects.bbox_min_y[i].unwrap_or_default(),
1181                relational.cityobjects.bbox_min_z[i].unwrap_or_default(),
1182                relational.cityobjects.bbox_max_x[i].unwrap_or_default(),
1183                relational.cityobjects.bbox_max_y[i].unwrap_or_default(),
1184                relational.cityobjects.bbox_max_z[i].unwrap_or_default(),
1185            )));
1186        }
1187        let start = usize::try_from(relational.cityobjects.geometry_start[i]).unwrap_or(usize::MAX);
1188        let len = usize::try_from(relational.cityobjects.geometry_len[i]).unwrap_or(usize::MAX);
1189        for geometry in &relational.cityobjects.geometries[start..start + len] {
1190            object.add_geometry(geometry_handles[geometry.index()]);
1191        }
1192        cityobject_handles.push(model.cityobjects_mut().add(object)?);
1193    }
1194
1195    for i in 0..relational.cityobjects.len() {
1196        let parents_start =
1197            usize::try_from(relational.cityobjects.parent_start[i]).unwrap_or(usize::MAX);
1198        let parents_len =
1199            usize::try_from(relational.cityobjects.parent_len[i]).unwrap_or(usize::MAX);
1200        for parent in &relational.cityobjects.parents[parents_start..parents_start + parents_len] {
1201            model
1202                .cityobjects_mut()
1203                .get_mut(cityobject_handles[i])
1204                .ok_or_else(|| {
1205                    Error::Import("missing cityobject during parent import".to_string())
1206                })?
1207                .add_parent(cityobject_handles[parent.index()]);
1208        }
1209        let children_start =
1210            usize::try_from(relational.cityobjects.child_start[i]).unwrap_or(usize::MAX);
1211        let children_len =
1212            usize::try_from(relational.cityobjects.child_len[i]).unwrap_or(usize::MAX);
1213        for child in &relational.cityobjects.children[children_start..children_start + children_len]
1214        {
1215            model
1216                .cityobjects_mut()
1217                .get_mut(cityobject_handles[i])
1218                .ok_or_else(|| Error::Import("missing cityobject during child import".to_string()))?
1219                .add_child(cityobject_handles[child.index()]);
1220        }
1221    }
1222
1223    if let Some(feature_root) = relational.feature_root {
1224        model.set_id(Some(cityobject_handles[feature_root.index()]));
1225    }
1226
1227    if let Some(metadata) = relational.metadata() {
1228        let target = model.metadata_mut();
1229        if let Some(extent) = metadata.geographical_extent {
1230            target.set_geographical_extent(BBox::new(
1231                extent[0], extent[1], extent[2], extent[3], extent[4], extent[5],
1232            ));
1233        }
1234        if let Some(id) = metadata.identifier_symbol {
1235            target.set_identifier(CityModelIdentifier::new(relational.symbol(id)?.to_string()));
1236        }
1237        if let Some(date) = metadata.reference_date_symbol {
1238            target.set_reference_date(crate::v2_0::Date::new(relational.symbol(date)?.to_string()));
1239        }
1240        if let Some(crs) = metadata.reference_system_symbol {
1241            target.set_reference_system(CRS::new(relational.symbol(crs)?.to_string()));
1242        }
1243        if let Some(title) = metadata.title_symbol {
1244            target.set_title(relational.symbol(title)?.to_string());
1245        }
1246        if let Some(extra_root) = metadata.extra_root {
1247            let attrs = decode_attributes(
1248                &relational.attributes,
1249                &relational.symbols.values,
1250                extra_root,
1251                Some(&geometry_handles),
1252            )?
1253            .ok_or_else(|| Error::Import("metadata extra root was not an object".to_string()))?;
1254            target.set_extra(Some(attrs));
1255        }
1256        if let Some(contact) = &metadata.point_of_contact {
1257            let mut target_contact = Contact::new();
1258            target_contact.set_contact_name(relational.symbol(contact.name_symbol)?.to_string());
1259            target_contact.set_email_address(relational.symbol(contact.email_symbol)?.to_string());
1260            target_contact.set_role(
1261                contact
1262                    .role_symbol
1263                    .map(|id| parse_contact_role(relational.symbol(id).unwrap_or("Author"))),
1264            );
1265            target_contact.set_website(
1266                contact
1267                    .website_symbol
1268                    .map(|id| relational.symbol(id).unwrap_or_default().to_string()),
1269            );
1270            target_contact.set_contact_type(
1271                contact
1272                    .contact_type_symbol
1273                    .map(|id| parse_contact_type(relational.symbol(id).unwrap_or("Individual"))),
1274            );
1275            target_contact.set_phone(
1276                contact
1277                    .phone_symbol
1278                    .map(|id| relational.symbol(id).unwrap_or_default().to_string()),
1279            );
1280            target_contact.set_organization(
1281                contact
1282                    .organization_symbol
1283                    .map(|id| relational.symbol(id).unwrap_or_default().to_string()),
1284            );
1285            if let Some(address_root) = contact.address_root {
1286                let attrs = decode_attributes(
1287                    &relational.attributes,
1288                    &relational.symbols.values,
1289                    address_root,
1290                    Some(&geometry_handles),
1291                )?
1292                .ok_or_else(|| {
1293                    Error::Import("contact address root was not an object".to_string())
1294                })?;
1295                target_contact.set_address(Some(attrs));
1296            }
1297            target.set_point_of_contact(Some(target_contact));
1298        }
1299    }
1300
1301    if let Some(transform) = relational.transform() {
1302        model.transform_mut().set_scale(transform.scale);
1303        model.transform_mut().set_translate(transform.translate);
1304    }
1305
1306    if let Some(theme) = relational.defaults.material_theme_symbol {
1307        model.set_default_material_theme(Some(ThemeName::new(
1308            relational.symbol(theme)?.to_string(),
1309        )));
1310    }
1311    if let Some(theme) = relational.defaults.texture_theme_symbol {
1312        model
1313            .set_default_texture_theme(Some(ThemeName::new(relational.symbol(theme)?.to_string())));
1314    }
1315
1316    for i in 0..relational.extensions.len() {
1317        model.extensions_mut().add(Extension::new(
1318            relational
1319                .symbol(relational.extensions.name_symbols[i])?
1320                .to_string(),
1321            relational
1322                .symbol(relational.extensions.url_symbols[i])?
1323                .to_string(),
1324            relational
1325                .symbol(relational.extensions.version_symbols[i])?
1326                .to_string(),
1327        ));
1328    }
1329
1330    if options.validate_default_themes {
1331        model.validate_default_themes()?;
1332    }
1333
1334    Ok(model)
1335}
1336
1337fn encode_vertex_table(vertices: &[RealWorldCoordinate]) -> VertexTableOwned {
1338    let mut table = VertexTableOwned::default();
1339    table.x.reserve(vertices.len());
1340    table.y.reserve(vertices.len());
1341    table.z.reserve(vertices.len());
1342    for vertex in vertices {
1343        table.x.push(vertex.x());
1344        table.y.push(vertex.y());
1345        table.z.push(vertex.z());
1346    }
1347    table
1348}
1349
1350fn encode_uv_table(vertices: &[UVCoordinate]) -> UvVertexTableOwned {
1351    let mut table = UvVertexTableOwned::default();
1352    table.u.reserve(vertices.len());
1353    table.v.reserve(vertices.len());
1354    for vertex in vertices {
1355        table.u.push(f64::from(vertex.u()));
1356        table.v.push(f64::from(vertex.v()));
1357    }
1358    table
1359}
1360
1361fn encode_cityobjects(
1362    model: &OwnedCityModel,
1363    cityobject_ids: &HashMap<u32, u32>,
1364    geometry_ids: &HashMap<u32, u32>,
1365    symbols: &mut SymbolCollector,
1366    attributes: &mut AttributeArenaOwned,
1367) -> CityObjectTableOwned {
1368    let mut table = CityObjectTableOwned::default();
1369
1370    for (dense_index, (handle, object)) in model.cityobjects().iter().enumerate() {
1371        table
1372            .ids
1373            .push(CityObjectId(u32::try_from(dense_index).unwrap_or(u32::MAX)));
1374        table.external_id_symbols.push(symbols.intern(object.id()));
1375        table
1376            .object_type_symbols
1377            .push(symbols.intern(&cityobject_type_name(object.type_cityobject())));
1378
1379        let parent_start = table.parents.len();
1380        for parent in object.parents().into_iter().flatten() {
1381            table.parents.push(CityObjectId(
1382                *cityobject_ids.get(&slot(*parent)).unwrap_or(&u32::MAX),
1383            ));
1384        }
1385        table
1386            .parent_start
1387            .push(u32::try_from(parent_start).unwrap_or(u32::MAX));
1388        table
1389            .parent_len
1390            .push(u32::try_from(table.parents.len() - parent_start).unwrap_or(u32::MAX));
1391
1392        let child_start = table.children.len();
1393        for child in object.children().into_iter().flatten() {
1394            table.children.push(CityObjectId(
1395                *cityobject_ids.get(&slot(*child)).unwrap_or(&u32::MAX),
1396            ));
1397        }
1398        table
1399            .child_start
1400            .push(u32::try_from(child_start).unwrap_or(u32::MAX));
1401        table
1402            .child_len
1403            .push(u32::try_from(table.children.len() - child_start).unwrap_or(u32::MAX));
1404
1405        let geometry_start = table.geometries.len();
1406        for geometry in object.geometry().into_iter().flatten() {
1407            table.geometries.push(GeometryId(
1408                *geometry_ids.get(&slot(*geometry)).unwrap_or(&u32::MAX),
1409            ));
1410        }
1411        table
1412            .geometry_start
1413            .push(u32::try_from(geometry_start).unwrap_or(u32::MAX));
1414        table
1415            .geometry_len
1416            .push(u32::try_from(table.geometries.len() - geometry_start).unwrap_or(u32::MAX));
1417
1418        table.attribute_root.push(
1419            object
1420                .attributes()
1421                .map(|values| encode_attributes(values, geometry_ids, symbols, attributes)),
1422        );
1423
1424        if let Some(bbox) = object.geographical_extent() {
1425            let values: [f64; 6] = (*bbox).into();
1426            table.bbox_min_x.push(Some(values[0]));
1427            table.bbox_min_y.push(Some(values[1]));
1428            table.bbox_min_z.push(Some(values[2]));
1429            table.bbox_max_x.push(Some(values[3]));
1430            table.bbox_max_y.push(Some(values[4]));
1431            table.bbox_max_z.push(Some(values[5]));
1432        } else {
1433            table.bbox_min_x.push(None);
1434            table.bbox_min_y.push(None);
1435            table.bbox_min_z.push(None);
1436            table.bbox_max_x.push(None);
1437            table.bbox_max_y.push(None);
1438            table.bbox_max_z.push(None);
1439        }
1440
1441        let _ = handle;
1442    }
1443
1444    table
1445}
1446
1447fn encode_semantics(
1448    model: &OwnedCityModel,
1449    semantic_ids: &HashMap<u32, u32>,
1450    symbols: &mut SymbolCollector,
1451    attributes: &mut AttributeArenaOwned,
1452) -> SemanticTableOwned {
1453    let mut table = SemanticTableOwned::default();
1454
1455    for (dense_index, (_handle, semantic)) in model.iter_semantics().enumerate() {
1456        table
1457            .ids
1458            .push(SemanticId(u32::try_from(dense_index).unwrap_or(u32::MAX)));
1459        table
1460            .semantic_type_symbols
1461            .push(symbols.intern(&semantic_type_name(semantic.type_semantic())));
1462        table.parent.push(
1463            semantic
1464                .parent()
1465                .map(|parent| SemanticId(*semantic_ids.get(&slot(parent)).unwrap_or(&u32::MAX))),
1466        );
1467        let child_start = table.children.len();
1468        for child in semantic.children().into_iter().flatten() {
1469            table.children.push(SemanticId(
1470                *semantic_ids.get(&slot(*child)).unwrap_or(&u32::MAX),
1471            ));
1472        }
1473        table
1474            .child_start
1475            .push(u32::try_from(child_start).unwrap_or(u32::MAX));
1476        table
1477            .child_len
1478            .push(u32::try_from(table.children.len() - child_start).unwrap_or(u32::MAX));
1479        table.attribute_root.push(
1480            semantic
1481                .attributes()
1482                .map(|values| encode_attributes(values, &HashMap::new(), symbols, attributes)),
1483        );
1484    }
1485
1486    table
1487}
1488
1489fn encode_materials(
1490    model: &OwnedCityModel,
1491    _material_ids: &HashMap<u32, u32>,
1492    symbols: &mut SymbolCollector,
1493) -> MaterialTableOwned {
1494    let mut table = MaterialTableOwned::default();
1495    for (dense_index, (_handle, material)) in model.iter_materials().enumerate() {
1496        table
1497            .ids
1498            .push(MaterialId(u32::try_from(dense_index).unwrap_or(u32::MAX)));
1499        table.name_symbols.push(symbols.intern(material.name()));
1500        table.ambient_intensity.push(material.ambient_intensity());
1501        table
1502            .diffuse_color
1503            .push(material.diffuse_color().map(Into::into));
1504        table
1505            .emissive_color
1506            .push(material.emissive_color().map(Into::into));
1507        table
1508            .specular_color
1509            .push(material.specular_color().map(Into::into));
1510        table.shininess.push(material.shininess());
1511        table.transparency.push(material.transparency());
1512        table.is_smooth.push(material.is_smooth());
1513    }
1514    table
1515}
1516
1517fn encode_textures(
1518    model: &OwnedCityModel,
1519    _texture_ids: &HashMap<u32, u32>,
1520    symbols: &mut SymbolCollector,
1521) -> TextureTableOwned {
1522    let mut table = TextureTableOwned::default();
1523    for (dense_index, (_handle, texture)) in model.iter_textures().enumerate() {
1524        table
1525            .ids
1526            .push(TextureId(u32::try_from(dense_index).unwrap_or(u32::MAX)));
1527        table
1528            .image_uri_symbols
1529            .push(symbols.intern(texture.image()));
1530        table.texture_type_symbols.push(
1531            texture
1532                .texture_type()
1533                .map(|value| symbols.intern(&value.to_string())),
1534        );
1535        table.wrap_mode_symbols.push(
1536            texture
1537                .wrap_mode()
1538                .map(|value| symbols.intern(&value.to_string())),
1539        );
1540        table
1541            .image_type_symbols
1542            .push(symbols.intern(&texture.image_type().to_string()));
1543        table
1544            .border_color
1545            .push(texture.border_color().map(Into::into));
1546    }
1547    table
1548}
1549
1550#[allow(clippy::too_many_lines)]
1551fn encode_geometries<'a>(
1552    items: impl Iterator<
1553        Item = (
1554            GeometryId,
1555            &'a Geometry<u32, crate::resources::storage::OwnedStringStorage>,
1556        ),
1557    >,
1558    semantic_ids: &HashMap<u32, u32>,
1559    material_ids: &HashMap<u32, u32>,
1560    texture_ids: &HashMap<u32, u32>,
1561    geometry_template_ids: &HashMap<u32, u32>,
1562    symbols: &mut SymbolCollector,
1563) -> GeometryTableOwned {
1564    let mut table = GeometryTableOwned::default();
1565
1566    for (dense_id, geometry) in items {
1567        table.ids.push(dense_id);
1568        table
1569            .geometry_type_symbols
1570            .push(symbols.intern(&geometry.type_geometry().to_string()));
1571        table
1572            .lod_symbols
1573            .push(geometry.lod().map(|lod| symbols.intern(&lod.to_string())));
1574
1575        if let Some(boundary) = geometry.boundaries() {
1576            push_boundary(boundary, &mut table);
1577        } else {
1578            push_empty_boundary(&mut table);
1579        }
1580
1581        if let Some(semantics) = geometry.semantics() {
1582            push_semantic_assignments(semantics, semantic_ids, &mut table);
1583        } else {
1584            push_empty_semantic_assignments(&mut table);
1585        }
1586
1587        let material_start = table.material_themes.len();
1588        if let Some(materials) = geometry.raw().materials() {
1589            for (theme, mapping) in materials {
1590                let theme_row_start_points = table.material_points.len();
1591                table
1592                    .material_points
1593                    .extend(mapping.points().iter().map(|value| {
1594                        value.map(|id| {
1595                            MaterialId(*material_ids.get(&id.index()).unwrap_or(&u32::MAX))
1596                        })
1597                    }));
1598                let theme_row_start_linestrings = table.material_linestrings.len();
1599                table
1600                    .material_linestrings
1601                    .extend(mapping.linestrings().iter().map(|value| {
1602                        value.map(|id| {
1603                            MaterialId(*material_ids.get(&id.index()).unwrap_or(&u32::MAX))
1604                        })
1605                    }));
1606                let theme_row_start_surfaces = table.material_surfaces.len();
1607                table
1608                    .material_surfaces
1609                    .extend(mapping.surfaces().iter().map(|value| {
1610                        value.map(|id| {
1611                            MaterialId(*material_ids.get(&id.index()).unwrap_or(&u32::MAX))
1612                        })
1613                    }));
1614                table.material_themes.push(GeometryMaterialThemeOwned {
1615                    geometry: dense_id,
1616                    theme_symbol: symbols.intern(theme.as_ref()),
1617                    point_start: u32::try_from(theme_row_start_points).unwrap_or(u32::MAX),
1618                    point_len: u32::try_from(table.material_points.len() - theme_row_start_points)
1619                        .unwrap_or(u32::MAX),
1620                    linestring_start: u32::try_from(theme_row_start_linestrings)
1621                        .unwrap_or(u32::MAX),
1622                    linestring_len: u32::try_from(
1623                        table.material_linestrings.len() - theme_row_start_linestrings,
1624                    )
1625                    .unwrap_or(u32::MAX),
1626                    surface_start: u32::try_from(theme_row_start_surfaces).unwrap_or(u32::MAX),
1627                    surface_len: u32::try_from(
1628                        table.material_surfaces.len() - theme_row_start_surfaces,
1629                    )
1630                    .unwrap_or(u32::MAX),
1631                });
1632            }
1633        }
1634        table
1635            .material_theme_start
1636            .push(u32::try_from(material_start).unwrap_or(u32::MAX));
1637        table
1638            .material_theme_len
1639            .push(u32::try_from(table.material_themes.len() - material_start).unwrap_or(u32::MAX));
1640
1641        let texture_start = table.texture_themes.len();
1642        if let Some(textures) = geometry.raw().textures() {
1643            for (theme, mapping) in textures {
1644                let vertex_start = table.texture_vertex_refs.len();
1645                table.texture_vertex_refs.extend(
1646                    mapping
1647                        .vertices()
1648                        .iter()
1649                        .map(|value| value.map(|vertex| VertexId(vertex.value()))),
1650                );
1651                let ring_start = table.texture_rings.len();
1652                table
1653                    .texture_rings
1654                    .extend(mapping.rings().iter().map(crate::v2_0::VertexIndex::value));
1655                let ring_texture_start = table.texture_ring_textures.len();
1656                table
1657                    .texture_ring_textures
1658                    .extend(mapping.ring_textures().iter().copied().map(|value| {
1659                        value
1660                            .map(|id| TextureId(*texture_ids.get(&id.index()).unwrap_or(&u32::MAX)))
1661                    }));
1662                table.texture_themes.push(GeometryTextureThemeOwned {
1663                    geometry: dense_id,
1664                    theme_symbol: symbols.intern(theme.as_ref()),
1665                    vertex_start: u32::try_from(vertex_start).unwrap_or(u32::MAX),
1666                    vertex_len: u32::try_from(table.texture_vertex_refs.len() - vertex_start)
1667                        .unwrap_or(u32::MAX),
1668                    ring_start: u32::try_from(ring_start).unwrap_or(u32::MAX),
1669                    ring_len: u32::try_from(table.texture_rings.len() - ring_start)
1670                        .unwrap_or(u32::MAX),
1671                    ring_texture_start: u32::try_from(ring_texture_start).unwrap_or(u32::MAX),
1672                    ring_texture_len: u32::try_from(
1673                        table.texture_ring_textures.len() - ring_texture_start,
1674                    )
1675                    .unwrap_or(u32::MAX),
1676                });
1677            }
1678        }
1679        table
1680            .texture_theme_start
1681            .push(u32::try_from(texture_start).unwrap_or(u32::MAX));
1682        table
1683            .texture_theme_len
1684            .push(u32::try_from(table.texture_themes.len() - texture_start).unwrap_or(u32::MAX));
1685
1686        if let Some(instance) = geometry.instance() {
1687            table.template_ref.push(Some(GeometryTemplateId(
1688                *geometry_template_ids
1689                    .get(&slot(instance.template()))
1690                    .unwrap_or(&u32::MAX),
1691            )));
1692            table
1693                .reference_point
1694                .push(Some(VertexId(instance.reference_point().value())));
1695            table
1696                .transform_matrix
1697                .push(instance.transformation().into_array());
1698        } else {
1699            table.template_ref.push(None);
1700            table.reference_point.push(None);
1701            table
1702                .transform_matrix
1703                .push(AffineTransform3D::identity().into_array());
1704        }
1705    }
1706
1707    table
1708}
1709
1710fn encode_extensions(
1711    extensions: Option<&crate::v2_0::Extensions<crate::resources::storage::OwnedStringStorage>>,
1712    symbols: &mut SymbolCollector,
1713) -> ExtensionTableOwned {
1714    let mut table = ExtensionTableOwned::default();
1715    if let Some(extensions) = extensions {
1716        for extension in extensions {
1717            table.name_symbols.push(symbols.intern(extension.name()));
1718            table.url_symbols.push(symbols.intern(extension.url()));
1719            table
1720                .version_symbols
1721                .push(symbols.intern(extension.version()));
1722        }
1723    }
1724    table
1725}
1726
1727fn encode_metadata(
1728    metadata: Option<&Metadata<crate::resources::storage::OwnedStringStorage>>,
1729    geometry_ids: &HashMap<u32, u32>,
1730    symbols: &mut SymbolCollector,
1731    attributes: &mut AttributeArenaOwned,
1732) -> Option<MetadataOwned> {
1733    metadata.map(|metadata| MetadataOwned {
1734        geographical_extent: metadata.geographical_extent().map(|bbox| (*bbox).into()),
1735        identifier_symbol: metadata
1736            .identifier()
1737            .map(|value| symbols.intern(&value.to_string())),
1738        reference_date_symbol: metadata
1739            .reference_date()
1740            .map(|value| symbols.intern(&value.to_string())),
1741        reference_system_symbol: metadata
1742            .reference_system()
1743            .map(|value| symbols.intern(&value.to_string())),
1744        title_symbol: metadata.title().map(|value| symbols.intern(value)),
1745        extra_root: metadata
1746            .extra()
1747            .map(|value| encode_attributes(value, geometry_ids, symbols, attributes)),
1748        point_of_contact: metadata.point_of_contact().map(|contact| ContactOwned {
1749            name_symbol: symbols.intern(contact.contact_name()),
1750            email_symbol: symbols.intern(contact.email_address()),
1751            role_symbol: contact.role().map(|role| symbols.intern(&role.to_string())),
1752            website_symbol: contact
1753                .website()
1754                .as_ref()
1755                .map(|value| symbols.intern(value)),
1756            contact_type_symbol: contact
1757                .contact_type()
1758                .map(|value| symbols.intern(&value.to_string())),
1759            address_root: contact
1760                .address()
1761                .map(|value| encode_attributes(value, geometry_ids, symbols, attributes)),
1762            phone_symbol: contact.phone().as_ref().map(|value| symbols.intern(value)),
1763            organization_symbol: contact
1764                .organization()
1765                .as_ref()
1766                .map(|value| symbols.intern(value)),
1767        }),
1768    })
1769}
1770
1771fn encode_attributes(
1772    attributes_map: &Attributes<crate::resources::storage::OwnedStringStorage>,
1773    geometry_ids: &HashMap<u32, u32>,
1774    symbols: &mut SymbolCollector,
1775    arena: &mut AttributeArenaOwned,
1776) -> AttributeNodeId {
1777    encode_attribute_value(
1778        &AttributeValue::Map(
1779            attributes_map
1780                .iter()
1781                .map(|(key, value)| (key.clone(), value.clone()))
1782                .collect(),
1783        ),
1784        None,
1785        geometry_ids,
1786        symbols,
1787        arena,
1788    )
1789}
1790
1791fn encode_attribute_value(
1792    value: &AttributeValue<crate::resources::storage::OwnedStringStorage>,
1793    key: Option<SymbolId>,
1794    geometry_ids: &HashMap<u32, u32>,
1795    symbols: &mut SymbolCollector,
1796    arena: &mut AttributeArenaOwned,
1797) -> AttributeNodeId {
1798    let id = AttributeNodeId(u32::try_from(arena.node_type.len()).unwrap_or(u32::MAX));
1799    arena.key_symbol.push(key);
1800    arena.string_value_symbol.push(None);
1801    arena.bool_value.push(None);
1802    arena.unsigned_value.push(None);
1803    arena.int_value.push(None);
1804    arena.float_value.push(None);
1805    arena.geometry_value.push(None);
1806    arena
1807        .first_child_offset
1808        .push(u32::try_from(arena.child_nodes.len()).unwrap_or(u32::MAX));
1809    arena.child_len.push(0);
1810
1811    match value {
1812        AttributeValue::Null => arena.node_type.push(AttributeNodeType::Null),
1813        AttributeValue::Bool(value) => {
1814            arena.node_type.push(AttributeNodeType::Bool);
1815            arena.bool_value[id.index()] = Some(*value);
1816        }
1817        AttributeValue::Unsigned(value) => {
1818            arena.node_type.push(AttributeNodeType::Unsigned);
1819            arena.unsigned_value[id.index()] = Some(*value);
1820        }
1821        AttributeValue::Integer(value) => {
1822            arena.node_type.push(AttributeNodeType::Integer);
1823            arena.int_value[id.index()] = Some(*value);
1824        }
1825        AttributeValue::Float(value) => {
1826            arena.node_type.push(AttributeNodeType::Float);
1827            arena.float_value[id.index()] = Some(*value);
1828        }
1829        AttributeValue::String(value) => {
1830            arena.node_type.push(AttributeNodeType::String);
1831            arena.string_value_symbol[id.index()] = Some(symbols.intern(value));
1832        }
1833        AttributeValue::Geometry(handle) => {
1834            arena.node_type.push(AttributeNodeType::GeometryRef);
1835            arena.geometry_value[id.index()] = Some(GeometryId(
1836                *geometry_ids.get(&slot(*handle)).unwrap_or(&u32::MAX),
1837            ));
1838        }
1839        AttributeValue::Vec(values) => {
1840            arena.node_type.push(AttributeNodeType::Array);
1841            let start = arena.child_nodes.len();
1842            for child in values {
1843                let child_id = encode_attribute_value(child, None, geometry_ids, symbols, arena);
1844                arena.child_nodes.push(child_id);
1845            }
1846            arena.first_child_offset[id.index()] = u32::try_from(start).unwrap_or(u32::MAX);
1847            arena.child_len[id.index()] =
1848                u32::try_from(arena.child_nodes.len() - start).unwrap_or(u32::MAX);
1849        }
1850        AttributeValue::Map(values) => {
1851            arena.node_type.push(AttributeNodeType::Object);
1852            let mut ordered = values.iter().collect::<Vec<_>>();
1853            ordered.sort_by(|(left, _), (right, _)| left.as_str().cmp(right.as_str()));
1854            let start = arena.child_nodes.len();
1855            for (child_key, child_value) in ordered {
1856                let child_id = encode_attribute_value(
1857                    child_value,
1858                    Some(symbols.intern(child_key)),
1859                    geometry_ids,
1860                    symbols,
1861                    arena,
1862                );
1863                arena.child_nodes.push(child_id);
1864            }
1865            arena.first_child_offset[id.index()] = u32::try_from(start).unwrap_or(u32::MAX);
1866            arena.child_len[id.index()] =
1867                u32::try_from(arena.child_nodes.len() - start).unwrap_or(u32::MAX);
1868        }
1869    }
1870
1871    id
1872}
1873
1874#[allow(clippy::too_many_arguments)]
1875fn decode_geometry(
1876    table: &GeometryTableOwned,
1877    index: usize,
1878    template_handles: &[crate::resources::handles::GeometryTemplateHandle],
1879    semantic_handles: &[crate::resources::handles::SemanticHandle],
1880    material_handles: &[crate::resources::handles::MaterialHandle],
1881    texture_handles: &[crate::resources::handles::TextureHandle],
1882    template_vertices: bool,
1883    relational: &OwnedRelationalSnapshot,
1884) -> Result<Geometry<u32, crate::resources::storage::OwnedStringStorage>> {
1885    let boundary = decode_boundary(table, index)?;
1886    let semantics = decode_semantic_map(table, index, semantic_handles);
1887    let materials = decode_material_maps(table, index, material_handles, relational)?;
1888    let textures = decode_texture_maps(table, index, texture_handles, relational)?;
1889    let instance = match (table.template_ref[index], table.reference_point[index]) {
1890        (Some(template), Some(reference_point)) => Some(StoredGeometryInstance {
1891            template: template_handles[template.index()],
1892            reference_point: VertexIndex::new(reference_point.0),
1893            transformation: AffineTransform3D::new(table.transform_matrix[index]),
1894        }),
1895        _ => None,
1896    };
1897
1898    let _ = template_vertices;
1899
1900    Ok(Geometry::from_stored_parts(StoredGeometryParts {
1901        type_geometry: parse_geometry_type(relational.symbol(table.geometry_type_symbols[index])?),
1902        lod: table.lod_symbols[index]
1903            .map(|symbol| parse_lod(relational.symbol(symbol).unwrap_or("0"))),
1904        boundaries: boundary,
1905        semantics,
1906        materials,
1907        textures,
1908        instance,
1909    }))
1910}
1911
1912fn decode_boundary(table: &GeometryTableOwned, index: usize) -> Result<Option<Boundary<u32>>> {
1913    let vertices: Vec<VertexIndex<u32>> = slice_u32(
1914        &table.boundary_vertices,
1915        table.boundary_vertex_start[index],
1916        table.boundary_vertex_len[index],
1917    )?
1918    .iter()
1919    .map(|value| VertexIndex::new(value.0))
1920    .collect();
1921    let rings: Vec<VertexIndex<u32>> = slice_copy(
1922        &table.boundary_rings,
1923        table.boundary_ring_start[index],
1924        table.boundary_ring_len[index],
1925    )?
1926    .into_iter()
1927    .map(VertexIndex::new)
1928    .collect();
1929    let surfaces: Vec<VertexIndex<u32>> = slice_copy(
1930        &table.boundary_surfaces,
1931        table.boundary_surface_start[index],
1932        table.boundary_surface_len[index],
1933    )?
1934    .into_iter()
1935    .map(VertexIndex::new)
1936    .collect();
1937    let shells: Vec<VertexIndex<u32>> = slice_copy(
1938        &table.boundary_shells,
1939        table.boundary_shell_start[index],
1940        table.boundary_shell_len[index],
1941    )?
1942    .into_iter()
1943    .map(VertexIndex::new)
1944    .collect();
1945    let solids: Vec<VertexIndex<u32>> = slice_copy(
1946        &table.boundary_solids,
1947        table.boundary_solid_start[index],
1948        table.boundary_solid_len[index],
1949    )?
1950    .into_iter()
1951    .map(VertexIndex::new)
1952    .collect();
1953
1954    if vertices.is_empty()
1955        && rings.is_empty()
1956        && surfaces.is_empty()
1957        && shells.is_empty()
1958        && solids.is_empty()
1959    {
1960        return Ok(None);
1961    }
1962
1963    Ok(Some(Boundary::from_parts(
1964        vertices, rings, surfaces, shells, solids,
1965    )?))
1966}
1967
1968fn decode_semantic_map(
1969    table: &GeometryTableOwned,
1970    index: usize,
1971    semantic_handles: &[crate::resources::handles::SemanticHandle],
1972) -> Option<SemanticMap<u32>> {
1973    let points = slice_copy(
1974        &table.semantic_points,
1975        table.semantic_point_start[index],
1976        table.semantic_point_len[index],
1977    )
1978    .ok()?;
1979    let linestrings = slice_copy(
1980        &table.semantic_linestrings,
1981        table.semantic_linestring_start[index],
1982        table.semantic_linestring_len[index],
1983    )
1984    .ok()?;
1985    let surfaces = slice_copy(
1986        &table.semantic_surfaces,
1987        table.semantic_surface_start[index],
1988        table.semantic_surface_len[index],
1989    )
1990    .ok()?;
1991
1992    if points.is_empty() && linestrings.is_empty() && surfaces.is_empty() {
1993        return None;
1994    }
1995
1996    let mut map = SemanticMap::new();
1997    for value in points {
1998        map.add_point(value.map(|id| semantic_handles[id.index()]));
1999    }
2000    for value in linestrings {
2001        map.add_linestring(value.map(|id| semantic_handles[id.index()]));
2002    }
2003    for value in surfaces {
2004        map.add_surface(value.map(|id| semantic_handles[id.index()]));
2005    }
2006    Some(map)
2007}
2008
2009#[allow(clippy::type_complexity)]
2010fn decode_material_maps(
2011    table: &GeometryTableOwned,
2012    index: usize,
2013    material_handles: &[crate::resources::handles::MaterialHandle],
2014    relational: &OwnedRelationalSnapshot,
2015) -> Result<
2016    Option<
2017        Vec<(
2018            ThemeName<crate::resources::storage::OwnedStringStorage>,
2019            MaterialMap<u32>,
2020        )>,
2021    >,
2022> {
2023    let start = usize::try_from(table.material_theme_start[index]).unwrap_or(usize::MAX);
2024    let len = usize::try_from(table.material_theme_len[index]).unwrap_or(usize::MAX);
2025    if len == 0 {
2026        return Ok(None);
2027    }
2028
2029    let mut items = Vec::with_capacity(len);
2030    for row in &table.material_themes[start..start + len] {
2031        let mut map = MaterialMap::new();
2032        for value in slice_copy(&table.material_points, row.point_start, row.point_len)? {
2033            map.add_point(value.map(|id| material_handles[id.index()]));
2034        }
2035        for value in slice_copy(
2036            &table.material_linestrings,
2037            row.linestring_start,
2038            row.linestring_len,
2039        )? {
2040            map.add_linestring(value.map(|id| material_handles[id.index()]));
2041        }
2042        for value in slice_copy(&table.material_surfaces, row.surface_start, row.surface_len)? {
2043            map.add_surface(value.map(|id| material_handles[id.index()]));
2044        }
2045        items.push((
2046            ThemeName::new(relational.symbol(row.theme_symbol)?.to_string()),
2047            map,
2048        ));
2049    }
2050
2051    Ok(Some(items))
2052}
2053
2054#[allow(clippy::type_complexity)]
2055fn decode_texture_maps(
2056    table: &GeometryTableOwned,
2057    index: usize,
2058    texture_handles: &[crate::resources::handles::TextureHandle],
2059    relational: &OwnedRelationalSnapshot,
2060) -> Result<
2061    Option<
2062        Vec<(
2063            ThemeName<crate::resources::storage::OwnedStringStorage>,
2064            PublicTextureMap<u32>,
2065        )>,
2066    >,
2067> {
2068    let start = usize::try_from(table.texture_theme_start[index]).unwrap_or(usize::MAX);
2069    let len = usize::try_from(table.texture_theme_len[index]).unwrap_or(usize::MAX);
2070    if len == 0 {
2071        return Ok(None);
2072    }
2073
2074    let mut items = Vec::with_capacity(len);
2075    for row in &table.texture_themes[start..start + len] {
2076        let mut map = PublicTextureMap::new();
2077        for value in slice_copy(&table.texture_vertex_refs, row.vertex_start, row.vertex_len)? {
2078            map.add_vertex(value.map(|id| VertexIndex::new(id.0)));
2079        }
2080        for value in slice_copy(&table.texture_rings, row.ring_start, row.ring_len)? {
2081            map.add_ring(VertexIndex::new(value));
2082        }
2083        for value in slice_copy(
2084            &table.texture_ring_textures,
2085            row.ring_texture_start,
2086            row.ring_texture_len,
2087        )? {
2088            map.add_ring_texture(value.map(|id| texture_handles[id.index()]));
2089        }
2090        items.push((
2091            ThemeName::new(relational.symbol(row.theme_symbol)?.to_string()),
2092            map,
2093        ));
2094    }
2095
2096    Ok(Some(items))
2097}
2098
2099fn decode_attributes(
2100    arena: &AttributeArenaOwned,
2101    symbols: &[String],
2102    root: AttributeNodeId,
2103    geometry_handles: Option<&[crate::resources::handles::GeometryHandle]>,
2104) -> Result<Option<Attributes<crate::resources::storage::OwnedStringStorage>>> {
2105    match decode_attribute_value(arena, symbols, root, geometry_handles)? {
2106        AttributeValue::Map(values) => Ok(Some(Attributes::from(values))),
2107        _ => Ok(None),
2108    }
2109}
2110
2111fn decode_attribute_value(
2112    arena: &AttributeArenaOwned,
2113    symbols: &[String],
2114    id: AttributeNodeId,
2115    geometry_handles: Option<&[crate::resources::handles::GeometryHandle]>,
2116) -> Result<AttributeValue<crate::resources::storage::OwnedStringStorage>> {
2117    let index = id.index();
2118    let kind = arena
2119        .node_type
2120        .get(index)
2121        .copied()
2122        .ok_or_else(|| Error::Import(format!("missing attribute node {}", id.0)))?;
2123
2124    Ok(match kind {
2125        AttributeNodeType::Null => AttributeValue::Null,
2126        AttributeNodeType::Bool => AttributeValue::Bool(arena.bool_value[index].unwrap_or(false)),
2127        AttributeNodeType::Unsigned => {
2128            AttributeValue::Unsigned(arena.unsigned_value[index].unwrap_or_default())
2129        }
2130        AttributeNodeType::Integer => {
2131            AttributeValue::Integer(arena.int_value[index].unwrap_or_default())
2132        }
2133        AttributeNodeType::Float => {
2134            AttributeValue::Float(arena.float_value[index].unwrap_or_default())
2135        }
2136        AttributeNodeType::String => AttributeValue::String(
2137            arena
2138                .string_value_symbol
2139                .get(index)
2140                .copied()
2141                .flatten()
2142                .and_then(|symbol| symbols.get(symbol.index()).cloned())
2143                .unwrap_or_default(),
2144        ),
2145        AttributeNodeType::GeometryRef => AttributeValue::Geometry(
2146            arena.geometry_value[index]
2147                .and_then(|geometry| {
2148                    geometry_handles.and_then(|handles| handles.get(geometry.index()).copied())
2149                })
2150                .unwrap_or_default(),
2151        ),
2152        AttributeNodeType::Array => {
2153            let mut values = Vec::new();
2154            let start = usize::try_from(arena.first_child_offset[index]).unwrap_or(usize::MAX);
2155            let len = usize::try_from(arena.child_len[index]).unwrap_or(usize::MAX);
2156            for child in &arena.child_nodes[start..start + len] {
2157                values.push(decode_attribute_value(
2158                    arena,
2159                    symbols,
2160                    *child,
2161                    geometry_handles,
2162                )?);
2163            }
2164            AttributeValue::Vec(values)
2165        }
2166        AttributeNodeType::Object => {
2167            let mut values = HashMap::new();
2168            let start = usize::try_from(arena.first_child_offset[index]).unwrap_or(usize::MAX);
2169            let len = usize::try_from(arena.child_len[index]).unwrap_or(usize::MAX);
2170            for child in &arena.child_nodes[start..start + len] {
2171                let child_index = child.index();
2172                let key = arena.key_symbol[child_index]
2173                    .and_then(|symbol| symbols.get(symbol.index()).cloned())
2174                    .unwrap_or_default();
2175                values.insert(
2176                    key,
2177                    decode_attribute_value(arena, symbols, *child, geometry_handles)?,
2178                );
2179            }
2180            AttributeValue::Map(values)
2181        }
2182    })
2183}
2184
2185fn dense_cityobject_ids(model: &OwnedCityModel) -> HashMap<u32, u32> {
2186    model
2187        .cityobjects()
2188        .iter()
2189        .enumerate()
2190        .map(|(dense, (handle, _))| (slot(handle), u32::try_from(dense).unwrap_or(u32::MAX)))
2191        .collect()
2192}
2193
2194fn dense_cityobject_remap(model: &OwnedCityModel) -> DenseIndexRemap {
2195    dense_remap_from_slots(model.cityobjects().iter().map(|(handle, _)| slot(handle)))
2196}
2197
2198fn dense_geometry_ids(model: &OwnedCityModel) -> HashMap<u32, u32> {
2199    model
2200        .iter_geometries()
2201        .enumerate()
2202        .map(|(dense, (handle, _))| (slot(handle), u32::try_from(dense).unwrap_or(u32::MAX)))
2203        .collect()
2204}
2205
2206fn dense_geometry_template_ids(model: &OwnedCityModel) -> HashMap<u32, u32> {
2207    model
2208        .iter_geometry_templates()
2209        .enumerate()
2210        .map(|(dense, (handle, _))| (slot(handle), u32::try_from(dense).unwrap_or(u32::MAX)))
2211        .collect()
2212}
2213
2214fn dense_geometry_template_remap(model: &OwnedCityModel) -> DenseIndexRemap {
2215    dense_remap_from_slots(
2216        model
2217            .iter_geometry_templates()
2218            .map(|(handle, _)| slot(handle)),
2219    )
2220}
2221
2222fn dense_semantic_ids(model: &OwnedCityModel) -> HashMap<u32, u32> {
2223    model
2224        .iter_semantics()
2225        .enumerate()
2226        .map(|(dense, (handle, _))| (slot(handle), u32::try_from(dense).unwrap_or(u32::MAX)))
2227        .collect()
2228}
2229
2230fn dense_material_ids(model: &OwnedCityModel) -> HashMap<u32, u32> {
2231    model
2232        .iter_materials()
2233        .enumerate()
2234        .map(|(dense, (handle, _))| (slot(handle), u32::try_from(dense).unwrap_or(u32::MAX)))
2235        .collect()
2236}
2237
2238fn dense_texture_ids(model: &OwnedCityModel) -> HashMap<u32, u32> {
2239    model
2240        .iter_textures()
2241        .enumerate()
2242        .map(|(dense, (handle, _))| (slot(handle), u32::try_from(dense).unwrap_or(u32::MAX)))
2243        .collect()
2244}
2245
2246fn dense_remap_from_slots(slots: impl Iterator<Item = u32>) -> DenseIndexRemap {
2247    let occupied = slots
2248        .map(|slot| usize::try_from(slot).unwrap_or(usize::MAX))
2249        .collect::<Vec<_>>();
2250    let capacity = occupied.iter().copied().max().map_or(0, |max| max + 1);
2251    DenseIndexRemap::from_occupied_indices(capacity, occupied)
2252}
2253
2254fn slot<H>(handle: H) -> u32
2255where
2256    H: Copy,
2257    H: crate::resources::handles::HandleType,
2258{
2259    handle.to_raw().index()
2260}
2261
2262fn push_boundary(boundary: &Boundary<u32>, table: &mut GeometryTableOwned) {
2263    let vertex_start = table.boundary_vertices.len();
2264    table.boundary_vertices.extend(
2265        boundary
2266            .vertices()
2267            .iter()
2268            .map(|value| VertexId(value.value())),
2269    );
2270    table
2271        .boundary_vertex_start
2272        .push(u32::try_from(vertex_start).unwrap_or(u32::MAX));
2273    table
2274        .boundary_vertex_len
2275        .push(u32::try_from(table.boundary_vertices.len() - vertex_start).unwrap_or(u32::MAX));
2276
2277    let ring_start = table.boundary_rings.len();
2278    table
2279        .boundary_rings
2280        .extend(boundary.rings_raw().iter().copied());
2281    table
2282        .boundary_ring_start
2283        .push(u32::try_from(ring_start).unwrap_or(u32::MAX));
2284    table
2285        .boundary_ring_len
2286        .push(u32::try_from(table.boundary_rings.len() - ring_start).unwrap_or(u32::MAX));
2287
2288    let surface_start = table.boundary_surfaces.len();
2289    table
2290        .boundary_surfaces
2291        .extend(boundary.surfaces_raw().iter().copied());
2292    table
2293        .boundary_surface_start
2294        .push(u32::try_from(surface_start).unwrap_or(u32::MAX));
2295    table
2296        .boundary_surface_len
2297        .push(u32::try_from(table.boundary_surfaces.len() - surface_start).unwrap_or(u32::MAX));
2298
2299    let shell_start = table.boundary_shells.len();
2300    table
2301        .boundary_shells
2302        .extend(boundary.shells_raw().iter().copied());
2303    table
2304        .boundary_shell_start
2305        .push(u32::try_from(shell_start).unwrap_or(u32::MAX));
2306    table
2307        .boundary_shell_len
2308        .push(u32::try_from(table.boundary_shells.len() - shell_start).unwrap_or(u32::MAX));
2309
2310    let solid_start = table.boundary_solids.len();
2311    table
2312        .boundary_solids
2313        .extend(boundary.solids_raw().iter().copied());
2314    table
2315        .boundary_solid_start
2316        .push(u32::try_from(solid_start).unwrap_or(u32::MAX));
2317    table
2318        .boundary_solid_len
2319        .push(u32::try_from(table.boundary_solids.len() - solid_start).unwrap_or(u32::MAX));
2320}
2321
2322fn push_empty_boundary(table: &mut GeometryTableOwned) {
2323    table
2324        .boundary_vertex_start
2325        .push(u32::try_from(table.boundary_vertices.len()).unwrap_or(u32::MAX));
2326    table.boundary_vertex_len.push(0);
2327    table
2328        .boundary_ring_start
2329        .push(u32::try_from(table.boundary_rings.len()).unwrap_or(u32::MAX));
2330    table.boundary_ring_len.push(0);
2331    table
2332        .boundary_surface_start
2333        .push(u32::try_from(table.boundary_surfaces.len()).unwrap_or(u32::MAX));
2334    table.boundary_surface_len.push(0);
2335    table
2336        .boundary_shell_start
2337        .push(u32::try_from(table.boundary_shells.len()).unwrap_or(u32::MAX));
2338    table.boundary_shell_len.push(0);
2339    table
2340        .boundary_solid_start
2341        .push(u32::try_from(table.boundary_solids.len()).unwrap_or(u32::MAX));
2342    table.boundary_solid_len.push(0);
2343}
2344
2345fn push_semantic_assignments(
2346    semantics: crate::v2_0::geometry::SemanticMapView<'_, u32>,
2347    semantic_ids: &HashMap<u32, u32>,
2348    table: &mut GeometryTableOwned,
2349) {
2350    let point_start = table.semantic_points.len();
2351    table
2352        .semantic_points
2353        .extend(semantics.points().iter().map(|value| {
2354            value.map(|id| SemanticId(*semantic_ids.get(&slot(id)).unwrap_or(&u32::MAX)))
2355        }));
2356    table
2357        .semantic_point_start
2358        .push(u32::try_from(point_start).unwrap_or(u32::MAX));
2359    table
2360        .semantic_point_len
2361        .push(u32::try_from(table.semantic_points.len() - point_start).unwrap_or(u32::MAX));
2362
2363    let linestring_start = table.semantic_linestrings.len();
2364    table
2365        .semantic_linestrings
2366        .extend(semantics.linestrings().iter().map(|value| {
2367            value.map(|id| SemanticId(*semantic_ids.get(&slot(id)).unwrap_or(&u32::MAX)))
2368        }));
2369    table
2370        .semantic_linestring_start
2371        .push(u32::try_from(linestring_start).unwrap_or(u32::MAX));
2372    table.semantic_linestring_len.push(
2373        u32::try_from(table.semantic_linestrings.len() - linestring_start).unwrap_or(u32::MAX),
2374    );
2375
2376    let surface_start = table.semantic_surfaces.len();
2377    table
2378        .semantic_surfaces
2379        .extend(semantics.surfaces().iter().map(|value| {
2380            value.map(|id| SemanticId(*semantic_ids.get(&slot(id)).unwrap_or(&u32::MAX)))
2381        }));
2382    table
2383        .semantic_surface_start
2384        .push(u32::try_from(surface_start).unwrap_or(u32::MAX));
2385    table
2386        .semantic_surface_len
2387        .push(u32::try_from(table.semantic_surfaces.len() - surface_start).unwrap_or(u32::MAX));
2388}
2389
2390fn push_empty_semantic_assignments(table: &mut GeometryTableOwned) {
2391    table
2392        .semantic_point_start
2393        .push(u32::try_from(table.semantic_points.len()).unwrap_or(u32::MAX));
2394    table.semantic_point_len.push(0);
2395    table
2396        .semantic_linestring_start
2397        .push(u32::try_from(table.semantic_linestrings.len()).unwrap_or(u32::MAX));
2398    table.semantic_linestring_len.push(0);
2399    table
2400        .semantic_surface_start
2401        .push(u32::try_from(table.semantic_surfaces.len()).unwrap_or(u32::MAX));
2402    table.semantic_surface_len.push(0);
2403}
2404
2405fn slice_copy<T: Copy>(items: &[T], start: u32, len: u32) -> Result<Vec<T>> {
2406    let start = usize::try_from(start).unwrap_or(usize::MAX);
2407    let len = usize::try_from(len).unwrap_or(usize::MAX);
2408    items
2409        .get(start..start + len)
2410        .map(<[T]>::to_vec)
2411        .ok_or_else(|| Error::Import("invalid relational slice".to_string()))
2412}
2413
2414fn slice_u32(items: &[VertexId], start: u32, len: u32) -> Result<&[VertexId]> {
2415    let start = usize::try_from(start).unwrap_or(usize::MAX);
2416    let len = usize::try_from(len).unwrap_or(usize::MAX);
2417    items
2418        .get(start..start + len)
2419        .ok_or_else(|| Error::Import("invalid relational vertex slice".to_string()))
2420}
2421
2422fn cityobject_type_name(
2423    value: &CityObjectType<crate::resources::storage::OwnedStringStorage>,
2424) -> String {
2425    value.to_string()
2426}
2427
2428fn semantic_type_name(
2429    value: &SemanticType<crate::resources::storage::OwnedStringStorage>,
2430) -> String {
2431    match value {
2432        SemanticType::Extension(value) => value.clone(),
2433        _ => format!("{value:?}"),
2434    }
2435}
2436
2437fn parse_cityobject_type(
2438    value: &str,
2439) -> Result<CityObjectType<crate::resources::storage::OwnedStringStorage>> {
2440    CityObjectType::from_str(value)
2441}
2442
2443fn parse_geometry_type(value: &str) -> GeometryType {
2444    GeometryType::from_str(value).unwrap_or(GeometryType::MultiPoint)
2445}
2446
2447fn parse_semantic_type(value: &str) -> SemanticType<crate::resources::storage::OwnedStringStorage> {
2448    match value {
2449        "Default" => SemanticType::Default,
2450        "RoofSurface" => SemanticType::RoofSurface,
2451        "GroundSurface" => SemanticType::GroundSurface,
2452        "WallSurface" => SemanticType::WallSurface,
2453        "ClosureSurface" => SemanticType::ClosureSurface,
2454        "OuterCeilingSurface" => SemanticType::OuterCeilingSurface,
2455        "OuterFloorSurface" => SemanticType::OuterFloorSurface,
2456        "Window" => SemanticType::Window,
2457        "Door" => SemanticType::Door,
2458        "InteriorWallSurface" => SemanticType::InteriorWallSurface,
2459        "CeilingSurface" => SemanticType::CeilingSurface,
2460        "FloorSurface" => SemanticType::FloorSurface,
2461        "WaterSurface" => SemanticType::WaterSurface,
2462        "WaterGroundSurface" => SemanticType::WaterGroundSurface,
2463        "WaterClosureSurface" => SemanticType::WaterClosureSurface,
2464        "TrafficArea" => SemanticType::TrafficArea,
2465        "AuxiliaryTrafficArea" => SemanticType::AuxiliaryTrafficArea,
2466        "TransportationMarking" => SemanticType::TransportationMarking,
2467        "TransportationHole" => SemanticType::TransportationHole,
2468        other => SemanticType::Extension(other.to_string()),
2469    }
2470}
2471
2472fn parse_lod(value: &str) -> LoD {
2473    match value {
2474        "0.0" => LoD::LoD0_0,
2475        "0.1" => LoD::LoD0_1,
2476        "0.2" => LoD::LoD0_2,
2477        "0.3" => LoD::LoD0_3,
2478        "1" => LoD::LoD1,
2479        "1.0" => LoD::LoD1_0,
2480        "1.1" => LoD::LoD1_1,
2481        "1.2" => LoD::LoD1_2,
2482        "1.3" => LoD::LoD1_3,
2483        "2" => LoD::LoD2,
2484        "2.0" => LoD::LoD2_0,
2485        "2.1" => LoD::LoD2_1,
2486        "2.2" => LoD::LoD2_2,
2487        "2.3" => LoD::LoD2_3,
2488        "3" => LoD::LoD3,
2489        "3.0" => LoD::LoD3_0,
2490        "3.1" => LoD::LoD3_1,
2491        "3.2" => LoD::LoD3_2,
2492        "3.3" => LoD::LoD3_3,
2493        _ => LoD::LoD0,
2494    }
2495}
2496
2497fn parse_image_type(value: &str) -> crate::v2_0::ImageType {
2498    match value {
2499        "JPG" => crate::v2_0::ImageType::Jpg,
2500        _ => crate::v2_0::ImageType::Png,
2501    }
2502}
2503
2504fn parse_wrap_mode(value: &str) -> WrapMode {
2505    match value {
2506        "wrap" => WrapMode::Wrap,
2507        "mirror" => WrapMode::Mirror,
2508        "clamp" => WrapMode::Clamp,
2509        "border" => WrapMode::Border,
2510        _ => WrapMode::None,
2511    }
2512}
2513
2514fn parse_texture_type(value: &str) -> crate::v2_0::TextureType {
2515    match value {
2516        "specific" => crate::v2_0::TextureType::Specific,
2517        "typical" => crate::v2_0::TextureType::Typical,
2518        _ => crate::v2_0::TextureType::Unknown,
2519    }
2520}
2521
2522fn parse_contact_role(value: &str) -> ContactRole {
2523    match value {
2524        "CoAuthor" => ContactRole::CoAuthor,
2525        "Processor" => ContactRole::Processor,
2526        "PointOfContact" => ContactRole::PointOfContact,
2527        "Owner" => ContactRole::Owner,
2528        "User" => ContactRole::User,
2529        "Distributor" => ContactRole::Distributor,
2530        "Originator" => ContactRole::Originator,
2531        "Custodian" => ContactRole::Custodian,
2532        "ResourceProvider" => ContactRole::ResourceProvider,
2533        "RightsHolder" => ContactRole::RightsHolder,
2534        "Sponsor" => ContactRole::Sponsor,
2535        "PrincipalInvestigator" => ContactRole::PrincipalInvestigator,
2536        "Stakeholder" => ContactRole::Stakeholder,
2537        "Publisher" => ContactRole::Publisher,
2538        _ => ContactRole::Author,
2539    }
2540}
2541
2542fn parse_contact_type(value: &str) -> ContactType {
2543    match value {
2544        "Organization" => ContactType::Organization,
2545        _ => ContactType::Individual,
2546    }
2547}
2548
2549#[cfg(test)]
2550mod tests {
2551    use super::{RelationalAccess, RelationalImportOptions, RelationalModelBuilder};
2552    use crate::CityModelType;
2553    use crate::query::summary;
2554    use crate::v2_0::geometry::semantic::SemanticType;
2555    use crate::v2_0::{
2556        CityObject, CityObjectType, Extension, GeometryDraft, ImageType, Material,
2557        OwnedAttributeValue, OwnedCityModel, RingDraft, Semantic, SurfaceDraft, Texture, ThemeName,
2558    };
2559
2560    #[allow(clippy::too_many_lines)]
2561    #[test]
2562    fn owned_model_roundtrips_through_relational_builder() {
2563        let mut model = OwnedCityModel::new(CityModelType::CityJSONFeature);
2564
2565        let roof_semantic = model
2566            .add_semantic(Semantic::new(SemanticType::RoofSurface))
2567            .unwrap();
2568        let roof_material = model
2569            .add_material(Material::new("roof".to_string()))
2570            .unwrap();
2571        let roof_texture = model
2572            .add_texture(Texture::new("roof.png".to_string(), ImageType::Png))
2573            .unwrap();
2574
2575        let geometry = GeometryDraft::multi_surface(
2576            None,
2577            [SurfaceDraft::new(
2578                RingDraft::new([[0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0]]).with_texture(
2579                    "tex".to_string(),
2580                    roof_texture,
2581                    [[0.0, 0.0], [1.0, 0.0], [0.0, 1.0]],
2582                ),
2583                [],
2584            )
2585            .with_semantic(roof_semantic)
2586            .with_material("mat".to_string(), roof_material)],
2587        )
2588        .insert_into(&mut model)
2589        .unwrap();
2590
2591        let mut building = CityObject::new(
2592            crate::v2_0::CityObjectIdentifier::new("building-1".to_string()),
2593            CityObjectType::Building,
2594        );
2595        building.add_geometry(geometry);
2596        building.attributes_mut().insert(
2597            "name".to_string(),
2598            OwnedAttributeValue::String("demo".to_string()),
2599        );
2600        let building_handle = model.cityobjects_mut().add(building).unwrap();
2601        model.set_id(Some(building_handle));
2602        model.metadata_mut().set_title("demo dataset".to_string());
2603        model.metadata_mut().extra_mut().insert(
2604            "source".to_string(),
2605            OwnedAttributeValue::String("unit-test".to_string()),
2606        );
2607        model.set_default_material_theme(Some(ThemeName::new("mat".to_string())));
2608        model.set_default_texture_theme(Some(ThemeName::new("tex".to_string())));
2609        model.extensions_mut().add(Extension::new(
2610            "noise".to_string(),
2611            "https://example.com/noise.json".to_string(),
2612            "1.0".to_string(),
2613        ));
2614
2615        let relational = model.relational_snapshot();
2616        let baseline = summary(&model);
2617
2618        assert_eq!(relational.cityobjects().len(), 1);
2619        assert_eq!(relational.geometries().len(), 1);
2620        assert!(relational.symbols().len() >= 6);
2621
2622        let mut builder =
2623            RelationalModelBuilder::new(model.type_citymodel(), RelationalImportOptions::default());
2624        builder
2625            .push_symbols(relational.symbol_table().clone())
2626            .unwrap();
2627        builder
2628            .push_vertices(relational.vertex_table().clone())
2629            .unwrap();
2630        builder
2631            .push_template_vertices(relational.template_vertex_table().clone())
2632            .unwrap();
2633        builder
2634            .push_uv_vertices(relational.uv_vertex_table().clone())
2635            .unwrap();
2636        builder
2637            .push_semantics(relational.semantics().clone())
2638            .unwrap();
2639        builder
2640            .push_materials(relational.materials().clone())
2641            .unwrap();
2642        builder
2643            .push_textures(relational.textures().clone())
2644            .unwrap();
2645        builder
2646            .push_attributes(relational.attributes().clone())
2647            .unwrap();
2648        builder
2649            .push_cityobjects(relational.cityobjects().clone())
2650            .unwrap();
2651        builder
2652            .push_geometries(relational.geometries().clone())
2653            .unwrap();
2654        builder
2655            .push_geometry_templates(relational.geometry_templates().clone())
2656            .unwrap();
2657        builder
2658            .push_metadata(relational.metadata_owned().cloned())
2659            .unwrap();
2660        builder
2661            .push_transform(relational.transform_owned().copied())
2662            .unwrap();
2663        builder
2664            .push_defaults(relational.defaults_owned().clone())
2665            .unwrap();
2666        builder
2667            .push_extensions(relational.extensions().clone())
2668            .unwrap();
2669        builder
2670            .push_feature_root(relational.feature_root())
2671            .unwrap();
2672
2673        let rebuilt = builder.finish().unwrap();
2674        let rebuilt_summary = summary(&rebuilt);
2675
2676        assert_eq!(rebuilt_summary.cityobject_count, baseline.cityobject_count);
2677        assert_eq!(rebuilt_summary.geometry_count, baseline.geometry_count);
2678        assert_eq!(rebuilt_summary.semantic_count, baseline.semantic_count);
2679        assert_eq!(rebuilt_summary.material_count, baseline.material_count);
2680        assert_eq!(rebuilt_summary.texture_count, baseline.texture_count);
2681        assert_eq!(
2682            rebuilt.id().unwrap(),
2683            rebuilt.cityobjects().first().unwrap().0
2684        );
2685        assert_eq!(rebuilt.metadata().unwrap().title(), Some("demo dataset"));
2686        assert!(rebuilt.has_material_theme("mat"));
2687        assert!(rebuilt.has_texture_theme("tex"));
2688        assert_eq!(rebuilt.extensions().unwrap().len(), 1);
2689        assert_eq!(
2690            rebuilt
2691                .cityobjects()
2692                .first()
2693                .unwrap()
2694                .1
2695                .attributes()
2696                .unwrap()
2697                .get("name"),
2698            Some(&OwnedAttributeValue::String("demo".to_string()))
2699        );
2700    }
2701}