Skip to content

Commit

Permalink
objectify citygml structs
Browse files Browse the repository at this point in the history
  • Loading branch information
ciscorn committed Dec 6, 2023
1 parent ce00a4a commit 8754746
Show file tree
Hide file tree
Showing 18 changed files with 185 additions and 45 deletions.
75 changes: 67 additions & 8 deletions nusamai-plateau/citygml/macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,25 @@ fn generate_citygml_struct_model(
) -> Result<TokenStream, Error> {
let mut attribute_arms = Vec::new();
let mut chlid_arms = Vec::new();
let mut objectify_stmts = Vec::new();
let mut geom_objectify_expr = quote! { None };
let mut id_value = quote!(None);

for field in &struct_data.fields {
let Some(field_ident) = &field.ident else {
continue;
};

if field_ident == "id" {
id_value = quote! {
if let Some(id) = &self.id {
Some(id.as_ref())
} else {
None
}
};
};

let field_ty = &field.ty;
for attr in &field.attrs {
if !attr.path().is_ident(CITYGML_ATTR_IDENT) {
Expand All @@ -30,21 +44,37 @@ fn generate_citygml_struct_model(
// XML attributes (e.g. @gml:id)
attribute_arms.push(quote! {
#path => {
self.id = <#field_ty as citygml::CityGMLAttribute>::parse_attr_value(
self.#field_ident = <#field_ty as citygml::CityGMLAttribute>::parse_attr_value(
std::str::from_utf8(value).unwrap(),
)?;
Ok(())
}
});
objectify_stmts.push(
quote! {
if let Some(v) = self.#field_ident.objectify() {
attributes.insert(stringify!(#field_ident).into(), v);
}
}
)
} else {
// XML child elements (e.g. bldg:measuredHeight)
chlid_arms.push(quote! {
#path => <#field_ty as CityGMLElement>::parse(&mut self.#field_ident, st),
});
chlid_arms.push(
quote! {
#path => <#field_ty as CityGMLElement>::parse(&mut self.#field_ident, st),
}
);
objectify_stmts.push(
quote! {
if let Some(v) = self.#field_ident.objectify() {
attributes.insert(stringify!(#field_ident).into(), v);
}
}
)
}
Ok(())
}
else if meta.path.is_ident("auto_geom") {
else if meta.path.is_ident("geom") {
let prefix: LitByteStr = meta.value()?.parse()?;

let mut add_arm = |lod: u8, name: &[u8], geomtype: &str | {
Expand All @@ -70,6 +100,10 @@ fn generate_citygml_struct_model(
add_arm(4, b"lod4Geometry", "Geometry");
add_arm(1, b"tin", "Triangulated");

geom_objectify_expr = quote! {
Some(&self.#field_ident)
};

Ok(())
} else {
Err(meta.error("unrecognized attribute"))
Expand Down Expand Up @@ -102,6 +136,20 @@ fn generate_citygml_struct_model(
}
})
}

fn objectify(&self) -> Option<::citygml::object::ObjectValue> {
let attributes = {
let mut attributes = ::std::collections::HashMap::new();
#(#objectify_stmts)*
attributes
};
Some(::citygml::ObjectValue::FeatureOrData {
typename: stringify!(#struct_name).into(),
id: #id_value,
attributes,
geometries: #geom_objectify_expr,
})
}
}
})
}
Expand All @@ -111,6 +159,8 @@ fn generate_citygml_enum_model(
enum_data: &DataEnum,
) -> Result<TokenStream, Error> {
let mut child_arms = Vec::new();
let mut objectify_arms = Vec::new();

for variant in &enum_data.variants {
if variant.fields.len() > 1 {
return Err(Error::new_spanned(
Expand All @@ -134,15 +184,17 @@ fn generate_citygml_enum_model(
let value = meta.value()?;
let path: LitByteStr = value.parse()?;

let arm = quote! {
child_arms.push(quote! {
#path => {
let mut v: #field_ty = Default::default();
<#field_ty as CityGMLElement>::parse(&mut v, st)?;
*self = Self::#variant_ident(v);
Ok(())
}
};
child_arms.push(arm);
});
objectify_arms.push(quote! {
Self::#variant_ident(v) => v.objectify()
});
}
Ok(())
})?;
Expand All @@ -162,6 +214,13 @@ fn generate_citygml_enum_model(
}
})
}

fn objectify(&self) -> Option<::citygml::object::ObjectValue> {
match self {
#(#objectify_arms,)*
_ => None,
}
}
}
};

Expand Down
2 changes: 2 additions & 0 deletions nusamai-plateau/citygml/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
pub mod geometric;
pub mod model;
pub mod namespace;
pub mod object;
pub mod parser;

pub use geometric::*;
pub use model::*;
pub use namespace::*;
pub use object::*;
pub use parser::*;

pub use macros::CityGMLElement;
55 changes: 54 additions & 1 deletion nusamai-plateau/citygml/src/model.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
use crate::object::ObjectValue;
use crate::parser::{ParseError, SubTreeReader};
use std::io::BufRead;

pub trait CityGMLElement: Sized {
fn parse<R: BufRead>(&mut self, st: &mut SubTreeReader<R>) -> Result<(), ParseError>;

fn objectify(&self) -> Option<ObjectValue>;
}

impl CityGMLElement for String {
Expand All @@ -11,9 +14,13 @@ impl CityGMLElement for String {
self.push_str(st.parse_text()?);
Ok(())
}

fn objectify(&self) -> Option<ObjectValue> {
Some(ObjectValue::String(self.as_ref()))
}

Check warning on line 20 in nusamai-plateau/citygml/src/model.rs

View check run for this annotation

Codecov / codecov/patch

nusamai-plateau/citygml/src/model.rs#L18-L20

Added lines #L18 - L20 were not covered by tests
}

impl CityGMLElement for i32 {
impl CityGMLElement for i64 {
#[inline]
fn parse<R: BufRead>(&mut self, st: &mut SubTreeReader<R>) -> Result<(), ParseError> {
let text = st.parse_text()?;
Expand All @@ -28,6 +35,31 @@ impl CityGMLElement for i32 {
))),
}
}

fn objectify(&self) -> Option<ObjectValue> {
Some(ObjectValue::Integer(*self))
}

Check warning on line 41 in nusamai-plateau/citygml/src/model.rs

View check run for this annotation

Codecov / codecov/patch

nusamai-plateau/citygml/src/model.rs#L39-L41

Added lines #L39 - L41 were not covered by tests
}

impl CityGMLElement for i8 {
#[inline]
fn parse<R: BufRead>(&mut self, st: &mut SubTreeReader<R>) -> Result<(), ParseError> {
let text = st.parse_text()?;
match text.parse() {
Ok(v) => {
*self = v;
Ok(())

Check warning on line 51 in nusamai-plateau/citygml/src/model.rs

View check run for this annotation

Codecov / codecov/patch

nusamai-plateau/citygml/src/model.rs#L46-L51

Added lines #L46 - L51 were not covered by tests
}
Err(_) => Err(ParseError::InvalidValue(format!(
"Expected an integer, got {}",
text
))),

Check warning on line 56 in nusamai-plateau/citygml/src/model.rs

View check run for this annotation

Codecov / codecov/patch

nusamai-plateau/citygml/src/model.rs#L53-L56

Added lines #L53 - L56 were not covered by tests
}
}

Check warning on line 58 in nusamai-plateau/citygml/src/model.rs

View check run for this annotation

Codecov / codecov/patch

nusamai-plateau/citygml/src/model.rs#L58

Added line #L58 was not covered by tests

fn objectify(&self) -> Option<ObjectValue> {
Some(ObjectValue::Integer(*self as i64))
}

Check warning on line 62 in nusamai-plateau/citygml/src/model.rs

View check run for this annotation

Codecov / codecov/patch

nusamai-plateau/citygml/src/model.rs#L60-L62

Added lines #L60 - L62 were not covered by tests
}

impl CityGMLElement for f64 {
Expand All @@ -45,6 +77,10 @@ impl CityGMLElement for f64 {
))),
}
}

fn objectify(&self) -> Option<ObjectValue> {
Some(ObjectValue::Double(*self))
}

Check warning on line 83 in nusamai-plateau/citygml/src/model.rs

View check run for this annotation

Codecov / codecov/patch

nusamai-plateau/citygml/src/model.rs#L81-L83

Added lines #L81 - L83 were not covered by tests
}

impl<T: CityGMLElement + Default + std::fmt::Debug> CityGMLElement for Option<T> {
Expand All @@ -61,6 +97,13 @@ impl<T: CityGMLElement + Default + std::fmt::Debug> CityGMLElement for Option<T>
*self = Some(v);
Ok(())
}

fn objectify(&self) -> Option<ObjectValue> {
match self {
Some(v) => v.objectify(),
None => None,

Check warning on line 104 in nusamai-plateau/citygml/src/model.rs

View check run for this annotation

Codecov / codecov/patch

nusamai-plateau/citygml/src/model.rs#L101-L104

Added lines #L101 - L104 were not covered by tests
}
}

Check warning on line 106 in nusamai-plateau/citygml/src/model.rs

View check run for this annotation

Codecov / codecov/patch

nusamai-plateau/citygml/src/model.rs#L106

Added line #L106 was not covered by tests
}

impl<T: CityGMLElement + Default> CityGMLElement for Vec<T> {
Expand All @@ -71,6 +114,16 @@ impl<T: CityGMLElement + Default> CityGMLElement for Vec<T> {
self.push(v);
Ok(())
}

fn objectify(&self) -> Option<ObjectValue> {
if self.is_empty() {
None

Check warning on line 120 in nusamai-plateau/citygml/src/model.rs

View check run for this annotation

Codecov / codecov/patch

nusamai-plateau/citygml/src/model.rs#L118-L120

Added lines #L118 - L120 were not covered by tests
} else {
Some(ObjectValue::Array(
self.iter().filter_map(|v| v.objectify()).collect(),
))

Check warning on line 124 in nusamai-plateau/citygml/src/model.rs

View check run for this annotation

Codecov / codecov/patch

nusamai-plateau/citygml/src/model.rs#L122-L124

Added lines #L122 - L124 were not covered by tests
}
}

Check warning on line 126 in nusamai-plateau/citygml/src/model.rs

View check run for this annotation

Codecov / codecov/patch

nusamai-plateau/citygml/src/model.rs#L126

Added line #L126 was not covered by tests
}

pub trait CityGMLAttribute: Sized {
Expand Down
25 changes: 25 additions & 0 deletions nusamai-plateau/citygml/src/object.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
//! Objectify CityGML features and their thematic/geometric attributes
use crate::geometric::GeometryRef;
use std::collections::HashMap;

#[derive(Debug)]

Check warning on line 6 in nusamai-plateau/citygml/src/object.rs

View check run for this annotation

Codecov / codecov/patch

nusamai-plateau/citygml/src/object.rs#L6

Added line #L6 was not covered by tests
pub enum ObjectValue<'a> {
Unknown,
Null,
String(&'a str),
// Code(&'a Code),
Integer(i64),
Double(f64),
Measure(f64),
Bool(bool),
URI(&'a str),
// Date(Date),
Array(Vec<ObjectValue<'a>>),
FeatureOrData {
typename: &'a str,
id: Option<&'a str>,
attributes: HashMap<String, ObjectValue<'a>>,
geometries: Option<&'a GeometryRef>,
},
}
1 change: 1 addition & 0 deletions nusamai-plateau/examples/parse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ fn example_toplevel_dispatcher<R: BufRead>(
let mut cityobj: CityObject = Default::default();
cityobj.parse(st)?;
let geometries = st.collect_geometries();
println!("{:?}", cityobj.objectify());

TopLevelCityObject {
cityobj,
Expand Down
2 changes: 1 addition & 1 deletion nusamai-plateau/src/models/bridge.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use citygml::{CityGMLElement, GeometryRef};
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Default, Debug, CityGMLElement)]
pub struct Bridge {
#[citygml(auto_geom = b"brid")]
#[citygml(geom = b"brid")]
pub geometries: GeometryRef,

#[citygml(path = b"@gml:id")]
Expand Down
Loading

0 comments on commit 8754746

Please sign in to comment.