@@ -2,23 +2,25 @@ mod i_overlay_integration;
2
2
#[ cfg( test) ]
3
3
mod tests;
4
4
5
- use crate :: bool_ops:: i_overlay_integration:: convert:: {
6
- multi_polygon_from_shapes, ring_to_shape_path,
7
- } ;
8
- use crate :: bool_ops:: i_overlay_integration:: BoolOpsCoord ;
5
+ use i_overlay_integration:: convert:: { multi_polygon_from_shapes, ring_to_shape_path} ;
6
+ use i_overlay_integration:: BoolOpsCoord ;
7
+ pub use i_overlay_integration:: BoolOpsNum ;
8
+
9
+ use crate :: geometry:: { LineString , MultiLineString , MultiPolygon , Polygon } ;
10
+ use crate :: winding_order:: { Winding , WindingOrder } ;
11
+
9
12
use i_overlay:: core:: fill_rule:: FillRule ;
13
+ use i_overlay:: core:: overlay_rule:: OverlayRule ;
10
14
use i_overlay:: float:: clip:: FloatClip ;
15
+ use i_overlay:: float:: overlay:: FloatOverlay ;
11
16
use i_overlay:: float:: single:: SingleFloatOverlay ;
12
17
use i_overlay:: string:: clip:: ClipRule ;
13
- pub use i_overlay_integration:: BoolOpsNum ;
14
-
15
- use crate :: geometry:: { LineString , MultiLineString , MultiPolygon , Polygon } ;
16
18
17
19
/// Boolean Operations on geometry.
18
20
///
19
21
/// Boolean operations are set operations on geometries considered as a subset
20
- /// of the 2-D plane. The operations supported are: intersection, union, xor or
21
- /// symmetric difference, and set-difference on pairs of 2-D geometries and
22
+ /// of the 2-D plane. The operations supported are: intersection, union,
23
+ /// symmetric difference (xor) , and set-difference on pairs of 2-D geometries and
22
24
/// clipping a 1-D geometry with self.
23
25
///
24
26
/// These operations are implemented on [`Polygon`] and the [`MultiPolygon`]
@@ -34,6 +36,11 @@ use crate::geometry::{LineString, MultiLineString, MultiPolygon, Polygon};
34
36
/// In particular, taking `union` with an empty geom should remove degeneracies
35
37
/// and fix invalid polygons as long the interior-exterior requirement above is
36
38
/// satisfied.
39
+ ///
40
+ /// # Performance
41
+ ///
42
+ /// For union operations on a large number of [`Polygon`]s or [`MultiPolygons`],
43
+ /// using [`unary_union`] will yield far better performance.
37
44
pub trait BooleanOps {
38
45
type Scalar : BoolOpsNum ;
39
46
@@ -57,18 +64,26 @@ pub trait BooleanOps {
57
64
multi_polygon_from_shapes ( shapes)
58
65
}
59
66
67
+ /// Returns the overlapping regions shared by both `self` and `other`.
60
68
fn intersection (
61
69
& self ,
62
70
other : & impl BooleanOps < Scalar = Self :: Scalar > ,
63
71
) -> MultiPolygon < Self :: Scalar > {
64
72
self . boolean_op ( other, OpType :: Intersection )
65
73
}
74
+
75
+ /// Combines the regions of both `self` and `other` into a single geometry, removing
76
+ /// overlaps and merging boundaries.
66
77
fn union ( & self , other : & impl BooleanOps < Scalar = Self :: Scalar > ) -> MultiPolygon < Self :: Scalar > {
67
78
self . boolean_op ( other, OpType :: Union )
68
79
}
80
+
81
+ /// The regions that are in either `self` or `other`, but not in both.
69
82
fn xor ( & self , other : & impl BooleanOps < Scalar = Self :: Scalar > ) -> MultiPolygon < Self :: Scalar > {
70
83
self . boolean_op ( other, OpType :: Xor )
71
84
}
85
+
86
+ /// The regions of `self` which are not in `other`.
72
87
fn difference (
73
88
& self ,
74
89
other : & impl BooleanOps < Scalar = Self :: Scalar > ,
@@ -109,6 +124,75 @@ pub enum OpType {
109
124
Xor ,
110
125
}
111
126
127
+ /// Efficient [union](BooleanOps::union) of many adjacent / overlapping geometries
128
+ ///
129
+ /// This is typically much faster than `union`ing a bunch of geometries together one at a time.
130
+ ///
131
+ /// Note: Geometries can be wound in either direction, but the winding order must be consistent,
132
+ /// and the polygon's interiors must be wound opposite to its exterior.
133
+ ///
134
+ /// See [Orient] for more information.
135
+ ///
136
+ /// [Orient]: crate::algorithm::orient::Orient
137
+ ///
138
+ /// # Arguments
139
+ ///
140
+ /// `boppables`: A collection of `Polygon` or `MultiPolygons` to union together.
141
+ ///
142
+ /// returns the union of all the inputs.
143
+ ///
144
+ /// # Examples
145
+ ///
146
+ /// ```
147
+ /// use geo::algorithm::unary_union;
148
+ /// use geo::wkt;
149
+ ///
150
+ /// let right_piece = wkt!(POLYGON((4. 0.,4. 4.,8. 4.,8. 0.,4. 0.)));
151
+ /// let left_piece = wkt!(POLYGON((0. 0.,0. 4.,4. 4.,4. 0.,0. 0.)));
152
+ ///
153
+ /// // touches neither right nor left piece
154
+ /// let separate_piece = wkt!(POLYGON((14. 10.,14. 14.,18. 14.,18. 10.,14. 10.)));
155
+ ///
156
+ /// let polygons = vec![left_piece, separate_piece, right_piece];
157
+ /// let actual_output = unary_union(&polygons);
158
+ ///
159
+ /// let expected_output = wkt!(MULTIPOLYGON(
160
+ /// // left and right piece have been combined
161
+ /// ((0. 0., 0. 4., 8. 4., 8. 0., 0. 0.)),
162
+ /// // separate piece remains separate
163
+ /// ((14. 10., 14. 14., 18. 14.,18. 10., 14. 10.))
164
+ /// ));
165
+ /// assert_eq!(actual_output, expected_output);
166
+ /// ```
167
+ pub fn unary_union < ' a , B : BooleanOps + ' a > (
168
+ boppables : impl IntoIterator < Item = & ' a B > ,
169
+ ) -> MultiPolygon < B :: Scalar > {
170
+ let mut winding_order: Option < WindingOrder > = None ;
171
+ let subject = boppables
172
+ . into_iter ( )
173
+ . flat_map ( |boppable| {
174
+ let rings = boppable. rings ( ) ;
175
+ rings
176
+ . map ( |ring| {
177
+ if winding_order. is_none ( ) {
178
+ winding_order = ring. winding_order ( ) ;
179
+ }
180
+ ring_to_shape_path ( ring)
181
+ } )
182
+ . collect :: < Vec < _ > > ( )
183
+ } )
184
+ . collect :: < Vec < _ > > ( ) ;
185
+
186
+ let fill_rule = if winding_order == Some ( WindingOrder :: Clockwise ) {
187
+ FillRule :: Positive
188
+ } else {
189
+ FillRule :: Negative
190
+ } ;
191
+
192
+ let shapes = FloatOverlay :: with_subj ( & subject) . overlay ( OverlayRule :: Subject , fill_rule) ;
193
+ multi_polygon_from_shapes ( shapes)
194
+ }
195
+
112
196
impl < T : BoolOpsNum > BooleanOps for Polygon < T > {
113
197
type Scalar = T ;
114
198
0 commit comments