Consistent interpretation of contour levels across PyPlot,GR,Plotly[JS] backends.
GR: fixed colorbar rendering/alignment for contour[f]. Merged colorbar for mutiple series. :colorbar_entry property to include series in colorbar.
This commit is contained in:
parent
4849772435
commit
dd02a69e8f
@ -232,6 +232,7 @@ const _bar_width = 0.8
|
||||
|
||||
const _series_defaults = KW(
|
||||
:label => "AUTO",
|
||||
:colorbar_entry => true,
|
||||
:seriescolor => :auto,
|
||||
:seriesalpha => nothing,
|
||||
:seriestype => :path,
|
||||
|
||||
@ -332,7 +332,7 @@ const _gr_attr = merge_with_base_supported([
|
||||
:guidefontfamily, :guidefontsize, :guidefonthalign, :guidefontvalign,
|
||||
:guidefontrotation, :guidefontcolor,
|
||||
:grid, :gridalpha, :gridstyle, :gridlinewidth,
|
||||
:legend, :legendtitle, :colorbar, :colorbar_title,
|
||||
:legend, :legendtitle, :colorbar, :colorbar_title, :colorbar_entry,
|
||||
:fill_z, :line_z, :marker_z, :levels,
|
||||
:ribbon, :quiver,
|
||||
:orientation,
|
||||
@ -386,7 +386,7 @@ const _plotly_attr = merge_with_base_supported([
|
||||
:guide, :lims, :ticks, :scale, :flip, :rotation,
|
||||
:tickfont, :guidefont, :legendfont,
|
||||
:grid, :gridalpha, :gridlinewidth,
|
||||
:legend, :colorbar, :colorbar_title,
|
||||
:legend, :colorbar, :colorbar_title, :colorbar_entry,
|
||||
:marker_z, :fill_z, :line_z, :levels,
|
||||
:ribbon, :quiver,
|
||||
:orientation,
|
||||
@ -511,7 +511,7 @@ const _pyplot_attr = merge_with_base_supported([
|
||||
:tickfontfamily, :tickfontsize, :tickfontcolor,
|
||||
:guidefontfamily, :guidefontsize, :guidefontcolor,
|
||||
:grid, :gridalpha, :gridstyle, :gridlinewidth,
|
||||
:legend, :legendtitle, :colorbar,
|
||||
:legend, :legendtitle, :colorbar, :colorbar_title, :colorbar_entry,
|
||||
:marker_z, :line_z, :fill_z,
|
||||
:levels,
|
||||
:ribbon, :quiver, :arrow,
|
||||
@ -524,7 +524,6 @@ const _pyplot_attr = merge_with_base_supported([
|
||||
:clims,
|
||||
:inset_subplots,
|
||||
:dpi,
|
||||
:colorbar_title,
|
||||
:stride,
|
||||
:framestyle,
|
||||
:tick_direction,
|
||||
|
||||
@ -415,24 +415,92 @@ function gr_set_viewport_polar()
|
||||
r
|
||||
end
|
||||
|
||||
mutable struct GRColorbar
|
||||
heatmapseries::Union{Series,Nothing}
|
||||
fillseries::Union{Series,Nothing}
|
||||
linesseries::Union{Series,Nothing}
|
||||
GRColorbar() = new(nothing,nothing,nothing)
|
||||
end
|
||||
|
||||
function gr_update_colorbar!(cbar::GRColorbar, series::Series)
|
||||
if iscontour(series)
|
||||
if isfilledcontour(series)
|
||||
cbar.fillseries = series
|
||||
else
|
||||
cbar.linesseries = series
|
||||
end
|
||||
elseif series[:seriestype] == :heatmap
|
||||
cbar.heatmapseries = series
|
||||
end
|
||||
end
|
||||
|
||||
function gr_contour_levels(series::Series, clims)
|
||||
levels = contour_levels(series, clims)
|
||||
if isfilledcontour(series)
|
||||
# GR implicitly uses the maximal z value as the highest level
|
||||
if levels[end] < clims[2]
|
||||
@warn("GR: highest contour level less than maximal z value is not supported.")
|
||||
end
|
||||
levels = levels[1:end-1]
|
||||
end
|
||||
levels
|
||||
end
|
||||
|
||||
function gr_colorbar_colors(series::Series, clims)
|
||||
if iscontour(series)
|
||||
levels = gr_contour_levels(series, clims)
|
||||
if isfilledcontour(series)
|
||||
# GR.contourf uses a color range according to supplied levels
|
||||
zrange = ignorenan_extrema(levels)
|
||||
else
|
||||
# GR.contour uses a color range according to data range
|
||||
zrange = clims
|
||||
end
|
||||
colors = 1000 .+ 255 .* (levels .- zrange[1]) ./ (zrange[2] - zrange[1])
|
||||
else
|
||||
colors = 1000:1255
|
||||
end
|
||||
round.(Int,colors)
|
||||
end
|
||||
|
||||
# add the colorbar
|
||||
function gr_colorbar(sp::Subplot, clims, levels)
|
||||
function gr_draw_colorbar(cbar::GRColorbar, sp::Subplot, clims)
|
||||
GR.savestate()
|
||||
xmin, xmax = gr_xy_axislims(sp)[1:2]
|
||||
zmin, zmax = clims[1:2]
|
||||
gr_set_viewport_cmap(sp)
|
||||
l = if levels === nothing
|
||||
(1000:1255)'
|
||||
elseif length(levels) > 1
|
||||
min_level, max_level = ignorenan_minimum(levels), ignorenan_maximum(levels)
|
||||
round.(Int32, 1000 .+ (levels .- min_level) ./ (max_level - min_level) .* 255)
|
||||
else
|
||||
Int32[1000, 1255]
|
||||
end
|
||||
GR.setscale(0)
|
||||
GR.setwindow(xmin, xmax, zmin, zmax)
|
||||
GR.cellarray(xmin, xmax, zmax, zmin, 1, length(l), l)
|
||||
if (hm = cbar.heatmapseries) !== nothing
|
||||
gr_set_gradient!(hm)
|
||||
# TODO should use z limits of the specific series
|
||||
GR.cellarray(xmin, xmax, zmax, zmin, 1, 256, 1000:1255)
|
||||
end
|
||||
|
||||
if (series = cbar.fillseries) !== nothing
|
||||
gr_set_gradient!(cbar.fillseries)
|
||||
GR.setfillintstyle(GR.INTSTYLE_SOLID)
|
||||
levels = contour_levels(series, clims)
|
||||
colors = gr_colorbar_colors(series, clims)
|
||||
for (from, to, color) in zip(levels[1:end-1], levels[2:end], colors)
|
||||
GR.setfillcolorind(color)
|
||||
GR.fillrect( xmin, xmax, from, to )
|
||||
end
|
||||
end
|
||||
|
||||
if (series = cbar.linesseries) !== nothing
|
||||
gr_set_gradient!(series)
|
||||
gr_set_line(get_linewidth(series), get_linestyle(series), get_linecolor(series, clims))
|
||||
levels = contour_levels(series, clims)
|
||||
colors = gr_colorbar_colors(series, clims)
|
||||
for (line, color) in zip(levels, colors)
|
||||
GR.setlinecolorind(color)
|
||||
GR.polyline([xmin,xmax], [line,line] )
|
||||
end
|
||||
end
|
||||
|
||||
ztick = 0.5 * GR.tick(zmin, zmax)
|
||||
gr_set_line(1, :solid, plot_color(:black))
|
||||
GR.axes(0, ztick, xmax, zmin, 0, 1, 0.005)
|
||||
|
||||
gr_set_font(guidefont(sp[:yaxis]))
|
||||
@ -489,6 +557,21 @@ function gr_set_gradient(c)
|
||||
grad
|
||||
end
|
||||
|
||||
function gr_set_gradient!(series::Series)
|
||||
st = series[:seriestype]
|
||||
if st in (:surface, :heatmap) || isfilledcontour(series)
|
||||
gr_set_gradient(series[:fillcolor])
|
||||
elseif st in (:contour, :wireframe)
|
||||
gr_set_gradient(series[:linecolor])
|
||||
elseif series[:marker_z] != nothing
|
||||
series[:markercolor] = gr_set_gradient(series[:markercolor])
|
||||
elseif series[:line_z] != nothing
|
||||
series[:linecolor] = gr_set_gradient(series[:linecolor])
|
||||
elseif series[:fill_z] != nothing
|
||||
series[:fillcolor] = gr_set_gradient(series[:fillcolor])
|
||||
end
|
||||
end
|
||||
|
||||
# this is our new display func... set up the viewport_canvas, compute bounding boxes, and display each subplot
|
||||
function gr_display(plt::Plot, fmt="")
|
||||
GR.clearws()
|
||||
@ -671,7 +754,7 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
|
||||
outside_ticks = false
|
||||
# calculate the colorbar limits once for a subplot
|
||||
clims = get_clims(sp)
|
||||
clevels = nothing
|
||||
cbar = GRColorbar()
|
||||
|
||||
draw_axes = sp[:framestyle] != :none
|
||||
# axes_2d = true
|
||||
@ -695,8 +778,7 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
|
||||
data_lims = gr_xy_axislims(sp)
|
||||
end
|
||||
|
||||
# color levels overwritten by the last relevant series
|
||||
hascolorbar(series) && (clevels = colorbar_levels(series, clims))
|
||||
hascolorbar(series) && gr_update_colorbar!(cbar,series)
|
||||
end
|
||||
|
||||
# set our plot area view
|
||||
@ -963,24 +1045,12 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
|
||||
|
||||
for (idx, series) in enumerate(series_list(sp))
|
||||
st = series[:seriestype]
|
||||
|
||||
# update the current stored gradient
|
||||
if st in (:surface, :heatmap) ||
|
||||
(st == :contour && series[:fillrange] !== nothing)
|
||||
gr_set_gradient(series[:fillcolor]) #, series[:fillalpha])
|
||||
elseif st in (:contour, :wireframe)
|
||||
gr_set_gradient(series[:linecolor])
|
||||
elseif series[:marker_z] != nothing
|
||||
series[:markercolor] = gr_set_gradient(series[:markercolor])
|
||||
elseif series[:line_z] != nothing
|
||||
series[:linecolor] = gr_set_gradient(series[:linecolor])
|
||||
elseif series[:fill_z] != nothing
|
||||
series[:fillcolor] = gr_set_gradient(series[:fillcolor])
|
||||
end
|
||||
|
||||
# update the current stored gradient and the series
|
||||
gr_set_gradient!(series)
|
||||
|
||||
GR.savestate()
|
||||
|
||||
|
||||
# update the bounding window
|
||||
if ispolar(sp)
|
||||
gr_set_viewport_polar()
|
||||
@ -1046,12 +1116,12 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
|
||||
|
||||
elseif st == :contour
|
||||
GR.setspace(clims[1], clims[2], 0, 90)
|
||||
h = colorbar_levels(series, clims)
|
||||
GR.setlinetype(gr_linetype[get_linestyle(series)])
|
||||
GR.setlinewidth(max(0, get_linewidth(series) / (sum(gr_plot_size) * 0.001)))
|
||||
is_lc_black = let black=plot_color(:black)
|
||||
plot_color(series[:linecolor]) in (black,[black])
|
||||
end
|
||||
h = gr_contour_levels(series, clims)
|
||||
if series[:fillrange] != nothing
|
||||
if series[:fillcolor] != series[:linecolor] && !is_lc_black
|
||||
@warn("GR: filled contour only supported with black contour lines")
|
||||
@ -1221,7 +1291,7 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
|
||||
end
|
||||
|
||||
# draw the colorbar
|
||||
hascolorbar(sp) && gr_colorbar(sp, clims, clevels)
|
||||
hascolorbar(sp) && gr_draw_colorbar(cbar, sp, clims)
|
||||
|
||||
# add the legend
|
||||
if sp[:legend] != :none
|
||||
|
||||
@ -506,13 +506,13 @@ function plotly_series(plt::Plot, series::Series)
|
||||
plotattributes_out[:showscale] = hascolorbar(sp)
|
||||
|
||||
elseif st == :contour
|
||||
filled = isfilledcontour(series)
|
||||
plotattributes_out[:type] = "contour"
|
||||
plotattributes_out[:x], plotattributes_out[:y], plotattributes_out[:z] = x, y, z
|
||||
# plotattributes_out[:showscale] = series[:colorbar] != :none
|
||||
plotattributes_out[:ncontours] = series[:levels]
|
||||
plotattributes_out[:contours] = KW(:coloring => series[:fillrange] != nothing ? "fill" : "lines", :showlabels => series[:contour_labels] == true)
|
||||
plotattributes_out[:ncontours] = series[:levels] + 2
|
||||
plotattributes_out[:contours] = KW(:coloring => filled ? "fill" : "lines", :showlabels => series[:contour_labels] == true)
|
||||
plotattributes_out[:colorscale] = plotly_colorscale(series[:linecolor], series[:linealpha])
|
||||
plotattributes_out[:showscale] = hascolorbar(sp)
|
||||
plotattributes_out[:showscale] = hascolorbar(sp) && hascolorbar(series)
|
||||
|
||||
elseif st in (:surface, :wireframe)
|
||||
plotattributes_out[:type] = "surface"
|
||||
|
||||
22
src/utils.jl
22
src/utils.jl
@ -582,6 +582,9 @@ end
|
||||
_update_clims(zmin, zmax, emin, emax) = min(zmin, emin), max(zmax, emax)
|
||||
|
||||
function hascolorbar(series::Series)
|
||||
if get(series, :colorbar_entry, true) == false
|
||||
return false
|
||||
end
|
||||
st = series[:seriestype]
|
||||
hascbar = st == :heatmap
|
||||
if st == :contour
|
||||
@ -610,15 +613,20 @@ function hascolorbar(sp::Subplot)
|
||||
hascbar
|
||||
end
|
||||
|
||||
function colorbar_levels(series::Series, clims)
|
||||
if series[:seriestype] == :contour
|
||||
iscontour(series::Series) = series[:seriestype] == :contour
|
||||
isfilledcontour(series::Series) = iscontour(series) && series[:fillrange] !== nothing
|
||||
|
||||
function contour_levels(series::Series, clims)
|
||||
if iscontour(series)
|
||||
zmin, zmax = clims
|
||||
levels = series[:levels]
|
||||
levels isa AbstractArray ?
|
||||
levels :
|
||||
levels > 1 ?
|
||||
range(zmin, stop=zmax, length=levels) :
|
||||
[(zmin + zmax) / 2]
|
||||
if levels isa Integer
|
||||
levels = range(zmin, stop=zmax, length=levels+2)
|
||||
if !isfilledcontour(series)
|
||||
levels = levels[2:end-1]
|
||||
end
|
||||
end
|
||||
levels
|
||||
else # including heatmap, surface
|
||||
nothing
|
||||
end
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user