plotly subplot layouts
This commit is contained in:
parent
851fe9f72c
commit
c1baca181c
@ -214,6 +214,7 @@ const _subplot_defaults = KW(
|
|||||||
:top_margin => :match,
|
:top_margin => :match,
|
||||||
:right_margin => :match,
|
:right_margin => :match,
|
||||||
:bottom_margin => :match,
|
:bottom_margin => :match,
|
||||||
|
:subplot_index => -1,
|
||||||
)
|
)
|
||||||
|
|
||||||
const _axis_defaults = KW(
|
const _axis_defaults = KW(
|
||||||
@ -236,6 +237,7 @@ const _suppress_warnings = KW(
|
|||||||
:y_discrete_indices => nothing,
|
:y_discrete_indices => nothing,
|
||||||
:z_discrete_indices => nothing,
|
:z_discrete_indices => nothing,
|
||||||
:subplot => nothing,
|
:subplot => nothing,
|
||||||
|
:subplot_index => nothing,
|
||||||
)
|
)
|
||||||
|
|
||||||
# add defaults for the letter versions
|
# add defaults for the letter versions
|
||||||
|
|||||||
@ -211,6 +211,27 @@ end
|
|||||||
|
|
||||||
use_axis_field(ticks) = !(ticks in (nothing, :none))
|
use_axis_field(ticks) = !(ticks in (nothing, :none))
|
||||||
|
|
||||||
|
# this method gets the start/end in percentage of the canvas for this axis direction
|
||||||
|
function plotly_domain(sp::Subplot, letter)
|
||||||
|
figw, figh = sp.plt.attr[:size]
|
||||||
|
# @show letter,figw,figh sp.plotarea
|
||||||
|
pcts = bbox_to_pcts(sp.plotarea, figw*px, figh*px)
|
||||||
|
# @show pcts
|
||||||
|
i1,i2 = (letter == :x ? (1,3) : (2,4))
|
||||||
|
# @show i1,i2
|
||||||
|
[pcts[i1], pcts[i1]+pcts[i2]]
|
||||||
|
end
|
||||||
|
|
||||||
|
# TODO: this should actually take into account labels, font sizes, etc
|
||||||
|
# sets (left, top, right, bottom)
|
||||||
|
function plotly_minpad(sp::Subplot)
|
||||||
|
(12mm, 2mm, 2mm, 8mm)
|
||||||
|
end
|
||||||
|
|
||||||
|
function _update_min_padding!(sp::Subplot{PlotlyBackend})
|
||||||
|
sp.minpad = plotly_minpad(sp)
|
||||||
|
end
|
||||||
|
|
||||||
# tickssym(letter) = symbol(letter * "ticks")
|
# tickssym(letter) = symbol(letter * "ticks")
|
||||||
# limssym(letter) = symbol(letter * "lims")
|
# limssym(letter) = symbol(letter * "lims")
|
||||||
# flipsym(letter) = symbol(letter * "flip")
|
# flipsym(letter) = symbol(letter * "flip")
|
||||||
@ -230,6 +251,14 @@ function plotly_axis(axis::Axis, sp::Subplot)
|
|||||||
# fgcolor = webcolor(d[:foreground_color])
|
# fgcolor = webcolor(d[:foreground_color])
|
||||||
# tsym = tickssym(letter)
|
# tsym = tickssym(letter)
|
||||||
|
|
||||||
|
# spidx = sp.attr[:subplot_index]
|
||||||
|
# d_out[:xaxis] = "x$spidx"
|
||||||
|
# d_out[:yaxis] = "y$spidx"
|
||||||
|
if letter in (:x,:y)
|
||||||
|
ax[:domain] = plotly_domain(sp, letter)
|
||||||
|
ax[:anchor] = "$(letter==:x ? :y : :x)$(plotly_subplot_index(sp))"
|
||||||
|
end
|
||||||
|
|
||||||
rot = d[:rotation]
|
rot = d[:rotation]
|
||||||
if rot != 0
|
if rot != 0
|
||||||
ax[:tickangle] = rot
|
ax[:tickangle] = rot
|
||||||
@ -279,36 +308,41 @@ end
|
|||||||
function plotly_layout(plt::Plot)
|
function plotly_layout(plt::Plot)
|
||||||
d_out = KW()
|
d_out = KW()
|
||||||
|
|
||||||
# for now, we only support 1 subplot
|
# # for now, we only support 1 subplot
|
||||||
if length(plt.subplots) > 1
|
# if length(plt.subplots) > 1
|
||||||
warn("Subplots not supported yet")
|
# warn("Subplots not supported yet")
|
||||||
end
|
# end
|
||||||
sp = plt.subplots[1]
|
# sp = plt.subplots[1]
|
||||||
|
|
||||||
d_out[:width], d_out[:height] = plt.attr[:size]
|
d_out[:width], d_out[:height] = plt.attr[:size]
|
||||||
|
d_out[:paper_bgcolor] = webcolor(plt.attr[:background_color_outside])
|
||||||
|
|
||||||
|
for sp in plt.subplots
|
||||||
|
sp_out = KW()
|
||||||
|
spidx = plotly_subplot_index(sp)
|
||||||
|
|
||||||
# set the fields for the plot
|
# set the fields for the plot
|
||||||
d_out[:title] = sp.attr[:title]
|
d_out[:title] = sp.attr[:title]
|
||||||
d_out[:titlefont] = plotlyfont(sp.attr[:titlefont], webcolor(sp.attr[:foreground_color_title]))
|
d_out[:titlefont] = plotlyfont(sp.attr[:titlefont], webcolor(sp.attr[:foreground_color_title]))
|
||||||
|
|
||||||
# TODO: use subplot positioning logic
|
# # TODO: use subplot positioning logic
|
||||||
d_out[:margin] = KW(:l=>35, :b=>30, :r=>8, :t=>20)
|
# d_out[:margin] = KW(:l=>35, :b=>30, :r=>8, :t=>20)
|
||||||
|
d_out[:margin] = KW(:l=>0, :b=>0, :r=>0, :t=>30)
|
||||||
|
|
||||||
d_out[:plot_bgcolor] = webcolor(sp.attr[:background_color_inside])
|
d_out[:plot_bgcolor] = webcolor(sp.attr[:background_color_inside])
|
||||||
d_out[:paper_bgcolor] = webcolor(plt.attr[:background_color_outside])
|
|
||||||
|
|
||||||
# TODO: x/y axis tick values/labels
|
# TODO: x/y axis tick values/labels
|
||||||
|
|
||||||
# if any(is3d, seriesargs)
|
# if any(is3d, seriesargs)
|
||||||
if is3d(sp)
|
if is3d(sp)
|
||||||
d_out[:scene] = KW(
|
d_out[:scene] = KW(
|
||||||
:xaxis => plotly_axis(sp.attr[:xaxis], sp),
|
symbol("xaxis$spidx") => plotly_axis(sp.attr[:xaxis], sp),
|
||||||
:yaxis => plotly_axis(sp.attr[:yaxis], sp),
|
symbol("yaxis$spidx") => plotly_axis(sp.attr[:yaxis], sp),
|
||||||
:xzxis => plotly_axis(sp.attr[:zaxis], sp),
|
symbol("zaxis$spidx") => plotly_axis(sp.attr[:zaxis], sp),
|
||||||
)
|
)
|
||||||
else
|
else
|
||||||
d_out[:xaxis] = plotly_axis(sp.attr[:xaxis], sp)
|
d_out[symbol("xaxis$spidx")] = plotly_axis(sp.attr[:xaxis], sp)
|
||||||
d_out[:yaxis] = plotly_axis(sp.attr[:yaxis], sp)
|
d_out[symbol("yaxis$spidx")] = plotly_axis(sp.attr[:yaxis], sp)
|
||||||
end
|
end
|
||||||
|
|
||||||
# legend
|
# legend
|
||||||
@ -321,12 +355,6 @@ function plotly_layout(plt::Plot)
|
|||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
# if haskey(plt.attr, :annotation_list)
|
|
||||||
# append!(plt.attr[:annotation_list], anns)
|
|
||||||
# else
|
|
||||||
# plt.attr[:annotation_list] = anns
|
|
||||||
# end
|
|
||||||
|
|
||||||
# annotations
|
# annotations
|
||||||
anns = get(sp.attr, :annotations, [])
|
anns = get(sp.attr, :annotations, [])
|
||||||
d_out[:annotations] = if isempty(anns)
|
d_out[:annotations] = if isempty(anns)
|
||||||
@ -354,6 +382,9 @@ function plotly_layout(plt::Plot)
|
|||||||
d_out
|
d_out
|
||||||
end
|
end
|
||||||
|
|
||||||
|
d_out
|
||||||
|
end
|
||||||
|
|
||||||
function plotly_layout_json(plt::Plot)
|
function plotly_layout_json(plt::Plot)
|
||||||
JSON.json(plotly_layout(plt))
|
JSON.json(plotly_layout(plt))
|
||||||
end
|
end
|
||||||
@ -374,14 +405,25 @@ const _plotly_markers = KW(
|
|||||||
:hline => "line-ew",
|
:hline => "line-ew",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
function plotly_subplot_index(sp::Subplot)
|
||||||
|
spidx = sp.attr[:subplot_index]
|
||||||
|
spidx == 1 ? "" : spidx
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
# get a dictionary representing the series params (d is the Plots-dict, d_out is the Plotly-dict)
|
# get a dictionary representing the series params (d is the Plots-dict, d_out is the Plotly-dict)
|
||||||
function plotly_series(plt::Plot, series::Series)
|
function plotly_series(plt::Plot, series::Series)
|
||||||
d = series.d
|
d = series.d
|
||||||
|
sp = d[:subplot]
|
||||||
d_out = KW()
|
d_out = KW()
|
||||||
|
|
||||||
|
# these are the axes that the series should be mapped to
|
||||||
|
spidx = plotly_subplot_index(sp)
|
||||||
|
d_out[:xaxis] = "x$spidx"
|
||||||
|
d_out[:yaxis] = "y$spidx"
|
||||||
|
|
||||||
x, y = collect(d[:x]), collect(d[:y])
|
x, y = collect(d[:x]), collect(d[:y])
|
||||||
d_out[:name] = d[:label]
|
d_out[:name] = d[:label]
|
||||||
|
|
||||||
st = d[:seriestype]
|
st = d[:seriestype]
|
||||||
isscatter = st in (:scatter, :scatter3d)
|
isscatter = st in (:scatter, :scatter3d)
|
||||||
hasmarker = isscatter || d[:markershape] != :none
|
hasmarker = isscatter || d[:markershape] != :none
|
||||||
@ -450,7 +492,12 @@ function plotly_series(plt::Plot, series::Series)
|
|||||||
|
|
||||||
elseif st == :pie
|
elseif st == :pie
|
||||||
d_out[:type] = "pie"
|
d_out[:type] = "pie"
|
||||||
d_out[:labels] = x
|
d_out[:labels] = if haskey(d,:x_discrete_indices)
|
||||||
|
dvals = sp.attr[:xaxis].d[:discrete_values]
|
||||||
|
[dvals[idx] for idx in d[:x_discrete_indices]]
|
||||||
|
else
|
||||||
|
d[:x]
|
||||||
|
end
|
||||||
d_out[:values] = y
|
d_out[:values] = y
|
||||||
d_out[:hoverinfo] = "label+percent+name"
|
d_out[:hoverinfo] = "label+percent+name"
|
||||||
|
|
||||||
@ -556,14 +603,24 @@ end
|
|||||||
|
|
||||||
# ----------------------------------------------------------------
|
# ----------------------------------------------------------------
|
||||||
|
|
||||||
|
# compute layout bboxes
|
||||||
|
function plotly_finalize(plt::Plot)
|
||||||
|
w, h = plt.attr[:size]
|
||||||
|
plt.layout.bbox = BoundingBox(0mm, 0mm, w*px, h*px)
|
||||||
|
update_child_bboxes!(plt.layout)
|
||||||
|
end
|
||||||
|
|
||||||
function Base.writemime(io::IO, ::MIME"image/png", plt::AbstractPlot{PlotlyBackend})
|
function Base.writemime(io::IO, ::MIME"image/png", plt::AbstractPlot{PlotlyBackend})
|
||||||
|
plotly_finalize(plt)
|
||||||
writemime_png_from_html(io, plt)
|
writemime_png_from_html(io, plt)
|
||||||
end
|
end
|
||||||
|
|
||||||
function Base.writemime(io::IO, ::MIME"text/html", plt::AbstractPlot{PlotlyBackend})
|
function Base.writemime(io::IO, ::MIME"text/html", plt::AbstractPlot{PlotlyBackend})
|
||||||
|
plotly_finalize(plt)
|
||||||
write(io, html_head(plt) * html_body(plt))
|
write(io, html_head(plt) * html_body(plt))
|
||||||
end
|
end
|
||||||
|
|
||||||
function Base.display(::PlotsDisplay, plt::AbstractPlot{PlotlyBackend})
|
function Base.display(::PlotsDisplay, plt::AbstractPlot{PlotlyBackend})
|
||||||
|
plotly_finalize(plt)
|
||||||
standalone_html_window(plt)
|
standalone_html_window(plt)
|
||||||
end
|
end
|
||||||
|
|||||||
@ -211,7 +211,13 @@ end
|
|||||||
# writemime_png_from_html(io, plt)
|
# writemime_png_from_html(io, plt)
|
||||||
# end
|
# end
|
||||||
|
|
||||||
|
|
||||||
|
function _update_min_padding!(sp::Subplot{PlotlyBackend})
|
||||||
|
sp.minpad = plotly_minpad(sp)
|
||||||
|
end
|
||||||
|
|
||||||
function Base.display(::PlotsDisplay, plt::Plot{PlotlyJSBackend})
|
function Base.display(::PlotsDisplay, plt::Plot{PlotlyJSBackend})
|
||||||
|
plotly_finalize(plt)
|
||||||
display(plt.o)
|
display(plt.o)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@ -67,7 +67,8 @@ function crop(parent::BoundingBox, child::BoundingBox)
|
|||||||
BoundingBox(l, t, w, h)
|
BoundingBox(l, t, w, h)
|
||||||
end
|
end
|
||||||
|
|
||||||
# convert a bounding box from absolute coords to percentages... returns an array of percentages of figure size: [left, bottom, width, height]
|
# convert a bounding box from absolute coords to percentages...
|
||||||
|
# returns an array of percentages of figure size: [left, bottom, width, height]
|
||||||
function bbox_to_pcts(bb::BoundingBox, figw, figh, flipy = true)
|
function bbox_to_pcts(bb::BoundingBox, figw, figh, flipy = true)
|
||||||
mms = Float64[f(bb).value for f in (left,bottom,width,height)]
|
mms = Float64[f(bb).value for f in (left,bottom,width,height)]
|
||||||
if flipy
|
if flipy
|
||||||
|
|||||||
@ -57,6 +57,7 @@ function plot(args...; kw...)
|
|||||||
plt.layout, plt.subplots, plt.spmap = build_layout(plt.attr)
|
plt.layout, plt.subplots, plt.spmap = build_layout(plt.attr)
|
||||||
for (idx,sp) in enumerate(plt.subplots)
|
for (idx,sp) in enumerate(plt.subplots)
|
||||||
sp.plt = plt
|
sp.plt = plt
|
||||||
|
sp.attr[:subplot_index] = idx
|
||||||
_update_subplot_args(plt, sp, copy(d), idx)
|
_update_subplot_args(plt, sp, copy(d), idx)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user