6.6 Layouts

A complete canvas/layout is defined by Figure, which can be filled with content after creation. We will start with a simple arrangement of one Axis, one Legend and one Colorbar. For this task we can think of the canvas as an arrangement of rows and columns in indexing a Figure much like a regular Array/Matrix. The Axis content will be in row 1, column 1, e.g. fig[1, 1], the Colorbar in row 1, column 2, namely fig[1, 2]. And the Legend in row 2 and across column 1 and 2, namely fig[2, 1:2].

  1. function first_layout()
  2. seed!(123)
  3. x, y, z = randn(6), randn(6), randn(6)
  4. fig = Figure(resolution=(600, 400), backgroundcolor=:grey90)
  5. ax = Axis(fig[1, 1], backgroundcolor=:white)
  6. pltobj = scatter!(ax, x, y; color=z, label="scatters")
  7. lines!(ax, x, 1.1y; label="line")
  8. Legend(fig[2, 1:2], ax, "labels", orientation=:horizontal)
  9. Colorbar(fig[1, 2], pltobj, label="colorbar")
  10. fig
  11. end
  12. JDS.first_layout()

Figure 27: First Layout.

Figure 27: First Layout.

This does look good already, but it could be better. We could fix spacing problems using the following keywords and methods:

  • figure_padding=(left, right, bottom, top)
  • padding=(left, right, bottom, top)

Taking into account the actual size for a Legend or Colorbar is done by

  • tellheight=true or false
  • tellwidth=true or false

Setting these to true will take into account the actual size (height or width) for a Legend or Colorbar. Consequently, things will be resized accordingly.

The space between columns and rows is specified as

  • colgap!(fig.layout, col, separation)
  • rowgap!(fig.layout, row, separation)

Column gap (colgap!), if col is given then the gap will be applied to that specific column. Row gap (rowgap!), if row is given then the gap will be applied to that specific row.

Also, we will see how to put content into the protrusions, i.e. the space reserved for title: x and y; either ticks or label. We do this by plotting into fig[i, j, protrusion] where protrusion can be Left(), Right(), Bottom() and Top(), or for each corner TopLeft(), TopRight(), BottomRight(), BottomLeft(). See below how these options are being used:

  1. function first_layout_fixed()
  2. seed!(123)
  3. x, y, z = randn(6), randn(6), randn(6)
  4. fig = Figure(figure_padding=(0, 3, 5, 2), resolution=(600, 400),
  5. backgroundcolor=:grey90, font="CMU Serif")
  6. ax = Axis(fig[1, 1], xlabel=L"x", ylabel=L"y",
  7. title="Layout example", backgroundcolor=:white)
  8. pltobj = scatter!(ax, x, y; color=z, label="scatters")
  9. lines!(ax, x, 1.1y, label="line")
  10. Legend(fig[2, 1:2], ax, "Labels", orientation=:horizontal,
  11. tellheight=true, titleposition=:left)
  12. Colorbar(fig[1, 2], pltobj, label="colorbar")
  13. # additional aesthetics
  14. Box(fig[1, 1, Right()], color=(:slateblue1, 0.35))
  15. Label(fig[1, 1, Right()], "protrusion", fontsize=18,
  16. rotation=pi / 2, padding=(3, 3, 3, 3))
  17. Label(fig[1, 1, TopLeft()], "(a)", fontsize=18, padding=(0, 3, 8, 0))
  18. colgap!(fig.layout, 5)
  19. rowgap!(fig.layout, 5)
  20. fig
  21. end
  22. JDS.first_layout_fixed()

Figure 28: First Layout Fixed.

Figure 28: First Layout Fixed.

Here, having the label (a) in the TopLeft() is probably not necessary, this will only make sense for more than one plot. For our next example let’s keep using the previous tools and some more to create a richer and complex figure.

You can hide decorations and axis’ spines with:

  • hidedecorations!(ax; kwargs...)
  • hidexdecorations!(ax; kwargs...)
  • hideydecorations!(ax; kwargs...)
  • hidespines!(ax; kwargs...)

Remember, we can always ask for help to see what kind of arguments we can use, e.g.,

  1. help(hidespines!)
  1. hidespines!(la::Axis, spines::Symbol... = (:l, :r, :b, :t)...)
  2. Hide all specified axis spines. Hides all spines by default, otherwise
  3. choose with the symbols :l, :r, :b and :t.
  4. hidespines! has the following function signatures:
  5. (Vector, Vector)
  6. (Vector, Vector, Vector)
  7. (Matrix)
  8. Available attributes for Combined{Makie.hidespines!} are:

Alternatively, for decorations

  1. help(hidedecorations!)
  1. hidedecorations!(la::Axis)
  2. Hide decorations of both x and y-axis: label, ticklabels, ticks and grid.
  3. hidedecorations! has the following function signatures:
  4. (Vector, Vector)
  5. (Vector, Vector, Vector)
  6. (Matrix)
  7. Available attributes for Combined{Makie.hidedecorations!} are:

For elements that you don’t want to hide, just pass them with false, i.e. hideydecorations!(ax; ticks=false, grid=false).

Synchronizing your Axis is done via:

  • linkaxes!, linkyaxes! and linkxaxes!

This could be useful when shared axis are desired. Another way of getting shared axis will be by setting limits!.

Setting limits at once or independently for each axis is done by calling

  • limits!(ax; l, r, b, t), where l is left, r right, b bottom, and t top.

You can also do ylims!(low, high) or xlims!(low, high), and even open ones by doing ylims!(low=0) or xlims!(high=1).

Now, the example:

  1. function complex_layout_double_axis()
  2. seed!(123)
  3. x = LinRange(0, 1, 10)
  4. y = LinRange(0, 1, 10)
  5. z = rand(10, 10)
  6. fig = Figure(resolution=(600, 400), font="CMU Serif", backgroundcolor=:grey90)
  7. ax1 = Axis(fig, xlabel=L"x", ylabel=L"y")
  8. ax2 = Axis(fig, xlabel=L"x")
  9. heatmap!(ax1, x, y, z; colorrange=(0, 1))
  10. series!(ax2, abs.(z[1:4, :]); labels=["lab $i" for i = 1:4], color=:Set1_4)
  11. hm = scatter!(10x, y; color=z[1, :], label="dots", colorrange=(0, 1))
  12. hideydecorations!(ax2, ticks=false, grid=false)
  13. linkyaxes!(ax1, ax2)
  14. #layout
  15. fig[1, 1] = ax1
  16. fig[1, 2] = ax2
  17. Label(fig[1, 1, TopLeft()], "(a)", fontsize=18, padding=(0, 6, 8, 0))
  18. Label(fig[1, 2, TopLeft()], "(b)", fontsize=18, padding=(0, 6, 8, 0))
  19. Colorbar(fig[2, 1:2], hm, label="colorbar", vertical=false, flipaxis=false)
  20. Legend(fig[1, 3], ax2, "Legend")
  21. colgap!(fig.layout, 5)
  22. rowgap!(fig.layout, 5)
  23. fig
  24. end
  25. JDS.complex_layout_double_axis()

Figure 29: Complex layout double axis.

Figure 29: Complex layout double axis.

So, now our Colorbar needs to be horizontal and the bar ticks need to be in the lower part. This is done by setting vertical=false and flipaxis=false. Additionally, note that we can call many Axis into fig, or even Colorbar’s and Legend’s, and then afterwards build the layout.

Another common layout is a grid of squares for heatmaps:

  1. function squares_layout()
  2. seed!(123)
  3. letters = reshape(collect('a':'d'), (2, 2))
  4. fig = Figure(resolution=(600, 400), fontsize=14, font="CMU Serif",
  5. backgroundcolor=:grey90)
  6. axs = [Axis(fig[i, j], aspect=DataAspect()) for i = 1:2, j = 1:2]
  7. hms = [heatmap!(axs[i, j], randn(10, 10), colorrange=(-2, 2))
  8. for i = 1:2, j = 1:2]
  9. Colorbar(fig[1:2, 3], hms[1], label="colorbar")
  10. [Label(fig[i, j, TopLeft()], "($(letters[i, j]))", fontsize=16,
  11. padding=(-2, 0, -20, 0)) for i = 1:2, j = 1:2]
  12. colgap!(fig.layout, 5)
  13. rowgap!(fig.layout, 5)
  14. fig
  15. end
  16. JDS.squares_layout()

Figure 30: Squares layout.

Figure 30: Squares layout.

where all labels are in the protrusions and each Axis has an AspectData() ratio. The Colorbar is located in the third column and expands from row 1 up to row 2.

The next case uses the so called Mixed() alignmode, which is especially useful when dealing with large empty spaces between Axis due to long ticks. Also, the Dates module from Julia’s standard library will be needed for this example.

  1. using Dates
  1. function mixed_mode_layout()
  2. seed!(123)
  3. longlabels = ["$(today() - Day(1))", "$(today())", "$(today() + Day(1))"]
  4. fig = Figure(resolution=(600, 400), fontsize=12,
  5. backgroundcolor=:grey90, font="CMU Serif")
  6. ax1 = Axis(fig[1, 1], xlabel="x", alignmode=Mixed(bottom=0))
  7. ax2 = Axis(fig[1, 2], xticklabelrotation=pi / 2, alignmode=Mixed(bottom=0),
  8. xticks=([1, 5, 10], longlabels))
  9. ax3 = Axis(fig[2, 1:2])
  10. ax4 = Axis(fig[3, 1:2])
  11. axs = [ax1, ax2, ax3, ax4]
  12. [lines!(ax, 1:10, rand(10)) for ax in axs]
  13. hidexdecorations!(ax3; ticks=false, grid=false)
  14. Box(fig[2:3, 1:2, Right()], color=(:slateblue1, 0.35))
  15. Label(fig[2:3, 1:2, Right()], "protrusion", rotation=pi / 2, fontsize=14,
  16. padding=(3, 3, 3, 3))
  17. Label(fig[1, 1:2, Top()], "Mixed alignmode", fontsize=16,
  18. padding=(0, 0, 15, 0))
  19. colsize!(fig.layout, 1, Auto(2))
  20. rowsize!(fig.layout, 2, Auto(0.5))
  21. rowsize!(fig.layout, 3, Auto(0.5))
  22. rowgap!(fig.layout, 1, 15)
  23. rowgap!(fig.layout, 2, 0)
  24. colgap!(fig.layout, 5)
  25. fig
  26. end
  27. JDS.mixed_mode_layout()

Figure 31: Mixed mode layout.

Figure 31: Mixed mode layout.

Here, the argument alignmode=Mixed(bottom=0) is shifting the bounding box to the bottom, so that this will align with the panel on the left filling the space.

Also, see how colsize! and rowsize! are being used for different columns and rows. You could also put a number instead of Auto() but then everything will be fixed. And, additionally, one could also give a height or width when defining the Axis, as in Axis(fig, height=50) which will be fixed as well.

6.6.1 Nested Axis (subplots)

It is also possible to define a set of Axis (subplots) explicitly, and use it to build a main figure with several rows and columns. For instance, the following is a “complicated” arrangement of Axis:

  1. function nested_sub_plot!(fig)
  2. color = rand(RGBf)
  3. ax1 = Axis(fig[1, 1], backgroundcolor=(color, 0.25))
  4. ax2 = Axis(fig[1, 2], backgroundcolor=(color, 0.25))
  5. ax3 = Axis(fig[2, 1:2], backgroundcolor=(color, 0.25))
  6. ax4 = Axis(fig[1:2, 3], backgroundcolor=(color, 0.25))
  7. return (ax1, ax2, ax3, ax4)
  8. end

which, when used to build a more complex figure by doing several calls, we obtain:

  1. function main_figure()
  2. fig = Figure()
  3. Axis(fig[1, 1])
  4. nested_sub_plot!(fig[1, 2])
  5. nested_sub_plot!(fig[1, 3])
  6. nested_sub_plot!(fig[2, 1:3])
  7. fig
  8. end
  9. JDS.main_figure()

Figure 32: Main figure.

Figure 32: Main figure.

Note that different subplot functions can be called here. Also, each Axis here is an independent part of Figure. So that, if you need to do some rowgap!’s or colsize!’s operations, you will need to do it in each one of them independently or to all of them together.

For grouped Axis (subplots) we can use GridLayout() which, then, could be used to compose a more complicated Figure.

6.6.2 Nested GridLayout

By using GridLayout() we can group subplots, allowing more freedom to build complex figures. Here, using our previous nested_sub_plot! we define three sub-groups and one normal Axis:

  1. function nested_Grid_Layouts()
  2. fig = Figure(backgroundcolor=RGBf(0.96, 0.96, 0.96))
  3. ga = fig[1, 1] = GridLayout()
  4. gb = fig[1, 2] = GridLayout()
  5. gc = fig[1, 3] = GridLayout()
  6. gd = fig[2, 1:3] = GridLayout()
  7. gA = Axis(ga[1, 1])
  8. nested_sub_plot!(gb)
  9. axsc = nested_sub_plot!(gc)
  10. nested_sub_plot!(gd)
  11. hidedecorations!.(axsc, grid=false, ticks=false)
  12. colgap!(gc, 5)
  13. rowgap!(gc, 5)
  14. rowsize!(fig.layout, 2, Auto(0.5))
  15. colsize!(fig.layout, 1, Auto(0.5))
  16. fig
  17. end
  18. JDS.nested_Grid_Layouts()

Figure 33: Nested Grid Layouts.

Figure 33: Nested Grid Layouts.

Now, using rowgap! or colsize! over each group is possible and rowsize!, colsize! can also be applied to the set of GridLayout()s.

6.6.3 Inset plots

Currently, doing inset plots is a little bit tricky. Here, we show two possible ways of doing it by initially defining auxiliary functions. The first one is by doing a BBox, which lives in the whole Figure space:

  1. function add_box_inset(fig; left=100, right=250, bottom=200, top=300,
  2. bgcolor=:grey90)
  3. inset_box = Axis(fig, bbox=BBox(left, right, bottom, top),
  4. xticklabelsize=12, yticklabelsize=12, backgroundcolor=bgcolor)
  5. # bring content upfront
  6. translate!(inset_box.scene, 0, 0, 10)
  7. return inset_box
  8. end

Then, the inset is easily done, as in:

  1. function figure_box_inset()
  2. fig = Figure(resolution=(600, 400))
  3. ax = Axis(fig[1, 1], backgroundcolor=:white)
  4. inset_ax1 = add_box_inset(fig; left=100, right=250, bottom=200, top=300,
  5. bgcolor=:grey90)
  6. inset_ax2 = add_box_inset(fig; left=500, right=580, bottom=100, top=200,
  7. bgcolor=(:white, 0.65))
  8. lines!(ax, 1:10)
  9. lines!(inset_ax1, 1:10)
  10. scatter!(inset_ax2, 1:10, color=:black)
  11. fig
  12. end
  13. JDS.figure_box_inset()

Figure 34: Figure box inset.

Figure 34: Figure box inset.

where the Box dimensions are bound by the Figure’s resolution. Note, that an inset can be also outside the Axis. The other approach, is by defining a new Axis into a position fig[i, j] specifying his width, height, halign and valign. We do that in the following function:

  1. function add_axis_inset(pos=fig[1, 1]; halign, valign, width=Relative(0.5),
  2. height=Relative(0.35), alignmode=Mixed(left=5, right=5), bgcolor=:lightgray)
  3. inset_box = Axis(pos; width, height, halign, valign, alignmode,
  4. xticklabelsize=12, yticklabelsize=12, backgroundcolor=bgcolor)
  5. # bring content upfront
  6. translate!(inset_box.scene, 0, 0, 10)
  7. return inset_box
  8. end

See that in the following example the Axis with gray background will be rescaled if the total figure size changes. The insets are bound by the Axis positioning.

  1. function figure_axis_inset()
  2. fig = Figure(resolution=(600, 400))
  3. ax = Axis(fig[1, 1], backgroundcolor=:white)
  4. inset_ax1 = add_axis_inset(fig[1, 1]; halign=:left, valign=:center,
  5. width=Relative(0.3), height=Relative(0.35),
  6. alignmode=Mixed(left=5, right=5, bottom=15),
  7. bgcolor=:grey90)
  8. inset_ax2 = add_axis_inset(fig[1, 1]; halign=:right, valign=:center,
  9. width=Relative(0.25), height=Relative(0.3), bgcolor=(:white, 0.65))
  10. lines!(ax, 1:10)
  11. lines!(inset_ax1, 1:10)
  12. scatter!(inset_ax2, 1:10, color=:black)
  13. fig
  14. end
  15. JDS.figure_axis_inset()

Figure 35: Figure axis inset.

Figure 35: Figure axis inset.

And this should cover most used cases for layouting with Makie. Now, let’s do some nice 3D examples with GLMakie.jl.

6.6 Layouts - 图10 Support this project
CC BY-NC-SA 4.0 Jose Storopoli, Rik Huijzer, Lazaro Alonso