JuliaGeo / GeoFormatTypes.jl

Wrapper types for spatial data formats like well known text, KML, Proj4 strings.
https://juliageo.github.io/GeoFormatTypes.jl/stable
MIT License
6 stars 0 forks source link

Make `EPSG` a subset of some `AgencyCode` abstract type #35

Open asinghvi17 opened 2 months ago

asinghvi17 commented 2 months ago

Motivation:

CRS can be addressed as eg ESRI:xyz or OGC:xyz as well, and such definitions exist online and in datasets. It would be useful to allow supporting arbitrary agencies (maybe with an error for unknown agencies) such that:

abstract type AgencyCodeCRS{AgencySymbol, NumTypes} end
const EPSG{N} = AgencyCodeCRS{:EPSG, N}
const EPSG = EPSG{1}

or something.

rafaqz commented 2 months ago

One problem is printing will be ugly

Can we just add OGC ?

asinghvi17 commented 2 months ago

You mean as another type? Sure, but we would have to implement all the converts in ArchGDAL too. An abstract supertype seems easier to extend, but the risk of typos also increases.

In terms of printing I think it should just print the alias?

rafaqz commented 2 months ago

Sorry guess I don't totally get your example, your EPSG is not concrete...

asinghvi17 commented 2 months ago

Ah, I guess the AuthorityCRS should be a concrete type (changing terminology a bit):


struct AuthorityCRS{Authority, N} <: GFT.CoordinateReferenceSystemFormat
    val::NTuple{N,Int}
    function AuthorityCRS{Authority, N}(input::NTuple{N, Integer}) where {Authority, N}
        @assert Authority isa Symbol
        # maybe @assert Authority in (:EPSG, :OGC, :ESRI)
        return new{Authority, N}(input)
    end
end
AuthorityCRS{Authority}(input::NTuple{N,Integer}) where {Authority, N} = AuthorityCRS{Authority, N}(convert(NTuple{N,Int}, input))
AuthorityCRS{Authority}(input::Vararg{Integer}) where {Authority} = AuthorityCRS{Authority}(input)

AuthorityCRS(input::Vararg{Integer}) = AuthorityCRS{:EPSG}(input) # assume EPSG as the default
AuthorityCRS(authority::Symbol, input::NTuple{N,Integer}) where {N} = AuthorityCRS{authority, N}(convert(NTuple{N,Int}, input))

function AuthorityCRS{Authority}(input::AbstractString) where Authority
    startswith(input, String(Authority)) || throw(ArgumentError("String $(input) does not start with $(Authority)"))
    code = Tuple(parse.(Int, split(input[findlast(String(Authority), input).stop+2:end], "+")))
    AuthorityCRS{Authority}(code)
end

const EPSG{N} = AuthorityCRS{:EPSG, N}
const OGC{N} = AuthorityCRS{:OGC, N}
const ESRI{N} = AuthorityCRS{:ESRI, N}

With these definitions,

julia> EPSG(4326)
EPSG{1}((4326,))

julia> GFT.EPSG(4326)
GeoFormatTypes.EPSG{1}((4326,))

which is basically the same to me, just a bit more flexible maybe. Dispatch also works since the types are separated by a parameter.

asinghvi17 commented 2 months ago

Just edited that comment - this now has functionality fully equivalent to EPSG, just that adding OGC and ESRI was a 1-liner. Users can add their own authorities or personal databases as they desire, so long as they're expressed as symbols.

rafaqz commented 2 months ago

I still prefer struct OGC just for the stack trace brevity

asinghvi17 commented 2 months ago

I'll check but I think that the alias should work there too.

asinghvi17 commented 2 months ago

Injecting an error() at the last line of the string parsing function,


julia> EPSG("EPSG:4321")
ERROR: 
Stacktrace:
 [1] error()
   @ Base ./error.jl:44
 [2] (EPSG)(input::String)
   @ Main ~/.julia/dev/GeoMakie/examples/specialized/tyler_noninteractive.jl:50
 [3] top-level scope
   @ REPL[94]:1

and

julia> _beer(x::EPSG) = error()
julia> _beer(EPSG(1234))
ERROR: 
Stacktrace:
 [1] error()
   @ Base ./error.jl:44
 [2] _beer(x::EPSG{1})
   @ Main ~/.julia/dev/GeoMakie/examples/specialized/tyler_noninteractive.jl:54
 [3] top-level scope
   @ REPL[95]:1
rafaqz commented 2 months ago

Right. But I'm pretty sure there are places the alias fails