Skip to content

Commit

Permalink
CityGMLのObject表現周りをいくらか整理 (#134)
Browse files Browse the repository at this point in the history
大きな変更点:

- TopLevelCityObject の名前を CityObject に変更する
    - Transformerで地物ツリーを展開して後段に送る可能性もあるため
- Feature (idやジオメトリをもつもの) のほかに Data (idやジオメトリをもたないもの) を追加
    - #114

その他細かい改善。

Closes: #114
  • Loading branch information
ciscorn authored Dec 28, 2023
1 parent 8133013 commit bc431bf
Show file tree
Hide file tree
Showing 23 changed files with 370 additions and 290 deletions.
8 changes: 4 additions & 4 deletions app/src-tauri/src/example.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,16 @@
//! nusamai-geojson の exmaple/gml2geojson を元にした、暫定的な処理
use nusamai::sink::geojson::toplevel_cityobj_to_geojson_features;
use nusamai_citygml::object::CityObject;
use nusamai_citygml::{CityGMLElement, CityGMLReader, ParseError, SubTreeReader};
use nusamai_plateau::TopLevelCityObject;
use std::fs;
use std::io::BufRead;
use std::io::BufWriter;

fn toplevel_dispatcher<R: BufRead>(
st: &mut SubTreeReader<R>,
) -> Result<Vec<TopLevelCityObject>, ParseError> {
let mut cityobjs: Vec<TopLevelCityObject> = vec![];
) -> Result<Vec<CityObject>, ParseError> {
let mut cityobjs: Vec<CityObject> = vec![];

match st.parse_children(|st| match st.current_path() {
b"core:cityObjectMember" => {
Expand All @@ -20,7 +20,7 @@ fn toplevel_dispatcher<R: BufRead>(
let geometries = st.collect_geometries();

if let Some(root) = cityobj.into_object() {
let obj = TopLevelCityObject { root, geometries };
let obj = CityObject { root, geometries };
cityobjs.push(obj);
}

Expand Down
4 changes: 2 additions & 2 deletions nusamai-citygml/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ edition = "2021"

[features]
default = ["serde"]
serde = ["dep:serde", "nusamai-geometry/serde"]
serde = ["dep:serde", "serde_json", "nusamai-geometry/serde"]

[dependencies]
chrono = { version = "0.4.31", features = ["serde"], default-features = false }
Expand All @@ -14,6 +14,6 @@ macros = { path = "./macros" }
nusamai-geometry = { path = "../nusamai-geometry", features = ["serde"]}
quick-xml = "0.31"
serde = { version = "1.0", features = ["derive"], optional = true }
serde_json = "1.0.108"
serde_json = { version = "1.0.108", optional = true }
thiserror = "1.0"
url = "2.5.0"
10 changes: 10 additions & 0 deletions nusamai-citygml/macros/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,10 +102,20 @@ pub enum FooBarProperty {
}
```


## Derive macros

### `#[derive(CityGMLElement)]`

It automatically implements the `CityGMLElement` trait for the target struct/enum, enabling it to parse corresponding CityGML fragments.

In most cases, you should use the attribute macros above instead of directly applying this derive macro.


## Check the result of macro expansion

```bash
cargo install cargo-expand
cd ./nusamai-plateau/
cargo expand models::building --no-default-features
```
84 changes: 63 additions & 21 deletions nusamai-citygml/macros/src/derive.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
//! CityGMLElement derive macro
extern crate proc_macro;

use proc_macro2::TokenStream;
use proc_macro2::{Ident, TokenStream};
use quote::{format_ident, quote};
use syn::{parse_macro_input, Data, DataEnum, DataStruct, DeriveInput, Error, LitByteStr, LitStr};

use crate::ElementType;

const CITYGML_ATTR_IDENT: &str = "citygml";

pub(crate) fn derive_citygml_element(token: proc_macro::TokenStream) -> proc_macro::TokenStream {
Expand Down Expand Up @@ -38,6 +38,7 @@ fn generate_citygml_impl_for_struct(
let mut id_value = quote!(None);
let struct_ident = &derive_input.ident;
let mut typename = String::from(stringify!(derive_input.ident));
let mut ty = ElementType::Feature;

for attr in &derive_input.attrs {
if !attr.path().is_ident(CITYGML_ATTR_IDENT) {
Expand All @@ -48,6 +49,16 @@ fn generate_citygml_impl_for_struct(
let name: LitStr = meta.value()?.parse()?;
typename = name.value();
}
if meta.path.is_ident("type") {
let ty_ident: Ident = meta.value()?.parse()?;
ty = match ty_ident.to_string().as_str() {
"feature" => ElementType::Feature,
"data" => ElementType::Data,
_ => {
return Err(meta.error("feature or data expected"));
}
};
}
Ok(())
})?;
}
Expand Down Expand Up @@ -175,8 +186,50 @@ fn generate_citygml_impl_for_struct(
}
});

let into_object_impl = match ty {
ElementType::Feature => {
quote! {
Some(::nusamai_citygml::object::Value::Feature(
::nusamai_citygml::object::Feature {
typename: #typename.into(),
id: #id_value,
attributes: {
let mut attributes = ::std::collections::HashMap::new();
#(#into_object_stmts)*
attributes
},
geometries: #geom_into_object_expr,
}
))
}
}
ElementType::Data => {
quote! {
Some(::nusamai_citygml::object::Value::Data(
::nusamai_citygml::object::Data {
typename: #typename.into(),
attributes: {
let mut attributes = ::std::collections::HashMap::new();
#(#into_object_stmts)*
attributes
},
}
))
}
}
_ => unreachable!(),
};

let element_type = match ty {
ElementType::Feature => quote! { ::nusamai_citygml::ElementType::FeatureType },
ElementType::Data => quote! { ::nusamai_citygml::ElementType::DataType },
_ => unreachable!(),
};

Ok(quote! {
impl #impl_generics ::nusamai_citygml::CityGMLElement for #struct_ident #ty_generics #where_clause {
const ELEMENT_TYPE: ::nusamai_citygml::ElementType = #element_type;

fn parse<R: std::io::BufRead>(&mut self, st: &mut ::nusamai_citygml::SubTreeReader<R>) -> Result<(), ::nusamai_citygml::ParseError> {
#attr_parsing

Expand All @@ -188,19 +241,8 @@ fn generate_citygml_impl_for_struct(
})
}

fn into_object(self) -> Option<::nusamai_citygml::object::ObjectValue> {
Some(::nusamai_citygml::ObjectValue::FeatureOrData(
::nusamai_citygml::FeatureOrData {
typename: #typename.into(),
id: #id_value,
attributes: {
let mut attributes = ::std::collections::HashMap::new();
#(#into_object_stmts)*
attributes
},
geometries: #geom_into_object_expr,
}
))
fn into_object(self) -> Option<::nusamai_citygml::object::Value> {
#into_object_impl
}
}
})
Expand Down Expand Up @@ -257,8 +299,10 @@ fn generate_citygml_impl_for_enum(
let (impl_generics, ty_generics, where_clause) = &derive_input.generics.split_for_impl();
let struct_name = &derive_input.ident;

let tokens = quote! {
Ok(quote! {
impl #impl_generics ::nusamai_citygml::CityGMLElement for #struct_name #ty_generics #where_clause {
const ELEMENT_TYPE: ::nusamai_citygml::ElementType = ::nusamai_citygml::ElementType::PropertyType;

fn parse<R: ::std::io::BufRead>(&mut self, st: &mut ::nusamai_citygml::SubTreeReader<R>) -> Result<(), ::nusamai_citygml::ParseError> {
st.parse_children(|st| {
match st.current_path() {
Expand All @@ -268,14 +312,12 @@ fn generate_citygml_impl_for_enum(
})
}

fn into_object(self) -> Option<::nusamai_citygml::object::ObjectValue> {
fn into_object(self) -> Option<::nusamai_citygml::object::Value> {
match self {
#(#into_object_arms,)*
_ => None,
}
}
}
};

Ok(tokens)
})
}
11 changes: 8 additions & 3 deletions nusamai-citygml/macros/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
extern crate proc_macro;

use proc_macro::TokenStream;

mod derive;
mod type_attrs;

use type_attrs::ElementType;
use proc_macro::TokenStream;

#[proc_macro_derive(CityGMLElement, attributes(citygml))]
pub fn derive_citygml_element(token: TokenStream) -> TokenStream {
Expand All @@ -26,3 +24,10 @@ pub fn citygml_data(args: TokenStream, input: TokenStream) -> TokenStream {
pub fn citygml_property(args: TokenStream, input: TokenStream) -> TokenStream {
type_attrs::citygml_type(ElementType::Property, args, input)
}

#[derive(Clone, Copy)]
pub(crate) enum ElementType {
Feature,
Data,
Property,
}
29 changes: 17 additions & 12 deletions nusamai-citygml/macros/src/type_attrs.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
extern crate proc_macro;

use crate::ElementType;
use proc_macro2::TokenStream;
use quote::quote;
use syn::meta::ParseNestedMeta;
use syn::{parse::Parser, parse_macro_input, Data, DeriveInput, Error, LitStr};
use syn::{parse_quote, LitByteStr};

/// Arguments for `#[citygml_feature(...)]` and `#[citygml_data(...)]`
#[derive(Default)]
struct FeatureArgs {
name: Option<LitStr>, // "bldg:Building"
Expand All @@ -28,13 +28,6 @@ impl FeatureArgs {
}
}

#[derive(Clone, Copy)]
pub(crate) enum ElementType {
Feature,
Data,
Property,
}

pub(crate) fn citygml_type(
ty: ElementType,
args: proc_macro::TokenStream,
Expand Down Expand Up @@ -74,9 +67,21 @@ fn modify(ty: &ElementType, args: &FeatureArgs, input: &mut DeriveInput) -> Resu
None => return Err(Error::new_spanned(input, "name is required")),
};

input.attrs.push(match &ty {
ElementType::Feature => {
syn::parse_quote! { #[citygml(type = feature)] }
}
ElementType::Data => {
syn::parse_quote! { #[citygml(type = data)] }
}
ElementType::Property => {
syn::parse_quote! { #[citygml(type = property)] }
}
});

match &mut input.data {
Data::Struct(data) => {
// #[citygml_feature], #[citygml_data]
// for #[citygml_feature] and #[citygml_data]

match ty {
ElementType::Feature | ElementType::Data => {}
Expand All @@ -85,7 +90,7 @@ fn modify(ty: &ElementType, args: &FeatureArgs, input: &mut DeriveInput) -> Resu

if let syn::Fields::Named(ref mut fields) = data.fields {
if let ElementType::Feature = ty {
// #[citygml_feature]
// for #[citygml_feature]

let prefix = args.prefix.as_ref().unwrap();
add_named_field(
Expand Down Expand Up @@ -157,7 +162,7 @@ fn modify(ty: &ElementType, args: &FeatureArgs, input: &mut DeriveInput) -> Resu
}
Data::Enum(_data) => match ty {
ElementType::Property => {
// #[citygml_property]
// for #[citygml_property]
_data.variants.push(parse_quote! {
#[default]
Unknown
Expand Down
1 change: 0 additions & 1 deletion nusamai-citygml/src/geometry.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use nusamai_geometry::{MultiLineString, MultiPoint, MultiPolygon};

#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
#[derive(Debug, Clone, Copy)]
pub enum GeometryParseType {
Geometry,
Expand Down
17 changes: 14 additions & 3 deletions nusamai-citygml/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,26 @@ pub mod parser;
pub mod values;

pub use geometry::*;
pub use macros::*;
pub use namespace::*;
pub use object::*;
pub use parser::*;
pub use values::*;

pub use macros::*;
pub use object::Value;

pub enum ElementType {
BasicType,
FeatureType,
DataType,
PropertyType,
}

pub trait CityGMLElement: Sized {
const ELEMENT_TYPE: ElementType;

/// Parse a XML fragment into this element.
fn parse<R: std::io::BufRead>(&mut self, st: &mut SubTreeReader<R>) -> Result<(), ParseError>;

fn into_object(self) -> Option<ObjectValue>;
/// Convert this element to a `Value` object representation.
fn into_object(self) -> Option<object::Value>;
}
Loading

0 comments on commit bc431bf

Please sign in to comment.