colorbar redesign
This commit is contained in:
parent
9fc1d574cd
commit
297ab3ef7e
@ -193,6 +193,7 @@ const _plotly_min_js_filename = "plotly-1.57.1.min.js"
|
|||||||
|
|
||||||
include("types.jl")
|
include("types.jl")
|
||||||
include("utils.jl")
|
include("utils.jl")
|
||||||
|
include("colorbars.jl")
|
||||||
include("axes.jl")
|
include("axes.jl")
|
||||||
include("args.jl")
|
include("args.jl")
|
||||||
include("components.jl")
|
include("components.jl")
|
||||||
|
|||||||
382
src/args.jl
382
src/args.jl
@ -151,54 +151,54 @@ const _shape_keys = Symbol[
|
|||||||
|
|
||||||
const _allMarkers = vcat(:none, :auto, _shape_keys) #sort(collect(keys(_shapes))))
|
const _allMarkers = vcat(:none, :auto, _shape_keys) #sort(collect(keys(_shapes))))
|
||||||
const _markerAliases = Dict{Symbol,Symbol}(
|
const _markerAliases = Dict{Symbol,Symbol}(
|
||||||
:n => :none,
|
:n => :none,
|
||||||
:no => :none,
|
:no => :none,
|
||||||
:a => :auto,
|
:a => :auto,
|
||||||
:ellipse => :circle,
|
:ellipse => :circle,
|
||||||
:c => :circle,
|
:c => :circle,
|
||||||
:circ => :circle,
|
:circ => :circle,
|
||||||
:square => :rect,
|
:square => :rect,
|
||||||
:sq => :rect,
|
:sq => :rect,
|
||||||
:r => :rect,
|
:r => :rect,
|
||||||
:d => :diamond,
|
:d => :diamond,
|
||||||
:^ => :utriangle,
|
:^ => :utriangle,
|
||||||
:ut => :utriangle,
|
:ut => :utriangle,
|
||||||
:utri => :utriangle,
|
:utri => :utriangle,
|
||||||
:uptri => :utriangle,
|
:uptri => :utriangle,
|
||||||
:uptriangle => :utriangle,
|
:uptriangle => :utriangle,
|
||||||
:v => :dtriangle,
|
:v => :dtriangle,
|
||||||
:V => :dtriangle,
|
:V => :dtriangle,
|
||||||
:dt => :dtriangle,
|
:dt => :dtriangle,
|
||||||
:dtri => :dtriangle,
|
:dtri => :dtriangle,
|
||||||
:downtri => :dtriangle,
|
:downtri => :dtriangle,
|
||||||
:downtriangle => :dtriangle,
|
:downtriangle => :dtriangle,
|
||||||
:> => :rtriangle,
|
:> => :rtriangle,
|
||||||
:rt => :rtriangle,
|
:rt => :rtriangle,
|
||||||
:rtri => :rtriangle,
|
:rtri => :rtriangle,
|
||||||
:righttri => :rtriangle,
|
:righttri => :rtriangle,
|
||||||
:righttriangle => :rtriangle,
|
:righttriangle => :rtriangle,
|
||||||
:< => :ltriangle,
|
:< => :ltriangle,
|
||||||
:lt => :ltriangle,
|
:lt => :ltriangle,
|
||||||
:ltri => :ltriangle,
|
:ltri => :ltriangle,
|
||||||
:lighttri => :ltriangle,
|
:lighttri => :ltriangle,
|
||||||
:lighttriangle => :ltriangle,
|
:lighttriangle => :ltriangle,
|
||||||
# :+ => :cross,
|
# :+ => :cross,
|
||||||
:plus => :cross,
|
:plus => :cross,
|
||||||
# :x => :xcross,
|
# :x => :xcross,
|
||||||
:X => :xcross,
|
:X => :xcross,
|
||||||
:star => :star5,
|
:star => :star5,
|
||||||
:s => :star5,
|
:s => :star5,
|
||||||
:star1 => :star5,
|
:star1 => :star5,
|
||||||
:s2 => :star8,
|
:s2 => :star8,
|
||||||
:star2 => :star8,
|
:star2 => :star8,
|
||||||
:p => :pentagon,
|
:p => :pentagon,
|
||||||
:pent => :pentagon,
|
:pent => :pentagon,
|
||||||
:h => :hexagon,
|
:h => :hexagon,
|
||||||
:hex => :hexagon,
|
:hex => :hexagon,
|
||||||
:hep => :heptagon,
|
:hep => :heptagon,
|
||||||
:o => :octagon,
|
:o => :octagon,
|
||||||
:oct => :octagon,
|
:oct => :octagon,
|
||||||
:spike => :vline,
|
:spike => :vline,
|
||||||
)
|
)
|
||||||
|
|
||||||
const _positionAliases = Dict{Symbol,Symbol}(
|
const _positionAliases = Dict{Symbol,Symbol}(
|
||||||
@ -273,58 +273,58 @@ const _bar_width = 0.8
|
|||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
|
|
||||||
const _series_defaults = KW(
|
const _series_defaults = KW(
|
||||||
:label => :auto,
|
:label => :auto,
|
||||||
:colorbar_entry => true,
|
:colorbar_entry => true,
|
||||||
:seriescolor => :auto,
|
:seriescolor => :auto,
|
||||||
:seriesalpha => nothing,
|
:seriesalpha => nothing,
|
||||||
:seriestype => :path,
|
:seriestype => :path,
|
||||||
:linestyle => :solid,
|
:linestyle => :solid,
|
||||||
:linewidth => :auto,
|
:linewidth => :auto,
|
||||||
:linecolor => :auto,
|
:linecolor => :auto,
|
||||||
:linealpha => nothing,
|
:linealpha => nothing,
|
||||||
:fillrange => nothing, # ribbons, areas, etc
|
:fillrange => nothing, # ribbons, areas, etc
|
||||||
:fillcolor => :match,
|
:fillcolor => :match,
|
||||||
:fillalpha => nothing,
|
:fillalpha => nothing,
|
||||||
:markershape => :none,
|
:markershape => :none,
|
||||||
:markercolor => :match,
|
:markercolor => :match,
|
||||||
:markeralpha => nothing,
|
:markeralpha => nothing,
|
||||||
:markersize => 4,
|
:markersize => 4,
|
||||||
:markerstrokestyle => :solid,
|
:markerstrokestyle => :solid,
|
||||||
:markerstrokewidth => 1,
|
:markerstrokewidth => 1,
|
||||||
:markerstrokecolor => :match,
|
:markerstrokecolor => :match,
|
||||||
:markerstrokealpha => nothing,
|
:markerstrokealpha => nothing,
|
||||||
:bins => :auto, # number of bins for hists
|
:bins => :auto, # number of bins for hists
|
||||||
:smooth => false, # regression line?
|
:smooth => false, # regression line?
|
||||||
:group => nothing, # groupby vector
|
:group => nothing, # groupby vector
|
||||||
:x => nothing,
|
:x => nothing,
|
||||||
:y => nothing,
|
:y => nothing,
|
||||||
:z => nothing, # depth for contour, surface, etc
|
:z => nothing, # depth for contour, surface, etc
|
||||||
:marker_z => nothing, # value for color scale
|
:marker_z => nothing, # value for color scale
|
||||||
:line_z => nothing,
|
:line_z => nothing,
|
||||||
:fill_z => nothing,
|
:fill_z => nothing,
|
||||||
:levels => 15,
|
:levels => 15,
|
||||||
:orientation => :vertical,
|
:orientation => :vertical,
|
||||||
:bar_position => :overlay, # for bar plots and histograms: could also be stack (stack up) or dodge (side by side)
|
:bar_position => :overlay, # for bar plots and histograms: could also be stack (stack up) or dodge (side by side)
|
||||||
:bar_width => nothing,
|
:bar_width => nothing,
|
||||||
:bar_edges => false,
|
:bar_edges => false,
|
||||||
:xerror => nothing,
|
:xerror => nothing,
|
||||||
:yerror => nothing,
|
:yerror => nothing,
|
||||||
:zerror => nothing,
|
:zerror => nothing,
|
||||||
:ribbon => nothing,
|
:ribbon => nothing,
|
||||||
:quiver => nothing,
|
:quiver => nothing,
|
||||||
:arrow => nothing, # allows for adding arrows to line/path... call `arrow(args...)`
|
:arrow => nothing, # allows for adding arrows to line/path... call `arrow(args...)`
|
||||||
:normalize => false, # do we want a normalized histogram?
|
:normalize => false, # do we want a normalized histogram?
|
||||||
:weights => nothing, # optional weights for histograms (1D and 2D)
|
:weights => nothing, # optional weights for histograms (1D and 2D)
|
||||||
:show_empty_bins => false, # should empty bins in 2D histogram be colored as zero (otherwise they are transparent)
|
:show_empty_bins => false, # should empty bins in 2D histogram be colored as zero (otherwise they are transparent)
|
||||||
:contours => false, # add contours to 3d surface and wireframe plots
|
:contours => false, # add contours to 3d surface and wireframe plots
|
||||||
:contour_labels => false,
|
:contour_labels => false,
|
||||||
:subplot => :auto, # which subplot(s) does this series belong to?
|
:subplot => :auto, # which subplot(s) does this series belong to?
|
||||||
:series_annotations => nothing, # a list of annotations which apply to the coordinates of this series
|
:series_annotations => nothing, # a list of annotations which apply to the coordinates of this series
|
||||||
:primary => true, # when true, this "counts" as a series for color selection, etc. the main use is to allow
|
:primary => true, # when true, this "counts" as a series for color selection, etc. the main use is to allow
|
||||||
# one logical series to be broken up (path and markers, for example)
|
# one logical series to be broken up (path and markers, for example)
|
||||||
:hover => nothing, # text to display when hovering over the data points
|
:hover => nothing, # text to display when hovering over the data points
|
||||||
:stride => (1,1), # array stride for wireframe/surface, the first element is the row stride and the second is the column stride.
|
:stride => (1,1), # array stride for wireframe/surface, the first element is the row stride and the second is the column stride.
|
||||||
:connections => nothing, # tuple of arrays to specifiy connectivity of a 3d mesh
|
:connections => nothing, # tuple of arrays to specifiy connectivity of a 3d mesh
|
||||||
:extra_kwargs => Dict()
|
:extra_kwargs => Dict()
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -363,104 +363,115 @@ const _plot_defaults = KW(
|
|||||||
|
|
||||||
|
|
||||||
const _subplot_defaults = KW(
|
const _subplot_defaults = KW(
|
||||||
:title => "",
|
:title => "",
|
||||||
:titlelocation => :center, # also :left or :right
|
:titlelocation => :center, # also :left or :right
|
||||||
:fontfamily_subplot => :match,
|
:fontfamily_subplot => :match,
|
||||||
:titlefontfamily => :match,
|
:titlefontfamily => :match,
|
||||||
:titlefontsize => 14,
|
:titlefontsize => 14,
|
||||||
:titlefonthalign => :hcenter,
|
:titlefonthalign => :hcenter,
|
||||||
:titlefontvalign => :vcenter,
|
:titlefontvalign => :vcenter,
|
||||||
:titlefontrotation => 0.0,
|
:titlefontrotation => 0.0,
|
||||||
:titlefontcolor => :match,
|
:titlefontcolor => :match,
|
||||||
:background_color_subplot => :match, # default for other bg colors... match takes plot default
|
:background_color_subplot => :match, # default for other bg colors... match takes plot default
|
||||||
:background_color_legend => :match, # background of legend
|
:background_color_legend => :match, # background of legend
|
||||||
:background_color_inside => :match, # background inside grid
|
:background_color_inside => :match, # background inside grid
|
||||||
:foreground_color_subplot => :match, # default for other fg colors... match takes plot default
|
:foreground_color_subplot => :match, # default for other fg colors... match takes plot default
|
||||||
:foreground_color_legend => :match, # foreground of legend
|
:foreground_color_legend => :match, # foreground of legend
|
||||||
:foreground_color_title => :match, # title color
|
:foreground_color_title => :match, # title color
|
||||||
:color_palette => :auto,
|
:color_palette => :auto,
|
||||||
:legend => :best,
|
:legend => :best,
|
||||||
:legendtitle => nothing,
|
:legendtitle => nothing,
|
||||||
:colorbar => :legend,
|
:colorbar => :legend,
|
||||||
:clims => :auto,
|
:clims => :auto,
|
||||||
:legendfontfamily => :match,
|
:colorbar_ticks => :auto,
|
||||||
:legendfontsize => 8,
|
:colorbar_tickfontfamily => :match,
|
||||||
:legendfonthalign => :hcenter,
|
:colorbar_tickfontsize => 8,
|
||||||
:legendfontvalign => :vcenter,
|
:colorbar_tickfonthalign => :hcenter,
|
||||||
:legendfontrotation => 0.0,
|
:colorbar_tickfontvalign => :vcenter,
|
||||||
:legendfontcolor => :match,
|
:colorbar_tickfontrotation => 0.0,
|
||||||
:legendtitlefontfamily => :match,
|
:colorbar_tickfontcolor => :match,
|
||||||
:legendtitlefontsize => 11,
|
:colorbar_scale => :identity,
|
||||||
:legendtitlefonthalign => :hcenter,
|
:colorbar_formatter => :auto,
|
||||||
:legendtitlefontvalign => :vcenter,
|
:colorbar_discrete_values => [],
|
||||||
:legendtitlefontrotation => 0.0,
|
:colorbar_continuous_values => zeros(0),
|
||||||
:legendtitlefontcolor => :match,
|
:legendfontfamily => :match,
|
||||||
:annotations => [], # annotation tuples... list of (x,y,annotation)
|
:legendfontsize => 8,
|
||||||
:projection => :none, # can also be :polar or :3d
|
:legendfonthalign => :hcenter,
|
||||||
:aspect_ratio => :auto, # choose from :none or :equal
|
:legendfontvalign => :vcenter,
|
||||||
:margin => 1mm,
|
:legendfontrotation => 0.0,
|
||||||
:left_margin => :match,
|
:legendfontcolor => :match,
|
||||||
:top_margin => :match,
|
:legendtitlefontfamily => :match,
|
||||||
:right_margin => :match,
|
:legendtitlefontsize => 11,
|
||||||
:bottom_margin => :match,
|
:legendtitlefonthalign => :hcenter,
|
||||||
:subplot_index => -1,
|
:legendtitlefontvalign => :vcenter,
|
||||||
:colorbar_title => "",
|
:legendtitlefontrotation => 0.0,
|
||||||
:colorbar_titlefontsize => 10,
|
:legendtitlefontcolor => :match,
|
||||||
:colorbar_title_location => :center, # also :left or :right
|
:annotations => [], # annotation tuples... list of (x,y,annotation)
|
||||||
:colorbar_fontfamily => :match,
|
:projection => :none, # can also be :polar or :3d
|
||||||
:colorbar_titlefontfamily => :match,
|
:aspect_ratio => :auto, # choose from :none or :equal
|
||||||
:colorbar_titlefonthalign => :hcenter,
|
:margin => 1mm,
|
||||||
:colorbar_titlefontvalign => :vcenter,
|
:left_margin => :match,
|
||||||
:colorbar_titlefontrotation => 0.0,
|
:top_margin => :match,
|
||||||
:colorbar_titlefontcolor => :match,
|
:right_margin => :match,
|
||||||
:framestyle => :axes,
|
:bottom_margin => :match,
|
||||||
:camera => (30,30),
|
:subplot_index => -1,
|
||||||
:extra_kwargs => Dict()
|
:colorbar_title => "",
|
||||||
|
:colorbar_titlefontsize => 10,
|
||||||
|
:colorbar_title_location => :center, # also :left or :right
|
||||||
|
:colorbar_fontfamily => :match,
|
||||||
|
:colorbar_titlefontfamily => :match,
|
||||||
|
:colorbar_titlefonthalign => :hcenter,
|
||||||
|
:colorbar_titlefontvalign => :vcenter,
|
||||||
|
:colorbar_titlefontrotation => 0.0,
|
||||||
|
:colorbar_titlefontcolor => :match,
|
||||||
|
:framestyle => :axes,
|
||||||
|
:camera => (30,30),
|
||||||
|
:extra_kwargs => Dict()
|
||||||
)
|
)
|
||||||
|
|
||||||
const _axis_defaults = KW(
|
const _axis_defaults = KW(
|
||||||
:guide => "",
|
:guide => "",
|
||||||
:guide_position => :auto,
|
:guide_position => :auto,
|
||||||
:lims => :auto,
|
:lims => :auto,
|
||||||
:ticks => :auto,
|
:ticks => :auto,
|
||||||
:scale => :identity,
|
:scale => :identity,
|
||||||
:rotation => 0,
|
:rotation => 0,
|
||||||
:flip => false,
|
:flip => false,
|
||||||
:link => [],
|
:link => [],
|
||||||
:tickfontfamily => :match,
|
:tickfontfamily => :match,
|
||||||
:tickfontsize => 8,
|
:tickfontsize => 8,
|
||||||
:tickfonthalign => :hcenter,
|
:tickfonthalign => :hcenter,
|
||||||
:tickfontvalign => :vcenter,
|
:tickfontvalign => :vcenter,
|
||||||
:tickfontrotation => 0.0,
|
:tickfontrotation => 0.0,
|
||||||
:tickfontcolor => :match,
|
:tickfontcolor => :match,
|
||||||
:guidefontfamily => :match,
|
:guidefontfamily => :match,
|
||||||
:guidefontsize => 11,
|
:guidefontsize => 11,
|
||||||
:guidefonthalign => :hcenter,
|
:guidefonthalign => :hcenter,
|
||||||
:guidefontvalign => :vcenter,
|
:guidefontvalign => :vcenter,
|
||||||
:guidefontrotation => 0.0,
|
:guidefontrotation => 0.0,
|
||||||
:guidefontcolor => :match,
|
:guidefontcolor => :match,
|
||||||
:foreground_color_axis => :match, # axis border/tick colors,
|
:foreground_color_axis => :match, # axis border/tick colors,
|
||||||
:foreground_color_border => :match, # plot area border/spines,
|
:foreground_color_border => :match, # plot area border/spines,
|
||||||
:foreground_color_text => :match, # tick text color,
|
:foreground_color_text => :match, # tick text color,
|
||||||
:foreground_color_guide => :match, # guide text color,
|
:foreground_color_guide => :match, # guide text color,
|
||||||
:discrete_values => [],
|
:discrete_values => [],
|
||||||
:formatter => :auto,
|
:formatter => :auto,
|
||||||
:mirror => false,
|
:mirror => false,
|
||||||
:grid => true,
|
:grid => true,
|
||||||
:foreground_color_grid => :match, # grid color
|
:foreground_color_grid => :match, # grid color
|
||||||
:gridalpha => 0.1,
|
:gridalpha => 0.1,
|
||||||
:gridstyle => :solid,
|
:gridstyle => :solid,
|
||||||
:gridlinewidth => 0.5,
|
:gridlinewidth => 0.5,
|
||||||
:foreground_color_minor_grid => :match, # grid color
|
:foreground_color_minor_grid => :match, # grid color
|
||||||
:minorgridalpha => 0.05,
|
:minorgridalpha => 0.05,
|
||||||
:minorgridstyle => :solid,
|
:minorgridstyle => :solid,
|
||||||
:minorgridlinewidth => 0.5,
|
:minorgridlinewidth => 0.5,
|
||||||
:tick_direction => :in,
|
:tick_direction => :in,
|
||||||
:minorticks => false,
|
:minorticks => false,
|
||||||
:minorgrid => false,
|
:minorgrid => false,
|
||||||
:showaxis => true,
|
:showaxis => true,
|
||||||
:widen => true,
|
:widen => true,
|
||||||
:draw_arrow => false,
|
:draw_arrow => false,
|
||||||
)
|
)
|
||||||
|
|
||||||
const _suppress_warnings = Set{Symbol}([
|
const _suppress_warnings = Set{Symbol}([
|
||||||
@ -1580,6 +1591,7 @@ function _update_subplot_args(plt::Plot, sp::Subplot, plotattributes_in, subplot
|
|||||||
lims_warned = true
|
lims_warned = true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
_update_subplot_colorbars(sp)
|
||||||
end
|
end
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
|
|||||||
@ -531,6 +531,10 @@ const _pyplot_attr = merge_with_base_supported([
|
|||||||
:guidefontfamily, :guidefontsize, :guidefontcolor,
|
:guidefontfamily, :guidefontsize, :guidefontcolor,
|
||||||
:grid, :gridalpha, :gridstyle, :gridlinewidth,
|
:grid, :gridalpha, :gridstyle, :gridlinewidth,
|
||||||
:legend, :legendtitle, :colorbar, :colorbar_title, :colorbar_entry,
|
:legend, :legendtitle, :colorbar, :colorbar_title, :colorbar_entry,
|
||||||
|
:colorbar_ticks, :colorbar_tickfontfamily, :colorbar_tickfontsize,
|
||||||
|
:colorbar_tickfonthalign, :colorbar_tickfontvalign,
|
||||||
|
:colorbar_tickfontrotation, :colorbar_tickfontcolor,
|
||||||
|
:colorbar_scale,
|
||||||
:marker_z, :line_z, :fill_z,
|
:marker_z, :line_z, :fill_z,
|
||||||
:levels,
|
:levels,
|
||||||
:ribbon, :quiver, :arrow,
|
:ribbon, :quiver, :arrow,
|
||||||
|
|||||||
209
src/colorbars.jl
Normal file
209
src/colorbars.jl
Normal file
@ -0,0 +1,209 @@
|
|||||||
|
# These functions return an operator for use in `get_clims(::Seres, op)`
|
||||||
|
process_clims(lims::Tuple{<:Number,<:Number}) = (zlims -> ifelse.(isfinite.(lims), lims, zlims)) ∘ ignorenan_extrema
|
||||||
|
process_clims(s::Union{Symbol,Nothing,Missing}) = ignorenan_extrema
|
||||||
|
# don't specialize on ::Function otherwise python functions won't work
|
||||||
|
process_clims(f) = f
|
||||||
|
|
||||||
|
function get_clims(sp::Subplot, op=process_clims(sp[:clims]))
|
||||||
|
zmin, zmax = Inf, -Inf
|
||||||
|
for series in series_list(sp)
|
||||||
|
if series[:colorbar_entry]
|
||||||
|
zmin, zmax = _update_clims(zmin, zmax, get_clims(series, op)...)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return zmin <= zmax ? (zmin, zmax) : (NaN, NaN)
|
||||||
|
end
|
||||||
|
|
||||||
|
function get_clims(sp::Subplot, series::Series, op=process_clims(sp[:clims]))
|
||||||
|
zmin, zmax = if series[:colorbar_entry]
|
||||||
|
get_clims(sp, op)
|
||||||
|
else
|
||||||
|
get_clims(series, op)
|
||||||
|
end
|
||||||
|
return zmin <= zmax ? (zmin, zmax) : (NaN, NaN)
|
||||||
|
end
|
||||||
|
|
||||||
|
"""
|
||||||
|
get_clims(::Series, op=Plots.ignorenan_extrema)
|
||||||
|
|
||||||
|
Finds the limits for the colorbar by taking the "z-values" for the series and passing them into `op`,
|
||||||
|
which must return the tuple `(zmin, zmax)`. The default op is the extrema of the finite
|
||||||
|
values of the input.
|
||||||
|
"""
|
||||||
|
function get_clims(series::Series, op=ignorenan_extrema)
|
||||||
|
zmin, zmax = Inf, -Inf
|
||||||
|
z_colored_series = (:contour, :contour3d, :heatmap, :histogram2d, :surface, :hexbin)
|
||||||
|
for vals in (series[:seriestype] in z_colored_series ? series[:z] : nothing, series[:line_z], series[:marker_z], series[:fill_z])
|
||||||
|
if (typeof(vals) <: AbstractSurface) && (eltype(vals.surf) <: Union{Missing, Real})
|
||||||
|
zmin, zmax = _update_clims(zmin, zmax, op(vals.surf)...)
|
||||||
|
elseif (vals !== nothing) && (eltype(vals) <: Union{Missing, Real})
|
||||||
|
zmin, zmax = _update_clims(zmin, zmax, op(vals)...)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return zmin <= zmax ? (zmin, zmax) : (NaN, NaN)
|
||||||
|
end
|
||||||
|
|
||||||
|
_update_clims(zmin, zmax, emin, emax) = NaNMath.min(zmin, emin), NaNMath.max(zmax, emax)
|
||||||
|
|
||||||
|
@enum ColorbarStyle cbar_gradient cbar_fill cbar_lines
|
||||||
|
|
||||||
|
function colorbar_style(series::Series)
|
||||||
|
colorbar_entry = series[:colorbar_entry]
|
||||||
|
if !(colorbar_entry isa Bool)
|
||||||
|
@warn "Non-boolean colorbar_entry ignored."
|
||||||
|
colorbar_entry = true
|
||||||
|
end
|
||||||
|
|
||||||
|
if !colorbar_entry
|
||||||
|
nothing
|
||||||
|
elseif isfilledcontour(series)
|
||||||
|
cbar_fill
|
||||||
|
elseif iscontour(series)
|
||||||
|
cbar_lines
|
||||||
|
elseif series[:seriestype] ∈ (:heatmap,:surface) ||
|
||||||
|
any(series[z] !== nothing for z ∈ [:marker_z,:line_z,:fill_z])
|
||||||
|
cbar_gradient
|
||||||
|
else
|
||||||
|
nothing
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
hascolorbar(series::Series) = colorbar_style(series) !== nothing
|
||||||
|
hascolorbar(sp::Subplot) = sp[:colorbar] != :none && any(hascolorbar(s) for s in series_list(sp))
|
||||||
|
|
||||||
|
function optimal_colorbar_ticks_and_labels(sp::Subplot, ticks = nothing)
|
||||||
|
amin, amax = get_clims(sp)
|
||||||
|
|
||||||
|
# scale the limits
|
||||||
|
scale = sp[:colorbar_scale]
|
||||||
|
sf = RecipesPipeline.scale_func(scale)
|
||||||
|
|
||||||
|
# Taken from optimal_ticks_and_labels, but needs a different method as there can only be 1 colorbar per subplot
|
||||||
|
#
|
||||||
|
# If the axis input was a Date or DateTime use a special logic to find
|
||||||
|
# "round" Date(Time)s as ticks
|
||||||
|
# This bypasses the rest of optimal_ticks_and_labels, because
|
||||||
|
# optimize_datetime_ticks returns ticks AND labels: the label format (Date
|
||||||
|
# or DateTime) is chosen based on the time span between amin and amax
|
||||||
|
# rather than on the input format
|
||||||
|
# TODO: maybe: non-trivial scale (:ln, :log2, :log10) for date/datetime
|
||||||
|
if ticks === nothing && scale == :identity
|
||||||
|
if sp[:colorbar_formatter] == RecipesPipeline.dateformatter
|
||||||
|
# optimize_datetime_ticks returns ticks and labels(!) based on
|
||||||
|
# integers/floats corresponding to the DateTime type. Thus, the axes
|
||||||
|
# limits, which resulted from converting the Date type to integers,
|
||||||
|
# are converted to 'DateTime integers' (actually floats) before
|
||||||
|
# being passed to optimize_datetime_ticks.
|
||||||
|
# (convert(Int, convert(DateTime, convert(Date, i))) == 87600000*i)
|
||||||
|
ticks, labels = optimize_datetime_ticks(864e5 * amin, 864e5 * amax;
|
||||||
|
k_min = 2, k_max = 4)
|
||||||
|
# Now the ticks are converted back to floats corresponding to Dates.
|
||||||
|
return ticks / 864e5, labels
|
||||||
|
elseif sp[:colorbar_formatter] == RecipesPipeline.datetimeformatter
|
||||||
|
return optimize_datetime_ticks(amin, amax; k_min = 2, k_max = 4)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# get a list of well-laid-out ticks
|
||||||
|
if ticks === nothing
|
||||||
|
scaled_ticks = optimize_ticks(
|
||||||
|
sf(amin),
|
||||||
|
sf(amax);
|
||||||
|
k_min = 4, # minimum number of ticks
|
||||||
|
k_max = 8, # maximum number of ticks
|
||||||
|
)[1]
|
||||||
|
elseif typeof(ticks) <: Int
|
||||||
|
scaled_ticks, viewmin, viewmax = optimize_ticks(
|
||||||
|
sf(amin),
|
||||||
|
sf(amax);
|
||||||
|
k_min = ticks, # minimum number of ticks
|
||||||
|
k_max = ticks, # maximum number of ticks
|
||||||
|
k_ideal = ticks,
|
||||||
|
# `strict_span = false` rewards cases where the span of the
|
||||||
|
# chosen ticks is not too much bigger than amin - amax:
|
||||||
|
strict_span = false,
|
||||||
|
)
|
||||||
|
sp[:clims] = map(RecipesPipeline.inverse_scale_func(scale), (viewmin, viewmax))
|
||||||
|
else
|
||||||
|
scaled_ticks = map(sf, (filter(t -> amin <= t <= amax, ticks)))
|
||||||
|
end
|
||||||
|
unscaled_ticks = map(RecipesPipeline.inverse_scale_func(scale), scaled_ticks)
|
||||||
|
|
||||||
|
labels = if any(isfinite, unscaled_ticks)
|
||||||
|
formatter = ap[:colorbar_formatter]
|
||||||
|
if formatter in (:auto, :plain, :scientific, :engineering)
|
||||||
|
map(labelfunc(scale, backend()), Showoff.showoff(scaled_ticks, formatter))
|
||||||
|
elseif formatter == :latex
|
||||||
|
map(x -> string("\$", replace(convert_sci_unicode(x), '×' => "\\times"), "\$"), Showoff.showoff(unscaled_ticks, :auto))
|
||||||
|
else
|
||||||
|
# there was an override for the formatter... use that on the unscaled ticks
|
||||||
|
map(formatter, unscaled_ticks)
|
||||||
|
# if the formatter left us with numbers, still apply the default formatter
|
||||||
|
# However it leave us with the problem of unicode number decoding by the backend
|
||||||
|
# if eltype(unscaled_ticks) <: Number
|
||||||
|
# Showoff.showoff(unscaled_ticks, :auto)
|
||||||
|
# end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
# no finite ticks to show...
|
||||||
|
String[]
|
||||||
|
end
|
||||||
|
|
||||||
|
# @show unscaled_ticks labels
|
||||||
|
# labels = Showoff.showoff(unscaled_ticks, scale == :log10 ? :scientific : :auto)
|
||||||
|
unscaled_ticks, labels
|
||||||
|
end
|
||||||
|
|
||||||
|
# return (continuous_values, discrete_values) for the ticks on this axis
|
||||||
|
function get_colorbar_ticks(sp::Subplot; update = true)
|
||||||
|
if update || !haskey(sp.attr, :colorbar_optimized_ticks)
|
||||||
|
ticks = _transform_ticks(sp[:colorbar_ticks])
|
||||||
|
if ticks in (:none, nothing, false)
|
||||||
|
sp.attr[:colorbar_optimized_ticks] = nothing
|
||||||
|
else
|
||||||
|
# treat :native ticks as :auto
|
||||||
|
ticks = ticks == :native ? :auto : ticks
|
||||||
|
|
||||||
|
dvals = sp[:colorbar_discrete_values]
|
||||||
|
cv, dv = if typeof(ticks) <: Symbol
|
||||||
|
if !isempty(dvals)
|
||||||
|
# discrete ticks...
|
||||||
|
n = length(dvals)
|
||||||
|
rng = if ticks == :auto && n > 15
|
||||||
|
Δ = ceil(Int, n / 10)
|
||||||
|
Δ:Δ:n
|
||||||
|
else # if ticks == :all
|
||||||
|
1:n
|
||||||
|
end
|
||||||
|
sp[:colorbar_continuous_values][rng], dvals[rng]
|
||||||
|
else
|
||||||
|
# compute optimal ticks and labels
|
||||||
|
optimal_colorbar_ticks_and_labels(sp)
|
||||||
|
end
|
||||||
|
elseif typeof(ticks) <: Union{AVec, Int}
|
||||||
|
if !isempty(dvals) && typeof(ticks) <: Int
|
||||||
|
rng = Int[round(Int,i) for i in range(1, stop=length(dvals), length=ticks)]
|
||||||
|
sp[:colorbar_continuous_values][rng], dvals[rng]
|
||||||
|
else
|
||||||
|
# override ticks, but get the labels
|
||||||
|
optimal_colorbar_ticks_and_labels(sp, ticks)
|
||||||
|
end
|
||||||
|
elseif typeof(ticks) <: NTuple{2, Any}
|
||||||
|
# assuming we're passed (ticks, labels)
|
||||||
|
ticks
|
||||||
|
else
|
||||||
|
error("Unknown ticks type in get_ticks: $(typeof(ticks))")
|
||||||
|
end
|
||||||
|
sp.attr[:colorbar_optimized_ticks] = (cv, dv)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
sp.attr[:colorbar_optimized_ticks]
|
||||||
|
end
|
||||||
|
|
||||||
|
_transform_ticks(ticks) = ticks
|
||||||
|
_transform_ticks(ticks::AbstractArray{T}) where T <: Dates.TimeType = Dates.value.(ticks)
|
||||||
|
_transform_ticks(ticks::NTuple{2, Any}) = (_transform_ticks(ticks[1]), ticks[2])
|
||||||
|
|
||||||
|
function _update_subplot_colorbars(sp::Subplot)
|
||||||
|
|
||||||
|
end
|
||||||
73
src/utils.jl
73
src/utils.jl
@ -428,79 +428,6 @@ xlims(sp_idx::Int = 1) = xlims(current(), sp_idx)
|
|||||||
ylims(sp_idx::Int = 1) = ylims(current(), sp_idx)
|
ylims(sp_idx::Int = 1) = ylims(current(), sp_idx)
|
||||||
zlims(sp_idx::Int = 1) = zlims(current(), sp_idx)
|
zlims(sp_idx::Int = 1) = zlims(current(), sp_idx)
|
||||||
|
|
||||||
# These functions return an operator for use in `get_clims(::Seres, op)`
|
|
||||||
process_clims(lims::Tuple{<:Number,<:Number}) = (zlims -> ifelse.(isfinite.(lims), lims, zlims)) ∘ ignorenan_extrema
|
|
||||||
process_clims(s::Union{Symbol,Nothing,Missing}) = ignorenan_extrema
|
|
||||||
# don't specialize on ::Function otherwise python functions won't work
|
|
||||||
process_clims(f) = f
|
|
||||||
|
|
||||||
function get_clims(sp::Subplot, op=process_clims(sp[:clims]))
|
|
||||||
zmin, zmax = Inf, -Inf
|
|
||||||
for series in series_list(sp)
|
|
||||||
if series[:colorbar_entry]
|
|
||||||
zmin, zmax = _update_clims(zmin, zmax, get_clims(series, op)...)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return zmin <= zmax ? (zmin, zmax) : (NaN, NaN)
|
|
||||||
end
|
|
||||||
|
|
||||||
function get_clims(sp::Subplot, series::Series, op=process_clims(sp[:clims]))
|
|
||||||
zmin, zmax = if series[:colorbar_entry]
|
|
||||||
get_clims(sp, op)
|
|
||||||
else
|
|
||||||
get_clims(series, op)
|
|
||||||
end
|
|
||||||
return zmin <= zmax ? (zmin, zmax) : (NaN, NaN)
|
|
||||||
end
|
|
||||||
|
|
||||||
"""
|
|
||||||
get_clims(::Series, op=Plots.ignorenan_extrema)
|
|
||||||
|
|
||||||
Finds the limits for the colorbar by taking the "z-values" for the series and passing them into `op`,
|
|
||||||
which must return the tuple `(zmin, zmax)`. The default op is the extrema of the finite
|
|
||||||
values of the input.
|
|
||||||
"""
|
|
||||||
function get_clims(series::Series, op=ignorenan_extrema)
|
|
||||||
zmin, zmax = Inf, -Inf
|
|
||||||
z_colored_series = (:contour, :contour3d, :heatmap, :histogram2d, :surface, :hexbin)
|
|
||||||
for vals in (series[:seriestype] in z_colored_series ? series[:z] : nothing, series[:line_z], series[:marker_z], series[:fill_z])
|
|
||||||
if (typeof(vals) <: AbstractSurface) && (eltype(vals.surf) <: Union{Missing, Real})
|
|
||||||
zmin, zmax = _update_clims(zmin, zmax, op(vals.surf)...)
|
|
||||||
elseif (vals !== nothing) && (eltype(vals) <: Union{Missing, Real})
|
|
||||||
zmin, zmax = _update_clims(zmin, zmax, op(vals)...)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return zmin <= zmax ? (zmin, zmax) : (NaN, NaN)
|
|
||||||
end
|
|
||||||
|
|
||||||
_update_clims(zmin, zmax, emin, emax) = NaNMath.min(zmin, emin), NaNMath.max(zmax, emax)
|
|
||||||
|
|
||||||
@enum ColorbarStyle cbar_gradient cbar_fill cbar_lines
|
|
||||||
|
|
||||||
function colorbar_style(series::Series)
|
|
||||||
colorbar_entry = series[:colorbar_entry]
|
|
||||||
if !(colorbar_entry isa Bool)
|
|
||||||
@warn "Non-boolean colorbar_entry ignored."
|
|
||||||
colorbar_entry = true
|
|
||||||
end
|
|
||||||
|
|
||||||
if !colorbar_entry
|
|
||||||
nothing
|
|
||||||
elseif isfilledcontour(series)
|
|
||||||
cbar_fill
|
|
||||||
elseif iscontour(series)
|
|
||||||
cbar_lines
|
|
||||||
elseif series[:seriestype] ∈ (:heatmap,:surface) ||
|
|
||||||
any(series[z] !== nothing for z ∈ [:marker_z,:line_z,:fill_z])
|
|
||||||
cbar_gradient
|
|
||||||
else
|
|
||||||
nothing
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
hascolorbar(series::Series) = colorbar_style(series) !== nothing
|
|
||||||
hascolorbar(sp::Subplot) = sp[:colorbar] != :none && any(hascolorbar(s) for s in series_list(sp))
|
|
||||||
|
|
||||||
iscontour(series::Series) = series[:seriestype] in (:contour, :contour3d)
|
iscontour(series::Series) = series[:seriestype] in (:contour, :contour3d)
|
||||||
isfilledcontour(series::Series) = iscontour(series) && series[:fillrange] !== nothing
|
isfilledcontour(series::Series) = iscontour(series) && series[:fillrange] !== nothing
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user