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: update nusamai dependencies and add support for LineString3D in geometry processing #35

Merged
merged 2 commits into from
Dec 2, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
9 changes: 9 additions & 0 deletions nusamai-citygml/src/geometry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,15 @@ impl GeometryCollector {
}));
}

pub fn add_linestring(&mut self, iter: impl IntoIterator<Item = [f64; 3]>) {
self.multilinestring
.add_linestring(iter.into_iter().map(|v| {
let vbits = [v[0].to_bits(), v[1].to_bits(), v[2].to_bits()];
let (index, _) = self.vertices.insert_full(vbits);
index as u32
}));
}

pub fn into_geometries(self, envelope_crs_uri: Option<String>) -> GeometryStore {
let mut vertices = Vec::with_capacity(self.vertices.len());
for vbits in &self.vertices {
Expand Down
235 changes: 197 additions & 38 deletions nusamai-citygml/src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ impl<'a> ParseContext<'a> {
}
}

impl<'a> Default for ParseContext<'a> {
impl Default for ParseContext<'_> {
fn default() -> Self {
Self {
source_uri: Url::parse("file:///").unwrap(),
Expand All @@ -111,6 +111,7 @@ impl<'a> Default for ParseContext<'a> {
}
}

#[allow(elided_named_lifetimes)]
impl<'a> CityGmlReader<'a> {
pub fn new(context: ParseContext<'a>) -> Self {
Self {
Expand Down Expand Up @@ -386,11 +387,7 @@ impl<'b, R: BufRead> SubTreeReader<'_, 'b, R> {
Triangulated => self.parse_triangulated_prop(geomref, lod, feature_id, feature_type)?, // FIXME
Point => todo!(), // FIXME
MultiPoint => todo!(), // FIXME
MultiCurve => {
log::warn!("CompositeCurve is not supported yet.");
self.skip_current_element()?;
return Ok(());
} // FIXME
MultiCurve => self.parse_multi_curve_prop(geomref, lod, feature_id, feature_type)?, // FIXME
}

self.state
Expand All @@ -400,6 +397,59 @@ impl<'b, R: BufRead> SubTreeReader<'_, 'b, R> {
Ok(())
}

fn parse_multi_curve_prop(
&mut self,
geomrefs: &mut GeometryRefs,
lod: u8,
feature_id: Option<String>,
feature_type: Option<String>,
) -> Result<(), ParseError> {
loop {
match self.reader.read_event_into(&mut self.state.buf1) {
Ok(Event::Start(start)) => {
let (nsres, localname) = self.reader.resolve_element(start.name());
let line_begin = self.state.geometry_collector.multilinestring.len();

let geomtype = match (nsres, localname.as_ref()) {
(Bound(GML31_NS), b"MultiCurve") => {
self.parse_multi_curve()?;
GeometryType::Curve
}
_ => {
return Err(ParseError::SchemaViolation(format!(
"Expected MultiCurve but found <{}>",
String::from_utf8_lossy(start.name().as_ref())
)))
}
};

let line_end = self.state.geometry_collector.multilinestring.len();
if line_end - line_begin > 0 {
geomrefs.push(GeometryRef {
ty: geomtype,
lod,
pos: line_begin as u32,
len: (line_end - line_begin) as u32,
id: None,
solid_ids: Vec::new(),
feature_id: feature_id.clone(),
feature_type: feature_type.clone(),
});
}
}
Ok(Event::End(_)) => break,
Ok(Event::Text(_)) => {
return Err(ParseError::SchemaViolation(
"Unexpected text content".into(),
))
}
Ok(_) => (),
Err(e) => return Err(e.into()),
}
}
Ok(())
}

fn parse_multi_surface_prop(
&mut self,
geomrefs: &mut GeometryRefs,
Expand Down Expand Up @@ -599,7 +649,9 @@ impl<'b, R: BufRead> SubTreeReader<'_, 'b, R> {
Ok(Event::Start(start)) => {
let (nsres, localname) = self.reader.resolve_element(start.name());
let poly_begin = self.state.geometry_collector.multipolygon.len();
let mut geometry_crs_uri = None;
let line_begin = self.state.geometry_collector.multilinestring.len();
let mut poly_end: Option<usize> = None;
let mut line_end: Option<usize> = None;

for attr in start.attributes().flatten() {
let (nsres, localname) = self.reader.resolve_attribute(attr.key);
Expand All @@ -609,7 +661,7 @@ impl<'b, R: BufRead> SubTreeReader<'_, 'b, R> {
surface_id = Some(LocalId::from(id));
}
if localname.as_ref() == b"srsName" {
geometry_crs_uri =
self.state.geometry_collector.geometry_crs_uri =
Some(String::from_utf8_lossy(attr.value.as_ref()).to_string());
}
}
Expand All @@ -626,17 +678,20 @@ impl<'b, R: BufRead> SubTreeReader<'_, 'b, R> {
}
(Bound(GML31_NS), b"Solid") => {
self.parse_solid()?;
poly_end = Some(self.state.geometry_collector.multipolygon.len());
GeometryType::Solid
}
(Bound(GML31_NS), b"MultiSurface") => {
let id = self.parse_multi_surface()?;
if surface_id.is_none() {
surface_id = id;
}
poly_end = Some(self.state.geometry_collector.multipolygon.len());
GeometryType::Surface
}
(Bound(GML31_NS), b"CompositeSurface") => {
self.parse_composite_surface()?;
poly_end = Some(self.state.geometry_collector.multipolygon.len());
GeometryType::Surface
}
(Bound(GML31_NS), b"OrientableSurface") => {
Expand All @@ -660,48 +715,52 @@ impl<'b, R: BufRead> SubTreeReader<'_, 'b, R> {
},
);
}
poly_end = Some(self.state.geometry_collector.multipolygon.len());
GeometryType::Surface
}
(Bound(GML31_NS), b"Polygon") => {
self.parse_polygon()?;
poly_end = Some(self.state.geometry_collector.multipolygon.len());
GeometryType::Surface
}
(Bound(GML31_NS), b"TriangulatedSurface") => todo!(),
(Bound(GML31_NS), b"Tin") => todo!(),
(
Bound(GML31_NS),
b"Point" | b"CompositeCurve" | b"MultiCurve" | b"LineString",
) => {
// FIXME, TODO
log::warn!(
"Point|CompositeCurve|MultiCurve|LineString is not supported yet."
);
self.reader
.read_to_end_into(start.name(), &mut self.state.buf2)?;

(Bound(GML31_NS), b"LineString") => {
self.parse_linestring()?;
line_end = Some(self.state.geometry_collector.multilinestring.len());
GeometryType::Curve
} // FIXME:
}
(Bound(GML31_NS), b"MultiCurve") => {
self.parse_multi_curve_prop(
geomrefs,
lod,
feature_id.clone(),
feature_type.clone(),
)?;
line_end = Some(self.state.geometry_collector.multilinestring.len());
GeometryType::Curve
}
(Bound(GML31_NS), b"TriangulatedSurface") => unimplemented!(),
(Bound(GML31_NS), b"Tin") => unimplemented!(),
(Bound(GML31_NS), b"Point" | b"CompositeCurve") => unimplemented!(), // FIXME:
_ => {
return Err(ParseError::SchemaViolation(format!(
"Unexpected geometry elements <{}>",
String::from_utf8_lossy(start.name().as_ref())
)))
}
};

let poly_end = self.state.geometry_collector.multipolygon.len();
if poly_end - poly_begin > 0 {
geomrefs.push(GeometryRef {
ty: geomtype,
lod,
pos: poly_begin as u32,
len: (poly_end - poly_begin) as u32,
id: surface_id.clone(),
solid_ids: Vec::new(),
feature_id: feature_id.clone(),
feature_type: feature_type.clone(),
});

if let Some(poly_end) = poly_end {
if poly_end - poly_begin > 0 {
geomrefs.push(GeometryRef {
ty: geomtype,
lod,
pos: poly_begin as u32,
len: (poly_end - poly_begin) as u32,
id: surface_id.clone(),
solid_ids: Vec::new(),
feature_id: feature_id.clone(),
feature_type: feature_type.clone(),
});
}
// record a partial surface span
if let Some(id) = surface_id.clone() {
self.state
Expand All @@ -713,8 +772,20 @@ impl<'b, R: BufRead> SubTreeReader<'_, 'b, R> {
end: poly_end as u32,
});
}

self.state.geometry_collector.geometry_crs_uri = geometry_crs_uri;
}
if let Some(line_end) = line_end {
if line_end - line_begin > 0 {
geomrefs.push(GeometryRef {
ty: geomtype,
lod,
pos: line_begin as u32,
len: (line_end - line_begin) as u32,
id: surface_id.clone(),
solid_ids: Vec::new(),
feature_id: feature_id.clone(),
feature_type: feature_type.clone(),
});
}
}
}
Ok(Event::End(_)) => break,
Expand Down Expand Up @@ -783,6 +854,34 @@ impl<'b, R: BufRead> SubTreeReader<'_, 'b, R> {
Ok(())
}

fn parse_multi_curve(&mut self) -> Result<(), ParseError> {
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"curveMember") => {
self.parse_linestring()?;
}
_ => {
return Err(ParseError::SchemaViolation(
"Unexpected element. Because only surface member".into(),
))
}
};
}
Ok(Event::End(_)) => return Ok(()),
Ok(Event::Text(_)) => {
return Err(ParseError::SchemaViolation(
"Unexpected text content".into(),
))
}
Ok(_) => (),
Err(e) => return Err(e.into()),
}
}
}

fn parse_solid(&mut self) -> Result<(Option<LocalId>, Vec<LocalId>), ParseError> {
if expect_start(self.reader, &mut self.state.buf1, GML31_NS, b"exterior")? {
let (surface_id, surface_ids) = self.parse_surface()?;
Expand Down Expand Up @@ -1003,6 +1102,66 @@ impl<'b, R: BufRead> SubTreeReader<'_, 'b, R> {
}
}

fn parse_linestring(&mut self) -> Result<(), ParseError> {
let mut depth = 1;
loop {
match self.reader.read_resolved_event_into(&mut self.state.buf1) {
Ok((Bound(GML31_NS), Event::Start(start))) => {
depth += 1;
println!("depth: {}, start: {:?}", depth, start.local_name());
}
Ok((_, Event::Start(start))) => {
return Err(ParseError::SchemaViolation(format!(
"Only GML elements are allowed but found <{}>",
String::from_utf8_lossy(start.name().as_ref())
)));
}
Ok((_, Event::End(_))) => {
depth -= 1;
if depth == 0 {
return Ok(());
}
}
Ok((_, Event::Text(text))) => {
// check poslist
if depth != 3 {
return Err(ParseError::SchemaViolation(
"Unexpected text content".into(),
));
}
// parse coordinate sequence
self.state.fp_buf.clear();
for s in text.unescape().unwrap().split_ascii_whitespace() {
if let Ok(v) = s.parse() {
self.state.fp_buf.push(v);
} else {
return Err(ParseError::InvalidValue(format!(
"Invalid floating point number: {}",
s
)));
}
}

if self.state.fp_buf.len() % 3 != 0 {
return Err(ParseError::InvalidValue(
"Length of coordinate numbers must be multiple of 3".into(),
));
}

let iter = self
.state
.fp_buf
.chunks_exact(3)
.map(|c| [c[0], c[1], c[2]]);

self.state.geometry_collector.add_linestring(iter);
}
Ok(_) => (),
Err(e) => return Err(e.into()),
}
}
}

fn parse_orientable_surface(&mut self) -> Result<Option<LocalId>, ParseError> {
let mut surface_id = None;
loop {
Expand Down
2 changes: 1 addition & 1 deletion nusamai-gltf/src/glb.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ pub struct Glb<'a> {
pub bin: Option<Cow<'a, [u8]>>,
}

impl<'a> Glb<'a> {
impl Glb<'_> {
/// Write GLB to writer.
pub fn to_writer<W: std::io::Write>(&self, writer: W) -> std::io::Result<()> {
self.to_writer_with_alignment(writer, 4)
Expand Down