Search code examples
pythonmatplotlibmplot3dmatplotlib-3d

How to create a colored sphere


I have a data set which maps a tuple of phi and theta to a value which represents the strength of the signal. I want to plot these on a sphere. I simply followed a demo from matplotlib and adjusted the code to my use case.

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

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

u = phi
v = theta
vals =vals/vals.max()
Map = cm.coolwarm
facecolors = Map(vals[:])

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, cmap=cm.coolwarm,
                       linewidth=0, antialiased=False, facecolors=facecolors)

plt.show()

This generates an error message IndexError: index 4 is out of bounds for axis 0 with size 4. I also looked into the source code, which seems to indicate to me that facecolors isn't formatted correctly, but I'm struggling to figure out, what formatting is needed exactly.


Solution

  • If your question is: "How to get rid of this IndexError?", I modified your code and now it works. plot_surface takes X,Y,Z and facecolors as 2D arrays of corresponding values on a 2D grid. Facecolors in your case weren't and this was the source of your error.

    from mpl_toolkits.mplot3d import Axes3D
    from matplotlib import cm, colors
    import matplotlib.pyplot as plt
    import numpy as np
    
    fig = plt.figure()
    ax = fig.add_subplot(111, projection='3d')
    
    u, v = np.mgrid[0:np.pi:50j, 0:2*np.pi:50j]
    
    strength = u
    norm=colors.Normalize(vmin = np.min(strength),
                          vmax = np.max(strength), clip = False)
    
    x = 10 * np.sin(u) * np.cos(v)
    y = 10 * np.sin(u) * np.sin(v)
    z = 10 * np.cos(u)
    
    ax.plot_surface(x, y, z, rstride=1, cstride=1, cmap=cm.coolwarm,
                           linewidth=0, antialiased=False,
                           facecolors=cm.coolwarm(norm(strength)))
    
    plt.show()
    

    image

    However, if your data is not on a 2D grid you are in trouble. Additionally if your grid is not regular the sphere you plot will look irregular as well. So if your question is: "How to plot a heatmap on a sphere?", there is already such a question and solution here using Basemap package produces this result:

    enter image description here