juliamakie.jl

Join Makie.jl Figures as Sublots


Is there a known way to take a fully generated Figure object and add it to another Figure as a subplot, using Makie? This question was asked years ago (and many versions ago) on discourse ( https://discourse.julialang.org/t/makie-is-there-an-easy-way-to-combine-several-figures-into-a-new-figure-without-re-plotting/64874/2 ), but the accepted solution does not seem to actually answer the question (it describes a new process in creating the Figures, which is useful, but is not how to join two existing ones).

The usecase here being that many of us create complex Makie recipes, and implementing them as subplots would require refactoring the recipes themselves, which doesn't seem necessary.

using Makie

# We have some recipe created that outputs a figure
function make_some_figure()
    # let's use heatmap as a placeholder recipe, which returns a FigureAxisPlot
    # This can be broken into its components easily
    # heatmap has functionality to add as subplot, but imagine a custom one where this is not integrated
    fig,axs,plt = heatmap(rand(100,100)) 
    return fig
end

# Now, we try to make a plot with two figures from make_some_figure() as subplots
Over

# Overall Fig
myfig = Figure()

# Make subplot figs
subfigA, subaxsA, subpltA = make_some_figure()
subfigB, subaxsB, subpltB = make_some_figure()


Now, is there a way to add the figures to myfig? There is no subplot!()-like function, so I have tried all sorts of strange things, with no success. For instance, Manually creating axes via Axis(myfig[1,1]);Axis(myfig[1,2]), and trying to copy data over using setproperty!() and getproperty() to essentially copy one of my figures and axes onto myfig, but eventually always run into an err/ missing method for what I am looking for. Is there any known method to do this? Thanks!


Solution

  • Makie's design wants you to add your subplots to an existing figure. This means you have to first create a Figure and then specify grid of Axis in your Figure, placing the heatmap plot in that grid by using heatmap! to modify your Figure or Axis.

    So, ideally, you would need to pass the Figure to the function that makes your plot and then plot! into that pre-existing Figure. If you do not do that, if you instead use plot() or heatmap(), Makie will tend to make a brand new Figure each time you want to plot, instead of placing the subplot in your Figure, as you want. So:

    using GLMakie # best to specify the backend, eg GLMakie for GL, CairoMakie for Cairo
    # We have some recipe created that outputs a figure
    function make_some_figure(fig, subfignumber)
        # let's use heatmap as a placeholder recipe, which returns a FigureAxisPlot
        # This can be broken into its components easily
        # heatmap has functionality to add as subplot, but imagine a custom one where this is not integrated
        fig[1, subfignumber] = Axis(fig) # specify grid location
        axs = fig[1, subfignumber]  # an Axis is a 2D plotting area
        plt = heatmap!(axs, rand(100,100)) # plot WITHIN the axis axs is first argument to heatmap! not heatmap which makes a new figure
        return fig, axs, plt
    end
    # Now, we try to make a plot with two figures from make_some_figure() as subplots
    # Overall Fig
    myfig = Figure()
    # Make subplot figs
    subfigA, subaxsA, subpltA = make_some_figure(myfig, 1)
    subfigB, subaxsB, subpltB = make_some_figure(myfig, 2)
    display(myfig)
    

    If you really must take a separate plot generated as a separate figure and place it, as already generated, into your own Figure as a subplot, you probably need to write the external plot to a file and then load it into your specified Axis with image!:

    using FileIO
    using GLMakie
    f1 = Figure()
    scatter(f1[1,1], rand(1:100, 30))
    save("test.png", f1)
    
    f2 = Figure()
    axs = Axis(f2[2,2])
    hist(f2[1,1], rand(1:100, 12))
    image!(axs, load("test.png"))