Skip to content

Commit 702a8d7

Browse files
change handling of segments to fix plotly bar plots
1 parent f3bcc51 commit 702a8d7

File tree

4 files changed

+109
-116
lines changed

4 files changed

+109
-116
lines changed

src/backends/gr.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1900,6 +1900,7 @@ end
19001900
function gr_draw_shapes(series, clims)
19011901
x, y = shape_data(series)
19021902
for segment in series_segments(series, :shape)
1903+
@show segment
19031904
i, rng = segment.attr_index, segment.range
19041905
if length(rng) > 1
19051906
# connect to the beginning

src/backends/plotly.jl

Lines changed: 90 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
# https://plot.ly/javascript/getting-started
22

33
_plotly_framestyle(style::Symbol) =
4-
if style in (:box, :axes, :zerolines, :grid, :none)
5-
style
6-
else
7-
default_style = get((semi = :box, origin = :zerolines), style, :axes)
8-
@warn "Framestyle :$style is not supported by Plotly and PlotlyJS. :$default_style was chosen instead."
9-
default_style
10-
end
4+
if style in (:box, :axes, :zerolines, :grid, :none)
5+
style
6+
else
7+
default_style = get((semi = :box, origin = :zerolines), style, :axes)
8+
@warn "Framestyle :$style is not supported by Plotly and PlotlyJS. :$default_style was chosen instead."
9+
default_style
10+
end
1111

1212
# --------------------------------------------------------------------------------------
1313

@@ -289,13 +289,11 @@ function plotly_layout(plt::Plot)
289289
for ann in sp[:annotations]
290290
append!(
291291
plotattributes_out[:annotations],
292-
KW[
293-
plotly_annotation_dict(
294-
locate_annotation(sp, ann...)...;
295-
xref = "x$(x_idx)",
296-
yref = "y$(y_idx)",
297-
),
298-
],
292+
KW[plotly_annotation_dict(
293+
locate_annotation(sp, ann...)...;
294+
xref = "x$(x_idx)",
295+
yref = "y$(y_idx)",
296+
),],
299297
)
300298
end
301299
# series_annotations
@@ -335,7 +333,8 @@ function plotly_layout(plt::Plot)
335333
plotattributes_out[:hovermode] = "none"
336334
end
337335

338-
return plotattributes_out = recursive_merge(plotattributes_out, plt.attr[:extra_plot_kwargs])
336+
return plotattributes_out =
337+
recursive_merge(plotattributes_out, plt.attr[:extra_plot_kwargs])
339338
end
340339

341340
function plotly_add_legend!(plotattributes_out::KW, sp::Subplot)
@@ -404,12 +403,12 @@ end
404403
plotly_legend_pos(pos::Symbol) =
405404
get(plotly_legend_position_mapping, pos, plotly_legend_position_mapping.default)
406405

407-
plotly_legend_pos(v::Tuple{S, T}) where {S <: Real, T <: Real} =
406+
plotly_legend_pos(v::Tuple{S,T}) where {S<:Real,T<:Real} =
408407
(coords = v, xanchor = "left", yanchor = "top")
409408

410409
plotly_legend_pos(theta::Real) = plotly_legend_pos((theta, :inner))
411410

412-
function plotly_legend_pos(v::Tuple{S, Symbol}) where {S <: Real}
411+
function plotly_legend_pos(v::Tuple{S,Symbol}) where {S<:Real}
413412
(s, c) = sincosd(v[1])
414413
xanchors = ["left", "center", "right"]
415414
yanchors = ["bottom", "middle", "top"]
@@ -435,12 +434,12 @@ plotly_layout_json(plt::Plot) = JSON.json(plotly_layout(plt), 4)
435434
plotly_colorscale(cg::ColorGradient, α = nothing) =
436435
map(v -> [v, rgba_string(plot_color(cg.colors[v], α))], cg.values)
437436
plotly_colorscale(c::AbstractVector{<:Colorant}, α = nothing) =
438-
if length(c) == 1
439-
[[0.0, rgba_string(plot_color(c[1], α))], [1.0, rgba_string(plot_color(c[1], α))]]
440-
else
441-
vals = range(0.0, stop = 1.0, length = length(c))
442-
map(i -> [vals[i], rgba_string(plot_color(c[i], α))], eachindex(c))
443-
end
437+
if length(c) == 1
438+
[[0.0, rgba_string(plot_color(c[1], α))], [1.0, rgba_string(plot_color(c[1], α))]]
439+
else
440+
vals = range(0.0, stop = 1.0, length = length(c))
441+
map(i -> [vals[i], rgba_string(plot_color(c[i], α))], eachindex(c))
442+
end
444443

445444
function plotly_colorscale(cg::PlotUtils.CategoricalColorGradient, α = nothing)
446445
n = length(cg)
@@ -508,33 +507,33 @@ end
508507
plotly_data(v) = v !== nothing ? collect(v) : v
509508
plotly_data(v::AbstractArray) = v
510509
plotly_data(surf::Surface) = surf.surf
511-
plotly_data(v::AbstractArray{R}) where {R <: Rational} = float(v)
510+
plotly_data(v::AbstractArray{R}) where {R<:Rational} = float(v)
512511

513512
plotly_native_data(axis::Axis, a::Surface) = Surface(plotly_native_data(axis, a.surf))
514513
plotly_native_data(axis::Axis, data::AbstractArray) =
515-
if !isempty(axis[:discrete_values])
516-
map(
517-
xi -> axis[:discrete_values][searchsortedfirst(axis[:continuous_values], xi)],
518-
data,
519-
)
520-
elseif axis[:formatter] in (datetimeformatter, dateformatter, timeformatter)
521-
plotly_convert_to_datetime(data, axis[:formatter])
522-
else
523-
data
524-
end
514+
if !isempty(axis[:discrete_values])
515+
map(
516+
xi -> axis[:discrete_values][searchsortedfirst(axis[:continuous_values], xi)],
517+
data,
518+
)
519+
elseif axis[:formatter] in (datetimeformatter, dateformatter, timeformatter)
520+
plotly_convert_to_datetime(data, axis[:formatter])
521+
else
522+
data
523+
end
525524

526525
plotly_convert_to_datetime(x::AbstractArray, formatter::Function) =
527-
if formatter == datetimeformatter
528-
map(xi -> isfinite(xi) ? replace(formatter(xi), "T" => " ") : missing, x)
529-
elseif formatter == dateformatter
530-
map(xi -> isfinite(xi) ? replace(formatter(xi), "T" => " ") : missing, x)
531-
elseif formatter == timeformatter
532-
map(xi -> isfinite(xi) ? string(Dates.today(), " ", formatter(xi)) : missing, x)
533-
else
534-
error(
535-
"Invalid DateTime formatter. Expected Plots.datetime/date/time formatter but got $formatter",
536-
)
537-
end
526+
if formatter == datetimeformatter
527+
map(xi -> isfinite(xi) ? replace(formatter(xi), "T" => " ") : missing, x)
528+
elseif formatter == dateformatter
529+
map(xi -> isfinite(xi) ? replace(formatter(xi), "T" => " ") : missing, x)
530+
elseif formatter == timeformatter
531+
map(xi -> isfinite(xi) ? string(Dates.today(), " ", formatter(xi)) : missing, x)
532+
else
533+
error(
534+
"Invalid DateTime formatter. Expected Plots.datetime/date/time formatter but got $formatter",
535+
)
536+
end
538537

539538
#ensures that a gradient is called if a single color is supplied where a gradient is needed (e.g. if a series recipe defines marker_z)
540539
as_gradient(grad::ColorGradient, α) = grad
@@ -572,7 +571,7 @@ function plotly_series(plt::Plot, series::Series)
572571

573572
x, y, z = (
574573
plotly_data(series, letter, data) for
575-
(letter, data) in zip((:x, :y, :z), (x, y, z))
574+
(letter, data) in zip((:x, :y, :z), (x, y, z))
576575
)
577576

578577
plotattributes_out[:name] = series[:label]
@@ -667,7 +666,7 @@ function plotly_series(plt::Plot, series::Series)
667666
plotattributes_out[:x], plotattributes_out[:y], plotattributes_out[:z] = x, y, z
668667

669668
if series[:connections] !== nothing
670-
if typeof(series[:connections]) <: Tuple{Array, Array, Array}
669+
if typeof(series[:connections]) <: Tuple{Array,Array,Array}
671670
# 0-based indexing
672671
i, j, k = series[:connections]
673672
if !(length(i) == length(j) == length(k))
@@ -680,7 +679,7 @@ function plotly_series(plt::Plot, series::Series)
680679
plotattributes_out[:i] = i
681680
plotattributes_out[:j] = j
682681
plotattributes_out[:k] = k
683-
elseif typeof(series[:connections]) <: AbstractVector{NTuple{3, Int}}
682+
elseif typeof(series[:connections]) <: AbstractVector{NTuple{3,Int}}
684683
# 1-based indexing
685684
i, j, k = broadcast(
686685
i -> [inds[i] - 1 for inds in series[:connections]],
@@ -722,19 +721,19 @@ function plotly_series(plt::Plot, series::Series)
722721
:size => 2_cycle(series[:markersize], inds),
723722
:color =>
724723
rgba_string.(
725-
plot_color.(
726-
get_markercolor.(series, inds),
727-
get_markeralpha.(series, inds),
724+
plot_color.(
725+
get_markercolor.(series, inds),
726+
get_markeralpha.(series, inds),
727+
),
728728
),
729-
),
730729
:line => KW(
731730
:color =>
732731
rgba_string.(
733-
plot_color.(
734-
get_markerstrokecolor.(series, inds),
735-
get_markerstrokealpha.(series, inds),
732+
plot_color.(
733+
get_markerstrokecolor.(series, inds),
734+
get_markerstrokealpha.(series, inds),
735+
),
736736
),
737-
),
738737
:width => _cycle(series[:markerstrokewidth], inds),
739738
),
740739
)
@@ -758,44 +757,34 @@ function plotly_colorbar(sp::Subplot)
758757
end
759758

760759
function plotly_series_shapes(plt::Plot, series::Series, clims)
761-
segments = series_segments(series; check = true)
762-
plotattributes_outs = map(i -> KW(), 1:length(collect(segments)))
763-
764-
# TODO: create a plotattributes_out for each polygon
765-
# x, y = series[:x], series[:y]
766-
767-
# these are the axes that the series should be mapped to
768-
x_idx, y_idx = plotly_link_indicies(plt, series[:subplot])
769-
plotattributes_base = KW(
770-
:xaxis => "x$(x_idx)",
771-
:yaxis => "y$(y_idx)",
772-
:name => series[:label],
773-
:legendgroup => series[:label],
774-
)
775-
776-
x, y = (
777-
plotly_data(series, letter, data) for
778-
(letter, data) in zip((:x, :y), shape_data(series, 100))
779-
)
780-
781-
for (k, segment) in enumerate(segments)
782-
i, rng = segment.attr_index, segment.range
783-
length(rng) < 2 && continue
760+
@show series[:x], series[:y]
761+
plotattributes_outs = KW[]
762+
i = 1
763+
for (xs, ys) in zip(nansplit(series[:x]), nansplit(series[:y]))
764+
# these are the axes that the series should be mapped to
765+
x_idx, y_idx = plotly_link_indicies(plt, series[:subplot])
766+
plotattributes_base = KW(
767+
:xaxis => "x$(x_idx)",
768+
:yaxis => "y$(y_idx)",
769+
:name => series[:label],
770+
:legendgroup => series[:label],
771+
)
784772

785773
# to draw polygons, we actually draw lines with fill
786774
plotattributes_out = merge(
787775
plotattributes_base,
788776
KW(
789777
:type => "scatter",
790778
:mode => "lines",
791-
:x => vcat(x[rng], x[rng[1]]),
792-
:y => vcat(y[rng], y[rng[1]]),
779+
:x => xs,
780+
:y => ys,
793781
:fill => "tozeroy",
794782
:fillcolor => rgba_string(
795783
plot_color(get_fillcolor(series, clims, i), get_fillalpha(series, i)),
796784
),
797785
),
798786
)
787+
@show plotattributes_out
799788
if series[:markerstrokewidth] > 0
800789
plotattributes_out[:line] = KW(
801790
:color => rgba_string(
@@ -805,10 +794,11 @@ function plotly_series_shapes(plt::Plot, series::Series, clims)
805794
:dash => string(get_linestyle(series, i)),
806795
)
807796
end
808-
plotattributes_out[:showlegend] = k == 1 ? should_add_to_legend(series) : false
797+
plotattributes_out[:showlegend] = i == 1 ? should_add_to_legend(series) : false
809798
plotly_polar!(plotattributes_out, series)
810799
plotly_adjust_hover_label!(plotattributes_out, _cycle(series[:hover], i))
811-
plotattributes_outs[k] = merge(plotattributes_out, series[:extra_kwargs])
800+
push!(plotattributes_outs, merge(plotattributes_out, series[:extra_kwargs]))
801+
i += 1
812802
end
813803
if series[:fill_z] !== nothing
814804
push!(plotattributes_outs, plotly_colorbar_hack(series, plotattributes_base, :fill))
@@ -854,13 +844,13 @@ function plotly_series_segments(series::Series, plotattributes_base::KW, x, y, z
854844
hasline ? "lines" : "none"
855845
end
856846
if series[:fillrange] == true ||
857-
series[:fillrange] == 0 ||
858-
isa(series[:fillrange], Tuple)
847+
series[:fillrange] == 0 ||
848+
isa(series[:fillrange], Tuple)
859849
plotattributes_out[:fill] = "tozeroy"
860850
plotattributes_out[:fillcolor] = rgba_string(
861851
plot_color(get_fillcolor(series, clims, i), get_fillalpha(series, i)),
862852
)
863-
elseif typeof(series[:fillrange]) <: Union{AbstractVector{<:Real}, Real}
853+
elseif typeof(series[:fillrange]) <: Union{AbstractVector{<:Real},Real}
864854
plotattributes_out[:fill] = "tonexty"
865855
plotattributes_out[:fillcolor] = rgba_string(
866856
plot_color(get_fillcolor(series, clims, i), get_fillalpha(series, i)),
@@ -902,9 +892,9 @@ function plotly_series_segments(series::Series, plotattributes_base::KW, x, y, z
902892
)
903893
lcolor_next =
904894
plot_color(
905-
get_markerstrokecolor(series, i + 1),
906-
get_markerstrokealpha(series, i + 1),
907-
) |> rgba_string
895+
get_markerstrokecolor(series, i + 1),
896+
get_markerstrokealpha(series, i + 1),
897+
) |> rgba_string
908898

909899
plotattributes_out[:marker] = KW(
910900
:symbol => get_plotly_marker(
@@ -1022,12 +1012,12 @@ function plotly_colorbar_hack(series::Series, plotattributes_base::KW, sym::Symb
10221012
end
10231013

10241014
plotly_polar!(plotattributes_out::KW, series::Series) =
1025-
if ispolar(series[:subplot]) # convert polar plots x/y to theta/radius
1026-
theta, r = pop!(plotattributes_out, :x), pop!(plotattributes_out, :y)
1027-
plotattributes_out[:theta] = rad2deg.(theta)
1028-
plotattributes_out[:r] = r
1029-
plotattributes_out[:type] = :scatterpolar
1030-
end
1015+
if ispolar(series[:subplot]) # convert polar plots x/y to theta/radius
1016+
theta, r = pop!(plotattributes_out, :x), pop!(plotattributes_out, :y)
1017+
plotattributes_out[:theta] = rad2deg.(theta)
1018+
plotattributes_out[:r] = r
1019+
plotattributes_out[:type] = :scatterpolar
1020+
end
10311021

10321022
function plotly_adjust_hover_label!(plotattributes_out::KW, hover)
10331023
if hover === nothing
@@ -1056,11 +1046,11 @@ html_head(plt::Plot{PlotlyBackend}) = plotly_html_head(plt)
10561046
html_body(plt::Plot{PlotlyBackend}) = plotly_html_body(plt)
10571047

10581048
plotly_url() =
1059-
if _use_local_dependencies[]
1060-
_plotly_data_url()
1061-
else
1062-
"https://cdn.plot.ly/$_plotly_min_js_filename"
1063-
end
1049+
if _use_local_dependencies[]
1050+
_plotly_data_url()
1051+
else
1052+
"https://cdn.plot.ly/$_plotly_min_js_filename"
1053+
end
10641054

10651055
function plotly_html_head(plt::Plot)
10661056
plotly = plotly_url()

src/recipes.jl

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -460,6 +460,7 @@ end
460460
push!(xseg, center - hwi, center - hwi, center + hwi, center + hwi, center - hwi)
461461
push!(yseg, yi, fi, fi, yi, yi)
462462
end
463+
@show xseg, yseg
463464

464465
# widen limits out a bit
465466
expand_extrema!(axis, scale_lims(ignorenan_extrema(xseg.pts)..., default_widen_factor))
@@ -482,15 +483,15 @@ end
482483
y := yseg.pts
483484
# expand attributes to match indices in new series data
484485
for k in _segmenting_vector_attributes _segmenting_array_attributes
485-
if (v = get(plotattributes, k, nothing)) isa AVec
486-
if eachindex(v) != eachindex(y)
487-
@warn "Indices $(eachindex(v)) of attribute `$k` do not match data indices $(eachindex(y))."
488-
end
489-
# Each segment is 6 elements long, including the NaN separator.
490-
# One segment is created for each non-NaN element of `procy`.
491-
# There is no trailing NaN, so the last repetition is dropped.
492-
plotattributes[k] = @views repeat(v[valid_i]; inner = 6)[1:(end - 1)]
493-
end
486+
# if (v = get(plotattributes, k, nothing)) isa AVec
487+
# if eachindex(v) != eachindex(y)
488+
# @warn "Indices $(eachindex(v)) of attribute `$k` do not match data indices $(eachindex(y))."
489+
# end
490+
# # Each segment is 6 elements long, including the NaN separator.
491+
# # One segment is created for each non-NaN element of `procy`.
492+
# # There is no trailing NaN, so the last repetition is dropped.
493+
# plotattributes[k] = @views repeat(v[valid_i]; inner = 6)[1:(end - 1)]
494+
# end
494495
end
495496
()
496497
end

0 commit comments

Comments
 (0)