diff --git a/nusamai-citygml/src/geometry.rs b/nusamai-citygml/src/geometry.rs index a2f22085..72f0a2c0 100644 --- a/nusamai-citygml/src/geometry.rs +++ b/nusamai-citygml/src/geometry.rs @@ -74,6 +74,9 @@ pub struct GeometryStore { /// Lists of surface for composite surface pub composite_surfaces: Vec, + /// Orientable surfaces for each surface + pub orientable_surfaces: Vec, + /// Assigned materials for each polygon. Empty if appearance resolution is not enabled. pub polygon_materials: Vec>, /// Assigned textures for each polygon. Empty if appearance resolution is not enabled. @@ -90,6 +93,13 @@ pub struct SurfaceSpan { pub end: u32, } +#[derive(Debug, Clone)] +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] +pub struct OrientableSurface { + pub surface_id: LocalId, + pub reverse: bool, +} + /// Temporary storage for the parser to collect geometries. #[derive(Default)] pub(crate) struct GeometryCollector { @@ -107,6 +117,9 @@ pub(crate) struct GeometryCollector { /// Lists of surface for composite surface pub composite_surfaces: Vec, + + /// Orientable surfaces for each surface + pub orientable_surfaces: Vec, } impl GeometryCollector { @@ -168,6 +181,7 @@ impl GeometryCollector { ring_ids: self.ring_ids, surface_spans: self.surface_spans, composite_surfaces: self.composite_surfaces, + orientable_surfaces: self.orientable_surfaces, ..Default::default() } } diff --git a/nusamai-citygml/src/parser.rs b/nusamai-citygml/src/parser.rs index d8199576..197b1ae0 100644 --- a/nusamai-citygml/src/parser.rs +++ b/nusamai-citygml/src/parser.rs @@ -18,7 +18,7 @@ use crate::{ GeometryType, }, namespace::{wellknown_prefix_from_nsres, APP_2_NS, GML31_NS}, - CityGmlAttribute, LocalId, SurfaceSpan, XLINK_NS, + CityGmlAttribute, LocalId, OrientableSurface, SurfaceSpan, XLINK_NS, }; static PROPERTY_PATTERN: Lazy = @@ -639,7 +639,29 @@ impl<'b, R: BufRead> SubTreeReader<'_, 'b, R> { self.parse_composite_surface()?; GeometryType::Surface } - (Bound(GML31_NS), b"OrientableSurface") => todo!(), + (Bound(GML31_NS), b"OrientableSurface") => { + let mut orientation = "+".to_string(); + for attr in start.attributes().flatten() { + let (_, localname) = self.reader.resolve_attribute(attr.key); + if localname.as_ref() == b"orientation" { + let value = + String::from_utf8_lossy(attr.value.as_ref()).to_string(); + orientation = value; + break; + } + } + let base_surface_id = self.parse_orientable_surface()?; + if let Some(base_surface_id) = base_surface_id { + surface_id = Some(base_surface_id.clone()); + self.state.geometry_collector.orientable_surfaces.push( + OrientableSurface { + surface_id: base_surface_id, + reverse: orientation == "-", + }, + ); + } + GeometryType::Surface + } (Bound(GML31_NS), b"Polygon") => { self.parse_polygon()?; GeometryType::Surface @@ -728,7 +750,7 @@ impl<'b, R: BufRead> SubTreeReader<'_, 'b, R> { (Bound(GML31_NS), b"Tin") => self.parse_triangulated_surface()?, _ => { return Err(ParseError::SchemaViolation(format!( - "Unexpected element <{}>", + "Unexpected element <{}> by parse triangulated prop", String::from_utf8_lossy(localname.as_ref()) ))) } @@ -792,7 +814,7 @@ impl<'b, R: BufRead> SubTreeReader<'_, 'b, R> { (Bound(GML31_NS), b"Triangle") => self.parse_polygon()?, _ => { return Err(ParseError::SchemaViolation(format!( - "Unexpected element <{}>", + "Unexpected element <{}> by parsing triangle patch array", String::from_utf8_lossy(localname.as_ref()) ))) } @@ -821,7 +843,11 @@ impl<'b, R: BufRead> SubTreeReader<'_, 'b, R> { let (surface_id, _) = self.parse_surface()?; surface_id } - _ => return Err(ParseError::SchemaViolation("Unexpected element".into())), + _ => { + return Err(ParseError::SchemaViolation( + "Unexpected element. Because only surface member".into(), + )) + } }; } Ok(Event::End(_)) => return Ok(surface_id), @@ -848,15 +874,19 @@ impl<'b, R: BufRead> SubTreeReader<'_, 'b, R> { if nsres == Bound(XLINK_NS) && localname.as_ref() == b"href" { let href = String::from_utf8_lossy(attr.value.as_ref()).to_string(); surface_id = Some(LocalId::from(href)); + break; } } match (nsres, localname.as_ref()) { (Bound(GML31_NS), b"surfaceMember") => { - self.parse_surface()?; + let (surface_member_id, _) = self.parse_surface()?; + if surface_id.is_none() { + surface_id = surface_member_id; + } } _ => { return Err(ParseError::SchemaViolation(format!( - "Unexpected element <{}>", + "Unexpected element <{}> by parsing composite surface", String::from_utf8_lossy(localname.as_ref()) ))) } @@ -870,7 +900,12 @@ impl<'b, R: BufRead> SubTreeReader<'_, 'b, R> { .push(id.clone()); } } - Ok(Event::End(_)) => return Ok(result), + Ok(Event::End(end)) => { + let (nsres, localname) = self.reader.resolve_element(end.name()); + if nsres == Bound(GML31_NS) && localname.as_ref() == b"CompositeSurface" { + return Ok(result); + } + } Ok(Event::Text(_)) => { return Err(ParseError::SchemaViolation( "Unexpected text content".into(), @@ -907,17 +942,35 @@ impl<'b, R: BufRead> SubTreeReader<'_, 'b, R> { surface_ids = self.parse_composite_surface()?; } (Bound(GML31_NS), b"OrientableSurface") => { - // FIXME: - // TODO: OrientableSurface - log::warn!("OrientableSurface is not supported yet."); - self.reader - .read_to_end_into(start.name(), &mut self.state.buf2)?; + let mut orientation = "+".to_string(); + for attr in start.attributes().flatten() { + let (_, localname) = self.reader.resolve_attribute(attr.key); + if localname.as_ref() == b"orientation" { + let value = + String::from_utf8_lossy(attr.value.as_ref()).to_string(); + orientation = value; + break; + } + } + let base_surface_id = self.parse_orientable_surface()?; + if let Some(base_surface_id) = base_surface_id { + surface_id = Some(base_surface_id.clone()); + self.state.geometry_collector.orientable_surfaces.push( + OrientableSurface { + surface_id: base_surface_id, + reverse: orientation == "-", + }, + ); + } + } + (Bound(GML31_NS), b"surfaceMember") => { + (surface_id, _) = self.parse_surface()?; } // (Bound(GML_NS), b"TriangulatedSurface") => // (Bound(GML_NS), b"Tin") => _ => { return Err(ParseError::SchemaViolation(format!( - "Unexpected element <{}>", + "Unexpected element <{}> by parsing surface", String::from_utf8_lossy(localname.as_ref()) ))) } @@ -950,6 +1003,43 @@ impl<'b, R: BufRead> SubTreeReader<'_, 'b, R> { } } + fn parse_orientable_surface(&mut self) -> Result, ParseError> { + let mut surface_id = None; + loop { + match self.reader.read_event_into(&mut self.state.buf1) { + Ok(Event::Start(start)) => { + let (nsres, localname) = self.reader.resolve_element(start.name()); + match (nsres, localname.as_ref()) { + (Bound(GML31_NS), b"baseSurface") => { + for attr in start.attributes().flatten() { + let (nsres, localname) = self.reader.resolve_attribute(attr.key); + if nsres == Bound(XLINK_NS) && localname.as_ref() == b"href" { + let href = + String::from_utf8_lossy(attr.value.as_ref()).to_string(); + surface_id = Some(LocalId::from(href)); + break; + } + } + } + _ => { + return Err(ParseError::SchemaViolation( + "Unexpected element. Because only base surface".into(), + )) + } + }; + } + Ok(Event::End(_)) => return Ok(surface_id), + Ok(Event::Text(_)) => { + return Err(ParseError::SchemaViolation( + "Unexpected text content".into(), + )) + } + Ok(_) => (), + Err(e) => return Err(e.into()), + } + } + } + fn parse_polygon(&mut self) -> Result<(), ParseError> { let mut depth = 1; let mut expect_exterior = true; @@ -1139,7 +1229,7 @@ impl<'b, R: BufRead> SubTreeReader<'_, 'b, R> { Ok(Event::Start(start)) => { if inside_coordinates { return Err(ParseError::SchemaViolation(format!( - "Unexpected elements <{}>", + "Unexpected elements <{}> inside ", String::from_utf8_lossy(start.name().as_ref()) ))); } @@ -1165,7 +1255,7 @@ impl<'b, R: BufRead> SubTreeReader<'_, 'b, R> { } _ => { return Err(ParseError::SchemaViolation(format!( - "Unexpected elements <{}>", + "Unexpected elements <{}> inside ", String::from_utf8_lossy(start.name().as_ref()) ))); } diff --git a/nusamai-plateau/src/models/iur/uro/underground_building.rs b/nusamai-plateau/src/models/iur/uro/underground_building.rs index 56b3a056..dafdb8c0 100644 --- a/nusamai-plateau/src/models/iur/uro/underground_building.rs +++ b/nusamai-plateau/src/models/iur/uro/underground_building.rs @@ -56,6 +56,7 @@ pub struct UndergroundBuilding { #[citygml(path = b"bldg:address/core:Address")] pub address: Vec, + #[citygml(path = b"uro:buildingDataQualityAttribute/uro:BuildingDataQualityAttribute")] #[citygml(path = b"uro:bldgDataQualityAttribute/uro:DataQualityAttribute")] pub bldg_data_quality_attribute: Option,