From 5c885b20e981c300368381691397b16c16f5b405 Mon Sep 17 00:00:00 2001 From: Alex Gardner <32276930+alex-s-gardner@users.noreply.github.com> Date: Mon, 23 Sep 2024 10:52:59 -0700 Subject: [PATCH] add trait promotion without array (#159) * add trait promotion without array * support FeatureCollection + tests * Apply suggestion from code review * Get tests working, propagate crs and extent up * Update src/wrappers.jl --------- Co-authored-by: Anshul Singhvi Co-authored-by: Rafael Schouten --- src/wrappers.jl | 24 +++++++++++++++++++----- test/test_wrappers.jl | 12 +++++++++++- 2 files changed, 30 insertions(+), 6 deletions(-) diff --git a/src/wrappers.jl b/src/wrappers.jl index 8103c10..be62c02 100644 --- a/src/wrappers.jl +++ b/src/wrappers.jl @@ -172,16 +172,23 @@ for (geomtype, trait, childtype, child_trait, length_check, nesting) in ( # But not if geom is already a WrapperGeometry convert(::Type{$geomtype}, ::$trait, geom::$geomtype) = geom end + @eval function $geomtype{Z,M}(geom::T; extent::E=nothing, crs::C=nothing) where {Z,M,T,E,C} Z isa Union{Bool,Nothing} || throw(ArgumentError("Z Parameter must be `true`, `false` or `nothing`")) M isa Union{Bool,Nothing} || throw(ArgumentError("M Parameter must be `true`, `false` or `nothing`")) - # Wrap some geometry at the same level + # Is geom a single geometry ? if isgeometry(geom) - geomtrait(geom) isa $trait || _argument_error(T, $trait) - Z1 = isnothing(Z) ? is3d(geom) : Z - M1 = isnothing(M) ? ismeasured(geom) : M - return $geomtype{Z1,M1,T,E,C}(geom, extent, crs) + if geomtrait(geom) isa $child_trait + # If geom is a child_trait, then make geom a vector and call again + return $geomtype([geom]; extent, crs) + else + # Wrap some geometry at the same level + geomtrait(geom) isa $trait || _argument_error(T, $trait) + Z1 = isnothing(Z) ? is3d(geom) : Z + M1 = isnothing(M) ? ismeasured(geom) : M + return $geomtype{Z1,M1,T,E,C}(geom, extent, crs) + end # Otherwise wrap an array of child geometries elseif geom isa AbstractArray @@ -428,6 +435,13 @@ end function FeatureCollection(parent; crs=nothing, extent=nothing) if isfeaturecollection(parent) FeatureCollection(parent, crs, extent) + elseif isfeature(parent) + # If `parent` is a single feature, wrap it in a featurecollection + FeatureCollection( + [parent], + isnothing(crs) ? Wrappers.crs(parent) : crs, + isnothing(extent) ? Wrappers.extent(parent) : extent + ) else features = (parent isa AbstractArray) ? parent : collect(parent) all(f -> isfeature(f), features) || _child_feature_error() diff --git a/test/test_wrappers.jl b/test/test_wrappers.jl index ef07b8c..d1f35ed 100644 --- a/test/test_wrappers.jl +++ b/test/test_wrappers.jl @@ -152,6 +152,7 @@ linearring_crs = GI.LinearRing(linearring; crs=EPSG(4326)) # Polygon polygon = GI.Polygon([linearring, linearring]) +@test GI.Polygon([linearring]) == GI.Polygon(linearring) @test polygon == GI.Polygon(polygon) @test GI.getgeom(polygon, 1) === linearring @test collect(GI.getgeom(polygon)) == [linearring, linearring] @@ -179,6 +180,7 @@ polygon3d = GI.Polygon([linearring3d, linearring3d]) # MultiPoint multipoint = GI.MultiPoint([(1, 2), (3, 4), (3, 2), (1, 4), (7, 8), (9, 10)]) @test multipoint == GI.MultiPoint(multipoint) +@test GI.MultiPoint([(1, 2)]) == GI.MultiPoint((1, 2)) @test GI.getgeom(multipoint, 1) === (1, 2) @test !GI.is3d(multipoint) @test GI.ncoord(multipoint) == 2 @@ -192,6 +194,7 @@ multipoint_crs = GI.MultiPoint(multipoint; crs=EPSG(4326)) # GeometryCollection geoms = [line, linestring, linearring, multipoint, (1, 2)] collection = GI.GeometryCollection(geoms) +@test GI.GeometryCollection([line]) == GI.GeometryCollection(line) @test collection == GI.GeometryCollection(collection) @test GI.getgeom(collection) == geoms @test GI.testgeometry(collection) @@ -205,6 +208,7 @@ collection_crs = GI.GeometryCollection(collection; crs=EPSG(4326)) # MultiCurve multicurve = GI.MultiCurve([linestring, linearring]) @test collect(GI.getpoint(multicurve)) == vcat(collect(GI.getpoint(linestring)), collect(GI.getpoint(linearring))) +@test GI.MultiCurve([linestring]) == GI.MultiCurve(linestring) @test multicurve == GI.MultiCurve(multicurve) @test GI.getgeom(multicurve, 1) === linestring @test !GI.is3d(multicurve) @@ -221,6 +225,7 @@ multicurve_crs = GI.MultiCurve(multicurve; crs=EPSG(4326)) polygon = GI.Polygon([linearring, linearring]) multipolygon = GI.MultiPolygon([polygon]) @test multipolygon == GI.MultiPolygon(multipolygon) +@test multipolygon == GI.MultiPolygon(polygon) @test GI.getgeom(multipolygon, 1) === polygon @test !GI.is3d(multipolygon) @test GI.ncoord(multipolygon) == 2 @@ -238,6 +243,7 @@ multipolygon_crs = GI.MultiPolygon(multipolygon; crs=EPSG(4326)) # PolyhedralSurface polyhedralsurface = GI.PolyhedralSurface([polygon, polygon]) @test polyhedralsurface == GI.PolyhedralSurface(polyhedralsurface) +@test GI.PolyhedralSurface(polygon) == GI.PolyhedralSurface(polygon) @test !GI.is3d(polyhedralsurface) @test GI.ncoord(polyhedralsurface) == 2 @test @inferred(GI.extent(polyhedralsurface)) == Extent(X=(1, 5), Y=(2, 6)) @@ -281,7 +287,11 @@ feature = GI.Feature(multipolygon; @test_throws ArgumentError GI.Feature(:not_a_feature; properties=(x=1, y=2, z=3)) # Feature Collection -fc = GI.FeatureCollection([feature]; crs=EPSG(4326), extent=GI.extent(feature)) +fc_unwrapped = GI.FeatureCollection(feature; crs=EPSG(4326), extent=GI.extent(feature)) +fc = GI.FeatureCollection(fc_unwrapped.parent; crs=EPSG(4326), extent=GI.extent(feature)) # so that `==` works since the underlying array is the same +@test fc_unwrapped == fc +@test GI.crs(fc) == GI.crs(GI.FeatureCollection(feature; crs=EPSG(4326), extent=GI.extent(feature))) +@test GI.extent(fc) == GI.extent(GI.FeatureCollection(feature; crs=EPSG(4326), extent=GI.extent(feature))) @test fc === GI.FeatureCollection(fc) @test GI.crs(fc) == EPSG(4326) @test GI.extent(fc) == fc.extent