1use crate::geometry::*;
2use crate::intersects::point_in_rect;
3use crate::kernels::*;
4use crate::{BoundingRect, HasDimensions, Intersects};
5use crate::{GeoNum, GeometryCow};
6
7#[derive(PartialEq, Eq, Clone, Copy, Debug)]
9pub enum CoordPos {
10 OnBoundary,
11 Inside,
12 Outside,
13}
14
15pub trait CoordinatePosition {
35 type Scalar: GeoNum;
36 fn coordinate_position(&self, coord: &Coord<Self::Scalar>) -> CoordPos {
37 let mut is_inside = false;
38 let mut boundary_count = 0;
39
40 self.calculate_coordinate_position(coord, &mut is_inside, &mut boundary_count);
41
42 if boundary_count % 2 == 1 {
48 CoordPos::OnBoundary
49 } else if is_inside {
50 CoordPos::Inside
51 } else {
52 CoordPos::Outside
53 }
54 }
55
56 fn calculate_coordinate_position(
60 &self,
61 coord: &Coord<Self::Scalar>,
62 is_inside: &mut bool,
63 boundary_count: &mut usize,
64 );
65}
66
67impl<T> CoordinatePosition for Coord<T>
68where
69 T: GeoNum,
70{
71 type Scalar = T;
72 fn calculate_coordinate_position(
73 &self,
74 coord: &Coord<T>,
75 is_inside: &mut bool,
76 _boundary_count: &mut usize,
77 ) {
78 if self == coord {
79 *is_inside = true;
80 }
81 }
82}
83
84impl<T> CoordinatePosition for Point<T>
85where
86 T: GeoNum,
87{
88 type Scalar = T;
89 fn calculate_coordinate_position(
90 &self,
91 coord: &Coord<T>,
92 is_inside: &mut bool,
93 _boundary_count: &mut usize,
94 ) {
95 if &self.0 == coord {
96 *is_inside = true;
97 }
98 }
99}
100
101impl<T> CoordinatePosition for Line<T>
102where
103 T: GeoNum,
104{
105 type Scalar = T;
106 fn calculate_coordinate_position(
107 &self,
108 coord: &Coord<T>,
109 is_inside: &mut bool,
110 boundary_count: &mut usize,
111 ) {
112 if self.start == self.end {
114 self.start
115 .calculate_coordinate_position(coord, is_inside, boundary_count);
116 return;
117 }
118
119 if coord == &self.start || coord == &self.end {
120 *boundary_count += 1;
121 } else if self.intersects(coord) {
122 *is_inside = true;
123 }
124 }
125}
126
127impl<T> CoordinatePosition for LineString<T>
128where
129 T: GeoNum,
130{
131 type Scalar = T;
132 fn calculate_coordinate_position(
133 &self,
134 coord: &Coord<T>,
135 is_inside: &mut bool,
136 boundary_count: &mut usize,
137 ) {
138 if self.0.len() < 2 {
139 debug_assert!(false, "invalid line string with less than 2 coords");
140 return;
141 }
142
143 if self.0.len() == 2 {
144 Line::new(self.0[0], self.0[1]).calculate_coordinate_position(
146 coord,
147 is_inside,
148 boundary_count,
149 );
150 return;
151 }
152
153 if !self.bounding_rect().unwrap().intersects(coord) {
156 return;
157 }
158
159 if !self.is_closed() {
161 if coord == self.0.first().unwrap() || coord == self.0.last().unwrap() {
163 *boundary_count += 1;
164 return;
165 }
166 }
167
168 if self.intersects(coord) {
169 *is_inside = true
172 }
173 }
174}
175
176impl<T> CoordinatePosition for Triangle<T>
177where
178 T: GeoNum,
179{
180 type Scalar = T;
181 fn calculate_coordinate_position(
182 &self,
183 coord: &Coord<T>,
184 is_inside: &mut bool,
185 boundary_count: &mut usize,
186 ) {
187 self.to_polygon()
189 .calculate_coordinate_position(coord, is_inside, boundary_count);
190 }
191}
192
193impl<T> CoordinatePosition for Rect<T>
194where
195 T: GeoNum,
196{
197 type Scalar = T;
198 fn calculate_coordinate_position(
199 &self,
200 coord: &Coord<T>,
201 is_inside: &mut bool,
202 boundary_count: &mut usize,
203 ) {
204 self.to_polygon()
206 .calculate_coordinate_position(coord, is_inside, boundary_count);
207 }
208}
209
210impl<T> CoordinatePosition for MultiPoint<T>
211where
212 T: GeoNum,
213{
214 type Scalar = T;
215 fn calculate_coordinate_position(
216 &self,
217 coord: &Coord<T>,
218 is_inside: &mut bool,
219 _boundary_count: &mut usize,
220 ) {
221 if self.0.iter().any(|p| &p.0 == coord) {
222 *is_inside = true;
223 }
224 }
225}
226
227impl<T> CoordinatePosition for Polygon<T>
228where
229 T: GeoNum,
230{
231 type Scalar = T;
232 fn calculate_coordinate_position(
233 &self,
234 coord: &Coord<T>,
235 is_inside: &mut bool,
236 boundary_count: &mut usize,
237 ) {
238 if self.is_empty() {
239 return;
240 }
241
242 if !self.bounding_rect().unwrap().intersects(coord) {
244 return;
245 }
246
247 match coord_pos_relative_to_ring(*coord, self.exterior()) {
248 CoordPos::Outside => {}
249 CoordPos::OnBoundary => {
250 *boundary_count += 1;
251 }
252 CoordPos::Inside => {
253 for hole in self.interiors() {
254 match coord_pos_relative_to_ring(*coord, hole) {
255 CoordPos::Outside => {}
256 CoordPos::OnBoundary => {
257 *boundary_count += 1;
258 return;
259 }
260 CoordPos::Inside => {
261 return;
262 }
263 }
264 }
265 *is_inside = true;
267 }
268 }
269 }
270}
271
272impl<T> CoordinatePosition for MultiLineString<T>
273where
274 T: GeoNum,
275{
276 type Scalar = T;
277 fn calculate_coordinate_position(
278 &self,
279 coord: &Coord<T>,
280 is_inside: &mut bool,
281 boundary_count: &mut usize,
282 ) {
283 for line_string in &self.0 {
284 line_string.calculate_coordinate_position(coord, is_inside, boundary_count);
285 }
286 }
287}
288
289impl<T> CoordinatePosition for MultiPolygon<T>
290where
291 T: GeoNum,
292{
293 type Scalar = T;
294 fn calculate_coordinate_position(
295 &self,
296 coord: &Coord<T>,
297 is_inside: &mut bool,
298 boundary_count: &mut usize,
299 ) {
300 for polygon in &self.0 {
301 polygon.calculate_coordinate_position(coord, is_inside, boundary_count);
302 }
303 }
304}
305
306impl<T> CoordinatePosition for GeometryCollection<T>
307where
308 T: GeoNum,
309{
310 type Scalar = T;
311 fn calculate_coordinate_position(
312 &self,
313 coord: &Coord<T>,
314 is_inside: &mut bool,
315 boundary_count: &mut usize,
316 ) {
317 for geometry in self {
318 geometry.calculate_coordinate_position(coord, is_inside, boundary_count);
319 }
320 }
321}
322
323impl<T> CoordinatePosition for Geometry<T>
324where
325 T: GeoNum,
326{
327 type Scalar = T;
328 crate::geometry_delegate_impl! {
329 fn calculate_coordinate_position(
330 &self,
331 coord: &Coord<T>,
332 is_inside: &mut bool,
333 boundary_count: &mut usize) -> ();
334 }
335}
336
337impl<'a, T: GeoNum> CoordinatePosition for GeometryCow<'a, T> {
338 type Scalar = T;
339 crate::geometry_cow_delegate_impl! {
340 fn calculate_coordinate_position(
341 &self,
342 coord: &Coord<T>,
343 is_inside: &mut bool,
344 boundary_count: &mut usize) -> ();
345 }
346}
347
348pub fn coord_pos_relative_to_ring<T>(coord: Coord<T>, linestring: &LineString<T>) -> CoordPos
351where
352 T: GeoNum,
353{
354 debug_assert!(linestring.is_closed());
355
356 if linestring.0.is_empty() {
358 return CoordPos::Outside;
359 }
360 if linestring.0.len() == 1 {
361 return if coord == linestring.0[0] {
364 CoordPos::OnBoundary
365 } else {
366 CoordPos::Outside
367 };
368 }
369
370 let mut winding_number = 0;
373 for line in linestring.lines() {
374 if line.start.y <= coord.y {
375 let o = T::Ker::orient2d(line.start, line.end, coord);
376 if o == Orientation::Collinear && point_in_rect(coord, line.start, line.end) {
377 return CoordPos::OnBoundary;
378 }
379 if line.end.y > coord.y && o == Orientation::CounterClockwise {
380 winding_number += 1;
381 }
382 } else if line.end.y <= coord.y {
383 let o = T::Ker::orient2d(line.start, line.end, coord);
384 if o == Orientation::Collinear && point_in_rect(coord, line.start, line.end) {
385 return CoordPos::OnBoundary;
386 }
387 if o == Orientation::Clockwise {
388 winding_number -= 1;
389 }
390 }
391 }
392 if winding_number == 0 {
393 CoordPos::Outside
394 } else {
395 CoordPos::Inside
396 }
397}
398
399#[cfg(test)]
400mod test {
401 use geo_types::coord;
402
403 use super::*;
404 use crate::{line_string, point, polygon};
405
406 #[test]
407 fn test_empty_poly() {
408 let square_poly: Polygon<f64> = Polygon::new(LineString::new(vec![]), vec![]);
409 assert_eq!(
410 square_poly.coordinate_position(&Coord::zero()),
411 CoordPos::Outside
412 );
413 }
414
415 #[test]
416 fn test_simple_poly() {
417 let square_poly = polygon![(x: 0.0, y: 0.0), (x: 2.0, y: 0.0), (x: 2.0, y: 2.0), (x: 0.0, y: 2.0), (x: 0.0, y: 0.0)];
418
419 let inside_coord = coord! { x: 1.0, y: 1.0 };
420 assert_eq!(
421 square_poly.coordinate_position(&inside_coord),
422 CoordPos::Inside
423 );
424
425 let vertex_coord = coord! { x: 0.0, y: 0.0 };
426 assert_eq!(
427 square_poly.coordinate_position(&vertex_coord),
428 CoordPos::OnBoundary
429 );
430
431 let boundary_coord = coord! { x: 0.0, y: 1.0 };
432 assert_eq!(
433 square_poly.coordinate_position(&boundary_coord),
434 CoordPos::OnBoundary
435 );
436
437 let outside_coord = coord! { x: 5.0, y: 5.0 };
438 assert_eq!(
439 square_poly.coordinate_position(&outside_coord),
440 CoordPos::Outside
441 );
442 }
443
444 #[test]
445 fn test_poly_interior() {
446 let poly = polygon![
447 exterior: [
448 (x: 11., y: 11.),
449 (x: 20., y: 11.),
450 (x: 20., y: 20.),
451 (x: 11., y: 20.),
452 (x: 11., y: 11.),
453 ],
454 interiors: [
455 [
456 (x: 13., y: 13.),
457 (x: 13., y: 17.),
458 (x: 17., y: 17.),
459 (x: 17., y: 13.),
460 (x: 13., y: 13.),
461 ]
462 ],
463 ];
464
465 let inside_hole = coord! { x: 14.0, y: 14.0 };
466 assert_eq!(poly.coordinate_position(&inside_hole), CoordPos::Outside);
467
468 let outside_poly = coord! { x: 30.0, y: 30.0 };
469 assert_eq!(poly.coordinate_position(&outside_poly), CoordPos::Outside);
470
471 let on_outside_border = coord! { x: 20.0, y: 15.0 };
472 assert_eq!(
473 poly.coordinate_position(&on_outside_border),
474 CoordPos::OnBoundary
475 );
476
477 let on_inside_border = coord! { x: 13.0, y: 15.0 };
478 assert_eq!(
479 poly.coordinate_position(&on_inside_border),
480 CoordPos::OnBoundary
481 );
482
483 let inside_coord = coord! { x: 12.0, y: 12.0 };
484 assert_eq!(poly.coordinate_position(&inside_coord), CoordPos::Inside);
485 }
486
487 #[test]
488 fn test_simple_line() {
489 use crate::point;
490 let line = Line::new(point![x: 0.0, y: 0.0], point![x: 10.0, y: 10.0]);
491
492 let start = coord! { x: 0.0, y: 0.0 };
493 assert_eq!(line.coordinate_position(&start), CoordPos::OnBoundary);
494
495 let end = coord! { x: 10.0, y: 10.0 };
496 assert_eq!(line.coordinate_position(&end), CoordPos::OnBoundary);
497
498 let interior = coord! { x: 5.0, y: 5.0 };
499 assert_eq!(line.coordinate_position(&interior), CoordPos::Inside);
500
501 let outside = coord! { x: 6.0, y: 5.0 };
502 assert_eq!(line.coordinate_position(&outside), CoordPos::Outside);
503 }
504
505 #[test]
506 fn test_degenerate_line() {
507 let line = Line::new(point![x: 0.0, y: 0.0], point![x: 0.0, y: 0.0]);
508
509 let start = coord! { x: 0.0, y: 0.0 };
510 assert_eq!(line.coordinate_position(&start), CoordPos::Inside);
511
512 let outside = coord! { x: 10.0, y: 10.0 };
513 assert_eq!(line.coordinate_position(&outside), CoordPos::Outside);
514 }
515
516 #[test]
517 fn test_point() {
518 let p1 = point![x: 2.0, y: 0.0];
519
520 let c1 = coord! { x: 2.0, y: 0.0 };
521 let c2 = coord! { x: 3.0, y: 3.0 };
522
523 assert_eq!(p1.coordinate_position(&c1), CoordPos::Inside);
524 assert_eq!(p1.coordinate_position(&c2), CoordPos::Outside);
525
526 assert_eq!(c1.coordinate_position(&c1), CoordPos::Inside);
527 assert_eq!(c1.coordinate_position(&c2), CoordPos::Outside);
528 }
529
530 #[test]
531 fn test_simple_line_string() {
532 let line_string =
533 line_string![(x: 0.0, y: 0.0), (x: 1.0, y: 1.0), (x: 2.0, y: 0.0), (x: 3.0, y: 0.0)];
534
535 let start = Coord::zero();
536 assert_eq!(
537 line_string.coordinate_position(&start),
538 CoordPos::OnBoundary
539 );
540
541 let midpoint = coord! { x: 0.5, y: 0.5 };
542 assert_eq!(line_string.coordinate_position(&midpoint), CoordPos::Inside);
543
544 let vertex = coord! { x: 2.0, y: 0.0 };
545 assert_eq!(line_string.coordinate_position(&vertex), CoordPos::Inside);
546
547 let end = coord! { x: 3.0, y: 0.0 };
548 assert_eq!(line_string.coordinate_position(&end), CoordPos::OnBoundary);
549
550 let outside = coord! { x: 3.0, y: 1.0 };
551 assert_eq!(line_string.coordinate_position(&outside), CoordPos::Outside);
552 }
553
554 #[test]
555 fn test_degenerate_line_strings() {
556 let line_string = line_string![(x: 0.0, y: 0.0), (x: 0.0, y: 0.0)];
557
558 let start = Coord::zero();
559 assert_eq!(line_string.coordinate_position(&start), CoordPos::Inside);
560
561 let line_string = line_string![(x: 0.0, y: 0.0), (x: 2.0, y: 0.0)];
562
563 let start = Coord::zero();
564 assert_eq!(
565 line_string.coordinate_position(&start),
566 CoordPos::OnBoundary
567 );
568 }
569
570 #[test]
571 fn test_closed_line_string() {
572 let line_string = line_string![(x: 0.0, y: 0.0), (x: 1.0, y: 1.0), (x: 2.0, y: 0.0), (x: 3.0, y: 2.0), (x: 0.0, y: 2.0), (x: 0.0, y: 0.0)];
573
574 assert!(line_string.is_closed());
576
577 let start = Coord::zero();
579 assert_eq!(line_string.coordinate_position(&start), CoordPos::Inside);
580
581 let midpoint = coord! { x: 0.5, y: 0.5 };
582 assert_eq!(line_string.coordinate_position(&midpoint), CoordPos::Inside);
583
584 let outside = coord! { x: 3.0, y: 1.0 };
585 assert_eq!(line_string.coordinate_position(&outside), CoordPos::Outside);
586 }
587
588 #[test]
589 fn test_boundary_rule() {
590 let multi_line_string = MultiLineString::new(vec![
591 line_string![(x: 0.0, y: 0.0), (x: 1.0, y: 1.0)],
593 line_string![(x: 0.0, y: 0.0), (x: -1.0, y: -1.0)],
594 line_string![(x: 0.0, y: 1.0), (x: 0.5, y: 0.5)],
596 line_string![(x: 0.0, y: -1.0), (x: -0.5, y: -0.5)],
599 line_string![(x: 0.0, y: -2.0), (x: -0.5, y: -0.5)],
600 ]);
601
602 let outside_of_all = coord! { x: 123.0, y: 123.0 };
603 assert_eq!(
604 multi_line_string.coordinate_position(&outside_of_all),
605 CoordPos::Outside
606 );
607
608 let end_of_one_line = coord! { x: -1.0, y: -1.0 };
609 assert_eq!(
610 multi_line_string.coordinate_position(&end_of_one_line),
611 CoordPos::OnBoundary
612 );
613
614 let shared_start = Coord::zero();
616 assert_eq!(
617 multi_line_string.coordinate_position(&shared_start),
618 CoordPos::Outside
619 );
620
621 let one_end_plus_midpoint = coord! { x: 0.5, y: 0.5 };
623 assert_eq!(
624 multi_line_string.coordinate_position(&one_end_plus_midpoint),
625 CoordPos::OnBoundary
626 );
627
628 let two_ends_plus_midpoint = coord! { x: -0.5, y: -0.5 };
630 assert_eq!(
631 multi_line_string.coordinate_position(&two_ends_plus_midpoint),
632 CoordPos::Inside
633 );
634 }
635
636 #[test]
637 fn test_rect() {
638 let rect = Rect::new((0.0, 0.0), (10.0, 10.0));
639 assert_eq!(
640 rect.coordinate_position(&coord! { x: 5.0, y: 5.0 }),
641 CoordPos::Inside
642 );
643 assert_eq!(
644 rect.coordinate_position(&coord! { x: 0.0, y: 5.0 }),
645 CoordPos::OnBoundary
646 );
647 assert_eq!(
648 rect.coordinate_position(&coord! { x: 15.0, y: 15.0 }),
649 CoordPos::Outside
650 );
651 }
652
653 #[test]
654 fn test_triangle() {
655 let triangle = Triangle::new((0.0, 0.0).into(), (5.0, 10.0).into(), (10.0, 0.0).into());
656 assert_eq!(
657 triangle.coordinate_position(&coord! { x: 5.0, y: 5.0 }),
658 CoordPos::Inside
659 );
660 assert_eq!(
661 triangle.coordinate_position(&coord! { x: 2.5, y: 5.0 }),
662 CoordPos::OnBoundary
663 );
664 assert_eq!(
665 triangle.coordinate_position(&coord! { x: 2.49, y: 5.0 }),
666 CoordPos::Outside
667 );
668 }
669
670 #[test]
671 fn test_collection() {
672 let triangle = Triangle::new((0.0, 0.0).into(), (5.0, 10.0).into(), (10.0, 0.0).into());
673 let rect = Rect::new((0.0, 0.0), (10.0, 10.0));
674 let collection = GeometryCollection::new_from(vec![triangle.into(), rect.into()]);
675
676 assert_eq!(
678 collection.coordinate_position(&coord! { x: 15.0, y: 15.0 }),
679 CoordPos::Outside
680 );
681
682 assert_eq!(
684 collection.coordinate_position(&coord! { x: 5.0, y: 5.0 }),
685 CoordPos::Inside
686 );
687
688 assert_eq!(
690 collection.coordinate_position(&coord! { x: 2.5, y: 5.0 }),
691 CoordPos::OnBoundary
692 );
693
694 assert_eq!(
696 collection.coordinate_position(&coord! { x: 5.0, y: 10.0 }),
697 CoordPos::Outside
698 );
699 }
700}