1mod coord;
11mod geometry;
12mod geometrycollection;
13mod line;
14mod linestring;
15mod multilinestring;
16mod multipoint;
17mod multipolygon;
18mod point;
19mod polygon;
20mod rect;
21mod triangle;
22mod utils;
23
24use std::boxed::Box;
25use std::fmt::Display;
26
27#[derive(Debug, PartialEq)]
28pub enum RingRole {
30 Exterior,
31 Interior(usize),
32}
33
34impl std::fmt::Display for RingRole {
35 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
36 match self {
37 RingRole::Exterior => write!(f, "exterior ring"),
38 RingRole::Interior(i) => write!(f, "interior ring n°{}", i),
39 }
40 }
41}
42
43#[derive(Debug, PartialEq)]
44pub struct GeometryPosition(usize);
46
47#[derive(Debug, PartialEq)]
48pub struct CoordinatePosition(isize);
50
51#[derive(Debug, PartialEq)]
52pub enum ProblemPosition {
54 Point,
55 Line(CoordinatePosition),
56 Triangle(CoordinatePosition),
57 Rect(CoordinatePosition),
58 MultiPoint(GeometryPosition),
59 LineString(CoordinatePosition),
60 MultiLineString(GeometryPosition, CoordinatePosition),
61 Polygon(RingRole, CoordinatePosition),
62 MultiPolygon(GeometryPosition, RingRole, CoordinatePosition),
63 GeometryCollection(GeometryPosition, Box<ProblemPosition>),
64}
65
66#[derive(Debug, PartialEq)]
67pub enum Problem {
69 NotFinite,
71 TooFewPoints,
73 IdenticalCoords,
75 CollinearCoords,
77 SelfIntersection,
79 IntersectingRingsOnALine,
81 IntersectingRingsOnAnArea,
83 InteriorRingNotContainedInExteriorRing,
85 ElementsOverlaps,
87 ElementsTouchOnALine,
89 ElementsAreIdentical,
91}
92
93#[derive(Debug, PartialEq)]
94pub struct ProblemAtPosition(pub Problem, pub ProblemPosition);
96
97impl Display for ProblemAtPosition {
98 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
99 write!(f, "{:?} at {:?}", self.0, self.1)
100 }
101}
102
103#[derive(Debug, PartialEq)]
105pub struct ProblemReport(pub Vec<ProblemAtPosition>);
106
107impl Display for ProblemPosition {
108 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
109 let mut str_buffer: Vec<String> = Vec::new();
110 match self {
111 ProblemPosition::Point => str_buffer.push(String::new()),
112 ProblemPosition::LineString(coord) => {
113 if coord.0 == -1 {
114 str_buffer.push(String::new())
115 } else {
116 str_buffer.push(format!(" at coordinate {} of the LineString", coord.0))
117 }
118 }
119 ProblemPosition::Triangle(coord) => {
120 if coord.0 == -1 {
121 str_buffer.push(String::new())
122 } else {
123 str_buffer.push(format!(" at coordinate {} of the Triangle", coord.0))
124 }
125 }
126 ProblemPosition::Polygon(ring_role, coord) => {
127 if coord.0 == -1 {
128 str_buffer.push(format!(" on the {}", ring_role))
129 } else {
130 str_buffer.push(format!(" at coordinate {} of the {}", coord.0, ring_role))
131 }
132 }
133 ProblemPosition::MultiPolygon(geom_number, ring_role, coord) => {
134 if coord.0 == -1 {
135 str_buffer.push(format!(
136 " on the {} of the Polygon n°{} of the MultiPolygon",
137 ring_role, geom_number.0
138 ))
139 } else {
140 str_buffer.push(format!(
141 " at coordinate {} of the {} of the Polygon n°{} of the MultiPolygon",
142 coord.0, ring_role, geom_number.0
143 ))
144 }
145 }
146 ProblemPosition::MultiLineString(geom_number, coord) => {
147 if coord.0 == -1 {
148 str_buffer.push(format!(
149 " on the LineString n°{} of the MultiLineString",
150 geom_number.0
151 ))
152 } else {
153 str_buffer.push(format!(
154 " at coordinate {} of the LineString n°{} of the MultiLineString",
155 coord.0, geom_number.0
156 ))
157 }
158 }
159 ProblemPosition::MultiPoint(geom_number) => str_buffer.push(format!(
160 " on the Point n°{} of the MultiPoint",
161 geom_number.0
162 )),
163 ProblemPosition::GeometryCollection(geom_number, problem_position) => {
164 str_buffer.push(format!(
165 "{} of the geometry n°{} of the GeometryCollection",
166 *problem_position, geom_number.0
167 ));
168 }
169 ProblemPosition::Rect(coord) => {
170 if coord.0 == -1 {
171 str_buffer.push(String::new())
172 } else {
173 str_buffer.push(format!(" at coordinate {} of the Rect", coord.0))
174 }
175 }
176 ProblemPosition::Line(coord) => {
177 if coord.0 == -1 {
178 str_buffer.push(String::new())
179 } else {
180 str_buffer.push(format!(" at coordinate {} of the Line", coord.0))
181 }
182 }
183 }
184 write!(f, "{}", str_buffer.join(""))
185 }
186}
187
188impl Display for ProblemReport {
189 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
190 let buffer =
191 self.0
192 .iter()
193 .map(|p| {
194 let (problem, position) = (&p.0, &p.1);
195 let mut str_buffer: Vec<String> = Vec::new();
196 let is_polygon = matches!(
197 position,
198 ProblemPosition::Polygon(_, _) | ProblemPosition::MultiPolygon(_, _, _)
199 );
200
201 str_buffer.push(format!("{}", position));
202
203 match *problem {
204 Problem::NotFinite => str_buffer
205 .push("Coordinate is not finite (NaN or infinite)".to_string()),
206 Problem::TooFewPoints => {
207 if is_polygon {
208 str_buffer.push("Polygon ring has too few points".to_string())
209 } else {
210 str_buffer.push("LineString has too few points".to_string())
211 }
212 }
213 Problem::IdenticalCoords => str_buffer.push("Identical coords".to_string()),
214 Problem::CollinearCoords => str_buffer.push("Collinear coords".to_string()),
215 Problem::SelfIntersection => {
216 str_buffer.push("Ring has a self-intersection".to_string())
217 }
218 Problem::IntersectingRingsOnALine => str_buffer.push(
219 "Two interior rings of a Polygon share a common line".to_string(),
220 ),
221 Problem::IntersectingRingsOnAnArea => str_buffer.push(
222 "Two interior rings of a Polygon share a common area".to_string(),
223 ),
224 Problem::InteriorRingNotContainedInExteriorRing => str_buffer.push(
225 "The interior ring of a Polygon is not contained in the exterior ring"
226 .to_string(),
227 ),
228 Problem::ElementsOverlaps => str_buffer
229 .push("Two Polygons of MultiPolygons overlap partially".to_string()),
230 Problem::ElementsTouchOnALine => str_buffer
231 .push("Two Polygons of MultiPolygons touch on a line".to_string()),
232 Problem::ElementsAreIdentical => str_buffer
233 .push("Two Polygons of MultiPolygons are identical".to_string()),
234 };
235 str_buffer.into_iter().rev().collect::<Vec<_>>().join("")
236 })
237 .collect::<Vec<String>>()
238 .join("\n");
239
240 write!(f, "{}", buffer)
241 }
242}
243
244pub trait Valid {
246 fn is_valid(&self) -> bool;
248 fn explain_invalidity(&self) -> Option<ProblemReport>;
250}