jw3126 / UnitfulRecipes.jl

Plots.jl recipes for Unitful.jl arrays
MIT License
37 stars 10 forks source link

`vline` and `hline` not supporting both `xunit` and `yunit` #54

Closed agerlach closed 3 years ago

agerlach commented 3 years ago
using Plots, Unitful, UnitfulRecipes

vline([2u"m"], xunit = u"ft") #works
vline([2u"m"], xunit = u"ft", yunit = u"ft") #LoadError: KeyError: key :unit not found
hline([2u"m"], yunit = u"ft") #works
hline([2u"m"], xunit = u"ft", yunit = u"ft") #LoadError: KeyError: key :unit not found

The calls where vline has a yunit and hline has a xunit produces

ERROR: LoadError: KeyError: key :unit not found
Stacktrace:
  [1] getindex(h::Dict{Symbol, Any}, key::Symbol)
    @ Base ./dict.jl:482
  [2] default(k::Symbol)
    @ Plots ~/.julia/packages/Plots/XuV6v/src/args.jl:740
  [3] warn_on_unsupported_args(pkg::Plots.GRBackend, plotattributes::RecipesPipeline.DefaultsDict)
    @ Plots ~/.julia/packages/Plots/XuV6v/src/args.jl:1215
  [4] _add_the_series(plt::Plots.Plot{Plots.GRBackend}, sp::Plots.Subplot{Plots.GRBackend}, plotattributes::RecipesPipeline.DefaultsDict)
    @ Plots ~/.julia/packages/Plots/XuV6v/src/pipeline.jl:359
  [5] add_series!(plt::Plots.Plot{Plots.GRBackend}, plotattributes::RecipesPipeline.DefaultsDict)
    @ Plots ~/.julia/packages/Plots/XuV6v/src/pipeline.jl:302
  [6] _process_seriesrecipe(plt::Any, plotattributes::Any)
    @ RecipesPipeline ~/.julia/packages/RecipesPipeline/CirY4/src/series_recipe.jl:46
  [7] _process_seriesrecipe(plt::Any, plotattributes::Any)
    @ RecipesPipeline ~/.julia/packages/RecipesPipeline/CirY4/src/series_recipe.jl:60
  [8] _process_seriesrecipes!(plt::Any, kw_list::Any)
    @ RecipesPipeline ~/.julia/packages/RecipesPipeline/CirY4/src/series_recipe.jl:27
  [9] recipe_pipeline!(plt::Any, plotattributes::Any, args::Any)
    @ RecipesPipeline ~/.julia/packages/RecipesPipeline/CirY4/src/RecipesPipeline.jl:97
 [10] _plot!(plt::Plots.Plot, plotattributes::Any, args::Any)
    @ Plots ~/.julia/packages/Plots/XuV6v/src/plot.jl:172
 [11] #plot#154
    @ ~/.julia/packages/Plots/XuV6v/src/plot.jl:58 [inlined]
 [12] vline(args::Any; kw::Any)
    @ Plots ~/.julia/packages/RecipesBase/92zOw/src/RecipesBase.jl:403
 [13] top-level scope
    @ ~/Library/Application Support/Code/User/globalStorage/buenon.scratchpads/scratchpads/34cdd249d862f9176704eaed2c12df8d/scratch1.jl:4
in expression starting at /Users/gerlacar/Library/Application Support/Code/User/globalStorage/buenon.scratchpads/scratchpads/34cdd249d862f9176704eaed2c12df8d/scratch1.jl:4

I realize that it doesn't really make sense to spec yunit for vline and xunit for hline , but this issue creeps up using vline and hline series types in a user recipe, e.g.

@userplot MyCoolPlot
@recipe function f(mcp::MyCoolPlot)
    x,y = mcp.args
    label := nothing
    @series begin
        seriestype := :vline
        seriescolor := :black
        linestyle := :dash
        x
    end
    @series begin
        seriestype := :hline
        seriescolor := :black
        linestyle := :dash
        y
    end
    seriestype --> :scatter
    x,y
end

mycoolplot(20*rand(3)*u"m", 20*rand(3)*u"m") # works
mycoolplot(20*rand(3)*u"m", 20*rand(3)*u"m", xunit = u"ft") # same issue as above
briochemc commented 3 years ago

Yep I'm not sure how we could handle that. AFAIU, this is actually not going through the recipe. E.g., without UnitfulRecipes, if you try

using Plots, Unitful
plot(rand(3), xunit=u"m")

you'll get the same error I think.


A workaround would be to first make an empty cool plot in ft and then add the data in m:

julia> mycoolplot(Float64[]*u"ft", Float64[]*u"ft") # works and sets the frame in ft

julia> mycoolplot!(20*rand(3)*u"m", 20*rand(3)*u"m") # now works and is converted to ft

🤷

agerlach commented 3 years ago

Thanks for the quick reply. Yeah, that produces the same error. Based on your suggestion, I think I'll use this as a convenient hack

@userplot UnitsPlot
@recipe function f(mcp::UnitsPlot)
    ux = mcp.args[1]
    uy = length(mcp.args) > 1 ? mcp.args[2] : ux
    if length(mcp.args) > 2
        return Float64[]*ux, Float64[]*uy, Float64[]*mcp.args[3]
    else
        return Float64[]*ux, Float64[]*uy
    end
end

unitsplot(u"ft")
mycoolplot!(20*rand(3)*u"m", 20*rand(3)*u"ft")

unitsplot(u"ft", u"m")
mycoolplot!(20*rand(3)*u"m", 20*rand(3)*u"ft")
agerlach commented 3 years ago

Actually, this is probably cleaner

@recipe function f(ux::Unitful.FreeUnits, uy::Unitful.FreeUnits = ux)
    label := nothing
    Float64[]*ux, Float64[]*uy
end

@recipe function f(ux::Unitful.FreeUnits, uy::Unitful.FreeUnits, uz::Unitful.FreeUnits)
    label := nothing
    Float64[]*ux, Float64[]*uy, Float64[]*uz
end

plot(u"ft", u"m")
mycoolplot!(20*rand(3)*u"m", 20*rand(3)*u"ft")