# https://github.com/dcjones/Gadfly.jl immutable GadflyPackage <: PlottingPackage end export gadfly! gadfly!() = plotter!(:gadfly) supportedArgs(::GadflyPackage) = setdiff(ARGS, [:heatmap_c, :pos]) supportedAxes(::GadflyPackage) = setdiff(ALL_AXES, [:right]) supportedTypes(::GadflyPackage) = [:none, :line, :step, :sticks, :scatter, :heatmap, :hexbin, :hist, :bar, :hline, :vline, :ohlc] supportedStyles(::GadflyPackage) = [:auto, :solid, :dash, :dot, :dashdot, :dashdotdot] supportedMarkers(::GadflyPackage) = [:none, :auto, :rect, :ellipse, :diamond, :utriangle, :dtriangle, :cross, :xcross, :star1, :star2, :hexagon, :octagon] include("gadfly_shapes.jl") function createGadflyPlotObject(d::Dict) @eval import DataFrames gplt = Gadfly.Plot() gplt.mapping = Dict() gplt.data_source = DataFrames.DataFrame() gplt.layers = gplt.layers[1:0] # add the title, axis labels, and theme gplt.guides = Gadfly.GuideElement[Gadfly.Guide.xlabel(d[:xlabel]), Gadfly.Guide.ylabel(d[:ylabel]), Gadfly.Guide.title(d[:title])] # add the legend? if d[:legend] unshift!(gplt.guides, Gadfly.Guide.manual_color_key("", AbstractString[], Color[])) end gplt.theme = Gadfly.Theme(background_color = d[:background_color]) gplt end # function getGeoms(linetype::Symbol, marker::Symbol, markercolor::Colorant, nbins::Int) function getLineGeoms(d::Dict) lt = d[:linetype] lt in (:heatmap,:hexbin) && return [Gadfly.Geom.hexbin(xbincount = d[:nbins], ybincount = d[:nbins])] lt == :hist && return [Gadfly.Geom.histogram(bincount = d[:nbins])] # lt == :none && return [Gadfly.Geom.path] lt == :line && return [Gadfly.Geom.path] lt == :scatter && return [Gadfly.Geom.point] lt == :bar && return [Gadfly.Geom.bar] lt == :step && return [Gadfly.Geom.step] # NOTE: we won't actually show this (we'll set width to 0 later), but we need a geom so that Gadfly doesn't complain if lt in (:none, :ohlc) return [Gadfly.Geom.path] end # lt == :sticks && return [Gadfly.Geom.bar] error("linetype $lt not currently supported with Gadfly") end # serious hack (I think?) to draw my own shapes as annotations... will it work? who knows... function getMarkerGeomsAndGuides(d::Dict) marker = d[:marker] if marker == :none && d[:linetype] != :ohlc return [],[] end return [], [createGadflyAnnotation(d)] end function addGadflyFixedLines!(gplt, d::Dict, theme) sz = d[:width] * Gadfly.px c = d[:color] if d[:linetype] == :hline geom = Gadfly.Geom.hline(color=c, size=sz) layer = Gadfly.layer(yintercept = d[:y], geom, theme) else geom = Gadfly.Geom.vline(color=c, size=sz) layer = Gadfly.layer(xintercept = d[:y], geom, theme) end prepend!(gplt.layers, layer) end function getGadflyStrokeVector(linestyle::Symbol) dash = 12 * Compose.mm dot = 3 * Compose.mm gap = 2 * Compose.mm linestyle == :solid && return nothing linestyle == :dash && return [dash, gap] linestyle == :dot && return [dot, gap] linestyle == :dashdot && return [dash, gap, dot, gap] linestyle == :dashdotdot && return [dash, gap, dot, gap, dot, gap] error("unsupported linestyle: ", linestyle) end function addGadflySeries!(gplt, d::Dict) gfargs = [] # if my PR isn't present, don't set the line_style extra_theme_args = Gadfly.isdefined(:getStrokeVector) ? [(:line_style, getGadflyStrokeVector(d[:linestyle]))] : [] # line_style = getGadflyStrokeVector(d[:linestyle]) # set theme: color, line width, and point size line_width = d[:width] * (d[:linetype] == :none ? 0 : 1) * Gadfly.px # 0 width when we don't show a line theme = Gadfly.Theme(default_color = d[:color], line_width = line_width, default_point_size = 0.5 * d[:markersize] * Gadfly.px, extra_theme_args...) # line_style = line_style) push!(gfargs, theme) # first things first... lets so the sticks hack if d[:linetype] == :sticks d, dScatter = sticksHack(;d...) # add the annotation if dScatter[:marker] != :none push!(gplt.guides, createGadflyAnnotation(dScatter)) end elseif d[:linetype] in (:hline, :vline) addGadflyFixedLines!(gplt, d, theme) return end # add the Geoms append!(gfargs, getLineGeoms(d)) # fillto if d[:fillto] != nothing fillto = makevec(d[:fillto]) n = length(fillto) push!(d[:kwargs], (:ymin, Float64[min(y, fillto[mod1(i,n)]) for (i,y) in enumerate(d[:y])])) push!(d[:kwargs], (:ymax, Float64[max(y, fillto[mod1(i,n)]) for (i,y) in enumerate(d[:y])])) # push!(d[:kwargs], (:ymax, Float64[max(y, fillto) for y in d[:y]])) push!(gfargs, Gadfly.Geom.ribbon) end # handle markers geoms, guides = getMarkerGeomsAndGuides(d) append!(gfargs, geoms) append!(gplt.guides, guides) # add a regression line? if d[:reg] push!(gfargs, Gadfly.Geom.smooth(method=:lm)) end # add to the legend if length(gplt.guides) > 0 && isa(gplt.guides[1], Gadfly.Guide.ManualColorKey) push!(gplt.guides[1].labels, d[:label]) push!(gplt.guides[1].colors, d[:marker] == :none ? d[:color] : d[:markercolor]) end # for histograms, set x=y x = d[d[:linetype] == :hist ? :y : :x] if d[:axis] != :left warn("Gadly only supports one y axis") end # add the layer to the Gadfly.Plot prepend!(gplt.layers, Gadfly.layer(unique(gfargs)..., d[:args]...; x = x, y = d[:y], d[:kwargs]...)) nothing end # --------------------------------------------------------------------------- # create a blank Gadfly.Plot object function plot(pkg::GadflyPackage; kw...) d = Dict(kw) gplt = createGadflyPlotObject(d) Plot(gplt, pkg, 0, d, Dict[]) end # plot one data series function plot!(::GadflyPackage, plt::Plot; kw...) d = Dict(kw) addGadflySeries!(plt.o, d) push!(plt.seriesargs, d) plt end function setGadflyDisplaySize(w,h) Compose.set_default_graphic_size(w * Compose.px, h * Compose.px) end function Base.display(::GadflyPackage, plt::Plot) setGadflyDisplaySize(plt.initargs[:size]...) display(plt.o) end # ------------------------------- function savepng(::GadflyPackage, plt::PlottingObject, fn::AbstractString; w = plt.initargs[:size][1] * Gadfly.px, # 6 * Gadfly.inch, h = plt.initargs[:size][2] * Gadfly.px) # 4 * Gadfly.inch) o = getGadflyContext(plt.plotter, plt) Gadfly.draw(Gadfly.PNG(fn, w, h), o) end # ------------------------------- getGadflyContext(::GadflyPackage, plt::Plot) = plt.o getGadflyContext(::GadflyPackage, subplt::Subplot) = buildGadflySubplotContext(subplt) # create my Compose.Context grid by hstacking and vstacking the Gadfly.Plot objects function buildGadflySubplotContext(subplt::Subplot) i = 0 rows = [] for rowcnt in subplt.layout.rowcounts push!(rows, Gadfly.hstack([getGadflyContext(plt.plotter, plt) for plt in subplt.plts[(1:rowcnt) + i]]...)) i += rowcnt end Gadfly.vstack(rows...) end # create the underlying object (each backend will do this differently) function buildSubplotObject!(::GadflyPackage, subplt::Subplot) subplt.o = nothing end function Base.display(::GadflyPackage, subplt::Subplot) setGadflyDisplaySize(plt.initargs[:size]...) display(buildGadflySubplotContext(subplt)) end