# Plotlyjs and composite figures

**URL:** <https://discourse.julialang.org/t/plotlyjs-and-composite-figures/97320>\
**Category:** General Usage\
**Tags:** plots, plotlyjs\
**Created:** [April 10, 2023, 9:35pm UTC](https://discourse.julialang.org/t/plotlyjs-and-composite-figures/97320 "2023-04-10T21:35:50Z")\
**Posts on this page:** 9\
**Page:** 1

<div class="post-metadata">

**Author:** ![Gravlax](https://avatars.discourse-cdn.com/v4/letter/g/dbc845/32.png) [@Gravlax](https://discourse.julialang.org/u/Gravlax)\
**Post date:** [April 10, 2023, 9:35pm UTC](https://discourse.julialang.org/t/plotlyjs-and-composite-figures/97320/1 "2023-04-10T21:35:50Z")

</div>

Hi all,

I’m trying to create composite figures which involve a `heatmap` as well as an overlying `contour`. I’d like to control the aspect ratio, the contour thickness and the colorbars. I chose `plotlyjs` backend of `Plots` because it’s light and it allows to hover the mouse and pick the data values directly, which I find extremely useful.

However, I have some trouble controlling the appearance of the figure, in particular:

- the aspect ratio does not appear correct. Even with aspect\_ratio=1.0, there is still a vertical exagerration (the circular contour appears as an ellipse).
- modifying the contour `linewidth` has no effect
- once two data sets are used (one for `heatmap`, one for `contour`), the colorbar somehow merges the values of the two datasets. I have tried setting `colorbar=false` for the contour plot but this had no effect.
- sometimes the contour does not appear on top of the heatmap

I have pasted a MWE below. Does anyone see obvious misuses? Is this an expected behaviour? Is there another (light) way to make such type of figures with Julia?

Cheers!

```julia
using Plots
plotlyjs()

function main()
    # Mesh
    x = LinRange(-2.0, 2.0, 100)
    y = LinRange(-1.0, 1.0, 100)
    # One continuous field 
    f = 1e3.*exp.(-x.^2 .- (y').^2)
    # One discontinuous field 
    g = zero(f)
    g[(x.^2 .+ (y').^2) .< 0.5^2] .= 1.0
    # Compose figure
    p = plot()
    p = heatmap!(x, y, f')
    p = contour!(x, y, g', c=:black, linewidth=0.1) # contour does not appear on top of heatmap, colorbar is messed up, linewidth fails
    p = plot!(aspect_ratio=1) # aspect ratio fails: does not seem 1.0 on my screen
    display(p)
end

main()

```

---

<div class="post-metadata">

**Author:** ![empet](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/empet/32/221303_2.png) [@empet](https://discourse.julialang.org/u/empet)\
**Post date:** [April 11, 2023, 5:31pm UTC](https://discourse.julialang.org/t/plotlyjs-and-composite-figures/97320/2 "2023-04-11T17:31:39Z")

</div>

I post here the code in pure PlotlyJS, to adapt it to Plots with plotlyjs():

```julia
using PlotlyJS
x = range(-2.0, 2.0, length=200)
y = range(-1.0, 1.0, length=100)
# One continuous field 
f = 1e3.*exp.(-x'.^2 .- y.^2)
t= range(0, 2pi, length=100)
p = Plot([heatmap(x=x, y=y, z=f, colorscale=colors.plasma,colorbar_thickness=24), 
        scatter(x=0.5*cos.(t), y=0.5*sin.(t), mode="lines",
                line_width=2, line_color="black")],
        Layout(width=600, height=320,
               xaxis_range=[-2,2], yaxis_range=[-1,1]))
display(p)

```

![heatmap-scatter](https://global.discourse-cdn.com/julialang/original/3X/a/b/ab0d36634e0072231202bee3880616e9e8be7815.png)

---

<div class="post-metadata">

**Author:** ![Gravlax](https://avatars.discourse-cdn.com/v4/letter/g/dbc845/32.png) [@Gravlax](https://discourse.julialang.org/u/Gravlax)\
**Post date:** [April 11, 2023, 8:03pm UTC](https://discourse.julialang.org/t/plotlyjs-and-composite-figures/97320/3 "2023-04-11T20:03:58Z")

</div>

Oh, thanks a lot, this looks great!  
The non fully functional / non working features as Plots’ backend are fully functional.  
I had no clue that there would be such a difference!

---

<div class="post-metadata">

**Author:** ![rafael.guerra](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rafael.guerra/32/216610_2.png) [@rafael.guerra](https://discourse.julialang.org/u/rafael.guerra)\
**Post date:** [April 11, 2023, 10:37pm UTC](https://discourse.julialang.org/t/plotlyjs-and-composite-figures/97320/4 "2023-04-11T22:37:02Z")

</div>

Your original code seemed more difficult, as it involved adding contours over a heatmap.

To draw a circle on top of a heatmap, you can use Plots.jl, `plotlyjs()`:

```julia
heatmap(x, y, f', ratio=1, xlims=(-2,2), ylims=(-1,1))
plot!(Plots.partialcircle(0, 2π, 100, 0.5), c=:black)

```

There is interactivity and the coordinates and data values can be read but, as in the example above, the circle looks elliptical to me:

 ![Plots_plotlyjs_heatmat_with_circle](https://global.discourse-cdn.com/julialang/original/3X/5/6/565c0a1885d3200e907313a1c27a8cb29f34f6b4.png)

**NB:**  
The `pythonplot()` backend displays a perfect circle but does not have the same data reading features.

---

<div class="post-metadata">

**Author:** ![Gravlax](https://avatars.discourse-cdn.com/v4/letter/g/dbc845/32.png) [@Gravlax](https://discourse.julialang.org/u/Gravlax)\
**Post date:** [April 12, 2023, 6:04am UTC](https://discourse.julialang.org/t/plotlyjs-and-composite-figures/97320/5 "2023-04-12T06:04:19Z")

</div>

Thanks a lot! In practice, I’d like to draw contours of arbitrary shapes. Here it just happened to be a circle for the purpose of the MWE.  
I will try to port the full PlotlyJS example into my visualisation code, then I will let you know if it is a good solution to my problems.

---

<div class="post-metadata">

**Author:** ![jd-foster](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jd-foster/32/35824_2.png) [@jd-foster](https://discourse.julialang.org/u/jd-foster)\
**Post date:** [April 12, 2023, 6:36am UTC](https://discourse.julialang.org/t/plotlyjs-and-composite-figures/97320/6 "2023-04-12T06:36:47Z")

</div>

> [@rafael.guerra](#):
>
> as in the example above, the circle looks elliptical to me

In `PlotlyJS`, the relevant attributes (see [Single-page reference in Julia](https://plotly.com/julia/reference/)) for fixing the aspect ratio are `scaleanchor`, `scaleratio` and (not allowed simultaneously with these) `matches`:

```julia
p = Plot([heatmap(x=x, y=y, z=f, colorscale=colors.plasma,colorbar_thickness=24), 
               scatter(x=0.5*cos.(t), y=0.5*sin.(t), mode="lines",
                       line_width=2, line_color="black")],
               Layout(width=600, height=320, yaxis_scaleanchor="x", yaxis_scaleratio=1,
                      xaxis_range=[-2,2], yaxis_range=[-1,1]))

```

 ![ex](https://global.discourse-cdn.com/julialang/original/3X/8/2/82ed378a6807307ec7b5196110a1d1227a8705d5.png)

---

<div class="post-metadata">

**Author:** ![Gravlax](https://avatars.discourse-cdn.com/v4/letter/g/dbc845/32.png) [@Gravlax](https://discourse.julialang.org/u/Gravlax)\
**Post date:** [April 12, 2023, 9:33pm UTC](https://discourse.julialang.org/t/plotlyjs-and-composite-figures/97320/7 "2023-04-12T21:33:41Z")

</div>

Thanks for the help! I’ve tried adapting the MWE. I would like to use `contour` instead of `scatter` (for the purpose of contouring another field than the one represented with a `heatmap`).  
It seems that adding a contour (the 0/1 boundary) erases the underlying heatmap and overprints the colorbar.  
Would there be a way to draw the contour line on top of the heatmap and not to link the contour to the colorbar?  
Also, while forcing the aspect ratio to avoid vertical exagerration, is there a way to crop out the additional grey space out of the xaxis\_range/yaxis-range?

 ![image](https://global.discourse-cdn.com/julialang/original/3X/a/3/a319ddd32d8edff2dc5c11a5c34fe8c102ec0620.png)

```julia
using PlotlyJS
# Mesh
x = LinRange(-2.0, 2.0, 200)
y = LinRange(-1.0, 1.0, 100)
Lx, Ly = x[end]-x[1], y[end]-y[1] # Added after edit
# One continuous field 
f = 1e3.*exp.(-x.^2 .- (y').^2)
# One discontinuous field 
g = zero(f)
g[(x.^2 .+ (y.^2)') .< 0.5^2] .= 1.0
# Compose figure
p = Plot([heatmap(x=x, y=y, z=f', colorscale=colors.plasma, colorbar_thickness=24), 
contour(x=x, y=y, z=g', mode="lines",
                line_width=2, colorscale=[[1, "rgb(255,255,255)"]])],
Layout(width=600, height=600*Ly/Lx,
                xaxis_range=[-2,2], yaxis_range=[-1,1], yaxis_scaleanchor="x", yaxis_scaleratio=1))
display(p)

```

EDIT: the contour overlying a heatmap issue is also discussed [there](https://discourse.julialang.org/t/overlay-a-contour-over-a-heatmap/53586/2). I have just tested the proposed Plots solution. It involves using clim to rescale the colobar while drawing the contour. This work with gr() but does not seem to work with plotlyJS().

EDIT: add transpose of in the heatmap and defined Lx and Ly for aspect ratio.

---

<div class="post-metadata">

**Author:** ![disberd](https://avatars.discourse-cdn.com/v4/letter/d/8edcca/32.png) [@disberd](https://discourse.julialang.org/u/disberd)\
**Post date:** [April 13, 2023, 9:11am UTC](https://discourse.julialang.org/t/plotlyjs-and-composite-figures/97320/8 "2023-04-13T09:11:27Z")

</div>

I can’t reproduce your exact code as `Lx` and `Ly` are not defined in the pasted code you gave so an error is thrown.  
You should be able to to remove the fill of the contour zones and have just the coloring of the contour lines controlled by the `line_color` attribute by adding to your countour call the following kwarg: `contours_coloring="none"`.  
So your contour call above would be:

```julia
contour(x=x, y=y, z=g', mode="lines", line_width=2, contours_coloring="none")

```

Note: I removed the colorscale to contour as if you set `contours_coloring` to `"none"` no colorbar is generated and the color of the line of the contour is not controlled by the colorscale but by `line_color`.

Edit: I just noted you also asked how to crop out the extra space in the axis. You can do this by changing the `constrain` parameter of the `xaxis` inside `Layout` to `"domain"`.

Here is an example in Pluto:

 ![image](https://global.discourse-cdn.com/julialang/original/3X/3/6/36c6b468fc8a07b7fb94f59b6a6903acbfaeca9d.png)

And here is the code modified to use PlotlyJS as you do instead of PlutoPlotly.

```julia
using PlotlyJS
# Mesh
x = LinRange(-2.0, 2.0, 200)
y = LinRange(-1.0, 1.0, 100)
# One continuous field 
f = 1e3.*exp.(-x.^2 .- (y').^2)
# One discontinuous field 
g = zero(f)
g[(x.^2 .+ (y.^2)') .< 0.5^2] .= 1.0
# Compose figure
p = Plot([
	heatmap(x=x, y=y, z=f, colorscale=colors.plasma, colorbar_thickness=24), 
	contour(x=x, y=y, z=g', mode="lines", line_width=1, contours_coloring="none")],
	Layout(width=600, height=320,
		xaxis = attr(;
			range = [-2,2],
			constrain = "domain",
		),
		yaxis = attr(;
			range = [-1,1],
			scaleanchor = "x",
			scaleratio = 1,
		)
	))
display(p)

```

---

<div class="post-metadata">

**Author:** ![Gravlax](https://avatars.discourse-cdn.com/v4/letter/g/dbc845/32.png) [@Gravlax](https://discourse.julialang.org/u/Gravlax)\
**Post date:** [April 13, 2023, 10:32am UTC](https://discourse.julialang.org/t/plotlyjs-and-composite-figures/97320/9 "2023-04-13T10:32:11Z")

</div>

wow, thanks a lot. This is now great!  
I’ve added your suggestions. One more thing, I added the heatmap back on top of the contour, such that mouse hovering will pick up heatmap values instead of that of the contoured field (old matlab inherited trick…).

 ![image](https://global.discourse-cdn.com/julialang/original/3X/6/6/6640374c861689274a03fffd4c60032a93d7a5ef.jpeg)

Here’s the complete code:

```julia
using PlotlyJS

function main_PlotlyJS()
    # Mesh
    x = LinRange(-2.0, 2.0, 200)
    y = LinRange(-1.0, 1.0, 100)
    Lx, Ly = x[end]-x[1], y[end]-y[1]
    # One continuous field 
    f = 1e3.*exp.(-x.^2 .- (y').^2)
    # One discontinuous field 
    g = zero(f)
    g[(x.^2 .+ (y.^2)') .< 0.5^2] .= 1.0
    # Compose figure
    p = Plot([heatmap(x=x, y=y, z=f', colorscale=colors.plasma, colorbar_thickness=24), 
    contour(x=x, y=y, z=g', mode="lines",
                    line_width=2, colorscale=[[1, "rgb(255,255,255)"]], contours_coloring="none"),
                    heatmap(x=x, y=y, z=f', colorscale=colors.plasma, colorbar_thickness=24)],
    Layout(width=600, height=600*Ly/Lx,
    xaxis = attr(;
    range = [-2,2],
    constrain = "domain",
    ),
    yaxis = attr(;
    range = [-1,1],
    scaleanchor = "x",
    scaleratio = 1,
    )
    ))
    display(p)
end

main_PlotlyJS()

```

I will try to repeat it by using Plots with PlotlyJS backend. Should be doable if `contours_coloring` is also exposed there… Many thanks again!
