1use geometry_model::{
27 Box, Linestring, MultiLinestring, MultiPoint, MultiPolygon, Polygon, Ring, Segment,
28};
29use geometry_trait::{Point as PointTrait, PointMut};
30
31#[must_use]
41pub fn convert<Src, Dst>(src: &Src) -> Dst
42where
43 Src: Convert<Dst>,
44{
45 src.convert()
46}
47
48#[doc(hidden)]
51pub trait Convert<Dst> {
52 fn convert(&self) -> Dst;
53}
54
55impl<P, const CW: bool, const CL: bool> Convert<Polygon<P, CW, CL>> for Box<P>
66where
67 P: PointMut + Default,
68{
69 fn convert(&self) -> Polygon<P, CW, CL> {
70 let min_x = self.min().get::<0>();
71 let min_y = self.min().get::<1>();
72 let max_x = self.max().get::<0>();
73 let max_y = self.max().get::<1>();
74 let corner = |x: P::Scalar, y: P::Scalar| {
75 let mut p = P::default();
76 p.set::<0>(x);
77 p.set::<1>(y);
78 p
79 };
80 let mut ring = if CW {
82 alloc::vec![
83 corner(min_x, min_y),
84 corner(min_x, max_y),
85 corner(max_x, max_y),
86 corner(max_x, min_y),
87 ]
88 } else {
89 alloc::vec![
90 corner(min_x, min_y),
91 corner(max_x, min_y),
92 corner(max_x, max_y),
93 corner(min_x, max_y),
94 ]
95 };
96 if CL {
97 ring.push(corner(min_x, min_y));
98 }
99 Polygon::new(Ring::<P, CW, CL>::from_vec(ring))
100 }
101}
102
103impl<P, const CW: bool, const CL: bool> Convert<Polygon<P, CW, CL>> for Ring<P, CW, CL>
108where
109 P: PointTrait + Copy,
110{
111 fn convert(&self) -> Polygon<P, CW, CL> {
112 Polygon::new(Ring::from_vec(self.0.clone()))
113 }
114}
115
116impl<P, const CW: bool, const CL: bool> Convert<Linestring<P>> for Ring<P, CW, CL>
122where
123 P: PointTrait + Copy,
124{
125 fn convert(&self) -> Linestring<P> {
126 Linestring(self.0.clone())
127 }
128}
129
130impl<P> Convert<Linestring<P>> for Segment<P>
135where
136 P: PointTrait + Copy,
137{
138 fn convert(&self) -> Linestring<P> {
139 Linestring(alloc::vec![*self.start(), *self.end()])
140 }
141}
142
143impl<P> Convert<MultiPoint<P>> for P
148where
149 P: PointTrait + Copy,
150{
151 fn convert(&self) -> MultiPoint<P> {
152 MultiPoint(alloc::vec![*self])
153 }
154}
155
156impl<P> Convert<MultiLinestring<Linestring<P>>> for Linestring<P>
161where
162 P: PointTrait + Copy,
163{
164 fn convert(&self) -> MultiLinestring<Linestring<P>> {
165 MultiLinestring(alloc::vec![self.clone()])
166 }
167}
168
169impl<P, const CW: bool, const CL: bool> Convert<MultiPolygon<Polygon<P, CW, CL>>>
174 for Polygon<P, CW, CL>
175where
176 P: PointTrait + Copy,
177{
178 fn convert(&self) -> MultiPolygon<Polygon<P, CW, CL>> {
179 MultiPolygon(alloc::vec![self.clone()])
180 }
181}
182
183#[cfg(test)]
184#[allow(
185 clippy::float_cmp,
186 reason = "Converted corner coordinates are exact literals."
187)]
188mod tests {
189 use super::convert;
196 use geometry_cs::Cartesian;
197 use geometry_model::{Box, Linestring, MultiPoint, Point2D, Polygon, Segment};
198 use geometry_trait::{Point as _, Polygon as _, Ring as _};
199
200 type Pt = Point2D<f64, Cartesian>;
201
202 #[test]
204 fn box_to_polygon_five_points_and_corners() {
205 let b: Box<Pt> = Box::from_corners(Pt::new(0., 0.), Pt::new(4., 3.));
206 let pg: Polygon<Pt> = convert(&b);
207 let pts: alloc::vec::Vec<(f64, f64)> = pg
208 .exterior()
209 .points()
210 .map(|p| (p.get::<0>(), p.get::<1>()))
211 .collect();
212 assert_eq!(pts.len(), 5);
214 assert_eq!(pts[0], (0., 0.));
215 assert_eq!(pts[4], (0., 0.));
216 assert!(pts.contains(&(0., 3.)));
217 assert!(pts.contains(&(4., 3.)));
218 assert!(pts.contains(&(4., 0.)));
219 }
220
221 #[test]
223 fn segment_to_linestring_endpoints() {
224 let s = Segment::new(Pt::new(0., 0.), Pt::new(3., 4.));
225 let ls: Linestring<Pt> = convert(&s);
226 assert_eq!(ls.0.len(), 2);
227 assert_eq!(ls.0[0].get::<0>(), 0.);
228 assert_eq!(ls.0[1].get::<0>(), 3.);
229 }
230
231 #[test]
232 fn point_to_multi_point_single_member() {
233 let mp: MultiPoint<Pt> = convert(&Pt::new(1., 2.));
234 assert_eq!(mp.0.len(), 1);
235 assert_eq!(mp.0[0].get::<0>(), 1.);
236 }
237
238 #[test]
241 fn ring_to_polygon_becomes_hole_free_exterior() {
242 use geometry_model::Ring;
243 let ring: Ring<Pt> = Ring::from_vec(vec![
244 Pt::new(0., 0.),
245 Pt::new(1., 0.),
246 Pt::new(1., 1.),
247 Pt::new(0., 0.),
248 ]);
249 let pg: Polygon<Pt> = convert(&ring);
250 assert_eq!(pg.exterior().points().count(), 4);
251 assert_eq!(pg.interiors().count(), 0);
252 let first = pg.exterior().points().next().unwrap();
253 assert_eq!((first.get::<0>(), first.get::<1>()), (0., 0.));
254 }
255
256 #[test]
258 fn ring_to_linestring_copies_points() {
259 use geometry_model::Ring;
260 let ring: Ring<Pt> =
261 Ring::from_vec(vec![Pt::new(2., 3.), Pt::new(4., 5.), Pt::new(2., 3.)]);
262 let ls: Linestring<Pt> = convert(&ring);
263 assert_eq!(ls.0.len(), 3);
264 assert_eq!((ls.0[1].get::<0>(), ls.0[1].get::<1>()), (4., 5.));
265 }
266
267 #[test]
269 fn linestring_to_multi_linestring_single_member() {
270 use geometry_model::MultiLinestring;
271 let ls: Linestring<Pt> = Linestring(vec![Pt::new(0., 0.), Pt::new(1., 1.)]);
272 let mls: MultiLinestring<Linestring<Pt>> = convert(&ls);
273 assert_eq!(mls.0.len(), 1);
274 assert_eq!(mls.0[0].0.len(), 2);
275 }
276
277 #[test]
279 fn polygon_to_multi_polygon_single_member() {
280 use geometry_model::{MultiPolygon, Ring};
281 let pg: Polygon<Pt> = Polygon::new(Ring::from_vec(vec![
282 Pt::new(0., 0.),
283 Pt::new(1., 0.),
284 Pt::new(1., 1.),
285 Pt::new(0., 0.),
286 ]));
287 let mpg: MultiPolygon<Polygon<Pt>> = convert(&pg);
288 assert_eq!(mpg.0.len(), 1);
289 assert_eq!(mpg.0[0].exterior().points().count(), 4);
290 }
291
292 #[test]
296 fn box_to_polygon_is_wound_clockwise() {
297 let b: Box<Pt> = Box::from_corners(Pt::new(0., 0.), Pt::new(4., 3.));
298 let pg: Polygon<Pt> = convert(&b);
299 let pts: alloc::vec::Vec<(f64, f64)> = pg
300 .exterior()
301 .points()
302 .map(|p| (p.get::<0>(), p.get::<1>()))
303 .collect();
304 assert_eq!(
305 pts,
306 alloc::vec![(0., 0.), (0., 3.), (4., 3.), (4., 0.), (0., 0.)]
307 );
308 assert_eq!(crate::area::ring_area(pg.exterior()), 12.0);
309 }
310
311 #[test]
314 fn box_to_ccw_polygon_is_wound_counter_clockwise() {
315 let b: Box<Pt> = Box::from_corners(Pt::new(0., 0.), Pt::new(4., 3.));
316 let pg: Polygon<Pt, false, true> = convert(&b);
317 let pts: alloc::vec::Vec<(f64, f64)> = pg
318 .exterior()
319 .points()
320 .map(|p| (p.get::<0>(), p.get::<1>()))
321 .collect();
322 assert_eq!(
323 pts,
324 alloc::vec![(0., 0.), (4., 0.), (4., 3.), (0., 3.), (0., 0.)]
325 );
326 assert_eq!(crate::area::ring_area(pg.exterior()), 12.0);
327 }
328
329 #[test]
332 fn box_to_open_polygon_has_four_stored_points() {
333 let b: Box<Pt> = Box::from_corners(Pt::new(0., 0.), Pt::new(4., 3.));
334 let pg: Polygon<Pt, true, false> = convert(&b);
335 assert_eq!(pg.exterior().points().count(), 4);
336 assert_eq!(crate::area::ring_area(pg.exterior()), 12.0);
337 }
338
339 #[test]
342 fn three_d_box_to_polygon_uses_the_first_two_dimensions() {
343 use geometry_model::Point3D;
344 type P3 = Point3D<f64, Cartesian>;
345 let b: Box<P3> = Box::from_corners(P3::new(0., 0., 0.), P3::new(4., 3., 1.));
346 let pg: Polygon<P3> = convert(&b);
347 assert_eq!(pg.exterior().points().count(), 5);
348 assert!(pg.exterior().points().all(|p| p.get::<2>() == 0.0));
349 assert_eq!(crate::area::ring_area(pg.exterior()), 12.0);
350 }
351}