use cjseq::{CityJSON, CityJSONFeature, Transform};
use std::path::PathBuf;
use crate::reader::read_input_file;
use crate::CliError;
pub struct MergeResult {
pub metadata: CityJSON,
pub features: Vec<CityJSONFeature>,
}
pub fn merge_files(paths: Vec<PathBuf>) -> Result<MergeResult, CliError> {
if paths.is_empty() {
return Err(CliError::NoInputFiles);
}
let mut paths_iter = paths.into_iter();
let first_path = paths_iter.next().ok_or(CliError::NoInputFiles)?;
let first_data = read_input_file(&first_path)?;
let reference_transform = first_data.metadata.transform.clone();
let mut result = MergeResult {
metadata: first_data.metadata,
features: first_data.features,
};
for path in paths_iter {
let data = read_input_file(&path)?;
if transforms_equal(&data.metadata.transform, &reference_transform) {
result.features.extend(data.features);
} else {
for feature in data.features {
let converted = convert_feature_transform(
feature,
&data.metadata.transform,
&reference_transform,
);
result.features.push(converted);
}
}
}
Ok(result)
}
fn transforms_equal(a: &Transform, b: &Transform) -> bool {
a.scale == b.scale && a.translate == b.translate
}
fn convert_feature_transform(
mut feature: CityJSONFeature,
source: &Transform,
target: &Transform,
) -> CityJSONFeature {
for vertex in &mut feature.vertices {
if vertex.len() >= 3 {
let real_x = (vertex[0] as f64 * source.scale[0]) + source.translate[0];
let real_y = (vertex[1] as f64 * source.scale[1]) + source.translate[1];
let real_z = (vertex[2] as f64 * source.scale[2]) + source.translate[2];
vertex[0] = ((real_x - target.translate[0]) / target.scale[0]).round() as i64;
vertex[1] = ((real_y - target.translate[1]) / target.scale[1]).round() as i64;
vertex[2] = ((real_z - target.translate[2]) / target.scale[2]).round() as i64;
}
}
feature
}
#[cfg(test)]
mod tests {
use super::*;
fn make_transform(scale: [f64; 3], translate: [f64; 3]) -> Transform {
Transform {
scale: scale.to_vec(),
translate: translate.to_vec(),
}
}
#[test]
fn test_transforms_equal() {
let t1 = make_transform([0.001, 0.001, 0.001], [0.0, 0.0, 0.0]);
let t2 = make_transform([0.001, 0.001, 0.001], [0.0, 0.0, 0.0]);
let t3 = make_transform([0.001, 0.001, 0.001], [1.0, 0.0, 0.0]);
assert!(transforms_equal(&t1, &t2));
assert!(!transforms_equal(&t1, &t3));
}
#[test]
fn test_convert_feature_transform_identity() {
let source = make_transform([0.001, 0.001, 0.001], [0.0, 0.0, 0.0]);
let target = source.clone();
let mut feature = CityJSONFeature::new();
feature.vertices = vec![vec![1000, 2000, 3000]];
let converted = convert_feature_transform(feature.clone(), &source, &target);
assert_eq!(converted.vertices[0], vec![1000, 2000, 3000]);
}
#[test]
fn test_convert_feature_transform_different() {
let source = make_transform([0.001, 0.001, 0.001], [0.0, 0.0, 0.0]);
let target = make_transform([0.002, 0.002, 0.002], [0.0, 0.0, 0.0]);
let mut feature = CityJSONFeature::new();
feature.vertices = vec![vec![1000, 2000, 3000]];
let converted = convert_feature_transform(feature, &source, &target);
assert_eq!(converted.vertices[0], vec![500, 1000, 1500]);
}
#[test]
fn test_convert_feature_transform_with_translate() {
let source = make_transform([0.001, 0.001, 0.001], [100.0, 200.0, 0.0]);
let target = make_transform([0.001, 0.001, 0.001], [0.0, 0.0, 0.0]);
let mut feature = CityJSONFeature::new();
feature.vertices = vec![vec![0, 0, 0]];
let converted = convert_feature_transform(feature, &source, &target);
assert_eq!(converted.vertices[0], vec![100000, 200000, 0]);
}
}