From 9ea0585d71d3df164cf7cb78281aaf50c05f669c Mon Sep 17 00:00:00 2001 From: Thomas Breloff Date: Mon, 26 Oct 2015 18:07:22 -0400 Subject: [PATCH] support for contours in gadfly/immerse/pyplot --- .travis.yml | 3 +- REQUIRE | 2 +- src/args.jl | 5 ++- src/backends/gadfly.jl | 6 +++- src/backends/pyplot.jl | 76 ++++++++++++++++++++++----------------- src/backends/supported.jl | 8 +++-- src/plot.jl | 28 ++++++++++++--- test/REQUIRE | 2 +- 8 files changed, 84 insertions(+), 46 deletions(-) diff --git a/.travis.yml b/.travis.yml index 7796ec41..3b2ea8c1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,7 +4,6 @@ os: - linux - osx julia: - - 0.3 - 0.4 #- nightly notifications: @@ -17,5 +16,5 @@ script: - julia -e 'Pkg.clone("Cairo"); Pkg.build("Cairo")' - julia -e 'ENV["PYTHON"] = ""; Pkg.clone("PyPlot"); Pkg.build("PyPlot")' - julia -e 'Pkg.clone(pwd()); Pkg.build("Plots")' - - julia -e 'Pkg.test("Plots"; coverage=true)' + - julia -e 'Pkg.test("Plots"; coverage=false)' # - julia -e 'cd(Pkg.dir("Plots")); Pkg.add("Coverage"); using Coverage; Coveralls.submit(process_folder()); Codecov.submit(process_folder())' diff --git a/REQUIRE b/REQUIRE index 0871e558..04b7f31e 100644 --- a/REQUIRE +++ b/REQUIRE @@ -1,4 +1,4 @@ -julia 0.3 +julia 0.4 Colors Reexport diff --git a/src/args.jl b/src/args.jl index de5cf4c3..1b9900c4 100644 --- a/src/args.jl +++ b/src/args.jl @@ -8,7 +8,7 @@ const _allAxes = [:auto, :left, :right] ) const _allTypes = [:none, :line, :path, :steppre, :steppost, :sticks, :scatter, - :heatmap, :hexbin, :hist, :bar, :hline, :vline, :ohlc] + :heatmap, :hexbin, :hist, :bar, :hline, :vline, :ohlc, :contour] @compat const _typeAliases = Dict( :n => :none, :no => :none, @@ -26,6 +26,7 @@ const _allTypes = [:none, :line, :path, :steppre, :steppost, :sticks, :scatter, :stems => :sticks, :dots => :scatter, :histogram => :hist, + :contours => :contours, ) const _allStyles = [:auto, :solid, :dash, :dot, :dashdot, :dashdotdot] @@ -130,6 +131,8 @@ _seriesDefaults[:z] = nothing # depth for contour, color sca # _seriesDefaults[:args] = [] # additional args to pass to the backend # _seriesDefaults[:kwargs] = [] # additional keyword args to pass to the backend # # note: can be Vector{Dict} or Vector{Tuple} +_seriesDefaults[:surface] = nothing +_seriesDefaults[:nlevels] = 15 const _plotDefaults = Dict{Symbol, Any}() diff --git a/src/backends/gadfly.jl b/src/backends/gadfly.jl index 8ffe1ecb..7a69301e 100644 --- a/src/backends/gadfly.jl +++ b/src/backends/gadfly.jl @@ -39,6 +39,8 @@ function getLineGeom(d::Dict) Gadfly.Geom.hline(color = getColor(d[:color]), size = d[:linewidth] * Gadfly.px) elseif lt == :vline Gadfly.Geom.vline(color = getColor(d[:color]), size = d[:linewidth] * Gadfly.px) + elseif lt == :contour + Gadfly.Geom.contour(levels = d[:nlevels]) else nothing end @@ -94,6 +96,8 @@ function addGadflyLine!(plt::Plot, d::Dict, geoms...) w = 0.01 * mean(diff(d[:x])) kwargs[:xmin] = d[:x] - w kwargs[:xmax] = d[:x] + w + elseif lt == :contour + kwargs[:z] = d[:surface] end # add the layer @@ -224,7 +228,7 @@ function addGadflySeries!(plt::Plot, d::Dict) prepend!(layers, addGadflyMarker!(plt, d, plt.initargs, smooth...)) end - lt in (:hist, :heatmap, :hexbin) || addToGadflyLegend(plt, d) + lt in (:hist, :heatmap, :hexbin, :contour) || addToGadflyLegend(plt, d) # now save the layers that apply to this series d[:gadflylayers] = layers diff --git a/src/backends/pyplot.jl b/src/backends/pyplot.jl index d6de810b..f56f4971 100644 --- a/src/backends/pyplot.jl +++ b/src/backends/pyplot.jl @@ -8,10 +8,10 @@ getPyPlotColor(c::Colorant) = map(f->float(f(c)), (red, green, blue, alpha)) getPyPlotColor(scheme::ColorScheme) = getPyPlotColor(getColor(scheme)) getPyPlotColor(c) = getPyPlotColor(convertColor(c)) -# getPyPlotColorMap(c::ColorGradient) = PyPlot.matplotlib[:colors][:ListedColormap](map(getPyPlotColor, getColorVector(c))) function getPyPlotColorMap(c::ColorGradient) pycolors.pymember("LinearSegmentedColormap")[:from_list]("tmp", map(getPyPlotColor, getColorVector(c))) end +getPyPlotColorMap(c) = getPyPlotColorMap(ColorGradient(:redsblues)) # get the style (solid, dashed, etc) function getPyPlotLineStyle(linetype::Symbol, linestyle::Symbol) @@ -107,7 +107,8 @@ function getPyPlotFunction(plt::Plot, axis::Symbol, linetype::Symbol) :bar => :bar, :heatmap => :hexbin, :hexbin => :hexbin, - :scatter => :scatter + :scatter => :scatter, + :contour => :contour, ) return ax[get(fmap, linetype, :plot)] end @@ -125,6 +126,7 @@ function updateAxisColors(ax, fgcolor) ax[:title][:set_color](fgcolor) end + nop() = nothing @@ -195,7 +197,7 @@ function plot!(pkg::PyPlotPackage, plt::Plot; kw...) end lt = d[:linetype] - extraargs = Dict() + extra_kwargs = Dict() plotfunc = getPyPlotFunction(plt, d[:axis], lt) @@ -203,63 +205,71 @@ function plot!(pkg::PyPlotPackage, plt::Plot; kw...) if lt in (:hist, :sticks, :bar) # NOTE: this is unsupported because it does the wrong thing... it shifts the whole axis - # extraargs[:bottom] = d[:fill] + # extra_kwargs[:bottom] = d[:fill] if lt == :hist - extraargs[:bins] = d[:nbins] + extra_kwargs[:bins] = d[:nbins] else - extraargs[:linewidth] = (lt == :sticks ? 0.1 : 0.9) + extra_kwargs[:linewidth] = (lt == :sticks ? 0.1 : 0.9) end elseif lt in (:heatmap, :hexbin) + extra_kwargs[:gridsize] = d[:nbins] + extra_kwargs[:cmap] = getPyPlotColorMap(d[:color]) - extraargs[:gridsize] = d[:nbins] - c = d[:color] - if !isa(c, ColorGradient) - c = ColorGradient(:redsblues) - end - # c = ColorGradient(d[:color]) - extraargs[:cmap] = getPyPlotColorMap(c) + elseif lt == :contour + extra_kwargs[:cmap] = getPyPlotColorMap(d[:color]) + extra_kwargs[:linewidths] = d[:linewidth] + extra_kwargs[:linestyles] = getPyPlotLineStyle(lt, d[:linestyle]) + # TODO: will need to call contourf to fill in the contours else - extraargs[:linestyle] = getPyPlotLineStyle(lt, d[:linestyle]) - extraargs[:marker] = getPyPlotMarker(d[:markershape]) + extra_kwargs[:linestyle] = getPyPlotLineStyle(lt, d[:linestyle]) + extra_kwargs[:marker] = getPyPlotMarker(d[:markershape]) if lt == :scatter - extraargs[:s] = d[:markersize]^2 + extra_kwargs[:s] = d[:markersize]^2 c = d[:markercolor] if isa(c, ColorGradient) && d[:z] != nothing - extraargs[:c] = convert(Vector{Float64}, d[:z]) - extraargs[:cmap] = getPyPlotColorMap(c) + extra_kwargs[:c] = convert(Vector{Float64}, d[:z]) + extra_kwargs[:cmap] = getPyPlotColorMap(c) else - extraargs[:c] = getPyPlotColor(c) + extra_kwargs[:c] = getPyPlotColor(c) end else - extraargs[:markersize] = d[:markersize] - extraargs[:markerfacecolor] = getPyPlotColor(d[:markercolor]) - extraargs[:markeredgecolor] = getPyPlotColor(plt.initargs[:foreground_color]) - extraargs[:markeredgewidth] = d[:linewidth] - extraargs[:drawstyle] = getPyPlotDrawStyle(lt) + extra_kwargs[:markersize] = d[:markersize] + extra_kwargs[:markerfacecolor] = getPyPlotColor(d[:markercolor]) + extra_kwargs[:markeredgecolor] = getPyPlotColor(plt.initargs[:foreground_color]) + extra_kwargs[:markeredgewidth] = d[:linewidth] + extra_kwargs[:drawstyle] = getPyPlotDrawStyle(lt) end end # set these for all types - extraargs[:color] = getPyPlotColor(d[:color]) - extraargs[:linewidth] = d[:linewidth] - extraargs[:label] = d[:label] + if lt != :contour + extra_kwargs[:color] = getPyPlotColor(d[:color]) + extra_kwargs[:linewidth] = d[:linewidth] + extra_kwargs[:label] = d[:label] + end # do the plot d[:serieshandle] = if lt == :hist - plotfunc(d[:y]; extraargs...)[1] + plotfunc(d[:y]; extra_kwargs...)[1] + elseif lt == :contour + handle = plotfunc(d[:x], d[:y], d[:surface], d[:nlevels]; extra_kwargs...) + if d[:fillrange] != nothing + handle = ax[:contourf](d[:x], d[:y], d[:surface], d[:nlevels]; cmap = getPyPlotColorMap(d[:fillcolor])) + end + handle elseif lt in (:scatter, :heatmap, :hexbin) - plotfunc(d[:x], d[:y]; extraargs...) + plotfunc(d[:x], d[:y]; extra_kwargs...) else - plotfunc(d[:x], d[:y]; extraargs...)[1] + plotfunc(d[:x], d[:y]; extra_kwargs...)[1] end # add the colorbar legend - if plt.initargs[:legend] && haskey(extraargs, :cmap) + if plt.initargs[:legend] && haskey(extra_kwargs, :cmap) PyPlot.colorbar(d[:serieshandle]) end @@ -267,7 +277,7 @@ function plot!(pkg::PyPlotPackage, plt::Plot; kw...) ax[:set_axis_bgcolor](getPyPlotColor(plt.initargs[:background_color])) fillrange = d[:fillrange] - if fillrange != nothing + if fillrange != nothing && lt != :contour fillcolor = getPyPlotColor(d[:fillcolor]) if typeof(fillrange) <: @compat(Union{Real, AVec}) ax[:fill_between](d[:x], fillrange, d[:y], facecolor = fillcolor) @@ -494,7 +504,7 @@ end function addPyPlotLegend(plt::Plot, ax) if plt.initargs[:legend] # gotta do this to ensure both axes are included - args = filter(x -> !(x[:linetype] in (:hist,:hexbin,:heatmap,:hline,:vline)), plt.seriesargs) + args = filter(x -> !(x[:linetype] in (:hist,:hexbin,:heatmap,:hline,:vline,:contour)), plt.seriesargs) if length(args) > 0 ax[:legend]([d[:serieshandle] for d in args], [d[:label] for d in args], diff --git a/src/backends/supported.jl b/src/backends/supported.jl index 5303930e..ee750052 100644 --- a/src/backends/supported.jl +++ b/src/backends/supported.jl @@ -49,9 +49,11 @@ supportedArgs(::GadflyPackage) = [ :guidefont, :legendfont, :grid, + :surface, + :nlevels, ] supportedAxes(::GadflyPackage) = [:auto, :left] -supportedTypes(::GadflyPackage) = [:none, :line, :path, :steppre, :steppost, :sticks, :scatter, :heatmap, :hexbin, :hist, :bar, :hline, :vline, :ohlc] +supportedTypes(::GadflyPackage) = [:none, :line, :path, :steppre, :steppost, :sticks, :scatter, :heatmap, :hexbin, :hist, :bar, :hline, :vline, :contour] supportedStyles(::GadflyPackage) = [:auto, :solid, :dash, :dot, :dashdot, :dashdotdot] supportedMarkers(::GadflyPackage) = vcat(_allMarkers, Shape) supportedScales(::GadflyPackage) = [:identity, :log, :log2, :log10, :asinh, :sqrt] @@ -122,9 +124,11 @@ supportedArgs(::PyPlotPackage) = [ :guidefont, :legendfont, # :grid, + :surface, + :nlevels, ] supportedAxes(::PyPlotPackage) = _allAxes -supportedTypes(::PyPlotPackage) = [:none, :line, :path, :steppre, :steppost, :sticks, :scatter, :heatmap, :hexbin, :hist, :bar, :hline, :vline] +supportedTypes(::PyPlotPackage) = [:none, :line, :path, :steppre, :steppost, :sticks, :scatter, :heatmap, :hexbin, :hist, :bar, :hline, :vline, :contour] supportedStyles(::PyPlotPackage) = [:auto, :solid, :dash, :dot, :dashdot] # supportedMarkers(::PyPlotPackage) = [:none, :auto, :rect, :ellipse, :diamond, :utriangle, :dtriangle, :cross, :xcross, :star5, :hexagon] supportedMarkers(::PyPlotPackage) = vcat(_allMarkers, Shape) diff --git a/src/plot.jl b/src/plot.jl index 8cf03f43..6c27f34c 100644 --- a/src/plot.jl +++ b/src/plot.jl @@ -321,20 +321,38 @@ function createKWargsList(plt::PlottingObject, y; kw...) createKWargsList(plt, nothing, y; kw...) end -function createKWargsList(plt::PlottingObject, x::AVec, y::AVec, z::AVec; kw...) +# contours or surfaces... irregular data +function createKWargsList(plt::PlottingObject, x::AVec, y::AVec, zvec::AVec; kw...) error("TODO: contours or surfaces... irregular data") end -function createKWargsList(plt::PlottingObject, x::AVec, y::AVec, z::Function; kw...) - error("TODO: contours or surfaces... function grid") + +# contours or surfaces... function grid +function createKWargsList(plt::PlottingObject, x::AVec, y::AVec, zf::Function; kw...) + # only allow sorted x/y for now + # TODO: auto sort x/y/z properly + @assert x == sort(x) + @assert y == sort(y) + surface = Float64[zf(xi, yi) for xi in x, yi in y] + createKWargsList(plt, x, y, surface; kw...) # passes it to the zmat version end -function createKWargsList(plt::PlottingObject, x::AVec, y::AVec, z::AMat; kw...) - error("TODO: contours or surfaces... grid") + +# contours or surfaces... matrix grid +function createKWargsList{T<:Real}(plt::PlottingObject, x::AVec, y::AVec, zmat::AMat{T}; kw...) + # only allow sorted x/y for now + # TODO: auto sort x/y/z properly + @assert x == sort(x) + @assert y == sort(y) + @assert size(zmat) == (length(x), length(y)) + surf = Array(Any,1,1) + surf[1,1] = convert(Matrix{Float64}, zmat) + createKWargsList(plt, x, y; kw..., surface = surf, linetype = :contour) end function createKWargsList(plt::PlottingObject, f::FuncOrFuncs; kw...) error("Can't pass a Function or Vector{Function} for y without also passing x") end +# list of functions function createKWargsList(plt::PlottingObject, f::FuncOrFuncs, x; kw...) @assert !(typeof(x) <: FuncOrFuncs) # otherwise we'd hit infinite recursion here createKWargsList(plt, x, f; kw...) diff --git a/test/REQUIRE b/test/REQUIRE index a5de3587..8dbd52ff 100644 --- a/test/REQUIRE +++ b/test/REQUIRE @@ -1,4 +1,4 @@ -julia 0.3 +julia 0.4 Colors Reexport