1use nodedb_types::geometry::{Geometry, point_in_polygon};
10use nodedb_types::geometry_bbox;
11
12use super::edge::{point_on_ring_boundary, ring_edges, segments_intersect};
13
14pub fn st_intersects(a: &Geometry, b: &Geometry) -> bool {
16 let a_bb = geometry_bbox(a);
18 let b_bb = geometry_bbox(b);
19 if !a_bb.intersects(&b_bb) {
20 return false;
21 }
22
23 match (a, b) {
24 (Geometry::Point { coordinates: ca }, Geometry::Point { coordinates: cb }) => {
26 (ca[0] - cb[0]).abs() < 1e-12 && (ca[1] - cb[1]).abs() < 1e-12
27 }
28
29 (Geometry::Point { coordinates: pt }, Geometry::LineString { coordinates: line })
31 | (Geometry::LineString { coordinates: line }, Geometry::Point { coordinates: pt }) => {
32 point_on_ring_boundary(*pt, line)
33 }
34
35 (Geometry::Point { coordinates: pt }, Geometry::Polygon { coordinates: rings })
37 | (Geometry::Polygon { coordinates: rings }, Geometry::Point { coordinates: pt }) => {
38 point_intersects_polygon(*pt, rings)
39 }
40
41 (Geometry::LineString { coordinates: la }, Geometry::LineString { coordinates: lb }) => {
43 linestrings_intersect(la, lb)
44 }
45
46 (Geometry::LineString { coordinates: line }, Geometry::Polygon { coordinates: rings })
48 | (Geometry::Polygon { coordinates: rings }, Geometry::LineString { coordinates: line }) => {
49 linestring_intersects_polygon(line, rings)
50 }
51
52 (Geometry::Polygon { coordinates: ra }, Geometry::Polygon { coordinates: rb }) => {
54 polygons_intersect(ra, rb)
55 }
56
57 (Geometry::MultiPoint { coordinates }, other)
59 | (other, Geometry::MultiPoint { coordinates }) => coordinates
60 .iter()
61 .any(|pt| st_intersects(&Geometry::Point { coordinates: *pt }, other)),
62
63 (Geometry::MultiLineString { coordinates }, other)
64 | (other, Geometry::MultiLineString { coordinates }) => coordinates.iter().any(|ls| {
65 st_intersects(
66 &Geometry::LineString {
67 coordinates: ls.clone(),
68 },
69 other,
70 )
71 }),
72
73 (Geometry::MultiPolygon { coordinates }, other)
74 | (other, Geometry::MultiPolygon { coordinates }) => coordinates.iter().any(|poly| {
75 st_intersects(
76 &Geometry::Polygon {
77 coordinates: poly.clone(),
78 },
79 other,
80 )
81 }),
82
83 (Geometry::GeometryCollection { geometries }, other)
84 | (other, Geometry::GeometryCollection { geometries }) => {
85 geometries.iter().any(|g| st_intersects(g, other))
86 }
87 }
88}
89
90fn point_intersects_polygon(pt: [f64; 2], rings: &[Vec<[f64; 2]>]) -> bool {
92 let Some(exterior) = rings.first() else {
93 return false;
94 };
95
96 if point_on_ring_boundary(pt, exterior) {
98 return true;
99 }
100
101 if !point_in_polygon(pt[0], pt[1], exterior) {
103 return false;
104 }
105
106 for hole in &rings[1..] {
108 if point_on_ring_boundary(pt, hole) {
109 return true;
110 }
111 if point_in_polygon(pt[0], pt[1], hole) {
112 return false;
113 }
114 }
115
116 true
117}
118
119fn linestrings_intersect(la: &[[f64; 2]], lb: &[[f64; 2]]) -> bool {
121 for i in 0..la.len().saturating_sub(1) {
122 for j in 0..lb.len().saturating_sub(1) {
123 if segments_intersect(la[i], la[i + 1], lb[j], lb[j + 1]) {
124 return true;
125 }
126 }
127 }
128 false
129}
130
131fn linestring_intersects_polygon(line: &[[f64; 2]], rings: &[Vec<[f64; 2]>]) -> bool {
133 for pt in line {
135 if point_intersects_polygon(*pt, rings) {
136 return true;
137 }
138 }
139
140 let Some(exterior) = rings.first() else {
142 return false;
143 };
144 let ext_edges = ring_edges(exterior);
145 for i in 0..line.len().saturating_sub(1) {
146 for &(pe_a, pe_b) in &ext_edges {
147 if segments_intersect(line[i], line[i + 1], pe_a, pe_b) {
148 return true;
149 }
150 }
151 }
152
153 for hole in &rings[1..] {
155 let hole_edges = ring_edges(hole);
156 for i in 0..line.len().saturating_sub(1) {
157 for &(he_a, he_b) in &hole_edges {
158 if segments_intersect(line[i], line[i + 1], he_a, he_b) {
159 return true;
160 }
161 }
162 }
163 }
164
165 false
166}
167
168fn polygons_intersect(ra: &[Vec<[f64; 2]>], rb: &[Vec<[f64; 2]>]) -> bool {
170 let Some(ext_a) = ra.first() else {
171 return false;
172 };
173 let Some(ext_b) = rb.first() else {
174 return false;
175 };
176
177 for pt in ext_b {
179 if point_intersects_polygon(*pt, ra) {
180 return true;
181 }
182 }
183
184 for pt in ext_a {
186 if point_intersects_polygon(*pt, rb) {
187 return true;
188 }
189 }
190
191 let a_edges = ring_edges(ext_a);
193 let b_edges = ring_edges(ext_b);
194 for &(a1, a2) in &a_edges {
195 for &(b1, b2) in &b_edges {
196 if segments_intersect(a1, a2, b1, b2) {
197 return true;
198 }
199 }
200 }
201
202 false
203}
204
205#[cfg(test)]
206mod tests {
207 use super::super::{st_disjoint, st_within};
208 use super::*;
209
210 fn square() -> Geometry {
211 Geometry::polygon(vec![vec![
212 [0.0, 0.0],
213 [10.0, 0.0],
214 [10.0, 10.0],
215 [0.0, 10.0],
216 [0.0, 0.0],
217 ]])
218 }
219
220 #[test]
221 fn point_inside_polygon_intersects() {
222 assert!(st_intersects(&Geometry::point(5.0, 5.0), &square()));
223 }
224
225 #[test]
226 fn point_on_edge_intersects() {
227 assert!(st_intersects(&Geometry::point(5.0, 0.0), &square()));
229 }
230
231 #[test]
232 fn point_on_vertex_intersects() {
233 assert!(st_intersects(&Geometry::point(0.0, 0.0), &square()));
234 }
235
236 #[test]
237 fn point_outside_does_not_intersect() {
238 assert!(!st_intersects(&Geometry::point(20.0, 20.0), &square()));
239 }
240
241 #[test]
242 fn linestring_crosses_polygon() {
243 let line = Geometry::line_string(vec![[-5.0, 5.0], [15.0, 5.0]]);
244 assert!(st_intersects(&line, &square()));
245 }
246
247 #[test]
248 fn linestring_inside_polygon() {
249 let line = Geometry::line_string(vec![[2.0, 2.0], [8.0, 8.0]]);
250 assert!(st_intersects(&line, &square()));
251 }
252
253 #[test]
254 fn linestring_outside_polygon() {
255 let line = Geometry::line_string(vec![[20.0, 20.0], [30.0, 30.0]]);
256 assert!(!st_intersects(&line, &square()));
257 }
258
259 #[test]
260 fn polygons_overlap() {
261 let other = Geometry::polygon(vec![vec![
262 [5.0, 5.0],
263 [15.0, 5.0],
264 [15.0, 15.0],
265 [5.0, 15.0],
266 [5.0, 5.0],
267 ]]);
268 assert!(st_intersects(&square(), &other));
269 }
270
271 #[test]
272 fn polygons_shared_boundary() {
273 let adjacent = Geometry::polygon(vec![vec![
274 [10.0, 0.0],
275 [20.0, 0.0],
276 [20.0, 10.0],
277 [10.0, 10.0],
278 [10.0, 0.0],
279 ]]);
280 assert!(st_intersects(&square(), &adjacent));
282 }
283
284 #[test]
285 fn polygons_disjoint() {
286 let far = Geometry::polygon(vec![vec![
287 [20.0, 20.0],
288 [30.0, 20.0],
289 [30.0, 30.0],
290 [20.0, 30.0],
291 [20.0, 20.0],
292 ]]);
293 assert!(!st_intersects(&square(), &far));
294 assert!(st_disjoint(&square(), &far));
295 }
296
297 #[test]
298 fn linestrings_cross() {
299 let a = Geometry::line_string(vec![[0.0, 0.0], [10.0, 10.0]]);
300 let b = Geometry::line_string(vec![[0.0, 10.0], [10.0, 0.0]]);
301 assert!(st_intersects(&a, &b));
302 }
303
304 #[test]
305 fn linestrings_parallel_no_cross() {
306 let a = Geometry::line_string(vec![[0.0, 0.0], [10.0, 0.0]]);
307 let b = Geometry::line_string(vec![[0.0, 1.0], [10.0, 1.0]]);
308 assert!(!st_intersects(&a, &b));
309 }
310
311 #[test]
312 fn st_within_works() {
313 let inner = Geometry::point(5.0, 5.0);
314 assert!(st_within(&inner, &square()));
315 assert!(!st_within(&square(), &inner));
316 }
317
318 #[test]
319 fn st_disjoint_works() {
320 assert!(!st_disjoint(&Geometry::point(5.0, 5.0), &square()));
321 assert!(st_disjoint(&Geometry::point(20.0, 20.0), &square()));
322 }
323
324 #[test]
325 fn linestring_endpoint_touches_polygon() {
326 let line = Geometry::line_string(vec![[10.0, 5.0], [15.0, 5.0]]);
327 assert!(st_intersects(&line, &square()));
329 }
330
331 #[test]
332 fn polygon_inside_polygon_intersects() {
333 let inner = Geometry::polygon(vec![vec![
334 [2.0, 2.0],
335 [8.0, 2.0],
336 [8.0, 8.0],
337 [2.0, 8.0],
338 [2.0, 2.0],
339 ]]);
340 assert!(st_intersects(&square(), &inner));
341 }
342}