diff --git a/src/args.jl b/src/args.jl index e2586610..808822b3 100644 --- a/src/args.jl +++ b/src/args.jl @@ -159,6 +159,21 @@ const _markerAliases = Dict{Symbol,Symbol}( :spike => :vline, ) +const _positionAliases = Dict{Symbol,Symbol}( + :top_left => :topleft, + :tl => :topleft, + :top_center => :topcenter, + :tc => :topcenter, + :top_right => :topright, + :tr => :topright, + :bottom_left => :bottomleft, + :bl => :bottomleft, + :bottom_center => :bottomcenter, + :bc => :bottomcenter, + :bottom_right => :bottomright, + :br => :bottomright, +) + const _allScales = [:identity, :ln, :log2, :log10, :asinh, :sqrt] const _logScales = [:ln, :log2, :log10] const _logScaleBases = Dict(:ln => e, :log2 => 2.0, :log10 => 10.0) @@ -1294,11 +1309,9 @@ end function _update_subplot_periphery(sp::Subplot, anns::AVec) # extend annotations, and ensure we always have a (x,y,PlotText) tuple - newanns = vcat(anns, sp[:annotations]) - for (i,ann) in enumerate(newanns) - x,y,tmp = ann - ptxt = isa(tmp, PlotText) ? tmp : text(tmp) - newanns[i] = (x,y,ptxt) + newanns = [] + for ann in vcat(anns, sp[:annotations]) + append!(newanns, process_annotation(sp, ann...)) end sp.attr[:annotations] = newanns diff --git a/src/backends/gr.jl b/src/backends/gr.jl index d10420eb..1ef54d63 100644 --- a/src/backends/gr.jl +++ b/src/backends/gr.jl @@ -1330,7 +1330,7 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas) end end for ann in sp[:annotations] - x, y, val = ann + x, y, val = locate_annotation(sp, ann...) x, y = if is3d(sp) # GR.wc3towc(x, y, z) else diff --git a/src/backends/pgfplots.jl b/src/backends/pgfplots.jl index db4e8b26..1956f1b3 100644 --- a/src/backends/pgfplots.jl +++ b/src/backends/pgfplots.jl @@ -465,7 +465,7 @@ function _update_plot_object(plt::Plot{PGFPlotsBackend}) # add the annotations for ann in sp[:annotations] - pgf_add_annotation!(o,ann...) + pgf_add_annotation!(o, locate_annotation(sp, ann...)...) end diff --git a/src/backends/plotly.jl b/src/backends/plotly.jl index 5b4dcd8d..92a25809 100644 --- a/src/backends/plotly.jl +++ b/src/backends/plotly.jl @@ -385,8 +385,9 @@ function plotly_layout(plt::Plot) end # annotations - append!(d_out[:annotations], KW[plotly_annotation_dict(ann...; xref = "x$spidx", yref = "y$spidx") for ann in sp[:annotations]]) - + for ann in sp[:annotations] + append!(d_out[:annotations], KW[plotly_annotation_dict(locate_annotation(sp, ann...)...; xref = "x$spidx", yref = "y$spidx")]) + end # series_annotations for series in series_list(sp) anns = series[:series_annotations] diff --git a/src/backends/pyplot.jl b/src/backends/pyplot.jl index d2566d44..8630c931 100644 --- a/src/backends/pyplot.jl +++ b/src/backends/pyplot.jl @@ -969,7 +969,7 @@ function _before_layout_calcs(plt::Plot{PyPlotBackend}) # add the annotations for ann in sp[:annotations] - py_add_annotations(sp, ann...) + py_add_annotations(sp, locate_annotation(sp, ann...)...) end # title diff --git a/src/components.jl b/src/components.jl index 7f452b97..2b1284ce 100644 --- a/src/components.jl +++ b/src/components.jl @@ -348,6 +348,7 @@ PlotText(str) = PlotText(string(str), font()) Create a PlotText object wrapping a string with font info, for plot annotations """ text(t::PlotText) = t +text(t::PlotText, font::Font) = PlotText(t.str, font) text(str::AbstractString, f::Font) = PlotText(str, f) function text(str, args...) PlotText(string(str), font(args...)) @@ -535,6 +536,54 @@ annotations(anns::AVec) = anns annotations(anns) = Any[anns] annotations(sa::SeriesAnnotations) = sa +# Expand arrays of coordinates, positions and labels into induvidual annotations +# and make sure labels are of type PlotText +function process_annotation(sp::Subplot, xs, ys, labs, font = font()) + anns = [] + labs = makevec(labs) + for i in 1:max(length(xs), length(ys), length(labs)) + x, y, lab = _cycle(xs, i), _cycle(ys, i), _cycle(labs, i) + if lab == :auto + alphabet = "abcdefghijklmnopqrstuvwxyz" + push!(anns, (x, y, text(string("(", alphabet[sp[:subplot_index]], ")"), font))) + else + push!(anns, (x, y, isa(lab, PlotText) ? lab : text(lab, font))) + end + end + anns +end +function process_annotation(sp::Subplot, positions::Union{AVec{Symbol},Symbol}, labs, font = font()) + anns = [] + positions, labs = makevec(positions), makevec(labs) + for i in 1:max(length(positions), length(labs)) + pos, lab = _cycle(positions, i), _cycle(labs, i) + pos = get(_positionAliases, pos, pos) + if lab == :auto + alphabet = "abcdefghijklmnopqrstuvwxyz" + push!(anns, (pos, text(string("(", alphabet[sp[:subplot_index]], ")"), font))) + else + push!(anns, (pos, isa(lab, PlotText) ? lab : text(lab, font))) + end + end + anns +end + +# Give each annotation coordinates based on specified position +function locate_annotation(sp::Subplot, pos::Symbol, lab::PlotText) + position_multiplier = Dict{Symbol, Tuple{Float64,Float64}}( + :topleft => (0.1, 0.9), + :topcenter => (0.5, 0.9), + :topright => (0.9, 0.9), + :bottomleft => (0.1, 0.1), + :bottomcenter => (0.5, 0.1), + :bottomright => (0.9, 0.1), + ) + xmin, xmax = ignorenan_extrema(sp[:xaxis]) + ymin, ymax = ignorenan_extrema(sp[:yaxis]) + x, y = (xmin, ymin).+ position_multiplier[pos].* (xmax - xmin, ymax - ymin) + (x, y, lab) +end +locate_annotation(sp::Subplot, x, y, label::PlotText) = (x, y, label) # ----------------------------------------------------------------------- "type which represents z-values for colors and sizes (and anything else that might come up)"