Skip to content

Commit

Permalink
Update dependencies, move to Julia extensions (#104)
Browse files Browse the repository at this point in the history
  • Loading branch information
Datseris authored Jan 31, 2024
1 parent 3177919 commit 9d689f3
Show file tree
Hide file tree
Showing 4 changed files with 106 additions and 94 deletions.
17 changes: 11 additions & 6 deletions Project.toml
Original file line number Diff line number Diff line change
@@ -1,28 +1,33 @@
name = "ClimateBase"
uuid = "35604d93-0fb8-4872-9436-495b01d137e2"
authors = ["Datseris <[email protected]>", "Philippe Roy <[email protected]>"]
version = "0.16.5"
version = "0.17.0"

[deps]
Dates = "ade2ca70-3891-5945-98fb-dc099432e06a"
DimensionalData = "0703355e-b756-11e9-17c0-8b28908087d0"
Interpolations = "a98d9a8b-a2ab-59e6-89dd-64a1c18fca59"
NCDatasets = "85f8d34a-cbdd-5861-8df4-14fed0d494ab"
Requires = "ae029012-a4dd-5104-9daa-d747884805df"
SignalDecomposition = "11a47235-7b84-4c7c-b885-fc3e2a9cf955"
StaticArrays = "90137ffa-7385-5640-81b9-e52037218182"
Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2"
StatsBase = "2913bbd2-ae8a-5f71-8c99-4fb6c76f3a91"

[weakdeps]
GeoMakie = "db073c08-6b98-4ee5-b6a4-5efafb3259c6"

[extensions]
ClimateBaseVisualizations = "GeoMakie"

[compat]
DimensionalData = "0.20.1, 0.21, 0.22, 0.23, 0.24"
Interpolations = "0.13.2, 0.14"
GeoMakie = "0.6"
Interpolations = "0.13.2, 0.14, 0.15"
NCDatasets = "0.11, 0.12"
Requires = "1"
SignalDecomposition = "1"
SignalDecomposition = "1.1"
StaticArrays = "0.12, 1.0"
StatsBase = "0.33, 0.34"
julia = "1.5"
julia = "1.9"

[extras]
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
Expand Down
88 changes: 88 additions & 0 deletions ext/ClimateBaseVisualizations.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
module ClimateBaseVisualizations

using ClimateBase, GeoMakie

function ClimateBase.climplot(A, args...; scatter = spacestructure(A) == CoordinateSpace(),
source = "+proj=longlat +datum=WGS84", dest = "+proj=eqearth",
colorbar = true, name = string(DimensionalData.name(A)), coastkwargs = NamedTuple(), kwargs...)

# TODO: Perhaps setting custom colorrange leads to better plots?
# vmin = haskey(kwargs, :vmin) ? kwargs[:vmin] : quantile(data, 0.025)
# vmax = haskey(kwargs, :vmax) ? kwargs[:vmax] : quantile(data, 0.975)

fig = GeoMakie.Figure()
ax = GeoMakie.GeoAxis(fig[1,1]; source, dest, title = GeoMakie.Observable(""))

coastplot = lines!(ax, GeoMakie.coastlines(); color = :black, overdraw = true, coastkwargs...)
translate!(coastplot, 0, 0, 99) # ensure they are on top of other plotted elements

# TODO: @assert A has only space dimension

if scatter
el = climscatter!(ax, A; kwargs...)
else
el = climsurface!(ax, A; kwargs...)
end

if colorbar
cb = GeoMakie.Colorbar(fig[1, 2], el; label = name)
else
cb = nothing
end
return fig, ax, el, cb
end

##########################################################################################
# # Scatter
##########################################################################################
# Notice that `A` is not declared as `ClimArray`, but assumed to be.
# Duck-typing for Observables.
function ClimateBase.climscatter!(ax, A; colormap = :dense, kwargs...)
if hasdim(A, Coord)
lonlat = dims(A, Coord).val
elseif dims(A)[1] isa Lon
londim = dims(A, Lon)
latdim = dims(A, Lat)
lonlat = [GeoMakie.Point2f0(l,lat) for lat in latdim for l in londim]
else
error("Unknown spatial dimensions for input.")
end
data = GeoMakie.lift(A -> vec(A.data), A)
GeoMakie.scatter!(ax, lonlat; color = data, colormap, kwargs...)
end

##########################################################################################
# # Surface
##########################################################################################
# TODO: Change this to `contourf`

# Notice that `A` is not declared as `ClimArray`, but assumed to be.
# Duck-typing for Observables.
function ClimateBase.climsurface!(ax, A; colormap = :dense, kwargs...)
# TODO: @assert A has only space
if hasdim(A, Coord)
# TODO: @pkeil this is for you
error("Surface plots for `Coord` arrays are not supported yet!")
end
# TODO: This needs to depend on lon_0 or use the "meridian cut" function
B = GeoMakie.lift(A -> longitude_circshift(A), A)
lon = dims(B, Lon).val
lat = dims(B, Lat).val
data = GeoMakie.lift(B -> B.data, B)
GeoMakie.surface!(ax, lon, lat, data; shading = false, colormap, kwargs...)
end


##########################################################################################
# # Observables overloads
##########################################################################################
# These overloads allows me to write generic code that does not care if input
# is observable or not. This leads to simple, clean, small, elegant code for
# animating fields by simply replacing them with their observables.

GeoMakie.lift(f, x::ClimArray) = f(x)
ClimateBase.DimensionalData.dims(o::GeoMakie.Observable, args...) = dims(o.val, args...)
ClimateBase.DimensionalData.name(o::GeoMakie.Observable, args...) = name(o.val, args...)
ClimateBase.spacestructure(A::GeoMakie.Observable) = spacestructure(A.val)

end
8 changes: 1 addition & 7 deletions src/ClimateBase.jl
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,6 @@ include("tsa/continuation.jl")
include("tsa/decomposition.jl")

include("exports.jl")

using Requires
function __init__()
@require GeoMakie="db073c08-6b98-4ee5-b6a4-5efafb3259c6" begin
include("plotting/geomakie.jl")
end
end
include("plotting/geomakie.jl")

end # module
87 changes: 6 additions & 81 deletions src/plotting/geomakie.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ export climscatter, climscatter!, climsurface!, climsurface, climplot

"""
climplot(A::ClimArray; kwargs...) → fig, ax, el, cb
Main plotting function that dispatches to [`climscatter!`](@ref) if `A` has
an [`CoordinateSpace`](@ref) dimension, or to [`climsurface!`](@ref) for [`OrthogonalSpace`](@ref).
Expand All @@ -13,85 +14,9 @@ Plotting from ClimateBase.jl also works with `Observable`s that enclose a `ClimA
You can update the values of the observable with another `ClimArray` with the same spatial
dimension, and the plot will be updated. See documentation online for examples.
"""
function climplot(A, args...; scatter = spacestructure(A) == CoordinateSpace(),
source = "+proj=longlat +datum=WGS84", dest = "+proj=eqearth",
colorbar = true, name = string(DimensionalData.name(A)), kwargs...)

# TODO: Perhaps setting custom colorrange leads to better plots?
# vmin = haskey(kwargs, :vmin) ? kwargs[:vmin] : quantile(data, 0.025)
# vmax = haskey(kwargs, :vmax) ? kwargs[:vmax] : quantile(data, 0.975)

fig = GeoMakie.Figure()
ax = GeoMakie.GeoAxis(fig[1,1]; source, dest, title = GeoMakie.Observable(""))

coastplot = lines!(ax, GeoMakie.coastlines(); color = :black, overdraw = true, coastkwargs...)
translate!(coastplot, 0, 0, 99) # ensure they are on top of other plotted elements

# TODO: @assert A has only space dimension

if scatter
el = climscatter!(ax, A; kwargs...)
else
el = climsurface!(ax, A; kwargs...)
end

if colorbar
cb = GeoMakie.Colorbar(fig[1, 2], el; label = name)
else
cb = nothing
end
return fig, ax, el, cb
end

##########################################################################################
# # Scatter
##########################################################################################
# Notice that `A` is not declared as `ClimArray`, but assumed to be.
# Duck-typing for Observables.
function climscatter!(ax, A; colormap = :dense, kwargs...)
if hasdim(A, Coord)
lonlat = dims(A, Coord).val
elseif dims(A)[1] isa Lon
londim = dims(A, Lon)
latdim = dims(A, Lat)
lonlat = [GeoMakie.Point2f0(l,lat) for lat in latdim for l in londim]
else
error("Unknown spatial dimensions for input.")
end
data = GeoMakie.lift(A -> vec(A.data), A)
GeoMakie.scatter!(ax, lonlat; color = data, colormap, kwargs...)
end

##########################################################################################
# # Surface
##########################################################################################
# TODO: Change this to `contourf`

# Notice that `A` is not declared as `ClimArray`, but assumed to be.
# Duck-typing for Observables.
function climsurface!(ax, A; colormap = :dense, kwargs...)
# TODO: @assert A has only space
if hasdim(A, Coord)
# TODO: @pkeil this is for you
error("Surface plots for `Coord` arrays are not supported yet!")
end
# TODO: This needs to depend on lon_0 or use the "meridian cut" function
B = GeoMakie.lift(A -> longitude_circshift(A), A)
lon = dims(B, Lon).val
lat = dims(B, Lat).val
data = GeoMakie.lift(B -> B.data, B)
GeoMakie.surface!(ax, lon, lat, data; shading = false, colormap, kwargs...)
end


##########################################################################################
# # Observables overloads
##########################################################################################
# These overloads allows me to write generic code that does not care if input
# is observable or not. This leads to simple, clean, small, elegant code for
# animating fields by simply replacing them with their observables.
function climplot end

GeoMakie.lift(f, x::ClimArray) = f(x)
DimensionalData.dims(o::GeoMakie.Observable, args...) = dims(o.val, args...)
DimensionalData.name(o::GeoMakie.Observable, args...) = name(o.val, args...)
spacestructure(A::GeoMakie.Observable) = spacestructure(A.val)
function climscatter end
function climscatter! end
function climsurface end
function climsurface! end

0 comments on commit 9d689f3

Please sign in to comment.