1use geo::{Coord, GeoFloat, Line, LineString, MultiLineString, MultiPoint, Point, Rect, Triangle};
2use thiserror::Error;
3
4#[derive(Error, Clone, Debug, PartialEq)]
8pub enum GeometryValidationError {
9 #[error("Coordinate is NaN")]
11 CoordinateNaN,
12
13 #[error("Ring has too few points: found {found}, minimum required {min}")]
15 RingTooFewPoints { found: usize, min: usize },
16
17 #[error("Ring is not closed: first {first:?} != last {last:?}")]
19 RingNotClosed { first: Coord<f64>, last: Coord<f64> },
20
21 #[error("Ring has self-intersections")]
23 SelfIntersection,
24
25 #[error("Ring has repeated non-consecutive vertices (pinch point)")]
27 PinchPoint,
28
29 #[error("Hole lies outside shell")]
31 HoleOutsideShell,
32
33 #[error("Holes are nested")]
35 NestedHoles,
36
37 #[error("Interior ring is disconnected from shell")]
39 DisconnectedInteriorRing,
40
41 #[error("Wrong ring orientation: exterior should be CCW, interior CW")]
43 WrongOrientation,
44
45 #[error("Collinear ring: all points lie on a line")]
47 CollinearRing,
48
49 #[error("Geometry has repeated (duplicate) points")]
51 RepeatedPoint,
52
53 #[error("Geometry contains duplicate rings")]
55 DuplicatedRings,
56
57 #[error("MultiPoint contains duplicate points")]
59 MultiPointDuplicatePoints,
60
61 #[error("MultiLineString contains duplicate linestrings")]
63 MultiLineStringDuplicateLines,
64
65 #[error("Line has zero length (start == end at {0:?})")]
67 ZeroLengthLine(Coord<f64>),
68
69 #[error("Polygon exterior ring is degenerate (collapsed)")]
71 DegenerateExterior,
72
73 #[error("Geometry is not simple: components intersect at interior points")]
75 NotSimple,
76
77 #[error("GeometryCollection nesting exceeds maximum depth")]
79 ExcessiveNesting,
80}
81
82#[derive(Clone, Debug, PartialEq)]
104pub struct ValidationResult {
105 pub valid: bool,
107 pub errors: Vec<GeometryValidationError>,
109}
110
111impl ValidationResult {
112 pub fn valid() -> Self {
114 Self {
115 valid: true,
116 errors: Vec::new(),
117 }
118 }
119
120 pub fn invalid(errors: Vec<GeometryValidationError>) -> Self {
122 Self {
123 valid: false,
124 errors,
125 }
126 }
127
128 pub fn reason(&self) -> String {
133 if self.valid {
134 "Valid Geometry".to_string()
135 } else {
136 self.errors
137 .iter()
138 .map(|e| e.to_string())
139 .collect::<Vec<_>>()
140 .join("; ")
141 }
142 }
143}
144
145pub trait GeoValidation {
151 type Scalar: GeoFloat;
153
154 fn is_valid(&self) -> bool {
156 self.validate().valid
157 }
158
159 fn validate(&self) -> ValidationResult;
161
162 fn validate_reason(&self) -> String {
167 let result = self.validate();
168 if result.valid {
169 "Valid Geometry".to_string()
170 } else {
171 result
172 .errors
173 .iter()
174 .map(|e| e.to_string())
175 .collect::<Vec<_>>()
176 .join("; ")
177 }
178 }
179}
180
181pub(crate) fn ring_has_non_finite(ring: &[Coord<f64>]) -> bool {
182 ring.iter().any(|c| !c.x.is_finite() || !c.y.is_finite())
183}
184
185pub(crate) fn check_ring_validity(
186 ring: &[Coord<f64>],
187 is_exterior: bool,
188) -> Vec<GeometryValidationError> {
189 let mut errors = Vec::new();
190
191 if ring_has_non_finite(ring) {
192 errors.push(GeometryValidationError::CoordinateNaN);
193 return errors;
194 }
195
196 if ring.len() < 4 {
197 errors.push(GeometryValidationError::RingTooFewPoints {
198 found: ring.len(),
199 min: 4,
200 });
201 return errors;
202 }
203
204 if ring.first() != ring.last() {
205 errors.push(GeometryValidationError::RingNotClosed {
206 first: ring[0],
207 last: ring[ring.len() - 1],
208 });
209 return errors;
210 }
211
212 let n = ring.len() - 1;
213
214 let (mut min_x, mut max_x, mut min_y, mut max_y) = (f64::MAX, f64::MIN, f64::MAX, f64::MIN);
215 for c in &ring[..n] {
216 min_x = min_x.min(c.x);
217 max_x = max_x.max(c.x);
218 min_y = min_y.min(c.y);
219 max_y = max_y.max(c.y);
220 }
221 let scale = (max_x - min_x).abs().max((max_y - min_y).abs()).max(1.0);
222 let eps = 1e-12 * scale;
223 if (max_x - min_x).abs() < f64::EPSILON * scale || (max_y - min_y).abs() < f64::EPSILON * scale
224 {
225 if is_exterior {
226 errors.push(GeometryValidationError::DegenerateExterior);
227 } else {
228 errors.push(GeometryValidationError::CollinearRing);
229 }
230 return errors;
231 }
232
233 {
234 let mut prev_coord = &ring[0];
235 for c in &ring[1..n] {
236 if c.x == prev_coord.x && c.y == prev_coord.y {
237 errors.push(GeometryValidationError::RepeatedPoint);
238 break;
239 }
240 prev_coord = c;
241 }
242 }
243
244 let mut seen: rustc_hash::FxHashMap<(u64, u64), usize> =
245 rustc_hash::FxHashMap::with_capacity_and_hasher(n, Default::default());
246 for (idx, c) in ring[..n].iter().enumerate() {
247 let key = (c.x.to_bits(), c.y.to_bits());
248 if let Some(&prev) = seen.get(&key) {
249 if prev + 1 == idx {
250 continue;
251 }
252 errors.push(GeometryValidationError::PinchPoint);
253 break;
254 } else {
255 seen.insert(key, idx);
256 }
257 }
258
259 #[cfg(feature = "rstar")]
260 {
261 struct EdgeEnv {
262 idx: u32,
263 env: rstar::AABB<[f64; 2]>,
264 }
265 impl rstar::RTreeObject for EdgeEnv {
266 type Envelope = rstar::AABB<[f64; 2]>;
267 fn envelope(&self) -> Self::Envelope {
268 self.env
269 }
270 }
271 if n > 64 {
272 let mut edges = Vec::with_capacity(n);
273 for i in 0..n {
274 let (lo_x, hi_x) = if ring[i].x < ring[(i + 1) % n].x {
275 (ring[i].x, ring[(i + 1) % n].x)
276 } else {
277 (ring[(i + 1) % n].x, ring[i].x)
278 };
279 let (lo_y, hi_y) = if ring[i].y < ring[(i + 1) % n].y {
280 (ring[i].y, ring[(i + 1) % n].y)
281 } else {
282 (ring[(i + 1) % n].y, ring[i].y)
283 };
284 let ext = (hi_x - lo_x).abs().max((hi_y - lo_y).abs()).max(1.0) * 1e-10;
285 edges.push(EdgeEnv {
286 idx: i as u32,
287 env: rstar::AABB::from_corners(
288 [lo_x - ext, lo_y - ext],
289 [hi_x + ext, hi_y + ext],
290 ),
291 });
292 }
293 let tree = rstar::RTree::bulk_load(edges);
294 for i in 0..n {
295 let (lo_x, hi_x) = if ring[i].x < ring[(i + 1) % n].x {
296 (ring[i].x, ring[(i + 1) % n].x)
297 } else {
298 (ring[(i + 1) % n].x, ring[i].x)
299 };
300 let (lo_y, hi_y) = if ring[i].y < ring[(i + 1) % n].y {
301 (ring[i].y, ring[(i + 1) % n].y)
302 } else {
303 (ring[(i + 1) % n].y, ring[i].y)
304 };
305 let ext = (hi_x - lo_x).abs().max((hi_y - lo_y).abs()).max(1.0) * 1e-10;
306 let env =
307 rstar::AABB::from_corners([lo_x - ext, lo_y - ext], [hi_x + ext, hi_y + ext]);
308 let found = tree.locate_in_envelope_intersecting_int(&env, |c| {
309 let j = c.idx as usize;
310 if j <= i {
311 return std::ops::ControlFlow::Continue(());
312 }
313 if i.abs_diff(j) <= 1 || (i == 0 && j == n - 1) {
314 return std::ops::ControlFlow::Continue(());
315 }
316 if check_edge_pair_intersection(ring, i, j, eps) {
317 std::ops::ControlFlow::Break(())
318 } else {
319 std::ops::ControlFlow::<(), ()>::Continue(())
320 }
321 });
322 if found.is_break() {
323 errors.push(GeometryValidationError::SelfIntersection);
324 return errors;
325 }
326 }
327 } else {
328 for i in 0..n {
329 for j in i + 2..n {
330 if i == 0 && j == n - 1 {
331 continue;
332 }
333 if check_edge_pair_intersection(ring, i, j, eps) {
334 errors.push(GeometryValidationError::SelfIntersection);
335 return errors;
336 }
337 }
338 }
339 }
340 }
341 #[cfg(not(feature = "rstar"))]
342 {
343 for i in 0..n {
344 for j in i + 2..n {
345 if i == 0 && j == n - 1 {
346 continue;
347 }
348 if check_edge_pair_intersection(ring, i, j, eps) {
349 errors.push(GeometryValidationError::SelfIntersection);
350 return errors;
351 }
352 }
353 }
354 }
355
356 errors
357}
358
359pub(crate) fn edges_intersect_general(
360 a1: Coord<f64>,
361 a2: Coord<f64>,
362 b1: Coord<f64>,
363 b2: Coord<f64>,
364 eps: f64,
365) -> bool {
366 let o1 = crate::orient::orient2d_fast(a1, a2, b1);
369 let o2 = crate::orient::orient2d_fast(a1, a2, b2);
370 let o3 = crate::orient::orient2d_fast(b1, b2, a1);
371 let o4 = crate::orient::orient2d_fast(b1, b2, a2);
372
373 if o1 * o2 < 0.0 && o3 * o4 < 0.0 {
375 return true;
376 }
377
378 let collinear = o1.abs() <= eps && o2.abs() <= eps;
380 if collinear {
381 let dx = a2.x - a1.x;
382 let dy = a2.y - a1.y;
383 let len2 = dx * dx + dy * dy;
384 if len2 > eps {
385 let t1 = ((b1.x - a1.x) * dx + (b1.y - a1.y) * dy) / len2;
386 let t2 = ((b2.x - a1.x) * dx + (b2.y - a1.y) * dy) / len2;
387 let lo = 0.0f64.max(t1.min(t2));
388 let hi = 1.0f64.min(t1.max(t2));
389 if hi - lo > eps {
390 return true;
391 }
392 }
393 }
394
395 false
396}
397
398pub(crate) fn check_edge_pair_intersection(
399 coords: &[Coord<f64>],
400 i: usize,
401 j: usize,
402 eps: f64,
403) -> bool {
404 let n = coords.len() - 1;
405 let a1 = coords[i];
406 let a2 = coords[(i + 1) % n];
407 let b1 = coords[j];
408 let b2 = coords[(j + 1) % n];
409 edges_intersect_general(a1, a2, b1, b2, eps)
410}
411
412#[cfg(feature = "rstar")]
414struct EdgeIdx {
415 idx: usize,
416 env: rstar::AABB<[f64; 2]>,
417}
418#[cfg(feature = "rstar")]
419impl rstar::RTreeObject for EdgeIdx {
420 type Envelope = rstar::AABB<[f64; 2]>;
421 fn envelope(&self) -> Self::Envelope {
422 self.env
423 }
424}
425
426#[cfg(feature = "rstar")]
428fn build_ring_edge_tree(ring: &[Coord<f64>]) -> rstar::RTree<EdgeIdx> {
429 let n = ring.len() - 1;
430 rstar::RTree::bulk_load(
431 (0..n)
432 .map(|i| {
433 let a = ring[i];
434 let b = ring[(i + 1) % n];
435 let (lo_x, hi_x) = if a.x < b.x { (a.x, b.x) } else { (b.x, a.x) };
436 let (lo_y, hi_y) = if a.y < b.y { (a.y, b.y) } else { (b.y, a.y) };
437 EdgeIdx {
438 idx: i,
439 env: rstar::AABB::from_corners([lo_x, lo_y], [hi_x, hi_y]),
440 }
441 })
442 .collect(),
443 )
444}
445
446#[cfg(feature = "rstar")]
448fn build_ls_edge_tree(coords: &[Coord<f64>]) -> rstar::RTree<EdgeIdx> {
449 let n = coords.len() - 1;
450 if n < 1 {
451 return rstar::RTree::bulk_load(Vec::new());
452 }
453 rstar::RTree::bulk_load(
454 (0..n)
455 .map(|i| {
456 let a = coords[i];
457 let b = coords[i + 1];
458 let (lo_x, hi_x) = if a.x < b.x { (a.x, b.x) } else { (b.x, a.x) };
459 let (lo_y, hi_y) = if a.y < b.y { (a.y, b.y) } else { (b.y, a.y) };
460 EdgeIdx {
461 idx: i,
462 env: rstar::AABB::from_corners([lo_x, lo_y], [hi_x, hi_y]),
463 }
464 })
465 .collect(),
466 )
467}
468
469pub(crate) fn check_rings_intersect(ring1: &[Coord<f64>], ring2: &[Coord<f64>], eps: f64) -> bool {
473 let n1 = ring1.len().max(2) - 1;
474 let n2 = ring2.len().max(2) - 1;
475 if n1 < 2 || n2 < 2 {
476 return false;
477 }
478
479 if n1.max(n2) <= 64 {
481 for i in 0..n1 {
482 let a1 = ring1[i];
483 let a2 = ring1[(i + 1) % n1];
484 for j in 0..n2 {
485 let b1 = ring2[j];
486 let b2 = ring2[(j + 1) % n2];
487 if edges_intersect_general(a1, a2, b1, b2, eps) {
488 return true;
489 }
490 }
491 }
492 return false;
493 }
494
495 #[cfg(feature = "rstar")]
498 {
499 let (build_ring, query_ring, n_query) = if n1 < n2 {
500 (ring1, ring2, n2)
501 } else {
502 (ring2, ring1, n1)
503 };
504 let n_build = build_ring.len() - 1;
505 let tree = build_ring_edge_tree(build_ring);
506
507 for i in 0..n_query {
508 let a1 = query_ring[i];
509 let a2 = query_ring[(i + 1) % n_query];
510 let (lo_x, hi_x) = if a1.x < a2.x {
511 (a1.x, a2.x)
512 } else {
513 (a2.x, a1.x)
514 };
515 let (lo_y, hi_y) = if a1.y < a2.y {
516 (a1.y, a2.y)
517 } else {
518 (a2.y, a1.y)
519 };
520 let query = rstar::AABB::from_corners([lo_x, lo_y], [hi_x, hi_y]);
521 let found = tree.locate_in_envelope_intersecting_int(&query, |c| {
522 let b1 = build_ring[c.idx];
523 let b2 = build_ring[(c.idx + 1) % n_build];
524 if edges_intersect_general(a1, a2, b1, b2, eps) {
525 std::ops::ControlFlow::Break(())
526 } else {
527 std::ops::ControlFlow::<(), ()>::Continue(())
528 }
529 });
530 if found.is_break() {
531 return true;
532 }
533 }
534 }
535 #[cfg(not(feature = "rstar"))]
536 {
537 for i in 0..n1 {
538 let a1 = ring1[i];
539 let a2 = ring1[(i + 1) % n1];
540 for j in 0..n2 {
541 let b1 = ring2[j];
542 let b2 = ring2[(j + 1) % n2];
543 if edges_intersect_general(a1, a2, b1, b2, eps) {
544 return true;
545 }
546 }
547 }
548 }
549 false
550}
551
552pub(crate) fn check_orientation(ring: &[Coord<f64>]) -> bool {
553 if ring.len() < 4 {
554 return true;
555 }
556 crate::util::shoelace_sum(ring) > 0.0
557}
558
559pub(crate) fn point_in_ring_exclusive(pt: Coord<f64>, ring: &[Coord<f64>]) -> bool {
560 let n = ring.len();
561 let mut wn = 0i32;
562 for i in 0..n - 1 {
563 let p1 = ring[i];
564 let p2 = ring[i + 1];
565 if p1.y <= pt.y {
566 if p2.y > pt.y {
567 let o = (p2.x - p1.x) * (pt.y - p1.y) - (p2.y - p1.y) * (pt.x - p1.x);
568 if o > 0.0 {
569 wn += 1;
570 }
571 }
572 } else if p2.y <= pt.y {
573 let o = (p2.x - p1.x) * (pt.y - p1.y) - (p2.y - p1.y) * (pt.x - p1.x);
574 if o < 0.0 {
575 wn -= 1;
576 }
577 }
578 }
579 wn != 0
580}
581
582pub(crate) fn point_on_segment(pt: Coord<f64>, a: Coord<f64>, b: Coord<f64>, eps: f64) -> bool {
583 let o = (b.x - a.x) * (pt.y - a.y) - (b.y - a.y) * (pt.x - a.x);
584 if o.abs() > eps {
585 return false;
586 }
587 let min_x = a.x.min(b.x) - eps;
588 let max_x = a.x.max(b.x) + eps;
589 let min_y = a.y.min(b.y) - eps;
590 let max_y = a.y.max(b.y) + eps;
591 pt.x >= min_x && pt.x <= max_x && pt.y >= min_y && pt.y <= max_y
592}
593
594pub(crate) fn point_on_ring(pt: Coord<f64>, ring: &[Coord<f64>], eps: f64) -> bool {
595 let n = ring.len() - 1;
596 if n == 0 {
597 return false;
598 }
599 for i in 0..n {
600 if point_on_segment(pt, ring[i], ring[(i + 1) % n], eps) {
601 return true;
602 }
603 }
604 false
605}
606
607pub(crate) fn ring_dup_fingerprint(ring: &[Coord<f64>]) -> (usize, u64) {
614 let n = ring.len() - 1;
615 if n == 0 {
616 return (ring.len(), 0);
617 }
618 let min_idx = {
619 let mut idx = 0usize;
620 for i in 1..n {
621 let c = ring[i];
622 let m = ring[idx];
623 if c.x < m.x || (c.x == m.x && c.y < m.y) {
624 idx = i;
625 }
626 }
627 idx
628 };
629 let mut h_fwd = 0u64;
630 let mut h_rev = 0u64;
631 for i in 0..n {
632 let c = ring[(min_idx + i) % n];
633 h_fwd = h_fwd
634 .wrapping_mul(6364136223846793005)
635 .wrapping_add(c.x.to_bits());
636 h_fwd = h_fwd
637 .wrapping_mul(6364136223846793005)
638 .wrapping_add(c.y.to_bits());
639 let d = ring[(min_idx + n - i) % n];
640 h_rev = h_rev
641 .wrapping_mul(6364136223846793005)
642 .wrapping_add(d.x.to_bits());
643 h_rev = h_rev
644 .wrapping_mul(6364136223846793005)
645 .wrapping_add(d.y.to_bits());
646 }
647 (ring.len(), h_fwd ^ h_rev)
648}
649
650pub(crate) fn is_rotated_duplicate(a: &[Coord<f64>], b: &[Coord<f64>]) -> bool {
654 if a.len() != b.len() || a.len() < 2 {
655 return false;
656 }
657 let n = a.len() - 1;
659 if n == 0 {
660 return false;
661 }
662 for start in 0..n {
664 if a[start] != b[0] {
665 continue;
666 }
667 let mut match_ = true;
668 for i in 0..n {
669 if a[(start + i) % n] != b[i] {
670 match_ = false;
671 break;
672 }
673 }
674 if match_ {
675 return true;
676 }
677 }
678 for start in 0..n {
680 if a[start] != b[0] {
681 continue;
682 }
683 let mut match_ = true;
684 for i in 0..n {
685 if a[(start + n - i) % n] != b[i] {
686 match_ = false;
687 break;
688 }
689 }
690 if match_ {
691 return true;
692 }
693 }
694 false
695}
696
697pub(crate) fn check_holes_valid(
698 shell: &[Coord<f64>],
699 interiors: &[LineString<f64>],
700) -> Vec<GeometryValidationError> {
701 let mut errors = Vec::new();
702
703 #[cfg(feature = "simd")]
705 let (min_x, max_x, min_y, max_y) = crate::simd::aabb_minmax_simd(shell);
706 #[cfg(not(feature = "simd"))]
707 let (mut min_x, mut max_x, mut min_y, mut max_y) = (f64::MAX, f64::MIN, f64::MAX, f64::MIN);
708 #[cfg(not(feature = "simd"))]
709 for c in shell {
710 min_x = min_x.min(c.x);
711 max_x = max_x.max(c.x);
712 min_y = min_y.min(c.y);
713 max_y = max_y.max(c.y);
714 }
715 let scale = (max_x - min_x).abs().max((max_y - min_y).abs()).max(1.0);
716 let eps = 1e-12 * scale;
717
718 for hole in interiors {
719 if check_rings_intersect(&hole.0[..], shell, eps) {
721 errors.push(GeometryValidationError::HoleOutsideShell);
722 return errors;
723 }
724
725 let touch_count = hole
727 .0
728 .iter()
729 .filter(|&&hp| point_on_ring(hp, shell, eps))
730 .count();
731 if touch_count >= 2 {
732 errors.push(GeometryValidationError::DisconnectedInteriorRing);
733 return errors;
734 }
735
736 let any_inside = hole.0.iter().any(|&hp| point_in_ring_exclusive(hp, shell));
739 if !any_inside {
740 errors.push(GeometryValidationError::HoleOutsideShell);
741 return errors;
742 }
743 }
744 let holes: Vec<&[Coord<f64>]> = interiors.iter().map(|h| &h.0[..]).collect();
745 if holes.len() > 1 {
746 for i in 0..holes.len() {
748 for j in (i + 1)..holes.len() {
749 if check_rings_intersect(holes[i], holes[j], eps) {
750 errors.push(GeometryValidationError::DisconnectedInteriorRing);
751 return errors;
752 }
753 }
754 }
755
756 #[cfg(feature = "rstar")]
758 {
759 struct HoleEnv2 {
760 idx: usize,
761 env: rstar::AABB<[f64; 2]>,
762 }
763 impl rstar::RTreeObject for HoleEnv2 {
764 type Envelope = rstar::AABB<[f64; 2]>;
765 fn envelope(&self) -> Self::Envelope {
766 self.env
767 }
768 }
769 let mut envs = Vec::with_capacity(holes.len());
770 for (i, h) in holes.iter().enumerate() {
771 let first = h.first().map(|c| (c.x, c.y)).unwrap_or((0.0, 0.0));
772 let (mut min_x, mut max_x, mut min_y, mut max_y) =
773 (first.0, first.0, first.1, first.1);
774 for c in *h {
775 min_x = min_x.min(c.x);
776 max_x = max_x.max(c.x);
777 min_y = min_y.min(c.y);
778 max_y = max_y.max(c.y);
779 }
780 envs.push(HoleEnv2 {
781 idx: i,
782 env: rstar::AABB::from_corners([min_x, min_y], [max_x, max_y]),
783 });
784 }
785 let tree = rstar::RTree::bulk_load(envs);
786 for (i, h2) in holes.iter().enumerate() {
787 let Some(pt) = h2.first().copied() else {
788 continue;
789 };
790 let query = rstar::AABB::from_corners([pt.x, pt.y], [pt.x, pt.y]);
791 let mut overlaps = false;
792 let _ = tree.locate_in_envelope_intersecting_int(&query, |c| {
793 if c.idx != i && point_in_ring_exclusive(pt, holes[c.idx]) {
794 overlaps = true;
795 std::ops::ControlFlow::Break(())
796 } else {
797 std::ops::ControlFlow::<(), ()>::Continue(())
798 }
799 });
800 if overlaps {
801 errors.push(GeometryValidationError::NestedHoles);
802 return errors;
803 }
804 }
805 }
806 #[cfg(not(feature = "rstar"))]
807 {
808 for i in 0..holes.len() {
809 for j in 0..holes.len() {
810 if i == j {
811 continue;
812 }
813 if let Some(pt) = holes[j].first().copied()
814 && point_in_ring_exclusive(pt, holes[i]) {
815 errors.push(GeometryValidationError::NestedHoles);
816 return errors;
817 }
818 }
819 }
820 }
821 }
822 errors
823}
824
825impl GeoValidation for Point<f64> {
826 type Scalar = f64;
827
828 fn validate(&self) -> ValidationResult {
829 if !self.0.x.is_finite() || !self.0.y.is_finite() {
830 return ValidationResult::invalid(vec![GeometryValidationError::CoordinateNaN]);
831 }
832 ValidationResult::valid()
833 }
834}
835
836impl GeoValidation for MultiPoint<f64> {
837 type Scalar = f64;
838
839 fn validate(&self) -> ValidationResult {
840 let mut errors = Vec::new();
841 for p in &self.0 {
842 let r = p.validate();
843 if !r.valid {
844 errors.extend(r.errors);
845 }
846 }
847 if self.0.len() > 1 {
849 let mut seen: rustc_hash::FxHashSet<(u64, u64)> =
850 rustc_hash::FxHashSet::with_capacity_and_hasher(self.0.len(), Default::default());
851 for p in &self.0 {
852 let key = (p.x().to_bits(), p.y().to_bits());
853 if !seen.insert(key) {
854 errors.push(GeometryValidationError::MultiPointDuplicatePoints);
855 break;
856 }
857 }
858 }
859 if errors.is_empty() {
860 ValidationResult::valid()
861 } else {
862 ValidationResult::invalid(errors)
863 }
864 }
865}
866
867impl GeoValidation for Line<f64> {
868 type Scalar = f64;
869
870 fn validate(&self) -> ValidationResult {
871 if !self.start.x.is_finite()
872 || !self.start.y.is_finite()
873 || !self.end.x.is_finite()
874 || !self.end.y.is_finite()
875 {
876 return ValidationResult::invalid(vec![GeometryValidationError::CoordinateNaN]);
877 }
878 if self.start == self.end {
879 return ValidationResult::invalid(vec![GeometryValidationError::ZeroLengthLine(
880 self.start,
881 )]);
882 }
883 ValidationResult::valid()
884 }
885}
886
887impl GeoValidation for LineString<f64> {
888 type Scalar = f64;
889
890 fn validate(&self) -> ValidationResult {
891 let coords = &self.0;
892 if coords.len() < 2 {
893 return ValidationResult::invalid(vec![GeometryValidationError::RingTooFewPoints {
894 found: coords.len(),
895 min: 2,
896 }]);
897 }
898 if ring_has_non_finite(coords) {
899 return ValidationResult::invalid(vec![GeometryValidationError::CoordinateNaN]);
900 }
901 for i in 1..coords.len() {
902 if coords[i] == coords[i - 1] {
903 return ValidationResult::invalid(vec![GeometryValidationError::RepeatedPoint]);
904 }
905 }
906 if check_linestring_self_intersection(coords) {
908 return ValidationResult::invalid(vec![GeometryValidationError::NotSimple]);
909 }
910 ValidationResult::valid()
911 }
912}
913
914pub(crate) fn check_linestring_self_intersection(coords: &[Coord<f64>]) -> bool {
916 let n = coords.len() - 1;
917 if n < 3 {
918 return false;
919 }
920 let scale = {
921 let mut min_x = f64::MAX;
922 let mut max_x = f64::MIN;
923 let mut min_y = f64::MAX;
924 let mut max_y = f64::MIN;
925 for c in coords {
926 min_x = min_x.min(c.x);
927 max_x = max_x.max(c.x);
928 min_y = min_y.min(c.y);
929 max_y = max_y.max(c.y);
930 }
931 (max_x - min_x).abs().max((max_y - min_y).abs()).max(1.0)
932 };
933 let eps = 1e-12 * scale;
934
935 if n <= 64 {
937 for i in 0..n {
938 let a1 = coords[i];
939 let a2 = coords[i + 1];
940 for j in i + 2..n {
941 let b1 = coords[j];
942 let b2 = coords[j + 1];
943 if edges_intersect_general(a1, a2, b1, b2, eps) {
944 return true;
945 }
946 }
947 }
948 return false;
949 }
950
951 #[cfg(feature = "rstar")]
952 {
953 let tree = build_ls_edge_tree(coords);
954 for i in 0..n {
955 let a1 = coords[i];
956 let a2 = coords[i + 1];
957 let (lo_x, hi_x) = if a1.x < a2.x {
958 (a1.x, a2.x)
959 } else {
960 (a2.x, a1.x)
961 };
962 let (lo_y, hi_y) = if a1.y < a2.y {
963 (a1.y, a2.y)
964 } else {
965 (a2.y, a1.y)
966 };
967 let query = rstar::AABB::from_corners([lo_x, lo_y], [hi_x, hi_y]);
968 let found = tree.locate_in_envelope_intersecting_int(&query, |c| {
969 let j = c.idx;
970 if j <= i + 1 {
971 return std::ops::ControlFlow::<(), ()>::Continue(());
972 }
973 let b1 = coords[j];
974 let b2 = coords[j + 1];
975 if edges_intersect_general(a1, a2, b1, b2, eps) {
976 std::ops::ControlFlow::Break(())
977 } else {
978 std::ops::ControlFlow::<(), ()>::Continue(())
979 }
980 });
981 if found.is_break() {
982 return true;
983 }
984 }
985 false
986 }
987 #[cfg(not(feature = "rstar"))]
988 {
989 for i in 0..n {
990 let a1 = coords[i];
991 let a2 = coords[i + 1];
992 for j in i + 2..n {
993 let b1 = coords[j];
994 let b2 = coords[j + 1];
995 if edges_intersect_general(a1, a2, b1, b2, eps) {
996 return true;
997 }
998 }
999 }
1000 false
1001 }
1002}
1003
1004pub(crate) fn check_line_components_intersect(
1006 ls1: &[Coord<f64>],
1007 ls2: &[Coord<f64>],
1008 eps: f64,
1009) -> bool {
1010 let n1 = ls1.len();
1011 let n2 = ls2.len();
1012 if n1 < 2 || n2 < 2 {
1013 return false;
1014 }
1015
1016 if n1.max(n2) <= 64 {
1018 for i in 0..n1 - 1 {
1019 let a1 = ls1[i];
1020 let a2 = ls1[i + 1];
1021 for j in 0..n2 - 1 {
1022 let b1 = ls2[j];
1023 let b2 = ls2[j + 1];
1024 if edges_intersect_general(a1, a2, b1, b2, eps) {
1025 return true;
1026 }
1027 }
1028 }
1029 return false;
1030 }
1031
1032 #[cfg(feature = "rstar")]
1033 {
1034 let (small, large) = if n1 < n2 { (ls1, ls2) } else { (ls2, ls1) };
1035 let n_small = small.len();
1036 let tree = build_ls_edge_tree(large);
1037
1038 for i in 0..n_small - 1 {
1039 let a1 = small[i];
1040 let a2 = small[i + 1];
1041 let (lo_x, hi_x) = if a1.x < a2.x {
1042 (a1.x, a2.x)
1043 } else {
1044 (a2.x, a1.x)
1045 };
1046 let (lo_y, hi_y) = if a1.y < a2.y {
1047 (a1.y, a2.y)
1048 } else {
1049 (a2.y, a1.y)
1050 };
1051 let query = rstar::AABB::from_corners([lo_x, lo_y], [hi_x, hi_y]);
1052 let found = tree.locate_in_envelope_intersecting_int(&query, |c| {
1053 let b1 = large[c.idx];
1054 let b2 = large[c.idx + 1];
1055 if edges_intersect_general(a1, a2, b1, b2, eps) {
1056 std::ops::ControlFlow::Break(())
1057 } else {
1058 std::ops::ControlFlow::<(), ()>::Continue(())
1059 }
1060 });
1061 if found.is_break() {
1062 return true;
1063 }
1064 }
1065 false
1066 }
1067 #[cfg(not(feature = "rstar"))]
1068 {
1069 for i in 0..n1 - 1 {
1070 let a1 = ls1[i];
1071 let a2 = ls1[i + 1];
1072 for j in 0..n2 - 1 {
1073 let b1 = ls2[j];
1074 let b2 = ls2[j + 1];
1075 if edges_intersect_general(a1, a2, b1, b2, eps) {
1076 return true;
1077 }
1078 }
1079 }
1080 false
1081 }
1082}
1083
1084impl GeoValidation for MultiLineString<f64> {
1085 type Scalar = f64;
1086
1087 fn validate(&self) -> ValidationResult {
1088 let mut errors = Vec::new();
1089 for ls in &self.0 {
1090 let r = ls.validate();
1091 if !r.valid {
1092 errors.extend(r.errors);
1093 }
1094 }
1095 if self.0.len() > 1 {
1097 let mut seen: rustc_hash::FxHashSet<Vec<(u64, u64)>> =
1098 rustc_hash::FxHashSet::with_capacity_and_hasher(self.0.len(), Default::default());
1099 for ls in &self.0 {
1100 let key: Vec<(u64, u64)> =
1101 ls.0.iter()
1102 .map(|c| (c.x.to_bits(), c.y.to_bits()))
1103 .collect();
1104 if !seen.insert(key) {
1105 errors.push(GeometryValidationError::MultiLineStringDuplicateLines);
1106 return ValidationResult::invalid(errors);
1107 }
1108 }
1109 }
1110 if self.0.len() > 1 {
1112 let (mut gmin_x, mut gmax_x, mut gmin_y, mut gmax_y) =
1114 (f64::MAX, f64::MIN, f64::MAX, f64::MIN);
1115 for ls in &self.0 {
1116 for c in &ls.0 {
1117 gmin_x = gmin_x.min(c.x);
1118 gmax_x = gmax_x.max(c.x);
1119 gmin_y = gmin_y.min(c.y);
1120 gmax_y = gmax_y.max(c.y);
1121 }
1122 }
1123 let scale = (gmax_x - gmin_x)
1124 .abs()
1125 .max((gmax_y - gmin_y).abs())
1126 .max(1.0);
1127 let eps = 1e-12 * scale;
1128
1129 for i in 0..self.0.len() {
1130 for j in (i + 1)..self.0.len() {
1131 if check_line_components_intersect(&self.0[i].0, &self.0[j].0, eps) {
1132 errors.push(GeometryValidationError::NotSimple);
1133 return ValidationResult::invalid(errors);
1134 }
1135 }
1136 }
1137 }
1138 if errors.is_empty() {
1139 ValidationResult::valid()
1140 } else {
1141 ValidationResult::invalid(errors)
1142 }
1143 }
1144}
1145
1146impl GeoValidation for Rect<f64> {
1147 type Scalar = f64;
1148
1149 fn validate(&self) -> ValidationResult {
1150 if !self.min().x.is_finite()
1151 || !self.min().y.is_finite()
1152 || !self.max().x.is_finite()
1153 || !self.max().y.is_finite()
1154 {
1155 return ValidationResult::invalid(vec![GeometryValidationError::CoordinateNaN]);
1156 }
1157 if (self.max().x - self.min().x).abs() < f64::EPSILON
1158 || (self.max().y - self.min().y).abs() < f64::EPSILON
1159 {
1160 return ValidationResult::invalid(vec![GeometryValidationError::DegenerateExterior]);
1161 }
1162 ValidationResult::valid()
1163 }
1164}
1165
1166impl GeoValidation for Triangle<f64> {
1167 type Scalar = f64;
1168
1169 fn validate(&self) -> ValidationResult {
1170 let coords = [self.v1(), self.v2(), self.v3()];
1171 if ring_has_non_finite(&coords) {
1172 return ValidationResult::invalid(vec![GeometryValidationError::CoordinateNaN]);
1173 }
1174 if coords[0] == coords[1] || coords[1] == coords[2] || coords[0] == coords[2] {
1175 return ValidationResult::invalid(vec![GeometryValidationError::DegenerateExterior]);
1176 }
1177 let area = ((coords[1].x - coords[0].x) * (coords[2].y - coords[0].y)
1179 - (coords[1].y - coords[0].y) * (coords[2].x - coords[0].x))
1180 .abs();
1181 if area < 1e-12 {
1182 return ValidationResult::invalid(vec![GeometryValidationError::CollinearRing]);
1183 }
1184 ValidationResult::valid()
1185 }
1186}
1187
1188pub fn is_valid(geom: &geo::Geometry<f64>) -> bool {
1197 GeoValidation::is_valid(geom)
1198}
1199
1200pub fn validate(geom: &geo::Geometry<f64>) -> ValidationResult {
1204 GeoValidation::validate(geom)
1205}
1206
1207pub fn validate_reason(geom: &geo::Geometry<f64>) -> String {
1213 GeoValidation::validate_reason(geom)
1214}