Skip to content

Commit 58da60c

Browse files
committed
Split types out into multiple files + add algo interface
1 parent 0136c7d commit 58da60c

File tree

8 files changed

+374
-168
lines changed

8 files changed

+374
-168
lines changed

GeometryOpsCore/src/GeometryOpsCore.jl

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import GeoInterface
66
import GeoInterface as GI
77
import GeoInterface: Extents
88

9-
# Import all names from GeoInterface and Extents, so users can do `GO.extent` or `GO.trait`.
9+
# Import all exported names from GeoInterface and Extents, so users can do `GO.extent` or `GO.trait`.
1010
for name in names(GeoInterface)
1111
@eval using GeoInterface: $name
1212
end
@@ -19,7 +19,14 @@ using DataAPI
1919
import StableTasks
2020

2121
include("keyword_docs.jl")
22-
include("types.jl")
22+
include("constants.jl")
23+
24+
include("types/manifold.jl")
25+
include("types/algorithm.jl")
26+
include("types/operation.jl")
27+
include("types/exceptions.jl")
28+
include("types/booltypes.jl")
29+
include("types/traittarget.jl")
2330

2431
include("apply.jl")
2532
include("applyreduce.jl")

GeometryOpsCore/src/types.jl

Lines changed: 0 additions & 166 deletions
This file was deleted.
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
#=
2+
# `Algorithm`s
3+
4+
An `Algorithm` is a type that describes the algorithm used to perform some [`Operation`](@ref).
5+
6+
An algorithm may be associated with one or many [`Manifold`](@ref)s. It may either have the manifold as a field, or have it as a static parameter (e.g. `struct GEOS <: Manifold{Planar}`).
7+
8+
9+
Algorithms are:
10+
* Ways to perform an operation
11+
* For example: LHuilier, Bessel, Ericsson for spherical area
12+
* May be manifold agnostic (like simplification) or restrictive (like GEOS only works on planar, PROJ algorithm for arclength and area only works on geodesic)
13+
* May or may not carry manifolds around, but manifold should always be accessible from manifold(alg) - it's not necessary that fixed manifold args can skip carrying the manifold around, eg in the case of Proj{Geodesic}.
14+
15+
=#
16+
17+
export Algorithm, AutoAlgorithm, ManifoldIndependentAlgorithm, SingleManifoldAlgorithm, NoAlgorithm
18+
19+
abstract type Algorithm{M <: Manifold} end
20+
21+
struct AutoAlgorithm{T, M <: Manifold} <: Algorithm{M}
22+
manifold::M
23+
x::T
24+
end
25+
26+
AutoAlgorithm(m::Manifold; kwargs...) = AutoAlgorithm(m, kwargs)
27+
AutoAlgorithm(; kwargs...) = AutoAlgorithm(AutoManifold(), kwargs)
28+
29+
30+
abstract type ManifoldIndependentAlgorithm{M <: Manifold} <: Algorithm{M} end
31+
32+
abstract type SingleManifoldAlgorithm{M <: Manifold} <: Algorithm{M} end
33+
34+
struct NoAlgorithm{M <: Manifold} <: Algorithm{M}
35+
m::M
36+
end
37+
38+
NoAlgorithm() = NoAlgorithm(Planar()) # TODO: add a NoManifold or AutoManifold type?
39+
# Maybe AutoManifold
40+
# and then we have DD.format like materialization
41+
42+
function (Alg::Type{<: SingleManifoldAlgorithm{M}})(m::M; kwargs...) where {M}
43+
# successful - the algorithm is designed for this manifold
44+
# in this case, just return `Alg(; kwargs...)`
45+
return Alg(; kwargs...)
46+
end
47+
48+
function (Alg::Type{<: ManifoldIndependentAlgorithm{M}})(m::Manifold; kwargs...) where {M}
49+
# this catches the case where the algorithm doesn't match the manifold
50+
# throw a WrongManifoldException and be done with it
51+
throw(WrongManifoldException{typeof(m), M, Alg}())
52+
end
53+
54+
# for example
55+
56+
struct MyExternalArbitraryPackageAlgorithm <: SingleManifoldAlgorithm{Planar}
57+
kw1::Int
58+
kw2::String
59+
end # this already has the methods specified
60+
61+
struct MyIndependentAlgorithm{M <: Manifold} <: ManifoldIndependentAlgorithm{M}
62+
m::M
63+
kw1::Int
64+
kw2::String
65+
end
66+
67+
MyIndependentAlgorithm(m::Manifold; kw1 = 1, kw2 = "hello") = MyIndependentAlgorithm(m, kw1, kw2)
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
#=
2+
# `BoolsAsTypes`
3+
4+
In `apply` and `applyreduce`, we pass `threading` and `calc_extent` as types, not simple boolean values.
5+
6+
This is to help compilation - with a type to hold on to, it's easier for
7+
the compiler to separate threaded and non-threaded code paths.
8+
9+
Note that if we didn't include the parent abstract type, this would have been really
10+
type unstable, since the compiler couldn't tell what would be returned!
11+
12+
We had to add the type annotation on the `booltype(::Bool)` method for this reason as well.
13+
14+
15+
!!! note Static.jl
16+
17+
Static.jl is a package that provides a way to store and manipulate static values.
18+
But it creates a lot of invalidations since it breaks the assumption that operations
19+
like `<`, `>` and `==` can only return booleans. So we don't use it here.
20+
21+
=#
22+
23+
export BoolsAsTypes, True, False, booltype
24+
25+
"""
26+
abstract type BoolsAsTypes
27+
28+
"""
29+
abstract type BoolsAsTypes end
30+
31+
"""
32+
struct True <: BoolsAsTypes
33+
34+
A struct that means `true`.
35+
"""
36+
struct True <: BoolsAsTypes end
37+
38+
"""
39+
struct False <: BoolsAsTypes
40+
41+
A struct that means `false`.
42+
"""
43+
struct False <: BoolsAsTypes end
44+
45+
"""
46+
booltype(x)
47+
48+
Returns a [`BoolsAsTypes`](@ref) from `x`, whether it's a boolean or a BoolsAsTypes.
49+
"""
50+
function booltype end
51+
52+
@inline booltype(x::Bool)::BoolsAsTypes = x ? True() : False()
53+
@inline booltype(x::BoolsAsTypes)::BoolsAsTypes = x
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
#=
2+
# Errors and exceptions
3+
4+
We create a few custom exception types in this file,
5+
that have nice show methods that we can use for certain errors.
6+
7+
This makes it substantially easier to catch specific kinds of errors and show them.
8+
For example, we can catch `WrongManifoldException` and show a nice error message,
9+
and error hinters can be specialized to that as well.
10+
=#
11+
12+
export WrongManifoldException
13+
14+
"""
15+
WrongManifoldException{InputManifold, DesiredManifold, Algorithm} <: Exception
16+
17+
This error is thrown when an `Algorithm` is called with a manifold that it was not designed for.
18+
19+
It's mainly called for [`SingleManifoldAlgorithm`](@ref) types.
20+
"""
21+
struct WrongManifoldException{InputManifold, DesiredManifold, Algorithm} <: Base.Exception
22+
description::String
23+
end
24+
25+
WrongManifoldException{I, D, A}() where {I, D, A} = WrongManifoldException{I, D, A}("")
26+
27+
function Base.showerror(io::IO, e::WrongManifoldException{I,D,A}) where {I,D,A}
28+
print(io, "Algorithm ")
29+
printstyled(io, A; bold = true, color = :green)
30+
print(io, " is only compatible with manifold ")
31+
printstyled(io, D; bold = true, color = :blue)
32+
print(io, ",\n but it was called with manifold ")
33+
printstyled(io, I; bold = true, color = :red)
34+
print(io, ".")
35+
36+
println(io, """
37+
\n
38+
To fix this issue, you can specify the manifold explicitly,
39+
e.g. `$A($D(); kwargs...)`, when constructing the algorithm.
40+
""")
41+
if !isempty(e.description)
42+
print(io, "\n\n")
43+
print(io, e.description)
44+
end
45+
end

0 commit comments

Comments
 (0)