1use geometry_model::{
27 Box, Linestring, MultiLinestring, MultiPoint, MultiPolygon, Polygon, Ring, Segment,
28};
29use geometry_trait::{Point as PointTrait, PointMut};
30
31use crate::make::make_point;
32
33#[must_use]
43pub fn convert<Src, Dst>(src: &Src) -> Dst
44where
45 Src: Convert<Dst>,
46{
47 src.convert()
48}
49
50#[doc(hidden)]
53pub trait Convert<Dst> {
54 fn convert(&self) -> Dst;
55}
56
57impl<P, const CW: bool, const CL: bool> Convert<Polygon<P, CW, CL>> for Box<P>
64where
65 P: PointMut + Default,
66{
67 fn convert(&self) -> Polygon<P, CW, CL> {
68 let min_x = self.min().get::<0>();
69 let min_y = self.min().get::<1>();
70 let max_x = self.max().get::<0>();
71 let max_y = self.max().get::<1>();
72 let ring = Ring::<P, CW, CL>::from_vec(alloc::vec![
75 make_point(&[min_x, min_y]),
76 make_point(&[min_x, max_y]),
77 make_point(&[max_x, max_y]),
78 make_point(&[max_x, min_y]),
79 make_point(&[min_x, min_y]),
80 ]);
81 Polygon::new(ring)
82 }
83}
84
85impl<P, const CW: bool, const CL: bool> Convert<Polygon<P, CW, CL>> for Ring<P, CW, CL>
90where
91 P: PointTrait + Copy,
92{
93 fn convert(&self) -> Polygon<P, CW, CL> {
94 Polygon::new(Ring::from_vec(self.0.clone()))
95 }
96}
97
98impl<P, const CW: bool, const CL: bool> Convert<Linestring<P>> for Ring<P, CW, CL>
104where
105 P: PointTrait + Copy,
106{
107 fn convert(&self) -> Linestring<P> {
108 Linestring(self.0.clone())
109 }
110}
111
112impl<P> Convert<Linestring<P>> for Segment<P>
117where
118 P: PointTrait + Copy,
119{
120 fn convert(&self) -> Linestring<P> {
121 Linestring(alloc::vec![*self.start(), *self.end()])
122 }
123}
124
125impl<P> Convert<MultiPoint<P>> for P
130where
131 P: PointTrait + Copy,
132{
133 fn convert(&self) -> MultiPoint<P> {
134 MultiPoint(alloc::vec![*self])
135 }
136}
137
138impl<P> Convert<MultiLinestring<Linestring<P>>> for Linestring<P>
143where
144 P: PointTrait + Copy,
145{
146 fn convert(&self) -> MultiLinestring<Linestring<P>> {
147 MultiLinestring(alloc::vec![self.clone()])
148 }
149}
150
151impl<P, const CW: bool, const CL: bool> Convert<MultiPolygon<Polygon<P, CW, CL>>>
156 for Polygon<P, CW, CL>
157where
158 P: PointTrait + Copy,
159{
160 fn convert(&self) -> MultiPolygon<Polygon<P, CW, CL>> {
161 MultiPolygon(alloc::vec![self.clone()])
162 }
163}
164
165#[cfg(test)]
166#[allow(
167 clippy::float_cmp,
168 reason = "Converted corner coordinates are exact literals."
169)]
170mod tests {
171 use super::convert;
178 use geometry_cs::Cartesian;
179 use geometry_model::{Box, Linestring, MultiPoint, Point2D, Polygon, Segment};
180 use geometry_trait::{Point as _, Polygon as _, Ring as _};
181
182 type Pt = Point2D<f64, Cartesian>;
183
184 #[test]
186 fn box_to_polygon_five_points_and_corners() {
187 let b: Box<Pt> = Box::from_corners(Pt::new(0., 0.), Pt::new(4., 3.));
188 let pg: Polygon<Pt> = convert(&b);
189 let pts: alloc::vec::Vec<(f64, f64)> = pg
190 .exterior()
191 .points()
192 .map(|p| (p.get::<0>(), p.get::<1>()))
193 .collect();
194 assert_eq!(pts.len(), 5);
196 assert_eq!(pts[0], (0., 0.));
197 assert_eq!(pts[4], (0., 0.));
198 assert!(pts.contains(&(0., 3.)));
199 assert!(pts.contains(&(4., 3.)));
200 assert!(pts.contains(&(4., 0.)));
201 }
202
203 #[test]
205 fn segment_to_linestring_endpoints() {
206 let s = Segment::new(Pt::new(0., 0.), Pt::new(3., 4.));
207 let ls: Linestring<Pt> = convert(&s);
208 assert_eq!(ls.0.len(), 2);
209 assert_eq!(ls.0[0].get::<0>(), 0.);
210 assert_eq!(ls.0[1].get::<0>(), 3.);
211 }
212
213 #[test]
214 fn point_to_multi_point_single_member() {
215 let mp: MultiPoint<Pt> = convert(&Pt::new(1., 2.));
216 assert_eq!(mp.0.len(), 1);
217 assert_eq!(mp.0[0].get::<0>(), 1.);
218 }
219
220 #[test]
223 fn ring_to_polygon_becomes_hole_free_exterior() {
224 use geometry_model::Ring;
225 let ring: Ring<Pt> = Ring::from_vec(vec![
226 Pt::new(0., 0.),
227 Pt::new(1., 0.),
228 Pt::new(1., 1.),
229 Pt::new(0., 0.),
230 ]);
231 let pg: Polygon<Pt> = convert(&ring);
232 assert_eq!(pg.exterior().points().count(), 4);
233 assert_eq!(pg.interiors().count(), 0);
234 let first = pg.exterior().points().next().unwrap();
235 assert_eq!((first.get::<0>(), first.get::<1>()), (0., 0.));
236 }
237
238 #[test]
240 fn ring_to_linestring_copies_points() {
241 use geometry_model::Ring;
242 let ring: Ring<Pt> =
243 Ring::from_vec(vec![Pt::new(2., 3.), Pt::new(4., 5.), Pt::new(2., 3.)]);
244 let ls: Linestring<Pt> = convert(&ring);
245 assert_eq!(ls.0.len(), 3);
246 assert_eq!((ls.0[1].get::<0>(), ls.0[1].get::<1>()), (4., 5.));
247 }
248
249 #[test]
251 fn linestring_to_multi_linestring_single_member() {
252 use geometry_model::MultiLinestring;
253 let ls: Linestring<Pt> = Linestring(vec![Pt::new(0., 0.), Pt::new(1., 1.)]);
254 let mls: MultiLinestring<Linestring<Pt>> = convert(&ls);
255 assert_eq!(mls.0.len(), 1);
256 assert_eq!(mls.0[0].0.len(), 2);
257 }
258
259 #[test]
261 fn polygon_to_multi_polygon_single_member() {
262 use geometry_model::{MultiPolygon, Ring};
263 let pg: Polygon<Pt> = Polygon::new(Ring::from_vec(vec![
264 Pt::new(0., 0.),
265 Pt::new(1., 0.),
266 Pt::new(1., 1.),
267 Pt::new(0., 0.),
268 ]));
269 let mpg: MultiPolygon<Polygon<Pt>> = convert(&pg);
270 assert_eq!(mpg.0.len(), 1);
271 assert_eq!(mpg.0[0].exterior().points().count(), 4);
272 }
273}