From 26d47c88107f862c9e21fa6f23580650f3e184e9 Mon Sep 17 00:00:00 2001 From: Rafael Schouten Date: Fri, 22 Dec 2023 19:05:17 +0100 Subject: [PATCH 1/4] actually test embed_extent --- src/GeometryOps.jl | 1 + src/transformations/extent.jl | 19 ++++++++++++------- test/runtests.jl | 1 + test/transformations/extent.jl | 17 +++++++++++++++++ 4 files changed, 31 insertions(+), 7 deletions(-) create mode 100644 test/transformations/extent.jl diff --git a/src/GeometryOps.jl b/src/GeometryOps.jl index 9e19dd553..b5d06507d 100644 --- a/src/GeometryOps.jl +++ b/src/GeometryOps.jl @@ -33,6 +33,7 @@ include("methods/polygonize.jl") include("methods/barycentric.jl") include("methods/equals.jl") +include("transformations/extent.jl") include("transformations/flip.jl") include("transformations/simplify.jl") include("transformations/reproject.jl") diff --git a/src/transformations/extent.jl b/src/transformations/extent.jl index 98051bf6e..c317c0c68 100644 --- a/src/transformations/extent.jl +++ b/src/transformations/extent.jl @@ -6,19 +6,24 @@ calculating and adding an `Extents.Extent` to all objects. This can improve performance when extents need to be checked multiple times. """ -embed_extent(x; kw...) = apply(AbstractTrait, x; kw...) +embed_extent(x; kw...) = apply(extent_applicator, GI.AbstractTrait, x; kw...) +# We recursively run `embed_extent` so the lowest level +# extent is calculated first and bubbles back up. +# This means we touch each point only once. extent_applicator(x) = extent_applicator(trait(x), x) extent_applicator(::Nothing, xs::AbstractArray) = embed_extent.(xs) -extent_applicator(::Union{AbstractCurveTrait,MultiPointTrait}, point) = point - -function extent_applicator(trait::AbstractGeometryTrait, geom) +function extent_applicator(trait::GI.AbstractGeometryTrait, geom) children_with_extents = map(GI.getgeom(geom)) do g embed_extent(g) end wrapper_type = GI.geointerface_geomtype(trait) extent = GI.extent(wrapper_type(children_with_extents)) - return wrapper_type(children_with_extents, extent) + return wrapper_type(children_with_extents; extent, crs=GI.crs(geom)) end -extent_applicator(::PointTrait, point) = point -extent_applicator(::PointTrait, point) = point +function extent_applicator(trait::Union{GI.AbstractCurveTrait,GI.MultiPointTrait}, geom) + wrapper_type = GI.geointerface_geomtype(trait) + extent = GI.extent(geom) + return wrapper_type(geom; extent, crs=GI.crs(geom)) +end +extent_applicator(::GI.PointTrait, point) = point diff --git a/test/runtests.jl b/test/runtests.jl index ee2065017..88e71bec3 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -23,6 +23,7 @@ const GO = GeometryOps @testset "Signed Area" begin include("methods/signed_area.jl") end @testset "Overlaps" begin include("methods/overlaps.jl") end # Transformations + @testset "Embed Extent" begin include("transformations/extent.jl") end @testset "Reproject" begin include("transformations/reproject.jl") end @testset "Flip" begin include("transformations/flip.jl") end @testset "Simplify" begin include("transformations/simplify.jl") end diff --git a/test/transformations/extent.jl b/test/transformations/extent.jl new file mode 100644 index 000000000..0613265ba --- /dev/null +++ b/test/transformations/extent.jl @@ -0,0 +1,17 @@ +using Test + +import GeoInterface as GI +import GeometryOps as GO +import Extents +using GeoInterface.Wrappers + +@testset "embed_extent" begin + poly = GI.Polygon([GI.LinearRing([(1, 2), (3, 4), (5, 6), (1, 2)]), + GI.LinearRing([(3, 4), (5, 6), (6, 7), (3, 4)])]) + + ext_poly = GO.embed_extent(poly) + lr1, lr2 = GI.getgeom(ext_poly) + @test ext_poly.extent == Extents.Extent(X=(1, 6), Y=(2, 7)) + @test lr1.extent == Extents.Extent(X=(1, 5), Y=(2, 6)) + @test lr2.extent == Extents.Extent(X=(3, 6), Y=(4, 7)) +end From 7a572f63775524a0f66504fea30db52db9e1961f Mon Sep 17 00:00:00 2001 From: rafaqz Date: Sat, 23 Dec 2023 19:52:44 +0100 Subject: [PATCH 2/4] fix Extents dep --- test/transformations/extent.jl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/transformations/extent.jl b/test/transformations/extent.jl index 0613265ba..beadeba9f 100644 --- a/test/transformations/extent.jl +++ b/test/transformations/extent.jl @@ -2,8 +2,7 @@ using Test import GeoInterface as GI import GeometryOps as GO -import Extents -using GeoInterface.Wrappers +using GeoInterface: Extents @testset "embed_extent" begin poly = GI.Polygon([GI.LinearRing([(1, 2), (3, 4), (5, 6), (1, 2)]), From ae8ce95a90993d0441da84e3a3883855ad8e34b4 Mon Sep 17 00:00:00 2001 From: rafaqz Date: Sat, 23 Dec 2023 23:06:13 +0100 Subject: [PATCH 3/4] document transformation keywords --- src/primitives.jl | 17 ++++++++++++++-- src/transformations/extent.jl | 34 +++++++++++--------------------- src/transformations/flip.jl | 4 ++++ src/transformations/reproject.jl | 1 + src/transformations/simplify.jl | 15 +++++++++++--- src/transformations/tuples.jl | 9 ++++++++- 6 files changed, 51 insertions(+), 29 deletions(-) diff --git a/src/primitives.jl b/src/primitives.jl index 1ca4e0224..9a9ea8127 100644 --- a/src/primitives.jl +++ b/src/primitives.jl @@ -1,10 +1,19 @@ +const THREADED_KEYWORD = "- `threaded`: `true` or `false`. Whether to use multithreading. Defaults to `false`." +const CRS_KEYWORD = "- `crs`: The CRS to attach to geometries. Defaults to `nothing`." +const CALC_EXTENT_KEYWORD = "- `calc_extent`: `true` or `false`. Whether to calculate the extent. Defaults to `false`." + +const APPLY_KEYWORDS = """ +$THREADED_KEYWORD +$CRS_KEYWORD +$CALC_EXTENT_KEYWORD +""" # # Primitive functions # This file mainly defines the [`apply`](@ref) function. """ - apply(f, target::Type{<:AbstractTrait}, obj; crs) + apply(f, target::Type{<:AbstractTrait}, obj; kw...) Reconstruct a geometry or feature using the function `f` on the `target` trait. @@ -12,7 +21,11 @@ Reconstruct a geometry or feature using the function `f` on the `target` trait. The result is an functionally similar geometry with values depending on `f` -# Flipped point the order in any feature or geometry, or iterables of either: +$APPLY_KEYWORDS + +# Example + +Flipped point the order in any feature or geometry, or iterables of either: ```juia import GeoInterface as GI diff --git a/src/transformations/extent.jl b/src/transformations/extent.jl index c317c0c68..9055de135 100644 --- a/src/transformations/extent.jl +++ b/src/transformations/extent.jl @@ -1,29 +1,17 @@ """ embed_extent(obj) -Recursively wrap the object with a `GeoInterface.Wrappers` geometry, +Recursively wrap the object with a GeoInterface.jl geometry, calculating and adding an `Extents.Extent` to all objects. -This can improve performance when extents need to be checked multiple times. -""" -embed_extent(x; kw...) = apply(extent_applicator, GI.AbstractTrait, x; kw...) +This can improve performance when extents need to be checked multiple times, +such when needing to check if many points are in geometries, and using their extents +as a quick filter for obviously exterior points. + +# Keywords -# We recursively run `embed_extent` so the lowest level -# extent is calculated first and bubbles back up. -# This means we touch each point only once. -extent_applicator(x) = extent_applicator(trait(x), x) -extent_applicator(::Nothing, xs::AbstractArray) = embed_extent.(xs) -function extent_applicator(trait::GI.AbstractGeometryTrait, geom) - children_with_extents = map(GI.getgeom(geom)) do g - embed_extent(g) - end - wrapper_type = GI.geointerface_geomtype(trait) - extent = GI.extent(wrapper_type(children_with_extents)) - return wrapper_type(children_with_extents; extent, crs=GI.crs(geom)) -end -function extent_applicator(trait::Union{GI.AbstractCurveTrait,GI.MultiPointTrait}, geom) - wrapper_type = GI.geointerface_geomtype(trait) - extent = GI.extent(geom) - return wrapper_type(geom; extent, crs=GI.crs(geom)) -end -extent_applicator(::GI.PointTrait, point) = point +$THREADED_KEYWORD +$CRS_KEYWORD +""" +embed_extent(x; threaded=false, crs=nothing) = + apply(identity, GI.PointTrait, x; calc_extent=true, threaded, crs) diff --git a/src/transformations/flip.jl b/src/transformations/flip.jl index e746456bb..eee3bed42 100644 --- a/src/transformations/flip.jl +++ b/src/transformations/flip.jl @@ -9,6 +9,10 @@ Swap all of the x and y coordinates in obj, otherwise keeping the original structure (but not necessarily the original type). + +## Keywords + +$APPLY_KEYWORDS """ function flip(geom; kw...) if _is3d(geom) diff --git a/src/transformations/reproject.jl b/src/transformations/reproject.jl index dd61c15bd..5c3a0e7a0 100644 --- a/src/transformations/reproject.jl +++ b/src/transformations/reproject.jl @@ -33,6 +33,7 @@ needed if it is not retreivable from the geometry with `GeoInterface.crs(geometr -`always_xy`: force x, y coordinate order, `true` by default. `false` will expect and return points in the crs coordinate order. -`time`: the time for the coordinates. `Inf` by default. +$APPLY_KEYWORDS """ function reproject(geom; source_crs=nothing, target_crs=nothing, transform=nothing, kw... diff --git a/src/transformations/simplify.jl b/src/transformations/simplify.jl index ee095f5b0..1dd2fb55b 100644 --- a/src/transformations/simplify.jl +++ b/src/transformations/simplify.jl @@ -22,6 +22,7 @@ abstract type SimplifyAlg end const SIMPLIFY_ALG_KEYWORDS = """ ## Keywords + - `ratio`: the fraction of points that should remain after `simplify`. Useful as it will generalise for large collections of objects. - `number`: the number of points that should remain after `simplify`. @@ -48,7 +49,7 @@ end """ simplify(obj; kw...) - simplify(::SimplifyAlg, obj) + simplify(::SimplifyAlg, obj; kw...) Simplify a geometry, feature, feature collection, or nested vectors or a table of these. @@ -62,6 +63,14 @@ listed in order of increasing quality but decreaseing performance. The default behaviour is `simplify(DouglasPeucker(; kw...), obj)`. Pass in other [`SimplifyAlg`](@ref) to use other algorithms. +# Keywords + +$APPLY_KEYWORDS + +Keywords for DouglasPeucker are allowed when no algorithm is specified: + +$SIMPLIFY_ALG_KEYWORDS + # Example Simplify a polygon to have six points: @@ -99,8 +108,8 @@ GI.npoint(simple) 6 ``` """ -simplify(data; calc_extent=false, threaded=false, kw...) = - _simplify(DouglasPeucker(; kw...), data; calc_extent, threaded) +simplify(data; calc_extent=false, threaded=false, crs=nothing, kw...) = + _simplify(DouglasPeucker(; kw...), data; calc_extent, threaded, crs) simplify(alg::SimplifyAlg, data; kw...) = _simplify(alg, data; kw...) function _simplify(alg::SimplifyAlg, data; kw...) diff --git a/src/transformations/tuples.jl b/src/transformations/tuples.jl index ff82b781b..fe1bf0026 100644 --- a/src/transformations/tuples.jl +++ b/src/transformations/tuples.jl @@ -3,7 +3,14 @@ """ tuples(obj) -Convert all points on obj to `Tuple`s. +Convert all points in `obj` to `Tuple`s, wherever the are nested. + +Returns a similar object or collection of objects using GeoInterface.jl +geometries wrapping `Tuple` points. + +# Keywords + +$APPLY_KEYWORDS """ function tuples(geom; kw...) if _is3d(geom) From 318bfd6163f1b761aff656baa2b20dc4da531156 Mon Sep 17 00:00:00 2001 From: rafaqz Date: Sat, 23 Dec 2023 23:35:12 +0100 Subject: [PATCH 4/4] bugfix keyword formatting --- src/transformations/reproject.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/transformations/reproject.jl b/src/transformations/reproject.jl index 5c3a0e7a0..d59cbd92f 100644 --- a/src/transformations/reproject.jl +++ b/src/transformations/reproject.jl @@ -30,9 +30,9 @@ needed if it is not retreivable from the geometry with `GeoInterface.crs(geometr ## Keywords --`always_xy`: force x, y coordinate order, `true` by default. +- `always_xy`: force x, y coordinate order, `true` by default. `false` will expect and return points in the crs coordinate order. --`time`: the time for the coordinates. `Inf` by default. +- `time`: the time for the coordinates. `Inf` by default. $APPLY_KEYWORDS """ function reproject(geom;