From ebb45d28d5c94f0257d61b50ada5f1dea7bb38df Mon Sep 17 00:00:00 2001 From: Benedikt Schwab Date: Mon, 5 Aug 2024 13:53:58 +0200 Subject: [PATCH] extended citygml data structures --- .github/workflows/ci.yml | 7 + Cargo.toml | 13 +- README.md | 3 +- crates/ecitygml-cli/Cargo.toml | 3 +- crates/ecitygml-cli/README.md | 3 +- .../ecitygml-cli/src/commands/statistics.rs | 223 +++++++++++--- crates/ecitygml-core/Cargo.toml | 1 + crates/ecitygml-core/README.md | 3 +- crates/ecitygml-core/src/lib.rs | 19 +- crates/ecitygml-core/src/model/building.rs | 63 ++++ .../ecitygml-core/src/model/city_furniture.rs | 60 ++-- crates/ecitygml-core/src/model/city_model.rs | 204 ++++--------- .../ecitygml-core/src/model/construction.rs | 162 ++++++++-- crates/ecitygml-core/src/model/core.rs | 273 ++++++++++++++++- crates/ecitygml-core/src/model/mod.rs | 3 +- .../src/model/solitary_vegetation_object.rs | 34 ++- .../ecitygml-core/src/model/transportation.rs | 287 +++++++++++++++++- .../src/operations/geometry_collector.rs | 87 ++++++ crates/ecitygml-core/src/operations/mod.rs | 28 ++ crates/ecitygml-core/src/operations/traits.rs | 26 ++ .../ecitygml-core/src/operations/visitor.rs | 236 ++++++++++++++ crates/ecitygml-core/src/util.rs | 7 + crates/ecitygml-io/Cargo.toml | 9 +- crates/ecitygml-io/README.md | 3 +- crates/ecitygml-io/src/lib.rs | 2 +- crates/ecitygml-io/src/parse.rs | 107 ------- crates/ecitygml-io/src/parser/attributes.rs | 37 +++ crates/ecitygml-io/src/parser/building.rs | 118 +++++++ crates/ecitygml-io/src/parser/mod.rs | 4 + crates/ecitygml-io/src/parser/space.rs | 279 +++++++++++++++++ .../ecitygml-io/src/parser/transportation.rs | 252 +++++++++++++++ crates/ecitygml-io/src/read.rs | 3 +- crates/ecitygml-io/src/read_impl.rs | 117 +++---- crates/ecitygml-transform/Cargo.toml | 3 +- crates/ecitygml-transform/README.md | 3 +- crates/ecitygml-transform/src/error.rs | 2 + crates/ecitygml-transform/src/filter.rs | 164 ++++++---- crates/ecitygml-transform/src/lib.rs | 1 - crates/ecitygml-transform/src/offset.rs | 92 ------ crates/ecitygml/Cargo.toml | 6 +- crates/ecitygml/README.md | 3 +- crates/ecitygml/src/lib.rs | 6 +- 42 files changed, 2284 insertions(+), 672 deletions(-) create mode 100644 crates/ecitygml-core/src/model/building.rs create mode 100644 crates/ecitygml-core/src/operations/geometry_collector.rs create mode 100644 crates/ecitygml-core/src/operations/mod.rs create mode 100644 crates/ecitygml-core/src/operations/traits.rs create mode 100644 crates/ecitygml-core/src/operations/visitor.rs create mode 100644 crates/ecitygml-core/src/util.rs delete mode 100644 crates/ecitygml-io/src/parse.rs create mode 100644 crates/ecitygml-io/src/parser/attributes.rs create mode 100644 crates/ecitygml-io/src/parser/building.rs create mode 100644 crates/ecitygml-io/src/parser/mod.rs create mode 100644 crates/ecitygml-io/src/parser/space.rs create mode 100644 crates/ecitygml-io/src/parser/transportation.rs delete mode 100644 crates/ecitygml-transform/src/offset.rs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9c18a84..673000d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -8,6 +8,13 @@ on: pull_request: {} +env: + CARGO_INCREMENTAL: 0 + CARGO_NET_RETRY: 10 + RUSTUP_MAX_RETRIES: 10 + RUST_BACKTRACE: short + + jobs: build: diff --git a/Cargo.toml b/Cargo.toml index 9eff107..944b1c9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,21 +6,24 @@ members = [ resolver = "2" [workspace.package] -version = "0.0.1-alpha.3" +version = "0.0.1-alpha.4" authors = ["Benedikt Schwab "] edition = "2021" license = "MIT OR Apache-2.0" repository = "https://github.com/envis-space/ecitygml" [workspace.dependencies] -egml = { version = "0.0.1-alpha.3" } +egml = { version = "0.0.1-alpha.4" } -thiserror = "1.0.61" -clap = "4.5.9" +thiserror = "1.0.63" +clap = "4.5.13" tracing = "0.1.40" tracing-subscriber = "0.3.18" nalgebra = "0.33.0" serde = "1.0.204" serde_yaml = "0.9" -quick-xml = "0.36.0" +quick-xml = "0.36.1" itertools = "0.13.0" +rayon = "1.10.0" +walkdir = "2.5.0" +uuid = "1.3.2" diff --git a/README.md b/README.md index af747d1..99fafe2 100644 --- a/README.md +++ b/README.md @@ -2,8 +2,7 @@ A Rust library for processing [CityGML](https://www.ogc.org/standard/citygml/) data. -> [!WARNING] -> The library is at an early stage of development. +The library is at an early stage of development. ## Contributing diff --git a/crates/ecitygml-cli/Cargo.toml b/crates/ecitygml-cli/Cargo.toml index 5c3a0d6..61ac232 100644 --- a/crates/ecitygml-cli/Cargo.toml +++ b/crates/ecitygml-cli/Cargo.toml @@ -8,7 +8,7 @@ repository.workspace = true description = "CLI tool for processing CityGML data." [dependencies] -ecitygml = { version = "0.0.1-alpha.3", path = "../ecitygml" } +ecitygml = { version = "0.0.1-alpha.4", path = "../ecitygml" } egml = { workspace = true } @@ -16,3 +16,4 @@ clap = { workspace = true, features = ["derive"] } tracing = { workspace = true } tracing-subscriber = { workspace = true } nalgebra = { workspace = true } +walkdir = { workspace = true } diff --git a/crates/ecitygml-cli/README.md b/crates/ecitygml-cli/README.md index a7498ce..6c14ec4 100644 --- a/crates/ecitygml-cli/README.md +++ b/crates/ecitygml-cli/README.md @@ -2,8 +2,7 @@ CLI tool for processing [CityGML](https://www.ogc.org/standard/citygml/) data. -> [!WARNING] -> The library is at an early stage of development. +The library is at an early stage of development. ## Contributing diff --git a/crates/ecitygml-cli/src/commands/statistics.rs b/crates/ecitygml-cli/src/commands/statistics.rs index a989546..4060c22 100644 --- a/crates/ecitygml-cli/src/commands/statistics.rs +++ b/crates/ecitygml-cli/src/commands/statistics.rs @@ -1,102 +1,231 @@ +use ecitygml::io::{FILE_EXTENSION_CITYGML_GML_FORMAT, FILE_EXTENSION_CITYGML_XML_FORMAT}; +use ecitygml::model::construction::{GroundSurface, RoofSurface, WallSurface}; +use ecitygml::model::core::{OccupiedSpace, Space, ThematicSurface}; +use ecitygml::operations::FeatureWithGeometry; use std::path::Path; - +use std::time::Instant; use tracing::info; +use walkdir::WalkDir; + +pub fn run(path: impl AsRef) { + info!("Creating statistics for: {}", path.as_ref().display()); + + if path.as_ref().is_file() { + print_citygml_model_statistics(path); + } else if path.as_ref().is_dir() { + for entry in WalkDir::new(path) + .sort_by(|a, b| a.file_name().cmp(b.file_name())) + .into_iter() + .filter_map(Result::ok) + .filter(|e| e.file_type().is_file() && e.path().extension().is_some()) + .filter(|e| { + e.path().extension().expect("must have an extension") + == FILE_EXTENSION_CITYGML_GML_FORMAT + || e.path().extension().expect("must have an extension") + == FILE_EXTENSION_CITYGML_XML_FORMAT + }) + { + info!("Start reading: {:?}", entry); + let now = Instant::now(); + let citygml_model = ecitygml::io::CitygmlReader::from_path(entry.into_path()) + .unwrap() + .finish() + .unwrap(); + info!("Read model in {:.3?}", now.elapsed()); + } + } +} -pub fn run(file_path: impl AsRef) { +fn print_citygml_model_statistics(file_path: impl AsRef) { + let now = Instant::now(); let citygml_model = ecitygml::io::CitygmlReader::from_path(file_path) .unwrap() .finish() .unwrap(); + info!("Read model in {:.3?}", now.elapsed()); info!( "Number of city objects: {}\n", citygml_model.number_of_objects() ); + let envelope = citygml_model.envelope().unwrap(); + info!( + "Envelope: lower corner{}, upper corner {}\n", + envelope.lower_corner(), + envelope.upper_corner() + ); + + info!("Total Building: {}", citygml_model.building.len()); + let wall_surfaces: Vec<&WallSurface> = citygml_model + .building + .iter() + .flat_map(|x| &x.wall_surface) + .collect(); + info!("Total WallSurface: {}", wall_surfaces.len()); + if !wall_surfaces.is_empty() { + print_statistics_thematic_surface( + wall_surfaces.iter().map(|x| &x.thematic_surface).collect(), + ); + } + + let roof_surfaces: Vec<&RoofSurface> = citygml_model + .building + .iter() + .flat_map(|x| &x.roof_surface) + .collect(); + info!("Total RoofSurface: {}", roof_surfaces.len()); + if !roof_surfaces.is_empty() { + print_statistics_thematic_surface( + roof_surfaces.iter().map(|x| &x.thematic_surface).collect(), + ); + } + + let ground_surfaces: Vec<&GroundSurface> = citygml_model + .building + .iter() + .flat_map(|x| &x.ground_surface) + .collect(); + info!("Total GroundSurface: {}", ground_surfaces.len()); + if !ground_surfaces.is_empty() { + print_statistics_thematic_surface( + ground_surfaces + .iter() + .map(|x| &x.thematic_surface) + .collect(), + ); + } + info!( "Total CityFurniture: {}", - citygml_model.city_furniture().len() + citygml_model.city_furniture.len() ); + if !citygml_model.city_furniture.is_empty() { + print_statistics_occupied_space( + citygml_model + .city_furniture + .iter() + .map(|x| &x.occupied_space) + .collect(), + ); + } + + info!("Total Road: {}", citygml_model.road.len()); + info!( - "\t- with lod1_solid: {}", - citygml_model - .city_furniture() + "Total SolitaryVegetationObject: {}", + citygml_model.solitary_vegetation_object.len() + ); + if !citygml_model.solitary_vegetation_object.is_empty() { + print_statistics_occupied_space( + citygml_model + .solitary_vegetation_object + .iter() + .map(|x| &x.occupied_space) + .collect(), + ); + } + + /*info!("Offset city model"); + let translation = nalgebra::Vector3::new(-678071.2478652871, -5403609.8367785765, -415.28); + let isometry = Isometry3::new(translation, Default::default()); + citygml_model.apply_transform(&isometry); + info!( + "Number of city objects: {}", + citygml_model.number_of_objects() + );*/ +} + +fn print_statistics_occupied_space(occupied_space: Vec<&OccupiedSpace>) { + info!( + "\t- with lod1_implicit_representation: {}", + occupied_space .iter() - .filter(|c| c.lod1_solid().is_some()) + .filter(|x| x.lod1_implicit_representation.is_some()) .count() ); info!( - "\t- with lod2_solid: {}", - citygml_model - .city_furniture() + "\t- with lod2_implicit_representation: {}", + occupied_space .iter() - .filter(|c| c.lod2_solid().is_some()) + .filter(|x| x.lod2_implicit_representation.is_some()) .count() ); info!( - "\t- with lod2_multi_surface: {}", - citygml_model - .city_furniture() + "\t- with lod3_implicit_representation: {}", + occupied_space .iter() - .filter(|c| c.lod2_multi_surface().is_some()) + .filter(|x| x.lod3_implicit_representation.is_some()) .count() ); + + print_statistics_space(occupied_space.iter().map(|x| &x.space).collect()); +} + +fn print_statistics_space(space: Vec<&Space>) { + info!( + "\t- with lod1_solid: {}", + space.iter().filter(|x| x.lod1_solid.is_some()).count() + ); + info!( + "\t- with lod2_solid: {}", + space.iter().filter(|x| x.lod2_solid.is_some()).count() + ); info!( - "\t- with reference_point: {}", - citygml_model - .city_furniture() + "\t- with lod3_solid: {}", + space.iter().filter(|x| x.lod3_solid.is_some()).count() + ); + + info!( + "\t- with lod0_multi_surface: {}", + space .iter() - .filter(|c| c.reference_point().is_some()) + .filter(|x| x.lod0_multi_surface.is_some()) .count() ); - - info!("Total TrafficArea: {}", citygml_model.traffic_area().len()); info!( "\t- with lod2_multi_surface: {}", - citygml_model - .traffic_area() + space + .iter() + .filter(|x| x.lod2_multi_surface.is_some()) + .count() + ); + info!( + "\t- with lod3_multi_surface: {}", + space .iter() - .filter(|c| c.lod2_multi_surface().is_some()) + .filter(|x| x.lod3_multi_surface.is_some()) .count() ); +} +fn print_statistics_thematic_surface(thematic_surface: Vec<&ThematicSurface>) { info!( - "Total SolitaryVegetationObject: {}", - citygml_model.solitary_vegetation_object().len() + "\t- with lod0_multi_surface: {}", + thematic_surface + .iter() + .filter(|x| x.lod0_multi_surface.is_some()) + .count() ); info!( - "\t- with lod1_solid: {}", - citygml_model - .solitary_vegetation_object() + "\t- with lod1_multi_surface: {}", + thematic_surface .iter() - .filter(|c| c.lod1_solid().is_some()) + .filter(|x| x.lod1_multi_surface.is_some()) .count() ); - - info!("Total WallSurface: {}", citygml_model.wall_surface().len()); info!( "\t- with lod2_multi_surface: {}", - citygml_model - .wall_surface() + thematic_surface .iter() - .filter(|c| c.lod2_multi_surface().is_some()) + .filter(|x| x.lod2_multi_surface.is_some()) .count() ); info!( "\t- with lod3_multi_surface: {}", - citygml_model - .wall_surface() + thematic_surface .iter() - .filter(|c| c.lod3_multi_surface().is_some()) + .filter(|x| x.lod3_multi_surface.is_some()) .count() ); - - info!("Offset city model"); - let offset = nalgebra::Vector3::new(-678071.2478652871, -5403609.8367785765, -415.28); - let transformed_citygml_model = - ecitygml::transform::offset::offset_citygml_model(citygml_model, &offset).unwrap(); - info!( - "Number of city objects: {}", - transformed_citygml_model.number_of_objects() - ); } diff --git a/crates/ecitygml-core/Cargo.toml b/crates/ecitygml-core/Cargo.toml index f19dcb5..fec6fa0 100644 --- a/crates/ecitygml-core/Cargo.toml +++ b/crates/ecitygml-core/Cargo.toml @@ -12,4 +12,5 @@ egml = { workspace = true } thiserror = { workspace = true } nalgebra = { workspace = true } +rayon = { workspace = true } # parry3d-f64 = { workspace = true, features = ["f64"] } diff --git a/crates/ecitygml-core/README.md b/crates/ecitygml-core/README.md index 89c5d87..531c094 100644 --- a/crates/ecitygml-core/README.md +++ b/crates/ecitygml-core/README.md @@ -2,8 +2,7 @@ Core primitives and operations for processing [CityGML](https://www.ogc.org/standard/citygml/) data. -> [!WARNING] -> The library is at an early stage of development. +The library is at an early stage of development. ## Contributing diff --git a/crates/ecitygml-core/src/lib.rs b/crates/ecitygml-core/src/lib.rs index 1ce4fd0..9f23498 100644 --- a/crates/ecitygml-core/src/lib.rs +++ b/crates/ecitygml-core/src/lib.rs @@ -1,20 +1,7 @@ mod error; -mod model; - -#[doc(inline)] -pub use model::city_model::CitygmlModel; - -#[doc(inline)] -pub use model::city_furniture::CityFurniture; - -#[doc(inline)] -pub use model::transportation::TrafficArea; - -#[doc(inline)] -pub use model::solitary_vegetation_object::SolitaryVegetationObject; - -#[doc(inline)] -pub use model::construction::WallSurface; +pub mod model; +pub mod operations; +pub mod util; #[doc(inline)] pub use error::Error; diff --git a/crates/ecitygml-core/src/model/building.rs b/crates/ecitygml-core/src/model/building.rs new file mode 100644 index 0000000..62d3e06 --- /dev/null +++ b/crates/ecitygml-core/src/model/building.rs @@ -0,0 +1,63 @@ +use crate::model::construction::{GroundSurface, RoofSurface, WallSurface}; +use crate::model::core::CityObject; +use crate::operations::{FeatureWithGeometry, Visitable, Visitor}; +use egml::model::base; +use egml::model::base::Gml; +use egml::model::geometry::Envelope; +use nalgebra::Isometry3; + +#[derive(Debug, Clone, PartialEq)] +pub struct Building { + pub city_object: CityObject, // TODO: space + pub wall_surface: Vec, + pub roof_surface: Vec, + pub ground_surface: Vec, +} + +impl Building { + pub fn new(id: base::Id) -> Self { + let gml = Gml::new(id); + let city_object = CityObject::new(gml); + + Self { + city_object, + wall_surface: Vec::new(), + roof_surface: Vec::new(), + ground_surface: Vec::new(), + } + } +} + +impl Visitable for Building { + fn accept(&self, visitor: &mut V) { + visitor.visit_building(self); + self.wall_surface.iter().for_each(|x| x.accept(visitor)); + self.roof_surface.iter().for_each(|x| x.accept(visitor)); + self.ground_surface.iter().for_each(|x| x.accept(visitor)); + } +} + +impl FeatureWithGeometry for Building { + fn envelope(&self) -> Option { + // todo: let mut envelopes: Vec> = vec![self.occupied_space.envelope()]; + let mut envelopes: Vec> = vec![]; + envelopes.extend(self.wall_surface.iter().map(|x| x.envelope())); + envelopes.extend(self.roof_surface.iter().map(|x| x.envelope())); + envelopes.extend(self.ground_surface.iter().map(|x| x.envelope())); + + Envelope::from_optional_envelopes(&envelopes).expect("should work") + } + + fn apply_transform(&mut self, m: &Isometry3) { + // todo: self.occupied_space.apply_transform(m); + self.wall_surface + .iter_mut() + .for_each(|x| x.apply_transform(m)); + self.roof_surface + .iter_mut() + .for_each(|x| x.apply_transform(m)); + self.ground_surface + .iter_mut() + .for_each(|x| x.apply_transform(m)); + } +} diff --git a/crates/ecitygml-core/src/model/city_furniture.rs b/crates/ecitygml-core/src/model/city_furniture.rs index 0636f08..73cb8e3 100644 --- a/crates/ecitygml-core/src/model/city_furniture.rs +++ b/crates/ecitygml-core/src/model/city_furniture.rs @@ -1,56 +1,32 @@ -use egml::base; -use egml::geometry; +use crate::model::core::OccupiedSpace; +use crate::operations::{FeatureWithGeometry, Visitable, Visitor}; +use egml::model::geometry::Envelope; +use nalgebra::Isometry3; #[derive(Debug, Clone, PartialEq)] pub struct CityFurniture { - id: base::Id, - name: String, - reference_point: Option, - lod2_multi_surface: Option, - lod2_solid: Option, - lod1_solid: Option, + pub occupied_space: OccupiedSpace, } impl CityFurniture { - pub fn new(id: base::Id, name: String) -> Self { - Self { - id, - name, - reference_point: None, - lod2_multi_surface: None, - lod2_solid: None, - lod1_solid: None, - } - } - - pub fn reference_point(&self) -> &Option { - &self.reference_point - } - - pub fn set_reference_point(&mut self, reference_point: Option) { - self.reference_point = reference_point; - } - - pub fn lod2_multi_surface(&self) -> &Option { - &self.lod2_multi_surface - } - - pub fn set_lod2_multi_surface(&mut self, lod2_multi_surface: Option) { - self.lod2_multi_surface = lod2_multi_surface; + pub fn new(occupied_space: OccupiedSpace) -> Self { + Self { occupied_space } } +} - pub fn lod1_solid(&self) -> &Option { - &self.lod1_solid +impl Visitable for CityFurniture { + fn accept(&self, visitor: &mut V) { + visitor.visit_city_furniture(self); + self.occupied_space.accept(visitor); } +} - pub fn set_lod1_solid(&mut self, lod2_solid: Option) { - self.lod1_solid = lod2_solid; - } - pub fn lod2_solid(&self) -> &Option { - &self.lod2_solid +impl FeatureWithGeometry for CityFurniture { + fn envelope(&self) -> Option { + self.occupied_space.envelope() } - pub fn set_lod2_solid(&mut self, lod2_solid: Option) { - self.lod2_solid = lod2_solid; + fn apply_transform(&mut self, m: &Isometry3) { + self.occupied_space.apply_transform(m); } } diff --git a/crates/ecitygml-core/src/model/city_model.rs b/crates/ecitygml-core/src/model/city_model.rs index c7735f7..cf4f854 100644 --- a/crates/ecitygml-core/src/model/city_model.rs +++ b/crates/ecitygml-core/src/model/city_model.rs @@ -1,190 +1,102 @@ -use crate::{CityFurniture, TrafficArea, WallSurface}; - +use crate::model::building::Building; +use crate::model::city_furniture::CityFurniture; use crate::model::solitary_vegetation_object::SolitaryVegetationObject; -use egml::geometry::{enlarge_envelopes, Envelope}; +use crate::model::transportation::Road; +use crate::operations::{FeatureWithGeometry, Visitable, Visitor}; +use egml::model::geometry::Envelope; +use nalgebra::Isometry3; +use rayon::prelude::*; #[derive(Debug, Clone, PartialEq, Default)] pub struct CitygmlModel { - city_furniture: Vec, - traffic_area: Vec, - solitary_vegetation_object: Vec, - wall_surface: Vec, + pub building: Vec, + pub city_furniture: Vec, + pub road: Vec, + pub solitary_vegetation_object: Vec, } impl CitygmlModel { pub fn new( + building: Vec, city_furniture: Vec, - traffic_area: Vec, + road: Vec, solitary_vegetation_object: Vec, - wall_surface: Vec, ) -> Self { Self { + building, city_furniture, - traffic_area, + road, solitary_vegetation_object, - wall_surface, } } pub fn from_citygml_models(citygml_models: &Vec) -> Self { + let building: Vec = citygml_models + .iter() + .flat_map(|x| x.building.iter().cloned()) + .collect(); let city_furniture: Vec = citygml_models .iter() .flat_map(|x| x.city_furniture.iter().cloned()) .collect(); - let traffic_area: Vec = citygml_models + let road: Vec = citygml_models .iter() - .flat_map(|x| x.traffic_area.iter().cloned()) + .flat_map(|x| x.road.iter().cloned()) .collect(); let solitary_vegetation_object: Vec = citygml_models .iter() .flat_map(|x| x.solitary_vegetation_object.iter().cloned()) .collect(); - let wall_surface: Vec = citygml_models - .iter() - .flat_map(|x| x.wall_surface.iter().cloned()) - .collect(); - - CitygmlModel::new( - city_furniture, - traffic_area, - solitary_vegetation_object, - wall_surface, - ) - } - pub fn city_furniture(&self) -> &Vec { - &self.city_furniture - } - pub fn set_city_furniture(&mut self, val: Vec) { - self.city_furniture = val; - } - - pub fn traffic_area(&self) -> &Vec { - &self.traffic_area - } - pub fn set_traffic_area(&mut self, val: Vec) { - self.traffic_area = val; - } - - pub fn solitary_vegetation_object(&self) -> &Vec { - &self.solitary_vegetation_object - } - pub fn set_solitary_vegetation_object(&mut self, val: Vec) { - self.solitary_vegetation_object = val; - } - - pub fn wall_surface(&self) -> &Vec { - &self.wall_surface - } - pub fn set_wall_surface(&mut self, val: Vec) { - self.wall_surface = val; + CitygmlModel::new(building, city_furniture, road, solitary_vegetation_object) } pub fn is_empty(&self) -> bool { - self.city_furniture.is_empty() - && self.traffic_area.is_empty() - && self.wall_surface.is_empty() - } - - pub fn push_city_furniture(&mut self, city_furniture: CityFurniture) { - self.city_furniture.push(city_furniture); - } - - pub fn push_traffic_area(&mut self, traffic_area: TrafficArea) { - self.traffic_area.push(traffic_area); - } - - pub fn push_solitary_vegetation_object( - &mut self, - solitary_vegetation_object: SolitaryVegetationObject, - ) { - self.solitary_vegetation_object - .push(solitary_vegetation_object); - } - - pub fn push_wall_surface(&mut self, wall_surface: WallSurface) { - self.wall_surface.push(wall_surface); + self.building.is_empty() + && self.city_furniture.is_empty() + && self.road.is_empty() + && self.solitary_vegetation_object.is_empty() } pub fn number_of_objects(&self) -> usize { - self.city_furniture.len() + self.traffic_area.len() + self.wall_surface.len() + self.building.len() + + self.city_furniture.len() + + self.road.len() + + self.solitary_vegetation_object.len() } +} - pub fn get_envelope(&self) -> Option { - let mut envelopes: Vec = Vec::new(); - let mut feature_envelopes: Vec = self - .city_furniture - .iter() - .flat_map(|f| f.lod2_multi_surface()) - .map(|f| f.get_envelope().unwrap()) - .collect(); - envelopes.append(&mut feature_envelopes); - - let mut feature_envelopes: Vec = self - .city_furniture - .iter() - .flat_map(|f| f.lod2_solid()) - .map(|f| f.get_envelope().unwrap()) - .collect(); - envelopes.append(&mut feature_envelopes); - - let mut feature_envelopes: Vec = self - .wall_surface - .iter() - .flat_map(|f| f.lod2_multi_surface()) - .map(|f| f.get_envelope().unwrap()) - .collect(); - envelopes.append(&mut feature_envelopes); - - let mut feature_envelopes: Vec = self - .traffic_area +impl Visitable for CitygmlModel { + fn accept(&self, visitor: &mut V) { + visitor.visit_city_model(self); + self.building.iter().for_each(|x| x.accept(visitor)); + self.city_furniture.iter().for_each(|x| x.accept(visitor)); + self.road.iter().for_each(|x| x.accept(visitor)); + self.solitary_vegetation_object .iter() - .flat_map(|f| f.lod2_multi_surface()) - .map(|f| f.get_envelope().unwrap()) - .collect(); - envelopes.append(&mut feature_envelopes); - - if envelopes.is_empty() { - return None; - } - let city_model_envelopes = enlarge_envelopes(&envelopes).unwrap(); - Some(city_model_envelopes) + .for_each(|x| x.accept(visitor)); } +} - /*pub(crate) fn all_vertices(&self) -> Vec<&Point3> { - /*let all_vertices: Vec<&Point3> = self - .city_objects - .iter() - .flat_map(|m| &m.geometries) - .flat_map(|p| p.points()) - .collect();*/ +impl FeatureWithGeometry for CitygmlModel { + fn envelope(&self) -> Option { + let mut envelopes: Vec> = vec![]; + envelopes.extend(self.building.iter().map(|x| x.envelope())); + envelopes.extend(self.city_furniture.iter().map(|x| x.envelope())); + envelopes.extend(self.road.iter().map(|x| x.envelope())); + envelopes.extend(self.solitary_vegetation_object.iter().map(|x| x.envelope())); - let all_vertices: Vec<&Point3> = Vec::new(); - all_vertices + Envelope::from_optional_envelopes(&envelopes).expect("should work") } - pub fn get_min(&self) -> Point3 { - let vertices: Vec<&Point3> = self.all_vertices(); - - let min_x = vertices - .iter() - .map(|p| p.x) - .collect::>() - .iter() - .fold(f64::INFINITY, |a, &b| a.min(b)); - let min_y = vertices - .iter() - .map(|p| p.y) - .collect::>() - .iter() - .fold(f64::INFINITY, |a, &b| a.min(b)); - let min_z = vertices - .iter() - .map(|p| p.z) - .collect::>() - .iter() - .fold(f64::INFINITY, |a, &b| a.min(b)); - - Point3::new(min_x, min_y, min_z) - }*/ + fn apply_transform(&mut self, m: &Isometry3) { + self.building.iter_mut().for_each(|x| x.apply_transform(m)); + self.city_furniture + .iter_mut() + .for_each(|x| x.apply_transform(m)); + self.road.iter_mut().for_each(|x| x.apply_transform(m)); + self.solitary_vegetation_object + .iter_mut() + .for_each(|x| x.apply_transform(m)); + } } diff --git a/crates/ecitygml-core/src/model/construction.rs b/crates/ecitygml-core/src/model/construction.rs index efaff0d..c56940e 100644 --- a/crates/ecitygml-core/src/model/construction.rs +++ b/crates/ecitygml-core/src/model/construction.rs @@ -1,36 +1,162 @@ -use egml::{base, geometry}; +use crate::model::core::{OccupiedSpace, ThematicSurface}; +use crate::operations::{FeatureWithGeometry, Visitable, Visitor}; +use egml::model::geometry::Envelope; +use nalgebra::Isometry3; #[derive(Debug, Clone, PartialEq)] pub struct WallSurface { - id: base::Id, - name: String, - lod2_multi_surface: Option, - lod3_multi_surface: Option, + pub thematic_surface: ThematicSurface, + pub door_surface: Vec, + pub window_surface: Vec, } impl WallSurface { - pub fn new(id: base::Id, name: String) -> Self { + pub fn new(thematic_surface: ThematicSurface) -> Self { Self { - id, - name, - lod2_multi_surface: None, - lod3_multi_surface: None, + thematic_surface, + door_surface: Vec::new(), + window_surface: Vec::new(), } } +} + +impl Visitable for WallSurface { + fn accept(&self, visitor: &mut V) { + visitor.visit_wall_surface(self); + self.thematic_surface.accept(visitor); + self.door_surface.iter().for_each(|x| x.accept(visitor)); + self.window_surface.iter().for_each(|x| x.accept(visitor)); + } +} - pub fn lod2_multi_surface(&self) -> &Option { - &self.lod2_multi_surface +impl FeatureWithGeometry for WallSurface { + fn envelope(&self) -> Option { + let mut envelopes: Vec> = vec![self.thematic_surface.envelope()]; + envelopes.extend(self.door_surface.iter().map(|x| x.envelope())); + envelopes.extend(self.window_surface.iter().map(|x| x.envelope())); + + Envelope::from_optional_envelopes(&envelopes).expect("should work") } - pub fn set_lod2_multi_surface(&mut self, lod2_multi_surface: Option) { - self.lod2_multi_surface = lod2_multi_surface; + fn apply_transform(&mut self, m: &Isometry3) { + self.thematic_surface.apply_transform(m); + self.door_surface + .iter_mut() + .for_each(|x| x.apply_transform(m)); + self.window_surface + .iter_mut() + .for_each(|x| x.apply_transform(m)); } +} + +#[derive(Debug, Clone, PartialEq)] +pub struct RoofSurface { + pub thematic_surface: ThematicSurface, +} + +impl RoofSurface { + pub fn new(thematic_surface: ThematicSurface) -> Self { + Self { thematic_surface } + } +} + +impl Visitable for RoofSurface { + fn accept(&self, visitor: &mut V) { + visitor.visit_roof_surface(self); + self.thematic_surface.accept(visitor); + } +} + +impl FeatureWithGeometry for RoofSurface { + fn envelope(&self) -> Option { + self.thematic_surface.envelope() + } + + fn apply_transform(&mut self, m: &Isometry3) { + self.thematic_surface.apply_transform(m); + } +} + +#[derive(Debug, Clone, PartialEq)] +pub struct GroundSurface { + pub thematic_surface: ThematicSurface, +} + +impl GroundSurface { + pub fn new(thematic_surface: ThematicSurface) -> Self { + Self { thematic_surface } + } +} + +impl Visitable for GroundSurface { + fn accept(&self, visitor: &mut V) { + visitor.visit_ground_surface(self); + self.thematic_surface.accept(visitor); + } +} + +impl FeatureWithGeometry for GroundSurface { + fn envelope(&self) -> Option { + self.thematic_surface.envelope() + } + + fn apply_transform(&mut self, m: &Isometry3) { + self.thematic_surface.apply_transform(m); + } +} + +#[derive(Debug, Clone, PartialEq)] +pub struct WindowSurface { + pub occupied_space: OccupiedSpace, +} + +impl WindowSurface { + pub fn new(occupied_space: OccupiedSpace) -> Self { + Self { occupied_space } + } +} + +impl Visitable for WindowSurface { + fn accept(&self, visitor: &mut V) { + visitor.visit_window_surface(self); + self.occupied_space.accept(visitor); + } +} + +impl FeatureWithGeometry for WindowSurface { + fn envelope(&self) -> Option { + self.occupied_space.envelope() + } + + fn apply_transform(&mut self, m: &Isometry3) { + self.occupied_space.apply_transform(m); + } +} + +#[derive(Debug, Clone, PartialEq)] +pub struct DoorSurface { + pub occupied_space: OccupiedSpace, +} + +impl DoorSurface { + pub fn new(occupied_space: OccupiedSpace) -> Self { + Self { occupied_space } + } +} + +impl Visitable for DoorSurface { + fn accept(&self, visitor: &mut V) { + visitor.visit_door_surface(self); + self.occupied_space.accept(visitor); + } +} - pub fn lod3_multi_surface(&self) -> &Option { - &self.lod3_multi_surface +impl FeatureWithGeometry for DoorSurface { + fn envelope(&self) -> Option { + self.occupied_space.envelope() } - pub fn set_lod3_multi_surface(&mut self, lod3_multi_surface: Option) { - self.lod3_multi_surface = lod3_multi_surface; + fn apply_transform(&mut self, m: &Isometry3) { + self.occupied_space.apply_transform(m); } } diff --git a/crates/ecitygml-core/src/model/core.rs b/crates/ecitygml-core/src/model/core.rs index e402f16..a74ad09 100644 --- a/crates/ecitygml-core/src/model/core.rs +++ b/crates/ecitygml-core/src/model/core.rs @@ -1,7 +1,272 @@ -use egml::geometry::LinearRing; +use crate::operations::{FeatureWithGeometry, Visitable, Visitor}; +use egml::model::base::Gml; +use egml::model::geometry; +use egml::model::geometry::{DirectPosition, Envelope}; +use egml::operations::geometry::Geometry; +use nalgebra::Isometry3; -#[derive(Debug, Clone, PartialEq, Default)] +#[derive(Debug, Clone, PartialEq)] pub struct CityObject { - pub name: String, - pub geometries: Vec, + pub gml: Gml, + // TODO: generic_attributes: Vec, +} + +impl CityObject { + pub fn new(gml: Gml) -> Self { + Self { gml } + } +} + +#[derive(Debug, Clone, PartialEq, Default)] +pub struct ImplicitGeometry { + pub reference_point: geometry::DirectPosition, +} + +impl ImplicitGeometry { + pub fn new(reference_point: geometry::DirectPosition) -> Self { + Self { reference_point } + } +} + +impl Visitable for ImplicitGeometry { + fn accept(&self, visitor: &mut V) { + visitor.visit_implicit_geometry(self); + } +} + +impl Geometry for ImplicitGeometry { + fn points(&self) -> Vec<&DirectPosition> { + vec![&self.reference_point] + } + + fn apply_transform(&mut self, m: &Isometry3) { + self.reference_point.apply_transform(m); + } +} + +#[derive(Debug, Clone, PartialEq)] +pub struct Space { + pub city_object: CityObject, + + pub lod1_solid: Option, + pub lod2_solid: Option, + pub lod3_solid: Option, + + pub lod0_multi_surface: Option, + pub lod2_multi_surface: Option, + pub lod3_multi_surface: Option, +} + +impl Space { + pub fn new(city_object: CityObject) -> Self { + Self { + city_object, + lod1_solid: None, + lod2_solid: None, + lod3_solid: None, + lod0_multi_surface: None, + lod2_multi_surface: None, + lod3_multi_surface: None, + } + } +} + +impl Visitable for Space { + fn accept(&self, visitor: &mut V) { + visitor.visit_space(self); + + if let Some(g) = &self.lod1_solid { + g.accept(visitor); + } + if let Some(g) = &self.lod2_solid { + g.accept(visitor); + } + if let Some(g) = &self.lod3_solid { + g.accept(visitor); + } + if let Some(g) = &self.lod0_multi_surface { + g.accept(visitor); + } + if let Some(g) = &self.lod2_multi_surface { + g.accept(visitor); + } + if let Some(g) = &self.lod3_multi_surface { + g.accept(visitor); + } + } +} + +impl FeatureWithGeometry for Space { + fn envelope(&self) -> Option { + let envelopes: Vec> = vec![ + self.lod1_solid.as_ref().map(|x| x.envelope()), + self.lod2_solid.as_ref().map(|x| x.envelope()), + self.lod3_solid.as_ref().map(|x| x.envelope()), + self.lod0_multi_surface.as_ref().map(|x| x.envelope()), + self.lod2_multi_surface.as_ref().map(|x| x.envelope()), + self.lod3_multi_surface.as_ref().map(|x| x.envelope()), + ]; + + Envelope::from_optional_envelopes(&envelopes).expect("should work") + } + + fn apply_transform(&mut self, m: &Isometry3) { + if let Some(g) = &mut self.lod0_multi_surface { + g.apply_transform(m); + } + if let Some(g) = &mut self.lod1_solid { + g.apply_transform(m); + } + if let Some(g) = &mut self.lod2_solid { + g.apply_transform(m); + } + if let Some(g) = &mut self.lod3_solid { + g.apply_transform(m); + } + + if let Some(g) = &mut self.lod0_multi_surface { + g.apply_transform(m); + } + if let Some(g) = &mut self.lod2_multi_surface { + g.apply_transform(m); + } + if let Some(g) = &mut self.lod3_multi_surface { + g.apply_transform(m); + } + } +} + +#[derive(Debug, Clone, PartialEq)] +pub struct OccupiedSpace { + pub space: Space, + pub lod1_implicit_representation: Option, + pub lod2_implicit_representation: Option, + pub lod3_implicit_representation: Option, +} + +impl OccupiedSpace { + pub fn new(space: Space) -> Self { + Self { + space, + lod1_implicit_representation: None, + lod2_implicit_representation: None, + lod3_implicit_representation: None, + } + } +} + +impl Visitable for OccupiedSpace { + fn accept(&self, visitor: &mut V) { + visitor.visit_occupied_space(self); + self.space.accept(visitor); + if let Some(g) = &self.lod1_implicit_representation { + g.accept(visitor); + } + if let Some(g) = &self.lod2_implicit_representation { + g.accept(visitor); + } + if let Some(g) = &self.lod3_implicit_representation { + g.accept(visitor); + } + } +} + +impl FeatureWithGeometry for OccupiedSpace { + fn envelope(&self) -> Option { + let envelopes: Vec> = vec![ + self.space.envelope(), + self.lod1_implicit_representation + .as_ref() + .map(|x| x.envelope()), + self.lod2_implicit_representation + .as_ref() + .map(|x| x.envelope()), + self.lod3_implicit_representation + .as_ref() + .map(|x| x.envelope()), + ]; + + Envelope::from_optional_envelopes(&envelopes).expect("should work") + } + + fn apply_transform(&mut self, m: &Isometry3) { + self.space.apply_transform(m); + + if let Some(g) = &mut self.lod1_implicit_representation { + g.apply_transform(m); + } + if let Some(g) = &mut self.lod2_implicit_representation { + g.apply_transform(m); + } + if let Some(g) = &mut self.lod3_implicit_representation { + g.apply_transform(m); + } + } +} + +#[derive(Debug, Clone, PartialEq)] +pub struct ThematicSurface { + pub city_object: CityObject, + pub lod0_multi_surface: Option, + pub lod1_multi_surface: Option, + pub lod2_multi_surface: Option, + pub lod3_multi_surface: Option, +} + +impl ThematicSurface { + pub fn new(city_object: CityObject) -> Self { + Self { + city_object, + lod0_multi_surface: None, + lod1_multi_surface: None, + lod2_multi_surface: None, + lod3_multi_surface: None, + } + } +} + +impl Visitable for ThematicSurface { + fn accept(&self, visitor: &mut V) { + visitor.visit_thematic_surface(self); + if let Some(g) = &self.lod0_multi_surface { + g.accept(visitor); + } + if let Some(g) = &self.lod1_multi_surface { + g.accept(visitor); + } + if let Some(g) = &self.lod2_multi_surface { + g.accept(visitor); + } + if let Some(g) = &self.lod3_multi_surface { + g.accept(visitor); + } + } +} + +impl FeatureWithGeometry for ThematicSurface { + fn envelope(&self) -> Option { + let envelopes: Vec> = vec![ + self.lod0_multi_surface.as_ref().map(|x| x.envelope()), + self.lod1_multi_surface.as_ref().map(|x| x.envelope()), + self.lod2_multi_surface.as_ref().map(|x| x.envelope()), + self.lod3_multi_surface.as_ref().map(|x| x.envelope()), + ]; + + Envelope::from_optional_envelopes(&envelopes).expect("should work") + } + + fn apply_transform(&mut self, m: &Isometry3) { + if let Some(g) = &mut self.lod0_multi_surface { + g.apply_transform(m); + } + if let Some(g) = &mut self.lod1_multi_surface { + g.apply_transform(m); + } + if let Some(g) = &mut self.lod2_multi_surface { + g.apply_transform(m); + } + if let Some(g) = &mut self.lod3_multi_surface { + g.apply_transform(m); + } + } } diff --git a/crates/ecitygml-core/src/model/mod.rs b/crates/ecitygml-core/src/model/mod.rs index 4e73457..bbd1938 100644 --- a/crates/ecitygml-core/src/model/mod.rs +++ b/crates/ecitygml-core/src/model/mod.rs @@ -1,6 +1,7 @@ +pub mod building; pub mod city_furniture; pub mod city_model; pub mod construction; -mod core; +pub mod core; pub mod solitary_vegetation_object; pub mod transportation; diff --git a/crates/ecitygml-core/src/model/solitary_vegetation_object.rs b/crates/ecitygml-core/src/model/solitary_vegetation_object.rs index 0df5e63..d5c6746 100644 --- a/crates/ecitygml-core/src/model/solitary_vegetation_object.rs +++ b/crates/ecitygml-core/src/model/solitary_vegetation_object.rs @@ -1,26 +1,32 @@ -use egml::{base, geometry}; +use crate::model::core::OccupiedSpace; +use crate::operations::{FeatureWithGeometry, Visitable, Visitor}; +use egml::model::geometry::Envelope; +use nalgebra::Isometry3; #[derive(Debug, Clone, PartialEq)] pub struct SolitaryVegetationObject { - id: base::Id, - name: String, - lod1_solid: Option, + pub occupied_space: OccupiedSpace, } impl SolitaryVegetationObject { - pub fn new(id: base::Id, name: String) -> Self { - Self { - id, - name, - lod1_solid: None, - } + pub fn new(occupied_space: OccupiedSpace) -> Self { + Self { occupied_space } } +} + +impl Visitable for SolitaryVegetationObject { + fn accept(&self, visitor: &mut V) { + visitor.visit_solitary_vegetation_object(self); + self.occupied_space.accept(visitor); + } +} - pub fn lod1_solid(&self) -> &Option { - &self.lod1_solid +impl FeatureWithGeometry for SolitaryVegetationObject { + fn envelope(&self) -> Option { + self.occupied_space.envelope() } - pub fn set_lod1_solid(&mut self, lod1_solid: Option) { - self.lod1_solid = lod1_solid; + fn apply_transform(&mut self, m: &Isometry3) { + self.occupied_space.apply_transform(m); } } diff --git a/crates/ecitygml-core/src/model/transportation.rs b/crates/ecitygml-core/src/model/transportation.rs index fb648b3..a0e9e2a 100644 --- a/crates/ecitygml-core/src/model/transportation.rs +++ b/crates/ecitygml-core/src/model/transportation.rs @@ -1,26 +1,285 @@ -use egml::{base, geometry}; +use crate::model::core::{CityObject, Space, ThematicSurface}; +use crate::operations::{FeatureWithGeometry, Visitable, Visitor}; +use egml::model::base; +use egml::model::base::Gml; +use egml::model::geometry::Envelope; +use nalgebra::Isometry3; #[derive(Debug, Clone, PartialEq)] -pub struct TrafficArea { - id: base::Id, - name: String, - lod2_multi_surface: Option, +pub struct Road { + pub city_object: CityObject, // TODO: space + pub section: Vec
, + pub intersection: Vec, } -impl TrafficArea { - pub fn new(id: base::Id, name: String) -> Self { +impl Road { + pub fn new(id: base::Id) -> Self { + let gml = Gml::new(id); + let city_object = CityObject::new(gml); + + Self { + city_object, + section: Default::default(), + intersection: Default::default(), + } + } +} + +impl Visitable for Road { + fn accept(&self, visitor: &mut V) { + visitor.visit_road(self); + self.section.iter().for_each(|x| x.accept(visitor)); + self.intersection.iter().for_each(|x| x.accept(visitor)); + } +} + +impl FeatureWithGeometry for Road { + fn envelope(&self) -> Option { + let mut envelopes: Vec> = vec![]; + envelopes.extend(self.section.iter().map(|x| x.envelope())); + envelopes.extend(self.intersection.iter().map(|x| x.envelope())); + + Envelope::from_optional_envelopes(&envelopes).expect("should work") + } + + fn apply_transform(&mut self, m: &Isometry3) { + self.section.iter_mut().for_each(|x| x.apply_transform(m)); + self.intersection + .iter_mut() + .for_each(|x| x.apply_transform(m)); + } +} + +#[derive(Debug, Clone, PartialEq)] +pub struct Section { + pub city_object: CityObject, // TODO: space + pub traffic_space: Vec, + pub auxiliary_traffic_space: Vec, +} + +impl Section { + pub fn new(id: base::Id) -> Self { + let gml = Gml::new(id); + let city_object = CityObject::new(gml); + + Self { + city_object, + traffic_space: Vec::new(), + auxiliary_traffic_space: Vec::new(), + } + } +} + +impl Visitable for Section { + fn accept(&self, visitor: &mut V) { + visitor.visit_section(self); + self.traffic_space.iter().for_each(|x| x.accept(visitor)); + self.auxiliary_traffic_space + .iter() + .for_each(|x| x.accept(visitor)); + } +} + +impl FeatureWithGeometry for Section { + fn envelope(&self) -> Option { + let mut envelopes: Vec> = vec![]; + envelopes.extend(self.traffic_space.iter().map(|x| x.envelope())); + envelopes.extend(self.auxiliary_traffic_space.iter().map(|x| x.envelope())); + + Envelope::from_optional_envelopes(&envelopes).expect("should work") + } + + fn apply_transform(&mut self, m: &Isometry3) { + self.traffic_space + .iter_mut() + .for_each(|x| x.apply_transform(m)); + self.auxiliary_traffic_space + .iter_mut() + .for_each(|x| x.apply_transform(m)); + } +} + +#[derive(Debug, Clone, PartialEq)] +pub struct Intersection { + pub city_object: CityObject, // TODO: space + pub traffic_space: Vec, + pub auxiliary_traffic_space: Vec, +} + +impl Intersection { + pub fn new(id: base::Id) -> Self { + let gml = Gml::new(id); + let city_object = CityObject::new(gml); + + Self { + city_object, + traffic_space: Vec::new(), + auxiliary_traffic_space: Vec::new(), + } + } +} + +impl Visitable for Intersection { + fn accept(&self, visitor: &mut V) { + visitor.visit_intersection(self); + self.traffic_space.iter().for_each(|x| x.accept(visitor)); + self.auxiliary_traffic_space + .iter() + .for_each(|x| x.accept(visitor)); + } +} + +impl FeatureWithGeometry for Intersection { + fn envelope(&self) -> Option { + let mut envelopes: Vec> = vec![]; + envelopes.extend(self.traffic_space.iter().map(|x| x.envelope())); + envelopes.extend(self.auxiliary_traffic_space.iter().map(|x| x.envelope())); + + Envelope::from_optional_envelopes(&envelopes).expect("should work") + } + + fn apply_transform(&mut self, m: &Isometry3) { + self.traffic_space + .iter_mut() + .for_each(|x| x.apply_transform(m)); + self.auxiliary_traffic_space + .iter_mut() + .for_each(|x| x.apply_transform(m)); + } +} + +#[derive(Debug, Clone, PartialEq)] +pub struct TrafficSpace { + pub space: Space, + pub traffic_area: Vec, // this should be located in boundaries the space struct +} + +impl TrafficSpace { + pub fn new(space: Space) -> Self { Self { - id, - name, - lod2_multi_surface: None, + space, + traffic_area: Vec::new(), } } +} + +impl Visitable for TrafficSpace { + fn accept(&self, visitor: &mut V) { + visitor.visit_traffic_space(self); + self.space.accept(visitor); + self.traffic_area.iter().for_each(|x| x.accept(visitor)); + } +} + +impl FeatureWithGeometry for TrafficSpace { + fn envelope(&self) -> Option { + let mut envelopes: Vec> = vec![self.space.envelope()]; + envelopes.extend(self.traffic_area.iter().map(|x| x.envelope())); + + Envelope::from_optional_envelopes(&envelopes).expect("should work") + } + + fn apply_transform(&mut self, m: &Isometry3) { + self.space.apply_transform(m); + self.traffic_area + .iter_mut() + .for_each(|x| x.apply_transform(m)); + } +} + +#[derive(Debug, Clone, PartialEq)] +pub struct AuxiliaryTrafficSpace { + pub space: Space, + pub auxiliary_traffic_area: Vec, // this should be located in boundaries the space struct +} + +impl AuxiliaryTrafficSpace { + pub fn new(space: Space) -> Self { + Self { + space, + auxiliary_traffic_area: Vec::new(), + } + } +} + +impl Visitable for AuxiliaryTrafficSpace { + fn accept(&self, visitor: &mut V) { + visitor.visit_auxiliary_traffic_space(self); + self.space.accept(visitor); + self.auxiliary_traffic_area + .iter() + .for_each(|x| x.accept(visitor)); + } +} + +impl FeatureWithGeometry for AuxiliaryTrafficSpace { + fn envelope(&self) -> Option { + let mut envelopes: Vec> = vec![self.space.envelope()]; + envelopes.extend(self.auxiliary_traffic_area.iter().map(|x| x.envelope())); + + Envelope::from_optional_envelopes(&envelopes).expect("should work") + } + + fn apply_transform(&mut self, m: &Isometry3) { + self.space.apply_transform(m); + self.auxiliary_traffic_area + .iter_mut() + .for_each(|x| x.apply_transform(m)); + } +} + +#[derive(Debug, Clone, PartialEq)] +pub struct TrafficArea { + pub thematic_surface: ThematicSurface, +} + +impl TrafficArea { + pub fn new(thematic_surface: ThematicSurface) -> Self { + Self { thematic_surface } + } +} + +impl Visitable for TrafficArea { + fn accept(&self, visitor: &mut V) { + visitor.visit_traffic_area(self); + self.thematic_surface.accept(visitor); + } +} + +impl FeatureWithGeometry for TrafficArea { + fn envelope(&self) -> Option { + self.thematic_surface.envelope() + } + + fn apply_transform(&mut self, m: &Isometry3) { + self.thematic_surface.apply_transform(m); + } +} + +#[derive(Debug, Clone, PartialEq)] +pub struct AuxiliaryTrafficArea { + pub thematic_surface: ThematicSurface, +} + +impl AuxiliaryTrafficArea { + pub fn new(thematic_surface: ThematicSurface) -> Self { + Self { thematic_surface } + } +} + +impl Visitable for AuxiliaryTrafficArea { + fn accept(&self, visitor: &mut V) { + visitor.visit_auxiliary_traffic_area(self); + self.thematic_surface.accept(visitor); + } +} - pub fn lod2_multi_surface(&self) -> &Option { - &self.lod2_multi_surface +impl FeatureWithGeometry for AuxiliaryTrafficArea { + fn envelope(&self) -> Option { + self.thematic_surface.envelope() } - pub fn set_lod2_multi_surface(&mut self, lod2_multi_surface: Option) { - self.lod2_multi_surface = lod2_multi_surface; + fn apply_transform(&mut self, m: &Isometry3) { + self.thematic_surface.apply_transform(m); } } diff --git a/crates/ecitygml-core/src/operations/geometry_collector.rs b/crates/ecitygml-core/src/operations/geometry_collector.rs new file mode 100644 index 0000000..9a0438f --- /dev/null +++ b/crates/ecitygml-core/src/operations/geometry_collector.rs @@ -0,0 +1,87 @@ +use crate::model::building::Building; +use crate::model::city_furniture::CityFurniture; +use crate::model::city_model::CitygmlModel; +use crate::model::construction::{ + DoorSurface, GroundSurface, RoofSurface, WallSurface, WindowSurface, +}; +use crate::model::core::{ImplicitGeometry, OccupiedSpace, Space, ThematicSurface}; +use crate::model::solitary_vegetation_object::SolitaryVegetationObject; +use crate::model::transportation::{ + AuxiliaryTrafficArea, AuxiliaryTrafficSpace, Intersection, Road, Section, TrafficArea, + TrafficSpace, +}; +use crate::operations::Visitor; +use egml::model::geometry::{DirectPosition, LinearRing, MultiSurface, Polygon, Solid}; + +#[derive(Debug, Clone, PartialEq, Default)] +pub struct GeometryCollector { + pub multi_surface: Vec, + pub solid: Vec, +} + +impl GeometryCollector { + pub fn new() -> Self { + Self { + multi_surface: Vec::new(), + solid: Vec::new(), + } + } +} + +impl Visitor for GeometryCollector { + type Result = (); + + fn visit_direct_position(&mut self, v: &DirectPosition) -> Self::Result {} + + fn visit_linear_ring(&mut self, v: &LinearRing) -> Self::Result {} + + fn visit_polygon(&mut self, v: &Polygon) -> Self::Result {} + + fn visit_multi_surface(&mut self, v: &MultiSurface) -> Self::Result { + self.multi_surface.push(v.clone()); + } + + fn visit_solid(&mut self, v: &Solid) -> Self::Result { + self.solid.push(v.clone()); + } + + fn visit_implicit_geometry(&mut self, v: &ImplicitGeometry) -> Self::Result {} + + fn visit_thematic_surface(&mut self, v: &ThematicSurface) -> Self::Result {} + + fn visit_space(&mut self, v: &Space) -> Self::Result {} + + fn visit_occupied_space(&mut self, v: &OccupiedSpace) -> Self::Result {} + + fn visit_city_model(&mut self, v: &CitygmlModel) -> Self::Result {} + + fn visit_city_furniture(&mut self, v: &CityFurniture) -> Self::Result {} + + fn visit_building(&mut self, v: &Building) -> Self::Result {} + + fn visit_roof_surface(&mut self, v: &RoofSurface) -> Self::Result {} + + fn visit_ground_surface(&mut self, v: &GroundSurface) -> Self::Result {} + + fn visit_wall_surface(&mut self, v: &WallSurface) -> Self::Result {} + + fn visit_window_surface(&mut self, v: &WindowSurface) -> Self::Result {} + + fn visit_door_surface(&mut self, v: &DoorSurface) -> Self::Result {} + + fn visit_solitary_vegetation_object(&mut self, v: &SolitaryVegetationObject) -> Self::Result {} + + fn visit_road(&mut self, v: &Road) -> Self::Result {} + + fn visit_section(&mut self, v: &Section) -> Self::Result {} + + fn visit_intersection(&mut self, v: &Intersection) -> Self::Result {} + + fn visit_traffic_space(&mut self, v: &TrafficSpace) -> Self::Result {} + + fn visit_auxiliary_traffic_space(&mut self, v: &AuxiliaryTrafficSpace) -> Self::Result {} + + fn visit_traffic_area(&mut self, v: &TrafficArea) -> Self::Result {} + + fn visit_auxiliary_traffic_area(&mut self, v: &AuxiliaryTrafficArea) -> Self::Result {} +} diff --git a/crates/ecitygml-core/src/operations/mod.rs b/crates/ecitygml-core/src/operations/mod.rs new file mode 100644 index 0000000..95202c2 --- /dev/null +++ b/crates/ecitygml-core/src/operations/mod.rs @@ -0,0 +1,28 @@ +mod geometry_collector; +mod traits; +mod visitor; + +#[doc(inline)] +pub use visitor::Interpreter; + +#[doc(inline)] +pub use visitor::Visitor; + +#[doc(inline)] +pub use visitor::Visitable; + +#[doc(inline)] +pub use traits::FeatureWithGeometry; + +#[doc(inline)] +pub use geometry_collector::GeometryCollector; + +/*#[doc(inline)] +pub use traits::SpaceFeature; + +#[doc(inline)] +pub use traits::GeometryId; + +#[doc(inline)] +pub use traits::GeometryCollection; +*/ diff --git a/crates/ecitygml-core/src/operations/traits.rs b/crates/ecitygml-core/src/operations/traits.rs new file mode 100644 index 0000000..71107ef --- /dev/null +++ b/crates/ecitygml-core/src/operations/traits.rs @@ -0,0 +1,26 @@ +use egml::model::geometry::Envelope; +use nalgebra::Isometry3; + +pub trait FeatureWithGeometry { + fn envelope(&self) -> Option; + + fn apply_transform(&mut self, m: &Isometry3); +} +/* +pub trait SpaceFeature { + fn collect_multi_surfaces(&self) -> HashMap; + fn collect_solids(&self) -> HashMap; +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct GeometryId { + feature_id: Id, + geometry_id: Id, +} + +#[derive(Debug, Clone, PartialEq, Default)] +pub struct GeometryCollection<'a> { + pub multi_surface: HashMap, + pub solid: HashMap, +} +impl GeometryCollection<'_> {}*/ diff --git a/crates/ecitygml-core/src/operations/visitor.rs b/crates/ecitygml-core/src/operations/visitor.rs new file mode 100644 index 0000000..af3b9d0 --- /dev/null +++ b/crates/ecitygml-core/src/operations/visitor.rs @@ -0,0 +1,236 @@ +use crate::model::building::Building; +use crate::model::city_furniture::CityFurniture; +use crate::model::city_model::CitygmlModel; +use crate::model::construction::{ + DoorSurface, GroundSurface, RoofSurface, WallSurface, WindowSurface, +}; +use crate::model::core::{ImplicitGeometry, OccupiedSpace, Space, ThematicSurface}; +use crate::model::solitary_vegetation_object::SolitaryVegetationObject; +use crate::model::transportation::{ + AuxiliaryTrafficArea, AuxiliaryTrafficSpace, Intersection, Road, Section, TrafficArea, + TrafficSpace, +}; +use egml::model::geometry::{DirectPosition, LinearRing, MultiSurface, Polygon, Solid}; +use egml::operations::geometry::Geometry; + +pub trait Visitable { + fn accept(&self, visitor: &mut V); +} + +pub trait Visitor { + type Result; + + fn visit_direct_position(&mut self, v: &DirectPosition) -> Self::Result; + fn visit_linear_ring(&mut self, v: &LinearRing) -> Self::Result; + fn visit_polygon(&mut self, v: &Polygon) -> Self::Result; + fn visit_multi_surface(&mut self, v: &MultiSurface) -> Self::Result; + fn visit_solid(&mut self, v: &Solid) -> Self::Result; + + fn visit_implicit_geometry(&mut self, v: &ImplicitGeometry) -> Self::Result; + fn visit_thematic_surface(&mut self, v: &ThematicSurface) -> Self::Result; + fn visit_space(&mut self, v: &Space) -> Self::Result; + fn visit_occupied_space(&mut self, v: &OccupiedSpace) -> Self::Result; + + fn visit_city_model(&mut self, v: &CitygmlModel) -> Self::Result; + + fn visit_city_furniture(&mut self, v: &CityFurniture) -> Self::Result; + + fn visit_building(&mut self, v: &Building) -> Self::Result; + fn visit_roof_surface(&mut self, v: &RoofSurface) -> Self::Result; + fn visit_ground_surface(&mut self, v: &GroundSurface) -> Self::Result; + fn visit_wall_surface(&mut self, v: &WallSurface) -> Self::Result; + fn visit_window_surface(&mut self, v: &WindowSurface) -> Self::Result; + fn visit_door_surface(&mut self, v: &DoorSurface) -> Self::Result; + + fn visit_solitary_vegetation_object(&mut self, v: &SolitaryVegetationObject) -> Self::Result; + + fn visit_road(&mut self, v: &Road) -> Self::Result; + fn visit_section(&mut self, v: &Section) -> Self::Result; + fn visit_intersection(&mut self, v: &Intersection) -> Self::Result; + fn visit_traffic_space(&mut self, v: &TrafficSpace) -> Self::Result; + fn visit_auxiliary_traffic_space(&mut self, v: &AuxiliaryTrafficSpace) -> Self::Result; + fn visit_traffic_area(&mut self, v: &TrafficArea) -> Self::Result; + fn visit_auxiliary_traffic_area(&mut self, v: &AuxiliaryTrafficArea) -> Self::Result; +} + +pub struct Interpreter; + +impl Default for Interpreter { + fn default() -> Self { + Self::new() + } +} + +impl Interpreter { + pub fn new() -> Self { + Self {} + } +} +impl Visitor for Interpreter { + type Result = (); + + fn visit_direct_position(&mut self, v: &DirectPosition) -> Self::Result { + println!("hello direct_position"); + } + + fn visit_linear_ring(&mut self, v: &LinearRing) -> Self::Result { + println!("hello linear_ring {}", v.gml.id); + } + + fn visit_polygon(&mut self, v: &Polygon) -> Self::Result { + println!("hello polygon {}", v.gml.id); + } + + fn visit_multi_surface(&mut self, v: &MultiSurface) -> Self::Result { + println!("hello multi_surface {}", v.gml.id); + } + + fn visit_solid(&mut self, v: &Solid) -> Self::Result { + println!("hello solid {}", v.gml.id); + } + + fn visit_implicit_geometry(&mut self, v: &ImplicitGeometry) -> Self::Result { + println!("hello implicit_geometry",); + } + + fn visit_thematic_surface(&mut self, v: &ThematicSurface) -> Self::Result { + println!("hello thematic_surface {}", v.city_object.gml.id); + } + + fn visit_space(&mut self, v: &Space) -> Self::Result { + println!("hello space {}", v.city_object.gml.id); + } + + fn visit_occupied_space(&mut self, v: &OccupiedSpace) -> Self::Result { + println!("hello occupied_space {}", v.space.city_object.gml.id); + } + + fn visit_city_model(&mut self, v: &CitygmlModel) -> Self::Result { + println!("hello city_model"); + } + + fn visit_city_furniture(&mut self, v: &CityFurniture) -> Self::Result { + println!( + "hello city_furniture {}", + v.occupied_space.space.city_object.gml.id + ); + } + + fn visit_building(&mut self, v: &Building) -> Self::Result { + println!("hello building {}", v.city_object.gml.id); + } + + fn visit_roof_surface(&mut self, v: &RoofSurface) -> Self::Result { + println!( + "hello roof_surface {}", + v.thematic_surface.city_object.gml.id + ); + } + + fn visit_ground_surface(&mut self, v: &GroundSurface) -> Self::Result { + println!( + "hello ground_surface {}", + v.thematic_surface.city_object.gml.id + ); + } + + fn visit_wall_surface(&mut self, v: &WallSurface) -> Self::Result { + println!( + "hello wall_surface {}", + v.thematic_surface.city_object.gml.id + ); + } + + fn visit_window_surface(&mut self, v: &WindowSurface) -> Self::Result { + println!( + "hello window_surface {}", + v.occupied_space.space.city_object.gml.id + ); + } + + fn visit_door_surface(&mut self, v: &DoorSurface) -> Self::Result { + println!( + "hello door_surface {}", + v.occupied_space.space.city_object.gml.id + ); + } + + fn visit_solitary_vegetation_object(&mut self, v: &SolitaryVegetationObject) -> Self::Result { + println!( + "hello solitary_vegetation_object {}", + v.occupied_space.space.city_object.gml.id + ); + } + + fn visit_road(&mut self, v: &Road) -> Self::Result { + println!("hello road {}", v.city_object.gml.id); + } + + fn visit_section(&mut self, v: &Section) -> Self::Result { + println!("hello section {}", v.city_object.gml.id); + } + + fn visit_intersection(&mut self, v: &Intersection) -> Self::Result { + println!("hello intersection {}", v.city_object.gml.id); + } + + fn visit_traffic_space(&mut self, v: &TrafficSpace) -> Self::Result { + println!("hello traffic_space {}", v.space.city_object.gml.id); + } + + fn visit_auxiliary_traffic_space(&mut self, v: &AuxiliaryTrafficSpace) -> Self::Result { + println!( + "hello auxiliary_traffic_space {}", + v.space.city_object.gml.id + ); + } + + fn visit_traffic_area(&mut self, v: &TrafficArea) -> Self::Result { + println!( + "hello traffic_area {}", + v.thematic_surface.city_object.gml.id + ); + } + + fn visit_auxiliary_traffic_area(&mut self, v: &AuxiliaryTrafficArea) -> Self::Result { + println!( + "hello auxiliary_traffic_area {}", + v.thematic_surface.city_object.gml.id + ); + } +} + +impl Visitable for DirectPosition { + fn accept(&self, visitor: &mut V) { + visitor.visit_direct_position(self); + } +} + +impl Visitable for LinearRing { + fn accept(&self, visitor: &mut V) { + visitor.visit_linear_ring(self); + self.points().iter().for_each(|x| x.accept(visitor)); + } +} + +impl Visitable for Polygon { + fn accept(&self, visitor: &mut V) { + visitor.visit_polygon(self); + visitor.visit_linear_ring(&self.exterior); + self.interior.iter().for_each(|x| x.accept(visitor)); + } +} + +impl Visitable for MultiSurface { + fn accept(&self, visitor: &mut V) { + visitor.visit_multi_surface(self); + self.surface_member().iter().for_each(|x| x.accept(visitor)); + } +} + +impl Visitable for Solid { + fn accept(&self, visitor: &mut V) { + visitor.visit_solid(self); + self.members().iter().for_each(|x| x.accept(visitor)); + } +} diff --git a/crates/ecitygml-core/src/util.rs b/crates/ecitygml-core/src/util.rs new file mode 100644 index 0000000..7548e2b --- /dev/null +++ b/crates/ecitygml-core/src/util.rs @@ -0,0 +1,7 @@ +#[derive(Debug, Copy, Hash, Eq, Clone, PartialEq)] +pub enum LevelOfDetail { + Zero, + One, + Two, + Three, +} diff --git a/crates/ecitygml-io/Cargo.toml b/crates/ecitygml-io/Cargo.toml index 2de5a0c..f1d07b5 100644 --- a/crates/ecitygml-io/Cargo.toml +++ b/crates/ecitygml-io/Cargo.toml @@ -9,15 +9,16 @@ description = "IO operations for processing CityGML data." [dependencies] -ecitygml-core = { version = "0.0.1-alpha.3", path = "../ecitygml-core" } +ecitygml-core = { version = "0.0.1-alpha.4", path = "../ecitygml-core" } egml = { workspace = true } thiserror = { workspace = true } -serde = { workspace = true, features = [ "derive" ] } -quick-xml = { workspace = true, features = [ "serialize" ] } +serde = { workspace = true, features = ["derive"] } +quick-xml = { workspace = true, features = ["serialize"] } serde_yaml = { workspace = true } nalgebra = { workspace = true } # parry3d-f64 = { workspace = true, features = ["f64"] } itertools = { workspace = true } -log = "0.4.22" +uuid = { workspace = true, features = ["v4"] } +tracing = { workspace = true } diff --git a/crates/ecitygml-io/README.md b/crates/ecitygml-io/README.md index 0c6b53b..fa7bedc 100644 --- a/crates/ecitygml-io/README.md +++ b/crates/ecitygml-io/README.md @@ -2,8 +2,7 @@ IO operations for processing [CityGML](https://www.ogc.org/standard/citygml/) data. -> [!WARNING] -> The library is at an early stage of development. +The library is at an early stage of development. ## Contributing diff --git a/crates/ecitygml-io/src/lib.rs b/crates/ecitygml-io/src/lib.rs index e16c80d..a834e3b 100644 --- a/crates/ecitygml-io/src/lib.rs +++ b/crates/ecitygml-io/src/lib.rs @@ -1,5 +1,5 @@ mod error; -mod parse; +mod parser; mod read; mod read_impl; pub mod validate; diff --git a/crates/ecitygml-io/src/parse.rs b/crates/ecitygml-io/src/parse.rs deleted file mode 100644 index c93a067..0000000 --- a/crates/ecitygml-io/src/parse.rs +++ /dev/null @@ -1,107 +0,0 @@ -use crate::error::Error; - -use egml::geometry::{DirectPosition, MultiSurface, Solid}; -use log::warn; - -use quick_xml::events::Event; -use quick_xml::Reader; - -#[derive(Debug, Clone, PartialEq, Default)] -pub struct ParsedGeometries { - pub lod1_solid: Option, - pub lod2_solid: Option, - pub lod3_solid: Option, - pub lod0_multi_surface: Option, - pub lod1_multi_surface: Option, - pub lod2_multi_surface: Option, - pub lod3_multi_surface: Option, -} - -pub fn parse_geometries(xml_document: &String) -> Result { - let mut reader = Reader::from_str(xml_document.as_str()); - reader.config_mut().trim_text_start = true; - reader.config_mut().trim_text_end = true; - - let mut txt = Vec::new(); - let mut buf = Vec::new(); - - let mut parsed_geometries = ParsedGeometries::default(); - loop { - match reader.read_event_into(&mut buf) { - Ok(Event::Start(e)) => match e.name().as_ref() { - b"lod1Solid" => { - let xml_snippet: String = reader.read_text(e.name()).unwrap().to_string(); - parsed_geometries.lod1_solid = Some(egml::io::parse_solid(&xml_snippet)?); - } - b"lod2Solid" => { - let xml_snippet: String = reader.read_text(e.name()).unwrap().to_string(); - parsed_geometries.lod2_solid = Some(egml::io::parse_solid(&xml_snippet)?); - } - b"lod0MultiSurface" => { - let xml_snippet: String = reader.read_text(e.name()).unwrap().to_string(); - parsed_geometries.lod0_multi_surface = - Some(egml::io::parse_multi_surface(&xml_snippet)?); - } - b"lod1MultiSurface" => { - let xml_snippet: String = reader.read_text(e.name()).unwrap().to_string(); - parsed_geometries.lod1_multi_surface = - Some(egml::io::parse_multi_surface(&xml_snippet)?); - } - b"lod2MultiSurface" => { - let xml_snippet: String = reader.read_text(e.name()).unwrap().to_string(); - parsed_geometries.lod2_multi_surface = - Some(egml::io::parse_multi_surface(&xml_snippet)?); - } - b"lod3MultiSurface" => { - let xml_snippet: String = reader.read_text(e.name()).unwrap().to_string(); - match egml::io::parse_multi_surface(&xml_snippet) { - Ok(lod3_multi_surface) => { - parsed_geometries.lod3_multi_surface = Some(lod3_multi_surface); - } - Err(e) => { - warn!( - "lod3MultiSurface contains invalid geometry: {}", - e.to_string() - ) - } - } - } - _ => {} - }, - Ok(Event::Eof) => break, - Err(e) => panic!("Error at position {}: {:?}", reader.buffer_position(), e), - Ok(Event::Text(e)) => txt.push(e.unescape().unwrap().into_owned()), - _ => (), - } - } - - Ok(parsed_geometries) -} - -// TODO: also move into parse geometries fn -pub fn parse_reference_point(xml_document: &String) -> Result, Error> { - let mut reader = Reader::from_str(xml_document.as_str()); - reader.config_mut().trim_text_start = true; - reader.config_mut().trim_text_end = true; - - let mut txt = Vec::new(); - let mut buf = Vec::new(); - loop { - match reader.read_event_into(&mut buf) { - Ok(Event::Start(e)) => match e.name().as_ref() { - b"referencePoint" => { - let xml_snippet: String = reader.read_text(e.name()).unwrap().to_string(); - let direct_position = egml::io::parse_point(&xml_snippet)?; - return Ok(Some(direct_position)); - } - _ => {} - }, - Ok(Event::Eof) => break, - Err(e) => panic!("Error at position {}: {:?}", reader.buffer_position(), e), - Ok(Event::Text(e)) => txt.push(e.unescape().unwrap().into_owned()), - _ => (), - } - } - - Ok(None) -} diff --git a/crates/ecitygml-io/src/parser/attributes.rs b/crates/ecitygml-io/src/parser/attributes.rs new file mode 100644 index 0000000..4a1b994 --- /dev/null +++ b/crates/ecitygml-io/src/parser/attributes.rs @@ -0,0 +1,37 @@ +use quick_xml::events::BytesStart; +use quick_xml::Reader; +use std::collections::HashMap; + +pub fn extract_attributes(reader: &Reader<&[u8]>, e: &BytesStart) -> HashMap { + let extracted_attributes: HashMap = e + .attributes() + .map(|attr_res| match attr_res { + Ok(a) => { + let key = reader + .decoder() + .decode(a.key.local_name().as_ref()) + .unwrap() + .to_string(); + let value: String = a + .decode_and_unescape_value(reader.decoder()) + .unwrap() + .to_string(); + /*println!( + "key: {}, value: {}", + reader + .decoder() + .decode(a.key.local_name().as_ref()) + .unwrap(), + value + );*/ + (key, value) + } + Err(err) => { + dbg!("unable to read key in DefaultSettings, err = {:?}", err); + (String::new(), String::new()) + } + }) + .collect(); + + extracted_attributes +} diff --git a/crates/ecitygml-io/src/parser/building.rs b/crates/ecitygml-io/src/parser/building.rs new file mode 100644 index 0000000..2dab6a1 --- /dev/null +++ b/crates/ecitygml-io/src/parser/building.rs @@ -0,0 +1,118 @@ +use crate::parser::attributes::extract_attributes; +use crate::parser::space::{parse_occupied_space, parse_space, parse_thematic_surface}; +use crate::Error; +use ecitygml_core::model::building::Building; +use ecitygml_core::model::construction::{ + DoorSurface, GroundSurface, RoofSurface, WallSurface, WindowSurface, +}; +use egml::model::base::Id; +use quick_xml::events::Event; +use quick_xml::Reader; +use std::collections::HashMap; + +pub fn parse_building(id: &Id, xml_document: &String) -> Result { + let mut building = Building::new(id.clone()); + // todo: building.occupied_space = parse_occupied_space(&xml_document)?; + + let mut reader = Reader::from_str(xml_document.as_str()); + reader.config_mut().trim_text_start = true; + reader.config_mut().trim_text_end = true; + + let mut txt = Vec::new(); + let mut buf = Vec::new(); + + loop { + match reader.read_event_into(&mut buf) { + Ok(Event::Start(e)) => { + let extracted_attributes: HashMap = extract_attributes(&reader, &e); + let id: Option = extracted_attributes + .get("id") + .map(|x| Id::try_from(x.as_str()).ok()) + .flatten(); + + match e.name().as_ref() { + b"con:WallSurface" => { + let xml_snippet: String = reader.read_text(e.name()).unwrap().to_string(); + let id: Id = id.unwrap_or(Id::from_hashed_string(&xml_snippet)); + + let wall_surface = parse_wall_surface(&id, &xml_snippet)?; + building.wall_surface.push(wall_surface); + } + b"con:RoofSurface" => { + let xml_snippet: String = reader.read_text(e.name()).unwrap().to_string(); + let id: Id = id.unwrap_or(Id::from_hashed_string(&xml_snippet)); + + let thematic_surface = parse_thematic_surface(&id, &xml_snippet)?; + let roof_surface = RoofSurface::new(thematic_surface); + building.roof_surface.push(roof_surface); + } + b"con:GroundSurface" => { + let xml_snippet: String = reader.read_text(e.name()).unwrap().to_string(); + let id: Id = id.unwrap_or(Id::from_hashed_string(&xml_snippet)); + + let thematic_surface = parse_thematic_surface(&id, &xml_snippet)?; + let ground_surface = GroundSurface::new(thematic_surface); + building.ground_surface.push(ground_surface); + } + _ => {} + } + } + Ok(Event::Eof) => break, + Err(e) => panic!("Error at position {}: {:?}", reader.buffer_position(), e), + Ok(Event::Text(e)) => txt.push(e.unescape().unwrap().into_owned()), + _ => (), + } + } + + Ok(building) +} + +pub fn parse_wall_surface(id: &Id, xml_document: &String) -> Result { + let thematic_surface = parse_thematic_surface(&id, &xml_document)?; + let mut wall_surface = WallSurface::new(thematic_surface); + + let mut reader = Reader::from_str(xml_document.as_str()); + reader.config_mut().trim_text_start = true; + reader.config_mut().trim_text_end = true; + + let mut txt = Vec::new(); + let mut buf = Vec::new(); + + loop { + match reader.read_event_into(&mut buf) { + Ok(Event::Start(e)) => { + let extracted_attributes: HashMap = extract_attributes(&reader, &e); + let id: Option = extracted_attributes + .get("id") + .map(|x| Id::try_from(x.as_str()).ok()) + .flatten(); + + match e.name().as_ref() { + b"con:WindowSurface" => { + let xml_snippet: String = reader.read_text(e.name()).unwrap().to_string(); + let id: Id = id.unwrap_or(Id::from_hashed_string(&xml_snippet)); + + let occupied_space = parse_occupied_space(&id, &xml_snippet)?; + let window_surface = WindowSurface::new(occupied_space); + wall_surface.window_surface.push(window_surface); + } + b"con:DoorSurface" => { + let xml_snippet: String = reader.read_text(e.name()).unwrap().to_string(); + let id: Id = id.unwrap_or(Id::from_hashed_string(&xml_snippet)); + + let occupied_space = parse_occupied_space(&id, &xml_snippet)?; + let door_surface = DoorSurface::new(occupied_space); + wall_surface.door_surface.push(door_surface); + } + _ => {} + } + } + Ok(Event::Eof) => break, + Err(e) => panic!("Error at position {}: {:?}", reader.buffer_position(), e), + Ok(Event::Text(e)) => txt.push(e.unescape().unwrap().into_owned()), + _ => (), + } + } + + Ok(wall_surface) +} diff --git a/crates/ecitygml-io/src/parser/mod.rs b/crates/ecitygml-io/src/parser/mod.rs new file mode 100644 index 0000000..215cadd --- /dev/null +++ b/crates/ecitygml-io/src/parser/mod.rs @@ -0,0 +1,4 @@ +pub mod attributes; +pub mod building; +pub mod space; +pub mod transportation; diff --git a/crates/ecitygml-io/src/parser/space.rs b/crates/ecitygml-io/src/parser/space.rs new file mode 100644 index 0000000..1aa1e9b --- /dev/null +++ b/crates/ecitygml-io/src/parser/space.rs @@ -0,0 +1,279 @@ +use crate::error::Error; +use egml::io::{parse_multi_surface, parse_solid}; +use egml::model::base; +use egml::model::base::{Gml, Id}; + +use ecitygml_core::model::core::{ + CityObject, ImplicitGeometry, OccupiedSpace, Space, ThematicSurface, +}; +use quick_xml::events::Event; +use quick_xml::Reader; +use tracing::warn; + +pub fn parse_space(id: &Id, xml_document: &String) -> Result { + let mut gml = Gml::new(id.clone()); + gml.name = vec!["name".to_string()]; // TODO + let city_object = CityObject::new(gml); + let mut space = Space::new(city_object); + + let mut reader = Reader::from_str(xml_document.as_str()); + reader.config_mut().trim_text_start = true; + reader.config_mut().trim_text_end = true; + + let mut txt = Vec::new(); + let mut buf = Vec::new(); + + loop { + match reader.read_event_into(&mut buf) { + Ok(Event::Start(e)) => match e.name().as_ref() { + b"lod1Solid" => { + let xml_snippet: String = reader.read_text(e.name()).unwrap().to_string(); + space.lod1_solid = parse_solid(&xml_snippet) + .map_err(|e| { + warn!( + "lod1_solid of feature (id={}) contains invalid geometry: {}", + id, + e.to_string() + ); + }) + .ok(); + } + b"lod2Solid" => { + let xml_snippet: String = reader.read_text(e.name()).unwrap().to_string(); + space.lod2_solid = parse_solid(&xml_snippet) + .map_err(|e| { + warn!( + "lod2_solid of feature (id={}) contains invalid geometry: {}", + id, + e.to_string() + ); + }) + .ok(); + } + b"lod3Solid" => { + let xml_snippet: String = reader.read_text(e.name()).unwrap().to_string(); + space.lod3_solid = parse_solid(&xml_snippet) + .map_err(|e| { + warn!( + "lod3_solid of feature (id={}) contains invalid geometry: {}", + id, + e.to_string() + ); + }) + .ok(); + } + b"lod0MultiSurface" => { + let xml_snippet: String = reader.read_text(e.name()).unwrap().to_string(); + space.lod0_multi_surface = parse_multi_surface(&xml_snippet) + .map_err(|e| { + warn!( + "lod0_multi_surface of feature (id={}) contains invalid geometry: {}", + id, + e.to_string() + ); + }) + .ok(); + } + b"lod2MultiSurface" => { + let xml_snippet: String = reader.read_text(e.name()).unwrap().to_string(); + space.lod2_multi_surface = parse_multi_surface(&xml_snippet) + .map_err(|e| { + warn!( + "lod2_multi_surface of feature (id={}) contains invalid geometry: {}", + id, + e.to_string() + ); + }) + .ok(); + } + b"lod3MultiSurface" => { + let xml_snippet: String = reader.read_text(e.name()).unwrap().to_string(); + space.lod3_multi_surface = parse_multi_surface(&xml_snippet) + .map_err(|e| { + warn!( + "lod3_multi_surface of feature (id={}) contains invalid geometry: {}", + id, + e.to_string() + ); + }) + .ok(); + } + _ => {} + }, + Ok(Event::Eof) => break, + Err(e) => panic!("Error at position {}: {:?}", reader.buffer_position(), e), + Ok(Event::Text(e)) => txt.push(e.unescape().unwrap().into_owned()), + _ => (), + } + } + + Ok(space) +} + +pub fn parse_occupied_space(id: &Id, xml_document: &String) -> Result { + let space = parse_space(id, xml_document)?; + let mut occupied_space = OccupiedSpace::new(space); + + let mut reader = Reader::from_str(xml_document.as_str()); + reader.config_mut().trim_text_start = true; + reader.config_mut().trim_text_end = true; + + let mut txt = Vec::new(); + let mut buf = Vec::new(); + + loop { + match reader.read_event_into(&mut buf) { + Ok(Event::Start(e)) => match e.name().as_ref() { + b"lod1ImplicitRepresentation" => { + let xml_snippet: String = reader.read_text(e.name()).unwrap().to_string(); + occupied_space.lod1_implicit_representation = + parse_implicit_geometry(&xml_snippet) + .map_err(|e| { + warn!( + "lod1_implicit_representation of feature (id={}) contains invalid geometry: {}", + id, + e.to_string() + ); + }) + .ok(); + } + b"lod2ImplicitRepresentation" => { + let xml_snippet: String = reader.read_text(e.name()).unwrap().to_string(); + occupied_space.lod2_implicit_representation = + parse_implicit_geometry(&xml_snippet) + .map_err(|e| { + warn!( + "lod2_implicit_representation of feature (id={}) contains invalid geometry: {}", + id, + e.to_string() + ); + }) + .ok(); + } + b"lod3ImplicitRepresentation" => { + let xml_snippet: String = reader.read_text(e.name()).unwrap().to_string(); + occupied_space.lod3_implicit_representation = + parse_implicit_geometry(&xml_snippet) + .map_err(|e| { + warn!( + "lod3_implicit_representation of feature (id={}) contains invalid geometry: {}", + id, + e.to_string() + ); + }) + .ok(); + } + _ => {} + }, + Ok(Event::Eof) => break, + Err(e) => panic!("Error at position {}: {:?}", reader.buffer_position(), e), + Ok(Event::Text(e)) => txt.push(e.unescape().unwrap().into_owned()), + _ => (), + } + } + + Ok(occupied_space) +} + +pub fn parse_thematic_surface(id: &Id, xml_document: &String) -> Result { + let mut gml = Gml::new(id.clone()); + gml.name = vec!["name".to_string()]; // TODO + let city_object = CityObject::new(gml); + let mut thematic_surface = ThematicSurface::new(city_object); + + let mut reader = Reader::from_str(xml_document.as_str()); + reader.config_mut().trim_text_start = true; + reader.config_mut().trim_text_end = true; + + let mut txt = Vec::new(); + let mut buf = Vec::new(); + + loop { + match reader.read_event_into(&mut buf) { + Ok(Event::Start(e)) => match e.name().as_ref() { + b"lod0MultiSurface" => { + let xml_snippet: String = reader.read_text(e.name()).unwrap().to_string(); + thematic_surface.lod0_multi_surface = parse_multi_surface(&xml_snippet) + .map_err(|e| { + warn!( + "lod0_multi_surface of feature (id={}) contains invalid geometry: {}", + id, + e.to_string() + ); + }) + .ok(); + } + b"lod1MultiSurface" => { + let xml_snippet: String = reader.read_text(e.name()).unwrap().to_string(); + thematic_surface.lod1_multi_surface = parse_multi_surface(&xml_snippet) + .map_err(|e| { + warn!( + "lod1_multi_surface of feature (id={}) contains invalid geometry: {}", + id, + e.to_string() + ); + }) + .ok(); + } + b"lod2MultiSurface" => { + let xml_snippet: String = reader.read_text(e.name()).unwrap().to_string(); + thematic_surface.lod2_multi_surface = parse_multi_surface(&xml_snippet) + .map_err(|e| { + warn!( + "lod2_multi_surface of feature (id={}) contains invalid geometry: {}", + id, + e.to_string() + ); + }) + .ok(); + } + b"lod3MultiSurface" => { + let xml_snippet: String = reader.read_text(e.name()).unwrap().to_string(); + thematic_surface.lod3_multi_surface = parse_multi_surface(&xml_snippet) + .map_err(|e| { + warn!( + "lod3_multi_surface of feature (id={}) contains invalid geometry: {}",id, + e.to_string() + ); + }) + .ok(); + } + _ => {} + }, + Ok(Event::Eof) => break, + Err(e) => panic!("Error at position {}: {:?}", reader.buffer_position(), e), + Ok(Event::Text(e)) => txt.push(e.unescape().unwrap().into_owned()), + _ => (), + } + } + + Ok(thematic_surface) +} + +pub fn parse_implicit_geometry(xml_document: &String) -> Result { + let mut implicit_geometry = ImplicitGeometry::default(); + + let mut reader = Reader::from_str(xml_document.as_str()); + reader.config_mut().trim_text_start = true; + reader.config_mut().trim_text_end = true; + + let mut txt = Vec::new(); + let mut buf = Vec::new(); + loop { + match reader.read_event_into(&mut buf) { + Ok(Event::Start(e)) => match e.name().as_ref() { + b"referencePoint" => { + let xml_snippet: String = reader.read_text(e.name()).unwrap().to_string(); + implicit_geometry.reference_point = egml::io::parse_point(&xml_snippet)?; + } + _ => {} + }, + Ok(Event::Eof) => break, + Err(e) => panic!("Error at position {}: {:?}", reader.buffer_position(), e), + Ok(Event::Text(e)) => txt.push(e.unescape().unwrap().into_owned()), + _ => (), + } + } + + Ok(implicit_geometry) +} diff --git a/crates/ecitygml-io/src/parser/transportation.rs b/crates/ecitygml-io/src/parser/transportation.rs new file mode 100644 index 0000000..cc5cf0d --- /dev/null +++ b/crates/ecitygml-io/src/parser/transportation.rs @@ -0,0 +1,252 @@ +use crate::parser::attributes::extract_attributes; +use crate::parser::space::{parse_space, parse_thematic_surface}; +use crate::Error; +use ecitygml_core::model::transportation::{ + AuxiliaryTrafficArea, AuxiliaryTrafficSpace, Intersection, Road, Section, TrafficArea, + TrafficSpace, +}; +use egml::model::base::Id; +use quick_xml::events::Event; +use quick_xml::Reader; +use std::collections::HashMap; + +pub fn parse_road(id: &Id, xml_document: &String) -> Result { + let mut road = Road::new(id.clone()); + // todo: road.space = parse_space(xml_document)?; + + let mut reader = Reader::from_str(xml_document.as_str()); + reader.config_mut().trim_text_start = true; + reader.config_mut().trim_text_end = true; + + let mut txt = Vec::new(); + let mut buf = Vec::new(); + + loop { + match reader.read_event_into(&mut buf) { + Ok(Event::Start(e)) => { + let extracted_attributes: HashMap = extract_attributes(&reader, &e); + let id: Option = extracted_attributes + .get("id") + .map(|x| Id::try_from(x.as_str()).ok()) + .flatten(); + + match e.name().as_ref() { + b"tran:Section" => { + let xml_snippet: String = reader.read_text(e.name()).unwrap().to_string(); + let id: Id = id.unwrap_or(Id::from_hashed_string(&xml_snippet)); + + let section = parse_section(&id, &xml_snippet)?; + road.section.push(section); + } + b"tran:Intersection" => { + let xml_snippet: String = reader.read_text(e.name()).unwrap().to_string(); + let id: Id = id.unwrap_or(Id::from_hashed_string(&xml_snippet)); + + let intersection = parse_intersection(&id, &xml_snippet)?; + road.intersection.push(intersection); + } + _ => {} + } + } + Ok(Event::Eof) => break, + Err(e) => panic!("Error at position {}: {:?}", reader.buffer_position(), e), + Ok(Event::Text(e)) => txt.push(e.unescape().unwrap().into_owned()), + _ => (), + } + } + + Ok(road) +} + +pub fn parse_section(id: &Id, xml_document: &String) -> Result { + let mut section = Section::new(id.clone()); + // todo: section.space = parse_space(xml_document)?; + + let mut reader = Reader::from_str(xml_document.as_str()); + reader.config_mut().trim_text_start = true; + reader.config_mut().trim_text_end = true; + + let mut txt = Vec::new(); + let mut buf = Vec::new(); + + loop { + match reader.read_event_into(&mut buf) { + Ok(Event::Start(e)) => { + let extracted_attributes: HashMap = extract_attributes(&reader, &e); + let id: Option = extracted_attributes + .get("id") + .map(|x| Id::try_from(x.as_str()).ok()) + .flatten(); + + match e.name().as_ref() { + b"tran:TrafficSpace" => { + let xml_snippet: String = reader.read_text(e.name()).unwrap().to_string(); + let id: Id = id.unwrap_or(Id::from_hashed_string(&xml_snippet)); + + let traffic_space = parse_traffic_space(&id, &xml_snippet)?; + section.traffic_space.push(traffic_space); + } + b"tran:AuxiliaryTrafficSpace" => { + let xml_snippet: String = reader.read_text(e.name()).unwrap().to_string(); + let id: Id = id.unwrap_or(Id::from_hashed_string(&xml_snippet)); + + let auxiliary_traffic_space = + parse_auxiliary_traffic_space(&id, &xml_snippet)?; + section + .auxiliary_traffic_space + .push(auxiliary_traffic_space); + } + _ => {} + } + } + Ok(Event::Eof) => break, + Err(e) => panic!("Error at position {}: {:?}", reader.buffer_position(), e), + Ok(Event::Text(e)) => txt.push(e.unescape().unwrap().into_owned()), + _ => (), + } + } + + Ok(section) +} + +pub fn parse_intersection(id: &Id, xml_document: &String) -> Result { + let mut intersection = Intersection::new(id.clone()); + // todo: intersection.space = parse_space(xml_document)?; + + let mut reader = Reader::from_str(xml_document.as_str()); + reader.config_mut().trim_text_start = true; + reader.config_mut().trim_text_end = true; + + let mut txt = Vec::new(); + let mut buf = Vec::new(); + + loop { + match reader.read_event_into(&mut buf) { + Ok(Event::Start(e)) => { + let extracted_attributes: HashMap = extract_attributes(&reader, &e); + let id: Option = extracted_attributes + .get("id") + .map(|x| Id::try_from(x.as_str()).ok()) + .flatten(); + + match e.name().as_ref() { + b"tran:TrafficSpace" => { + let xml_snippet: String = reader.read_text(e.name()).unwrap().to_string(); + let id: Id = id.unwrap_or(Id::from_hashed_string(&xml_snippet)); + + let traffic_space = parse_traffic_space(&id, &xml_snippet)?; + intersection.traffic_space.push(traffic_space); + } + b"tran:AuxiliaryTrafficSpace" => { + let xml_snippet: String = reader.read_text(e.name()).unwrap().to_string(); + let id: Id = id.unwrap_or(Id::from_hashed_string(&xml_snippet)); + + let auxiliary_traffic_space = + parse_auxiliary_traffic_space(&id, &xml_snippet)?; + intersection + .auxiliary_traffic_space + .push(auxiliary_traffic_space); + } + _ => {} + } + } + Ok(Event::Eof) => break, + Err(e) => panic!("Error at position {}: {:?}", reader.buffer_position(), e), + Ok(Event::Text(e)) => txt.push(e.unescape().unwrap().into_owned()), + _ => (), + } + } + + Ok(intersection) +} + +pub fn parse_traffic_space(id: &Id, xml_document: &String) -> Result { + let space = parse_space(id, &xml_document)?; + let mut traffic_space = TrafficSpace::new(space); + + let mut reader = Reader::from_str(xml_document.as_str()); + reader.config_mut().trim_text_start = true; + reader.config_mut().trim_text_end = true; + + let mut txt = Vec::new(); + let mut buf = Vec::new(); + + loop { + match reader.read_event_into(&mut buf) { + Ok(Event::Start(e)) => { + let extracted_attributes: HashMap = extract_attributes(&reader, &e); + let id: Option = extracted_attributes + .get("id") + .map(|x| Id::try_from(x.as_str()).ok()) + .flatten(); + + match e.name().as_ref() { + b"tran:TrafficArea" => { + let xml_snippet: String = reader.read_text(e.name()).unwrap().to_string(); + let id: Id = id.unwrap_or(Id::from_hashed_string(&xml_snippet)); + + let thematic_surface = parse_thematic_surface(&id, &xml_snippet)?; + let traffic_area = TrafficArea::new(thematic_surface); + traffic_space.traffic_area.push(traffic_area); + } + _ => {} + } + } + Ok(Event::Eof) => break, + Err(e) => panic!("Error at position {}: {:?}", reader.buffer_position(), e), + Ok(Event::Text(e)) => txt.push(e.unescape().unwrap().into_owned()), + _ => (), + } + } + + Ok(traffic_space) +} + +pub fn parse_auxiliary_traffic_space( + id: &Id, + xml_document: &String, +) -> Result { + let space = parse_space(id, &xml_document)?; + let mut auxiliary_traffic_space = AuxiliaryTrafficSpace::new(space); + + let mut reader = Reader::from_str(xml_document.as_str()); + reader.config_mut().trim_text_start = true; + reader.config_mut().trim_text_end = true; + + let mut txt = Vec::new(); + let mut buf = Vec::new(); + + loop { + match reader.read_event_into(&mut buf) { + Ok(Event::Start(e)) => { + let extracted_attributes: HashMap = extract_attributes(&reader, &e); + let id: Option = extracted_attributes + .get("id") + .map(|x| Id::try_from(x.as_str()).ok()) + .flatten(); + + match e.name().as_ref() { + b"tran:AuxiliaryTrafficArea" => { + let xml_snippet: String = reader.read_text(e.name()).unwrap().to_string(); + let id: Id = id.unwrap_or(Id::from_hashed_string(&xml_snippet)); + + let thematic_surface = parse_thematic_surface(&id, &xml_snippet)?; + let mut auxiliary_traffic_area = + AuxiliaryTrafficArea::new(thematic_surface); + + auxiliary_traffic_space + .auxiliary_traffic_area + .push(auxiliary_traffic_area); + } + _ => {} + } + } + Ok(Event::Eof) => break, + Err(e) => panic!("Error at position {}: {:?}", reader.buffer_position(), e), + Ok(Event::Text(e)) => txt.push(e.unescape().unwrap().into_owned()), + _ => (), + } + } + + Ok(auxiliary_traffic_space) +} diff --git a/crates/ecitygml-io/src/read.rs b/crates/ecitygml-io/src/read.rs index d63f97e..c74bdbe 100644 --- a/crates/ecitygml-io/src/read.rs +++ b/crates/ecitygml-io/src/read.rs @@ -6,6 +6,7 @@ use std::io::{Read, Seek}; use crate::error::Error::{InvalidFileExtension, NoFileExtension}; use crate::validate_impl::validate_from_reader; use crate::{FILE_EXTENSION_CITYGML_GML_FORMAT, FILE_EXTENSION_CITYGML_XML_FORMAT}; +use ecitygml_core::model::city_model::CitygmlModel; use std::path::Path; /// `CitygmlReader` reads CityGML datasets. @@ -25,7 +26,7 @@ impl CitygmlReader { validate_from_reader(self.reader) } - pub fn finish(self) -> Result { + pub fn finish(self) -> Result { read_from_file(self.reader) } } diff --git a/crates/ecitygml-io/src/read_impl.rs b/crates/ecitygml-io/src/read_impl.rs index 07c89e9..1f57c54 100644 --- a/crates/ecitygml-io/src/read_impl.rs +++ b/crates/ecitygml-io/src/read_impl.rs @@ -1,21 +1,24 @@ use crate::error::Error; -use ecitygml_core::{ - CityFurniture, CitygmlModel, SolitaryVegetationObject, TrafficArea, WallSurface, -}; use quick_xml::events::Event; use quick_xml::Reader; use std::collections::HashMap; +use crate::parser::attributes::extract_attributes; +use crate::parser::building::parse_building; +use crate::parser::space::parse_occupied_space; +use crate::parser::transportation::parse_road; +use ecitygml_core::model::city_furniture::CityFurniture; +use ecitygml_core::model::city_model::CitygmlModel; +use ecitygml_core::model::solitary_vegetation_object::SolitaryVegetationObject; +use egml::model::base::Id; use std::io::{BufReader, Read, Seek}; - -use crate::parse::{parse_geometries, parse_reference_point}; -use egml::base::Id; +use uuid::Uuid; extern crate quick_xml; extern crate serde; -pub fn read_from_file(reader: R) -> Result { +pub fn read_from_file(reader: R) -> Result { let mut citygml_model = CitygmlModel::default(); // TODO: improve @@ -31,94 +34,48 @@ pub fn read_from_file(reader: R) -> Result { - let extracted_attributes: HashMap = e - .attributes() - .map(|attr_res| match attr_res { - Ok(a) => { - let key = reader - .decoder() - .decode(a.key.local_name().as_ref()) - .unwrap() - .to_string(); - let value: String = a - .decode_and_unescape_value(reader.decoder()) - .unwrap() - .to_string(); - /*println!( - "key: {}, value: {}", - reader - .decoder() - .decode(a.key.local_name().as_ref()) - .unwrap(), - value - );*/ - (key, value) - } - Err(err) => { - dbg!("unable to read key in DefaultSettings, err = {:?}", err); - (String::new(), String::new()) - } - }) - .collect(); - - let id: Id = extracted_attributes + let extracted_attributes: HashMap = extract_attributes(&reader, &e); + let id: Option = extracted_attributes .get("id") - .unwrap_or(&String::new()) - .to_string() - .into(); + .map(|x| Id::try_from(x.as_str()).ok()) + .flatten(); match e.name().as_ref() { - b"frn:CityFurniture" => { + b"bldg:Building" => { let xml_snippet: String = reader.read_text(e.name()).unwrap().to_string(); + let id: Id = id.unwrap_or(Id::from_hashed_string(&xml_snippet)); - let name = String::new(); - let mut city_furniture = CityFurniture::new(id, name); - - let parsed_geometries = parse_geometries(&xml_snippet)?; + let building = parse_building(&id, &xml_snippet)?; - let point_geometry = parse_reference_point(&xml_snippet)?; - city_furniture.set_reference_point(point_geometry); - city_furniture.set_lod1_solid(parsed_geometries.lod1_solid); - city_furniture.set_lod2_solid(parsed_geometries.lod2_solid); - city_furniture.set_lod2_multi_surface(parsed_geometries.lod2_multi_surface); - - citygml_model.push_city_furniture(city_furniture); + citygml_model.building.push(building); } - b"tran:TrafficArea" => { + b"frn:CityFurniture" => { let xml_snippet: String = reader.read_text(e.name()).unwrap().to_string(); + let id: Id = id.unwrap_or(Id::from_hashed_string(&xml_snippet)); - let name = String::new(); - let mut traffic_area = TrafficArea::new(id, name); - - let parsed_geometries = parse_geometries(&xml_snippet)?; - traffic_area.set_lod2_multi_surface(parsed_geometries.lod2_multi_surface); - - citygml_model.push_traffic_area(traffic_area); + let occupied_space = parse_occupied_space(&id, &xml_snippet)?; + let mut city_furniture = CityFurniture::new(occupied_space); + citygml_model.city_furniture.push(city_furniture); } - b"veg:SolitaryVegetationObject" => { + b"tran:Road" => { let xml_snippet: String = reader.read_text(e.name()).unwrap().to_string(); + let id: Id = id.unwrap_or(Id::from_hashed_string(&xml_snippet)); - let name = String::new(); - let mut solitary_vegetation_object = - SolitaryVegetationObject::new(id, name); - - let parsed_geometries = parse_geometries(&xml_snippet)?; - solitary_vegetation_object.set_lod1_solid(parsed_geometries.lod1_solid); - - citygml_model.push_solitary_vegetation_object(solitary_vegetation_object); + let road = parse_road(&id, &xml_snippet)?; + citygml_model.road.push(road); } - b"con:WallSurface" => { + b"veg:SolitaryVegetationObject" => { let xml_snippet: String = reader.read_text(e.name()).unwrap().to_string(); - - let name = String::new(); - let mut wall_surface = WallSurface::new(id, name); - - let parsed_geometries = parse_geometries(&xml_snippet)?; - wall_surface.set_lod2_multi_surface(parsed_geometries.lod2_multi_surface); - wall_surface.set_lod3_multi_surface(parsed_geometries.lod3_multi_surface); - - citygml_model.push_wall_surface(wall_surface); + let id: Id = id.unwrap_or(Id::from_hashed_string(&xml_snippet)); + + let occupied_space = parse_occupied_space(&id, &xml_snippet)?; + let solitary_vegetation_object = + SolitaryVegetationObject::new(occupied_space); + citygml_model + .solitary_vegetation_object + .push(solitary_vegetation_object); } + _ => (), } } diff --git a/crates/ecitygml-transform/Cargo.toml b/crates/ecitygml-transform/Cargo.toml index 1c86ba5..5fdbd5a 100644 --- a/crates/ecitygml-transform/Cargo.toml +++ b/crates/ecitygml-transform/Cargo.toml @@ -9,9 +9,10 @@ description = "Supplementary operations for CityGML data." [dependencies] -ecitygml-core = { version = "0.0.1-alpha.3", path = "../ecitygml-core" } +ecitygml-core = { version = "0.0.1-alpha.4", path = "../ecitygml-core" } egml = { workspace = true } thiserror = { workspace = true } nalgebra = { workspace = true } +rayon = { workspace = true } diff --git a/crates/ecitygml-transform/README.md b/crates/ecitygml-transform/README.md index b965c69..db2458c 100644 --- a/crates/ecitygml-transform/README.md +++ b/crates/ecitygml-transform/README.md @@ -2,8 +2,7 @@ Supplementary operations for [CityGML](https://www.ogc.org/standard/citygml/) data. -> [!WARNING] -> The library is at an early stage of development. +The library is at an early stage of development. ## Contributing diff --git a/crates/ecitygml-transform/src/error.rs b/crates/ecitygml-transform/src/error.rs index 6621d5b..84bda89 100644 --- a/crates/ecitygml-transform/src/error.rs +++ b/crates/ecitygml-transform/src/error.rs @@ -4,4 +4,6 @@ use thiserror::Error; pub enum Error { #[error(transparent)] EcitygmlError(#[from] ecitygml_core::Error), + #[error(transparent)] + EgmlError(#[from] egml::Error), } diff --git a/crates/ecitygml-transform/src/filter.rs b/crates/ecitygml-transform/src/filter.rs index 54cc4bb..f4b3cf6 100644 --- a/crates/ecitygml-transform/src/filter.rs +++ b/crates/ecitygml-transform/src/filter.rs @@ -1,70 +1,118 @@ use crate::error::Error; -use ecitygml_core::{ - CityFurniture, CitygmlModel, SolitaryVegetationObject, TrafficArea, WallSurface, -}; -use egml::geometry::Envelope; +use ecitygml_core::model::city_model::CitygmlModel; +use ecitygml_core::model::core::{OccupiedSpace, Space, ThematicSurface}; +use egml::model::geometry::Envelope; +use egml::operations::geometry::Geometry; pub fn filter_by_bounding_box( mut city_model: CitygmlModel, filter_envelope: &Envelope, ) -> Result { - // x.lod2_solid() - let filtered_city_furniture: Vec = city_model - .city_furniture() - .clone() - .into_iter() - .filter(|f| { - f.lod1_solid().as_ref().map_or(false, |g| { - filter_envelope.contains_envelope_partially(&g.get_envelope().unwrap()) - }) || f.lod2_solid().as_ref().map_or(false, |g| { - filter_envelope.contains_envelope_partially(&g.get_envelope().unwrap()) - }) || f.lod2_multi_surface().as_ref().map_or(false, |g| { - filter_envelope.contains_envelope_partially(&g.get_envelope().unwrap()) - }) || f - .reference_point() - .as_ref() - .map_or(false, |g| filter_envelope.contains(g)) - }) - .collect(); - city_model.set_city_furniture(filtered_city_furniture); + city_model.building.retain(|f| { + f.wall_surface + .iter() + .any(|w| contains_thematic_surface(filter_envelope, &w.thematic_surface)) + || f.roof_surface + .iter() + .any(|w| contains_thematic_surface(filter_envelope, &w.thematic_surface)) + || f.ground_surface + .iter() + .any(|w| contains_thematic_surface(filter_envelope, &w.thematic_surface)) + }); - let filtered_traffic_area: Vec = city_model - .traffic_area() - .clone() - .into_iter() - .filter(|f| { - f.lod2_multi_surface().as_ref().map_or(false, |g| { - filter_envelope.contains_envelope_partially(&g.get_envelope().unwrap()) - }) - }) - .collect(); - city_model.set_traffic_area(filtered_traffic_area); + // TODO road - let filtered_solitary_vegetation_object: Vec = city_model - .solitary_vegetation_object() - .clone() - .into_iter() - .filter(|f| { - f.lod1_solid().as_ref().map_or(false, |g| { - filter_envelope.contains_envelope_partially(&g.get_envelope().unwrap()) - }) - }) - .collect(); - city_model.set_solitary_vegetation_object(filtered_solitary_vegetation_object); + city_model + .city_furniture + .retain(|f| contains_occupied_space(filter_envelope, &f.occupied_space)); - let filtered_wall_surface: Vec = city_model - .wall_surface() - .clone() - .into_iter() - .filter(|f| { - f.lod2_multi_surface().as_ref().map_or(false, |g| { - filter_envelope.contains_envelope_partially(&g.get_envelope().unwrap()) - }) || f.lod3_multi_surface().as_ref().map_or(false, |g| { - filter_envelope.contains_envelope_partially(&g.get_envelope().unwrap()) - }) - }) - .collect(); - city_model.set_wall_surface(filtered_wall_surface); + city_model + .solitary_vegetation_object + .retain(|f| contains_occupied_space(filter_envelope, &f.occupied_space)); Ok(city_model) } + +fn contains_thematic_surface( + filter_envelope: &Envelope, + thematic_surface: &ThematicSurface, +) -> bool { + if let Some(g) = &thematic_surface.lod0_multi_surface { + if filter_envelope.contains_envelope_partially(&g.envelope()) { + return true; + } + } + if let Some(g) = &thematic_surface.lod1_multi_surface { + if filter_envelope.contains_envelope_partially(&g.envelope()) { + return true; + } + } + if let Some(g) = &thematic_surface.lod2_multi_surface { + if filter_envelope.contains_envelope_partially(&g.envelope()) { + return true; + } + } + if let Some(g) = &thematic_surface.lod3_multi_surface { + if filter_envelope.contains_envelope_partially(&g.envelope()) { + return true; + } + } + + false +} + +fn contains_occupied_space(filter_envelope: &Envelope, occupied_space: &OccupiedSpace) -> bool { + if let Some(g) = &occupied_space.lod1_implicit_representation { + if filter_envelope.contains(&g.reference_point) { + return true; + } + } + if let Some(g) = &occupied_space.lod2_implicit_representation { + if filter_envelope.contains(&g.reference_point) { + return true; + } + } + if let Some(g) = &occupied_space.lod3_implicit_representation { + if filter_envelope.contains(&g.reference_point) { + return true; + } + } + + false +} + +fn contains_space(filter_envelope: &Envelope, space: &Space) -> bool { + if let Some(g) = &space.lod1_solid { + if filter_envelope.contains_envelope_partially(&g.envelope()) { + return true; + } + } + if let Some(g) = &space.lod2_solid { + if filter_envelope.contains_envelope_partially(&g.envelope()) { + return true; + } + } + if let Some(g) = &space.lod3_solid { + if filter_envelope.contains_envelope_partially(&g.envelope()) { + return true; + } + } + + if let Some(g) = &space.lod0_multi_surface { + if filter_envelope.contains_envelope_partially(&g.envelope()) { + return true; + } + } + if let Some(g) = &space.lod2_multi_surface { + if filter_envelope.contains_envelope_partially(&g.envelope()) { + return true; + } + } + if let Some(g) = &space.lod3_multi_surface { + if filter_envelope.contains_envelope_partially(&g.envelope()) { + return true; + } + } + + false +} diff --git a/crates/ecitygml-transform/src/lib.rs b/crates/ecitygml-transform/src/lib.rs index 52f9766..2857f4c 100644 --- a/crates/ecitygml-transform/src/lib.rs +++ b/crates/ecitygml-transform/src/lib.rs @@ -1,6 +1,5 @@ mod error; pub mod filter; -pub mod offset; #[doc(inline)] pub use crate::error::Error; diff --git a/crates/ecitygml-transform/src/offset.rs b/crates/ecitygml-transform/src/offset.rs deleted file mode 100644 index 8d6db9f..0000000 --- a/crates/ecitygml-transform/src/offset.rs +++ /dev/null @@ -1,92 +0,0 @@ -use crate::error::Error; -use ecitygml_core::{ - CityFurniture, CitygmlModel, SolitaryVegetationObject, TrafficArea, WallSurface, -}; -use egml::transform::offset::{offset_multi_surface, offset_position, offset_solid}; -use nalgebra::Vector3; - -pub fn offset_citygml_model( - mut city_model: CitygmlModel, - offset: &Vector3, -) -> Result { - // TODO improve in-situ transformation without cloning - - let mut transformed_city_furniture: Vec = city_model.city_furniture().clone(); - transformed_city_furniture - .iter_mut() - .filter(|x| x.reference_point().is_some()) - .for_each(|x| { - let reference_point = x.reference_point().unwrap(); - let offset_reference_point = offset_position(reference_point, offset).unwrap(); - x.set_reference_point(Some(offset_reference_point)); - }); - transformed_city_furniture - .iter_mut() - .filter(|x| x.lod2_multi_surface().is_some()) - .for_each(|x| { - let geom = x.lod2_multi_surface().to_owned().unwrap(); - let offset_geom = offset_multi_surface(geom, offset).unwrap(); - x.set_lod2_multi_surface(Some(offset_geom)); - }); - transformed_city_furniture - .iter_mut() - .filter(|x| x.lod1_solid().is_some()) - .for_each(|x| { - let geom = x.lod1_solid().to_owned().unwrap(); - let offset_geom = offset_solid(geom, offset).unwrap(); - x.set_lod1_solid(Some(offset_geom)); - }); - transformed_city_furniture - .iter_mut() - .filter(|x| x.lod2_solid().is_some()) - .for_each(|x| { - let geom = x.lod2_solid().to_owned().unwrap(); - let offset_geom = offset_solid(geom, offset).unwrap(); - x.set_lod2_solid(Some(offset_geom)); - }); - city_model.set_city_furniture(transformed_city_furniture); - - let mut transformed_traffic_area: Vec = city_model.traffic_area().clone(); - transformed_traffic_area - .iter_mut() - .filter(|x| x.lod2_multi_surface().is_some()) - .for_each(|x| { - let geom = x.lod2_multi_surface().to_owned().unwrap(); - let offset_geom = offset_multi_surface(geom, offset).unwrap(); - x.set_lod2_multi_surface(Some(offset_geom)); - }); - city_model.set_traffic_area(transformed_traffic_area); - - let mut transformed_solitary_vegeation_object: Vec = - city_model.solitary_vegetation_object().clone(); - transformed_solitary_vegeation_object - .iter_mut() - .filter(|x| x.lod1_solid().is_some()) - .for_each(|x| { - let geom = x.lod1_solid().to_owned().unwrap(); - let offset_geom = offset_solid(geom, offset).unwrap(); - x.set_lod1_solid(Some(offset_geom)); - }); - city_model.set_solitary_vegetation_object(transformed_solitary_vegeation_object); - - let mut transformed_wall_surface: Vec = city_model.wall_surface().clone(); - transformed_wall_surface - .iter_mut() - .filter(|x| x.lod2_multi_surface().is_some()) - .for_each(|x| { - let geom = x.lod2_multi_surface().to_owned().unwrap(); - let offset_geom = offset_multi_surface(geom, offset).unwrap(); - x.set_lod2_multi_surface(Some(offset_geom)); - }); - transformed_wall_surface - .iter_mut() - .filter(|x| x.lod3_multi_surface().is_some()) - .for_each(|x| { - let geom = x.lod3_multi_surface().to_owned().unwrap(); - let offset_geom = offset_multi_surface(geom, offset).unwrap(); - x.set_lod3_multi_surface(Some(offset_geom)); - }); - city_model.set_wall_surface(transformed_wall_surface); - - Ok(city_model) -} diff --git a/crates/ecitygml/Cargo.toml b/crates/ecitygml/Cargo.toml index da6e480..62b4692 100644 --- a/crates/ecitygml/Cargo.toml +++ b/crates/ecitygml/Cargo.toml @@ -9,6 +9,6 @@ description = "Library for processing CityGML data." [dependencies] -ecitygml-core = { version = "0.0.1-alpha.3", path = "../ecitygml-core" } -ecitygml-io = { version = "0.0.1-alpha.3", path = "../ecitygml-io" } -ecitygml-transform = { version = "0.0.1-alpha.3", path = "../ecitygml-transform" } +ecitygml-core = { version = "0.0.1-alpha.4", path = "../ecitygml-core" } +ecitygml-io = { version = "0.0.1-alpha.4", path = "../ecitygml-io" } +ecitygml-transform = { version = "0.0.1-alpha.4", path = "../ecitygml-transform" } diff --git a/crates/ecitygml/README.md b/crates/ecitygml/README.md index dac2426..db92fa8 100644 --- a/crates/ecitygml/README.md +++ b/crates/ecitygml/README.md @@ -2,8 +2,7 @@ Library for processing [CityGML](https://www.ogc.org/standard/citygml/) data. -> [!WARNING] -> The library is at an early stage of development. +The library is at an early stage of development. ## Contributing diff --git a/crates/ecitygml/src/lib.rs b/crates/ecitygml/src/lib.rs index 5ad083e..36002da 100644 --- a/crates/ecitygml/src/lib.rs +++ b/crates/ecitygml/src/lib.rs @@ -1,9 +1,7 @@ -//! Library for representing [CityGML3](https://www.ogc.org/standards/citygml). +//! `ecitygml` is a library for processing [CityGML](https://www.ogc.org/standard/citygml/) data. //! -pub use ecitygml_core::{ - CityFurniture, CitygmlModel, Error, SolitaryVegetationObject, TrafficArea, WallSurface, -}; +pub use ecitygml_core::{model, operations, util, Error}; pub use ecitygml_io as io;