I am trying to plot a curve on a sphere but I can not plot them at the same time. I identified some points with Euclidean norm 10 for my curve, and some other points to plot the sphere of radius 10, respectively as following.
Points for curve:
random_numbers=[]
basevalues=np.linspace(-0.9,0.9,100)
for i in range(len(basevalues)):
t=random.random()
random_numbers.append(t*10)
xvalues=[random_numbers[i]*np.cos(basevalues[i]) for i in range(len(basevalues))]
yvalues=[random_numbers[i]*np.sin(basevalues[i]) for i in range(len(basevalues))]
zvalues=[np.sqrt(100-xvalues[i]**2-yvalues[i]**2)for i in range(len(basevalues))]
Where xvalues, yvalues and zvalues are our points Euclidean components.
Points for sphere:
u = np.linspace(0, 2 * np.pi, 100)
v = np.linspace(0, np.pi, 100)
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))
Where x,y and z are Euclidean components of sphere points.
My problem:
When I try to plot the curve, without plotting sphere, it works. But when I plot them together, then it just return the sphere.
The whole code is the following:
import matplotlib.pyplot as plt
import numpy as np
import random
#Curve points
random_numbers=[]
basevalues=np.linspace(-0.9,0.9,100)
for i in range(len(basevalues)):
t=random.random()
random_numbers.append(t*10)
xvalues=[random_numbers[i]*np.cos(basevalues[i]) for i in range(len(basevalues))]
yvalues=[random_numbers[i]*np.sin(basevalues[i]) for i in range(len(basevalues))]
zvalues=[np.sqrt(100-xvalues[i]**2-yvalues[i]**2)for i in range(len(basevalues))]
# Sphere points
u = np.linspace(0, 2 * np.pi, 100)
v = np.linspace(0, np.pi, 100)
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))
# Plot the surface and curve
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
circ = ax.plot(xvalues,yvalues,zvalues, color='green',linewidth=1)
sphere=ax.plot_surface(x, y, z, color='r')
ax.set_zlim(-10, 10)
plt.xlabel("X axes")
plt.ylabel("Y axes")
plt.show()
What I want to occur:
I would like to plot the curve on the sphere, but it dose not happen in my code. I appreciate any hint.
If you use a "." option for plotting the points, like
circ = ax.plot(xvalues, yvalues,zvalues, '.', color='green', linewidth=1)
you will see the points on top of the sphere for certain viewing angles, but disappear sometimes even if they are in front of the sphere. This is a known bug explained in the matplotlib documentation:
My 3D plot doesn’t look right at certain viewing angles:
This is probably the most commonly reported issue with mplot3d. The problem is that – from some viewing angles – a 3D object would appear in front of another object, even though it is physically behind it. This can result in plots that do not look “physically correct.”
In the same doc, the developers recommend to use Mayavi for more advanced use of 3D plots in Python.
Using spherical coordinates, you can easily do that:
## plot a circle on the sphere using spherical coordinate.
import numpy as np
import matplotlib.pyplot as plt
# a complete sphere
R = 10
theta = np.linspace(0, 2 * np.pi, 1000)
phi = np.linspace(0, np.pi, 1000)
x_sphere = R * np.outer(np.cos(theta), np.sin(phi))
y_sphere = R * np.outer(np.sin(theta), np.sin(phi))
z_sphere = R * np.outer(np.ones(np.size(theta)), np.cos(phi))
# a complete circle on the sphere
x_circle = R * np.sin(theta)
y_circle = R * np.cos(theta)
# 3d plot
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.plot_surface(x_sphere, y_sphere, z_sphere, color='blue', alpha=0.2)
ax.plot(x_circle, y_circle, 0, color='green')
plt.show()
Related
I am new to animations with matplotlib, and I am trying to animate a shrinking ellipsoid.
Specifically, I want to animate an ellipsoid that shrinks its axes proportionally. (Mathematically, I'm looking for a shrinking factor of e^(-t) multiplied to each axis, where t is time.)
I have made a function of time t that outputs a static ellipsoid with the code below:
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
from mpl_toolkits.mplot3d import Axes3D
def param_surface(t):
fig = plt.figure(figsize = (10, 10))
ax = fig.add_subplot(111, projection='3d')
u = np.linspace(0, 2 * np.pi, 100)
v = np.linspace(0, np.pi, 100)
x = axis_a(4 * t / 50) * np.outer(np.cos(u), np.sin(v))
y = axis_a(4 * t / 50) * np.outer(np.sin(u), np.sin(v))
z = axis_b(4 * t / 50) * np.outer(np.ones(np.size(u)), np.cos(v))
return(ax.plot_surface(x, y, z, rstride = 4, cstride = 4))
I have seen animations (such as the one here: https://pythonmatplotlibtips.blogspot.com/2018/11/animation-3d-surface-plot-funcanimation-matplotlib.html) that allow you to animate 3D plots in which z is defined as a function of x, y. However, in the case of the shrinking ellipsoid, I need to use spherical coordinates, which complicates things.
Can someone explain what to add to my code to go from static to the desired shrinking animation?
I want to plot a quantity which is given on a parametric surface in 3d space (for example the temperature distribution on a sphere). I can plot a parametric 3D plot of the sphere (as a function of the two parameters phi and theta) but I don't know how to make the colors of the polygons making up the sphere depend on the parameters theta and phi (normally, the color of a polygon is simply determined by the z-Position of the polygon).
Here's a basic example which plots a torus with colormap:
import numpy as np
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
angle = np.linspace(0, 2 * np.pi, 32)
theta, phi = np.meshgrid(angle, angle)
r, R = .25, 1.
X = (R + r * np.cos(phi)) * np.cos(theta)
Y = (R + r * np.cos(phi)) * np.sin(theta)
Z = r * np.sin(phi)
# Display the mesh
fig = plt.figure()
ax = fig.gca(projection = '3d')
ax.set_xlim3d(-1, 1)
ax.set_ylim3d(-1, 1)
ax.set_zlim3d(-1, 1)
ax.plot_surface(X, Y, Z, rstride = 1, cstride = 1,cmap="hot")
plt.show()
However, the colors of the files are given by the z position of the tile, I want the color to be given by a function f(x,y).
Does anyone know how I can achieve this dependency in Matplotlib?
Thanks very much!
Ok, if anyone else is looking for a solution to this problem here's a possible solution:
The colors of the individual faces making up the surface plot can be set using the keyword argument facecolors. The following code will use the function X**2+Y**2 for coloring the faces of the parametric surface:
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.colors as mcolors
from matplotlib import cm
import matplotlib.pyplot as plt
import numpy as np
# Generate torus mesh
angle = np.linspace(0, 2 * np.pi, 32)
theta, phi = np.meshgrid(angle, angle)
r, R = .25, 1.
X = (R + r * np.cos(phi)) * np.cos(theta)
Y = (R + r * np.cos(phi)) * np.sin(theta)
Z = r * np.sin(phi)
colorfunction=(X**2+Y**2)
norm=mcolors.Normalize(colorfunction.min(),colorfunction.max())
# Display the mesh
fig = plt.figure(figsize=(7, 7))
ax = fig.add_subplot(projection='3d')
ax.set_xlim3d(-1, 1)
ax.set_ylim3d(-1, 1)
ax.set_zlim3d(-1, 1)
ax.plot_surface(X, Y, Z, rstride = 1, cstride = 1, facecolors=cm.jet(norm(colorfunction)))
plt.show()
I am wanting to plot all my spheres on the same graph, which should be 1 by 1 by 1. I am calling the plotting function shown below in several loops, as I want the spheres to change colours depending on some conditions. The coordinates of the centre of the spheres are described in another function in disk. However, all the spheres that I plot do change colour but are each on separate graphs. How would I change the code below so that each time I call the function plot_disks2, the spheres are added to the same graph. My code so far is:
def plot_disks2(disk, radius, c, ax=None):
fig = plt.figure(figsize=(12,12), dpi=300)
ax = fig.add_subplot(111, projection='3d')
u = np.linspace(0, 2 * np.pi, 100)
v = np.linspace(0, np.pi, 100)
x = radius * np.outer(np.cos(u), np.sin(v))
y = radius * np.outer(np.sin(u), np.sin(v))
z = radius * np.outer(np.ones(np.size(u)), np.cos(v))
sphere = ax.plot_surface(x+disk[0], y+disk[1], z+disk[2], rstride=4, cstride=4, color=c, linewidth=0, alpha=0.5)
ax.add_artist(sphere)
I need to plot contour and quiver plots of scalar and vector fields defined on an uneven grid in (r,theta) coordinates.
As a minimal example of the problem I have, consider the contour plot of a Stream function for a magnetic dipole, contours of such a function are streamlines of the corresponeding vector field (in this case, the magnetic field).
The code below takes an uneven grid in (r,theta) coordinates, maps it to the cartesian plane and plots a contour plot of the stream function.
import numpy as np
import matplotlib.pyplot as plt
r = np.logspace(0,1,200)
theta = np.linspace(0,np.pi/2,100)
N_r = len(r)
N_theta = len(theta)
# Polar to cartesian coordinates
theta_matrix, r_matrix = np.meshgrid(theta, r)
x = r_matrix * np.cos(theta_matrix)
y = r_matrix * np.sin(theta_matrix)
m = 5
psi = np.zeros((N_r, N_theta))
# Stream function for a magnetic dipole
psi = m * np.sin(theta_matrix)**2 / r_matrix
contour_levels = m * np.sin(np.linspace(0, np.pi/2,40))**2.
fig, ax = plt.subplots()
# ax.plot(x,y,'b.') # plot grid points
ax.set_aspect('equal')
ax.contour(x, y, psi, 100, colors='black',levels=contour_levels)
plt.show()
For some reason though, the plot I get doesn't look right:
If I interchange x and y in the contour function call, I get the desired result:
Same thing happens when I try to make a quiver plot of a vector field defined on the same grid and mapped to the x-y plane, except that interchanging x and y in the function call no longer works.
It seems like I made a stupid mistake somewhere but I can't figure out what it is.
If psi = m * np.sin(theta_matrix)**2 / r_matrix
then psi increases as theta goes from 0 to pi/2 and psi decreases as r increases.
So a contour line for psi should increase in r as theta increases. That results
in a curve that goes counterclockwise as it radiates out from the center. This is
consistent with the first plot you posted, and the result returned by the first version of your code with
ax.contour(x, y, psi, 100, colors='black',levels=contour_levels)
An alternative way to confirm the plausibility of the result is to look at a surface plot of psi:
import numpy as np
import matplotlib.pyplot as plt
import mpl_toolkits.mplot3d.axes3d as axes3d
r = np.logspace(0,1,200)
theta = np.linspace(0,np.pi/2,100)
N_r = len(r)
N_theta = len(theta)
# Polar to cartesian coordinates
theta_matrix, r_matrix = np.meshgrid(theta, r)
x = r_matrix * np.cos(theta_matrix)
y = r_matrix * np.sin(theta_matrix)
m = 5
# Stream function for a magnetic dipole
psi = m * np.sin(theta_matrix)**2 / r_matrix
contour_levels = m * np.sin(np.linspace(0, np.pi/2,40))**2.
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1, projection='3d')
ax.set_aspect('equal')
ax.plot_surface(x, y, psi, rstride=8, cstride=8, alpha=0.3)
ax.contour(x, y, psi, colors='black',levels=contour_levels)
plt.show()
I have the following problem:
a have N points on a sphere specified by a array x, with x.shape=(N,3). This array contains their cartesian coordinates. Furthermore, at each point, I have a specified temperature. This quantity is saved in an array T, with T.shape=(N,).
Is there any straight forward way to map this temperature distribution into the plane using different colors?
If it simplifies the task, the position can also be given in polar coordinates (\theta,\phi).
To plot your data, you can use Basemap. The only problem is, that both contour and contourf routines needs gridded data. Here is example with naive (and slow) IDW-like interpolation on sphere. Any comments are welcome.
import numpy as np
from mpl_toolkits.basemap import Basemap
import matplotlib.pyplot as plt
def cart2sph(x, y, z):
dxy = np.sqrt(x**2 + y**2)
r = np.sqrt(dxy**2 + z**2)
theta = np.arctan2(y, x)
phi = np.arctan2(z, dxy)
theta, phi = np.rad2deg([theta, phi])
return theta % 360, phi, r
def sph2cart(theta, phi, r=1):
theta, phi = np.deg2rad([theta, phi])
z = r * np.sin(phi)
rcosphi = r * np.cos(phi)
x = rcosphi * np.cos(theta)
y = rcosphi * np.sin(theta)
return x, y, z
# random data
pts = 1 - 2 * np.random.rand(500, 3)
l = np.sqrt(np.sum(pts**2, axis=1))
pts = pts / l[:, np.newaxis]
T = 150 * np.random.rand(500)
# naive IDW-like interpolation on regular grid
theta, phi, r = cart2sph(*pts.T)
nrows, ncols = (90,180)
lon, lat = np.meshgrid(np.linspace(0,360,ncols), np.linspace(-90,90,nrows))
xg,yg,zg = sph2cart(lon,lat)
Ti = np.zeros_like(lon)
for r in range(nrows):
for c in range(ncols):
v = np.array([xg[r,c], yg[r,c], zg[r,c]])
angs = np.arccos(np.dot(pts, v))
idx = np.where(angs == 0)[0]
if idx.any():
Ti[r,c] = T[idx[0]]
else:
idw = 1 / angs**2 / sum(1 / angs**2)
Ti[r,c] = np.sum(T * idw)
# set up map projection
map = Basemap(projection='ortho', lat_0=45, lon_0=15)
# draw lat/lon grid lines every 30 degrees.
map.drawmeridians(np.arange(0, 360, 30))
map.drawparallels(np.arange(-90, 90, 30))
# compute native map projection coordinates of lat/lon grid.
x, y = map(lon, lat)
# contour data over the map.
cs = map.contourf(x, y, Ti, 15)
plt.title('Contours of T')
plt.show()
One way to do this is to set facecolors by mapping your heat data through the colormap.
Here's an example:
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, 80)
v = np.linspace(0, np.pi, 80)
# create the sphere surface
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))
# simulate heat pattern (striped)
myheatmap = np.abs(np.sin(y))
ax.plot_surface(x, y, z, cstride=1, rstride=1, facecolors=cm.hot(myheatmap))
plt.show()
Here, my "heatmap" is just stripes along the y-axis, which I made using the function np.abs(np.sin(y)), but anything that goes form 0 to 1 will work (and, of course, it needs to match the shapes on x, etc.