Skip to content

Commit de4679e

Browse files
committed
Fix CRS propagation in union and difference clipping operations
Extend CRS handling from intersection to union and difference operations. Use `mutual_crs` to preserve coordinate reference system information when creating result polygons in non-crossing cases, hole handling, and multipolygon operations. Fixes type mismatches when clipping geometries with different CRS configurations.
1 parent 416de76 commit de4679e

File tree

3 files changed

+33
-24
lines changed

3 files changed

+33
-24
lines changed

docs/Project.toml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
[deps]
22
AccurateArithmetic = "22286c92-06ac-501d-9306-4abd417d9753"
33
AdaptivePredicates = "35492f91-a3bd-45ad-95db-fcad7dcfedb7"
4+
ArchGDAL = "c9ce4bd3-c3d5-55b8-8973-c0e20141b8c3"
45
Base64 = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f"
56
BenchmarkTools = "6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf"
67
CairoMakie = "13f3f980-e62b-5c42-98c6-ff1f3baf88f0"
@@ -33,17 +34,17 @@ LibGEOS = "a90b1aa1-3769-5649-ba7e-abc5a9d163eb"
3334
Literate = "98b081ad-f1c9-55d3-8b20-4c87d4299306"
3435
Makie = "ee78f7c6-11fb-53f2-987a-cfe4a2b5a57a"
3536
MakieThemes = "e296ed71-da82-5faf-88ab-0034a9761098"
37+
Markdown = "d6f4376e-aef5-505a-96c1-9c027394607a"
3638
Measurements = "eff96d63-e80a-5855-80a2-b1b0885c5ab7"
3739
MonteCarloMeasurements = "0987c9cc-fe09-11e8-30f0-b96dd679fdca"
38-
Markdown = "d6f4376e-aef5-505a-96c1-9c027394607a"
3940
MultiFloats = "bdf0d083-296b-4888-a5b6-7498122e68a5"
4041
NaturalEarth = "436b0209-26ab-4e65-94a9-6526d86fea76"
4142
Printf = "de0858da-6303-5e67-8744-51eddeeeb8d7"
4243
Proj = "c94c279d-25a6-4763-9509-64d165bea63e"
4344
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
4445
Shapefile = "8e980c4a-a4fe-5da2-b3a7-4b4b0353a2f4"
45-
Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2"
4646
SortTileRecursiveTree = "746ee33f-1797-42c2-866d-db2fce69d14d"
47+
Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2"
4748
TGGeometry = "d7e755d2-3c95-4bcf-9b3c-79ab1a78647b"
4849

4950
[sources]
@@ -52,4 +53,4 @@ GeometryOpsCore = {path = "../GeometryOpsCore"}
5253

5354
[compat]
5455
DocumenterVitepress = ">=0.2.1"
55-
GeoInterface = "1.6"
56+
GeoInterface = "1.6"

src/methods/clipping/difference.jl

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -57,19 +57,21 @@ function _difference(
5757
# Get the exterior of the polygons
5858
ext_a = GI.getexterior(poly_a)
5959
ext_b = GI.getexterior(poly_b)
60+
61+
crs = mutual_crs(poly_a, poly_b)
6062
# Find the difference of the exterior of the polygons
6163
a_list, b_list, a_idx_list = _build_ab_list(alg, T, ext_a, ext_b, _diff_delay_cross_f, _diff_delay_bounce_f; exact)
62-
polys = _trace_polynodes(alg, T, a_list, b_list, a_idx_list, _diff_step, poly_a, poly_b)
64+
polys = _trace_polynodes(alg, T, a_list, b_list, a_idx_list, _diff_step, poly_a, poly_b; crs)
6365
# if no crossing points, determine if either poly is inside of the other
6466
if isempty(polys)
6567
a_in_b, b_in_a = _find_non_cross_orientation(alg.manifold, a_list, b_list, ext_a, ext_b; exact)
6668
# add case for if they polygons are the same (all intersection points!)
6769
# add a find_first check to find first non-inter poly!
6870
if b_in_a && !a_in_b # b in a and can't be the same polygon
69-
poly_a_b_hole = GI.Polygon([tuples(ext_a), tuples(ext_b)])
71+
poly_a_b_hole = GI.Polygon([tuples(ext_a; crs), tuples(ext_b; crs)]; crs)
7072
push!(polys, poly_a_b_hole)
7173
elseif !b_in_a && !a_in_b # polygons don't intersect
72-
push!(polys, tuples(poly_a))
74+
push!(polys, tuples(poly_a; crs))
7375
return polys
7476
end
7577
end
@@ -119,7 +121,8 @@ function _difference(
119121
::GI.MultiPolygonTrait, multipoly_b;
120122
kwargs...,
121123
) where T
122-
polys = [tuples(poly_a, T)]
124+
crs = mutual_crs(poly_a, multipoly_b)
125+
polys = [tuples(poly_a, T; crs)]
123126
for poly_b in GI.getpolygon(multipoly_b)
124127
isempty(polys) && break
125128
polys = mapreduce(p -> difference(alg, p, poly_b; target), append!, polys)
@@ -140,7 +143,8 @@ function _difference(
140143
if !isnothing(fix_multipoly) # Fix multipoly_a to prevent returning an invalid multipolygon
141144
multipoly_a = fix_multipoly(multipoly_a)
142145
end
143-
polys = Vector{_get_poly_type(T)}()
146+
crs = mutual_crs(multipoly_a, poly_b)
147+
polys = Vector{_get_poly_type(T, crs)}()
144148
sizehint!(polys, GI.npolygon(multipoly_a))
145149
for poly_a in GI.getpolygon(multipoly_a)
146150
append!(polys, difference(alg, poly_a, poly_b; target))
@@ -163,6 +167,7 @@ function _difference(
163167
multipoly_a = fix_multipoly(multipoly_a)
164168
fix_multipoly = nothing
165169
end
170+
crs = mutual_crs(multipoly_a, multipoly_b)
166171
local polys
167172
for (i, poly_b) in enumerate(GI.getpolygon(multipoly_b))
168173
#= Removing intersections of `multipoly_a`` with pieces of `multipoly_b`` - as
@@ -171,7 +176,7 @@ function _difference(
171176
polys = if i == 1
172177
difference(alg, multipoly_a, poly_b; target, fix_multipoly)
173178
else
174-
difference(alg, GI.MultiPolygon(polys), poly_b; target, fix_multipoly)
179+
difference(alg, GI.MultiPolygon(polys; crs), poly_b; target, fix_multipoly)
175180
end
176181
#= One multipoly_a has been completely covered (and thus removed) there is no need to
177182
continue taking the difference =#

src/methods/clipping/union.jl

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -66,21 +66,23 @@ function _union(
6666
# First, I get the exteriors of the two polygons
6767
ext_a = GI.getexterior(poly_a)
6868
ext_b = GI.getexterior(poly_b)
69+
70+
crs = mutual_crs(poly_a, poly_b)
6971
# Then, I get the union of the exteriors
7072
a_list, b_list, a_idx_list = _build_ab_list(alg, T, ext_a, ext_b, _union_delay_cross_f, _union_delay_bounce_f; exact)
71-
polys = _trace_polynodes(alg, T, a_list, b_list, a_idx_list, _union_step, poly_a, poly_b)
73+
polys = _trace_polynodes(alg, T, a_list, b_list, a_idx_list, _union_step, poly_a, poly_b; crs)
7274
n_pieces = length(polys)
7375
# Check if one polygon totally within other and if so, return the larger polygon
7476
a_in_b, b_in_a = false, false
7577
if n_pieces == 0 # no crossing points, determine if either poly is inside the other
7678
a_in_b, b_in_a = _find_non_cross_orientation(alg, a_list, b_list, ext_a, ext_b; exact)
7779
if a_in_b
78-
push!(polys, GI.Polygon([_linearring(tuples(ext_b))]))
80+
push!(polys, GI.Polygon([_linearring(tuples(ext_b; crs))]; crs))
7981
elseif b_in_a
80-
push!(polys, GI.Polygon([_linearring(tuples(ext_a))]))
82+
push!(polys, GI.Polygon([_linearring(tuples(ext_a; crs))]; crs))
8183
else
82-
push!(polys, tuples(poly_a))
83-
push!(polys, tuples(poly_b))
84+
push!(polys, tuples(poly_a; crs))
85+
push!(polys, tuples(poly_b; crs))
8486
return polys
8587
end
8688
elseif n_pieces > 1
@@ -94,7 +96,7 @@ function _union(
9496
end
9597
# Add in holes
9698
if GI.nhole(poly_a) != 0 || GI.nhole(poly_b) != 0
97-
_add_union_holes!(alg, polys, a_in_b, b_in_a, poly_a, poly_b; exact)
99+
_add_union_holes!(alg, polys, a_in_b, b_in_a, poly_a, poly_b; exact, crs)
98100
end
99101
# Remove unneeded collinear points on same edge
100102
_remove_collinear_points!(alg, polys, [false], poly_a, poly_b)
@@ -121,11 +123,11 @@ _union_step(x, _) = x ? (-1) : 1
121123
#= Add holes from two polygons to the exterior polygon formed by their union. If adding the
122124
the holes reveals that the polygons aren't actually intersecting, return the original
123125
polygons. =#
124-
function _add_union_holes!(alg::FosterHormannClipping, polys, a_in_b, b_in_a, poly_a, poly_b; exact)
126+
function _add_union_holes!(alg::FosterHormannClipping, polys, a_in_b, b_in_a, poly_a, poly_b; exact, crs)
125127
if a_in_b
126-
_add_union_holes_contained_polys!(alg, polys, poly_a, poly_b; exact)
128+
_add_union_holes_contained_polys!(alg, polys, poly_a, poly_b; exact, crs)
127129
elseif b_in_a
128-
_add_union_holes_contained_polys!(alg, polys, poly_b, poly_a; exact)
130+
_add_union_holes_contained_polys!(alg, polys, poly_b, poly_a; exact, crs)
129131
else # Polygons intersect, but neither is contained in the other
130132
n_a_holes = GI.nhole(poly_a)
131133
ext_poly_a = GI.Polygon(StaticArrays.SVector(GI.getexterior(poly_a)))
@@ -166,7 +168,7 @@ end
166168
#= Add holes holes to the union of two polygons where one of the original polygons was
167169
inside of the other. If adding the the holes reveal that the polygons aren't actually
168170
intersecting, return the original polygons.=#
169-
function _add_union_holes_contained_polys!(alg::FosterHormannClipping, polys, interior_poly, exterior_poly; exact)
171+
function _add_union_holes_contained_polys!(alg::FosterHormannClipping, polys, interior_poly, exterior_poly; exact, crs)
170172
union_poly = polys[1]
171173
ext_int_ring = GI.getexterior(interior_poly)
172174
for (i, ih) in enumerate(GI.gethole(exterior_poly))
@@ -176,8 +178,8 @@ function _add_union_holes_contained_polys!(alg::FosterHormannClipping, polys, in
176178
if !on_ih && !out_ih
177179
#= interior polygon is completely within the ith hole - polygons aren't
178180
touching and do not actually form a union =#
179-
polys[1] = tuples(interior_poly)
180-
push!(polys, tuples(exterior_poly))
181+
polys[1] = tuples(interior_poly; crs)
182+
push!(polys, tuples(exterior_poly; crs))
181183
return polys
182184
else
183185
#= interior polygon is partially within the ith hole - area of interior
@@ -231,19 +233,20 @@ function _union(
231233
if !isnothing(fix_multipoly) # Fix multipoly_b to prevent repeated regions in the output
232234
multipoly_b = fix_multipoly(multipoly_b)
233235
end
234-
polys = [tuples(poly_a, T)]
236+
crs = mutual_crs(poly_a, multipoly_b)
237+
polys = [tuples(poly_a, T; crs)]
235238
for poly_b in GI.getpolygon(multipoly_b)
236239
if intersects(#=TODO: alg.manifold, =#polys[1], poly_b)
237240
# If polygons intersect and form a new polygon, swap out polygon
238241
new_polys = union(alg, polys[1], poly_b; target)
239242
if length(new_polys) > 1 # case where they intersect by just one point
240-
push!(polys, tuples(poly_b, T)) # add poly_b to list
243+
push!(polys, tuples(poly_b, T; crs)) # add poly_b to list
241244
else
242245
polys[1] = new_polys[1]
243246
end
244247
else
245248
# If they don't intersect, poly_b is now a part of the union as its own polygon
246-
push!(polys, tuples(poly_b, T))
249+
push!(polys, tuples(poly_b, T; crs))
247250
end
248251
end
249252
return polys

0 commit comments

Comments
 (0)