6.5 Colors and Colormaps

Choosing an appropiate set of colors or colorbar for your plot is an essential part when presenting results. Using Colors.jl is supported in Makie.jl so that you can use named colors or pass RGB or RGBA values. Regarding colormaps, all those that work with Plots.jl also do here. Additionally, colormaps from ColorSchemes.jl and PerceptualColourMaps.jl can also be used. It is worth knowing that you can reverse a colormap by doing Reverse(:colormap_name) and obtain a transparent color or colormap with color= (:red,0.5) and colormap = (:viridis, 0.5).

Different use cases will be shown next. Then we will difine a custom theme with new colors and a colorbar palette.

By default Makie.jl has a predefined set of colors in order to cycle trough them automatically. As shown in the previous figures, where no specific color was set. Overwriting these defaults is done by calling the keyword color in the plotting function and specifying a new color via a Symbol or String. See this in action in the following example:

function set_colors_and_cycle()
    # Epicycloid lines
    x(r,k,θ) = r*(k .+ 1.0).*cos.(θ) .- r*cos.((k .+ 1.0) .* θ)
    y(r,k,θ) = r*(k .+ 1.0).*sin.(θ) .- r*sin.((k .+ 1.0) .* θ)
    θ = LinRange(0,6.2π,1000)

    axis = (; xlabel = L"x(\theta)", ylabel = L"y(\theta)",
        title = "Epicycloid", aspect= DataAspect())
    figure = (;resolution = (500,400), font= "CMU Serif")

    fig, ax, _ = lines(x(1,1,θ), y(1,1,θ); color = "firebrick1", # string
        label = L"1.0", axis = axis, figure = figure)
    lines!(ax, x(4,2,θ), y(4,2,θ); color = :royalblue1, #symbol
        label = L"2.0")
    for k in 2.5:0.5:5.5
        lines!(ax, x(2k,k,θ), y(2k,k,θ); label = latexstring("$(k)")) #cycle
    end
    Legend(fig[1, 2], ax, latexstring("k, r = 2k"), merge = true)
    fig
end
set_colors_and_cycle()
Figure 21: Set colors and cycle.

Where, in the first two lines we have used the keyword color to specify our color. The rest is using the default cycle set of colors. Later, we will learn how to do a custom cycle.

Regarding colormaps, we are already familiar with the keyword colormap for heatmaps and scatters. Here, we show that a colormap can also be specified via a Symbol or a String, similar to colors. Or, even a vector of RGB colors. Let’s do our first an example by calling colormaps as a Symbol, String and cgrad for categorical values. See ?cgrad for more information.

figure = (;resolution = (400,300), font= "CMU Serif")
axis = (; xlabel = L"x", ylabel = L"y", aspect= DataAspect())

fig, ax, pltobj = heatmap(rand(20,20); colorrange = (0,1),
    colormap = Reverse(:viridis), axis = axis, figure = figure)
Colorbar(fig[1,2], pltobj, label = "Reverse colormap Sequential")
fig
Figure 22: Reverse colormap sequential and colorrange.

When setting a colorrange usually the values outside this range are colored with the first and last color from the colormap. However, sometimes is better to specify the color you want at both ends. We do that with highclip and lowclip:

using ColorSchemes
figure = (;resolution = (400,300), font= "CMU Serif")
axis = (; xlabel = L"x", ylabel = L"y", aspect= DataAspect())

fig, ax, pltobj = heatmap(randn(20,20); colorrange = (-2,2),
    colormap = "diverging_rainbow_bgymr_45_85_c67_n256",
    highclip = :black, lowclip = :white, axis = axis, figure = figure)
Colorbar(fig[1,2], pltobj, label = "Diverging colormap")
fig
Figure 23: Diverging Colormap with low and high clip.

But we mentioned that also RGB vectors are valid options. For our next example you could pass the custom colormap perse or use cgrad to force a categorical Colorbar.

using Colors, ColorSchemes
figure = (;resolution = (400,300), font= "CMU Serif")
axis = (; xlabel = L"x", ylabel = L"y", aspect= DataAspect())
cmap = ColorScheme(range(colorant"red", colorant"green", length=3))
mygrays = ColorScheme([RGB{Float64}(i, i, i) for i in [0.0,0.5,1.0]])

fig, ax, pltobj = heatmap(rand(-1:1,20,20);
    colormap = cgrad(mygrays, 3, categorical = true, rev = true), # cgrad and Symbol, mygrays,
    axis = axis, figure = figure)
cbar = Colorbar(fig[1,2], pltobj, label = "Categories")
cbar.ticks = ([-0.66,0,0.66], ["-1","0","1"])
fig
Figure 24: Categorical Colormap.

Lastly, the ticks in the colorbar for the categorial case are not centered by default in each color. This is fixed by passing custom ticks, as in cbar.ticks = (positions, ticks). The last situation is when passing a tuple of two colors to colormap as symbols, strings or a mix. You will get an interpolated colormap between these two colors.

Also, hex coded colors are also accepted. So, on top or our heatmap let’s put one semi-transparent point using this.

figure = (;resolution = (400,300), font= "CMU Serif")
axis = (; xlabel = L"x", ylabel = L"y", aspect= DataAspect())

fig, ax, pltobj = heatmap(rand(20,20); colorrange = (0,1),
    colormap = (:red, "black"), axis = axis, figure = figure)
scatter!(ax,[11],[11],color=("#C0C0C0", 0.5),markersize=150)
Colorbar(fig[1,2], pltobj, label = "2 colors")
fig
Figure 25: Colormap from two colors.

6.5.1 Custom cycle

Here, we could define a global Theme with a new cycle for colors, however that is not the recommend way to do it. It’s better to define a new theme and use as shown before. Lets define a new one with a cycle for :color, :linestyle, :marker and a new colormap default. Lets add this new attributes to our previous publication_theme.

function new_cycle_theme()
    # https://nanx.me/ggsci/reference/pal_locuszoom.html
    my_colors = ["#D43F3AFF", "#EEA236FF", "#5CB85CFF", "#46B8DAFF",
        "#357EBDFF", "#9632B8FF", "#B8B8B8FF"]
    cycle = Cycle([:color, :linestyle, :marker], covary = true) # alltogether
    my_markers = [:circle, :rect, :utriangle, :dtriangle, :diamond,
        :pentagon, :cross, :xcross]
    my_linestyle = [nothing, :dash, :dot, :dashdot, :dashdotdot]

    Theme(
        fontsize = 16, font="CMU Serif",
        colormap = :linear_bmy_10_95_c78_n256,
        palette=(color=my_colors,marker=my_markers,linestyle=my_linestyle),
        Lines = (cycle = cycle,), Scatter = (cycle = cycle,),

        Axis = (xlabelsize= 20,xgridstyle=:dash,ygridstyle=:dash,
            xtickalign = 1, ytickalign=1,yticksize=10, xticksize=10,
            xlabelpadding = -5, xlabel = "x", ylabel = "y"),
        Legend = (framecolor = (:black, 0.5), bgcolor = (:white, 0.5)),
        Colorbar = (ticksize=16, tickalign = 1, spinewidth = 0.5)
    )
end

And apply it to a plotting function like the following:

function scatters_and_lines()
    x = collect(0:10)
    xh = LinRange(4,6,25)
    yh = LinRange(70,95,25)
    h = randn(25,25)

    fig = Figure(resolution = (650,400), font="CMU Serif")
    ax = Axis(fig[1,1], xlabel = L"x", ylabel = L"f(x,a)")
    for i in x
        lines!(ax, x, i .* x; label = latexstring("$(i) x"))
        scatter!(ax, x, i .* x; markersize = 13, strokewidth = 0.25,
            label = latexstring("$(i) x"))
    end
    hm = heatmap!(xh, yh, h)
    axislegend(L"f(x)"; merge = true,position = :lt,nbanks=2,labelsize=14)
    Colorbar(fig[1,2], hm, label = "new default colormap")
    limits!(ax,-0.5,10.5,-5,105)
    colgap!(fig.layout, 5)
    fig
end
with_theme(scatters_and_lines, new_cycle_theme())
Figure 26: Custom theme with new cycle and colormap.

At this point you should be able to have complete control over your colors, line styles, markers and colormaps for your plots. Next, we will dive into how to manage and control layouts.



CC BY-NC-SA 4.0 Jose Storopoli, Rik Huijzer and Lazaro Alonso