1use geo::algorithm::winding_order::Winding;
2use geo::{
3 CoordNum, Coordinate, GeoNum, Geometry, GeometryCollection, LineString, MultiPolygon, Polygon,
4};
5use num_traits;
6
7pub trait Normalized<T: num_traits::Float> {
8 fn normalized(&self) -> Self;
44}
45
46impl<T: num_traits::Float + CoordNum + GeoNum> Normalized<T> for GeometryCollection<T> {
49 fn normalized(&self) -> Self {
50 GeometryCollection(
51 self.0
52 .iter()
53 .map(|p| match p {
54 Geometry::Polygon { .. } => {
55 Geometry::Polygon(p.clone().into_polygon().unwrap().normalized())
56 }
57 Geometry::MultiPolygon { .. } => {
58 Geometry::MultiPolygon(p.clone().into_multi_polygon().unwrap().normalized())
59 }
60 _ => p.clone(),
61 })
62 .collect::<Vec<Geometry<T>>>(),
63 )
64 }
65}
66
67impl<T: num_traits::Float + CoordNum + GeoNum> Normalized<T> for MultiPolygon<T> {
70 fn normalized(&self) -> Self {
71 MultiPolygon::from(
72 self.0
73 .iter()
74 .map(|x| x.normalized())
75 .collect::<Vec<Polygon<T>>>(),
76 )
77 }
78}
79
80impl<T: num_traits::Float + CoordNum + GeoNum> Normalized<T> for Polygon<T> {
81 fn normalized(&self) -> Self {
82 normalized_polygon(self)
83 }
84}
85
86fn normalized_polygon<T: num_traits::Float + CoordNum + GeoNum>(poly: &Polygon<T>) -> Polygon<T> {
90 Polygon::new(
91 LineString::from(
92 poly.exterior()
93 .points_cw()
94 .map(|x| x.0)
95 .collect::<Vec<Coordinate<T>>>(),
96 ),
97 poly.interiors()
98 .iter()
99 .map(|ring| {
100 LineString::from(
101 ring.clone()
102 .points_ccw()
103 .map(|x| x.0)
104 .collect::<Vec<Coordinate<T>>>(),
105 )
106 })
107 .collect(),
108 )
109}
110
111#[cfg(test)]
114mod tests {
115 use super::*;
116 use geo::polygon;
117
118 #[test]
119 fn does_not_change_good_polygon() {
120 let (good, _) = get_bad_outer_poly();
121 let norm = good.normalized();
122 assert_eq!(norm, good);
123 }
124
125 #[test]
126 fn can_normalize_bad_outer_polygon() {
127 let (good, bad) = get_bad_outer_poly();
128 let norm = bad.normalized();
129 assert_eq!(norm, good);
130 }
131
132 #[test]
133 fn can_normalize_good_outer_bad_inner_polygon() {
134 let (good, bad) = get_good_outer_bad_inner_poly();
135 let norm = bad.normalized();
136 assert_eq!(norm, good);
137 }
138
139 #[test]
140 fn can_normalize_bad_outer_bad_inner_polygon() {
141 let (good, bad) = get_bad_outer_bad_inner_poly();
142 let norm = bad.normalized();
143 assert_eq!(norm, good);
144 }
145
146 #[test]
147 fn can_normalize_bad_outer_good_inner_polygon() {
148 let (good, bad) = get_bad_outer_good_inner_poly();
149 let norm = bad.normalized();
150 assert_eq!(norm, good);
151 }
152
153 #[test]
154 fn can_process_multi_polygon() {
155 let (good, bad) = get_bad_outer_good_inner_poly();
156 let mp = MultiPolygon(vec![good.clone(), bad]);
157 let norm = mp.normalized();
158 for poly in norm {
159 assert_eq!(good, poly);
160 }
161 }
162
163 fn get_bad_outer_poly() -> (Polygon<f64>, Polygon<f64>) {
164 let bad = polygon![
165 (x: 1.0, y: 1.0),
166 (x: 4.0, y: 1.0),
167 (x: 4.0, y: 4.0),
168 (x: 1.0, y: 4.0),
169 (x: 1.0, y: 1.0),
170 ];
171 let good = polygon![
172 (x: 1.0, y: 1.0),
173 (x: 1.0, y: 4.0),
174 (x: 4.0, y: 4.0),
175 (x: 4.0, y: 1.0),
176 (x: 1.0, y: 1.0),
177 ];
178 (good, bad)
179 }
180
181 fn get_good_outer_bad_inner_poly() -> (Polygon<f64>, Polygon<f64>) {
182 let bad = polygon!(
183 exterior: [
184 (x: 0., y: 0.),
185 (x: 0., y: 50.),
186 (x: 50., y: 50.),
187 (x: 50., y: 0.),
188 ],
189 interiors: [
190 [
191 (x: 10., y: 10.),
192 (x: 10., y: 20.),
193 (x: 20., y: 20.),
194 (x: 20., y: 10.),
195 ],
196 ],
197 );
198 let good = polygon!(
199 exterior: [
200 (x: 0., y: 0.),
201 (x: 0., y: 50.),
202 (x: 50., y: 50.),
203 (x: 50., y: 0.),
204 ],
205 interiors: [
206 [
207 (x: 10., y: 10.),
208 (x: 20., y: 10.),
209 (x: 20., y: 20.),
210 (x: 10., y: 20.),
211 ],
212 ],
213 );
214 (good, bad)
215 }
216
217 fn get_bad_outer_bad_inner_poly() -> (Polygon<f64>, Polygon<f64>) {
218 let bad = polygon!(
219 exterior: [
220 (x: 0., y: 0.),
221 (x: 50., y: 0.),
222 (x: 50., y: 50.),
223 (x: 0., y: 50.),
224 ],
225 interiors: [
226 [
227 (x: 10., y: 10.),
228 (x: 10., y: 20.),
229 (x: 20., y: 20.),
230 (x: 20., y: 10.),
231 ],
232 ],
233 );
234 let good = polygon!(
235 exterior: [
236 (x: 0., y: 0.),
237 (x: 0., y: 50.),
238 (x: 50., y: 50.),
239 (x: 50., y: 0.),
240 ],
241 interiors: [
242 [
243 (x: 10., y: 10.),
244 (x: 20., y: 10.),
245 (x: 20., y: 20.),
246 (x: 10., y: 20.),
247 ],
248 ],
249 );
250 (good, bad)
251 }
252
253 fn get_bad_outer_good_inner_poly() -> (Polygon<f64>, Polygon<f64>) {
254 let bad = polygon!(
255 exterior: [
256 (x: 0., y: 0.),
257 (x: 50., y: 0.),
258 (x: 50., y: 50.),
259 (x: 0., y: 50.),
260 ],
261 interiors: [
262 [
263 (x: 10., y: 10.),
264 (x: 20., y: 10.),
265 (x: 20., y: 20.),
266 (x: 10., y: 20.),
267 ],
268 ],
269 );
270 let good = polygon!(
271 exterior: [
272 (x: 0., y: 0.),
273 (x: 0., y: 50.),
274 (x: 50., y: 50.),
275 (x: 50., y: 0.),
276 ],
277 interiors: [
278 [
279 (x: 10., y: 10.),
280 (x: 20., y: 10.),
281 (x: 20., y: 20.),
282 (x: 10., y: 20.),
283 ],
284 ],
285 );
286 (good, bad)
287 }
288}