Skip to content

Commit

Permalink
transformer: ネストされたフィールドを Jsonify する(とりあえずの実装) (#270)
Browse files Browse the repository at this point in the history
`object::Value` のうち Object と Arrayを Jsonify する Transform を追加します。(Object
または Array になりうるフィールドは、スキーマ上で JsonString に変更されます)。

この Transform はデフォルトで適用されます。
  • Loading branch information
ciscorn authored Feb 12, 2024
1 parent b5b5dbc commit 3d9c299
Show file tree
Hide file tree
Showing 31 changed files with 494 additions and 400 deletions.
4 changes: 2 additions & 2 deletions nusamai-citygml/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ default = ["serde"]
serde = ["dep:serde", "serde_json", "nusamai-geometry/serde"]

[dependencies]
ahash = "0.8.7"
chrono = { version = "0.4.31", features = ["serde"], default-features = false }
ahash = "0.8.8"
chrono = { version = "0.4.34", features = ["serde"], default-features = false }
indexmap = { version = "2.1", features = ["serde"] }
log = "0.4.20"
macros = { path = "./macros" }
Expand Down
116 changes: 116 additions & 0 deletions nusamai-citygml/src/values.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ pub type MeasureOrNullList = String; // TODO?
pub type BuildingLODType = String; // TODO?
pub type DoubleList = String; // TODO?
pub type LODType = u64; // TODO?
pub type Double01 = f64; // TODO?

impl CityGmlElement for String {
#[inline]
Expand Down Expand Up @@ -367,6 +368,121 @@ impl CityGmlAttribute for LocalId {
}
}

#[derive(Debug, Default, Clone, Copy, serde::Serialize, serde::Deserialize)]
pub struct Color {
pub r: f64,
pub g: f64,
pub b: f64,
}

impl Color {
pub fn new(r: f64, g: f64, b: f64) -> Self {
Self { r, g, b }
}
}

impl std::hash::Hash for Color {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.r.to_bits().hash(state);
self.g.to_bits().hash(state);
self.b.to_bits().hash(state);
}
}

impl CityGmlElement for Color {
fn parse<R: BufRead>(&mut self, st: &mut SubTreeReader<R>) -> Result<(), ParseError> {
let text = st.parse_text()?;
let r: Result<Vec<_>, _> = text
.split_ascii_whitespace()
.map(|s| s.parse::<f64>())
.collect();
match r {
Ok(v) if v.len() == 3 => {
(self.r, self.g, self.b) = (v[0], v[1], v[2]);
}
_ => {
return Err(ParseError::InvalidValue(format!(
"Failed to parse color value: {}",
text
)))
}
}
Ok(())
}

fn into_object(self) -> Option<Value> {
Some(Value::Array(vec![
Value::Double(self.r),
Value::Double(self.g),
Value::Double(self.b),
]))
}

fn collect_schema(_schema: &mut schema::Schema) -> schema::Attribute {
schema::Attribute {
type_ref: schema::TypeRef::Double,
min_occurs: 3,
max_occurs: Some(3),
}
}
}

#[derive(Debug, Default, Clone, Copy, serde::Serialize, serde::Deserialize)]
pub struct ColorPlusOpacity {
pub r: Double01,
pub g: Double01,
pub b: Double01,
pub a: Double01,
}

impl ColorPlusOpacity {
pub fn new(r: f64, g: f64, b: f64, a: f64) -> Self {
Self { r, g, b, a }
}
}

impl CityGmlElement for ColorPlusOpacity {
fn parse<R: BufRead>(&mut self, st: &mut SubTreeReader<R>) -> Result<(), ParseError> {
let text = st.parse_text()?;
let r: Result<Vec<_>, _> = text
.split_ascii_whitespace()
.map(|s| s.parse::<f64>())
.collect();
match r {
Ok(v) if v.len() == 3 => {
(self.r, self.g, self.b, self.a) = (v[0], v[1], v[2], 1.0);
}
Ok(v) if v.len() == 4 => {
(self.r, self.g, self.b, self.a) = (v[0], v[1], v[2], v[3]);
}
_ => {
return Err(ParseError::InvalidValue(format!(
"Failed to parse color value: {}",
text
)))
}
}
Ok(())
}

fn into_object(self) -> Option<Value> {
Some(Value::Array(vec![
Value::Double(self.r),
Value::Double(self.g),
Value::Double(self.b),
Value::Double(self.a),
]))
}

fn collect_schema(_schema: &mut schema::Schema) -> schema::Attribute {
schema::Attribute {
type_ref: schema::TypeRef::Double,
min_occurs: 4,
max_occurs: Some(4),
}
}
}

impl<T: CityGmlElement + Default + std::fmt::Debug> CityGmlElement for Option<T> {
#[inline]
fn parse<R: BufRead>(&mut self, st: &mut SubTreeReader<R>) -> Result<(), ParseError> {
Expand Down
2 changes: 1 addition & 1 deletion nusamai-czml/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ version = "0.1.0"
edition = "2021"

[dependencies]
chrono = { version = "0.4.33", features = ["serde"] }
chrono = { version = "0.4.34", features = ["serde"] }
serde = { version = "1.0.195", features = ["derive"] }
serde_json = { version = "1.0.111", features = ["float_roundtrip"] }
nusamai-geometry = { path = "../nusamai-geometry" }
Expand Down
4 changes: 2 additions & 2 deletions nusamai-geometry/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@ version = "0.1.0"
edition = "2021"

[dependencies]
num-traits = "0.2.17"
num-traits = "0.2.18"
serde = { version = "1.0.193", features = ["derive"], optional = true }

[dev-dependencies]
byteorder = "1.5.0"
geo-types = "0.7.11"
geojson = "0.24.1"
indexmap = "2.1.0"
indexmap = "2.2.3"
quick-xml = "0.31.0"
thiserror = "1.0.50"
earcut-rs = { git = "https://github.com/MIERUNE/earcut-rs.git" }
Expand Down
2 changes: 1 addition & 1 deletion nusamai-gltf/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,6 @@ nusamai-geometry = { path = "../nusamai-geometry" }
quick-xml = "0.31.0"
thiserror = "1.0.50"
earcut-rs = { git = "https://github.com/MIERUNE/earcut-rs.git" }
indexmap = "2.1.0"
indexmap = "2.2.3"
byteorder = "1.5.0"
serde_json = "1.0.113"
2 changes: 1 addition & 1 deletion nusamai-gpkg/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,4 @@ url = "2.5.0"
indexmap = "2.2.2"

[dev-dependencies]
tokio = { version = "1.35.1", features = ["full"] }
tokio = { version = "1.36", features = ["full"] }
4 changes: 2 additions & 2 deletions nusamai-mvt/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ version = "0.1.0"
edition = "2021"

[dependencies]
ahash = "0.8.7"
indexmap = "2.1.0"
ahash = "0.8.8"
indexmap = "2.2.3"
prost = "0.12.3"

[build-dependencies]
Expand Down
5 changes: 3 additions & 2 deletions nusamai-plateau/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,12 @@ quick-xml = "0.31.0"
serde = { version = "1.0.193", features = ["derive"], optional = true }
nusamai-citygml = { path = "../nusamai-citygml", features = ["serde"]}
nusamai-geometry = { path = "../nusamai-geometry" }
chrono = { version = "0.4.31", features = ["serde"], default-features = false }
chrono = { version = "0.4.34", features = ["serde"], default-features = false }
url = "2.5.0"
stretto = "0.8.2"
stretto = { git = "https://github.com/ciscorn/stretto.git" , branch = "update-wg" }
hashbrown = "0.14.3"
indexmap = "2.2.2"
log = "0.4.20"

[dev-dependencies]
zstd = { version = "0.13.0", features = ["zdict_builder"] }
Expand Down
110 changes: 88 additions & 22 deletions nusamai-plateau/src/appearance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,76 @@
use crate::models::appearance::{self, ParameterizedTexture, SurfaceDataProperty, X3DMaterial};
use hashbrown::HashMap;
use nusamai_citygml::{appearance::TextureAssociation, LocalId, SurfaceSpan};
use nusamai_citygml::{appearance::TextureAssociation, Color, LocalId, SurfaceSpan, URI};
use nusamai_geometry::LineString2;
use std::hash::{Hash, Hasher};

#[derive(Debug, Default, serde::Serialize, serde::Deserialize)]
pub struct Theme {
ring_id_to_texture: HashMap<LocalId, (u32, LineString2<'static>)>,
surface_id_to_material: HashMap<LocalId, u32>,
}

/// Material (CityGML's X3DMaterial)
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct Material {
pub diffuse_color: Color,
pub specular_color: Color,
pub ambient_intensity: f64,
// TOOD: other parameters
}

impl From<X3DMaterial> for Material {
fn from(src: X3DMaterial) -> Self {
Self {
diffuse_color: src.diffuse_color.unwrap_or(Color::new(0.8, 0.8, 0.8)),
specular_color: src.specular_color.unwrap_or(Color::new(1., 1., 1.)),
ambient_intensity: src.ambient_intensity.unwrap_or(0.2),
}
}
}

impl Default for Material {
fn default() -> Self {
Self {
diffuse_color: Color::new(0.8, 0.8, 0.8),
specular_color: Color::new(1., 1., 1.),
ambient_intensity: 0.2,
}
}
}

impl Hash for Material {
fn hash<H: Hasher>(&self, state: &mut H) {
self.diffuse_color.hash(state);
self.specular_color.hash(state);
self.ambient_intensity.to_bits().hash(state);
}
}
#[derive(Debug, Default, serde::Serialize, serde::Deserialize)]
pub struct AppearanceStore {
textures: Vec<ParameterizedTexture>,
materials: Vec<X3DMaterial>,
textures: Vec<Texture>,
materials: Vec<Material>,
themes: HashMap<String, Theme>,
}

#[derive(Debug, Default, serde::Serialize, serde::Deserialize)]
pub struct Theme {
ring_id_to_texture: HashMap<LocalId, (u32, LineString2<'static>)>,
surface_id_to_material: HashMap<LocalId, u32>,
/// Texture (CityGML's ParameterizedTexture)
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct Texture {
pub image_uri: String,
// TOOD: other parameters
}

impl From<ParameterizedTexture> for Texture {
fn from(src: ParameterizedTexture) -> Self {
let url = src.image_uri.unwrap_or_else(|| {
log::warn!("image_uri is not set");
URI::new("url_not_found.jpg")
});
Self {
image_uri: url.value().to_string(),
}
}
}

impl AppearanceStore {
Expand All @@ -37,14 +93,14 @@ impl AppearanceStore {
}
}
}
self.textures.push(texture);
self.textures.push(texture.into());
}
SurfaceDataProperty::X3DMaterial(mut material) => {
let mat_idx = self.materials.len() as u32;
for target in material.target.drain(..) {
theme.surface_id_to_material.insert(target, mat_idx);
}
self.materials.push(material);
self.materials.push(material.into());
}
_ => unimplemented!(),
}
Expand Down Expand Up @@ -125,17 +181,21 @@ mod tests {

#[test]
fn merge_appearance() {
use crate::models::appearance::{ParameterizedTexture, X3DMaterial};

let mut app_local = AppearanceStore::default();
let mut app_global = AppearanceStore::default();

{
app_local.textures.push(ParameterizedTexture::default());
app_local.textures.push(ParameterizedTexture::default());
app_local.textures.push(ParameterizedTexture::default());
app_local.materials.push(X3DMaterial::default());
app_local.materials.push(X3DMaterial::default());
app_local.textures.push(Texture {
image_uri: "local1.jpg".to_string(),
});
app_local.textures.push(Texture {
image_uri: "local2.jpg".to_string(),
});
app_local.textures.push(Texture {
image_uri: "local3.jpg".to_string(),
});
app_local.materials.push(Material::default());
app_local.materials.push(Material::default());
let theme = app_local.themes.entry("default".to_string()).or_default();
theme
.ring_id_to_texture
Expand All @@ -152,12 +212,18 @@ mod tests {
}

{
app_global.textures.push(ParameterizedTexture::default());
app_global.textures.push(ParameterizedTexture::default());
app_global.textures.push(ParameterizedTexture::default());
app_global.materials.push(X3DMaterial::default());
app_global.materials.push(X3DMaterial::default());
app_global.materials.push(X3DMaterial::default());
app_global.textures.push(Texture {
image_uri: "global1.jpg".to_string(),
});
app_global.textures.push(Texture {
image_uri: "global2.jpg".to_string(),
});
app_global.textures.push(Texture {
image_uri: "global3.jpg".to_string(),
});
app_global.materials.push(Material::default());
app_global.materials.push(Material::default());
app_global.materials.push(Material::default());
let theme = app_global.themes.entry("default".to_string()).or_default();
theme
.ring_id_to_texture
Expand Down
Loading

0 comments on commit 3d9c299

Please sign in to comment.