geo_types/
debug.rs

1use core::fmt::{Debug, Formatter};
2
3use crate::geometry::*;
4use crate::CoordNum;
5
6impl<T: CoordNum> Debug for Coord<T> {
7    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
8        write!(f, "COORD({x:?} {y:?})", x = self.x, y = self.y)
9    }
10}
11
12impl<T: CoordNum> Debug for Point<T> {
13    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
14        write!(f, "POINT({x:?} {y:?})", x = self.x(), y = self.y())
15    }
16}
17
18impl<T: CoordNum> Debug for Line<T> {
19    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
20        write!(f, "LINE")?;
21        write_coord_seq(f, [self.start, self.end].iter())
22    }
23}
24
25impl<T: CoordNum> Debug for LineString<T> {
26    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
27        write!(f, "LINESTRING")?;
28        if self.0.is_empty() {
29            write!(f, " ")?;
30        }
31        write_coord_seq(f, self.0.iter())?;
32        Ok(())
33    }
34}
35
36impl<T: CoordNum> Debug for Polygon<T> {
37    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
38        write!(f, "POLYGON")?;
39        if self.exterior().0.is_empty() && self.interiors().is_empty() {
40            write!(f, " ")?;
41        }
42        write_polygon_inner(f, self)
43    }
44}
45
46impl<T: CoordNum> Debug for MultiPoint<T> {
47    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
48        write!(f, "MULTIPOINT")?;
49        if self.0.is_empty() {
50            write!(f, " ")?;
51        }
52        write_coord_seq(f, self.0.iter().map(|p| &p.0))
53    }
54}
55
56impl<T: CoordNum> Debug for MultiLineString<T> {
57    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
58        write!(f, "MULTILINESTRING")?;
59        let mut line_strings = self.0.iter();
60        let Some(first) = line_strings.next() else {
61            return write!(f, " EMPTY");
62        };
63        write!(f, "(")?;
64        write_coord_seq(f, first.0.iter())?;
65        for line_string in line_strings {
66            write!(f, ",")?;
67            write_coord_seq(f, line_string.0.iter())?;
68        }
69        write!(f, ")")
70    }
71}
72impl<T: CoordNum> Debug for MultiPolygon<T> {
73    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
74        write!(f, "MULTIPOLYGON")?;
75        let mut polygons = self.0.iter();
76        let Some(first) = polygons.next() else {
77            return write!(f, " EMPTY");
78        };
79        write!(f, "(")?;
80        write_polygon_inner(f, first)?;
81        for polygon in polygons {
82            write!(f, ",")?;
83            write_polygon_inner(f, polygon)?;
84        }
85        write!(f, ")")
86    }
87}
88
89impl<T: CoordNum> Debug for Rect<T> {
90    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
91        write!(f, "RECT")?;
92        write_coord_seq(f, [self.min(), self.max()].iter())
93    }
94}
95
96impl<T: CoordNum> Debug for Triangle<T> {
97    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
98        write!(f, "TRIANGLE")?;
99        write_coord_seq(f, [self.0, self.1, self.2].iter())
100    }
101}
102
103impl<T: CoordNum> Debug for Geometry<T> {
104    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
105        match self {
106            Geometry::Point(inner) => inner.fmt(f),
107            Geometry::Line(inner) => inner.fmt(f),
108            Geometry::LineString(inner) => inner.fmt(f),
109            Geometry::Polygon(inner) => inner.fmt(f),
110            Geometry::MultiPoint(inner) => inner.fmt(f),
111            Geometry::MultiLineString(inner) => inner.fmt(f),
112            Geometry::MultiPolygon(inner) => inner.fmt(f),
113            Geometry::GeometryCollection(inner) => inner.fmt(f),
114            Geometry::Rect(inner) => inner.fmt(f),
115            Geometry::Triangle(inner) => inner.fmt(f),
116        }
117    }
118}
119
120impl<T: CoordNum> Debug for GeometryCollection<T> {
121    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
122        write!(f, "GEOMETRYCOLLECTION")?;
123        let mut geometries = self.0.iter();
124        let Some(first) = geometries.next() else {
125            return write!(f, " EMPTY");
126        };
127        write!(f, "({first:?}")?;
128        for geometry in geometries {
129            write!(f, ",{geometry:?}")?;
130        }
131        write!(f, ")")
132    }
133}
134
135fn write_coord_seq<'a, T: CoordNum + 'a>(
136    f: &mut Formatter<'_>,
137    mut coords: impl Iterator<Item = &'a Coord<T>>,
138) -> core::fmt::Result {
139    let Some(coord) = coords.next() else {
140        write!(f, "EMPTY")?;
141        return Ok(());
142    };
143    write!(f, "({x:?} {y:?}", x = coord.x, y = coord.y)?;
144    for coord in coords {
145        write!(f, ",{x:?} {y:?}", x = coord.x, y = coord.y)?;
146    }
147    write!(f, ")")
148}
149
150fn write_polygon_inner<T: CoordNum>(
151    f: &mut Formatter<'_>,
152    polygon: &Polygon<T>,
153) -> core::fmt::Result {
154    if polygon.exterior().0.is_empty() {
155        let mut interiors = polygon.interiors().iter();
156        let Some(interior) = interiors.next() else {
157            write!(f, "EMPTY")?;
158            return Ok(());
159        };
160
161        // Invalid polygon - having interiors but no exterior!
162        // Still, we should try to print something meaningful.
163        write!(f, "(EMPTY,")?;
164        write_coord_seq(f, interior.0.iter())?;
165        for interior in interiors {
166            write!(f, ",")?;
167            write_coord_seq(f, interior.0.iter())?;
168        }
169        write!(f, ")")?;
170    } else {
171        write!(f, "(")?;
172        write_coord_seq(f, polygon.exterior().0.iter())?;
173        for interior in polygon.interiors().iter() {
174            write!(f, ",")?;
175            write_coord_seq(f, interior.0.iter())?;
176        }
177        write!(f, ")")?;
178    }
179    Ok(())
180}
181
182#[cfg(feature = "std")]
183#[cfg(test)]
184mod tests {
185    use super::*;
186
187    #[test]
188    fn float_coord() {
189        let coord = Coord { x: 1.0, y: 2.0 };
190        assert_eq!("COORD(1.0 2.0)", format!("{coord:?}"));
191    }
192    #[test]
193    fn int_coord() {
194        let coord = Coord { x: 1, y: 2 };
195        assert_eq!("COORD(1 2)", format!("{coord:?}"));
196    }
197    #[test]
198    fn float_point() {
199        let point = Point::new(1.0, 2.0);
200        assert_eq!("POINT(1.0 2.0)", format!("{point:?}"));
201    }
202    #[test]
203    fn int_point() {
204        let point = Point::new(1, 2);
205        assert_eq!("POINT(1 2)", format!("{point:?}"));
206    }
207    #[test]
208    fn line() {
209        let line_string = Line::new((1, 2), (3, 4));
210        assert_eq!("LINE(1 2,3 4)", format!("{line_string:?}"));
211    }
212    #[test]
213    fn line_string() {
214        let line_string = LineString::new(vec![(1, 2).into(), (3, 4).into()]);
215        assert_eq!("LINESTRING(1 2,3 4)", format!("{line_string:?}"));
216    }
217    #[test]
218    fn line_string_with_single_element() {
219        let line_string = LineString::new(vec![(1, 2).into()]);
220        assert_eq!("LINESTRING(1 2)", format!("{line_string:?}"));
221    }
222    #[test]
223    fn empty_line_string() {
224        let line_string = LineString::<i32>::new(vec![]);
225        assert_eq!("LINESTRING EMPTY", format!("{line_string:?}"));
226    }
227    #[test]
228    fn polygon_no_holes() {
229        let polygon = wkt!(POLYGON((1 2,3 4,5 6)));
230        assert_eq!("POLYGON((1 2,3 4,5 6,1 2))", format!("{polygon:?}"));
231    }
232    #[test]
233    fn polygon_with_hole() {
234        let polygon = wkt!(POLYGON(
235            (1 1,10 1,10 10,1 10,1 1),
236            (3 3,7 3,7 7,3 7,3 3)
237        ));
238        assert_eq!(
239            "POLYGON((1 1,10 1,10 10,1 10,1 1),(3 3,7 3,7 7,3 7,3 3))",
240            format!("{polygon:?}")
241        );
242    }
243    #[test]
244    fn polygon_with_multiple_holes() {
245        let polygon = wkt!(POLYGON(
246            (0 0,10 0,10 10,0 10,0 0),
247            (2 2,4 2,4 4,2 4,2 2),
248            (6 6,8 6,8 8,6 8,6 6)
249        ));
250        assert_eq!(
251            "POLYGON((0 0,10 0,10 10,0 10,0 0),(2 2,4 2,4 4,2 4,2 2),(6 6,8 6,8 8,6 8,6 6))",
252            format!("{polygon:?}")
253        );
254    }
255    #[test]
256    fn invalid_polygon_interior_but_no_exterior() {
257        // Not a valid polygon, but we should still have reasonable debug output - note this is *not* valid WKT
258        let interior = LineString::new(vec![(1, 2).into()]);
259        let polygon = Polygon::new(LineString::new(vec![]), vec![interior]);
260        assert_eq!("POLYGON(EMPTY,(1 2))", format!("{polygon:?}"));
261    }
262    #[test]
263    fn empty_polygon() {
264        let polygon: Polygon = wkt!(POLYGON EMPTY);
265        assert_eq!("POLYGON EMPTY", format!("{polygon:?}"));
266    }
267    #[test]
268    fn multi_point_empty() {
269        let multi_point: MultiPoint = wkt!(MULTIPOINT EMPTY);
270        assert_eq!("MULTIPOINT EMPTY", format!("{multi_point:?}"));
271    }
272    #[test]
273    fn multi_point_one_point() {
274        let multi_point = wkt!(MULTIPOINT(1 2));
275        assert_eq!("MULTIPOINT(1 2)", format!("{multi_point:?}"));
276    }
277    #[test]
278    fn multi_point_three_points() {
279        let multi_point = wkt!(MULTIPOINT(1 2,3 4,5 6));
280        assert_eq!("MULTIPOINT(1 2,3 4,5 6)", format!("{multi_point:?}"));
281    }
282    #[test]
283    fn multilinestring_empty() {
284        let multi_line_string: MultiLineString = wkt!(MULTILINESTRING EMPTY);
285        assert_eq!("MULTILINESTRING EMPTY", format!("{multi_line_string:?}"));
286    }
287
288    #[test]
289    fn multi_line_string_one_line() {
290        let multi_line_string = wkt!(MULTILINESTRING((1 2, 3 4, 5 6)));
291        assert_eq!(
292            "MULTILINESTRING((1 2,3 4,5 6))",
293            format!("{multi_line_string:?}")
294        );
295    }
296
297    #[test]
298    fn multi_line_string_multiple_lines() {
299        let multi_line_string = wkt!(MULTILINESTRING(
300            (1 2, 3 4, 5 6),
301            (7 8, 9 10, 11 12)
302        ));
303        assert_eq!(
304            "MULTILINESTRING((1 2,3 4,5 6),(7 8,9 10,11 12))",
305            format!("{multi_line_string:?}")
306        );
307    }
308
309    #[test]
310    fn multi_line_string_multiple_lines_with_empty() {
311        let multi_line_string = wkt!(MULTILINESTRING(
312            (1 2, 3 4, 5 6),
313            EMPTY,
314            (7 8, 9 10, 11 12)
315        ));
316        assert_eq!(
317            "MULTILINESTRING((1 2,3 4,5 6),EMPTY,(7 8,9 10,11 12))",
318            format!("{multi_line_string:?}")
319        );
320    }
321    #[test]
322    fn multi_polygon_empty() {
323        let multi_polygon: MultiPolygon = wkt!(MULTIPOLYGON EMPTY);
324        assert_eq!("MULTIPOLYGON EMPTY", format!("{multi_polygon:?}"));
325    }
326
327    #[test]
328    fn multi_polygon_one_polygon() {
329        let multi_polygon = wkt!(MULTIPOLYGON(
330            ((1 2, 3 4, 5 6, 1 2))
331        ));
332        assert_eq!(
333            "MULTIPOLYGON(((1 2,3 4,5 6,1 2)))",
334            format!("{multi_polygon:?}")
335        );
336    }
337
338    #[test]
339    fn multi_polygon_multiple_polygons() {
340        let multi_polygon = wkt!(MULTIPOLYGON(
341            ((1 2, 3 4, 5 6, 1 2)),
342            ((7 8, 9 10, 11 12, 7 8))
343        ));
344        assert_eq!(
345            "MULTIPOLYGON(((1 2,3 4,5 6,1 2)),((7 8,9 10,11 12,7 8)))",
346            format!("{multi_polygon:?}")
347        );
348    }
349
350    #[test]
351    fn multi_polygon_with_holes() {
352        let multi_polygon = wkt!(MULTIPOLYGON(
353            (
354                (1 1, 10 1, 10 10, 1 10, 1 1)
355            ),
356            (
357                (20 20, 30 20, 30 30, 20 30, 20 20),
358                (22 22, 28 22, 28 28, 22 28, 22 22)
359            )
360        ));
361        assert_eq!(
362            "MULTIPOLYGON(((1 1,10 1,10 10,1 10,1 1)),((20 20,30 20,30 30,20 30,20 20),(22 22,28 22,28 28,22 28,22 22)))",
363            format!("{multi_polygon:?}")
364        );
365    }
366    #[test]
367    fn multi_polygon_with_holes_and_empty_polygon() {
368        let multi_polygon = wkt!(MULTIPOLYGON(
369            (
370                (1 1, 10 1, 10 10, 1 10, 1 1)
371            ),
372            EMPTY,
373            (
374                (20 20, 30 20, 30 30, 20 30, 20 20),
375                (22 22, 28 22, 28 28, 22 28, 22 22)
376            )
377        ));
378        assert_eq!(
379            "MULTIPOLYGON(((1 1,10 1,10 10,1 10,1 1)),EMPTY,((20 20,30 20,30 30,20 30,20 20),(22 22,28 22,28 28,22 28,22 22)))",
380            format!("{multi_polygon:?}")
381        );
382    }
383    #[test]
384    fn rect() {
385        let rect = Rect::new((1, 2), (3, 4));
386        assert_eq!("RECT(1 2,3 4)", format!("{rect:?}"));
387
388        let rect = Rect::new((3, 4), (1, 2));
389        // output is always (min, max)
390        assert_eq!("RECT(1 2,3 4)", format!("{rect:?}"));
391    }
392    #[test]
393    fn triangle() {
394        let rect = Triangle::new((1, 2).into(), (3, 4).into(), (5, 6).into());
395        assert_eq!("TRIANGLE(1 2,3 4,5 6)", format!("{rect:?}"));
396    }
397
398    #[test]
399    fn geometry() {
400        let rect = Geometry::Triangle(Triangle::new((1, 2).into(), (3, 4).into(), (5, 6).into()));
401        assert_eq!("TRIANGLE(1 2,3 4,5 6)", format!("{rect:?}"));
402    }
403
404    #[test]
405    fn geometry_collection() {
406        let rect = Geometry::Triangle(Triangle::new((1, 2).into(), (3, 4).into(), (5, 6).into()));
407        assert_eq!("TRIANGLE(1 2,3 4,5 6)", format!("{rect:?}"));
408    }
409
410    #[test]
411    fn empty_geometry_collection() {
412        let geometry_collection: GeometryCollection = GeometryCollection::default();
413        assert_eq!(
414            "GEOMETRYCOLLECTION EMPTY",
415            format!("{geometry_collection:?}")
416        );
417    }
418
419    #[test]
420    fn geometry_collection_with_mixed_geometries() {
421        let geometry_collection: GeometryCollection<i32> = GeometryCollection::from(vec![
422            Geometry::Point(Point::new(1, 2)),
423            Geometry::Line(Line::new((1, 2), (3, 4))),
424            Geometry::Polygon(Polygon::new(
425                LineString::from(vec![(0, 0), (1, 0), (1, 1), (0, 0)]),
426                vec![],
427            )),
428        ]);
429
430        assert_eq!(
431            "GEOMETRYCOLLECTION(POINT(1 2),LINE(1 2,3 4),POLYGON((0 0,1 0,1 1,0 0)))",
432            format!("{geometry_collection:?}")
433        );
434    }
435
436    #[test]
437    fn nested_geometry_collection() {
438        let inner_collection: GeometryCollection<i32> = GeometryCollection::from(vec![
439            Geometry::Point(Point::new(5, 6)),
440            Geometry::LineString(LineString::from(vec![(1, 2), (3, 4)])),
441        ]);
442
443        let outer_collection: GeometryCollection<i32> = GeometryCollection::from(vec![
444            Geometry::Point(Point::new(1, 2)),
445            Geometry::GeometryCollection(inner_collection),
446        ]);
447
448        assert_eq!(
449            "GEOMETRYCOLLECTION(POINT(1 2),GEOMETRYCOLLECTION(POINT(5 6),LINESTRING(1 2,3 4)))",
450            format!("{outer_collection:?}")
451        );
452    }
453
454    #[test]
455    fn geometry_collection_with_no_coordinates() {
456        let geometry_collection: GeometryCollection<f64> = GeometryCollection::from(vec![
457            Geometry::Point(Point::new(0.0, 0.0)),
458            Geometry::Polygon(Polygon::new(LineString::new(vec![]), vec![])),
459        ]);
460
461        assert_eq!(
462            "GEOMETRYCOLLECTION(POINT(0.0 0.0),POLYGON EMPTY)",
463            format!("{geometry_collection:?}")
464        );
465    }
466}