Python Forum

Full Version: 3-d surface plotting
You're currently viewing a stripped down version of our content. View the full version with proper formatting.
Pages: 1 2
Looking for a Python package for publication quality 3d surface plots. The surfaces I deal with have just a single peak but can have odd shapes near the base (e.g. banana shaped or triangular-ish shaped). I want to be able to "slice" the surface at a particular z value and highlight the resulting contour in some way. I'd also like to cut the base at a particular z value and discard everything below that value. I've fiddled with matplotlib quite a bit and can't really get the result I want. I'd also like to be able to easily rotate the plot to determine the best viewing angle. It appears plotly might have some promise, but most of the videos I find show everything but surface plots. Is there some clear winner for surface plotting? I'd even consider a ray tracing package (probably not Python based) but am wary about the learning curve.
Here is some sample code with plotly, it may not do exactly what you are looking for but perhaps it can give you a starting point:

import numpy as np
import plotly.graph_objects as go

# Generate example data
x = np.linspace(-10, 10, 100)
y = np.linspace(-10, 10, 100)
x, y = np.meshgrid(x, y)
z = np.sin(np.sqrt(x**2 + y**2))

# Specify the z-value for slicing and cutting the base
slice_z_value = 0.5
cut_base_z_value = -0.5

# Create a mask to discard values below the cut_base_z_value
z[z < cut_base_z_value] = np.nan

# Create the 3D surface plot
fig = go.Figure(data=[go.Surface(z=z, x=x, y=y)])

# Add contour at the slice_z_value
fig.add_trace(go.Surface(
    z=np.ones_like(z) * slice_z_value,
    x=x,
    y=y,
    showscale=False,
    opacity=0.5,
    surfacecolor=np.where(z >= slice_z_value, z, np.nan),
    contours={"z": {"show": True, "start": slice_z_value, "end": slice_z_value, "size": 0.1}}
))

# Update layout for better visualization
fig.update_layout(scene=dict(
    zaxis=dict(range=[cut_base_z_value, np.nanmax(z)])
))

# Show plot
fig.show()
Thanks so much! I use spyder, so didn't get any plot. Using:

https://community.plotly.com/t/plotly-in-spyder/5414/3

I was able to get an interactive plot in my web browser by using

from plotly.offline import plot
plot(fig)

Next, I tried to save a static imaging based on:

https://plotly.com/python/static-image-export/

by installing kaleido:

conda install -c conda-forge python-kaleido

and writing the image with

fig.write_image('fig1.png')

For some reason, the program just hangs/stops and doesn't write the image file. I get no error messages, but the program doesn't complete. Nothing. What am I doing wrong?
I'm also a Spyder user. Here after my code works perfectly. The figure is plotted on Spyder and saved.

# -*- coding: utf-8 -*-

import matplotlib.pyplot as plt
from matplotlib.ticker import LinearLocator, FormatStrFormatter
import numpy as np
import os

Path = os.getcwd()
fig = plt.figure(figsize=(18, 18))
ax = fig.add_subplot(projection='3d')
ax.set_xlabel('X axis')
ax.set_ylabel('Y axis')
ax.set_zlabel('Z axis')

# Make data.
x = np.linspace(-10, 10, 100)
y = np.linspace(-10, 10, 100)
x, y = np.meshgrid(x, y)
z = np.sin(np.sqrt(x**2 + y**2))

# Plot the surface.
surf = ax.plot_wireframe(x, y, z, color='black')
surf = ax.plot_surface(x, y, z, rstride=1, cstride=1, linewidth=1, antialiased=True, edgecolor='black')

# Customize the z axis.
ax.set_zlim(-1.01, 1.01)
ax.zaxis.set_major_locator(LinearLocator(10))
ax.zaxis.set_major_formatter(FormatStrFormatter('%.02f'))

plt.show()
fig.savefig(Path + '/fig.png', dpi=300)
[attachment=2920]
On my previous post, I forgot to add "elevation" and "azimut" features

import matplotlib.pyplot as plt
from matplotlib.ticker import LinearLocator, FormatStrFormatter
import numpy as np
import os

elevation = 45;     # vertical
azimut = 30.;       # theta

Path = os.getcwd()
fig = plt.figure(figsize=(18, 18))
ax = fig.add_subplot(projection='3d')
ax.view_init(elevation, azimut)
ax.set_xlabel('X axis')
ax.set_ylabel('Y axis')
ax.set_zlabel('Z axis')

# Make data.
x = np.linspace(-10, 10, 100)
y = np.linspace(-10, 10, 100)
x, y = np.meshgrid(x, y)
z = np.sin(np.sqrt(x**2 + y**2))

# Plot the surface.
surf = ax.plot_wireframe(x, y, z, color='black')
surf = ax.plot_surface(x, y, z, rstride=1, cstride=1, linewidth=1, antialiased=True, edgecolor='black')

# Customize the z axis.
ax.set_zlim(-1.01, 1.01)
ax.zaxis.set_major_locator(LinearLocator(10))
ax.zaxis.set_major_formatter(FormatStrFormatter('%.02f'))

plt.show()
fig.savefig(Path + '/fig.png', dpi=100)
Thanks, but I'm trying to use plotly instead of matplotlib.
I was not aware about plotly.

The following information partially answers (i guess) to your need:

1) From sawtooth500 code, the picture can be directly displayed in your browser with
fig.write_html(Path + '/figure.html', auto_open=True)
It also writes an html file.

2) you can record the file using an icon in the browser (see screenshot), but in my case, it's done in the Download repertory.

3) I tried to install imgkit on Anaconda, but i failed so far.
import imgkit
imgkit.from_file(Path + '/figure.html', Path + '/out.png')
Complete code:
# -*- coding: utf-8 -*-

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

Path = os.getcwd()
# Generate example data
x = np.linspace(-10, 10, 100)
y = np.linspace(-10, 10, 100)
x, y = np.meshgrid(x, y)
z = np.sin(np.sqrt(x**2 + y**2))
 
# Specify the z-value for slicing and cutting the base
slice_z_value = 0.5
cut_base_z_value = -0.5
 
# Create a mask to discard values below the cut_base_z_value
z[z < cut_base_z_value] = np.nan
 
# Create the 3D surface plot
fig = go.Figure(data=[go.Surface(z=z, x=x, y=y)])
 
# Add contour at the slice_z_value
fig.add_trace(go.Surface(
    z=np.ones_like(z) * slice_z_value,
    x=x,
    y=y,
    showscale=False,
    opacity=0.5,
    surfacecolor=np.where(z >= slice_z_value, z, np.nan),
    contours={"z": {"show": True, "start": slice_z_value, "end": slice_z_value, "size": 0.1}}
))
 
# Update layout for better visualization
fig.update_layout(scene=dict(
    zaxis=dict(range=[cut_base_z_value, np.nanmax(z)])
))
 
# Show plot
fig.show()
fig.write_html(Path + '/figure.html', auto_open=True)

# import imgkit
# imgkit.from_file(Path + '/figure.html', Path + '/out.png')
I'm trying to write png or jpg files to import into a Latex document, but my Python script hangs whenever it gets to the command:

fig.write_image('plotly_test.png')
I have to restart the Python kernel from Spyder to recover. I have no idea what's wrong.
I've had to install kaleido as well, but just typing conda install python-kaleido (see conda-forge) and it works fine for me. I would uninstall first your kaleido release and reinstall it, otherwise it may come from anaconda incompatibilities, broken links ...

Personnal feeling: In my opinion it's better to print the figure from the browser (after rotating, zooming, etc.)
Sorry for the pivot, but it looks like I was too quick to abandon matplotlib. After implementing some tips in this thread (thanks!) I was able to get m-u-c-h closer to what I'm looking for. See attached. What I can't figure out how to do though, is restrict the face color to the area inside the axes. I want everything outside the axes to be white. How to do it? I've seen it done with 2d plots but not 3d. This is what I included:

ax.set_facecolor('Blue')
Pages: 1 2