From b009da4b47d3ac5032506949ab7354a3853b672c Mon Sep 17 00:00:00 2001 From: Andrew Palugniok Date: Mon, 9 Apr 2018 21:20:41 +0100 Subject: [PATCH 1/2] Fix :native DateTime and categorical ticks for Plotly. --- src/backends/plotly.jl | 59 ++++++++++++++++++++++++++++++++++-------- src/utils.jl | 6 ++++- 2 files changed, 53 insertions(+), 12 deletions(-) diff --git a/src/backends/plotly.jl b/src/backends/plotly.jl index 8578b32e..ed29279d 100644 --- a/src/backends/plotly.jl +++ b/src/backends/plotly.jl @@ -485,6 +485,21 @@ function plotly_close_shapes(x, y) nanvcat(xs), nanvcat(ys) end +function plotly_data(series::Series, letter::Symbol, data) + axis = series[:subplot][Symbol(letter, :axis)] + + data = if axis[:ticks] == :native && data != nothing + plotly_native_data(axis, data) + else + data + end + + if series[:seriestype] in (:heatmap, :contour, :surface, :wireframe) + plotly_surface_data(series, data) + else + plotly_data(data) + end +end plotly_data(v) = v != nothing ? collect(v) : v plotly_data(surf::Surface) = surf.surf plotly_data(v::AbstractArray{R}) where {R<:Rational} = float(v) @@ -493,6 +508,28 @@ plotly_surface_data(series::Series, a::AbstractVector) = a plotly_surface_data(series::Series, a::AbstractMatrix) = transpose_z(series, a, false) plotly_surface_data(series::Series, a::Surface) = plotly_surface_data(series, a.surf) +function plotly_native_data(axis::Axis, data::AbstractArray) + if !isempty(axis[:discrete_values]) + construct_categorical_data(data, axis) + elseif axis[:formatter] in (datetimeformatter, dateformatter, timeformatter) + plotly_convert_to_datetime(data, axis[:formatter]) + else + data + end +end +plotly_native_data(axis::Axis, a::Surface) = Surface(plotly_native_data(axis, a.surf)) + +function plotly_convert_to_datetime(x::AbstractArray, formatter::Function) + if formatter == datetimeformatter + map(xi -> replace(formatter(xi), "T", " "), x) + elseif formatter == dateformatter + map(xi -> string(formatter(xi), " 00:00:00"), x) + elseif formatter == timeformatter + map(xi -> string(Dates.Date(Dates.now()), " ", formatter(xi)), x) + else + error("Invalid DateTime formatter. Expected Plots.datetime/date/time formatter but got $formatter") + end +end #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) as_gradient(grad::ColorGradient, α) = grad as_gradient(grad, α) = cgrad(alpha = α) @@ -513,20 +550,17 @@ function plotly_series(plt::Plot, series::Series) d_out[:yaxis] = "y$(y_idx)" d_out[:showlegend] = should_add_to_legend(series) - - x, y, z = map(letter -> (axis = sp[Symbol(letter, :axis)]; - if axis[:ticks] == :native && !isempty(axis[:discrete_values]) - axis[:discrete_values] - elseif st in (:heatmap, :contour, :surface, :wireframe) - plotly_surface_data(series, series[letter]) - else - plotly_data(series[letter]) - end), (:x, :y, :z)) - if st == :straightline x, y = straightline_data(series) + z = series[:z] + else + x, y, z = series[:x], series[:y], series[:z] end + x, y, z = (plotly_data(series, letter, data) + for (letter, data) in zip((:x, :y, :z), (x, y, z)) + ) + d_out[:name] = series[:label] isscatter = st in (:scatter, :scatter3d, :scattergl) @@ -643,7 +677,10 @@ function plotly_series_shapes(plt::Plot, series::Series) base_d[:name] = series[:label] # base_d[:legendgroup] = series[:label] - x, y = shape_data(series) + x, y = (plotly_data(series, letter, data) + for (letter, data) in zip((:x, :y), shape_data(series)) + ) + for (i,rng) in enumerate(segments) length(rng) < 2 && continue diff --git a/src/utils.jl b/src/utils.jl index 80d5487f..849c048e 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -207,7 +207,7 @@ function iter_segments(series::Series) end # helpers to figure out if there are NaN values in a list of array types -anynan(i::Int, args::Tuple) = any(a -> !isfinite(_cycle(a,i)), args) +anynan(i::Int, args::Tuple) = any(a -> try isnan(_cycle(a,i)) catch MethodError false end, args) anynan(istart::Int, iend::Int, args::Tuple) = any(i -> anynan(i, args), istart:iend) allnan(istart::Int, iend::Int, args::Tuple) = all(i -> anynan(i, args), istart:iend) @@ -1193,3 +1193,7 @@ function shape_data(series) end return x, y end + +function construct_categorical_data(x::AbstractArray, axis::Axis) + map(xi -> axis[:discrete_values][searchsortedfirst(axis[:continuous_values], xi)], x) +end From aeaa0c5f8d74cb709d682114230bd9d9e227e40f Mon Sep 17 00:00:00 2001 From: Andrew Palugniok Date: Mon, 9 Apr 2018 21:28:45 +0100 Subject: [PATCH 2/2] Fix axis limits for ticks = :native --- src/backends/plotly.jl | 5 ++++- src/backends/pyplot.jl | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/backends/plotly.jl b/src/backends/plotly.jl index ed29279d..7fb94e41 100644 --- a/src/backends/plotly.jl +++ b/src/backends/plotly.jl @@ -264,7 +264,10 @@ function plotly_axis(plt::Plot, axis::Axis, sp::Subplot) ax[:tickangle] = -axis[:rotation] lims = axis_limits(axis) - axis[:ticks] != :native ? ax[:range] = map(scalefunc(axis[:scale]), lims) : nothing + + if axis[:ticks] != :native || axis[:lims] != :auto + ax[:range] = map(scalefunc(axis[:scale]), lims) + end if !(axis[:ticks] in (nothing, :none, false)) ax[:titlefont] = plotly_font(guidefont(axis)) diff --git a/src/backends/pyplot.jl b/src/backends/pyplot.jl index cdf63b46..38d9f15f 100644 --- a/src/backends/pyplot.jl +++ b/src/backends/pyplot.jl @@ -1077,7 +1077,7 @@ function _before_layout_calcs(plt::Plot{PyPlotBackend}) pyaxis[Symbol(:tick_, pos)]() # the tick labels end py_set_scale(ax, axis) - axis[:ticks] != :native ? py_set_lims(ax, axis) : nothing + axis[:ticks] != :native || axis[:lims] != :auto ? py_set_lims(ax, axis) : nothing if ispolar(sp) && letter == :y ax[:set_rlabel_position](90) end