1

In Matplotlib, I want to draw a sphere with a mesh on its surface, divided into 30 degrees steps in spherical coordinates.

The code:

from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import numpy as np

fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')

u = np.linspace(0, 2 * np.pi, 13)
v = np.linspace(0, np.pi, 7)

x = 10 * np.outer(np.cos(u), np.sin(v))
y = 10 * np.outer(np.sin(u), np.sin(v))
z = 10 * np.outer(np.ones(np.size(u)), np.cos(v))
ax.plot_surface(x, y, z, rstride=1, cstride=1, color='w', shade=0)

plt.show()

Produces the figure:

enter image description here

However, I want the contours on the sphere to be smooth, rather than drawn directly between the plotted points. If I increase the density of sampling, I get a smoother sphere, but the lines are drawn too densely:

enter image description here

How can I plot lines on a smooth sphere that are separated by 30 degrees?

Karnivaurus
  • 22,823
  • 57
  • 147
  • 247

2 Answers2

0

You can interpolate your data first, and then plot using a larger stride.

from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import numpy as np
import scipy.ndimage

fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')

u = np.linspace(0, 2 * np.pi, 13)
v = np.linspace(0, np.pi, 7)

x = 10 * np.outer(np.cos(u), np.sin(v))
y = 10 * np.outer(np.sin(u), np.sin(v))
z = 10 * np.outer(np.ones(np.size(u)), np.cos(v))

# use scipy to interpolate
xdata = scipy.ndimage.zoom(x, 3)
ydata = scipy.ndimage.zoom(y, 3)
zdata = scipy.ndimage.zoom(z, 3)

ax.plot_surface(xdata, ydata, zdata, rstride=3, cstride=3, color='w', shade=0)

plt.show()

Based on this other issue here.

Note that scipy.ndimage.zoom might be overkill. I don't see why you can't use smaller resolution on u and v, and then bump up the stride accordingly. This seems to work just fine as well:

...
# increased num pts by 3
u = np.linspace(0, 2 * np.pi, 39)
v = np.linspace(0, np.pi, 21)

x = 10 * np.outer(np.cos(u), np.sin(v))
y = 10 * np.outer(np.sin(u), np.sin(v))
z = 10 * np.outer(np.ones(np.size(u)), np.cos(v))

# Use 3x the stride, no scipy zoom
ax.plot_surface(x, y, z, rstride=3, cstride=3, color='w', shade=0)
...
Community
  • 1
  • 1
bjornruffians
  • 671
  • 5
  • 16
  • Thanks. I tried your code, however it gave me the following figure: http://i.stack.imgur.com/LBxAQ.png, which doesn't seem quite right..... – Karnivaurus Nov 05 '15 at 18:34
  • I think the wobblyness is just due to how it's plotted...not sure though. If you rotate it, the actual black lines are smooth. – bjornruffians Nov 05 '15 at 21:06
0

The solution of bornruffians looks wobbly, because you don't have actual surface where you expect to. So you sort of see through it, that's why the wireframe appears bent.

It is hard to find other solution, because you do need a good resolution sphere (that is, with small parameter step), otherwise you will see "through" it. An then you need to draw lines on top of this good resolution sphere.

You can look at Basemap module in matplotlib: http://matplotlib.org/basemap/users/examples.html It might be a bit of an overkill, but it'll work.

enter image description here

There might be a solution with Poly3DColelction and Line3D Collection: Transparency for Poly3DCollection plot in matplotlib

Community
  • 1
  • 1
Noidea
  • 1,405
  • 11
  • 17