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]
.
function first_layout()
seed!(123)
x, y, z = randn(6), randn(6), randn(6)
fig = Figure(resolution=(600, 400), backgroundcolor=:grey90)
ax = Axis(fig[1, 1], backgroundcolor=:white)
pltobj = scatter!(ax, x, y; color=z, label="scatters")
lines!(ax, x, 1.1y; label="line")
Legend(fig[2, 1:2], ax, "labels", orientation=:horizontal)
Colorbar(fig[1, 2], pltobj, label="colorbar")
fig
end
JDS.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
orfalse
tellwidth=true
orfalse
Setting these to
true
will take into account the actual size (height or width) for aLegend
orColorbar
. 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!
), ifcol
is given then the gap will be applied to that specific column. Row gap (rowgap!
), ifrow
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:
function first_layout_fixed()
seed!(123)
x, y, z = randn(6), randn(6), randn(6)
fig = Figure(figure_padding=(0, 3, 5, 2), resolution=(600, 400),
backgroundcolor=:grey90, font="CMU Serif")
ax = Axis(fig[1, 1], xlabel=L"x", ylabel=L"y",
title="Layout example", backgroundcolor=:white)
pltobj = scatter!(ax, x, y; color=z, label="scatters")
lines!(ax, x, 1.1y, label="line")
Legend(fig[2, 1:2], ax, "Labels", orientation=:horizontal,
tellheight=true, titleposition=:left)
Colorbar(fig[1, 2], pltobj, label="colorbar")
# additional aesthetics
Box(fig[1, 1, Right()], color=(:slateblue1, 0.35))
Label(fig[1, 1, Right()], "protrusion", fontsize=18,
rotation=pi / 2, padding=(3, 3, 3, 3))
Label(fig[1, 1, TopLeft()], "(a)", fontsize=18, padding=(0, 3, 8, 0))
colgap!(fig.layout, 5)
rowgap!(fig.layout, 5)
fig
end
JDS.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.,
help(hidespines!)
hidespines!(la::Axis, spines::Symbol... = (:l, :r, :b, :t)...)
Hide all specified axis spines. Hides all spines by default, otherwise
choose with the symbols :l, :r, :b and :t.
hidespines! has the following function signatures:
(Vector, Vector)
(Vector, Vector, Vector)
(Matrix)
Available attributes for Combined{Makie.hidespines!} are:
Alternatively, for decorations
help(hidedecorations!)
hidedecorations!(la::Axis)
Hide decorations of both x and y-axis: label, ticklabels, ticks and grid.
hidedecorations! has the following function signatures:
(Vector, Vector)
(Vector, Vector, Vector)
(Matrix)
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!
andlinkxaxes!
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)
, wherel
is left,r
right,b
bottom, andt
top.You can also do
ylims!(low, high)
orxlims!(low, high)
, and even open ones by doingylims!(low=0)
orxlims!(high=1)
.
Now, the example:
function complex_layout_double_axis()
seed!(123)
x = LinRange(0, 1, 10)
y = LinRange(0, 1, 10)
z = rand(10, 10)
fig = Figure(resolution=(600, 400), font="CMU Serif", backgroundcolor=:grey90)
ax1 = Axis(fig, xlabel=L"x", ylabel=L"y")
ax2 = Axis(fig, xlabel=L"x")
heatmap!(ax1, x, y, z; colorrange=(0, 1))
series!(ax2, abs.(z[1:4, :]); labels=["lab $i" for i = 1:4], color=:Set1_4)
hm = scatter!(10x, y; color=z[1, :], label="dots", colorrange=(0, 1))
hideydecorations!(ax2, ticks=false, grid=false)
linkyaxes!(ax1, ax2)
#layout
fig[1, 1] = ax1
fig[1, 2] = ax2
Label(fig[1, 1, TopLeft()], "(a)", fontsize=18, padding=(0, 6, 8, 0))
Label(fig[1, 2, TopLeft()], "(b)", fontsize=18, padding=(0, 6, 8, 0))
Colorbar(fig[2, 1:2], hm, label="colorbar", vertical=false, flipaxis=false)
Legend(fig[1, 3], ax2, "Legend")
colgap!(fig.layout, 5)
rowgap!(fig.layout, 5)
fig
end
JDS.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:
function squares_layout()
seed!(123)
letters = reshape(collect('a':'d'), (2, 2))
fig = Figure(resolution=(600, 400), fontsize=14, font="CMU Serif",
backgroundcolor=:grey90)
axs = [Axis(fig[i, j], aspect=DataAspect()) for i = 1:2, j = 1:2]
hms = [heatmap!(axs[i, j], randn(10, 10), colorrange=(-2, 2))
for i = 1:2, j = 1:2]
Colorbar(fig[1:2, 3], hms[1], label="colorbar")
[Label(fig[i, j, TopLeft()], "($(letters[i, j]))", fontsize=16,
padding=(-2, 0, -20, 0)) for i = 1:2, j = 1:2]
colgap!(fig.layout, 5)
rowgap!(fig.layout, 5)
fig
end
JDS.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.
using Dates
function mixed_mode_layout()
seed!(123)
longlabels = ["$(today() - Day(1))", "$(today())", "$(today() + Day(1))"]
fig = Figure(resolution=(600, 400), fontsize=12,
backgroundcolor=:grey90, font="CMU Serif")
ax1 = Axis(fig[1, 1], xlabel="x", alignmode=Mixed(bottom=0))
ax2 = Axis(fig[1, 2], xticklabelrotation=pi / 2, alignmode=Mixed(bottom=0),
xticks=([1, 5, 10], longlabels))
ax3 = Axis(fig[2, 1:2])
ax4 = Axis(fig[3, 1:2])
axs = [ax1, ax2, ax3, ax4]
[lines!(ax, 1:10, rand(10)) for ax in axs]
hidexdecorations!(ax3; ticks=false, grid=false)
Box(fig[2:3, 1:2, Right()], color=(:slateblue1, 0.35))
Label(fig[2:3, 1:2, Right()], "protrusion", rotation=pi / 2, fontsize=14,
padding=(3, 3, 3, 3))
Label(fig[1, 1:2, Top()], "Mixed alignmode", fontsize=16,
padding=(0, 0, 15, 0))
colsize!(fig.layout, 1, Auto(2))
rowsize!(fig.layout, 2, Auto(0.5))
rowsize!(fig.layout, 3, Auto(0.5))
rowgap!(fig.layout, 1, 15)
rowgap!(fig.layout, 2, 0)
colgap!(fig.layout, 5)
fig
end
JDS.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
:
function nested_sub_plot!(fig)
color = rand(RGBf)
ax1 = Axis(fig[1, 1], backgroundcolor=(color, 0.25))
ax2 = Axis(fig[1, 2], backgroundcolor=(color, 0.25))
ax3 = Axis(fig[2, 1:2], backgroundcolor=(color, 0.25))
ax4 = Axis(fig[1:2, 3], backgroundcolor=(color, 0.25))
return (ax1, ax2, ax3, ax4)
end
which, when used to build a more complex figure by doing several calls, we obtain:
function main_figure()
fig = Figure()
Axis(fig[1, 1])
nested_sub_plot!(fig[1, 2])
nested_sub_plot!(fig[1, 3])
nested_sub_plot!(fig[2, 1:3])
fig
end
JDS.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
:
function nested_Grid_Layouts()
fig = Figure(backgroundcolor=RGBf(0.96, 0.96, 0.96))
ga = fig[1, 1] = GridLayout()
gb = fig[1, 2] = GridLayout()
gc = fig[1, 3] = GridLayout()
gd = fig[2, 1:3] = GridLayout()
gA = Axis(ga[1, 1])
nested_sub_plot!(gb)
axsc = nested_sub_plot!(gc)
nested_sub_plot!(gd)
hidedecorations!.(axsc, grid=false, ticks=false)
colgap!(gc, 5)
rowgap!(gc, 5)
rowsize!(fig.layout, 2, Auto(0.5))
colsize!(fig.layout, 1, Auto(0.5))
fig
end
JDS.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:
function add_box_inset(fig; left=100, right=250, bottom=200, top=300,
bgcolor=:grey90)
inset_box = Axis(fig, bbox=BBox(left, right, bottom, top),
xticklabelsize=12, yticklabelsize=12, backgroundcolor=bgcolor)
# bring content upfront
translate!(inset_box.scene, 0, 0, 10)
return inset_box
end
Then, the inset
is easily done, as in:
function figure_box_inset()
fig = Figure(resolution=(600, 400))
ax = Axis(fig[1, 1], backgroundcolor=:white)
inset_ax1 = add_box_inset(fig; left=100, right=250, bottom=200, top=300,
bgcolor=:grey90)
inset_ax2 = add_box_inset(fig; left=500, right=580, bottom=100, top=200,
bgcolor=(:white, 0.65))
lines!(ax, 1:10)
lines!(inset_ax1, 1:10)
scatter!(inset_ax2, 1:10, color=:black)
fig
end
JDS.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:
function add_axis_inset(pos=fig[1, 1]; halign, valign, width=Relative(0.5),
height=Relative(0.35), alignmode=Mixed(left=5, right=5), bgcolor=:lightgray)
inset_box = Axis(pos; width, height, halign, valign, alignmode,
xticklabelsize=12, yticklabelsize=12, backgroundcolor=bgcolor)
# bring content upfront
translate!(inset_box.scene, 0, 0, 10)
return inset_box
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.
function figure_axis_inset()
fig = Figure(resolution=(600, 400))
ax = Axis(fig[1, 1], backgroundcolor=:white)
inset_ax1 = add_axis_inset(fig[1, 1]; halign=:left, valign=:center,
width=Relative(0.3), height=Relative(0.35),
alignmode=Mixed(left=5, right=5, bottom=15),
bgcolor=:grey90)
inset_ax2 = add_axis_inset(fig[1, 1]; halign=:right, valign=:center,
width=Relative(0.25), height=Relative(0.3), bgcolor=(:white, 0.65))
lines!(ax, 1:10)
lines!(inset_ax1, 1:10)
scatter!(inset_ax2, 1:10, color=:black)
fig
end
JDS.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
.
Support this project
CC BY-NC-SA 4.0 Jose Storopoli, Rik Huijzer, Lazaro Alonso