Skip to content

Commit

Permalink
都市オブジェクトの構造体を "Objectify" する仕組み (#42)
Browse files Browse the repository at this point in the history
Rustの構造体として読み込まれたCityGMLの都市オプジェクトを、より汎化されたデータ型での表現(JSONのようなものをイメージしてもらえれば)に変換できるようにする。これによって後段での加工や、出力ドライバでの扱いができるようになる。

今後も色々とブラッシュアップしていくと思いますが、大枠はこれでいいのでいったんPR投げます。

<img width="484" alt="object"
src="https://github.com/MIERUNE/nusamai/assets/5351911/49fa57a7-1b6c-4c89-97ff-9b5904c23045">

Closes #40
  • Loading branch information
ciscorn authored Dec 7, 2023
1 parent 0873217 commit 61c81e8
Show file tree
Hide file tree
Showing 21 changed files with 478 additions and 146 deletions.
1 change: 1 addition & 0 deletions nusamai-plateau/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ default = ["serde"]
quick-xml = "0.31"
serde = { version = "1.0.193", features = ["derive"], optional = true }
citygml = { path = "./citygml", features = ["serde"]}
chrono = { version = "0.4.31", features = ["serde"] }

[dev-dependencies]
zstd = { version = "0.13.0", features = ["zdict_builder"] }
Expand Down
1 change: 1 addition & 0 deletions nusamai-plateau/citygml/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ default = ["serde"]
serde = ["dep:serde", "nusamai-geometry/serde"]

[dependencies]
chrono = "0.4.31"
indexmap = "2.1"
macros = { path = "./macros" }
nusamai-geometry = { path = "../../nusamai-geometry", features = ["serde"]}
Expand Down
77 changes: 69 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,22 @@ 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(
::citygml::FeatureOrData {
typename: stringify!(#struct_name).into(),
id: #id_value,
attributes,
geometries: #geom_objectify_expr,
}
))
}
}
})
}
Expand All @@ -111,6 +161,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 +186,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 +216,13 @@ fn generate_citygml_enum_model(
}
})
}

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

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

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

pub use macros::CityGMLElement;

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

fn objectify(&self) -> Option<ObjectValue>;
}
92 changes: 0 additions & 92 deletions nusamai-plateau/citygml/src/model.rs

This file was deleted.

29 changes: 29 additions & 0 deletions nusamai-plateau/citygml/src/object.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
//! Objectify CityGML features and their thematic/geometric attributes
use crate::geometric::GeometryRef;
use crate::values::{Code, Point, URI};
use chrono::NaiveDate;
use std::collections::HashMap;

#[derive(Debug)]
pub struct FeatureOrData<'a> {
pub typename: &'a str,
pub id: Option<&'a str>,
pub attributes: HashMap<String, ObjectValue<'a>>,
pub geometries: Option<&'a GeometryRef>,
}

#[derive(Debug)]
pub enum ObjectValue<'a> {
String(&'a str),
Code(&'a Code),
Integer(i64),
Double(f64),
Measure(f64),
Boolean(bool),
URI(&'a URI),
Date(&'a NaiveDate),
Point(&'a Point),
Array(Vec<ObjectValue<'a>>),
FeatureOrData(FeatureOrData<'a>),
}
Loading

0 comments on commit 61c81e8

Please sign in to comment.