1#![allow(clippy::float_cmp)]
2
3use byteorder::{ByteOrder, NativeEndian};
4use geo::algorithm::contains::Contains;
5use geo::algorithm::intersects::Intersects;
6use geo_types::{Coordinate, Line, LineString, MultiPolygon, Point, Polygon};
7use linked_hash_map::LinkedHashMap;
8use num_traits;
9use std::hash::{Hash, Hasher};
10
11pub struct ValidationErrors<T>
15where
16 T: num_traits::Float,
17{
18 pub valid: bool,
20 pub has_less_than_three_points: bool,
22 pub is_multi_polygon: bool,
24 pub unsupported_floating_point_values: Vec<T>,
26 pub open_rings: Vec<LineString<T>>,
28 pub ring_intersects_other_ring: Vec<Coordinate<T>>,
31 pub self_intersections: Vec<Coordinate<T>>,
33 pub point_touching_line: Vec<Coordinate<T>>,
35 pub repeated_points: Vec<Coordinate<T>>,
37}
38
39pub trait Validate<T>
40where
41 T: num_traits::Float,
42{
43 fn validate(&self) -> bool;
86
87 fn validate_detailed(&self) -> ValidationErrors<T>;
147}
148
149impl<T> Validate<T> for MultiPolygon<T>
152where
153 T: num_traits::Float,
154{
155 fn validate(&self) -> bool {
156 validate_multi_polygon(self, true).valid
157 }
158
159 fn validate_detailed(&self) -> ValidationErrors<T> {
160 validate_multi_polygon(self, false)
161 }
162}
163
164fn validate_multi_polygon<T: num_traits::Float>(
165 mp: &MultiPolygon<T>,
166 quick: bool,
167) -> ValidationErrors<T> {
168 let mut validation_errors = ValidationErrors::<T> {
169 valid: true,
170 has_less_than_three_points: false,
171 is_multi_polygon: false,
172 unsupported_floating_point_values: vec![] as Vec<T>,
173 open_rings: vec![] as Vec<LineString<T>>,
174 ring_intersects_other_ring: vec![] as Vec<Coordinate<T>>,
175 self_intersections: vec![] as Vec<Coordinate<T>>,
176 point_touching_line: vec![] as Vec<Coordinate<T>>,
177 repeated_points: vec![] as Vec<Coordinate<T>>,
178 };
179 for poly in mp.0.iter() {
180 if quick {
181 let valid = poly.validate();
182 if !valid {
183 validation_errors.valid = false;
184 return validation_errors; }
186 } else {
187 let err = poly.validate_detailed();
188 if !err.valid && validation_errors.valid {
189 validation_errors.valid = err.valid
190 }
191 if err.has_less_than_three_points && !validation_errors.has_less_than_three_points {
192 validation_errors.has_less_than_three_points = err.has_less_than_three_points
193 }
194 validation_errors
195 .unsupported_floating_point_values
196 .extend(err.unsupported_floating_point_values);
197 validation_errors.open_rings.extend(err.open_rings);
198 validation_errors
199 .ring_intersects_other_ring
200 .extend(err.ring_intersects_other_ring);
201 validation_errors
202 .self_intersections
203 .extend(err.self_intersections);
204 validation_errors
205 .point_touching_line
206 .extend(err.point_touching_line);
207 validation_errors
208 .repeated_points
209 .extend(err.repeated_points);
210 }
211 }
212 validation_errors
213}
214
215impl<T> Validate<T> for Polygon<T>
216where
217 T: num_traits::Float,
218{
219 fn validate(&self) -> bool {
220 validate_polygon(self, true).valid
221 }
222
223 fn validate_detailed(&self) -> ValidationErrors<T> {
224 validate_polygon(self, false)
225 }
226}
227
228fn validate_polygon<T>(poly: &Polygon<T>, quick: bool) -> ValidationErrors<T>
234where
235 T: num_traits::Float,
236{
237 let mut validation_errors = ValidationErrors::<T> {
238 valid: true,
239 has_less_than_three_points: false,
240 is_multi_polygon: false,
241 unsupported_floating_point_values: vec![] as Vec<T>,
242 open_rings: vec![] as Vec<LineString<T>>,
243 ring_intersects_other_ring: vec![] as Vec<Coordinate<T>>,
244 self_intersections: vec![] as Vec<Coordinate<T>>,
245 point_touching_line: vec![] as Vec<Coordinate<T>>,
246 repeated_points: vec![] as Vec<Coordinate<T>>,
247 };
248
249 let exterior_ring = Polygon::new(poly.exterior().clone(), vec![]);
251 for inner_ring in poly.interiors() {
252 if !exterior_ring.contains(inner_ring) {
253 validation_errors.valid = false;
254 if quick {
255 return validation_errors;
256 }
257 validation_errors.is_multi_polygon = true;
258 }
259 }
260
261 let mut poly_lines = vec![] as Vec<Line<T>>;
262 let mut rings = vec![poly.exterior()];
263 rings.extend(poly.interiors());
264 let mut ring_start_idx = 0; for ring in rings.into_iter() {
266 let ring_points_count = ring.0.len();
268 if ring_points_count < 4 {
271 validation_errors.valid = false;
272 if quick {
273 return validation_errors;
274 }
275 validation_errors.has_less_than_three_points = true;
276 }
277
278 if !ring.0[0].x.eq(&ring.0[ring_points_count - 1].x)
283 && !ring.0[0].y.eq(&ring.0[ring_points_count - 1].y)
284 {
285 validation_errors.valid = false;
286 if quick {
287 return validation_errors;
288 }
289 validation_errors.open_rings.push(ring.clone());
290 }
291
292 let mut prev_point = ring.0[0];
294 if !prev_point.x.is_finite() {
295 validation_errors.valid = false;
296 if quick {
297 return validation_errors;
298 }
299 validation_errors
300 .unsupported_floating_point_values
301 .push(prev_point.x);
302 }
303 if !prev_point.y.is_finite() {
304 validation_errors.valid = false;
305 if quick {
306 return validation_errors;
307 }
308 validation_errors
309 .unsupported_floating_point_values
310 .push(prev_point.y);
311 }
312
313 let mut ring_points_map = LinkedHashMap::<CompCoord<T>, Coordinate<T>>::new();
314 for i in 1..(ring_points_count) {
315 let point = ring.0[i];
316
317 if !point.x.is_finite() {
319 validation_errors.valid = false;
320 if quick {
321 return validation_errors;
322 }
323 validation_errors
324 .unsupported_floating_point_values
325 .push(point.x);
326 }
327 if !point.y.is_finite() {
328 validation_errors.valid = false;
329 if quick {
330 return validation_errors;
331 }
332 validation_errors
333 .unsupported_floating_point_values
334 .push(point.y);
335 }
336
337 let pp_comp = CompCoord {
339 0: Coordinate {
340 x: prev_point.x,
341 y: prev_point.y,
342 },
343 };
344 if ring_points_map.contains_key(&pp_comp) {
345 validation_errors.valid = false;
346 if quick {
347 return validation_errors;
348 }
349 validation_errors.repeated_points.push(prev_point);
350 }
351 let current_line = Line::<T>::new(prev_point, point);
353 for (line_idx, line) in poly_lines.iter().enumerate() {
354 if !line.end.eq(¤t_line.start) && !line.start.eq(¤t_line.end) {
355 let start_point: Point<T> = current_line.start.into();
357 if line.intersects(&start_point) {
358 validation_errors.valid = false;
359 if quick {
360 return validation_errors;
361 }
362 validation_errors
363 .point_touching_line
364 .push(current_line.start);
365 } else if line.intersects(¤t_line) {
366 validation_errors.valid = false;
367 if quick {
368 return validation_errors;
369 }
370 if line_idx > ring_start_idx {
371 validation_errors
372 .self_intersections
373 .push(line.intersection_point(¤t_line));
374 } else {
375 validation_errors
376 .ring_intersects_other_ring
377 .push(line.intersection_point(¤t_line));
378 }
379 }
380 }
381 }
382 poly_lines.push(current_line);
383 prev_point = point;
384 ring_points_map.insert(pp_comp, point);
385 }
386 ring_start_idx = poly_lines.len();
387 }
388
389 validation_errors
390}
391
392struct CompCoord<T: num_traits::Float>(Coordinate<T>);
393
394impl<T: num_traits::Float> PartialEq for CompCoord<T> {
395 fn eq(&self, other: &CompCoord<T>) -> bool {
396 transform_coord_to_array_of_u8(self) == transform_coord_to_array_of_u8(other)
401 }
402}
403
404impl<T: num_traits::Float> Eq for CompCoord<T> {}
405
406impl<T: num_traits::Float> Hash for CompCoord<T> {
407 fn hash<H: Hasher>(&self, state: &mut H) {
408 transform_coord_to_array_of_u8(self).hash(state);
409 }
410}
411fn transform_coord_to_array_of_u8<T>(coord: &CompCoord<T>) -> [u8; 16]
415where
416 T: num_traits::Float,
417{
418 let mut buf1 = [0; 8];
419 NativeEndian::write_f64(&mut buf1, T::to_f64(&coord.0.x).unwrap());
420 let mut buf2 = [0; 8];
421 NativeEndian::write_f64(&mut buf2, T::to_f64(&coord.0.y).unwrap());
422
423 [
424 buf1[0], buf1[1], buf1[2], buf1[3], buf1[4], buf1[5], buf1[6], buf1[7], buf2[0], buf2[1],
425 buf2[2], buf2[3], buf2[4], buf2[5], buf2[6], buf2[7],
426 ]
427}
428
429pub trait IntersectionPoint<T>
431where
432 T: num_traits::Float,
433{
434 fn intersection_point(&self, line: &Line<T>) -> Coordinate<T>;
435}
436
437impl<T> IntersectionPoint<T> for Line<T>
438where
439 T: num_traits::Float,
440{
441 fn intersection_point(&self, line: &Line<T>) -> Coordinate<T> {
443 let a1 = self.end.y - self.start.y;
445 let b1 = self.start.x - self.end.x;
446 let c1 = a1 * (self.start.x) + b1 * (self.start.y);
447
448 let a2 = line.end.y - line.start.y;
450 let b2 = line.start.x - line.end.x;
451 let c2 = a2 * (line.start.x) + b2 * (line.start.y);
452
453 let determinant = a1 * b2 - a2 * b1;
454 if determinant.is_normal()
455 {
457 let x = (b2 * c1 - b1 * c2) / determinant;
458 let y = (a1 * c2 - a2 * c1) / determinant;
459 Coordinate { x, y }
460 } else {
461 Coordinate {
463 x: T::infinity(),
464 y: T::infinity(),
465 }
466 }
467 }
468}
469
470#[cfg(test)]
473mod tests {
474 use super::*;
475 use geo_types::{line_string, point, polygon};
476
477 #[test]
478 fn can_validate_polygon() {
479 let poly = polygon![
480 (x: 1.0, y: 1.0),
481 (x: 4.000000007, y: 1.0),
482 (x: 4.0, y: 4.0),
483 (x: 1.0, y: 4.0),
484 (x: 1.0, y: 1.0),
485 ];
486
487 let valid = validate_polygon(&poly, false);
488 assert_eq!(valid.valid, true);
489 }
490
491 #[test]
492 fn can_validate_complex_polygon() {
493 let poly = polygon!(
494 exterior: [
495 (x: 0., y: 0.),
496 (x: 0., y: 20.),
497 (x: 20., y: 20.),
498 (x: 20., y: 0.),
499 ],
500 interiors: [
501 [
502 (x: 10., y: 10.),
503 (x: 15., y: 10.),
504 (x: 15., y: 15.),
505 (x: 10., y: 15.),
506 ],
507 ],
508 );
509
510 let valid = validate_polygon(&poly, true);
511 assert_eq!(valid.valid, true);
512 }
513
514 #[test]
515 fn can_find_multiple_errors_in_complex_polygon() {
516 let poly = polygon!(
517 exterior: [
518 (x: 0., y: 0.),
519 (x: 0., y: 200.),
520 (x: 200., y: 0.),
521 (x: 200., y: 200.),
522 ],
523 interiors: [
524 [
525 (x: 10., y: 20.),
526 (x: 50., y: 20.),
527 (x: 20., y: 50.),
528 (x: 50., y: 50.),
529 ],
530 ],
531 );
532
533 let valid = validate_polygon(&poly, false);
534 assert_eq!(valid.valid, false);
535 assert_eq!(valid.ring_intersects_other_ring.len(), 3);
536 assert_eq!(valid.self_intersections.len(), 2);
537 assert_eq!(valid.point_touching_line.len(), 1);
538
539 assert_eq!(valid.ring_intersects_other_ring[0].x, 20_f64);
540 assert_eq!(valid.ring_intersects_other_ring[0].y, 20_f64);
541 assert_eq!(valid.ring_intersects_other_ring[1].x, 35_f64);
542 assert_eq!(valid.ring_intersects_other_ring[1].y, 35_f64);
543 assert_eq!(valid.ring_intersects_other_ring[2].x, 50_f64);
544 assert_eq!(valid.ring_intersects_other_ring[2].y, 50_f64);
545
546 assert_eq!(valid.self_intersections[0].x, 100_f64);
547 assert_eq!(valid.self_intersections[0].y, 100_f64);
548 assert_eq!(valid.self_intersections[1].x, 32.857142857142854_f64);
549 assert_eq!(valid.self_intersections[1].y, 37.142857142857146_f64);
550
551 assert_eq!(valid.point_touching_line[0].x, 50_f64);
552 assert_eq!(valid.point_touching_line[0].y, 50_f64);
553 }
554
555 #[test]
556 fn can_recognize_self_intersecting_polygon() {
557 let poly = polygon![
558 (x: 1.0_f64, y: 1.0),
559 (x: 4.0, y: 1.0),
560 (x: 1.0, y: 4.0),
561 (x: 4.0, y: 4.0),
562 (x: 1.0, y: 1.0),
563 ];
564
565 let valid = validate_polygon(&poly, false);
566 assert_eq!(valid.valid, false);
567 assert_eq!(valid.self_intersections.len(), 1);
568 assert_eq!(valid.self_intersections[0].x, 2.5);
569 assert_eq!(valid.self_intersections[0].y, 2.5);
570 }
571
572 #[test]
573 fn can_recognize_multi_polygon() {
574 let poly = polygon!(
575 exterior: [
576 (x: 0., y: 0.),
577 (x: 0., y: 20.),
578 (x: 20., y: 20.),
579 (x: 0., y: 20.),
580 (x: 0., y: 0.),
581 ],
582 interiors: [
583 [
584 (x: 25., y: 25.),
585 (x: 25., y: 30.),
586 (x: 30., y: 30.),
587 (x: 30., y: 25.),
588 (x: 25., y: 25.),
589 ],
590 ],
591 );
592
593 let valid = validate_polygon(&poly, false);
594 assert!(!valid.valid);
595 assert!(valid.is_multi_polygon);
596 }
597
598 #[test]
599 fn rejects_polygon_with_too_few_points() {
600 let poly = polygon![
601 (x: 1.0_f64, y: 1.0),
602 (x: 4.0, y: 1.0),
603 (x: 1.0, y: 1.0),
604 ];
605
606 let valid = validate_polygon(&poly, false);
607 assert!(!valid.valid);
608 assert!(valid.has_less_than_three_points);
609 }
610
611 #[test]
612 fn rejects_polygon_with_nan_and_infinity() {
613 let poly = polygon![
614 (x: 1.0_f64, y: 1.0),
615 (x: 1.0, y: 4.0),
616 (x: 4.0, y: 4.0),
617 (x: 4.0, y: 1.0),
618 (x: std::f64::INFINITY, y: std::f64::NAN),
619 ];
620
621 let valid = validate_polygon(&poly, false);
622 assert!(!valid.valid);
623 assert_eq!(valid.unsupported_floating_point_values.len(), 2);
624 assert!(valid.unsupported_floating_point_values[0].is_infinite());
625 assert!(valid.unsupported_floating_point_values[1].is_nan());
626 }
627
628 #[test]
629 fn rejects_polygon_with_repeated_points() {
630 let poly = polygon![
631 (x: 1.0_f64, y: 1.0),
632 (x: 1.0, y: 4.0),
633 (x: 4.0, y: 4.0),
634 (x: 4.0, y: 1.0),
635 (x: 1.0, y: 1.0),
636 (x: 1.0, y: 1.0),
637 (x: 1.0, y: 1.0),
638 ];
639
640 let valid = validate_polygon(&poly, false);
641 assert!(!valid.valid);
642 assert_eq!(valid.repeated_points.len(), 2);
643 assert!(valid.repeated_points[0].eq(&Coordinate { x: 1.0_f64, y: 1.0 }));
644 assert!(valid.repeated_points[1].eq(&Coordinate { x: 1.0_f64, y: 1.0 }));
645
646 let poly2 = polygon![
647 (x: 1.0_f64, y: 1.0),
648 (x: 1.0, y: 4.0),
649 (x: 4.0, y: 4.0),
650 (x: 4.0, y: 1.0),
651 (x: 4.0, y: 1.0),
652 (x: 4.0, y: 1.0),
653 (x: 1.0, y: 1.0),
654 ];
655
656 let valid2 = validate_polygon(&poly2, false);
657 assert!(!valid2.valid);
658 assert_eq!(valid2.repeated_points.len(), 2);
659 assert!(valid2.repeated_points[0].eq(&Coordinate { x: 4.0_f64, y: 1.0 }));
660 assert!(valid2.repeated_points[1].eq(&Coordinate { x: 4.0_f64, y: 1.0 }));
661 }
662}