Mayavi: How can I flip the Z-axis of a scene? - python

I am trying to flip Z-axis in the mayavi volumetric 3D plot. I figured how to rotate the camera etc, but that is not what I want. I just want to flip the direction of Z-axis. Without manipulating the data itself
#Minimum working example
import numpy as np
from mayavi import mlab
x, y, z = np.ogrid[-5:5:64j, -5:5:64j, -5:5:64j] #Generate XYZ
data = np.arange(x.shape[0])
x = x.ravel()
y = y.ravel()
z = z.ravel()
mlab.points3d(x, y, z, data) #Produce volumetric plot
mlab.axes(xlabel='X', ylabel='Y', zlabel='Z') #Display axis
mlab.orientation_axes()
mlab.show()

Could you please explain what you mean using non-symmetric data using this example.
Do you want negative z to be at the top side?
And why does rotating the camera not produce the result you want to see?
You can add the code from the macro editor (explained below).
import numpy as np
from mayavi import mlab
x, y, z = np.ogrid[-5:5:64j, -5:5:64j, -5:5:64j] #Generate XYZ
data = np.arange(x.shape[0])
x = x.ravel()
y = y.ravel()
z = z.ravel()
# Recorded script from Mayavi2
from numpy import array
try:
engine = mayavi.engine
except (AttributeError, NameError):
from mayavi.api import Engine
engine = Engine()
engine.start()
if len(engine.scenes) == 0:
engine.new_scene()
# -------------------------------------------
scene = engine.scenes[0]
scene.scene.camera.position = [20.68813263960946, 20.334388554161922, 20.518300376103046]
scene.scene.camera.focal_point = [0.24373197555541992, 0.24373197555541992, 0.25]
scene.scene.camera.view_angle = 30.0
scene.scene.camera.view_up = [-0.41179533881878827, -0.4046701524210215, 0.81649658092772626]
scene.scene.camera.clipping_range = [15.729834995160559, 58.864284541884331]
scene.scene.camera.compute_view_plane_normal()
scene.scene.render()
mlab.points3d(x, y, z, data) #Produce volumetric plot
mlab.axes(xlabel='X', ylabel='Y', zlabel='Z') #Display axis
mlab.orientation_axes()
mlab.show()
If you really can set the view you want manually I would just do that.
To get the correct coordinates to pass to mlab.view() read them from the interactive plot while rotating the scene:

Related

Geoviews FilledContours: keeping filled colours but removing countour lines

I would like to plot something that resembles a kdeplot using geoviews without actually plotting the contour lines. The geoplot library supports something like this:
How can I make such a plot in geoviews?
Here is a very basic example of the kind of kdeplot I am managing to generate via geoviews, which by default plots the black lines that separates different intensities:
import geoviews.tile_sources as gts
import geoviews as gv
import numpy as np
from sklearn.neighbors import KernelDensity
gv.extension('bokeh')
np.random.seed(2021)
# Define extent of GPS coordinates
xmean = -12.015358
ymean = -76.990665
xmin, xmax = xmean*0.9, xmean*1.1
ymin, ymax = ymean*0.9, ymean*1.1
xrange = np.linspace(xmin, xmax, num=1000)
yrange = np.linspace(ymin, ymax, num=1000)
# Sample GPS coordinates
latlon = np.vstack([np.random.choice(xrange, 100), np.random.choice(yrange, 100)]).T
# Fit a gaussian kernel
kde = KernelDensity(bandwidth=0.03)
kde.fit(latlon)
# Apply gaussian kernel on grid
X, Y = np.mgrid[xmin:xmax:100j, ymin:ymax:100j]
positions = np.vstack([X.ravel(), Y.ravel()])
Z = kde.score_samples(positions.T).reshape(X.shape)
# Define Map
kde_plot = gv.FilledContours((Y, X, Z)).opts(cmap='PuBu', fill_alpha=0.5)
background_plot = gts.CartoLight
geomap = (kde_plot * background_plot).opts(width=800, height=550, xaxis=None, yaxis=None)
geomap
I could not find any parameter settings in gv.FilledCountours that remove these lines.
The argument you have to use is line_color and in your case you want to set it to None.
Applying the change to this line of code
kde_plot = gv.FilledContours((Y, X, Z)).opts(cmap='PuBu', fill_alpha=0.5, line_color=None)
you will get this plot as a return.

Colormap with colored quiver

I am plotting a map with arrows on top of it. These arrows represent winddirections, average windspeed (per direction) and the occurence (per direction).
The direction is indicated by the direction of the arrow. The length of the arrow indicated the average windspeed in that direction. The color of the arrow indicates the occurence of winds in such a direction.
This all works fine with the script below:
windData = pd.read_csv(src+'.txt'), sep='\t', names=['lat', 'lon', 'wind_dir_start', 'wind_dir_end', 'total_num_data_points','num_data_points', 'avg_windspeed']).dropna()
# plot map
m = Basemap(llcrnrlon=minLon, llcrnrlat=minLat, urcrnrlon=maxLon, urcrnrlat=maxLat, resolution='i')
Left, Bottom = m(minLon, minLat)
Right, Top = m(maxLon, maxLat)
# get x y
x, y = m(windData['lon'], windData['lat'])
# angles
angleStart = -windData['wind_start']+90
angleStart[angleStart<0] = np.radians(angleStart[angleStart<0]+360.)
angleEnd = -windData['wind_end']+90
angleEnd[angleEnd<0] = np.radians(angleEnd[angleEnd<0]+360.)
angle = angleStart + math.radians(binSize/2.)
xux = np.cos(angle) * windData['avg_windspeed']
yuy = np.sin(angle) * windData['avg_windspeed']
# occurence
occurence = (windData['num_data_points']/windData['total_num_data_points'])
xi = np.linspace(minLon, maxLon, 300)
yi = np.linspace(minLat, maxLat, 300)
# plotting
## xux and yuy are used negatively because they are measured as "coming from" and displayed as "going to"
# To make things more readable I left a threshold for the occurence out
# I usually plot x, y, xux, yuy and the colors as var[occurence>threshold]
Q = m.quiver(x, y, -xux, -yuy, scale=75, zorder=6, color=cm.jet, width=0.0003*Width, cmap=cm.jet)
qk = plt.quiverkey(Q, 0.5, 0.92, 3, r'$3 \frac{m}{s}$', labelpos='S', fontproperties={'weight': 'bold'})
m.scatter(x, y, c='k', s=20*np.ones(len(x)), zorder=10, vmin=4.5, vmax=39.)
This plot shows the arrows well, but now I want to add a colormap that indicates the percentage of occurence next to the plot. How would I do this?
OK
Usual imports, plus import matplotlib
%matplotlib inline
import matplotlib
import matplotlib.pyplot as plt
import numpy as np
Fake the data to be plotted (tx for the MCVE)
NP = 10
np.random.seed(1)
x = np.random.random(NP)
y = np.random.random(NP)
angle = 1.07+np.random.random(NP) # NE to NW
velocity = 1.50+np.random.random(NP)
o = np.random.random(NP)
occurrence = o/np.sum(o)
dx = np.cos(angle)*velocity
dy = np.sin(angle)*velocity
Create a mappable so that Matplotib has no reason to complain "RuntimeError: No mappable was found to use for colorbar creation."
norm = matplotlib.colors.Normalize()
norm.autoscale(occurrence)
cm = matplotlib.cm.copper
sm = matplotlib.cm.ScalarMappable(cmap=cm, norm=norm)
sm.set_array([])
and plot the data
plt.quiver(x, y, dx, dy, color=cm(norm(o)))
plt.colorbar(sm)
plt.show()
References:
A logarithmic colorbar in matplotlib scatter plot
,
Drawing a colorbar aside a line plot, using Matplotlib
and
Different colours for arrows in quiver plot.
P.S. In recent (for sure in 3.+) Matplotlib releases the cm.set_array incantation is no more necessary
Do you want the colorbar to show the different wind speeds? If so, it might be sufficient to place plt.colorbar() between the lines Q = m.quiver(...) and qk = ....

Get data array from object in Python

I'm using a library which produces 3 plots given an object k.
I need to figure the data points (x,y,z) that produced these plot, but the problem is that the plots comes from a function from k.
The library I'm using is pyKriging and this is their github repository.
A simplified version of their example code is:
import pyKriging
from pyKriging.krige import kriging
from pyKriging.samplingplan import samplingplan
sp = samplingplan(2)
X = sp.optimallhc(20)
testfun = pyKriging.testfunctions().branin
y = testfun(X)
k = kriging(X, y, testfunction=testfun, name='simple')
k.train()
k.plot()
The full code, comments and output can be found here.
In summary, I'm trying to get the numpy array that produced these plots so I can create plots that follows my formatting styles.
I'm not knowledgeable about going into library codes in Python and I appreciate any help!
There is no single data array that produces the plot. Instead many arrays used for plotting are generated inside the kriging plot function.
Changing the filled contours to line contours is of course not a style option. One therefore needs to use the code from the original plotting function.
An option is to subclass kriging and implement a custom plot function (let's call it myplot). In this function, one can use contour instead of contourf. Naturally, it's also possible to change it completely to one's needs.
import pyKriging
from pyKriging.krige import kriging
from pyKriging.samplingplan import samplingplan
import numpy as np
import matplotlib.pyplot as plt
class MyKriging(kriging):
def __init__(self,*args,**kwargs):
kriging.__init__(self,*args,**kwargs)
def myplot(self,labels=False, show=True, **kwargs):
fig = plt.figure(figsize=(8,6))
# Create a set of data to plot
plotgrid = 61
x = np.linspace(self.normRange[0][0], self.normRange[0][1], num=plotgrid)
y = np.linspace(self.normRange[1][0], self.normRange[1][1], num=plotgrid)
X, Y = np.meshgrid(x, y)
# Predict based on the optimized results
zs = np.array([self.predict([xi,yi]) for xi,yi in zip(np.ravel(X), np.ravel(Y))])
Z = zs.reshape(X.shape)
#Calculate errors
zse = np.array([self.predict_var([xi,yi]) for xi,yi in zip(np.ravel(X), np.ravel(Y))])
Ze = zse.reshape(X.shape)
spx = (self.X[:,0] * (self.normRange[0][1] - self.normRange[0][0])) + self.normRange[0][0]
spy = (self.X[:,1] * (self.normRange[1][1] - self.normRange[1][0])) + self.normRange[1][0]
contour_levels = kwargs.get("levels", 25)
ax = fig.add_subplot(222)
CS = plt.contour(X,Y,Ze, contour_levels)
plt.colorbar()
plt.plot(spx, spy,'or')
ax = fig.add_subplot(221)
if self.testfunction:
# Setup the truth function
zt = self.testfunction( np.array(zip(np.ravel(X), np.ravel(Y))) )
ZT = zt.reshape(X.shape)
CS = plt.contour(X,Y,ZT,contour_levels ,colors='k',zorder=2, alpha=0)
if self.testfunction:
contour_levels = CS.levels
delta = np.abs(contour_levels[0]-contour_levels[1])
contour_levels = np.insert(contour_levels, 0, contour_levels[0]-delta)
contour_levels = np.append(contour_levels, contour_levels[-1]+delta)
CS = plt.contour(X,Y,Z,contour_levels,zorder=1)
plt.plot(spx, spy,'or', zorder=3)
plt.colorbar()
ax = fig.add_subplot(212, projection='3d')
ax.plot_surface(X, Y, Z, rstride=3, cstride=3, alpha=0.4)
if self.testfunction:
ax.plot_wireframe(X, Y, ZT, rstride=3, cstride=3)
if show:
plt.show()
sp = samplingplan(2)
X = sp.optimallhc(20)
testfun = pyKriging.testfunctions().branin
y = testfun(X)
k = MyKriging(X, y, testfunction=testfun, name='simple')
k.train()
k.myplot()

How do I plot a function with multiple different values for constants?

I am trying to plot this function: y(x) = (e^-ax)cos(x)
For x value spanning the interval (0,4pi) and 'a' ranging from 0 to 1 in steps of 0.25. I have managed to successfully plot this for a=0.
However, I am wondering if I can write some code that will allow me to plot it for the range of 'a' values, instead of having to rewrite the code for each different 'a' value.
Here is the code I have: (Note y = cos(x) as this is for a = 0)
from numpy import *
import pylab as p
# function to plot ( a = 0 )
x = linspace(0, 4*pi, 100)
y = cos(x)
#plot the function
p.plot(x,y,'o')
# axis annotation
p.xlabel('x-variable')
p.ylabel('y=(e**-ax)cosx')
# graph title
p.title('Plot of function')
#set axis range
p.axis([-0.5, 4*pi, -1.3, 1.3])
# turn grid on
p.grid(True)
p.show()
Thank you!
assuming that you want everything on the same plot...
from numpy import linspace, pi, cos, exp
import pylab as p
for a in p.frange(xini=0, xfin=1, delta=0.25):
x = linspace(0, 4*pi, 100)
y = exp(-a*x)*cos(x)
p.plot(x,y,'o')
# axis annotation
p.xlabel('x-variable')
p.ylabel('y=(e**-ax)cosx')
# graph title
p.title('Plot of function')
#set axis range
p.axis([-0.5, 4*pi, -1.3, 1.3])
# turn grid on
p.grid(True)
p.show()

Quiver basemap plot - how to make the quiver density latitude-dependent?

I want to do a quiver plot on a polar basemap plot. I have a regular lat/lon grid, and because there are more grid boxes at the higher latitudes, my code plots as many quivers at the pole as on the equator, so they overlap etc. How can I make the density of quivers latitude-dependent?
This is the code I use
import numpy as np
from mpl_toolkits.basemap import Basemap, addcyclic
import matplotlib.pyplot as plt
m_mu = Basemap(projection='npstere',boundinglat=10,lon_0=0,resolution='l',round=True)
lats=np.arange(0.,91.,15.)
lons=np.arange(-180.,181.,30.)
valin_u=np.array([[np.random.randn() for y in range(len(lons))] for x in range(len(lats))])
valin_v=np.array([[np.random.randn() for y in range(len(lons))] for x in range(len(lats))])
del x,y
valin = np.sqrt( valin_u**2 + valin_v**2 )
mu_cyclic, lons_cyclic = addcyclic(valin, lons)
mu_cyclic_u, lons_cyclic = addcyclic(valin_u, lons)
mu_cyclic_v, lons_cyclic = addcyclic(valin_v, lons)
grid = np.meshgrid( lons_cyclic, lats )
x, y = m_mu( *grid)
plt.figure()
cs = m_mu.pcolormesh(x, y, mu_cyclic)
csquiv = m_mu.quiver(x[::1,::1], y[::1,::1], mu_cyclic_u[::1,::1], mu_cyclic_v[::1,::1])
plt.show()
I guess I could write a function to set a latitude-dependent number of the values to .nan, but hopefully there is a better solution?
Many thanks for your help :-)
Sabine

Categories