Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add OrientableSurface struct and parsing logic for enhanced geometry handling #34

Merged
merged 4 commits into from
Nov 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions nusamai-citygml/src/geometry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,9 @@ pub struct GeometryStore {
/// Lists of surface for composite surface
pub composite_surfaces: Vec<LocalId>,

/// Orientable surfaces for each surface
pub orientable_surfaces: Vec<OrientableSurface>,

/// Assigned materials for each polygon. Empty if appearance resolution is not enabled.
pub polygon_materials: Vec<Option<u32>>,
/// Assigned textures for each polygon. Empty if appearance resolution is not enabled.
Expand All @@ -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 {
Expand All @@ -107,6 +117,9 @@ pub(crate) struct GeometryCollector {

/// Lists of surface for composite surface
pub composite_surfaces: Vec<LocalId>,

/// Orientable surfaces for each surface
pub orientable_surfaces: Vec<OrientableSurface>,
}

impl GeometryCollector {
Expand Down Expand Up @@ -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()
}
}
Expand Down
122 changes: 106 additions & 16 deletions nusamai-citygml/src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Regex> =
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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())
)))
}
Expand Down Expand Up @@ -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())
)))
}
Expand Down Expand Up @@ -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),
Expand All @@ -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())
)))
}
Expand All @@ -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(),
Expand Down Expand Up @@ -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())
)))
}
Expand Down Expand Up @@ -950,6 +1003,43 @@ impl<'b, R: BufRead> SubTreeReader<'_, 'b, R> {
}
}

fn parse_orientable_surface(&mut self) -> Result<Option<LocalId>, 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;
Expand Down Expand Up @@ -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 <app:textureCoordinates>",
String::from_utf8_lossy(start.name().as_ref())
)));
}
Expand All @@ -1165,7 +1255,7 @@ impl<'b, R: BufRead> SubTreeReader<'_, 'b, R> {
}
_ => {
return Err(ParseError::SchemaViolation(format!(
"Unexpected elements <{}>",
"Unexpected elements <{}> inside <app:TexCoordList>",
String::from_utf8_lossy(start.name().as_ref())
)));
}
Expand Down
1 change: 1 addition & 0 deletions nusamai-plateau/src/models/iur/uro/underground_building.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ pub struct UndergroundBuilding {
#[citygml(path = b"bldg:address/core:Address")]
pub address: Vec<core::Address>,

#[citygml(path = b"uro:buildingDataQualityAttribute/uro:BuildingDataQualityAttribute")]
#[citygml(path = b"uro:bldgDataQualityAttribute/uro:DataQualityAttribute")]
pub bldg_data_quality_attribute: Option<uro::DataQualityAttribute>,

Expand Down