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

Update dependencies, move to Julia extensions #104

Merged
merged 1 commit into from
Jan 31, 2024
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
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
Loading