geo/algorithm/contains/
line_string.rs

1use super::{impl_contains_from_relate, impl_contains_geometry_for, Contains};
2use crate::algorithm::Intersects;
3use crate::geometry::*;
4use crate::{CoordNum, GeoFloat, GeoNum};
5
6// ┌────────────────────────────────┐
7// │ Implementations for LineString │
8// └────────────────────────────────┘
9
10impl<T> Contains<Coord<T>> for LineString<T>
11where
12    T: GeoNum,
13{
14    fn contains(&self, coord: &Coord<T>) -> bool {
15        if self.0.is_empty() {
16            return false;
17        }
18
19        if coord == &self.0[0] || coord == self.0.last().unwrap() {
20            return self.is_closed();
21        }
22
23        self.lines()
24            .enumerate()
25            .any(|(i, line)| line.contains(coord) || (i > 0 && coord == &line.start))
26    }
27}
28
29impl<T> Contains<Point<T>> for LineString<T>
30where
31    T: GeoNum,
32{
33    fn contains(&self, p: &Point<T>) -> bool {
34        self.contains(&p.0)
35    }
36}
37
38impl<T> Contains<Line<T>> for LineString<T>
39where
40    T: GeoNum,
41{
42    fn contains(&self, line: &Line<T>) -> bool {
43        if line.start == line.end {
44            return self.contains(&line.start);
45        }
46
47        // We copy the line as we may truncate the line as
48        // we find partial matches.
49        let mut line = *line;
50        let mut first_cut = None;
51
52        let lines_iter = self.lines();
53        let num_lines = lines_iter.len();
54
55        // We need to repeat the logic twice to handle cases
56        // where the linestring starts at the middle of the line.
57        for (i, segment) in self.lines().chain(lines_iter).enumerate() {
58            if i >= num_lines {
59                // The first loop was done. If we never cut
60                // the line, or at the cut segment again, we
61                // can exit now.
62                if let Some(upto_i) = first_cut {
63                    if i >= num_lines + upto_i {
64                        break;
65                    }
66                } else {
67                    break;
68                }
69            }
70            // Look for a segment that intersects at least
71            // one of the end points.
72            let other = if segment.intersects(&line.start) {
73                line.end
74            } else if segment.intersects(&line.end) {
75                line.start
76            } else {
77                continue;
78            };
79
80            // If the other end point also intersects this
81            // segment, then we are done.
82            let new_inside = if segment.intersects(&other) {
83                return true;
84            }
85            // otoh, if the line contains one of the ends of
86            // the segments, then we truncate the line to
87            // the part outside.
88            else if line.contains(&segment.start) {
89                segment.start
90            } else if line.contains(&segment.end) {
91                segment.end
92            } else {
93                continue;
94            };
95
96            first_cut = first_cut.or(Some(i));
97            if other == line.start {
98                line.end = new_inside;
99            } else {
100                line.start = new_inside;
101            }
102        }
103
104        false
105    }
106}
107
108impl<T> Contains<LineString<T>> for LineString<T>
109where
110    T: GeoNum,
111{
112    fn contains(&self, rhs: &LineString<T>) -> bool {
113        rhs.lines().all(|l| self.contains(&l))
114    }
115}
116
117impl_contains_from_relate!(LineString<T>, [Polygon<T>, MultiPoint<T>, MultiLineString<T>, MultiPolygon<T>, GeometryCollection<T>, Rect<T>, Triangle<T>]);
118impl_contains_geometry_for!(LineString<T>);
119
120// ┌─────────────────────────────────────┐
121// │ Implementations for MultiLineString │
122// └─────────────────────────────────────┘
123
124impl_contains_from_relate!(MultiLineString<T>, [Line<T>, LineString<T>, Polygon<T>, MultiPoint<T>, MultiLineString<T>, MultiPolygon<T>, GeometryCollection<T>, Rect<T>, Triangle<T>]);
125impl_contains_geometry_for!(MultiLineString<T>);
126
127impl<T> Contains<Point<T>> for MultiLineString<T>
128where
129    T: CoordNum,
130    LineString<T>: Contains<Point<T>>,
131{
132    fn contains(&self, rhs: &Point<T>) -> bool {
133        self.iter().any(|ls| ls.contains(rhs))
134    }
135}