How to add contours to a multi surface plot

I’m plotting a multi surfaces plot referring to this.
I wish to add one contour on each surface. According to the error message before:

‘contours’ is a valid attribute to ‘surface’ object.

I tried use contours=go.Contour(z=[1.0]) but it gives me:


But I didn’t include “type” when making my contour…

Then I tried contours=[1.0]:

I checked the webpage and I think I’m doing the correct thing. I don’t know what’s my problem…Could someone help?

Thank you in advance!

1 Like

@CEiffel From your question it is not clear if you want to plot contour lines on your surface or the contour plot. Here https://plot.ly/~empet/14521 are given examples on how to define data for both plots.

Hi, thank you so much for your help!

I tried your method, but the contours still doesn’t show up on my surface…I read the documentation yet I still couldn’t get it to work.

https://plot.ly/~YuanzeEiffelLuo/21/the-following-cell-is-just-reading-the-f/ Could you please have a look at my notebook and see where the problem might be?

Thanks again!

My filling is that your contours are not computed, because your surface is not defined as z=f(x,y). Your surface is given only by its z-values. Theoretically the algorithm computing the countour lines on a surface solves the system z=f(x,y), z=cst to get their equation or you don’t have any function of x,y.

Try the following trick: if haf is a numpy array of shape (m,n) choose some intervals for x, and y, such that to interpret your surface as being of equation z=haf(x,y), x in [a,b], y in [c,d]

x=np.linspace(a, b,  n)
y=np.linspace(c,d, m)
x,y=np.meshgrid(x,y)

and define:

surf2=dict(x=x, y=y, z=haf, 
       showscale=True, 
       opacity=0.8, showlegend=True, type='surface', 
       cauto=False, 
       colorscale=col,   
       name='H-alpha', 
       reversescale=False, colorbar=go.ColorBar(x=-0.84),
       contours=dict(z=dict(show=True,
                                        project=dict(z=True),
                                        color='rgb(50,50,50)')))

Tried but still doesn’t work…Looks like z has to be a function with exact formula to for plotly to generate the contour lines.
I guess I’ll have to adjust the colorscale to achieve my goal…
But your help is still invaluable!
Thank you very much!

No, the explicit function is not necessary, because we are working with discrete functions. Plotly is not aware of my function z=f(x,y). It works on the numpy arrays x, y and z I defined in the previous message. Something in your code is missing or data are not in a right format or shape.

Hum…I’ve got stuck on this for many days. I checked many times but couldn’t find the error…if it’s because of the original date then I think I won’t be able to do much about it. You remind me of two of my other questions:

  1. how to set the z values where I want to plot contours?
    contours=dict(z=dict(show=True, project=dict(z=True), color='rgb(50,50,50)'), z=[1.0,2.0]) ? put in z values as a list?
  2. if it is possible to pay for the plotly student plan just for a month, rather than subscribing it for a whole year?

Thank you so much!

  1. z is a numpy array of shape(m,n), or a list of equal length lists: z=[[0.8, 1.3, 1], [1.55, 0.93, 1.3], [1.14, 0.8, 1.5]]

Inspecting again your Jupyter notebook I noticed that you defined a layout as for a 2d plot or the surface is in the 3d space. That is why the surfaces are not displayed.
You should change the layout as follows:

axis = dict(showbackground=True, 
            backgroundcolor="rgb(230, 230,230)",
            gridcolor="rgb(255, 255, 255)",      
            zerolinecolor="rgb(255, 255, 255)",  
           )

layout=dict(title='My title'
            autosize=False,
            width=700,
            height=700,# since you  are plooting 3 surface increase height to 900 or 1000
            scene=dict(xaxis=axis,
                              yaxis=axis,  
                              zaxis=dict(axis),
                              aspectratio=dict(x=1, y=1, z=0.5)#z in [0,1]. As z in bigger the plotted surface is taller
         ))

Thanks! This does help to make the axis information show up in the graph.
However I don’t quite understand why should we put the z value for contours as an numpy array or list of the same size. I thought we only need to input the values of z at which we want to plot contour. If I only want one contour at one z value, wouldn’t we just put one value?
I’m confused =_=…Thanks again!!!

I’ve got the same question.

From this multi-surface plot:

fig_data = [
    go.Surface(contours=dict(), x=x, y=y, z=z,
               showscale=False,
               colorscale=colorscales[i],
               name='ID = {}'.format(i + 1))
    for i, id in enumerate(data)]

fig = go.Figure(data=fig_data)

I have this Bottom view:

I would like to take this contours plot (that was generated separetly):

fig = go.Figure(data=
go.Contour(
    z=min_costs, x=x, y=y,
    contours_coloring='lines',
    opacity=1,
    line_width=5,
    contours=dict(
        coloring='heatmap',
        showlabels=True,  # show labels on contours
        labelfont=dict(  # label font properties
            size=20,
            color='black',
        )
    )
))

To make something like this:
image

Any idea @empet @CEiffel ?

@matheusft

To plot the contour lines that are not projections of the contours lines on the surface, proceed as follows:

  • set scene_zaxis_range=[a, b]; The surface projection is then plotted onto the plane z=a.
  • your lines are defined as go.Scatter (x=[], y=[], mode='lines', ....). Add them to your figure as follows:
    fig.add_scatter3d(x=[], y=[], z=a+epsilon, mode='lines, .....), where epsilon is chosen by trial and error, such that to ensure visibility of the lines (epsilon=0.02, or 0.025, etc)

Hi, @empet

Thank you for your response.
I tried what you suggested and the contour lines are not being displayed whatsoever.
Running the code below (link to data) I get this:

Any idea how to plot the the z_contours contours on both sides of the surface?

link to data

import plotly.graph_objects as go
import numpy as np
import pickle


x,y,z,z_contours = pickle.load( open( "data.pkl", "rb" ) )

fig_data = [
    go.Surface(contours=dict(),
               x=x,
               y=y,
               z=z,
               showscale=False)]

fig = go.Figure(data=fig_data)

fig.update_layout(title="Title",
                  scene=dict(
                      zaxis=dict(visible=True, range=[0, z.max(axis=0).max()]),
                      xaxis_title='Acc',
                      yaxis_title='Range',
                      zaxis_title='Cost'))

fig.add_scatter3d(x=[], y=[], z=(z_contours + 0.025), mode='lines')

fig.update_traces(showlegend=True)

fig.show()



@matheusft

Initially you haven’t given sufficient information on your already recorded contours. I understood that they are planar lines. From your data above it seems that they are spatial. Hence you have to project them onto the plane
z=a+epsilon, i.e. to leave out their z-coordinates, and assign to the third coordinate, z=a+epsilon.

Mathematically, a point of coordinates (x0, y0, z0), projects orthogonally onto the plane z=a+epsilon, into the point (x0, y0, a+epsilon).

Sincerely I’m not sure if you want to project those contour lines or to plot them on the surface. You gave only the plots, and it’s difficult to deduce just looking at these charts, what you really want.

Hi @empet

Thank you for your help.
I see, I’m sorry about that.

You do not need to deduce whether I want to project those contour lines or to plot them on the surface. As I wrote above: “Any idea how to plot the the z_contours contours on both sides of the surface?”.

I want to plot the contour lines (z_contours from the code above which is a 28x28 numpy array) on both sides of the surface, not a projection on the X-Y Plane.

With your help I managed to achieve this:


by using

fig.add_scatter3d()

on my Surface as you suggested.
It is getting close to what I desire but it is not there yet.
The add_scatter3d is only drawing lines between the x,y,z coordinates passed.

@matheusft
To plot the contours under surface, too,
add one more scatter3d with z=z_contours-epsilon.
But it seems that add_scatter3d is not plotting what you expected, because your last sentence says “…is only drawing lines between…”.