use std::fs;
use std::path::Path;
use crate::data::{CellArray, Points, PolyData};
use crate::types::VtkError;
pub fn read_citygml(path: &Path) -> Result<PolyData, VtkError> {
let content = fs::read_to_string(path)?;
parse_citygml_string(&content)
}
pub fn parse_citygml_string(xml: &str) -> Result<PolyData, VtkError> {
let mut points = Points::<f64>::new();
let mut polys = CellArray::new();
let pos_list_start = "gml:posList";
let pos_list_end_tag = "</gml:posList>";
let mut search_from = 0;
while let Some(tag_start) = xml[search_from..].find(&format!("<{pos_list_start}")) {
let abs_start = search_from + tag_start;
let content_start = match xml[abs_start..].find('>') {
Some(i) => abs_start + i + 1,
None => break,
};
let content_end = match xml[content_start..].find(pos_list_end_tag) {
Some(i) => content_start + i,
None => break,
};
let text = xml[content_start..content_end].trim();
let coords = parse_pos_list(text);
if coords.len() >= 3 {
let _base = points.len() as i64;
let mut cell_ids = Vec::new();
for pt in &coords {
cell_ids.push(points.len() as i64);
points.push(*pt);
}
if coords.len() > 1 && coords.first() == coords.last() {
cell_ids.pop();
}
if cell_ids.len() >= 3 {
polys.push_cell(&cell_ids);
}
}
search_from = content_end + pos_list_end_tag.len();
}
let mut pd = PolyData::new();
pd.points = points;
pd.polys = polys;
Ok(pd)
}
fn parse_pos_list(text: &str) -> Vec<[f64; 3]> {
let nums: Vec<f64> = text
.split_whitespace()
.filter_map(|s| s.parse::<f64>().ok())
.collect();
nums.chunks_exact(3)
.map(|c| [c[0], c[1], c[2]])
.collect()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parse_inline_citygml() {
let xml = r#"<?xml version="1.0" encoding="UTF-8"?>
<CityModel xmlns:bldg="http://www.opengis.net/citygml/building/2.0"
xmlns:gml="http://www.opengis.net/gml">
<cityObjectMember>
<bldg:Building gml:id="b1">
<bldg:lod1Solid>
<gml:Solid>
<gml:exterior>
<gml:CompositeSurface>
<gml:surfaceMember>
<gml:Polygon>
<gml:exterior>
<gml:LinearRing>
<gml:posList>
0.0 0.0 0.0 10.0 0.0 0.0 10.0 10.0 0.0 0.0 10.0 0.0 0.0 0.0 0.0
</gml:posList>
</gml:LinearRing>
</gml:exterior>
</gml:Polygon>
</gml:surfaceMember>
<gml:surfaceMember>
<gml:Polygon>
<gml:exterior>
<gml:LinearRing>
<gml:posList>
0.0 0.0 5.0 10.0 0.0 5.0 10.0 10.0 5.0 0.0 10.0 5.0 0.0 0.0 5.0
</gml:posList>
</gml:LinearRing>
</gml:exterior>
</gml:Polygon>
</gml:surfaceMember>
</gml:CompositeSurface>
</gml:exterior>
</gml:Solid>
</bldg:lod1Solid>
</bldg:Building>
</cityObjectMember>
</CityModel>"#;
let pd = parse_citygml_string(xml).unwrap();
assert_eq!(pd.polys.num_cells(), 2);
assert!(pd.points.len() >= 8);
assert_eq!(pd.polys.cell(0).len(), 4);
assert_eq!(pd.polys.cell(1).len(), 4);
}
}