Skip to content

Commit f526f54

Browse files
authored
Check the dimensions of child geoms when rebuilding + add forcexy, forcexyz (#239)
1 parent ae6c92c commit f526f54

File tree

6 files changed

+93
-6
lines changed

6 files changed

+93
-6
lines changed

GeometryOpsCore/src/other_primitives.jl

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -159,10 +159,10 @@ on geometries from other packages and specify how to rebuild them.
159159
rebuild(geom, child_geoms; kw...) = rebuild(GI.trait(geom), geom, child_geoms; kw...)
160160
function rebuild(trait::GI.AbstractTrait, geom, child_geoms; crs=GI.crs(geom), extent=nothing)
161161
T = GI.geointerface_geomtype(trait)
162-
if GI.is3d(geom)
163-
# The Boolean type parameters here indicate "3d-ness" and "measure" coordinate, respectively.
164-
return T{true,false}(child_geoms; crs, extent)
165-
else
166-
return T{false,false}(child_geoms; crs, extent)
167-
end
162+
# Check the dimensionality of the first child geometry, since it may have changed
163+
# NOTE that without this, 2D to 3D conversions will fail
164+
hasZ = GI.is3d(first(child_geoms))
165+
hasM = GI.ismeasured(first(child_geoms))
166+
167+
return T{hasZ,hasM}(child_geoms; crs, extent)
168168
end

src/GeometryOps.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ include("transformations/segmentize.jl")
7070
include("transformations/simplify.jl")
7171
include("transformations/tuples.jl")
7272
include("transformations/transform.jl")
73+
include("transformations/forcedims.jl")
7374
include("transformations/correction/geometry_correction.jl")
7475
include("transformations/correction/closed_ring.jl")
7576
include("transformations/correction/intersecting_polygons.jl")

src/transformations/forcedims.jl

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
#=
2+
# Force dimensions (xy, xyz)
3+
4+
These functions force the geometry to be 2D or 3D. They work on any geometry, vector of geometries, feature collection, or table!
5+
6+
They're implemented by `apply` pretty simply.
7+
=#
8+
9+
export forcexy, forcexyz
10+
11+
"""
12+
forcexy(geom)
13+
14+
Force the geometry to be 2D. Works on any geometry, vector of geometries, feature collection, or table!
15+
"""
16+
function forcexy(geom)
17+
return apply(GI.PointTrait(), geom) do point
18+
(GI.x(point), GI.y(point))
19+
end
20+
end
21+
22+
"""
23+
forcexyz(geom, z = 0)
24+
25+
Force the geometry to be 3D. Works on any geometry, vector of geometries, feature collection, or table!
26+
27+
The `z` parameter is the default z value - if a point has no z value, it will be set to this value.
28+
If it does, then the z value will be kept.
29+
"""
30+
function forcexyz(geom, z = 0)
31+
return apply(GI.PointTrait(), geom) do point
32+
x, y = GI.x(point), GI.y(point)
33+
z = GI.is3d(geom) ? GI.z(point) : z
34+
(x, y, z)
35+
end
36+
end

test/runtests.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ include("helpers.jl")
2727
@safetestset "Simplify" begin include("transformations/simplify.jl") end
2828
@safetestset "Segmentize" begin include("transformations/segmentize.jl") end
2929
@safetestset "Transform" begin include("transformations/transform.jl") end
30+
@safetestset "Force Dimensions" begin include("transformations/forcedims.jl") end
3031
@safetestset "Geometry Correction" begin include("transformations/correction/geometry_correction.jl") end
3132
@safetestset "Closed Rings" begin include("transformations/correction/closed_ring.jl") end
3233
@safetestset "Intersecting Polygons" begin include("transformations/correction/intersecting_polygons.jl") end

test/transformations/forcedims.jl

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import GeometryOps as GO
2+
import GeoInterface as GI
3+
using ..TestHelpers
4+
5+
using Test
6+
7+
geom = GI.Polygon([GI.LinearRing([(1, 2), (3, 4), (5, 6), (1, 2)]),
8+
GI.LinearRing([(3, 4), (5, 6), (6, 7), (3, 4)])])
9+
10+
@testset_implementations "force dimensions" begin
11+
12+
# Test forcexy on 3D geometry
13+
geom3d = GO.transform($geom) do p
14+
(GI.x(p), GI.y(p), 1.0)
15+
end
16+
@test GI.is3d(geom3d)
17+
geom2d = GO.forcexy(geom3d)
18+
@test !GI.is3d(geom2d)
19+
@test GO.equals(geom2d, geom)
20+
21+
# Test forcexyz with default z
22+
geom3d_default = GO.forcexyz($geom)
23+
@test GI.is3d(geom3d_default)
24+
points3d = collect(GO.flatten(GI.PointTrait, geom3d_default))
25+
@test all(p -> GI.z(p) == 0, points3d)
26+
27+
# Test forcexyz with custom z
28+
geom3d_custom = GO.forcexyz($geom, 5.0)
29+
@test GI.is3d(geom3d_custom)
30+
points3d_custom = collect(GO.flatten(GI.PointTrait, geom3d_custom))
31+
@test all(p -> GI.z(p) == 5.0, points3d_custom)
32+
33+
# Test forcexyz preserves existing z values
34+
@test GO.equals(GO.forcexyz(geom3d), geom3d)
35+
end

test/transformations/transform.jl

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,17 @@ geom = GI.Polygon([GI.LinearRing([(1, 2), (3, 4), (5, 6), (1, 2)]),
1313
f = CoordinateTransformations.Translation(3.5, 1.5)
1414
@test GO.transform(f, $geom) == translated
1515
end
16+
17+
@testset_implementations "transform 2D to 3D" begin
18+
flat_points_raw = collect(GO.flatten(GI.PointTrait, $geom))
19+
flat_points_transformed = map(flat_points_raw) do p
20+
(GI.x(p), GI.y(p), hypot(GI.x(p), GI.y(p)))
21+
end
22+
23+
geom_transformed = GO.transform($geom) do p
24+
(GI.x(p), GI.y(p), hypot(GI.x(p), GI.y(p)))
25+
end
26+
@test collect(GO.flatten(GI.PointTrait, geom_transformed)) == flat_points_transformed
27+
@test GI.is3d(geom_transformed)
28+
@test !GI.ismeasured(geom_transformed)
29+
end

0 commit comments

Comments
 (0)