Search code examples
pythonmatplotlibplotlyfigure

Save 3D Matplot as interactive HTML


I´m trying to save my 3D trisurface Plot as an interactive HTML figure, so it should be possible to zoom in/ out and change the viewpoint. In the IDE the plot already exists and works so far, but I can`t save it in the HTML format because of the ValueError: "The fig parameter must be a dict or Figure. Received value of type <class 'matplotlib.figure.Figure'>: Figure(1600x900)". I don´t understand why the "<class 'matplotlib.figure.Figure'>" is not a Figure?

This was my approach: https://plotly.com/python/interactive-html-export/ And I tried it with go.Figure() (Export rotable 3D plots from Python to HTML) already but it didn´t work with the trisurf.

Is there a way to keep my Plot settings (use trisurf as it is) and get the interactive figure in HTML?

Thanks a lot for any answer

#Import libraries 
import matplotlib
#matplotlib.use('Agg')
import matplotlib.pyplot as plt 
import matplotlib.pyplot as plt
from matplotlib import cm
from matplotlib.ticker import LinearLocator
import numpy as np
from mpl_toolkits.mplot3d import axes3d, Axes3D
import pandas as pd
import plotly.express as px
import io
import plotly.io as pio
%matplotlib notebook


E = np.arange(225)
D = np.arange(225)
A = np.arange(225)

E = [10000.0, 10000.0, ...]
D = [500.0, 1000.0, ...]
A = [1.9495, 1.9644, ...]


#Create figure
fig = plt.figure(figsize =(16, 9)) 
ax = plt.axes(projection ='3d') 

# Creating color map
my_cmap = plt.get_cmap('hot')

# Data for three-dimensional scattered points
zdata = A
xdata = D
ydata = E

# Creating plot
trisurf = ax.plot_trisurf(xdata, ydata, zdata,
                         cmap = my_cmap,
                         linewidth = 0.2,
                         antialiased = True,
                         edgecolor = 'grey') 
fig.colorbar(trisurf, ax = ax, shrink = 0.5, aspect = 10)
ax.set_title('AIE_SIM0.003__lowE_10000_upE_460000_stepE_30000_lowD_500.0_upD_8000.0_stepD_500.0')

ax.set_xlabel('Damping Ns/m')
ax.set_ylabel('Stifness N/m')
ax.set_zlabel('Amplification')


A2 = np.arange(225)

A2.fill(20.757)


# Creating color map
my_cmap2 = plt.get_cmap('gray')

# Data for three-dimensional scattered points
zdata2 = A2
xdata = D
ydata = E

# Creating plot
trisurf2 = ax.plot_trisurf(xdata, ydata, zdata2,
                         cmap = my_cmap2,
                         linewidth = 0.2,
                         antialiased = False,
                         edgecolor = 'none', alpha = 0.2) 
fig.colorbar(trisurf2, ax = ax, shrink = 0.5, aspect = 10)
print(type(fig))
#fig.write_html("file.html")
plotly.io.to_html(fig=fig)

fig.savefig('3D_Plot_PNG_lowE_10000_upE_460000_stepE_30000_lowD_500.0_upD_8000.0_stepD_500.0.png')
fig.show()



------------------------------------------------------------------------------------------
Figure 1

printed: <class 'matplotlib.figure.Figure'>


ValueError: 
The fig parameter must be a dict or Figure.
    Received value of type <class 'matplotlib.figure.Figure'>: Figure(1600x900)

Solution

  • As far as I'm aware, Matplotlib is not able to generate 3D html plot.

    Moreover, what you tried above is wrong. That error message is telling you that Plotly's to_html only works with Plotly's Figure. So mixing Plotly and Matplotlib is not going to work. You need to create a Plotly figure.

    Also, I don't think that Plotly exposes something similar to Matplotlib's plot_trisurf. However, it exposes go.Mesh that allows us to achieve the same result.

    The recipe:

    • Generate your numerical data.
    • Create a triangulation. We will use Matplotlib's Triangulation class for this part.
    • Create the Plotly figure and add the surface.
    • Export the figure to html.

    Here I'm going to post an example to guide you:

    import numpy as np
    import matplotlib.tri as mtri
    import plotly.graph_objects as go
    
    ### DATA GENERATION
    # Make parameter spaces radii and angles.
    n_angles = 36
    n_radii = 8
    min_radius = 0.25
    radii = np.linspace(min_radius, 0.95, n_radii)
    
    angles = np.linspace(0, 2*np.pi, n_angles, endpoint=False)
    angles = np.repeat(angles[..., np.newaxis], n_radii, axis=1)
    angles[:, 1::2] += np.pi/n_angles
    
    # Map radius, angle pairs to x, y, z points.
    x = (radii*np.cos(angles)).flatten()
    y = (radii*np.sin(angles)).flatten()
    z = (np.cos(radii)*np.cos(3*angles)).flatten()
    
    ### TRIANGULATION
    # Create the Triangulation; no triangles so Delaunay triangulation created.
    triang = mtri.Triangulation(x, y)
    
    # Mask off unwanted triangles.
    xmid = x[triang.triangles].mean(axis=1)
    ymid = y[triang.triangles].mean(axis=1)
    mask = xmid**2 + ymid**2 < min_radius**2
    triangles = triang.triangles[~mask]
    
    ### PLOT
    fig = go.Figure(data=[
        # go.Mesh allows to provide the triangulation
        go.Mesh3d(
            x=x, y=y, z=z,
            colorbar_title='z',
            colorscale="aggrnyl",
            # Intensity of each vertex, which will be interpolated and color-coded
            intensity =z,
            # i, j and k give the vertices of triangles
            i = triangles[:, 0],
            j = triangles[:, 1],
            k = triangles[:, 2],
            showscale=True
        )
    ])
    
    fig.show()
    
    ### EXPORT TO HTML
    # Please, execute `help(fig.write_html)` to learn about all the
    # available keyword arguments to control the output
    fig.write_html("test.html", include_plotlyjs=True, full_html=True)