Coloring individual patches in Mayavi - python

I can do this with matplotlib I want to plot a torus and vary the coloring of the surface based on the small angle parameter. More generally, how do you color individual patches when you plot a surface with Mayavi?
I can do this easily in matplotlib using the 'facecolors' parameter when I plot, but because I need better 3D rendering I have to use Mayavi.
Moreover, here is how you can color individual points in Mayavi:
Plotting 3D points with different colors in Mayavi (Python)
But I haven't found something similar to work for surfaces.
From the documentation, the coloring is specified by default by the elevation z but I am not sure how I can change this to be specified based on some array I can pass to the plotting function.
Is there a way, to change this dependency?
'''
from mayavi import mlab
import numpy as np
mlab.options.offscreen = True
R = 2
r = 1
theta = np.linspace(0, 2 * np.pi, 20)
phi = np.linspace(0, 2 * np.pi, 20)
torus = np.zeros((3,20,20))
for i in range(0,20):
for j in range(0,20):
torus[0][i][j] = (R + r * np.cos(phi[j])) * np.cos(theta[i])
torus[1][i][j] = (R + r * np.cos(phi[j])) * np.sin(theta[i])
torus[2][i][j] = r * np.sin(phi[j])
mlab.mesh(torus[0], torus[1], torus[2])
mlab.show()
'''
I want the result to look like the following:

Related

Aitoff projections using healpy and projplot

I have a range of theta and phi’s using the healpy pix2ang command,
and then transforming to RA, Decl.::
ra = np.rad2deg(phi)
dec = np.rad2deg(0.5 * np.pi - theta)
I just want to project these onto an e.g. Aitoff type projection, but for the life of me can’t figure out how to do this via::
https://healpy.readthedocs.io/en/latest/generated/healpy.visufunc.projplot.html
projplot(ra, dec, 'bo')
doesn't really do anything.
hp.projplot is used to add lines to an existing plot. If you're just interested in plotting lines on a different projection, I recommend you check out matplotlib's projections.
For healpy, please find a quick example below.
import healpy as hp
import numpy as np
nside = 64
npix = hp.nside2npix(nside)
arr = np.random.randn(npix)
# Draw a circle
r = np.full(100, 20.)
phi = np.linspace(0., 2*np.pi, 100)
x = np.cos(phi)*r
y = np.sin(phi)*r
# Plot the map and the circle
hp.mollview(arr)
hp.projplot(x, y, c='r', lonlat=True)

Subplot in a non-grid constellation in python / Matplotlib?

I wonder if there's the possibility to display several plots or images in a non-grid constellation ? For example is there a way to display a set of images in one figure in a circular constellation along the perimeter using Matplotlib or any other python package alike ?
An axes can be created and positionned via fig.add_axes([x,y,width,height]) see documentation. Also see What are the differences between add_axes and add_subplot?
In this case we can add the axes to positions lying on a circle, creating some kind of manual radial grid of axes.
import numpy as np
import matplotlib.pyplot as plt
N = 8
t = np.linspace(0,2*np.pi, N, endpoint=False)
r = 0.37
h = 0.9 - 2*r
w = h
X,Y = r*np.cos(t)-w/2.+ 0.5, r*np.sin(t)-h/2.+ 0.5
fig = plt.figure()
axes = []
for x,y in zip(X,Y):
axes.append(fig.add_axes([x, y, w, h]))
plt.show()

Drawing log-linear plot on a square plot area in matplotlib

I would like to draw a plot with a logarithmic y axis and a linear x axis on a square plot area in matplotlib. I can draw linear-linear as well as log-log plots on squares, but the method I use, Axes.set_aspect(...), is not implemented for log-linear plots. Is there a good workaround?
linear-linear plot on a square:
from pylab import *
x = linspace(1,10,1000)
y = sin(x)**2+0.5
plot (x,y)
ax = gca()
data_aspect = ax.get_data_ratio()
ax.set_aspect(1./data_aspect)
show()
log-log plot on a square:
from pylab import *
x = linspace(1,10,1000)
y = sin(x)**2+0.5
plot (x,y)
ax = gca()
ax.set_yscale("log")
ax.set_xscale("log")
xmin,xmax = ax.get_xbound()
ymin,ymax = ax.get_ybound()
data_aspect = (log(ymax)-log(ymin))/(log(xmax)-log(xmin))
ax.set_aspect(1./data_aspect)
show()
But when I try this with a log-linear plot, I do not get the square area, but a warning
from pylab import *
x = linspace(1,10,1000)
y = sin(x)**2+0.5
plot (x,y)
ax = gca()
ax.set_yscale("log")
xmin,xmax = ax.get_xbound()
ymin,ymax = ax.get_ybound()
data_aspect = (log(ymax)-log(ymin))/(xmax-xmin)
ax.set_aspect(1./data_aspect)
show()
yielding the warning:
axes.py:1173: UserWarning: aspect is not supported for Axes with xscale=linear, yscale=log
Is there a good way of achieving square log-linear plots despite the lack support in Axes.set_aspect?
Well, there is a sort of a workaround. The actual axis area (the area where the plot is, not including external ticks &c) can be resized to any size you want it to have.
You may use the ax.set_position to set the relative (to the figure) size and position of the plot. In order to use it in your case we need a bit of maths:
from pylab import *
x = linspace(1,10,1000)
y = sin(x)**2+0.5
plot (x,y)
ax = gca()
ax.set_yscale("log")
# now get the figure size in real coordinates:
fig = gcf()
fwidth = fig.get_figwidth()
fheight = fig.get_figheight()
# get the axis size and position in relative coordinates
# this gives a BBox object
bb = ax.get_position()
# calculate them into real world coordinates
axwidth = fwidth * (bb.x1 - bb.x0)
axheight = fheight * (bb.y1 - bb.y0)
# if the axis is wider than tall, then it has to be narrowe
if axwidth > axheight:
# calculate the narrowing relative to the figure
narrow_by = (axwidth - axheight) / fwidth
# move bounding box edges inwards the same amount to give the correct width
bb.x0 += narrow_by / 2
bb.x1 -= narrow_by / 2
# else if the axis is taller than wide, make it vertically smaller
# works the same as above
elif axheight > axwidth:
shrink_by = (axheight - axwidth) / fheight
bb.y0 += shrink_by / 2
bb.y1 -= shrink_by / 2
ax.set_position(bb)
show()
A slight stylistic comment is that import pylab is not usually used. The lore goes:
import matplotlib.pyplot as plt
pylab as an odd mixture of numpy and matplotlib imports created to make interactive IPython use easier. (I use it, too.)

Python 3D plotting of measurement data

I have captured 3D measurement data on a sphere (this is an antenna radiation pattern, so the measurement antenna captured the radiation intensity from each phi,theta direction and logged this value as a function of phi,theta).
I am having great difficulty getting the data represented.
I have tried multiple options. This is the last one I am now trying:
import numpy as np
from mpl_toolkits.mplot3d import axes3d
import matplotlib.pyplot as plt
nElevationPoints = 16
nAzimuthPoints = 40
stepSizeRad = 0.05 * np.pi
def r(phi,theta):
radius = 1
return radius
phi = np.arange(0,nAzimuthPoints*stepSizeRad,stepSizeRad)
theta = np.arange(0,nElevationPoints*stepSizeRad,stepSizeRad)
x = (r(phi,theta)*np.outer(r(phi,theta)*np.cos(phi), np.sin(theta)))
y = (-r(phi,theta)*np.outer(np.sin(phi), np.sin(theta)))
z = (r(phi,theta)*np.outer(np.ones(np.size(phi)), np.cos(theta)))
fig = plt.figure(1)
ax = fig.add_subplot(111, projection='3d')
ax.plot_surface(x, y, z, rstride=4, cstride=4, color='b')
plt.ioff()
plt.show()
This code in itself is working, and it plots a sphere. Now the thing is, that in accordance with the measurement data, I would actually need the radius not be a constant "1", but corresponding with the radiation intensity measured. So it needs to be a function of phi,theta.
However, as soon as I change the "r" function to anything containing the phi or theta parameter, I get an error about operands that could not be broadcast.
If there's any work around that loops through phi,theta that would be perfectly fine as well.
But I'm stuck now, so I'd appreciate any help :-)
BTW, the reason I went for the above approach is because I couldn't make sense of how the x,y,z should be defined in order to be acceptable to the plot_surface function.
I did manage to generate a scatter plot, by calculating the actual positions (x,y,z) from the phi,theta,intensity data, but this is only a representation by individual points and doesn't generate any well visible antenna radiation pattern plot. For this I assume that a contour plot would be better, but then again I am stuck at either the "r" function call or by understanding how x,y,z should be formatted (the documentation refers to x,y,z needing to be 2D-arrays, but this is beyond my comprehension as x,y,z usually are one dimensional arrays in themselves).
Anyway, looking forward to any help anyone may be willing to give.
-- EDIT --
With #M4rtini 's suggested changes I come to the following:
import numpy as np
from mayavi import mlab
def r(phi,theta):
r = np.sin(phi)**2
return r
phi, theta = np.mgrid[0:2*np.pi:201j, 0:np.pi:101j]
x = r(phi,theta)*np.sin(phi)*np.cos(theta)
y = r(phi,theta)*np.sin(phi)*np.sin(theta)
z = r(phi,theta)*np.cos(phi)
intensity = phi * theta
obj = mlab.mesh(x, y, z, scalars=intensity, colormap='jet')
obj.enable_contours = True
obj.contour.filled_contours = True
obj.contour.number_of_contours = 20
mlab.show()
This works, thanks, #M4rtini, and I now am able to have a phi,theta dependent "r" function.
However, noted that the example now ensures phi and theta to be of the same length (due to the mgrid function). This is not the case in my measurement. When declaring phi and theta separately and of different dimensions, it doesn't work still. So I now will have a look into measurement interpolation.
This might not be the exact answer you were looking for, but if you can accept using intensity values as a mapping of a color, this should work.
Actually, you could probably calculate a specific r here also. But i did not test that.
Using mayavi since it is, in my opinion, far superior than matplotlib for 3D.
import numpy as np
from mayavi import mlab
r = 1.0
phi, theta = np.mgrid[0:np.pi:200j, 0:2*np.pi:101j]
x = r*np.sin(phi)*np.cos(theta)
y = r*np.sin(phi)*np.sin(theta)
z = r*np.cos(phi)
intensity = phi * theta
obj = mlab.mesh(x, y, z, scalars=intensity, colormap='jet')
obj.enable_contours = True
obj.contour.filled_contours = True
obj.contour.number_of_contours = 20
mlab.show()
Output of example script, now this is in a interactive gui. so you can rotate, translate, scale as you please. And even interactively manipulate the data, and the representation options.

matplotlib streamplot with a change of variables

Hi I'm an happy user of streamplot module in matplotlib (version 1.3). I've used it for plotting a stream flow in the usual way (vr(t,r),vphi(t,r) are velocity in 2D space, t the time basis and r 1D coordinate where I have measurements of vr and vphi)
from matplotlib import *
speed = np.sqrt(vr * vr + vphi * vphi)
lw = 15 * speed / speed.max()
fig = plt.figure(figsize=(10.,6.0))
ax = fig.add_subplot(111)
ax.streamplot(t, r, vt, vr, linewidth = lw, color='blue')
Now suppose that I've a variable u as function of t (u(t)). It has a monotonic dependence on t, i.e. it varies linearly with t. Now I would like to create the streamplot as a function of (u,r), i.e. something as
ax.streamplot(u,r,vt,vr,linewidth=lw,color='blue')
What I'm wondering is that, considering the algorithm at the basis of streamplot should I actually make a complete change of variables? i.e.
vt1(u) = vt(t)*d(u(t))/dt
vr1(u) = vr(t)*d(u(t))/dt
ax.streamplot(u,r,vt1,vr1,linewidth=lw,color='blue')
Am I right or there is something I do not understand?

Categories

Resources