1

I have created a 3d plot of a sphere in python using Mathplotlib using the code below

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

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

u = np.linspace(0,2*np.pi, 32)
v = np.linspace(0, np.pi, 16)

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=4, cstride=4, color='b')


plt.show()

Picture of plot:-

enter image description here

I would like to color each individual box a different color. I have tried to use a color map, but I was only able to change the color based on the z value with this.

Any suggestions would be greatly appreciated. I am open to using other tools or languages to accomplish this task, but I need the boxes to be the same size.

  • If you want to have different color for each patch, there has to be some logic if you want to implement it by programming. But I don't see any rule or logic explained other than just saying that you want different color. I think you should use photoShop if you just want to change color. – Hun Apr 14 '16 at 06:51
  • Check: http://stackoverflow.com/questions/28324545/adding-a-4th-variable-to-a-3d-plot-in-python – armatita Apr 14 '16 at 07:31
  • I have developed a device that tests the antenna coverage of GPS antenna's in cellphones. Each "box" represents a section of coverage by the antenna. I have Signal strength values for each of these "boxes" but I would like to give each "box" a color to show how strong the signal is in that "box". – user6202102 Apr 14 '16 at 18:55
  • For that you could alternatively use a 3D shape whose surface distance from the origin is proportional to the signal strength. – roadrunner66 Apr 15 '16 at 02:27
  • @roadrunner66 This is a possible option, and in this case I would have the radius of the sphere be dependent on the signal strength. The problem with this is the shape becomes very dis-formed and is sometimes hard to read. – user6202102 Apr 15 '16 at 03:20
  • If the shape is too extreme, you could make it nonlinear, e.g. logarithmic, but I agree that colors is a good solution. – roadrunner66 Apr 15 '16 at 04:20

2 Answers2

1

Besides the link given in comments it's possible to directly map a variable to the colormap. Check your example adapted with a solution:

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

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

u = np.linspace(0,2*np.pi, 32)
v = np.linspace(0, np.pi, 16)

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))

C = np.cos(x)

ax.plot_surface(x, y, z, facecolors = cm.jet(C), rstride=4, cstride=4) # Look how I gave a variable to the facecolors.

plt.show()

, this particularly bad example results in this:

enter image description here

armatita
  • 12,825
  • 8
  • 48
  • 49
1

@Armatita's solution is very elegant, especially if you have a mapping function. I upvoted it :). Another solution for arbitrary shapes can be done with Poly3DCollection. Now you could import actual data and the surfaces could be arbitray in space and wouldn't have to connect.

For comparison, however I used the same sphere. I edited the answer to assign the color as a function of the two angles based on your comments below.

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

fig = plt.figure()
ax = fig.gca(projection='3d')

r=1 # radius of sphere
phi = np.linspace(0,360, 12)/180.0*np.pi
theta = np.linspace(-90,90,7)/180.0*np.pi

# make up some function  like OP (original poster) suggested:
# numerical value in the range of 20-40.
vars=[]
for i  in range(len(phi)-1):
    for j in range(len(theta)-1):
        vars.append( (i*j* - i +j)/25+40)  # min at 20, max at 40

# set colors as function of data in vars        
# less than 25 as red, from 25-35 as yellow 
# and anything greater than 35 as green. –        
cols=[]
for var in vars:
    if var <25: 
        col='r'
    elif 25<=var<=35:
        col='y'
    else:
        col='g'
    cols.append(col)

verts2 = []
for i  in range(len(phi)-1):
    for j in range(len(theta)-1):
        cp0= r*np.cos(phi[i])
        cp1= r*np.cos(phi[i+1])
        sp0= r*np.sin(phi[i])
        sp1= r*np.sin(phi[i+1])

        ct0= np.cos(theta[j])
        ct1= np.cos(theta[j+1])

        st0= r*np.sin(theta[j])
        st1= r*np.sin(theta[j+1])

        verts=[]
        verts.append((cp0*ct0, sp0*ct0, st0))
        verts.append((cp1*ct0, sp1*ct0, st0))
        verts.append((cp1*ct1, sp1*ct1, st1))
        verts.append((cp0*ct1, sp0*ct1, st1))
        verts2.append(verts   )

poly3= Poly3DCollection(verts2 ,facecolor= cols)  

poly3.set_alpha(0.9)
ax.add_collection3d(poly3)
ax.set_xlabel('X')
ax.set_xlim3d(-1, 1)
ax.set_ylabel('Y')
ax.set_ylim3d(-1, 1)
ax.set_zlabel('Z')
ax.set_zlim3d(-1, 1)
plt.show()

enter image description here

roadrunner66
  • 7,772
  • 4
  • 32
  • 38
  • I really like how this one looks, How would I assign a color to a specific square? For example, in the picture I posted in my original post there are 32 boxes. I need to color these boxes based on a value from my data collection. The values from my data collection are a numerical value in the range of 20-40. I would like to show anything that is less than 25 as red, from 25-35 as yellow and anything greater than 35 as green. – user6202102 Apr 14 '16 at 23:37