geo/algorithm/bool_ops/
mod.rs

1use geo_types::{MultiLineString, MultiPolygon};
2
3use crate::{CoordsIter, GeoFloat, GeoNum, Polygon};
4
5/// Boolean Operations on geometry.
6///
7/// Boolean operations are set operations on geometries considered as a subset
8/// of the 2-D plane. The operations supported are: intersection, union, xor or
9/// symmetric difference, and set-difference on pairs of 2-D geometries and
10/// clipping a 1-D geometry with self.
11///
12/// These operations are implemented on [`Polygon`] and the [`MultiPolygon`]
13/// geometries.
14///
15/// # Validity
16///
17/// Note that the operations are strictly well-defined only on *valid*
18/// geometries. However, the implementation generally works well as long as the
19/// interiors of polygons are contained within their corresponding exteriors.
20///
21/// Degenerate 2-d geoms with 0 area are handled, and ignored by the algorithm.
22/// In particular, taking `union` with an empty geom should remove degeneracies
23/// and fix invalid polygons as long the interior-exterior requirement above is
24/// satisfied.
25pub trait BooleanOps: Sized {
26    type Scalar: GeoNum;
27
28    fn boolean_op(&self, other: &Self, op: OpType) -> MultiPolygon<Self::Scalar>;
29    fn intersection(&self, other: &Self) -> MultiPolygon<Self::Scalar> {
30        self.boolean_op(other, OpType::Intersection)
31    }
32    fn union(&self, other: &Self) -> MultiPolygon<Self::Scalar> {
33        self.boolean_op(other, OpType::Union)
34    }
35    fn xor(&self, other: &Self) -> MultiPolygon<Self::Scalar> {
36        self.boolean_op(other, OpType::Xor)
37    }
38    fn difference(&self, other: &Self) -> MultiPolygon<Self::Scalar> {
39        self.boolean_op(other, OpType::Difference)
40    }
41
42    /// Clip a 1-D geometry with self.
43    ///
44    /// Returns the set-theoeretic intersection of `self` and `ls` if `invert`
45    /// is false, and the difference (`ls - self`) otherwise.
46    fn clip(
47        &self,
48        ls: &MultiLineString<Self::Scalar>,
49        invert: bool,
50    ) -> MultiLineString<Self::Scalar>;
51}
52
53#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
54pub enum OpType {
55    Intersection,
56    Union,
57    Difference,
58    Xor,
59}
60
61impl<T: GeoFloat> BooleanOps for Polygon<T> {
62    type Scalar = T;
63
64    fn boolean_op(&self, other: &Self, op: OpType) -> MultiPolygon<Self::Scalar> {
65        let spec = BoolOp::from(op);
66        let mut bop = Proc::new(spec, self.coords_count() + other.coords_count());
67        bop.add_polygon(self, 0);
68        bop.add_polygon(other, 1);
69        bop.sweep()
70    }
71
72    fn clip(
73        &self,
74        ls: &MultiLineString<Self::Scalar>,
75        invert: bool,
76    ) -> MultiLineString<Self::Scalar> {
77        let spec = ClipOp::new(invert);
78        let mut bop = Proc::new(spec, self.coords_count() + ls.coords_count());
79        bop.add_polygon(self, 0);
80        ls.0.iter().enumerate().for_each(|(idx, l)| {
81            bop.add_line_string(l, idx + 1);
82        });
83        bop.sweep()
84    }
85}
86impl<T: GeoFloat> BooleanOps for MultiPolygon<T> {
87    type Scalar = T;
88
89    fn boolean_op(&self, other: &Self, op: OpType) -> MultiPolygon<Self::Scalar> {
90        let spec = BoolOp::from(op);
91        let mut bop = Proc::new(spec, self.coords_count() + other.coords_count());
92        bop.add_multi_polygon(self, 0);
93        bop.add_multi_polygon(other, 1);
94        bop.sweep()
95    }
96
97    fn clip(
98        &self,
99        ls: &MultiLineString<Self::Scalar>,
100        invert: bool,
101    ) -> MultiLineString<Self::Scalar> {
102        let spec = ClipOp::new(invert);
103        let mut bop = Proc::new(spec, self.coords_count() + ls.coords_count());
104        bop.add_multi_polygon(self, 0);
105        ls.0.iter().enumerate().for_each(|(idx, l)| {
106            bop.add_line_string(l, idx + 1);
107        });
108        bop.sweep()
109    }
110}
111
112mod op;
113use op::*;
114mod assembly;
115use assembly::*;
116mod spec;
117use spec::*;
118
119#[cfg(test)]
120mod tests;