Makie contours with NaN data: contour labels are on top of lines

Hello. I’ve recently switched from Plots.jl to Makie.jl and am having a problem with contour labels. My application is oceanographic, and my goal is to plot contours of ocean temperature versus a horizontal coordinate on the x axis and a depth coordinate on the y axis. Since the data come from a ship that cannot go through the ocean bottom, the data are NaN for those depth regions that are below the water. I find that the contour labels run on top of the contour lines.

I’m using Julia 1.12.7, with the following package versions.

  • GLMakie v0.13.15
  • Makie v0.24.15

I tried to make a simplified test case, as shown below, along with the plotted output. The left-hand panel, without the “bottom”, has well-labelled contours. However, the right-hand panel, with that “bottom” inserted, has labels that are on top of the contour lines.

Question: am I doing something wrong? (I am aware that I can get the results with Plots but I really am hoping to switch my work entirely to. Makie, because this lets me do some other types of plots that are needed in oceanographic work.)

Many thanks in advance.
Dan Kelley / Oceanography / Dalhousie University

# Demonstrate a problem with contour labelling data with NaN values
# Top panel: contoured field, with proper contour labels
# Bottom panel: contoured field (now with some NaN values), with improper contour labels
using GLMakie
f(x, y) = abs(sin(x) + cos(y))
bad(x, y) = (x - 2) > y
x = range(0, 5, length=50);
y = range(0, 5, length=50);
z = @. f(x', y);
isbad = @. bad(x', y);
zz = copy(z)
zz[isbad] .= NaN
fig = Figure()
contour(fig[1, 1], x, y, z', color=:black, labels=true)
contour(fig[1, 2], x, y, zz', color=:black, labels=true)
save("contour_bug.png", fig)

Something to do with how Makie handles NaNs

# Run with: julia contour_nan_workaround.jl
# For a headless/static check: julia contour_nan_workaround.jl --cairo
if "--cairo" in ARGS
    @eval using CairoMakie
else
    @eval using GLMakie
end

"""
Draw contours with opaque backgrounds behind their labels.

This preserves Makie's label positions and rotations. It visually covers the
lines beneath the labels; it does not cut the underlying contour geometry.
Requires a Makie version providing `textlabel!`. Access to the contour's Text
child is an internal API and may need adjustment in future Makie versions.
"""
function contour_with_label_background!(ax, x, y, z;
        background=:white, padding=2, kwargs...)
    cp = contour!(ax, x, y, z; labels=true, kwargs...)

    # Reuse the labels Makie has already positioned along the contours.
    t = only(filter(p -> p isa Makie.Text, cp.plots))
    t.visible = false
    textlabel!(ax, t[1];
        text=t.text,
        text_rotation=t.rotation,
        fontsize=t.fontsize,
        font=t.font,
        text_color=t.color,
        background_color=background,
        strokewidth=0,
        padding=padding,
        cornerradius=0,
        draw_on_top=true,
    )
    return cp
end

f(x, y) = abs(sin(x) + cos(y))
bad(x, y) = (x - 2) > y
x = range(0, 5, length=50)
y = range(0, 5, length=50)
z = @. f(x', y)
isbad = @. bad(x', y)
zz = copy(z)
zz[isbad] .= NaN

# Use identical levels so masking does not change the panel comparison.
lo, hi = extrema(z)
levels = collect(range(lo, hi; length=7))[2:end-1]

fig = Figure(size=(1000, 500))
ax1 = Axis(fig[1, 1]; title="Complete data", xlabel="x", ylabel="y")
ax2 = Axis(fig[1, 2]; title="NaN mask — label backgrounds", xlabel="x", ylabel="y")
contour!(ax1, x, y, z'; levels=levels, color=:black, labels=true)
contour_with_label_background!(ax2, x, y, zz'; levels=levels, color=:black)

output = joinpath(@__DIR__, "contour_nan_workaround.png")
save(output, fig)
println("Saved ", output)

This is lovely! Thanks very much. Dan

I agree, a beautiful workaround by @technocrat. I’ll bet part of the reason that you got a nice response is that you provided a great, self-contained, compact MWE (minimal working example). If only everyone did this!

I would like to suggest that you open an issue on the Makie repo where you provide this MWE. It’s clearly a bug.

@PeterSimon 100% agree. A solution that requires reverse engineering the problem is far less likely to be attempted than one that arrives with an MWE as good as the one that @dankelley provided.

Thanks. I’ve made an issue (at contour(x,y,z) retains contour lines under the labels if z has NaN values · Issue #5811 · MakieOrg/Makie.jl · GitHub)

Apparently Makie has been altered and the bug no longer appears (see contour(x,y,z) retains contour lines under the labels if z has NaN values · Issue #5811 · MakieOrg/Makie.jl · GitHub), although I’m not too sure how to update to the new version, since ] update doesn’t do it for me.

I suppose I could clone Makie and build it locally, but I’m after a solution that can work for folks who don’t want to do that. Therefore, my plan is to wait until the official (Julia repository) version of Makie is updated.

No need to wait. Just do, e.g.,

] add CairoMakie#master

until a new release is tagged.

Oh, I hadn’t realized that. I did it for CairoMakie, GLMakie and Makie, and now my test code works fine. Thanks so much! Dan.