1use std::collections::{BTreeSet, HashMap, HashSet};
7
8use crate::cityjson::resources::storage::OwnedStringStorage;
9use crate::cityjson::v2_0::attributes::Attributes;
10use crate::cityjson::v2_0::geometry::{
11 Geometry, GeometryType, StoredGeometryInstance, StoredGeometryParts,
12};
13use crate::cityjson::v2_0::metadata::BBox;
14use crate::cityjson::v2_0::{
15 CityObject, CityObjectIdentifier, MaterialMap, Metadata, SemanticMap, TextureMap, VertexIndex,
16};
17use crate::cityjson::{
18 CityModelType,
19 prelude::{
20 CityObjectHandle, GeometryHandle, GeometryTemplateHandle, MaterialHandle, SemanticHandle,
21 TextureHandle,
22 },
23 v2_0::Extensions,
24};
25use crate::{CityModel, Error, Result};
26
27type OwnedMetadata = Metadata<OwnedStringStorage>;
28type OwnedExtensions = Extensions<OwnedStringStorage>;
29type OwnedCityObject = CityObject<OwnedStringStorage>;
30type OwnedGeometry = Geometry<u32, OwnedStringStorage>;
31
32#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
33pub struct FilterOptions {
34 pub include_relatives: bool,
35}
36
37pub struct FilterContext<'a> {
38 model: &'a CityModel,
39 handle: CityObjectHandle,
40 cityobject: &'a CityObject<OwnedStringStorage>,
41}
42
43impl<'a> FilterContext<'a> {
44 pub fn model(&self) -> &'a CityModel {
45 self.model
46 }
47
48 pub fn handle(&self) -> CityObjectHandle {
49 self.handle
50 }
51
52 pub fn cityobject(&self) -> &'a CityObject<OwnedStringStorage> {
53 self.cityobject
54 }
55
56 pub fn id(&self) -> &'a str {
57 self.cityobject.id()
58 }
59}
60
61fn import_error(message: impl Into<String>) -> Error {
62 Error::Import(message.into())
63}
64
65fn unsupported(message: &'static str) -> Error {
66 Error::UnsupportedFeature(message.to_string())
67}
68
69fn same_transform(target: &CityModel, source: &CityModel) -> bool {
70 match source.transform() {
71 None => true,
72 Some(source_transform) => target.transform() == Some(source_transform),
73 }
74}
75
76fn append_kind_compatible(target_kind: CityModelType, source_kind: CityModelType) -> bool {
77 target_kind == source_kind
78 || (target_kind == CityModelType::CityJSON && source_kind == CityModelType::CityJSONFeature)
79}
80
81fn union_bbox(lhs: BBox, rhs: BBox) -> BBox {
82 BBox::new(
83 lhs.min_x().min(rhs.min_x()),
84 lhs.min_y().min(rhs.min_y()),
85 lhs.min_z().min(rhs.min_z()),
86 lhs.max_x().max(rhs.max_x()),
87 lhs.max_y().max(rhs.max_y()),
88 lhs.max_z().max(rhs.max_z()),
89 )
90}
91
92fn merge_attributes(
93 target: &mut Attributes<OwnedStringStorage>,
94 source: &Attributes<OwnedStringStorage>,
95) {
96 for (key, value) in source.iter() {
97 target.insert(key.clone(), value.clone());
98 }
99}
100
101fn merge_cityobject_extent(target: &mut OwnedCityObject, source: &OwnedCityObject) {
102 match (
103 target.geographical_extent().copied(),
104 source.geographical_extent().copied(),
105 ) {
106 (None, Some(extent)) => target.set_geographical_extent(Some(extent)),
107 (Some(lhs), Some(rhs)) if lhs != rhs => {
108 target.set_geographical_extent(Some(union_bbox(lhs, rhs)))
109 }
110 _ => {}
111 }
112}
113
114fn merge_metadata(target: &mut OwnedMetadata, source: &OwnedMetadata) {
115 if target.geographical_extent().is_none()
116 && let Some(extent) = source.geographical_extent().copied()
117 {
118 target.set_geographical_extent(extent);
119 } else if let (Some(lhs), Some(rhs)) = (
120 target.geographical_extent().copied(),
121 source.geographical_extent().copied(),
122 ) && lhs != rhs
123 {
124 target.set_geographical_extent(union_bbox(lhs, rhs));
125 }
126
127 if target.identifier().is_none()
128 && let Some(identifier) = source.identifier().cloned()
129 {
130 target.set_identifier(identifier);
131 }
132
133 if target.reference_date().is_none()
134 && let Some(date) = source.reference_date().cloned()
135 {
136 target.set_reference_date(date);
137 }
138
139 if target.reference_system().is_none()
140 && let Some(crs) = source.reference_system().cloned()
141 {
142 target.set_reference_system(crs);
143 }
144
145 if target.title().is_none()
146 && let Some(title) = source.title()
147 {
148 target.set_title(title.to_owned());
149 }
150
151 if target.point_of_contact().is_none()
152 && let Some(contact) = source.point_of_contact().cloned()
153 {
154 target.set_point_of_contact(Some(contact));
155 }
156
157 if let Some(extra) = source.extra() {
158 let target_extra = target.extra_mut();
159 for (key, value) in extra.iter() {
160 target_extra.insert(key.clone(), value.clone());
161 }
162 }
163}
164
165fn merge_root_extensions(target: &mut OwnedExtensions, source: &OwnedExtensions) {
166 for extension in source {
167 target.add(extension.clone());
168 }
169}
170
171fn remap_vertex_indices(
172 boundary: &crate::cityjson::v2_0::boundary::Boundary<u32>,
173 vertex_map: &[VertexIndex<u32>],
174) -> Result<crate::cityjson::v2_0::boundary::Boundary<u32>> {
175 let mut boundary = boundary.clone();
176 let remapped = boundary.vertices().iter().map(|index| {
177 vertex_map
178 .get(index.to_usize())
179 .copied()
180 .ok_or_else(|| import_error(format!("vertex index {} is out of range", index.value())))
181 });
182 boundary.set_vertices_from_iter(remapped.collect::<Result<Vec<_>>>()?);
183 Ok(boundary)
184}
185
186fn remap_texture_map(
187 map: &crate::cityjson::v2_0::geometry::TextureMapView<'_, u32>,
188 uv_map: &[VertexIndex<u32>],
189 texture_map: &HashMap<TextureHandle, TextureHandle>,
190) -> Result<TextureMap<u32>> {
191 let mut remapped = TextureMap::new();
192
193 for vertex in map.vertices() {
194 let mapped = vertex
195 .map(|index| {
196 uv_map.get(index.to_usize()).copied().ok_or_else(|| {
197 import_error(format!("uv vertex index {} is out of range", index.value()))
198 })
199 })
200 .transpose()?;
201 remapped.add_vertex(mapped);
202 }
203
204 for ring in map.rings() {
205 remapped.add_ring(*ring);
206 }
207
208 for texture in map.ring_textures() {
209 remapped.add_ring_texture(
210 texture.map(|handle| texture_map.get(&handle).copied().unwrap_or(handle)),
211 );
212 }
213
214 Ok(remapped)
215}
216
217fn remap_material_map<'a, I, J, K>(
218 points: I,
219 linestrings: J,
220 surfaces: K,
221 material_map: &HashMap<MaterialHandle, MaterialHandle>,
222) -> MaterialMap<u32>
223where
224 I: IntoIterator<Item = &'a Option<MaterialHandle>>,
225 J: IntoIterator<Item = &'a Option<MaterialHandle>>,
226 K: IntoIterator<Item = &'a Option<MaterialHandle>>,
227{
228 let mut remapped = MaterialMap::new();
229
230 for item in points {
231 remapped.add_point(match item {
232 Some(handle) => Some(material_map.get(handle).copied().unwrap_or(*handle)),
233 None => None,
234 });
235 }
236 for item in linestrings {
237 remapped.add_linestring(match item {
238 Some(handle) => Some(material_map.get(handle).copied().unwrap_or(*handle)),
239 None => None,
240 });
241 }
242 for item in surfaces {
243 remapped.add_surface(match item {
244 Some(handle) => Some(material_map.get(handle).copied().unwrap_or(*handle)),
245 None => None,
246 });
247 }
248
249 remapped
250}
251
252fn remap_semantic_map<'a, I, J, K>(
253 points: I,
254 linestrings: J,
255 surfaces: K,
256 semantic_map: &HashMap<SemanticHandle, SemanticHandle>,
257) -> SemanticMap<u32>
258where
259 I: IntoIterator<Item = &'a Option<SemanticHandle>>,
260 J: IntoIterator<Item = &'a Option<SemanticHandle>>,
261 K: IntoIterator<Item = &'a Option<SemanticHandle>>,
262{
263 let mut remapped = SemanticMap::new();
264
265 for item in points {
266 remapped.add_point(match item {
267 Some(handle) => Some(semantic_map.get(handle).copied().unwrap_or(*handle)),
268 None => None,
269 });
270 }
271 for item in linestrings {
272 remapped.add_linestring(match item {
273 Some(handle) => Some(semantic_map.get(handle).copied().unwrap_or(*handle)),
274 None => None,
275 });
276 }
277 for item in surfaces {
278 remapped.add_surface(match item {
279 Some(handle) => Some(semantic_map.get(handle).copied().unwrap_or(*handle)),
280 None => None,
281 });
282 }
283
284 remapped
285}
286
287fn remap_geometry(
288 geometry: &OwnedGeometry,
289 vertex_map: &[VertexIndex<u32>],
290 template_map: &HashMap<GeometryTemplateHandle, GeometryTemplateHandle>,
291 semantic_map: &HashMap<SemanticHandle, SemanticHandle>,
292 material_map: &HashMap<MaterialHandle, MaterialHandle>,
293 texture_map: &HashMap<TextureHandle, TextureHandle>,
294 uv_map: &[VertexIndex<u32>],
295) -> Result<OwnedGeometry> {
296 let stored_parts = if let Some(instance) = geometry.instance() {
297 let template = template_map
298 .get(&instance.template())
299 .copied()
300 .ok_or_else(|| {
301 import_error(format!(
302 "missing remap for geometry template {}",
303 instance.template()
304 ))
305 })?;
306 Geometry::from_stored_parts(StoredGeometryParts {
307 type_geometry: GeometryType::GeometryInstance,
308 lod: None,
309 boundaries: None,
310 semantics: None,
311 materials: None,
312 textures: None,
313 instance: Some(StoredGeometryInstance {
314 template,
315 reference_point: *vertex_map
316 .get(instance.reference_point().to_usize())
317 .ok_or_else(|| {
318 import_error(format!(
319 "vertex index {} is out of range",
320 instance.reference_point().value()
321 ))
322 })?,
323 transformation: instance.transformation(),
324 }),
325 })
326 } else {
327 let boundaries = geometry
328 .boundaries()
329 .map(|boundary| remap_vertex_indices(boundary, vertex_map))
330 .transpose()?;
331
332 let semantics = geometry.semantics().map(|theme| {
333 let points = theme.points();
334 let linestrings = theme.linestrings();
335 let surfaces = theme.surfaces();
336 remap_semantic_map(
337 points.iter(),
338 linestrings.iter(),
339 surfaces.iter(),
340 semantic_map,
341 )
342 });
343
344 let materials = geometry.materials().map(|themes| {
345 themes
346 .iter()
347 .map(|(name, theme)| {
348 let points = theme.points();
349 let linestrings = theme.linestrings();
350 let surfaces = theme.surfaces();
351 (
352 name.clone(),
353 remap_material_map(
354 points.iter(),
355 linestrings.iter(),
356 surfaces.iter(),
357 material_map,
358 ),
359 )
360 })
361 .collect::<Vec<_>>()
362 });
363
364 let textures = geometry
365 .textures()
366 .map(|themes| {
367 themes
368 .iter()
369 .map(|(name, theme)| {
370 remap_texture_map(&theme, uv_map, texture_map)
371 .map(|map| (name.clone(), map))
372 })
373 .collect::<Result<Vec<_>>>()
374 })
375 .transpose()?;
376
377 Geometry::from_stored_parts(StoredGeometryParts {
378 type_geometry: *geometry.type_geometry(),
379 lod: geometry.lod().copied(),
380 boundaries,
381 semantics,
382 materials,
383 textures,
384 instance: None,
385 })
386 };
387
388 Ok(stored_parts)
389}
390
391fn append_vertices(target: &mut CityModel, source: &CityModel) -> Result<Vec<VertexIndex<u32>>> {
392 let mut map = Vec::with_capacity(source.vertices().len());
393 for vertex in source.vertices().as_slice() {
394 map.push(target.add_vertex(*vertex)?);
395 }
396 Ok(map)
397}
398
399fn append_template_vertices(
400 target: &mut CityModel,
401 source: &CityModel,
402) -> Result<Vec<VertexIndex<u32>>> {
403 let mut map = Vec::with_capacity(source.template_vertices().len());
404 for vertex in source.template_vertices().as_slice() {
405 map.push(target.add_template_vertex(*vertex)?);
406 }
407 Ok(map)
408}
409
410fn append_uv_vertices(target: &mut CityModel, source: &CityModel) -> Result<Vec<VertexIndex<u32>>> {
411 let mut map = Vec::with_capacity(source.vertices_texture().len());
412 for uv in source.vertices_texture().as_slice() {
413 map.push(target.add_uv_coordinate((*uv).clone())?);
414 }
415 Ok(map)
416}
417
418fn append_semantics(
419 target: &mut CityModel,
420 source: &CityModel,
421) -> Result<HashMap<SemanticHandle, SemanticHandle>> {
422 let mut map = HashMap::with_capacity(source.semantic_count());
423 for (handle, semantic) in source.iter_semantics() {
424 map.insert(handle, target.add_semantic(semantic.clone())?);
425 }
426 Ok(map)
427}
428
429fn append_materials(
430 target: &mut CityModel,
431 source: &CityModel,
432) -> Result<HashMap<MaterialHandle, MaterialHandle>> {
433 let mut map = HashMap::with_capacity(source.material_count());
434 for (handle, material) in source.iter_materials() {
435 map.insert(handle, target.add_material(material.clone())?);
436 }
437 Ok(map)
438}
439
440fn append_textures(
441 target: &mut CityModel,
442 source: &CityModel,
443) -> Result<HashMap<TextureHandle, TextureHandle>> {
444 let mut map = HashMap::with_capacity(source.texture_count());
445 for (handle, texture) in source.iter_textures() {
446 map.insert(handle, target.add_texture(texture.clone())?);
447 }
448 Ok(map)
449}
450
451fn append_geometry_templates(
452 target: &mut CityModel,
453 source: &CityModel,
454 template_vertex_map: &[VertexIndex<u32>],
455 template_map: &HashMap<GeometryTemplateHandle, GeometryTemplateHandle>,
456 semantic_map: &HashMap<SemanticHandle, SemanticHandle>,
457 material_map: &HashMap<MaterialHandle, MaterialHandle>,
458 texture_map: &HashMap<TextureHandle, TextureHandle>,
459 uv_map: &[VertexIndex<u32>],
460) -> Result<HashMap<GeometryTemplateHandle, GeometryTemplateHandle>> {
461 let mut map = HashMap::with_capacity(source.geometry_template_count());
462 for (handle, geometry) in source.iter_geometry_templates() {
463 let remapped = remap_geometry(
464 geometry,
465 template_vertex_map,
466 template_map,
467 semantic_map,
468 material_map,
469 texture_map,
470 uv_map,
471 )?;
472 map.insert(handle, target.add_geometry_template(remapped)?);
473 }
474 Ok(map)
475}
476
477fn append_geometries(
478 target: &mut CityModel,
479 source: &CityModel,
480 vertex_map: &[VertexIndex<u32>],
481 template_map: &HashMap<GeometryTemplateHandle, GeometryTemplateHandle>,
482 semantic_map: &HashMap<SemanticHandle, SemanticHandle>,
483 material_map: &HashMap<MaterialHandle, MaterialHandle>,
484 texture_map: &HashMap<TextureHandle, TextureHandle>,
485 uv_map: &[VertexIndex<u32>],
486) -> Result<HashMap<GeometryHandle, GeometryHandle>> {
487 let mut map = HashMap::with_capacity(source.geometry_count());
488 for (handle, geometry) in source.iter_geometries() {
489 let remapped = remap_geometry(
490 geometry,
491 vertex_map,
492 template_map,
493 semantic_map,
494 material_map,
495 texture_map,
496 uv_map,
497 )?;
498 map.insert(handle, target.add_geometry(remapped)?);
499 }
500 Ok(map)
501}
502
503fn merge_cityobject(
504 target: &mut OwnedCityObject,
505 source: &OwnedCityObject,
506 cityobject_map: &HashMap<CityObjectHandle, CityObjectHandle>,
507 geometry_map: &HashMap<GeometryHandle, GeometryHandle>,
508) -> Result<()> {
509 if target.type_cityobject() != source.type_cityobject() {
510 return Err(import_error(format!(
511 "conflicting CityObject types for '{}'",
512 target.id()
513 )));
514 }
515
516 if let Some(attributes) = source.attributes() {
517 merge_attributes(target.attributes_mut(), attributes);
518 }
519 merge_cityobject_extent(target, source);
520
521 if let Some(extra) = source.extra() {
522 let target_extra = target.extra_mut();
523 for (key, value) in extra.iter() {
524 target_extra.insert(key.clone(), value.clone());
525 }
526 }
527
528 if let Some(geometry_handles) = source.geometry() {
529 let mut target_geometry = target
530 .geometry()
531 .map(|items| items.to_vec())
532 .unwrap_or_default();
533 for geometry in geometry_handles {
534 let mapped = geometry_map.get(geometry).copied().ok_or_else(|| {
535 import_error(format!(
536 "missing remap for geometry {}",
537 geometry.raw_parts().0
538 ))
539 })?;
540 if !target_geometry.contains(&mapped) {
541 target.add_geometry(mapped);
542 target_geometry.push(mapped);
543 }
544 }
545 }
546
547 if let Some(children) = source.children() {
548 let mut existing = target
549 .children()
550 .map(|items| items.to_vec())
551 .unwrap_or_default();
552 for child in children {
553 let mapped = cityobject_map.get(child).copied().ok_or_else(|| {
554 import_error(format!(
555 "missing remap for cityobject {}",
556 child.raw_parts().0
557 ))
558 })?;
559 if !existing.contains(&mapped) {
560 target.add_child(mapped);
561 existing.push(mapped);
562 }
563 }
564 }
565
566 if let Some(parents) = source.parents() {
567 let mut existing = target
568 .parents()
569 .map(|items| items.to_vec())
570 .unwrap_or_default();
571 for parent in parents {
572 let mapped = cityobject_map.get(parent).copied().ok_or_else(|| {
573 import_error(format!(
574 "missing remap for cityobject {}",
575 parent.raw_parts().0
576 ))
577 })?;
578 if !existing.contains(&mapped) {
579 target.add_parent(mapped);
580 existing.push(mapped);
581 }
582 }
583 }
584
585 Ok(())
586}
587
588fn merge_one(target: &mut CityModel, source: &CityModel) -> Result<()> {
589 if !same_transform(target, source) {
590 return Err(unsupported(
591 "model merge currently requires identical transform objects",
592 ));
593 }
594
595 if !append_kind_compatible(target.type_citymodel(), source.type_citymodel()) {
596 return Err(import_error(
597 "model merge currently requires compatible root types",
598 ));
599 }
600
601 if target.metadata().is_none() {
602 if let Some(metadata) = source.metadata() {
603 *target.metadata_mut() = metadata.clone();
604 }
605 } else if let Some(source_metadata) = source.metadata() {
606 merge_metadata(target.metadata_mut(), source_metadata);
607 }
608
609 if target.extra().is_none() {
610 if let Some(extra) = source.extra() {
611 *target.extra_mut() = extra.clone();
612 }
613 } else if let Some(extra) = source.extra() {
614 let target_extra = target.extra_mut();
615 for (key, value) in extra.iter() {
616 target_extra.insert(key.clone(), value.clone());
617 }
618 }
619
620 if target.extensions().is_none() {
621 if let Some(extensions) = source.extensions() {
622 *target.extensions_mut() = extensions.clone();
623 }
624 } else if let Some(extensions) = source.extensions() {
625 merge_root_extensions(target.extensions_mut(), extensions);
626 }
627
628 if target.transform().is_none() {
629 if let Some(transform) = source.transform() {
630 *target.transform_mut() = transform.clone();
631 }
632 }
633
634 if target.default_material_theme().is_none()
635 && let Some(theme) = source.default_material_theme().cloned()
636 {
637 target.set_default_material_theme(Some(theme));
638 }
639
640 if target.default_texture_theme().is_none()
641 && let Some(theme) = source.default_texture_theme().cloned()
642 {
643 target.set_default_texture_theme(Some(theme));
644 }
645
646 let vertex_map = append_vertices(target, source)?;
647 let template_vertex_map = append_template_vertices(target, source)?;
648 let uv_map = append_uv_vertices(target, source)?;
649 let semantic_map = append_semantics(target, source)?;
650 let material_map = append_materials(target, source)?;
651 let texture_map = append_textures(target, source)?;
652 let empty_template_map: HashMap<GeometryTemplateHandle, GeometryTemplateHandle> =
653 HashMap::new();
654 let template_map = append_geometry_templates(
655 target,
656 source,
657 &template_vertex_map,
658 &empty_template_map,
659 &semantic_map,
660 &material_map,
661 &texture_map,
662 &uv_map,
663 )?;
664 let geometry_map = append_geometries(
665 target,
666 source,
667 &vertex_map,
668 &template_map,
669 &semantic_map,
670 &material_map,
671 &texture_map,
672 &uv_map,
673 )?;
674
675 let mut cityobject_map = HashMap::with_capacity(source.cityobjects().len());
676 for (handle, source_cityobject) in source.cityobjects().iter() {
677 if let Some(existing) = target
678 .cityobjects()
679 .iter()
680 .find(|(_, cityobject)| cityobject.id() == source_cityobject.id())
681 .map(|(handle, _)| handle)
682 {
683 cityobject_map.insert(handle, existing);
684 continue;
685 }
686
687 let placeholder = CityObject::new(
688 CityObjectIdentifier::new(source_cityobject.id().to_owned()),
689 source_cityobject.type_cityobject().clone(),
690 );
691 let new_handle = target.cityobjects_mut().add(placeholder)?;
692 cityobject_map.insert(handle, new_handle);
693 }
694
695 if target.id().is_none()
696 && let Some(source_id) = source.id()
697 && let Some(mapped) = cityobject_map.get(&source_id).copied()
698 {
699 target.set_id(Some(mapped));
700 }
701
702 for (handle, source_cityobject) in source.cityobjects().iter() {
703 let target_handle = cityobject_map.get(&handle).copied().ok_or_else(|| {
704 import_error(format!(
705 "missing remap for cityobject {}",
706 source_cityobject.id()
707 ))
708 })?;
709 let target_cityobject =
710 target
711 .cityobjects_mut()
712 .get_mut(target_handle)
713 .ok_or_else(|| {
714 import_error(format!(
715 "missing target cityobject for {}",
716 source_cityobject.id()
717 ))
718 })?;
719 merge_cityobject(
720 target_cityobject,
721 source_cityobject,
722 &cityobject_map,
723 &geometry_map,
724 )?;
725 }
726
727 Ok(())
728}
729
730pub fn cleanup(model: &CityModel) -> Result<CityModel> {
731 cityjson_json::cleanup(model).map_err(Error::from)
732}
733
734fn collect_reachable_cityobjects<I>(
735 model: &CityModel,
736 roots: I,
737 include_parents: bool,
738 include_children: bool,
739) -> Result<HashSet<CityObjectHandle>>
740where
741 I: IntoIterator<Item = CityObjectHandle>,
742{
743 let mut selected = HashSet::new();
744 let mut stack = roots.into_iter().collect::<Vec<_>>();
745
746 while let Some(handle) = stack.pop() {
747 let cityobject = model.cityobjects().get(handle).ok_or_else(|| {
748 import_error(format!(
749 "missing CityObject handle in traversal: {handle:?}"
750 ))
751 })?;
752 if !selected.insert(handle) {
753 continue;
754 }
755
756 if include_children && let Some(children) = cityobject.children() {
757 stack.extend(children.iter().copied());
758 }
759
760 if include_parents && let Some(parents) = cityobject.parents() {
761 stack.extend(parents.iter().copied());
762 }
763 }
764
765 Ok(selected)
766}
767
768fn rebuild_model_with_cityobjects(
769 model: &CityModel,
770 selected: &HashSet<CityObjectHandle>,
771) -> Result<CityModel> {
772 let mut result = model.clone();
773 result.clear_cityobjects();
774
775 let mut old_to_new = HashMap::with_capacity(selected.len());
776 for (handle, cityobject) in model.cityobjects().iter() {
777 if !selected.contains(&handle) {
778 continue;
779 }
780
781 let mut cloned = cityobject.clone();
782 cloned.clear_children();
783 cloned.clear_parents();
784 let new_handle = result.cityobjects_mut().add(cloned)?;
785 old_to_new.insert(handle, new_handle);
786 }
787
788 for (handle, cityobject) in model.cityobjects().iter() {
789 if !selected.contains(&handle) {
790 continue;
791 }
792
793 let target_handle = *old_to_new.get(&handle).ok_or_else(|| {
794 import_error(format!("missing remap for CityObject {}", cityobject.id()))
795 })?;
796 let target = result
797 .cityobjects_mut()
798 .get_mut(target_handle)
799 .ok_or_else(|| {
800 import_error(format!("missing target CityObject {}", cityobject.id()))
801 })?;
802
803 if let Some(children) = cityobject.children() {
804 for child in children {
805 model.cityobjects().get(*child).ok_or_else(|| {
806 import_error(format!("missing child CityObject handle {child:?}"))
807 })?;
808 if let Some(mapped) = old_to_new.get(child).copied() {
809 target.add_child(mapped);
810 }
811 }
812 }
813
814 if let Some(parents) = cityobject.parents() {
815 for parent in parents {
816 model.cityobjects().get(*parent).ok_or_else(|| {
817 import_error(format!("missing parent CityObject handle {parent:?}"))
818 })?;
819 if let Some(mapped) = old_to_new.get(parent).copied() {
820 target.add_parent(mapped);
821 }
822 }
823 }
824 }
825
826 if let Some(root) = model.id() {
827 model
828 .cityobjects()
829 .get(root)
830 .ok_or_else(|| import_error("feature root references a missing CityObject"))?;
831 result.set_id(old_to_new.get(&root).copied());
832 }
833
834 Ok(result)
835}
836
837pub fn subset<'a, I>(model: &CityModel, cityobject_ids: I, exclude: bool) -> Result<CityModel>
838where
839 I: IntoIterator<Item = &'a str>,
840{
841 let ids = cityobject_ids
842 .into_iter()
843 .map(str::to_owned)
844 .collect::<BTreeSet<_>>();
845 if ids.is_empty() {
846 return Err(import_error(
847 "subset requires at least one CityObject identifier",
848 ));
849 }
850
851 let id_to_handle = model
852 .cityobjects()
853 .iter()
854 .map(|(handle, cityobject)| (cityobject.id().to_owned(), handle))
855 .collect::<HashMap<_, _>>();
856
857 let mut roots = Vec::new();
858 let mut matched_any = false;
859
860 for id in &ids {
861 if let Some(handle) = id_to_handle.get(id).copied() {
862 matched_any = true;
863 roots.push(handle);
864 }
865 }
866
867 if !matched_any {
868 return Err(import_error("subset selection matched no CityObjects"));
869 }
870
871 let mut selected = collect_reachable_cityobjects(model, roots, false, true)?;
872
873 if exclude {
874 let excluded = selected;
875 selected = model
876 .cityobjects()
877 .iter()
878 .map(|(handle, _)| handle)
879 .filter(|handle| !excluded.contains(handle))
880 .collect();
881 }
882
883 rebuild_model_with_cityobjects(model, &selected)
884}
885
886pub fn filter<F>(model: &CityModel, predicate: F) -> Result<CityModel>
887where
888 F: FnMut(FilterContext<'_>) -> bool,
889{
890 filter_with_options(model, FilterOptions::default(), predicate)
891}
892
893pub fn filter_with_options<F>(
894 model: &CityModel,
895 options: FilterOptions,
896 mut predicate: F,
897) -> Result<CityModel>
898where
899 F: FnMut(FilterContext<'_>) -> bool,
900{
901 let mut selected = model
902 .cityobjects()
903 .iter()
904 .filter_map(|(handle, cityobject)| {
905 predicate(FilterContext {
906 model,
907 handle,
908 cityobject,
909 })
910 .then_some(handle)
911 })
912 .collect::<HashSet<_>>();
913
914 if options.include_relatives {
915 selected = collect_reachable_cityobjects(model, selected, true, true)?;
916 }
917
918 rebuild_model_with_cityobjects(model, &selected)
919}
920
921pub fn append(target: &mut CityModel, source: &CityModel) -> Result<()> {
922 merge_one(target, source)
923}
924
925pub fn merge<I>(models: I) -> Result<CityModel>
926where
927 I: IntoIterator<Item = CityModel>,
928{
929 let mut models = models.into_iter();
930 let Some(mut merged) = models.next() else {
931 return Err(import_error("merge requires at least one model"));
932 };
933
934 for model in models {
935 merge_one(&mut merged, &model)?;
936 }
937
938 Ok(merged)
939}