Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add mode traits for convert #3

Merged
merged 1 commit into from
Dec 27, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 60 additions & 17 deletions src/GeoFormatTypes.jl
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,23 @@ end GeoFormatTypes

export GeoFormat

export CoordinateReferenceSystemFormat, EPSG, ProjString
export CoordinateReferenceSystemFormat, EPSG, ProjString, CoordSys

export GeometryFormat, GeoJSON, KML

export MixedFormat, GML
export MixedFormat, GML

export AbstractWellKnownText, WellKnownText, WellKnownText2, ESRIWellKnownText, WellKnownBinary

const PROJ_PREFIX = "+proj="
const EPSG_PREFIX = "EPSG:"
# TODO more verification that types are wrapping the right format.


# Traits for mixed crs/geometry formats.
abstract type FormatMode end
struct Geom <: FormatMode end
struct CRS <: FormatMode end
struct Mixed <: FormatMode end

"""
val(f::GeoFormat)
Expand All @@ -35,6 +39,26 @@ Abstract supertype for geospatial data formats
"""
abstract type GeoFormat end

# Convert from the same type does nothing.
Base.convert(::T, source::S) where {T<:GeoFormat,S<:T} = source
# Convert uses the `mode` trait to distinguish crs form geometry conversion
Base.convert(target::Type{<:GeoFormat}, input::GeoFormat) = begin
inputmode = mode(input)
targetmode = mode(target)
convertmode = if inputmode isa Mixed
if targetmode isa Mixed
Geom() # Geom is the default if both formats are mixed
else
targetmode
end
elseif targetmode isa typeof(inputmode)
inputmode
else
throw(ArgumentError("cannot convert $(typeof(input)) to $target"))
end
convert(target, convertmode, input)
end

"""
Formats representing coordinate reference systems
"""
Expand All @@ -49,10 +73,16 @@ abstract type GeometryFormat <: GeoFormat end
"""
Formats that may hold either or both coordinate reference systems and geometries.
"""
abstract type MixedFormat <: GeoFormat end
abstract type MixedFormat{X} <: GeoFormat end

val(x::GeoFormat) = x.val

mode(format::GeoFormat) = mode(typeof(format))
mode(::Type{<:GeometryFormat}) = Geom()
mode(::Type{<:CoordinateReferenceSystemFormat}) = CRS()
mode(::Type{<:MixedFormat}) = Mixed()
mode(::Type{<:MixedFormat{M}}) where M = M()

# Most GeoFormat types wrap String or have a constructor for string inputs
Base.convert(::Type{String}, input::GeoFormat) = val(input)
Base.convert(::Type{T}, input::AbstractString) where T <: GeoFormat = T(input)
Expand All @@ -70,44 +100,56 @@ struct ProjString <: CoordinateReferenceSystemFormat
end

"""
Well known text has a number of versions and standards, and can
Mapinfo CoordSys string
"""
struct CoordSys <: CoordinateReferenceSystemFormat
val::String
end

"""
Well known text has a number of versions and standards, and can
represent coordinate reference systems or geometric data.
"""
abstract type AbstractWellKnownText <: MixedFormat end
abstract type AbstractWellKnownText{X} <: MixedFormat{X} end

"""
Well known text v1 following the OGC standard
"""
struct WellKnownText <: AbstractWellKnownText
val::String
struct WellKnownText{X,T<:String} <: AbstractWellKnownText{X}
mode::X
val::T
end

"""
Well known text v2 following the new OGC standard
"""
struct WellKnownText2 <: AbstractWellKnownText
val::String
struct WellKnownText2{X,T<:String} <: AbstractWellKnownText{X}
mode::X
val::T
end

"""
Well known text following the ESRI standard
"""
struct ESRIWellKnownText <: AbstractWellKnownText
val::String
struct ESRIWellKnownText{X,T<:String} <: AbstractWellKnownText{X}
mode::X
val::T
end

"""
Well known binary
"""
struct WellKnownBinary{T} <: AbstractWellKnownText
struct WellKnownBinary{X,T} <: MixedFormat{X}
mode::X
val::T
end

Base.convert(::Type{String}, input::WellKnownBinary) = error("`convert` is not defined for `WellKnownBinary`")
Base.convert(::Type{String}, input::WellKnownBinary) =
error("`convert` to `String` is not defined for `WellKnownBinary`")


"""
EPSG code representing a coordinate reference system from the
EPSG code representing a coordinate reference system from the
EPSG spatial reference system registry.
"""
struct EPSG <: CoordinateReferenceSystemFormat
Expand Down Expand Up @@ -137,8 +179,9 @@ end
"""
Geography Markup Language
"""
struct GML <: MixedFormat
val::String
struct GML{X,T<:String} <: MixedFormat{X}
mode::X
val::T
end

"""
Expand Down
11 changes: 7 additions & 4 deletions test/runtests.jl
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using GeoFormatTypes, Test
using GeoFormatTypes: Geom, CRS, Mixed

@test_throws ArgumentError ProjString("+lat_ts=56.5 +ellps=GRS80")
convert(String, ProjString("+proj=merc +lat_ts=56.5 +ellps=GRS80")) == "+proj=merc +lat_ts=56.5 +ellps=GRS80"
Expand All @@ -8,9 +9,11 @@ convert(String, ProjString("+proj=merc +lat_ts=56.5 +ellps=GRS80")) == "+proj=me
@test convert(String, EPSG(4326)) == "EPSG:4326"
@test convert(Int, EPSG("EPSG:4326")) == 4326

@test convert(String, WellKnownText("test")) == "test"
@test convert(String, WellKnownText2("test")) == "test"
@test convert(String, ESRIWellKnownText("test")) == "test"
@test convert(String, GML("test")) == "test"
@test convert(String, WellKnownText(Geom(), "test")) == "test"
@test convert(String, WellKnownText2(CRS(), "test")) == "test"
@test convert(String, ESRIWellKnownText(Geom(), "test")) == "test"
@test convert(String, GML(Mixed(), "test")) == "test"
@test convert(String, KML("test")) == "test"
@test convert(String, GeoJSON("test")) == "test"

@test_throws ArgumentError convert(KML, ProjString("+proj=test"))