use crate::{CityJSON, CityJSONFeature, Geometry};
use std::fs::File;
use std::io::{Result as IoResult, Write};
use std::path::Path;
pub fn to_obj_string(city_json: &CityJSON) -> String {
let mut output = Vec::new();
to_obj(city_json, &mut output).unwrap();
String::from_utf8(output).unwrap()
}
pub fn to_obj_file(city_json: &CityJSON, path: impl AsRef<Path>) -> IoResult<()> {
let mut file = File::create(path)?;
to_obj(city_json, &mut file)
}
pub fn to_obj<W: Write>(city_json: &CityJSON, writer: &mut W) -> IoResult<()> {
writeln!(writer, "# Converted from CityJSON to OBJ")?;
writeln!(writer, "# by CJSeq converter")?;
writeln!(writer)?;
let scale = &city_json.transform.scale;
let translate = &city_json.transform.translate;
for vertex in &city_json.vertices {
let x = (vertex[0] as f64 * scale[0]) + translate[0];
let y = (vertex[1] as f64 * scale[1]) + translate[1];
let z = (vertex[2] as f64 * scale[2]) + translate[2];
writeln!(writer, "v {} {} {}", x, y, z)?;
}
writeln!(writer)?;
for (_id, city_object) in &city_json.city_objects {
if let Some(geometries) = &city_object.geometry {
let highest_lod_geometry = find_highest_lod_geometry(geometries);
for geometry in highest_lod_geometry {
convert_geometry_to_obj(&geometry.boundaries, writer)?;
}
}
}
Ok(())
}
pub fn jsonseq_file_to_obj(path: impl AsRef<Path>, output_path: impl AsRef<Path>) -> IoResult<()> {
use std::io::{BufRead, BufReader};
let f = File::open(path)?;
let br = BufReader::new(f);
let mut cjj = CityJSON::new();
for (i, line) in br.lines().enumerate() {
let l = line?;
if i == 0 {
cjj = CityJSON::from_str(&l)
.map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e))?;
} else {
let mut cjf = CityJSONFeature::from_str(&l)
.map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e))?;
cjj.add_cjfeature(&mut cjf);
}
}
cjj.remove_duplicate_vertices();
cjj.update_transform();
to_obj_file(&cjj, output_path)
}
fn find_highest_lod_geometry(geometries: &[Geometry]) -> Vec<&Geometry> {
let mut max_lod: Option<f64> = None;
for geometry in geometries {
if let Some(lod_str) = &geometry.lod {
if let Ok(lod) = lod_str.parse::<f64>() {
max_lod = Some(max_lod.map_or(lod, |max| max.max(lod)));
}
}
}
if max_lod.is_none() {
return geometries.iter().collect();
}
let max_lod_value = max_lod.unwrap();
geometries
.iter()
.filter(|g| {
if let Some(lod_str) = &g.lod {
if let Ok(lod) = lod_str.parse::<f64>() {
return (lod - max_lod_value).abs() < f64::EPSILON;
}
}
false
})
.collect()
}
fn convert_geometry_to_obj<W: Write>(
boundaries: &crate::Boundaries,
writer: &mut W,
) -> IoResult<()> {
match boundaries {
crate::Boundaries::Indices(indices) => {
write_obj_face(indices, writer)?;
}
crate::Boundaries::Nested(nested) => {
for boundary in nested {
convert_geometry_to_obj(boundary, writer)?;
}
}
}
Ok(())
}
fn write_obj_face<W: Write>(indices: &[u32], writer: &mut W) -> IoResult<()> {
if indices.is_empty() {
return Ok(());
}
write!(writer, "f")?;
for idx in indices {
write!(writer, " {}", idx + 1)?; }
writeln!(writer)?;
Ok(())
}