Skip to main content

oxigdal_algorithms/vector/topology/
overlay.rs

1//! Advanced overlay operations for geometric analysis
2//!
3//! This module implements sophisticated overlay operations that combine multiple
4//! geometries using different overlay types (intersection, union, difference, etc.).
5
6use crate::error::Result;
7use oxigdal_core::vector::{Coordinate, LineString, MultiPolygon, Point, Polygon};
8
9/// Type of overlay operation to perform
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11pub enum OverlayType {
12    /// Intersection - returns the common area
13    Intersection,
14    /// Union - returns the combined area
15    Union,
16    /// Difference - returns area in first but not in second
17    Difference,
18    /// Symmetric difference - returns area in either but not both
19    SymmetricDifference,
20    /// Identity - preserves first geometry's attributes
21    Identity,
22    /// Update - updates first geometry with second
23    Update,
24}
25
26/// Options for overlay operations
27#[derive(Debug, Clone)]
28pub struct OverlayOptions {
29    /// Tolerance for coordinate comparison
30    pub tolerance: f64,
31    /// Whether to preserve topology
32    pub preserve_topology: bool,
33    /// Whether to snap vertices to grid
34    pub snap_to_grid: bool,
35    /// Grid size for snapping (if enabled)
36    pub grid_size: f64,
37    /// Whether to simplify result
38    pub simplify_result: bool,
39    /// Simplification tolerance (if enabled)
40    pub simplify_tolerance: f64,
41}
42
43impl Default for OverlayOptions {
44    fn default() -> Self {
45        Self {
46            tolerance: 1e-10,
47            preserve_topology: true,
48            snap_to_grid: false,
49            grid_size: 1e-6,
50            simplify_result: false,
51            simplify_tolerance: 1e-8,
52        }
53    }
54}
55
56/// Represents an edge in the overlay graph
57#[derive(Debug, Clone)]
58struct OverlayEdge {
59    start: Coordinate,
60    end: Coordinate,
61    left_label: EdgeLabel,
62    right_label: EdgeLabel,
63}
64
65/// Label for an edge indicating which input geometries it belongs to
66#[derive(Debug, Clone, Copy, PartialEq, Eq)]
67struct EdgeLabel {
68    /// Edge is in geometry A
69    in_a: bool,
70    /// Edge is in geometry B
71    in_b: bool,
72    /// Edge is on boundary of A
73    on_boundary_a: bool,
74    /// Edge is on boundary of B
75    on_boundary_b: bool,
76}
77
78impl EdgeLabel {
79    fn new() -> Self {
80        Self {
81            in_a: false,
82            in_b: false,
83            on_boundary_a: false,
84            on_boundary_b: false,
85        }
86    }
87
88    fn should_include(&self, overlay_type: OverlayType) -> bool {
89        match overlay_type {
90            OverlayType::Intersection => self.in_a && self.in_b,
91            OverlayType::Union => self.in_a || self.in_b,
92            OverlayType::Difference => self.in_a && !self.in_b,
93            OverlayType::SymmetricDifference => self.in_a ^ self.in_b,
94            OverlayType::Identity => self.in_a,
95            OverlayType::Update => {
96                if self.in_b {
97                    true
98                } else {
99                    self.in_a && !self.on_boundary_a
100                }
101            }
102        }
103    }
104}
105
106/// Overlay graph for computing overlay operations
107struct OverlayGraph {
108    edges: Vec<OverlayEdge>,
109    vertices: Vec<Coordinate>,
110    tolerance: f64,
111}
112
113impl OverlayGraph {
114    fn new(tolerance: f64) -> Self {
115        Self {
116            edges: Vec::new(),
117            vertices: Vec::new(),
118            tolerance,
119        }
120    }
121
122    fn add_polygon(&mut self, polygon: &Polygon, is_a: bool) -> Result<()> {
123        // Add exterior ring edges
124        let coords = &polygon.exterior.coords;
125        for i in 0..coords.len().saturating_sub(1) {
126            let start = coords[i];
127            let end = coords[i + 1];
128
129            let mut label = EdgeLabel::new();
130            if is_a {
131                label.in_a = true;
132                label.on_boundary_a = true;
133            } else {
134                label.in_b = true;
135                label.on_boundary_b = true;
136            }
137
138            self.add_edge(start, end, label)?;
139        }
140
141        // Add interior ring edges (holes)
142        for interior in &polygon.interiors {
143            let coords = &interior.coords;
144            for i in 0..coords.len().saturating_sub(1) {
145                let start = coords[i];
146                let end = coords[i + 1];
147
148                let mut label = EdgeLabel::new();
149                if is_a {
150                    label.in_a = true;
151                    label.on_boundary_a = true;
152                } else {
153                    label.in_b = true;
154                    label.on_boundary_b = true;
155                }
156
157                self.add_edge(start, end, label)?;
158            }
159        }
160
161        Ok(())
162    }
163
164    fn add_edge(&mut self, start: Coordinate, end: Coordinate, label: EdgeLabel) -> Result<()> {
165        // Check for degenerate edge
166        if Self::coords_equal(&start, &end, self.tolerance) {
167            return Ok(());
168        }
169
170        // Find or add vertices
171        let start_idx = self.find_or_add_vertex(start);
172        let end_idx = self.find_or_add_vertex(end);
173
174        if start_idx == end_idx {
175            return Ok(());
176        }
177
178        // Create edge
179        let edge = OverlayEdge {
180            start: self.vertices[start_idx],
181            end: self.vertices[end_idx],
182            left_label: label,
183            right_label: label,
184        };
185
186        self.edges.push(edge);
187        Ok(())
188    }
189
190    fn find_or_add_vertex(&mut self, coord: Coordinate) -> usize {
191        // Look for existing vertex within tolerance
192        for (idx, vertex) in self.vertices.iter().enumerate() {
193            if Self::coords_equal(vertex, &coord, self.tolerance) {
194                return idx;
195            }
196        }
197
198        // Add new vertex
199        let idx = self.vertices.len();
200        self.vertices.push(coord);
201        idx
202    }
203
204    fn coords_equal(a: &Coordinate, b: &Coordinate, tolerance: f64) -> bool {
205        (a.x - b.x).abs() < tolerance && (a.y - b.y).abs() < tolerance
206    }
207
208    fn compute_intersections(&mut self) -> Result<()> {
209        // Use sweep line algorithm to find all edge intersections
210        let n = self.edges.len();
211
212        // First pass: Collect all intersections without modifying the edges vector.
213        let mut intersections = Vec::new();
214        for i in 0..n {
215            for j in (i + 1)..n {
216                if let Some(intersection) = self.compute_edge_intersection(i, j)? {
217                    intersections.push((i, j, intersection));
218                }
219            }
220        }
221
222        // Group intersections by edge index to handle multiple splits correctly
223        use std::collections::HashMap;
224        let mut edge_splits: HashMap<usize, Vec<Coordinate>> = HashMap::new();
225
226        for (i, j, intersection) in intersections {
227            edge_splits.entry(i).or_default().push(intersection);
228            edge_splits.entry(j).or_default().push(intersection);
229        }
230
231        // Split each edge at all its intersection points
232        // Process in reverse order to avoid index invalidation
233        let mut split_edges = Vec::new();
234        for edge_idx in (0..n).rev() {
235            if let Some(split_points) = edge_splits.get(&edge_idx) {
236                let edge = self.edges[edge_idx].clone();
237
238                // Filter out endpoints
239                let mut points: Vec<Coordinate> = split_points
240                    .iter()
241                    .filter(|p| {
242                        !Self::coords_equal(p, &edge.start, self.tolerance)
243                            && !Self::coords_equal(p, &edge.end, self.tolerance)
244                    })
245                    .copied()
246                    .collect();
247
248                if points.is_empty() {
249                    continue;
250                }
251
252                // Sort points along the edge
253                points.sort_by(|a, b| {
254                    let t_a = if (edge.end.x - edge.start.x).abs() > self.tolerance {
255                        (a.x - edge.start.x) / (edge.end.x - edge.start.x)
256                    } else {
257                        (a.y - edge.start.y) / (edge.end.y - edge.start.y)
258                    };
259                    let t_b = if (edge.end.x - edge.start.x).abs() > self.tolerance {
260                        (b.x - edge.start.x) / (edge.end.x - edge.start.x)
261                    } else {
262                        (b.y - edge.start.y) / (edge.end.y - edge.start.y)
263                    };
264                    t_a.partial_cmp(&t_b).unwrap_or(std::cmp::Ordering::Equal)
265                });
266
267                // Create split edges
268                let mut segments = Vec::new();
269                let mut current_start = edge.start;
270                for point in points {
271                    segments.push(OverlayEdge {
272                        start: current_start,
273                        end: point,
274                        left_label: edge.left_label,
275                        right_label: edge.right_label,
276                    });
277                    current_start = point;
278                }
279                // Final segment
280                segments.push(OverlayEdge {
281                    start: current_start,
282                    end: edge.end,
283                    left_label: edge.left_label,
284                    right_label: edge.right_label,
285                });
286
287                // Store for later
288                split_edges.push((edge_idx, segments));
289            }
290        }
291
292        // Apply splits (in reverse order to maintain indices)
293        for (edge_idx, segments) in split_edges {
294            self.edges[edge_idx] = segments[0].clone();
295            for segment in segments.into_iter().skip(1) {
296                self.edges.push(segment);
297            }
298        }
299
300        Ok(())
301    }
302
303    fn compute_edge_intersection(&self, i: usize, j: usize) -> Result<Option<Coordinate>> {
304        let e1 = &self.edges[i];
305        let e2 = &self.edges[j];
306
307        let p1 = &e1.start;
308        let p2 = &e1.end;
309        let p3 = &e2.start;
310        let p4 = &e2.end;
311
312        let d = (p1.x - p2.x) * (p3.y - p4.y) - (p1.y - p2.y) * (p3.x - p4.x);
313
314        if d.abs() < self.tolerance {
315            // Parallel or coincident
316            return Ok(None);
317        }
318
319        let t = ((p1.x - p3.x) * (p3.y - p4.y) - (p1.y - p3.y) * (p3.x - p4.x)) / d;
320        let u = -((p1.x - p2.x) * (p1.y - p3.y) - (p1.y - p2.y) * (p1.x - p3.x)) / d;
321
322        if (0.0..=1.0).contains(&t) && (0.0..=1.0).contains(&u) {
323            let x = p1.x + t * (p2.x - p1.x);
324            let y = p1.y + t * (p2.y - p1.y);
325            Ok(Some(Coordinate::new_2d(x, y)))
326        } else {
327            Ok(None)
328        }
329    }
330
331    fn split_edge_at_point(&mut self, edge_idx: usize, point: Coordinate) -> Result<()> {
332        let edge = self.edges[edge_idx].clone();
333
334        // Check if point is actually on the edge (not at endpoints)
335        if Self::coords_equal(&edge.start, &point, self.tolerance)
336            || Self::coords_equal(&edge.end, &point, self.tolerance)
337        {
338            return Ok(());
339        }
340
341        // Create two new edges
342        let edge1 = OverlayEdge {
343            start: edge.start,
344            end: point,
345            left_label: edge.left_label,
346            right_label: edge.right_label,
347        };
348
349        let edge2 = OverlayEdge {
350            start: point,
351            end: edge.end,
352            left_label: edge.left_label,
353            right_label: edge.right_label,
354        };
355
356        // Replace original edge with split edges
357        self.edges[edge_idx] = edge1;
358        self.edges.push(edge2);
359
360        Ok(())
361    }
362
363    fn label_edges(&mut self, poly_a: &Polygon, poly_b: &Polygon) -> Result<()> {
364        // Label each edge based on whether it's inside/outside each polygon
365        for edge in &mut self.edges {
366            // Compute midpoint of edge
367            let mid_x = (edge.start.x + edge.end.x) / 2.0;
368            let mid_y = (edge.start.y + edge.end.y) / 2.0;
369            let midpoint = Point::new(mid_x, mid_y);
370
371            // For edges NOT on boundary A, check if midpoint is in polygon A
372            if !edge.left_label.on_boundary_a {
373                edge.left_label.in_a = crate::vector::point_in_polygon(&midpoint.coord, poly_a)?;
374            }
375            // For edges ON boundary A, in_a is already set to true in add_polygon
376            // but we still need to check if it's in B
377
378            // For edges NOT on boundary B, check if midpoint is in polygon B
379            if !edge.left_label.on_boundary_b {
380                edge.left_label.in_b = crate::vector::point_in_polygon(&midpoint.coord, poly_b)?;
381            }
382            // For edges ON boundary B, in_b is already set to true in add_polygon
383            // but we still need to check if it's in A
384        }
385
386        Ok(())
387    }
388
389    fn extract_result(&self, overlay_type: OverlayType) -> Result<Vec<Polygon>> {
390        // Filter edges based on overlay type
391        let mut result_edges: Vec<&OverlayEdge> = self
392            .edges
393            .iter()
394            .filter(|e| e.left_label.should_include(overlay_type))
395            .collect();
396
397        // Remove duplicate edges (edges that are reverses of each other)
398        // This can happen when edges are split at intersection points
399        let mut to_remove = Vec::new();
400        for i in 0..result_edges.len() {
401            if to_remove.contains(&i) {
402                continue;
403            }
404            for j in (i + 1)..result_edges.len() {
405                if to_remove.contains(&j) {
406                    continue;
407                }
408                // Check if edge j is the reverse of edge i
409                if Self::coords_equal(&result_edges[i].start, &result_edges[j].end, self.tolerance)
410                    && Self::coords_equal(
411                        &result_edges[i].end,
412                        &result_edges[j].start,
413                        self.tolerance,
414                    )
415                {
416                    // Keep the edge with better orientation (arbitrary: keep first one)
417                    to_remove.push(j);
418                }
419            }
420        }
421
422        // Remove marked edges
423        for &idx in to_remove.iter().rev() {
424            result_edges.remove(idx);
425        }
426
427        if result_edges.is_empty() {
428            return Ok(Vec::new());
429        }
430
431        // Build polygons from edges
432        let mut polygons = Vec::new();
433        let mut used = vec![false; result_edges.len()];
434
435        for start_idx in 0..result_edges.len() {
436            if used[start_idx] {
437                continue;
438            }
439
440            let mut coords = Vec::new();
441            let mut current_idx = start_idx;
442            let start_coord = result_edges[start_idx].start;
443
444            loop {
445                if used[current_idx] {
446                    break;
447                }
448
449                used[current_idx] = true;
450                let edge = result_edges[current_idx];
451                coords.push(edge.start);
452
453                // Check if we've closed the loop (check before looking for next unused edge)
454                let next_start = edge.end;
455                if Self::coords_equal(&next_start, &start_coord, self.tolerance) {
456                    coords.push(start_coord); // Close the ring
457                    break;
458                }
459
460                // Find next edge
461                let mut found_next = false;
462
463                for (idx, e) in result_edges.iter().enumerate() {
464                    if !used[idx] && Self::coords_equal(&e.start, &next_start, self.tolerance) {
465                        current_idx = idx;
466                        found_next = true;
467                        break;
468                    }
469                }
470
471                if !found_next {
472                    break;
473                }
474            }
475
476            // Create polygon if we have enough coordinates
477            if coords.len() >= 4 {
478                if let Ok(exterior) = LineString::new(coords) {
479                    if let Ok(polygon) = Polygon::new(exterior, vec![]) {
480                        polygons.push(polygon);
481                    }
482                }
483            }
484        }
485
486        Ok(polygons)
487    }
488}
489
490/// Perform overlay operation on two polygons
491///
492/// # Arguments
493///
494/// * `poly_a` - First polygon
495/// * `poly_b` - Second polygon
496/// * `overlay_type` - Type of overlay to perform
497/// * `options` - Overlay options
498///
499/// # Returns
500///
501/// Result containing the overlay result as a vector of polygons
502///
503/// # Examples
504///
505/// ```
506/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
507/// use oxigdal_algorithms::vector::topology::{overlay_polygon, OverlayType, OverlayOptions};
508/// use oxigdal_algorithms::{Coordinate, LineString, Polygon};
509///
510/// let coords1 = vec![
511///     Coordinate::new_2d(0.0, 0.0),
512///     Coordinate::new_2d(10.0, 0.0),
513///     Coordinate::new_2d(10.0, 10.0),
514///     Coordinate::new_2d(0.0, 10.0),
515///     Coordinate::new_2d(0.0, 0.0),
516/// ];
517/// let exterior1 = LineString::new(coords1)?;
518/// let poly1 = Polygon::new(exterior1, vec![])?;
519///
520/// let coords2 = vec![
521///     Coordinate::new_2d(5.0, 5.0),
522///     Coordinate::new_2d(15.0, 5.0),
523///     Coordinate::new_2d(15.0, 15.0),
524///     Coordinate::new_2d(5.0, 15.0),
525///     Coordinate::new_2d(5.0, 5.0),
526/// ];
527/// let exterior2 = LineString::new(coords2)?;
528/// let poly2 = Polygon::new(exterior2, vec![])?;
529///
530/// let result = overlay_polygon(&poly1, &poly2, OverlayType::Intersection, &OverlayOptions::default())?;
531/// assert!(!result.is_empty());
532/// # Ok(())
533/// # }
534/// ```
535pub fn overlay_polygon(
536    poly_a: &Polygon,
537    poly_b: &Polygon,
538    overlay_type: OverlayType,
539    options: &OverlayOptions,
540) -> Result<Vec<Polygon>> {
541    // Create overlay graph
542    let mut graph = OverlayGraph::new(options.tolerance);
543
544    // Add both polygons to graph
545    graph.add_polygon(poly_a, true)?;
546    graph.add_polygon(poly_b, false)?;
547
548    // Compute intersections
549    graph.compute_intersections()?;
550
551    // Label edges
552    graph.label_edges(poly_a, poly_b)?;
553
554    // Extract result
555    let mut result = graph.extract_result(overlay_type)?;
556
557    // Apply post-processing
558    if options.simplify_result {
559        result = result
560            .into_iter()
561            .filter_map(|poly| simplify_polygon_result(&poly, options.simplify_tolerance))
562            .collect();
563    }
564
565    Ok(result)
566}
567
568fn simplify_polygon_result(polygon: &Polygon, tolerance: f64) -> Option<Polygon> {
569    use crate::vector::simplify::{SimplifyMethod, simplify_linestring};
570
571    let simplified_exterior =
572        simplify_linestring(&polygon.exterior, tolerance, SimplifyMethod::DouglasPeucker).ok()?;
573
574    let simplified_interiors: Result<Vec<LineString>> = polygon
575        .interiors
576        .iter()
577        .map(|interior| simplify_linestring(interior, tolerance, SimplifyMethod::DouglasPeucker))
578        .collect();
579
580    let simplified_interiors = simplified_interiors.ok()?;
581
582    Polygon::new(simplified_exterior, simplified_interiors).ok()
583}
584
585/// Perform overlay operation on multipolygons
586pub fn overlay_multipolygon(
587    multi_a: &MultiPolygon,
588    multi_b: &MultiPolygon,
589    overlay_type: OverlayType,
590    options: &OverlayOptions,
591) -> Result<MultiPolygon> {
592    let mut result_polygons = Vec::new();
593
594    for poly_a in &multi_a.polygons {
595        for poly_b in &multi_b.polygons {
596            let overlay_result = overlay_polygon(poly_a, poly_b, overlay_type, options)?;
597            result_polygons.extend(overlay_result);
598        }
599    }
600
601    Ok(MultiPolygon::new(result_polygons))
602}
603
604/// Perform overlay on generic geometries (handles various geometry types)
605pub fn overlay_geometries(
606    geom_a: &Polygon,
607    geom_b: &Polygon,
608    overlay_type: OverlayType,
609    options: &OverlayOptions,
610) -> Result<Vec<Polygon>> {
611    overlay_polygon(geom_a, geom_b, overlay_type, options)
612}
613
614/// Batch overlay operation for multiple polygon pairs
615pub fn overlay_polygon_batch(
616    pairs: &[(Polygon, Polygon)],
617    overlay_type: OverlayType,
618    options: &OverlayOptions,
619) -> Result<Vec<Vec<Polygon>>> {
620    pairs
621        .iter()
622        .map(|(a, b)| overlay_polygon(a, b, overlay_type, options))
623        .collect()
624}
625
626#[cfg(test)]
627mod tests {
628    use super::*;
629
630    fn create_square(x: f64, y: f64, size: f64) -> Polygon {
631        let coords = vec![
632            Coordinate::new_2d(x, y),
633            Coordinate::new_2d(x + size, y),
634            Coordinate::new_2d(x + size, y + size),
635            Coordinate::new_2d(x, y + size),
636            Coordinate::new_2d(x, y),
637        ];
638        let exterior = LineString::new(coords).expect("Failed to create linestring");
639        Polygon::new(exterior, vec![]).expect("Failed to create polygon")
640    }
641
642    #[test]
643    fn test_overlay_intersection() {
644        let poly1 = create_square(0.0, 0.0, 10.0);
645        let poly2 = create_square(5.0, 5.0, 10.0);
646
647        let result = overlay_polygon(
648            &poly1,
649            &poly2,
650            OverlayType::Intersection,
651            &OverlayOptions::default(),
652        );
653
654        assert!(result.is_ok());
655        let polygons = result.expect("Overlay failed");
656        assert!(!polygons.is_empty());
657    }
658
659    #[test]
660    fn test_overlay_union() {
661        let poly1 = create_square(0.0, 0.0, 10.0);
662        let poly2 = create_square(5.0, 5.0, 10.0);
663
664        let result = overlay_polygon(
665            &poly1,
666            &poly2,
667            OverlayType::Union,
668            &OverlayOptions::default(),
669        );
670
671        assert!(result.is_ok());
672        let polygons = result.expect("Overlay failed");
673        assert!(!polygons.is_empty());
674    }
675
676    #[test]
677    fn test_overlay_difference() {
678        let poly1 = create_square(0.0, 0.0, 10.0);
679        let poly2 = create_square(5.0, 5.0, 10.0);
680
681        let result = overlay_polygon(
682            &poly1,
683            &poly2,
684            OverlayType::Difference,
685            &OverlayOptions::default(),
686        );
687
688        assert!(result.is_ok());
689    }
690
691    #[test]
692    fn test_overlay_non_overlapping() {
693        let poly1 = create_square(0.0, 0.0, 5.0);
694        let poly2 = create_square(10.0, 10.0, 5.0);
695
696        let result = overlay_polygon(
697            &poly1,
698            &poly2,
699            OverlayType::Intersection,
700            &OverlayOptions::default(),
701        );
702
703        assert!(result.is_ok());
704        let polygons = result.expect("Overlay failed");
705        assert!(polygons.is_empty()); // No intersection
706    }
707
708    #[test]
709    fn test_edge_label_logic() {
710        let label = EdgeLabel::new();
711        assert!(!label.should_include(OverlayType::Intersection));
712
713        let mut label = EdgeLabel::new();
714        label.in_a = true;
715        label.in_b = true;
716        assert!(label.should_include(OverlayType::Intersection));
717        assert!(label.should_include(OverlayType::Union));
718        assert!(!label.should_include(OverlayType::Difference));
719    }
720
721    #[test]
722    fn test_overlay_batch() {
723        let poly1 = create_square(0.0, 0.0, 10.0);
724        let poly2 = create_square(5.0, 5.0, 10.0);
725        let poly3 = create_square(10.0, 0.0, 10.0);
726
727        let pairs = vec![(poly1.clone(), poly2), (poly1, poly3)];
728
729        let result = overlay_polygon_batch(
730            &pairs,
731            OverlayType::Intersection,
732            &OverlayOptions::default(),
733        );
734
735        assert!(result.is_ok());
736        let results = result.expect("Batch overlay failed");
737        assert_eq!(results.len(), 2);
738    }
739}