diff --git a/ext/DimensionalDataMakie.jl b/ext/DimensionalDataMakie.jl index ffab42dc1..d5cc8537e 100644 --- a/ext/DimensionalDataMakie.jl +++ b/ext/DimensionalDataMakie.jl @@ -1,6 +1,8 @@ module DimensionalDataMakie -using DimensionalData, Makie +using DimensionalData +using Makie +using IntervalSets using DimensionalData.Dimensions, DimensionalData.LookupArrays const DD = DimensionalData @@ -345,18 +347,18 @@ function _boxplotlike(A, attributes, labeldim) end # Plot type definitions. Not sure they will ever get called? -Makie.plottype(A::AbstractDimVector) = Makie.Scatter -Makie.plottype(A::AbstractDimMatrix) = Makie.Heatmap -Makie.plottype(A::AbstractDimArray{<:Any,3}) = Makie.Volume +Makie.plottype(::AbstractDimVector) = Makie.Scatter +Makie.plottype(::AbstractDimMatrix) = Makie.Heatmap +Makie.plottype(::AbstractDimArray{<:Any,3}) = Makie.Volume # Conversions function Makie.convert_arguments(t::Type{<:Makie.AbstractPlot}, A::AbstractDimMatrix) A1 = _prepare_for_makie(A) - xs, ys = map(parent, lookup(A1)) + xs, ys = map(_lookup_to_vector, lookup(A1)) return xs, ys, last(Makie.convert_arguments(t, parent(A1))) end function Makie.convert_arguments(t::Makie.PointBased, A::AbstractDimVector) - A = _prepare_for_makie(A) + A1 = _prepare_for_makie(A) xs = parent(lookup(A, 1)) return Makie.convert_arguments(t, xs, _floatornan(parent(A))) end @@ -365,24 +367,30 @@ function Makie.convert_arguments(t::Makie.PointBased, A::AbstractDimMatrix) end function Makie.convert_arguments(t::SurfaceLikeCompat, A::AbstractDimMatrix) A1 = _prepare_for_makie(A) - xs, ys = map(parent, lookup(A1)) + xs, ys = map(_lookup_to_vector, lookup(A1)) # the following will not work for irregular spacings, we'll need to add a check for this. - return first(xs)..last(xs), first(ys)..last(ys), last(Makie.convert_arguments(t, parent(A1))) + return xs, ys, last(Makie.convert_arguments(t, parent(A1))) +end +function Makie.convert_arguments(t::Makie.ImageLike, A::AbstractDimMatrix) + A1 = _prepare_for_makie(A) + xs, ys = map(_lookup_to_intervals, lookup(A)) + # the following will not work for irregular spacings, we'll need to add a check for this. + return xs, ys, last(Makie.convert_arguments(t, parent(A1))) end function Makie.convert_arguments( t::Makie.CellGrid, A::AbstractDimMatrix ) A1 = _prepare_for_makie(A) - xs, ys = map(parent, lookup(A1)) + xs, ys = map(_lookup_to_axis, lookup(A1)) return xs, ys, last(Makie.convert_arguments(t, parent(A1))) end function Makie.convert_arguments(t::Makie.VolumeLike, A::AbstractDimArray{<:Any,3}) A1 = _prepare_for_makie(A) - xs, ys, zs = map(parent, lookup(A1)) + xs, ys, zs = map(_lookup_to_vector, lookup(A1)) # the following will not work for irregular spacings - return first(xs)..last(xs), first(ys)..last(ys), first(zs)..last(zs), last(Makie.convert_arguments(t, parent(A1))) + return xs, ys, zs, last(Makie.convert_arguments(t, parent(A1))) end -# fallbacks with descriptive error messages +# # fallbacks with descriptive error messages function Makie.convert_arguments(t::Makie.ConversionTrait, A::AbstractDimArray{<:Any,N}) where {N} @warn "$t not implemented for `AbstractDimArray` with $N dims, falling back to parent array type" return Makie.convert_arguments(t, parent(A)) @@ -512,6 +520,27 @@ function _keywords2dimpairs(x, y) end end +function _lookup_to_vector(l) + if isintervals(l) + bs = intervalbounds(l) + x = first.(bs) + push!(x, last(last(bs))) + else + collect(parent(l)) + end +end + +function _lookup_to_interval(l) + l1 = if isnolookup(l) + Sampled(parent(l); order=ForwardOrdered(), sampling=Intervals(Center()), span=Regular(1)) + elseif ispoints(l) + set(l, Intervals()) + else + l + end + return IntervalSets.Interval(bounds(l1)...) +end + _floatornan(A::AbstractArray{<:Union{Missing,<:Real}}) = _floatornan64.(A) _floatornan(A::AbstractArray{<:Union{Missing,Float64}}) = _floatornan64.(A) _floatornan(A) = A diff --git a/test/plotrecipes.jl b/test/plotrecipes.jl index 27b737ec4..df59c3345 100644 --- a/test/plotrecipes.jl +++ b/test/plotrecipes.jl @@ -1,11 +1,17 @@ using DimensionalData, Test, Dates -using Plots, StatsPlots, Unitful +using Unitful import Distributions using DimensionalData: Metadata, NoMetadata, ForwardOrdered, ReverseOrdered, Unordered, Sampled, Categorical, NoLookup, Transformed, Regular, Irregular, Explicit, Points, Intervals, Start, Center, End + +@testset "Plots.jl" begin + +using Plots +using StatsPlots + A1 = rand(Distributions.Normal(), 20) ref = (Ti(Sampled(1:1; order=ForwardOrdered(), span=Regular(Day(1)), sampling=Points())),) da1_regular = DimArray(A1, X(1:50:1000); name=:Normal, refdims=ref) @@ -131,8 +137,6 @@ for da in (da3_regular, da3_noindex, da3_ni_r_ni, da3_c_c_c, da3_XYZ, da3_XTiZ, end end -nothing - # Not sure how recipes work for this # andrewsplot(da2) @@ -159,13 +163,19 @@ nothing # da_im2 = DimArray(im2, (X(10:10:100), Y(10:10:100)), "Image") # da_im2 |> plot -using CairoMakie: CairoMakie as M -using ColorTypes +end + + @testset "Makie" begin + using CairoMakie: CairoMakie as M + using ColorTypes + # 1d A1 = rand(X('a':'e'); name=:test) A1m = rand([missing, (1:3.)...], X('a':'e'); name=:test) + A1u = rand([missing, (1:3.)...], X(1u"s":1u"s":3u"s"); name=:test) + A1ui = rand([missing, (1:3.)...], X(1u"s":1u"s":3u"s"; sampling=Intervals(Start())); name=:test) A1num = rand(X(-10:10)) A1m .= A1 A1m[3] = missing @@ -174,6 +184,10 @@ using ColorTypes fig, ax, _ = M.plot(A1m) fig, ax, _ = M.plot(parent(A1m)) M.plot!(ax, A1m) + fig, ax, _ = M.plot(A1u) + M.plot!(ax, A1u) + fig, ax, _ = M.plot(A1ui) + M.plot!(ax, A1ui) fig, ax, _ = M.plot(A1num) M.reset_limits!(ax) org = first(ax.finallimits.val.origin) @@ -187,32 +201,46 @@ using ColorTypes M.scatter!(ax, A1m) fig, ax, _ = M.lines(A1) M.lines!(ax, A1) + fig, ax, _ = M.lines(A1u) + M.lines!(ax, A1u) fig, ax, _ = M.lines(A1m) M.lines!(ax, A1m) fig, ax, _ = M.scatterlines(A1) M.scatterlines!(ax, A1) + fig, ax, _ = M.scatterlines(A1u) + M.scatterlines!(ax, A1u) fig, ax, _ = M.scatterlines(A1m) M.scatterlines!(ax, A1m) fig, ax, _ = M.stairs(A1) M.stairs!(ax, A1) + fig, ax, _ = M.stairs(A1u) + M.stairs!(ax, A1u) fig, ax, _ = M.stairs(A1m) M.stairs!(ax, A1m) fig, ax, _ = M.stem(A1) M.stem!(ax, A1) + fig, ax, _ = M.stem(A1u) + M.stem!(ax, A1u) fig, ax, _ = M.stem(A1m) M.stem!(ax, A1m) fig, ax, _ = M.barplot(A1) M.barplot!(ax, A1) + fig, ax, _ = M.barplot(A1u) + M.barplot!(ax, A1u) fig, ax, _ = M.barplot(A1m) M.barplot!(ax, A1m) fig, ax, _ = M.waterfall(A1) M.waterfall!(ax, A1) + fig, ax, _ = M.waterfall(A1u) + M.waterfall!(ax, A1u) fig, ax, _ = M.waterfall(A1m) M.waterfall!(ax, A1m) # 2d A2 = rand(X(10:10:100), Y(['a', 'b', 'c'])) A2r = rand(Y(10:10:100), X(['a', 'b', 'c'])) A2m = rand([missing, (1:5)...], Y(10:10:100), X(['a', 'b', 'c'])) + A2u = rand(Y(10u"km":10u"km":100u"km"), X(['a', 'b', 'c'])) + A2ui = rand(Y(10u"km":10u"km":100u"km"; sampling=Intervals(Start())), X(['a', 'b', 'c'])) A2m[3] = missing A2rgb = rand(RGB, X(10:10:100), Y(['a', 'b', 'c'])) @@ -220,6 +248,10 @@ using ColorTypes M.plot!(ax, A2) fig, ax, _ = M.plot(A2m) M.plot!(ax, A2m) + fig, ax, _ = M.plot(A2u) + M.plot!(ax, A2u) + fig, ax, _ = M.plot(A2ui) + M.plot!(ax, A2ui) fig, ax, _ = M.plot(A2rgb) M.plot!(ax, A2rgb) fig, ax, _ = M.heatmap(A2) @@ -241,17 +273,26 @@ using ColorTypes fig, ax, _ = M.rainclouds(A2) M.rainclouds!(ax, A2) - # @test_throws ErrorException M.rainclouds(A2m) # MethodError ? missing values in data not supported - # @test_throws ErrorException M.rainclouds!(ax, A2m) + fig, ax, _ = M.rainclouds(A2u) + M.rainclouds!(ax, A2u) + @test_throws ErrorException M.rainclouds(A2m) # MethodError ? missing values in data not supported fig, ax, _ = M.surface(A2) M.surface!(ax, A2) + fig, ax, _ = M.surface(A2u) + M.surface!(ax, A2u) + fig, ax, _ = M.surface(A2ui) + M.surface!(ax, A2ui) fig, ax, _ = M.surface(A2m) M.surface!(ax, A2m) # Series also puts Categories in the legend no matter where they are # TODO: method series! is incomplete, we need to include the colors logic, as in series. There should not be any issue if the correct amount of colours is provided. fig, ax, _ = M.series(A2) # M.series!(ax, A2) + fig, ax, _ = M.series(A2u) + # M.series!(ax, A2u) + fig, ax, _ = M.series(A2ui) + # M.series!(ax, A2u) fig, ax, _ = M.series(A2r) # M.series!(ax, A2r) fig, ax, _ = M.series(A2r; labeldim=Y) @@ -289,6 +330,7 @@ using ColorTypes # 3d, all these work with GLMakie A3 = rand(X(7), Z(10), Y(5)) + A3u = rand(X((1:7)u"m"), Z((1.0:1:10.0)u"m"), Y((1:5)u"g")) A3m = rand([missing, (1:7)...], X(7), Z(10), Y(5)) A3m[3] = missing A3rgb = rand(RGB, X(7), Z(10), Y(5)) @@ -296,14 +338,24 @@ using ColorTypes M.volume!(ax, A3) fig, ax, _ = M.volume(A3m) M.volume!(ax, A3m) - # Broken in Makie ? + + # Units are broken in Makie ? + # fig, ax, _ = M.volume(A3u) + # M.volume!(ax, A3u) + + fig, ax, _ = M.volumeslices(A3) + M.volumeslices!(ax, A3) + # Need to manually specify colorrange + fig, ax, _ = M.volumeslices(A3m; colorrange=(1, 7)) + M.volumeslices!(ax, A3m; colorrange=(1, 7)) + + # Unitful volumeslices broken in Makie ? + # fig, ax, _ = M.volumeslices(A3u) + # M.volumeslices!(ax, A3u) + + # RGB volumeslices broken in Makie ? # fig, ax, _ = M.volumeslices(A3rgb) # M.volumeslices!(ax, A3rgb) - # fig, ax, _ = M.volumeslices(A3) - # M.volumeslices!(ax, A3) - # colorrange isn't detected here - # fig, ax, _ = M.volumeslices(A3m; colorrange=(1, 7)) - # M.volumeslices!(ax, A3m; colorrange=(1, 7)) # fig, ax, _ = M.volumeslices(A3rgb) # M.volumeslices!(ax, A3rgb) # x/y/z can be specified