diff --git a/src/GeometryOps.jl b/src/GeometryOps.jl index 4a48d91d8..566c252a7 100644 --- a/src/GeometryOps.jl +++ b/src/GeometryOps.jl @@ -4,6 +4,7 @@ module GeometryOps using GeoInterface using GeometryBasics +using GeometryBasics.StaticArrays import Proj using LinearAlgebra import ExactPredicates diff --git a/src/methods/angles.jl b/src/methods/angles.jl index 7f29baee2..c7af8732b 100644 --- a/src/methods/angles.jl +++ b/src/methods/angles.jl @@ -28,7 +28,7 @@ geometry trait. This is also used in the implementation, since it's a lot less work! =# -const _ANGLE_TARGETS = Union{GI.PolygonTrait,GI.AbstractCurveTrait,GI.MultiPointTrait,GI.PointTrait} +const _ANGLE_TARGETS = TraitTarget{Union{GI.PolygonTrait,GI.AbstractCurveTrait,GI.MultiPointTrait,GI.PointTrait}}() """ angles(geom, ::Type{T} = Float64) @@ -169,4 +169,4 @@ function _diffs_calc_angle(::Type{T}, (Δx_prev, Δy_prev), (Δx_curr, Δy_curr) val = clamp(dot_prod / (prev_mag * curr_mag), -one(T), one(T)) angle = real(acos(val) * 180 / π) return angle * (cross_prod < 0 ? -1 : 1) -end \ No newline at end of file +end diff --git a/src/methods/area.jl b/src/methods/area.jl index 3c48083c7..c6d472c42 100644 --- a/src/methods/area.jl +++ b/src/methods/area.jl @@ -48,7 +48,7 @@ This is why signed area is only implemented for polygons. =# # Targets for applys functions -const _AREA_TARGETS = Union{GI.PolygonTrait,GI.AbstractCurveTrait,GI.MultiPointTrait,GI.PointTrait} +const _AREA_TARGETS = TraitTarget{Union{GI.PolygonTrait,GI.AbstractCurveTrait,GI.MultiPointTrait,GI.PointTrait}}() """ area(geom, [T = Float64])::T diff --git a/src/methods/centroid.jl b/src/methods/centroid.jl index 7097d45cd..b7eb65bb0 100644 --- a/src/methods/centroid.jl +++ b/src/methods/centroid.jl @@ -107,7 +107,7 @@ end Returns the centroid and area of a given geometry. """ function centroid_and_area(geom, ::Type{T}=Float64; threaded=false) where T - target = Union{GI.PolygonTrait,GI.LineStringTrait,GI.LinearRingTrait} + target = TraitTarget{Union{GI.PolygonTrait,GI.LineStringTrait,GI.LinearRingTrait}}() init = (zero(T), zero(T)), zero(T) applyreduce(_combine_centroid_and_area, target, geom; threaded, init) do g _centroid_and_area(GI.trait(g), g, T) diff --git a/src/methods/clipping/clipping_processor.jl b/src/methods/clipping/clipping_processor.jl index e48fb5b42..773687a2d 100644 --- a/src/methods/clipping/clipping_processor.jl +++ b/src/methods/clipping/clipping_processor.jl @@ -505,7 +505,7 @@ function _add_holes_to_polys!(::Type{T}, return_polys, hole_iterator) where T if !on_ext && !out_ext # hole is completly within exterior push!(curr_poly.geom, new_hole) else # hole is partially within and outside of polygon's exterior - new_polys = difference(curr_poly_ext, new_hole_poly, T; target = GI.PolygonTrait) + new_polys = difference(curr_poly_ext, new_hole_poly, T; target=GI.PolygonTrait()) n_new_polys = length(new_polys) - 1 # replace original -> can't have a hole curr_poly.geom[1] = GI.getexterior(new_polys[1]) @@ -549,7 +549,7 @@ function _combine_holes!(::Type{T}, new_hole, curr_poly, return_polys) where T old_hole_poly = GI.Polygon([old_hole]) if intersects(new_hole_poly, old_hole_poly) # If the holes intersect, combine them into a bigger hole - hole_union = union(new_hole_poly, old_hole_poly, T; target = GI.PolygonTrait)[1] + hole_union = union(new_hole_poly, old_hole_poly, T; target = GI.PolygonTrait())[1] push!(remove_idx, k + 1) new_hole = GI.getexterior(hole_union) new_hole_poly = GI.Polygon([new_hole]) diff --git a/src/methods/clipping/coverage.jl b/src/methods/clipping/coverage.jl index 4fdd2f53a..a32dfa84a 100644 --- a/src/methods/clipping/coverage.jl +++ b/src/methods/clipping/coverage.jl @@ -37,7 +37,7 @@ with a linear ring. =# # Targets for applys functions -const _COVERAGE_TARGETS = Union{GI.PolygonTrait,GI.AbstractCurveTrait,GI.MultiPointTrait,GI.PointTrait} +const _COVERAGE_TARGETS = TraitTarget{Union{GI.PolygonTrait,GI.AbstractCurveTrait,GI.MultiPointTrait,GI.PointTrait}}() # Wall types for coverage const UNKNOWN, NORTH, EAST, SOUTH, WEST = 0:4 @@ -298,4 +298,4 @@ function _partial_edge_out_area((x1, y1), xmin, xmax, ymin, ymax, wall) x_wall = (wall == NORTH || wall == EAST) ? xmax : xmin y_wall = (wall == NORTH || wall == WEST) ? ymax : ymin return x1 * y_wall - x_wall * y1 -end \ No newline at end of file +end diff --git a/src/methods/clipping/difference.jl b/src/methods/clipping/difference.jl index f7edaa4f4..ce36c12d2 100644 --- a/src/methods/clipping/difference.jl +++ b/src/methods/clipping/difference.jl @@ -18,7 +18,7 @@ import GeoInterface as GI, GeometryOps as GO poly1 = GI.Polygon([[[0.0, 0.0], [5.0, 5.0], [10.0, 0.0], [5.0, -5.0], [0.0, 0.0]]]) poly2 = GI.Polygon([[[3.0, 0.0], [8.0, 5.0], [13.0, 0.0], [8.0, -5.0], [3.0, 0.0]]]) -diff_poly = GO.difference(poly1, poly2; target = GI.PolygonTrait) +diff_poly = GO.difference(poly1, poly2; target = GI.PolygonTrait()) GI.coordinates.(diff_poly) # output @@ -27,16 +27,16 @@ GI.coordinates.(diff_poly) ``` """ function difference( - geom_a, geom_b, ::Type{T} = Float64; target::Type{Target} = Nothing, -) where {T <: AbstractFloat, Target <: Union{Nothing, GI.AbstractTrait}} - return _difference(Target, T, GI.trait(geom_a), geom_a, GI.trait(geom_b), geom_b) + geom_a, geom_b, ::Type{T} = Float64; target=nothing, +) where {T<:AbstractFloat} + return _difference(TraitTarget(target), T, GI.trait(geom_a), geom_a, GI.trait(geom_b), geom_b) end #= The 'difference' function returns the difference of two polygons as a list of polygons. The algorithm to determine the difference was adapted from "Efficient clipping of efficient polygons," by Greiner and Hormann (1998). DOI: https://doi.org/10.1145/274363.274364 =# function _difference( - ::Type{GI.PolygonTrait}, ::Type{T}, + ::TraitTarget{GI.PolygonTrait}, ::Type{T}, ::GI.PolygonTrait, poly_a, ::GI.PolygonTrait, poly_b, ) where T @@ -76,7 +76,7 @@ end # Many type and target combos aren't implemented function _difference( - ::Type{Target}, ::Type{T}, + ::TraitTarget{Target}, ::Type{T}, trait_a::GI.AbstractTrait, geom_a, trait_b::GI.AbstractTrait, geom_b, ) where {Target, T} diff --git a/src/methods/clipping/intersection.jl b/src/methods/clipping/intersection.jl index 42321a6b6..b1ac5e46c 100644 --- a/src/methods/clipping/intersection.jl +++ b/src/methods/clipping/intersection.jl @@ -18,7 +18,7 @@ import GeoInterface as GI, GeometryOps as GO line1 = GI.Line([(124.584961,-12.768946), (126.738281,-17.224758)]) line2 = GI.Line([(123.354492,-15.961329), (127.22168,-14.008696)]) -inter_points = GO.intersection(line1, line2; target = GI.PointTrait) +inter_points = GO.intersection(line1, line2; target = GI.PointTrait()) GI.coordinates.(inter_points) # output @@ -27,14 +27,14 @@ GI.coordinates.(inter_points) ``` """ function intersection( - geom_a, geom_b, ::Type{T} = Float64; target::Type{Target} = Nothing, -) where {T <: AbstractFloat, Target <: Union{Nothing, GI.AbstractTrait}} - return _intersection(Target, T, GI.trait(geom_a), geom_a, GI.trait(geom_b), geom_b) + geom_a, geom_b, ::Type{T}=Float64; target=nothing, +) where {T<:AbstractFloat} + return _intersection(TraitTarget(target), T, GI.trait(geom_a), geom_a, GI.trait(geom_b), geom_b) end # Curve-Curve Intersections with target Point _intersection( - ::Type{GI.PointTrait}, ::Type{T}, + ::TraitTarget{GI.PointTrait}, ::Type{T}, trait_a::Union{GI.LineTrait, GI.LineStringTrait, GI.LinearRingTrait}, geom_a, trait_b::Union{GI.LineTrait, GI.LineStringTrait, GI.LinearRingTrait}, geom_b, ) where T = _intersection_points(T, trait_a, geom_a, trait_b, geom_b) @@ -45,7 +45,7 @@ The algorithm to determine the intersection was adapted from "Efficient clipping of efficient polygons," by Greiner and Hormann (1998). DOI: https://doi.org/10.1145/274363.274364 =# function _intersection( - ::Type{GI.PolygonTrait}, ::Type{T}, + ::TraitTarget{GI.PolygonTrait}, ::Type{T}, ::GI.PolygonTrait, poly_a, ::GI.PolygonTrait, poly_b, ) where {T} @@ -73,7 +73,7 @@ end # Many type and target combos aren't implemented function _intersection( - ::Type{Target}, ::Type{T}, + ::TraitTarget{Target}, ::Type{T}, trait_a::GI.AbstractTrait, geom_a, trait_b::GI.AbstractTrait, geom_b, ) where {Target, T} @@ -177,4 +177,4 @@ function _intersection_point(::Type{T}, (a1, a2)::Tuple, (b1, b2)::Tuple) where nothing, nothing end return point, fracs -end \ No newline at end of file +end diff --git a/src/methods/clipping/union.jl b/src/methods/clipping/union.jl index 7e9a6462b..6fbc1b24b 100644 --- a/src/methods/clipping/union.jl +++ b/src/methods/clipping/union.jl @@ -18,7 +18,7 @@ import GeoInterface as GI, GeometryOps as GO p1 = GI.Polygon([[(0.0, 0.0), (5.0, 5.0), (10.0, 0.0), (5.0, -5.0), (0.0, 0.0)]]) p2 = GI.Polygon([[(3.0, 0.0), (8.0, 5.0), (13.0, 0.0), (8.0, -5.0), (3.0, 0.0)]]) -union_poly = GO.union(p1, p2; target = GI.PolygonTrait) +union_poly = GO.union(p1, p2; target = GI.PolygonTrait()) GI.coordinates.(union_poly) # output @@ -27,16 +27,16 @@ GI.coordinates.(union_poly) ``` """ function union( - geom_a, geom_b, ::Type{T} = Float64; target::Type{Target} = Nothing, -) where {T <: AbstractFloat, Target <: Union{Nothing, GI.AbstractTrait}} - _union(Target, T, GI.trait(geom_a), geom_a, GI.trait(geom_b), geom_b) + geom_a, geom_b, ::Type{T}=Float64; target=nothing, +) where {T<:AbstractFloat} + _union(TraitTarget(target), T, GI.trait(geom_a), geom_a, GI.trait(geom_b), geom_b) end #= This 'union' implementation returns the union of two polygons. The algorithm to determine the union was adapted from "Efficient clipping of efficient polygons," by Greiner and Hormann (1998). DOI: https://doi.org/10.1145/274363.274364 =# function _union( - ::Type{GI.PolygonTrait}, ::Type{T}, + ::TraitTarget{GI.PolygonTrait}, ::Type{T}, ::GI.PolygonTrait, poly_a, ::GI.PolygonTrait, poly_b, ) where T @@ -74,12 +74,13 @@ function _union( return polys end + # Many type and target combos aren't implemented function _union( - ::Type{Target}, ::Type{T}, + ::TraitTarget{Target}, ::Type{T}, trait_a::GI.AbstractTrait, geom_a, trait_b::GI.AbstractTrait, geom_b, -) where {Target, T} +) where {Target,T} throw(ArgumentError("Union between $trait_a and $trait_b with target $Target isn't implemented yet.")) return nothing -end \ No newline at end of file +end diff --git a/src/methods/distance.jl b/src/methods/distance.jl index 9c7db79e8..ef470330f 100644 --- a/src/methods/distance.jl +++ b/src/methods/distance.jl @@ -63,7 +63,7 @@ Also note that singed_distance only makes sense for "filled-in" shapes, like polygons, so it isn't implemented for curves. =# -const _DISTANCE_TARGETS = Union{GI.AbstractPolygonTrait,GI.LineStringTrait,GI.LinearRingTrait,GI.LineTrait,GI.PointTrait} +const _DISTANCE_TARGETS = TraitTarget{Union{GI.AbstractPolygonTrait,GI.LineStringTrait,GI.LinearRingTrait,GI.LineTrait,GI.PointTrait}}() """ distance(point, geom, ::Type{T} = Float64)::T diff --git a/src/primitives.jl b/src/primitives.jl index 07e949326..e7f591d86 100644 --- a/src/primitives.jl +++ b/src/primitives.jl @@ -2,6 +2,23 @@ # This file mainly defines the [`apply`](@ref) function and its relatives. +#= +We pass `threading` and `calc_extent` as types, not simple boolean values. + +This is to help compilation - with a type to hold on to, it's easier for +the compiler to separate threaded and non-threaded code paths. +=# +abstract type BoolsAsTypes end +struct _True <: BoolsAsTypes end +struct _False <: BoolsAsTypes end + +# This struct holds a trait parameter or a union of trait parameters. +struct TraitTarget{T} end +TraitTarget(::Type{T}) where T = TraitTarget{T}() +TraitTarget(::T) where T<:GI.AbstractTrait = TraitTarget{T}() +TraitTarget(::TraitTarget{T}) where T = TraitTarget{T}() +TraitTarget(::Type{<:TraitTarget{T}}) where T = TraitTarget{T}() +TraitTarget(traits::GI.AbstractTrait...) where T = TraitTarget{Union{map(typeof, traits)...}}() 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`." @@ -94,57 +111,48 @@ flipped_geom = GO.apply(GI.PointTrait, geom) do p end """ @inline function apply( - f::F, ::Type{Target}, geom; calc_extent=false, threaded=false, kw... -) where {F,Target} + f::F, target, geom; calc_extent=false, threaded=false, kw... +) where F threaded = _booltype(threaded) calc_extent = _booltype(calc_extent) - _apply(f, Target, geom; threaded, calc_extent, kw...) + _apply(f, TraitTarget(target), geom; threaded, calc_extent, kw...) end -#= -We pass `threading` and `calc_extent` as types, not simple boolean values. - -This is to help compilation - with a type to hold on to, it's easier for -the compiler to separate threaded and non-threaded code paths. -=# -struct _True end -struct _False end - @inline _booltype(x::Bool) = x ? _True() : _False() @inline _booltype(x::Union{_True,_False}) = x # Call _apply again with the trait of `geom` -@inline _apply(f::F, ::Type{Target}, geom; kw...) where {Target,F} = - _apply(f, Target, GI.trait(geom), geom; kw...) +@inline _apply(f::F, target, geom; kw...) where F = + _apply(f, target, GI.trait(geom), geom; kw...) # There is no trait and this is an AbstractArray - so just iterate over it calling _apply on the contents -@inline function _apply(f::F, ::Type{Target}, ::Nothing, A::AbstractArray; threaded, kw...) where {F,Target} +@inline function _apply(f::F, target, ::Nothing, A::AbstractArray; threaded, kw...) where F # For an Array there is nothing else to do but map `_apply` over all values # _maptasks may run this level threaded if `threaded==true`, # but deeper `_apply` called in the closure will not be threaded - apply_to_array(i) = _apply(f, Target, A[i]; threaded=_False(), kw...) + apply_to_array(i) = _apply(f, target, A[i]; threaded=_False(), kw...) _maptasks(apply_to_array, eachindex(A), threaded) end # There is no trait and this is not an AbstractArray. # Try to call _apply over it. We can't use threading # as we don't know if we can can index into it. So just `map`. -@inline function _apply(f::F, ::Type{Target}, ::Nothing, iterable; threaded, kw...) where {F,Target} +@inline function _apply(f::F, target, ::Nothing, iterable; threaded, kw...) where F if threaded # `collect` first so we can use threads - _apply(f, Target, collect(iterable); threaded, kw...) + _apply(f, target, collect(iterable); threaded, kw...) else - apply_to_iterable(x) = _apply(f, Target, x; kw...) + apply_to_iterable(x) = _apply(f, target, x; kw...) map(apply_to_iterable, iterable) end end # Rewrap all FeatureCollectionTrait feature collections as GI.FeatureCollection # Maybe use threads to call _apply on componenet features -@inline function _apply(f::F, ::Type{Target}, ::GI.FeatureCollectionTrait, fc; +@inline function _apply(f::F, target, ::GI.FeatureCollectionTrait, fc; crs=GI.crs(fc), calc_extent=_False(), threaded -) where {F,Target} +) where F # Run _apply on all `features` in the feature collection, possibly threaded apply_to_feature(i) = - _apply(f, Target, GI.getfeature(fc, i); crs, calc_extent, threaded=_False())::GI.Feature + _apply(f, target, GI.getfeature(fc, i); crs, calc_extent, threaded=_False())::GI.Feature features = _maptasks(apply_to_feature, 1:GI.nfeature(fc), threaded) if calc_extent isa _True # Calculate the extent of the features @@ -157,11 +165,11 @@ end end end # Rewrap all FeatureTrait features as GI.Feature, keeping the properties -@inline function _apply(f::F, ::Type{Target}, ::GI.FeatureTrait, feature; +@inline function _apply(f::F, target, ::GI.FeatureTrait, feature; crs=GI.crs(feature), calc_extent=_False(), threaded -) where {F,Target} +) where F # Run _apply on the contained geometry - geometry = _apply(f, Target, GI.geometry(feature); crs, calc_extent, threaded) + geometry = _apply(f, target, GI.geometry(feature); crs, calc_extent, threaded) # Get the feature properties properties = GI.properties(feature) if calc_extent isa _True @@ -176,13 +184,13 @@ end end # Reconstruct nested geometries, # maybe using threads to call _apply on component geoms -@inline function _apply(f::F, ::Type{Target}, trait, geom; +@inline function _apply(f::F, target, trait, geom; crs=GI.crs(geom), calc_extent=_False(), threaded -)::(GI.geointerface_geomtype(trait)) where {F,Target} +)::(GI.geointerface_geomtype(trait)) where F # Map `_apply` over all sub geometries of `geom` # to create a new vector of geometries # TODO handle zero length - apply_to_geom(i) = _apply(f, Target, GI.getgeom(geom, i); crs, calc_extent, threaded=_False()) + apply_to_geom(i) = _apply(f, target, GI.getgeom(geom, i); crs, calc_extent, threaded=_False()) geoms = _maptasks(apply_to_geom, 1:GI.ngeom(geom), threaded) return _apply_inner(geom, geoms, crs, calc_extent) end @@ -199,19 +207,19 @@ function _apply_inner(geom, geoms, crs, calc_extent::_False) end # Fail loudly if we hit PointTrait without running `f` # (after PointTrait there is no further to dig with `_apply`) -@inline _apply(f, ::Type{Target}, trait::GI.PointTrait, geom; crs=nothing, kw...) where Target = - throw(ArgumentError("target $Target not found, but reached a `PointTrait` leaf")) +# @inline _apply(f, ::TraitTarget{Target}, trait::GI.PointTrait, geom; crs=nothing, kw...) where Target = + # throw(ArgumentError("target $Target not found, but reached a `PointTrait` leaf")) # Finally, these short methods are the main purpose of `apply`. # The `Trait` is a subtype of the `Target` (or identical to it) # So the `Target` is found. We apply `f` to geom and return it to previous # _apply calls to be wrapped with the outer geometries/feature/featurecollection/array. -_apply(f::F, ::Type{Target}, ::Trait, geom; crs=GI.crs(geom), kw...) where {F,Target,Trait<:Target} = f(geom) +_apply(f::F, ::TraitTarget{Target}, ::Trait, geom; crs=GI.crs(geom), kw...) where {F,Target,Trait<:Target} = f(geom) # Define some specific cases of this match to avoid method ambiguity for T in ( GI.PointTrait, GI.LinearRing, GI.LineString, GI.MultiPoint, GI.FeatureTrait, GI.FeatureCollectionTrait ) - @eval _apply(f::F, target::Type{$T}, trait::$T, x; kw...) where F = f(x) + @eval _apply(f::F, target::TraitTarget{<:$T}, trait::$T, x; kw...) where F = f(x) end """ @@ -226,61 +234,62 @@ If `threaded==true` threads will be used over arrays and iterables, feature collections and nested geometries. """ @inline function applyreduce( - f::F, op, ::Type{Target}, geom; threaded=false, init=nothing -) where {F,Target} + f::F, op, target, geom; threaded=false, init=nothing +) where F threaded = _booltype(threaded) - _applyreduce(f, op, Target, geom; threaded, init) + _applyreduce(f, op, TraitTarget(target), geom; threaded, init) end -@inline _applyreduce(f::F, op, ::Type{Target}, geom; threaded, init) where {F,Target} = - _applyreduce(f, op, Target, GI.trait(geom), geom; threaded, init) +@inline _applyreduce(f::F, op, target, geom; threaded, init) where F = + _applyreduce(f, op, target, GI.trait(geom), geom; threaded, init) # Maybe use threads recucing over arrays -@inline function _applyreduce(f::F, op, ::Type{Target}, ::Nothing, A::AbstractArray; threaded, init) where {F,Target} - applyreduce_array(i) = _applyreduce(f, op, Target, A[i]; threaded=_False(), init) +@inline function _applyreduce(f::F, op, target, ::Nothing, A::AbstractArray; threaded, init) where F + applyreduce_array(i) = _applyreduce(f, op, target, A[i]; threaded=_False(), init) _mapreducetasks(applyreduce_array, op, eachindex(A), threaded; init) end # Try to applyreduce over iterables -@inline function _applyreduce(f::F, op, ::Type{Target}, ::Nothing, iterable; threaded, init) where {F,Target} - applyreduce_iterable(i) = _applyreduce(f, op, Target, x; threaded=_False(), init) +@inline function _applyreduce(f::F, op, target, ::Nothing, iterable; threaded, init) where F + applyreduce_iterable(i) = _applyreduce(f, op, target, x; threaded=_False(), init) if threaded # Try to `collect` and reduce over the vector with threads - _applyreduce(f, op, Target, collect(iterable); threaded, init) + _applyreduce(f, op, target, collect(iterable); threaded, init) else # Try to `mapreduce` the iterable as-is mapreduce(applyreduce_iterable, op, iterable; init) end end # Maybe use threads reducing over features of feature collections -@inline function _applyreduce(f::F, op, ::Type{Target}, ::GI.FeatureCollectionTrait, fc; threaded, init) where {F,Target} - applyreduce_fc(i) = _applyreduce(f, op, Target, GI.getfeature(fc, i); threaded=_False(), init) +@inline function _applyreduce(f::F, op, target, ::GI.FeatureCollectionTrait, fc; threaded, init) where F + applyreduce_fc(i) = _applyreduce(f, op, target, GI.getfeature(fc, i); threaded=_False(), init) _mapreducetasks(applyreduce_fc, op, 1:GI.nfeature(fc), threaded; init) end # Features just applyreduce to their geometry -@inline _applyreduce(f::F, op, ::Type{Target}, ::GI.FeatureTrait, feature; threaded, init) where {F,Target} = +@inline _applyreduce(f::F, op, target, ::GI.FeatureTrait, feature; threaded, init) where F = _applyreduce(f, op, target, GI.geometry(feature); threaded, init) # Maybe use threads over components of nested geometries -@inline function _applyreduce(f::F, op, ::Type{Target}, trait, geom; threaded, init) where {F,Target} - applyreduce_geom(i) = _applyreduce(f, op, Target, GI.getgeom(geom, i); threaded=_False(), init) +@inline function _applyreduce(f::F, op, target, trait, geom; threaded, init) where F + applyreduce_geom(i) = _applyreduce(f, op, target, GI.getgeom(geom, i); threaded=_False(), init) _mapreducetasks(applyreduce_geom, op, 1:GI.ngeom(geom), threaded; init) end # Don't thread over points it won't pay off @inline function _applyreduce( - f::F, op, ::Type{Target}, trait::Union{GI.LinearRing,GI.LineString,GI.MultiPoint}, geom; + f::F, op, target, trait::Union{GI.LinearRing,GI.LineString,GI.MultiPoint}, geom; threaded, init -) where {F,Target} - _applyreduce(f, op, Target, GI.getgeom(geom); threaded=_False(), init) +) where F + _applyreduce(f, op, target, GI.getgeom(geom); threaded=_False(), init) end # Apply f to the target -@inline function _applyreduce(f::F, op, ::Type{Target}, ::Trait, x; kw...) where {F,Target<:GI.AbstractTrait,Trait<:Target} +@inline function _applyreduce(f::F, op, ::TraitTarget{Target}, ::Trait, x; kw...) where {F,Target,Trait<:Target} f(x) end # Fail if we hit PointTrait -_applyreduce(f, op, target, trait, geom; kw...) = throw(ArgumentError("target $target not found")) +# _applyreduce(f, op, target::TraitTarget{Target}, trait::PointTrait, geom; kw...) where Target = + # throw(ArgumentError("target $target not found")) # Specific cases to avoid method ambiguity for T in ( GI.PointTrait, GI.LinearRing, GI.LineString, GI.MultiPoint, GI.FeatureTrait, GI.FeatureCollectionTrait ) - @eval _applyreduce(f::F, op, target::Type{$T}, trait::$T, x; kw...) where F = f(x) + @eval _applyreduce(f::F, op, ::TraitTarget{<:$T}, trait::$T, x; kw...) where F = f(x) end """ diff --git a/src/transformations/extent.jl b/src/transformations/extent.jl index 90629af29..b11fa7b62 100644 --- a/src/transformations/extent.jl +++ b/src/transformations/extent.jl @@ -16,4 +16,4 @@ $THREADED_KEYWORD $CRS_KEYWORD """ embed_extent(x; threaded=false, crs=nothing) = - apply(identity, GI.PointTrait, x; calc_extent=true, threaded, crs) + apply(identity, GI.PointTrait(), x; calc_extent=true, threaded, crs) diff --git a/src/transformations/flip.jl b/src/transformations/flip.jl index eee3bed42..a8b73766f 100644 --- a/src/transformations/flip.jl +++ b/src/transformations/flip.jl @@ -16,11 +16,11 @@ $APPLY_KEYWORDS """ function flip(geom; kw...) if _is3d(geom) - return apply(PointTrait, geom; kw...) do p + return apply(PointTrait(), geom; kw...) do p (GI.y(p), GI.x(p), GI.z(p)) end else - return apply(PointTrait, geom; kw...) do p + return apply(PointTrait(), geom; kw...) do p (GI.y(p), GI.x(p)) end end diff --git a/src/transformations/reproject.jl b/src/transformations/reproject.jl index d59cbd92f..75c082bd3 100644 --- a/src/transformations/reproject.jl +++ b/src/transformations/reproject.jl @@ -66,11 +66,11 @@ function reproject(geom, source_crs, target_crs; end function reproject(geom, transform::Proj.Transformation; time=Inf, target_crs=nothing, kw...) if _is3d(geom) - return apply(PointTrait, geom; crs=target_crs, kw...) do p + return apply(PointTrait(), geom; crs=target_crs, kw...) do p transform(GI.x(p), GI.y(p), GI.z(p)) end else - return apply(PointTrait, geom; crs=target_crs, kw...) do p + return apply(PointTrait(), geom; crs=target_crs, kw...) do p transform(GI.x(p), GI.y(p)) end end diff --git a/src/transformations/simplify.jl b/src/transformations/simplify.jl index 712e6c5ab..8cfd526f3 100644 --- a/src/transformations/simplify.jl +++ b/src/transformations/simplify.jl @@ -28,6 +28,7 @@ f export simplify, VisvalingamWhyatt, DouglasPeucker, RadialDistance +const _SIMPLIFY_TARGET = TraitTarget{Union{GI.PolygonTrait, GI.AbstractCurveTrait, GI.MultiPointTrait, GI.PointTrait}}() const MIN_POINTS = 3 const SIMPLIFY_ALG_KEYWORDS = """ ## Keywords @@ -128,16 +129,12 @@ simplify( calc_extent=false, threaded=false, crs=nothing, kw..., ) = _simplify(DouglasPeucker(; kw...), data; prefilter_alg, calc_extent, threaded, crs) + #= For each algorithm, apply simplication to all curves, multipoints, and points, reconstructing everything else around them. =# -function _simplify(alg::SimplifyAlg, data; prefilter_alg = nothing, kw...) - simplifier(geom) = _simplify(GI.trait(geom), alg, geom; prefilter_alg = prefilter_alg) - return apply( - simplifier, - Union{GI.PolygonTrait, GI.AbstractCurveTrait, GI.MultiPointTrait, GI.PointTrait}, - data; - kw..., - ) +function _simplify(alg::SimplifyAlg, data; prefilter_alg=nothing, kw...) + simplifier(geom) = _simplify(GI.trait(geom), alg, geom; prefilter_alg) + return apply(simplifier, _SIMPLIFY_TARGET, data; kw...) end diff --git a/src/transformations/transform.jl b/src/transformations/transform.jl index 5848a020e..b09e85482 100644 --- a/src/transformations/transform.jl +++ b/src/transformations/transform.jl @@ -46,11 +46,11 @@ ctor{2, Int64}[[2, 1], [4, 3], [6, 5], [2, 1]], nothing, nothing), GeoInterface. """ function transform(f, geom; kw...) if _is3d(geom) - return apply(PointTrait, geom; kw...) do p + return apply(PointTrait(), geom; kw...) do p f(StaticArrays.SVector{3}((GI.x(p), GI.y(p), GI.z(p)))) end else - return apply(PointTrait, geom; kw...) do p + return apply(PointTrait(), geom; kw...) do p f(StaticArrays.SVector{2}((GI.x(p), GI.y(p)))) end end diff --git a/src/transformations/tuples.jl b/src/transformations/tuples.jl index fe1bf0026..9cc5799ba 100644 --- a/src/transformations/tuples.jl +++ b/src/transformations/tuples.jl @@ -14,11 +14,11 @@ $APPLY_KEYWORDS """ function tuples(geom; kw...) if _is3d(geom) - return apply(PointTrait, geom; kw...) do p + return apply(PointTrait(), geom; kw...) do p (Float64(GI.x(p)), Float64(GI.y(p)), Float64(GI.z(p))) end else - return apply(PointTrait, geom; kw...) do p + return apply(PointTrait(), geom; kw...) do p (Float64(GI.x(p)), Float64(GI.y(p))) end end diff --git a/test/methods/angles.jl b/test/methods/angles.jl index 3b4a24854..a70890764 100644 --- a/test/methods/angles.jl +++ b/test/methods/angles.jl @@ -41,4 +41,4 @@ p3_angles = [19.6538, 146.3099, 14.0362] # Multi-geometries @test all(isapprox.(GO.angles(mp1), [p2_angles; p3_angles], atol = 1e-3)) -@test all(isapprox.(GO.angles(c1), [concave_angles; p2_angles], atol = 1e-3)) \ No newline at end of file +@test all(isapprox.(GO.angles(c1), [concave_angles; p2_angles], atol = 1e-3)) diff --git a/test/methods/clipping/polygon_clipping.jl b/test/methods/clipping/polygon_clipping.jl index cec43409b..3a843b210 100644 --- a/test/methods/clipping/polygon_clipping.jl +++ b/test/methods/clipping/polygon_clipping.jl @@ -122,7 +122,7 @@ test_pairs = [ const ϵ = 1e-10 # Compare clipping results from GeometryOps and LibGEOS function compare_GO_LG_clipping(GO_f, LG_f, p1, p2) - GO_result_list = GO_f(p1, p2; target = GI.PolygonTrait) + GO_result_list = GO_f(p1, p2; target = GI.PolygonTrait()) LG_result_geom = LG_f(p1, p2) if LG_result_geom isa LG.GeometryCollection poly_list = LG.Polygon[] @@ -156,4 +156,4 @@ end @testset "Intersection" begin test_clipping(GO.intersection, LG.intersection, "intersection") end @testset "Union" begin test_clipping(GO.union, LG.union, "union") end -@testset "Difference" begin test_clipping(GO.difference, LG.difference, "difference") end \ No newline at end of file +@testset "Difference" begin test_clipping(GO.difference, LG.difference, "difference") end