diff --git a/nusamai-kml/examples/write_kml.rs b/nusamai-kml/examples/write_kml.rs
deleted file mode 100644
index f6b10751..00000000
--- a/nusamai-kml/examples/write_kml.rs
+++ /dev/null
@@ -1,70 +0,0 @@
-use kml::{
- types::{Geometry, Kml, MultiGeometry, Placemark},
- KmlWriter,
-};
-use nusamai_geometry::{MultiPoint, Polygon3};
-use nusamai_kml::conversion::{multipoint_to_kml, polygon_to_kml};
-use std::collections::HashMap;
-use std::fs::File;
-use std::io::{BufWriter, Write};
-
-fn poly_to_multigeom() -> MultiGeometry {
- let mut poly = Polygon3::new();
- poly.add_ring([
- [10., 10., 0.],
- [10., 20., 0.],
- [20., 20., 0.],
- [20., 10., 0.], // not closed
- ]);
- poly.add_ring([
- [15., 15., 0.],
- [18., 10., 0.],
- [18., 18., 0.],
- [15., 18., 0.],
- ]);
-
- polygon_to_kml(&poly)
-}
-fn point_to_multigeom() -> MultiGeometry {
- let mut mpoint = MultiPoint::<3>::new();
- mpoint.push(&[11., 12., 13.]);
- mpoint.push(&[21., 22., 23.]);
- mpoint.push(&[31., 32., 33.]);
-
- multipoint_to_kml(&mpoint)
-}
-
-fn multigeometry_to_file(multi_geom: MultiGeometry, filename: String) {
- let placemark = Placemark {
- geometry: Some(Geometry::MultiGeometry(multi_geom)),
- ..Default::default()
- };
-
- let folder = Kml::Folder {
- attrs: HashMap::new(),
- elements: vec![Kml::Placemark(placemark)],
- };
-
- let file_path = filename;
- let file = File::create(file_path).expect("Failed to create file");
-
- let mut buf_writer = BufWriter::new(file);
-
- writeln!(buf_writer, r#""#)
- .expect("Failed to write XML declaration");
- writeln!(
- buf_writer,
- r#""#
- ) // Add tag here
- .expect("Failed to write tag");
- let mut kml_writer = KmlWriter::from_writer(&mut buf_writer);
- kml_writer.write(&folder).expect("Failed to write KML data");
- writeln!(buf_writer, "") // Add tag here
- .expect("Failed to write tag");
-}
-
-fn main() {
- multigeometry_to_file(point_to_multigeom(), "point.kml".to_string());
-
- multigeometry_to_file(poly_to_multigeom(), "poly.kml".to_string());
-}
diff --git a/nusamai-kml/src/conversion.rs b/nusamai-kml/src/conversion.rs
index e4d042fe..b75e1f71 100644
--- a/nusamai-kml/src/conversion.rs
+++ b/nusamai-kml/src/conversion.rs
@@ -5,122 +5,103 @@ use nusamai_geometry::{CoordNum, MultiPoint, MultiPolygon, Polygon};
use std::{collections::HashMap, vec};
const EXTRUDE: bool = false;
-const ALTITUDEMODE: AltitudeMode = AltitudeMode::RelativeToGround;
+const ALTITUDE_MODE: AltitudeMode = AltitudeMode::RelativeToGround;
-pub fn multipolygon_to_kml(mpoly: &MultiPolygon<3>) -> MultiGeometry {
+pub fn multipolygon_to_kml(mpoly: &MultiPolygon<3>) -> Vec {
multipolygon_to_kml_with_mapping(mpoly, |c| c)
}
pub fn indexed_multipolygon_to_kml(
vertices: &[[f64; 3]],
mpoly_idx: &MultiPolygon<1, u32>,
-) -> MultiGeometry {
+) -> Vec {
multipolygon_to_kml_with_mapping(mpoly_idx, |idx| vertices[idx[0] as usize])
}
fn multipolygon_to_kml_with_mapping(
mpoly: &MultiPolygon,
mapping: impl Fn([T; D]) -> [f64; 3],
-) -> MultiGeometry {
- let polygons = mpoly
+) -> Vec {
+ mpoly
.iter()
- .map(|poly| polygon_to_kml_with_mapping(poly.clone(), &mapping))
- .collect::>();
- MultiGeometry {
- geometries: polygons.into_iter().map(Geometry::MultiGeometry).collect(),
+ .flat_map(|poly| polygon_to_kml_with_mapping(&poly, &mapping)) // Flatten the vector of vectors
+ .collect()
+}
+
+fn polygon_to_kml_polygon_with_mapping(
+ poly: &Polygon,
+ mapping: impl Fn([T; D]) -> [f64; 3],
+) -> KmlPolygon {
+ KmlPolygon {
+ outer: polygon_to_kml_outer_boundary_with_mapping(poly, &mapping),
+ inner: polygon_to_kml_inner_boundary_with_mapping(poly, &mapping),
+ extrude: EXTRUDE,
+ tessellate: false,
+ altitude_mode: ALTITUDE_MODE,
attrs: HashMap::new(),
}
}
fn polygon_to_kml_outer_boundary_with_mapping(
- poly: Polygon,
+ poly: &Polygon,
mapping: impl Fn([T; D]) -> [f64; 3],
) -> LinearRing {
let outer_coords: Vec = poly
.exterior()
.iter_closed()
.map(&mapping)
- .map(|coords| Coord {
- x: coords[0],
- y: coords[1],
- z: Some(coords[2]),
- })
+ .map(|[x, y, z]| Coord { x, y, z: Some(z) })
.collect();
LinearRing {
coords: outer_coords,
extrude: EXTRUDE,
tessellate: false,
- altitude_mode: ALTITUDEMODE,
+ altitude_mode: ALTITUDE_MODE,
attrs: HashMap::new(),
}
}
fn polygon_to_kml_inner_boundary_with_mapping(
- poly: Polygon,
+ poly: &Polygon,
mapping: impl Fn([T; D]) -> [f64; 3],
) -> Vec {
poly.interiors()
.map(|ring| {
ring.iter_closed()
.map(&mapping)
- .map(|coords| Coord {
- x: coords[0],
- y: coords[1],
- z: Some(coords[2]),
- })
- .collect::>()
+ .map(|[x, y, z]| Coord { x, y, z: Some(z) })
+ .collect()
})
.map(|coords| LinearRing {
coords,
extrude: EXTRUDE,
tessellate: false,
- altitude_mode: ALTITUDEMODE,
+ altitude_mode: ALTITUDE_MODE,
attrs: HashMap::new(),
})
.collect()
}
-fn polygon_to_kml_polygon_with_mapping(
- poly: Polygon,
- mapping: impl Fn([T; D]) -> [f64; 3],
-) -> KmlPolygon {
- let outer = polygon_to_kml_outer_boundary_with_mapping(poly.clone(), &mapping);
- let inner = polygon_to_kml_inner_boundary_with_mapping(poly, &mapping);
-
- KmlPolygon {
- outer,
- inner,
- extrude: EXTRUDE,
- tessellate: false,
- altitude_mode: ALTITUDEMODE,
- attrs: HashMap::new(),
- }
-}
-
/// Create a kml::MultiGeometry with Polygon from `nusamai_geometry::MultiPoint` with a mapping function.
pub fn polygon_to_kml_with_mapping(
- poly: Polygon,
+ poly: &Polygon,
mapping: impl Fn([T; D]) -> [f64; 3],
-) -> MultiGeometry {
- let polygons = vec![polygon_to_kml_polygon_with_mapping(poly, mapping)];
- MultiGeometry {
- geometries: polygons
- .into_iter()
- .map(|poly: KmlPolygon| Geometry::Polygon(poly))
- .collect(),
- attrs: HashMap::new(),
- }
+) -> Vec {
+ vec![polygon_to_kml_polygon_with_mapping(poly, mapping)]
}
/// Create a kml::MultiGeometry from a nusamai_geometry::MultiPolygon
-pub fn polygon_to_kml(poly: &Polygon<3>) -> MultiGeometry {
- polygon_to_kml_with_mapping(poly.clone(), |c| c)
+pub fn polygon_to_kml(poly: &Polygon<3>) -> Vec {
+ polygon_to_kml_with_mapping(poly, |c| c)
}
/// Create a kml::MultiGeometry with Polygon vertices and indices.
-pub fn indexed_polygon_to_kml(vertices: &[[f64; 3]], poly_idx: &Polygon<1, u32>) -> MultiGeometry {
- polygon_to_kml_with_mapping(poly_idx.clone(), |idx| vertices[idx[0] as usize])
+pub fn indexed_polygon_to_kml(
+ vertices: &[[f64; 3]],
+ poly_idx: &Polygon<1, u32>,
+) -> Vec {
+ polygon_to_kml_with_mapping(poly_idx, |idx| vertices[idx[0] as usize])
}
/// Create a kml::MultiGeometry with Points from `nusamai_geometry::MultiPoint` with a mapping function.
@@ -128,15 +109,11 @@ pub fn multipoint_to_kml_with_mapping(
mpoint: &MultiPoint,
mapping: impl Fn([T; D]) -> [f64; 3],
) -> MultiGeometry {
- let points = mpoint
- .iter()
- .map(&mapping)
- .map(|coords| Point::new(coords[0], coords[1], Some(coords[2])))
- .collect::>();
MultiGeometry {
- geometries: points
- .into_iter()
- .map(|pt: Point| Geometry::Point(pt))
+ geometries: mpoint
+ .iter()
+ .map(&mapping)
+ .map(|coord| Geometry::Point(Point::new(coord[0], coord[1], Some(coord[2]))))
.collect(),
attrs: HashMap::new(),
}
@@ -220,84 +197,90 @@ mod tests {
[15., 18., 0.],
]);
- let multi_geom = polygon_to_kml(&poly);
+ let polygons = polygon_to_kml(&poly);
- assert_eq!(&multi_geom.geometries.len(), &1);
+ assert_eq!(polygons[0].outer.coords.len(), 5);
+ assert_eq!(
+ polygons[0].outer.coords[0],
+ Coord {
+ x: 10.,
+ y: 10.,
+ z: Some(0.)
+ }
+ );
+ assert_eq!(
+ polygons[0].outer.coords[1],
+ Coord {
+ x: 10.,
+ y: 20.,
+ z: Some(0.)
+ }
+ );
+ assert_eq!(
+ polygons[0].outer.coords[2],
+ Coord {
+ x: 20.,
+ y: 20.,
+ z: Some(0.)
+ }
+ );
+ assert_eq!(
+ polygons[0].outer.coords[3],
+ Coord {
+ x: 20.,
+ y: 10.,
+ z: Some(0.)
+ }
+ );
+ assert_eq!(
+ polygons[0].outer.coords[4],
+ Coord {
+ x: 10.,
+ y: 10.,
+ z: Some(0.)
+ }
+ );
+ assert_eq!(polygons[0].inner[0].coords.len(), 5);
+ assert_eq!(
+ polygons[0].inner[0].coords[0],
+ Coord {
+ x: 15.,
+ y: 15.,
+ z: Some(0.)
+ }
+ );
+ assert_eq!(
+ polygons[0].inner[0].coords[1],
+ Coord {
+ x: 18.,
+ y: 10.,
+ z: Some(0.)
+ }
+ );
+ assert_eq!(
+ polygons[0].inner[0].coords[2],
+ Coord {
+ x: 18.,
+ y: 18.,
+ z: Some(0.)
+ }
+ );
+ assert_eq!(
+ polygons[0].inner[0].coords[3],
+ Coord {
+ x: 15.,
+ y: 18.,
+ z: Some(0.)
+ }
+ );
assert_eq!(
- &multi_geom.geometries[0],
- &Geometry::Polygon(KmlPolygon {
- outer: LinearRing {
- coords: vec![
- Coord {
- x: 10.,
- y: 10.,
- z: Some(0.),
- },
- Coord {
- x: 10.,
- y: 20.,
- z: Some(0.),
- },
- Coord {
- x: 20.,
- y: 20.,
- z: Some(0.),
- },
- Coord {
- x: 20.,
- y: 10.,
- z: Some(0.),
- },
- Coord {
- x: 10.0,
- y: 10.0,
- z: Some(0.0)
- }
- ],
- extrude: false,
- tessellate: false,
- altitude_mode: AltitudeMode::RelativeToGround,
- attrs: HashMap::new(),
- },
- inner: vec![LinearRing {
- coords: vec![
- Coord {
- x: 15.,
- y: 15.,
- z: Some(0.),
- },
- Coord {
- x: 18.,
- y: 10.,
- z: Some(0.),
- },
- Coord {
- x: 18.,
- y: 18.,
- z: Some(0.),
- },
- Coord {
- x: 15.,
- y: 18.,
- z: Some(0.),
- },
- Coord {
- x: 15.0,
- y: 15.0,
- z: Some(0.0)
- }
- ],
- extrude: false,
- tessellate: false,
- altitude_mode: AltitudeMode::RelativeToGround,
- attrs: HashMap::new(),
- }],
- extrude: false,
- tessellate: false,
- altitude_mode: AltitudeMode::RelativeToGround,
- attrs: HashMap::new(),
- })
+ polygons[0].inner[0].coords[4],
+ Coord {
+ x: 15.,
+ y: 15.,
+ z: Some(0.)
+ }
);
}
@@ -326,121 +309,92 @@ mod tests {
poly.add_ring([[4], [5], [6], [7], [4]]);
poly.add_ring([[8], [9], [10], [11], [8]]);
- let multi_geom = indexed_polygon_to_kml(&vertices, &poly);
+ let polygons = indexed_polygon_to_kml(&vertices, &poly);
- assert_eq!(&multi_geom.geometries.len(), &1);
+ assert_eq!(polygons.len(), 1);
+ assert_eq!(polygons[0].outer.coords.len(), 5);
+ assert_eq!(
+ polygons[0].outer.coords[0],
+ Coord {
+ x: 0.,
+ y: 0.,
+ z: Some(111.)
+ }
+ );
+ assert_eq!(
+ polygons[0].outer.coords[1],
+ Coord {
+ x: 5.,
+ y: 0.,
+ z: Some(111.)
+ }
+ );
assert_eq!(
- &multi_geom.geometries[0],
- &Geometry::Polygon(KmlPolygon {
- outer: LinearRing {
- coords: vec![
- Coord {
- x: 0.,
- y: 0.,
- z: Some(111.),
- },
- Coord {
- x: 5.,
- y: 0.,
- z: Some(111.),
- },
- Coord {
- x: 5.,
- y: 5.,
- z: Some(111.),
- },
- Coord {
- x: 0.,
- y: 5.,
- z: Some(111.),
- },
- Coord {
- x: 0.0,
- y: 0.0,
- z: Some(111.0)
- }
- ],
- extrude: false,
- tessellate: false,
- altitude_mode: AltitudeMode::RelativeToGround,
- attrs: HashMap::new(),
- },
- inner: vec![
- LinearRing {
- coords: vec![
- Coord {
- x: 1.,
- y: 1.,
- z: Some(111.),
- },
- Coord {
- x: 2.,
- y: 1.,
- z: Some(111.),
- },
- Coord {
- x: 2.,
- y: 2.,
- z: Some(111.),
- },
- Coord {
- x: 1.,
- y: 2.,
- z: Some(111.),
- },
- Coord {
- x: 1.0,
- y: 1.0,
- z: Some(111.0)
- }
- ],
- extrude: false,
- tessellate: false,
- altitude_mode: AltitudeMode::RelativeToGround,
- attrs: HashMap::new(),
- },
- LinearRing {
- coords: vec![
- Coord {
- x: 3.,
- y: 3.,
- z: Some(111.),
- },
- Coord {
- x: 4.,
- y: 3.,
- z: Some(111.),
- },
- Coord {
- x: 4.,
- y: 4.,
- z: Some(111.),
- },
- Coord {
- x: 3.,
- y: 4.,
- z: Some(111.),
- },
- Coord {
- x: 3.0,
- y: 3.0,
- z: Some(111.0)
- }
- ],
- extrude: false,
- tessellate: false,
- altitude_mode: AltitudeMode::RelativeToGround,
- attrs: HashMap::new(),
- }
- ],
- extrude: false,
- tessellate: false,
- altitude_mode: AltitudeMode::RelativeToGround,
- attrs: HashMap::new(),
- })
+ polygons[0].outer.coords[2],
+ Coord {
+ x: 5.,
+ y: 5.,
+ z: Some(111.)
+ }
+ );
+ assert_eq!(
+ polygons[0].outer.coords[3],
+ Coord {
+ x: 0.,
+ y: 5.,
+ z: Some(111.)
+ }
+ );
+ assert_eq!(
+ polygons[0].outer.coords[4],
+ Coord {
+ x: 0.,
+ y: 0.,
+ z: Some(111.)
+ }
+ );
+
+ assert_eq!(polygons[0].inner[0].coords.len(), 5);
+ assert_eq!(
+ polygons[0].inner[0].coords[0],
+ Coord {
+ x: 1.,
+ y: 1.,
+ z: Some(111.)
+ }
+ );
+ assert_eq!(
+ polygons[0].inner[0].coords[1],
+ Coord {
+ x: 2.,
+ y: 1.,
+ z: Some(111.)
+ }
+ );
+ assert_eq!(
+ polygons[0].inner[0].coords[2],
+ Coord {
+ x: 2.,
+ y: 2.,
+ z: Some(111.)
+ }
+ );
+ assert_eq!(
+ polygons[0].inner[0].coords[3],
+ Coord {
+ x: 1.,
+ y: 2.,
+ z: Some(111.)
+ }
+ );
+ assert_eq!(
+ polygons[0].inner[0].coords[4],
+ Coord {
+ x: 1.,
+ y: 1.,
+ z: Some(111.)
+ }
);
}
}
-
-
diff --git a/nusamai/src/sink/kml/mod.rs b/nusamai/src/sink/kml/mod.rs
index b47e2b69..04db7187 100644
--- a/nusamai/src/sink/kml/mod.rs
+++ b/nusamai/src/sink/kml/mod.rs
@@ -16,7 +16,7 @@ use nusamai_plateau::Entity;
use rayon::prelude::*;
use kml::{
- types::{Kml, MultiGeometry, Placemark},
+ types::{Geometry, Kml, MultiGeometry, Placemark, Polygon as KmlPolygon},
KmlWriter,
};
@@ -70,40 +70,37 @@ impl DataSink for KmlSink {
let (ra, rb) = rayon::join(
|| {
- // Convert CityObjects to GeoJSON objects
+ // Convert CityObjects to KML objects
upstream
.into_iter()
.par_bridge()
.try_for_each_with(sender, |sender, parcel| {
feedback.ensure_not_canceled()?;
- let multi_geom = entity_to_kml_mutilgeom(&parcel.entity);
+ let polygons = entity_to_kml_polygons(&parcel.entity);
- for geom in multi_geom.geometries {
- if sender.send(geom).is_err() {
- return Err(PipelineError::Canceled);
- }
+ let geoms = polygons.into_iter().map(Geometry::Polygon).collect();
+ let multi_geom = MultiGeometry {
+ geometries: geoms,
+ ..Default::default()
+ };
+
+ let placemark = Placemark {
+ geometry: Some(Geometry::MultiGeometry(multi_geom)),
+ ..Default::default()
+ };
+
+ if sender.send(placemark).is_err() {
+ return Err(PipelineError::Canceled);
}
Ok(())
})
},
|| {
- // Write GeoJSON to a file
- let mut placemarks: Vec = Vec::new();
-
- for geom in receiver.into_iter() {
- let placemark = Placemark {
- geometry: Some(geom),
- ..Default::default()
- };
-
- placemarks.push(Kml::Placemark(placemark));
- }
- // TODO: Handle output file path
-
+ let placemarks = receiver.into_iter().collect::>();
let folder = Kml::Folder {
attrs: HashMap::new(),
- elements: placemarks,
+ elements: placemarks.into_iter().map(Kml::Placemark).collect(),
};
let mut file = File::create(&self.output_path).unwrap();
@@ -146,15 +143,15 @@ fn extract_properties(value: &nusamai_citygml::object::Value) -> Option MultiGeometry {
+pub fn entity_to_kml_polygons(entity: &Entity) -> Vec {
let _properties = extract_properties(&entity.root);
let geom_store = entity.geometry_store.read().unwrap();
let Value::Object(obj) = &entity.root else {
- return MultiGeometry::default();
+ return Vec::new();
};
let ObjectStereotype::Feature { geometries, .. } = &obj.stereotype else {
- return MultiGeometry::default();
+ return Vec::new();
};
let mut mpoly = nusamai_geometry::MultiPolygon::<1, u32>::new();