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