Posts: 35
Threads: 11
Joined: Feb 2021
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.
Posts: 74
Threads: 23
Joined: Mar 2024
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()
Posts: 35
Threads: 11
Joined: Feb 2021
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?
Posts: 327
Threads: 82
Joined: Apr 2019
Jun-22-2024, 08:11 AM
(This post was last modified: Jun-22-2024, 08:11 AM by paul18fr.)
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)
Posts: 327
Threads: 82
Joined: Apr 2019
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)
Attached Files
Thumbnail(s)
Posts: 35
Threads: 11
Joined: Feb 2021
Thanks, but I'm trying to use plotly instead of matplotlib.
Posts: 327
Threads: 82
Joined: Apr 2019
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')
Attached Files
Thumbnail(s)
Posts: 35
Threads: 11
Joined: Feb 2021
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.
Posts: 327
Threads: 82
Joined: Apr 2019
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.)
Posts: 35
Threads: 11
Joined: Feb 2021
Jun-24-2024, 04:09 PM
(This post was last modified: Jun-24-2024, 04:10 PM by Tuxedo.)
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')
Attached Files
Thumbnail(s)
|