Skip to content

Commit 5cc40ad

Browse files
authored
Merge pull request #1275 from georust/mkirk/i_overlay_1.8
Upgrade to i_overlay ~~1.8~~ 1.9
2 parents 44b59ce + dda94eb commit 5cc40ad

File tree

5 files changed

+64
-290
lines changed

5 files changed

+64
-290
lines changed

geo/CHANGES.md

+3
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22

33
## Unreleased
44

5+
- Fix crash in `BoolOps` by updating `i_overlay` to 1.9.0.
6+
- <https://github.com/georust/geo/pull/1275>
7+
58
## 0.29.2 - 2024.11.15
69

710
- Pin `i_overlay` to < 1.8.0 to work around [recursion bug](https://github.com/georust/geo/issues/1270).

geo/Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ proj = { version = "0.27.0", optional = true }
3131
robust = "1.1.0"
3232
rstar = "0.12.0"
3333
serde = { version = "1.0", optional = true, features = ["derive"] }
34-
i_overlay = { version = "1.7.2, < 1.8.0", default-features = false }
34+
i_overlay = { version = "1.9.0, < 1.10.0", default-features = false }
3535

3636
[dev-dependencies]
3737
approx = ">= 0.4.0, < 0.6.0"

geo/src/algorithm/bool_ops/i_overlay_integration.rs

+29-248
Original file line numberDiff line numberDiff line change
@@ -1,290 +1,71 @@
11
use crate::geometry::Coord;
22
use crate::GeoNum;
3-
use i_overlay::core::fill_rule::FillRule;
4-
use i_overlay::core::overlay::ShapeType;
5-
use i_overlay::core::overlay_rule::OverlayRule;
6-
use i_overlay::string::clip::ClipRule;
3+
use i_overlay::i_float::float::compatible::FloatPointCompatible;
4+
use i_overlay::i_float::float::number::FloatNumber;
75

8-
pub trait BoolOpsCoord<T>: Copy {
9-
fn new(x: T, y: T) -> Self;
10-
fn x(&self) -> T;
11-
fn y(&self) -> T;
12-
}
13-
14-
/// A geometry coordinate number suitable for performing geometric boolean operations.
15-
pub trait BoolOpsNum: GeoNum {
16-
type CoordType: BoolOpsCoord<Self>;
17-
type OverlayType: BoolOpsOverlay<CoordType = Self::CoordType>;
18-
type StringOverlayType: BoolOpsStringOverlay<CoordType = Self::CoordType>;
19-
20-
fn to_bops_coord(geo_coord: Coord<Self>) -> Self::CoordType {
21-
Self::CoordType::new(geo_coord.x, geo_coord.y)
22-
}
23-
24-
fn to_geo_coord(bops_coord: Self::CoordType) -> Coord<Self> {
25-
Coord {
26-
x: bops_coord.x(),
27-
y: bops_coord.y(),
28-
}
29-
}
30-
}
31-
32-
pub trait BoolOpsOverlay {
33-
type CoordType;
34-
type OverlayGraph: BoolOpsOverlayGraph<CoordType = Self::CoordType>;
35-
fn new() -> Self;
36-
fn add_path(&mut self, path: Vec<Self::CoordType>, shape_type: ShapeType);
37-
fn into_graph(self, fill_rule: FillRule) -> Self::OverlayGraph;
38-
}
39-
40-
pub(super) trait BoolOpsOverlayGraph {
41-
type CoordType;
42-
fn extract_shapes(&self, overlay_rule: OverlayRule) -> Vec<Vec<Vec<Self::CoordType>>>;
43-
}
44-
45-
pub trait BoolOpsStringOverlay {
46-
type CoordType;
47-
type StringGraph: BoolOpsStringGraph<CoordType = Self::CoordType>;
48-
fn new() -> Self;
49-
fn add_shape_path(&mut self, path: Vec<Self::CoordType>);
50-
fn add_string_line(&mut self, path: [Self::CoordType; 2]);
51-
fn into_graph(self, fill_rule: FillRule) -> Self::StringGraph;
52-
}
53-
54-
pub(super) trait BoolOpsStringGraph {
55-
type CoordType;
56-
fn clip_string_lines(&self, clip_rule: ClipRule) -> Vec<Vec<Self::CoordType>>;
57-
}
58-
59-
mod f64 {
60-
use super::{ClipRule, FillRule, OverlayRule, ShapeType};
61-
use i_overlay::f64::{
62-
graph::F64OverlayGraph,
63-
overlay::F64Overlay,
64-
string::{F64StringGraph, F64StringOverlay},
65-
};
66-
use i_overlay::i_float::f64_point::F64Point;
67-
68-
impl super::BoolOpsNum for f64 {
69-
type CoordType = F64Point;
70-
type OverlayType = F64Overlay;
71-
type StringOverlayType = F64StringOverlay;
72-
}
73-
74-
impl super::BoolOpsCoord<f64> for F64Point {
75-
#[inline]
76-
fn new(x: f64, y: f64) -> Self {
77-
Self::new(x, y)
78-
}
79-
80-
#[inline]
81-
fn x(&self) -> f64 {
82-
self.x
83-
}
84-
85-
#[inline]
86-
fn y(&self) -> f64 {
87-
self.y
88-
}
89-
}
90-
91-
impl super::BoolOpsOverlay for F64Overlay {
92-
type CoordType = F64Point;
93-
type OverlayGraph = F64OverlayGraph;
94-
95-
#[inline]
96-
fn new() -> Self {
97-
Self::new()
98-
}
99-
100-
#[inline]
101-
fn add_path(&mut self, path: Vec<F64Point>, shape_type: ShapeType) {
102-
self.add_path(path, shape_type)
103-
}
104-
105-
#[inline]
106-
fn into_graph(self, fill_rule: FillRule) -> Self::OverlayGraph {
107-
self.into_graph(fill_rule)
108-
}
109-
}
110-
111-
impl super::BoolOpsOverlayGraph for F64OverlayGraph {
112-
type CoordType = F64Point;
113-
114-
#[inline]
115-
fn extract_shapes(&self, overlay_rule: OverlayRule) -> Vec<Vec<Vec<F64Point>>> {
116-
self.extract_shapes(overlay_rule)
117-
}
118-
}
119-
120-
impl super::BoolOpsStringOverlay for F64StringOverlay {
121-
type CoordType = F64Point;
122-
type StringGraph = F64StringGraph;
123-
124-
#[inline]
125-
fn new() -> Self {
126-
Self::new()
127-
}
6+
/// A geometry coordinate scalar suitable for performing geometric boolean operations.
7+
pub trait BoolOpsNum: GeoNum + FloatNumber {}
8+
impl<T: GeoNum + FloatNumber> BoolOpsNum for T {}
1289

129-
#[inline]
130-
fn add_shape_path(&mut self, path: Vec<Self::CoordType>) {
131-
self.add_shape_path(path)
132-
}
133-
134-
#[inline]
135-
fn add_string_line(&mut self, path: [Self::CoordType; 2]) {
136-
self.add_string_line(path)
137-
}
10+
/// New type for `Coord` that implements `FloatPointCompatible` for `BoolOpsNum` to
11+
/// circumvent orphan rule, since Coord is defined in geo_types.
12+
#[derive(Copy, Clone, Debug)]
13+
pub struct BoolOpsCoord<T: BoolOpsNum>(pub(crate) Coord<T>);
13814

139-
#[inline]
140-
fn into_graph(self, fill_rule: FillRule) -> Self::StringGraph {
141-
self.into_graph(fill_rule)
142-
}
15+
impl<T: BoolOpsNum> FloatPointCompatible<T> for BoolOpsCoord<T> {
16+
fn from_xy(x: T, y: T) -> Self {
17+
Self(Coord { x, y })
14318
}
14419

145-
impl super::BoolOpsStringGraph for F64StringGraph {
146-
type CoordType = F64Point;
147-
148-
#[inline]
149-
fn clip_string_lines(&self, clip_rule: ClipRule) -> Vec<Vec<Self::CoordType>> {
150-
self.clip_string_lines(clip_rule)
151-
}
20+
fn x(&self) -> T {
21+
self.0.x
15222
}
153-
}
154-
155-
mod f32 {
156-
use i_overlay::core::fill_rule::FillRule;
157-
use i_overlay::core::overlay::ShapeType;
158-
use i_overlay::core::overlay_rule::OverlayRule;
159-
use i_overlay::f32::graph::F32OverlayGraph;
160-
use i_overlay::f32::overlay::F32Overlay;
161-
use i_overlay::f32::string::{F32StringGraph, F32StringOverlay};
162-
use i_overlay::i_float::f32_point::F32Point;
163-
use i_overlay::string::clip::ClipRule;
16423

165-
impl super::BoolOpsNum for f32 {
166-
type CoordType = F32Point;
167-
type OverlayType = F32Overlay;
168-
type StringOverlayType = F32StringOverlay;
169-
}
170-
171-
impl super::BoolOpsCoord<f32> for F32Point {
172-
#[inline]
173-
fn new(x: f32, y: f32) -> Self {
174-
Self::new(x, y)
175-
}
176-
#[inline]
177-
fn x(&self) -> f32 {
178-
self.x
179-
}
180-
#[inline]
181-
fn y(&self) -> f32 {
182-
self.y
183-
}
184-
}
185-
186-
impl super::BoolOpsOverlay for F32Overlay {
187-
type CoordType = F32Point;
188-
type OverlayGraph = F32OverlayGraph;
189-
190-
#[inline]
191-
fn new() -> Self {
192-
Self::new()
193-
}
194-
195-
#[inline]
196-
fn add_path(&mut self, path: Vec<Self::CoordType>, shape_type: ShapeType) {
197-
self.add_path(path, shape_type)
198-
}
199-
200-
#[inline]
201-
fn into_graph(self, fill_rule: FillRule) -> Self::OverlayGraph {
202-
self.into_graph(fill_rule)
203-
}
204-
}
205-
206-
impl super::BoolOpsOverlayGraph for F32OverlayGraph {
207-
type CoordType = F32Point;
208-
209-
#[inline]
210-
fn extract_shapes(&self, overlay_rule: OverlayRule) -> Vec<Vec<Vec<F32Point>>> {
211-
self.extract_shapes(overlay_rule)
212-
}
213-
}
214-
215-
impl super::BoolOpsStringOverlay for F32StringOverlay {
216-
type CoordType = F32Point;
217-
type StringGraph = F32StringGraph;
218-
219-
#[inline]
220-
fn new() -> Self {
221-
Self::new()
222-
}
223-
224-
#[inline]
225-
fn add_shape_path(&mut self, path: Vec<Self::CoordType>) {
226-
self.add_shape_path(path)
227-
}
228-
229-
#[inline]
230-
fn add_string_line(&mut self, path: [Self::CoordType; 2]) {
231-
self.add_string_line(path)
232-
}
233-
234-
#[inline]
235-
fn into_graph(self, fill_rule: FillRule) -> Self::StringGraph {
236-
self.into_graph(fill_rule)
237-
}
238-
}
239-
240-
impl super::BoolOpsStringGraph for F32StringGraph {
241-
type CoordType = F32Point;
242-
243-
#[inline]
244-
fn clip_string_lines(&self, clip_rule: ClipRule) -> Vec<Vec<Self::CoordType>> {
245-
self.clip_string_lines(clip_rule)
246-
}
24+
fn y(&self) -> T {
25+
self.0.y
24726
}
24827
}
24928

25029
pub(super) mod convert {
25130
use super::super::OpType;
252-
use super::{BoolOpsNum, OverlayRule};
31+
use super::BoolOpsNum;
32+
use crate::bool_ops::i_overlay_integration::BoolOpsCoord;
25333
use crate::geometry::{LineString, MultiLineString, MultiPolygon, Polygon};
34+
use i_overlay::core::overlay_rule::OverlayRule;
25435

255-
pub fn line_string_from_path<T: BoolOpsNum>(path: Vec<T::CoordType>) -> LineString<T> {
256-
let coords = path.into_iter().map(T::to_geo_coord);
257-
LineString(coords.collect())
36+
pub fn line_string_from_path<T: BoolOpsNum>(path: Vec<BoolOpsCoord<T>>) -> LineString<T> {
37+
let coords = path.into_iter().map(|bops_coord| bops_coord.0).collect();
38+
LineString(coords)
25839
}
25940

26041
pub fn multi_line_string_from_paths<T: BoolOpsNum>(
261-
paths: Vec<Vec<T::CoordType>>,
42+
paths: Vec<Vec<BoolOpsCoord<T>>>,
26243
) -> MultiLineString<T> {
26344
let line_strings = paths.into_iter().map(|p| line_string_from_path(p));
26445
MultiLineString(line_strings.collect())
26546
}
26647

267-
pub fn polygon_from_shape<T: BoolOpsNum>(shape: Vec<Vec<T::CoordType>>) -> Polygon<T> {
48+
pub fn polygon_from_shape<T: BoolOpsNum>(shape: Vec<Vec<BoolOpsCoord<T>>>) -> Polygon<T> {
26849
let mut rings = shape.into_iter().map(|p| line_string_from_path(p));
26950
let exterior = rings.next().unwrap_or(LineString::new(vec![]));
27051
Polygon::new(exterior, rings.collect())
27152
}
27253

27354
pub fn multi_polygon_from_shapes<T: BoolOpsNum>(
274-
shapes: Vec<Vec<Vec<T::CoordType>>>,
55+
shapes: Vec<Vec<Vec<BoolOpsCoord<T>>>>,
27556
) -> MultiPolygon<T> {
27657
let polygons = shapes.into_iter().map(|s| polygon_from_shape(s));
27758
MultiPolygon(polygons.collect())
27859
}
27960

280-
pub fn ring_to_shape_path<T: BoolOpsNum>(line_string: &LineString<T>) -> Vec<T::CoordType> {
61+
pub fn ring_to_shape_path<T: BoolOpsNum>(line_string: &LineString<T>) -> Vec<BoolOpsCoord<T>> {
28162
if line_string.0.is_empty() {
28263
return vec![];
28364
}
28465
// In geo, Polygon rings are explicitly closed LineStrings — their final coordinate is the same as their first coordinate,
28566
// however in i_overlay, shape paths are implicitly closed, so we skip the last coordinate.
28667
let coords = &line_string.0[..line_string.0.len() - 1];
287-
coords.iter().copied().map(T::to_bops_coord).collect()
68+
coords.iter().copied().map(BoolOpsCoord).collect()
28869
}
28970

29071
impl From<OpType> for OverlayRule {
@@ -308,15 +89,15 @@ mod tests {
30889
#[test]
30990
fn two_empty_polygons() {
31091
let p1: Polygon = wkt!(POLYGON EMPTY);
311-
let p2 = wkt!(POLYGON EMPTY);
92+
let p2: Polygon = wkt!(POLYGON EMPTY);
31293
assert_eq!(&p1.union(&p2), &wkt!(MULTIPOLYGON EMPTY));
31394
assert_eq!(&p1.intersection(&p2), &wkt!(MULTIPOLYGON EMPTY));
31495
}
31596

31697
#[test]
31798
fn one_empty_polygon() {
31899
let p1: Polygon = wkt!(POLYGON((0. 0., 0. 1., 1. 1., 1. 0., 0. 0.)));
319-
let p2 = wkt!(POLYGON EMPTY);
100+
let p2: Polygon = wkt!(POLYGON EMPTY);
320101
assert_eq!(&p1.union(&p2), &MultiPolygon(vec![p1.clone()]));
321102
assert_eq!(&p1.intersection(&p2), &wkt!(MULTIPOLYGON EMPTY));
322103
}

0 commit comments

Comments
 (0)