Overlay a contour over a heatmap

Hello,

I have difficulty overlaying a contour plot over a heatmap. Here is an example

let
    f(x,y) = (x + 2y^2) * abs(sin(y) + cos(x))
    p1 = heatmap(1:0.5:20, 1:0.5:20, f, alpha=0.3)
    p2 = Plots.contourf(1:0.5:20, 1:0.5:20, (x,y)->10f(x,y), 
         c=:black, levels=[100,101], colorbar=false)
    p3 = heatmap(1:0.5:20, 1:0.5:20, f, alpha=0.3)
    Plots.contour!(1:0.5:20, 1:0.5:20, (x,y)->10f(x,y), 
         c=:black, levels=[100,101], colorbar=false)
    plot(p1, p2, p3, layout =(1,3), size=(1000,350))
end

The outcome is

That is I tried to overlay the second plot over the first one, but with contour! I got the third one, where the color distribution in the heatmap is almost invisible. Is there a way to get a proper overlaid plot in this case?

A solution with Makie [sorry is not with Plots]

using CairoMakie
f(x,y) = (x + 2y^2) * abs(sin(y) + cos(x))
x = y = 1:0.5:20
z = [f(x,y) for x in x, y in y]
fig = Figure(resolution=(750,300), fontsize = 14)
ax1 = Axis(fig, aspect = 1, xlabel = "x", ylabel = "y")
ax2 = Axis(fig, aspect = 1,  xlabel = "x")
ax3 = Axis(fig, aspect = 1,  xlabel = "x")
p1 = heatmap!(ax1, x, y, z, colormap = :Spectral)
contour!(ax2, x, y, z, color = :black, levels = 100:1:101) # contourf! also works... but with colormap
heatmap!(ax3, x, y, z, colormap = (:Spectral, 0.3))
contour!(ax3, x, y, z, color = :black, levels = 100:1:101)
cbar = Colorbar(fig, p1, width = 10, hight = Relative(0.8), ticklabelsize = 10)
limits!(ax2, 0,20,0,20)
hideydecorations!(ax2, grid = false)
hideydecorations!(ax3, grid = false)
fig[1,1] = ax1
fig[1,2] = cbar
fig[1,3] = ax2
fig[1,4] = ax3
save("./results/FigMixHeatContour.png", fig, px_per_unit = 2)
fig

8 Likes

Thank you for your suggestion!
This indeed works. Is there a solution with Plots?

A different solution using Plots and Images, that someone more knowledgeable may be able to refine further.
PS: set dpi=300 and displayed f in both heatmap and contour

using Images, Plots; gr()
f(x,y) = (x + 2y^2) * abs(sin(y) + cos(x))
x = y = 1:0.2:20
p1 = heatmap(x, y, f, size=(700,700), xlims=(1,20), ylims=(1,20), colorbar=false, dpi=300)
savefig("p1.png")
p2 = contour(x,y,(x,y)->f(x,y), c=:black,levels=[100,101],size=(700,700),xlims=(1,20),ylims=(1,20),colorbar=false, dpi=300)
savefig("p2.png")
p1_rgb, p2_rgb = load("p1.png"),  load("p2.png")
plot(0.5*p1_rgb + 0.5*p2_rgb, ticks=nothing, border=:none, size=(700,700),dpi=300)

I would be interested in knowing how to obtain the RGB image array directly from Plots, without saving/loading images to disk.

The issue was that 10f, plotted with contour, silently changed the clim (of f plotted with heatmap). I think you can fix that with the clim kwarg, e.g.,

using Plots
f(x,y) = (x + 2y^2) * abs(sin(y) + cos(x))
x, y = 1:0.5:20, 1:0.5:20
p1 = heatmap(x, y, f, size=(700,700), α=0.5)
contour!(p1, x, y, (x,y)->10f(x,y), c=:black, levels=100:100, clim=(0,1500)) # note the clim kwarg

5 Likes

Thank you for your solution!

No worries! My pleasure :smiley:

PS: I’m also happy you asked, because we also got a Makie solution :slight_smile:

Although, I’m afraid one of them is wrong. The contour lines are different. For me it makes sense to have lines around the big jumps, because the values are lower. I’m not sure what’s happening in the Plots solution. Increasing the grid points it’s more clear.

using CairoMakie
f(x,y) = (x + 2y^2) * abs(sin(y) + cos(x))
x = y = 1:0.2:20
z = [f(x,y) for x in x, y in y]
fig = Figure(resolution=(750,300), fontsize = 14)
ax1 = Axis(fig, aspect = 1, xlabel = "x", ylabel = "y")
ax2 = Axis(fig, aspect = 1,  xlabel = "x")
ax3 = Axis(fig, aspect = 1,  xlabel = "x")
p1 = heatmap!(ax1, x, y, z, colormap = :plasma)
contour!(ax2, x, y, z, color = :black, levels = 100:1:101) # contourf! also works... but with colormap
heatmap!(ax3, x, y, z, colormap = (:plasma, 0.5))
contour!(ax3, x, y, z, color = :white, levels = 100:1:101)
cbar = Colorbar(fig, p1, width = 10, hight = Relative(0.8), ticklabelsize = 10, tickalign = 1)
limits!(ax2, 1,20,1,20)
hideydecorations!(ax2, grid = false)
hideydecorations!(ax3, grid = false)
fig[1,1] = ax1
fig[1,2] = cbar
fig[1,3] = ax2
fig[1,4] = ax3
#save("./results/FigMixHeatContour.png", fig, px_per_unit = 2)
fig

2 Likes

@lazarusA, the problem is that original post heatmaps f but contours 10*f, if we fix this to display f only, results are similar.

ahh. :grinning_face_with_smiling_eyes: yes, ok then everything is fine.

Yes, I put this factor of 10 in purpose to expose the issue.

@fkguo, because it was not exposed enough without it?

sorry for the confusion. I meant to expose the issue that the color in the heatmap would be destroyed by the contour! if the values of the contoured function are much larger.
As pointed out in the Plots solution, the large values of the contoured function change clim so that the pattern in heatmap becomes invisible.

@fkguo, it is clear, thank you.