use crate::Geom;
use extendr_api::prelude::*;
use geo_types::{
coord, point, Coord, LineString, MultiLineString, MultiPoint, MultiPolygon, Point, Polygon,
};
pub fn geom_point(x: f64, y: f64) -> Robj {
Geom::from(Point::new(x, y))
.into_robj()
.set_class(["point", "Geom"])
.unwrap()
.clone()
}
pub fn geom_multipoint(x: RArray<f64, [usize; 2]>) -> Robj {
let mpnt = MultiPoint::new(matrix_to_points(x));
Geom::from(mpnt)
.into_robj()
.set_class(["multipoint", "Geom"])
.unwrap()
.clone()
}
pub fn geom_linestring(x: RArray<f64, [usize; 2]>) -> Robj {
let coords = matrix_to_coords(x);
let lns = LineString::new(coords);
Geom::from(lns)
.into_robj()
.set_class(["linestring", "Geom"])
.unwrap()
.clone()
}
pub fn geom_multilinestring(x: List) -> Robj {
let vec_lns = x
.into_iter()
.map(|(_, x)| LineString::new(matrix_to_coords(RMatrix::try_from(x).unwrap())))
.collect::<Vec<LineString>>();
Geom::from(MultiLineString::new(vec_lns))
.into_robj()
.set_class(["multilinestring", "Geom"])
.unwrap()
.clone()
}
pub fn geom_polygon(x: List) -> Robj {
let n = x.len();
let mut linestrings: Vec<LineString> = Vec::with_capacity(n);
let exterior = matrix_to_coords(x[0].as_matrix().unwrap());
let exterior = LineString::new(exterior);
if n > 1 {
for i in 1..n {
let xi: RMatrix<f64> = x[i].to_owned().try_into().unwrap();
let coords = matrix_to_coords(xi);
let line = LineString::new(coords);
linestrings.push(line);
}
}
let polygon = Polygon::new(exterior, linestrings);
Geom::from(polygon)
.into_robj()
.set_class(["polygon", "Geom"])
.unwrap()
.clone()
}
pub fn geom_multipolygon(x: List) -> Robj {
let res = MultiPolygon::new(
x.into_iter()
.map(|(_, x)| polygon_inner(List::try_from(x).unwrap()))
.collect::<Vec<Polygon>>(),
);
Geom::from(res)
.into_robj()
.set_class(["multipolygon", "Geom"])
.unwrap()
.clone()
}
pub fn matrix_to_coords(x: RMatrix<f64>) -> Vec<Coord> {
let nrow = x.nrows();
let ncol = x.ncols();
if ncol != 2 {
panic!("Matrix should have only 2 columns for x and y coordinates, respectively.")
}
let mut coords: Vec<Coord> = Vec::with_capacity(nrow);
for i in 0..nrow {
let crd = coord! {x: x[[i, 0]], y: x[[i, 1]]};
coords.push(crd);
}
coords
}
pub fn matrix_to_points(x: RMatrix<f64>) -> Vec<Point> {
let nrow = x.nrows();
let ncol = x.ncols();
if ncol != 2 {
panic!("Matrix should have only 2 columns for x and y coordinates, respectively.")
}
let mut coords: Vec<Point> = Vec::with_capacity(nrow);
for i in 0..nrow {
let crd = point! {x: x[[i, 0]], y: x[[i, 1]]};
coords.push(crd);
}
coords
}
fn polygon_inner(x: List) -> Polygon {
let n = x.len();
let mut linestrings: Vec<LineString> = Vec::with_capacity(n);
let exterior = matrix_to_coords(x[0].as_matrix().unwrap());
let exterior = LineString::new(exterior);
if n > 1 {
for i in 1..n {
let xi: RMatrix<f64> = x[i].to_owned().try_into().unwrap();
let coords = matrix_to_coords(xi);
let line = LineString::new(coords);
linestrings.push(line);
}
}
Polygon::new(exterior, linestrings)
}