geo/algorithm/validation/
geometry_collection.rs

1use super::{GeometryIndex, InvalidGeometry, Validation};
2use crate::{GeoFloat, GeometryCollection};
3
4use std::fmt;
5
6/// A [`GeometryCollection`] is valid if all its elements are valid.
7#[derive(Debug, Clone, PartialEq)]
8pub enum InvalidGeometryCollection {
9    /// Which element is invalid, and what was invalid about it.
10    InvalidGeometry(GeometryIndex, Box<InvalidGeometry>),
11}
12
13impl std::error::Error for InvalidGeometryCollection {}
14
15impl fmt::Display for InvalidGeometryCollection {
16    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
17        match self {
18            InvalidGeometryCollection::InvalidGeometry(idx, err) => {
19                write!(f, "geometry at index {} is invalid: {}", idx.0, err)
20            }
21        }
22    }
23}
24
25impl<F: GeoFloat> Validation for GeometryCollection<F> {
26    type Error = InvalidGeometryCollection;
27
28    fn visit_validation<T>(
29        &self,
30        mut handle_validation_error: Box<dyn FnMut(Self::Error) -> Result<(), T> + '_>,
31    ) -> Result<(), T> {
32        // Loop over all the geometries, collect the reasons of invalidity
33        // and change the ProblemPosition to reflect the GeometryCollection
34        for (i, geometry) in self.0.iter().enumerate() {
35            geometry.visit_validation(Box::new(&mut |geometry_err| {
36                let err = InvalidGeometryCollection::InvalidGeometry(
37                    GeometryIndex(i),
38                    Box::new(geometry_err),
39                );
40                handle_validation_error(err)
41            }))?;
42        }
43        Ok(())
44    }
45}
46#[cfg(test)]
47mod tests {
48    use super::*;
49    use crate::algorithm::validation::{assert_validation_errors, InvalidLineString};
50    use crate::wkt;
51
52    #[test]
53    fn test_geometrycollection_contain_invalid_element() {
54        let gc = wkt!(
55            GEOMETRYCOLLECTION(
56                POINT(0. 0.),
57                LINESTRING(0. 0.,1. 1.),
58                LINESTRING(0. 0.,0. 0.)
59            )
60        );
61        assert_validation_errors!(
62            gc,
63            vec![InvalidGeometryCollection::InvalidGeometry(
64                GeometryIndex(2),
65                Box::new(InvalidGeometry::InvalidLineString(
66                    InvalidLineString::TooFewPoints
67                )),
68            )]
69        );
70    }
71
72    #[test]
73    fn test_display() {
74        let gc = wkt!(
75            GEOMETRYCOLLECTION(
76                POINT(0. 0.),
77                LINESTRING(0. 0.,1. 1.),
78                LINESTRING(0. 0.,0. 0.),
79                POLYGON(
80                    (0. 0., 1. 1., 1. 0., 0. 0.),
81                    (0. 0., 1. 1., 1. 0., 0. 0.)
82                )
83            )
84        );
85        let errors = gc.validation_errors();
86        assert_eq!(
87            errors[0].to_string(),
88            "geometry at index 2 is invalid: line string must have at least 2 distinct points"
89        );
90
91        assert_eq!(
92            errors[1].to_string(),
93            "geometry at index 3 is invalid: interior ring at index 0 is not contained within the polygon's exterior"
94        );
95
96        assert_eq!(
97            errors[2].to_string(),
98            "geometry at index 3 is invalid: exterior ring and interior ring at index 0 intersect on a line"
99        );
100    }
101}