Basemap streamplot blank sphere - python

I want to make a streamplot in Basemap module, but I get a blank sphere. Please help me resolve this problem. I use matplotlib 1.3 and ordinary streamplot is working fine.
import matplotlib.pyplot as plt
import numpy as np
from mpl_toolkits.basemap import Basemap
map = Basemap(projection='ortho',lat_0=45,lon_0=-100,resolution='l')
# draw lat/lon grid lines every 30 degrees.
map.drawmeridians(np.arange(0,360,30))
map.drawparallels(np.arange(-90,90,30))
# prepare grids
lons = np.linspace(0, 2*np.pi, 100)
lats = np.linspace(-np.pi/2, np.pi/2, 100)
lons, lats = np.meshgrid(lons, lats)
# parameters for vector field
beta = 0.0
alpha = 1.0
u = -np.cos(lats)*(beta - alpha*np.cos(2.0*lons))
v = alpha*(1.0 - np.cos(lats)**2)*np.sin(2.0*lons)
speed = np.sqrt(u*u + v*v)
# compute native map projection coordinates of lat/lon grid.
x, y = map(lons*180./np.pi, lats*180./np.pi)
# contour data over the map.
cs = map.streamplot(x, y, u, v, latlon = True, color = speed, cmap=plt.cm.autumn, linewidth=0.5)
plt.show()

I can't exactly tell you what's wrong, but from the matplotlib.streamplot manual:
matplotlib.pyplot.streamplot(x, y, u, v, density=1, linewidth=None,
color=None, cmap=None, norm=None, arrowsize=1, arrowstyle=u'-|>',
minlength=0.1, transform=None, zorder=1, hold=None)ΒΆ
Draws streamlines of a vector flow.
x, y : 1d arrays
an evenly spaced grid.
u, v : 2d arrays
x and y-velocities. Number of rows should match length of y, and the number of columns should match x.
Additionally from matplotlib.basemap.streamplot you can read that
If latlon keyword is set to True, x,y are intrepreted as longitude and latitude in degrees.
Which corresponds to the fact that x and y should be 1D arrays (lat, lon). However in your example x and y are
>>> np.shape(x)
(100, 100)
>>> np.shape(y)
(100, 100)
Then again you call the method map() "to compute native map projection coordinates of lat/lon grid" which is coincidentally the same as the name of your basemap.map. So it depends on which one do you want? Because both will return a value! (or better to say, both will return an error)
Aditionally check out the values you have in your u array. They are of range e-17. While other values are easily in the range e+30. IIRC the way you get streamlines is by solving a differential equation in which points you sent it as values are used as parameters at coordinates you sent. It's not hard to imagine a that while calculating something with these numbers a floating point round-off occurs and you suddenly start getting NaN or 0 values.
Try to scale your example better or if you want to pursue the solution to the end you can try and use np.seterr to get a more detailed idea where it fails.
Sorry I couldn't have been of a bigger help.

Related

Extrapolated contour plot using numpy

I have data which is taken along the diameter of a circle in 1 mm increments i.e. starting at 90 degrees and ending at 270 degrees. I want to extrapolate the data at each 1 mm increment to fill in the rest of the circle (at that radial point). For example:
10 mm circle:
>Distance along diameter (mm) Value (a.u.)
>1 208
>5 210 (centre `(0,0)`)
>7 209
>10 208
Now I want something similar to: this image. With a legend/key on the right hand side and with the data extrapolated, so that the transitions are 'smooth' between the radial data points. Obviously, only one radius of values should really be needed, but I want the other half of the values in the diameter to reinforce the values observed in the first radius.
So, I want the values of the bottom radius to 'blend' in with the values of the top radius along the diameter from 180 degrees to 0 degrees. Is this clear?
My initial (terrible):
import numpy as np
import matplotlib.pyplot as plt
data = np.genfromtxt('data.txt',delimiter=',')
r = data[:,][:,0]
values = data[:,][:,1]
theta = np.zeros(len(data))
r, theta = np.meshgrid(r, theta)
plt.figure()
fig, ax = plt.subplots(subplot_kw=dict(projection='polar'))
ax.contourf(theta, r, values)
plt.show()
I understand that the input to ax.contourf must be a 2D array. I have no real theta values for my data, however. I just want a series of concentric circles, so I have modified my data as:
80,0,208.1790755
80,90,208.1790755
80,180,208.1790755
80,270,208.1790755
79,0,208.1322654
79,90,208.1322654
79,180,208.1322654
79,270,208.1322654
76,0,208.1804241
76,90,208.1804241
76,180,208.1804241
76,270,208.1804241
etc
I can't see why the following won't work, though:
data = np.genfromtxt('data.txt',delimiter=',')
r = data[:,][:,0]
values = np.array(data[:,][:,2])
theta = np.radians(data[:,][:,1])
r, theta = np.meshgrid(r, theta)
fig, ax = plt.subplots(subplot_kw=dict(projection='polar'))
ax.contourf(theta, r, values)
plt.show()
The reason is that ax.contourf(x, y, z) only takes a 2D array as z. The r array is already of the correct form, so values_2d = values*r/r will put your values into a 2D array which can be passed to ax.contourf() as ax.contourf(theta, r, values_2d, 40). 40 defines the resolution of the data, i.e. 40 discrete steps between the minimum and maximum values of your z data.

Coordinating basemap quiver and matplotlib arrow

I am trying to compare vectors of wind in matplotlib between gridded model output locations (via quiver on a basemap map) and scattered stations (via matplotlib arrow). The locations for both are in lat/lon, but wind vectors are in m/s.
When combined, I want the colors and lengths to vary by magnitude and for both qualities to be scaled the same way for the quiver and arrow data. I have given an example below where the quiver plot looks OK and is scaled in absolute length (inches). I don't know what to do to for arrow() to match. In the example I've divided it by SCALE to give a sense of what I'd like the final image to look like.
import numpy as np
import matplotlib.pylab as plt
from mpl_toolkits.basemap import Basemap
X, Y = np.meshgrid(np.arange(-123,-121,0.3),np.arange(37,39,0.3))
U = np.cos(X+123)*12
V = np.sin(Y-37)*12
mag = np.hypot(U,V)
fig,ax=plt.subplots(1)
m=Basemap(projection ='cyl',resolution='f',llcrnrlat=37,llcrnrlon=-123,
urcrnrlat=39,urcrnrlon=-121,ax=ax)
quiv = m.quiver(X,Y,U,V,mag,zorder=2,latlon=True,scale=30,scale_units='inches')
# Scattered points won't be on the grid
x0=X[2,2] - 0.025
y0=Y[2,2]
u0=U[2,2]
v0=V[2,2] + 0.5
SCALE = 72.
plt.arrow(x0,y0,u0/SCALE,v0/SCALE)
plt.show()
It's not terribly clear from the matplotlib documentation (in my opinion), but quiver does accept 1D arrays for all of X, Y, U and V, which do not need to be uniformly spaced. The basemap documentation gets that wrong, or is at least even more unclear. So as long as you form your scattered stations data into 1D arrays, you should be fine.
I added some random arrows to your plot by replacing your scattered points section with this (if you use the same seed you should get the same arrows):
# Make scattered locations
np.random.seed(33)
x0 = np.random.rand(5)*2.0 - 123
y0 = np.random.rand(5)*2.0 + 37
# Make some velocities
u0 = np.random.randn(5)*3 + 10
v0 = np.random.randn(5)*3 + 10
q2 = m.quiver(x0, y0, u0, v0, latlon=True, scale=30, scale_units='inches')
And this is the plot that I get (I use the YlGnBu_r colormap by default).
Be aware that if you start to use anything other than a cylindrical-type projection (and if your U and V are expressed in east-west and north-south) you will need to rotate the vectors to match the projection using the rotate_vector method.
You need to convert map coordinates into Cartesian coordinates by x,y = m(lon, lat), after this, plt.quiver(x,y, u, v) or m.quiver(x,y,u,v) will do the same job.

Points that follow a 1/R density distribution in an XY grid?

I have a XY grid with some gridpoints having certain values assigned to them, in this case, each value means a certain mass, so basically point masses in a grid. I now want to obtain a set of points which follow a density distribution of 1/R, where R is the distance from the center, so R = sqrt(x^2 + y^2). By density distribution, I mean the number of points has to fall off as 1/R. How would I go about coding this?
My code is below:
import numpy as np
x = np.linspace(-50,50,100)
y = np.linspace(-50,50,100)
X, Y = np.meshgrid(x,y)
zeta_a = (25,25)
zeta_b = (-10,5)
M_a = 150
M_b = 150
The zeta_a and zeta_b correspond to 2 point masses having masses of 150 units. I also need to perform follow up calculations using these points, so i'd also like to know how to use a more general format rather than using 'a','b','c' for n-point masses.
Thanks for your help.
Assuming I understood your question (if not comments are welcomed):
The way to create any given distribution is by interpolating over the inverse of the distribution CDF. This is my function to do it:
import numpy as np
import matplotlib.pyplot as plt
def randdist(PDF, x, n):
"""Create a distribution following PDF(x). PDF and x
must be of the same length. n is the number of samples."""
fp = np.random.rand(n,)
CDF = np.cumsum(PDF)
return np.interp(fp, CDF, x)
Now, in your case we're going to work in polar coordinates with R distributed as 1/r and Theta uniformly distributed:
num = 1000 # The number of points
r = np.linspace(-50, 50, 100)
PDF = np.abs(1/r)
PDF = PDF/np.sum(PDF) # PDF should be normalized
R = randdist(PDF, r, num)
Theta = 2*np.pi*np.random.rand(num,)
Now let's create the points x and y vectors
x = [R[k]*np.cos(Theta[k]) for k in range(num)]
y = [R[k]*np.sin(Theta[k]) for k in range(num)]
To plot
plot(x,y,'.')
Note that in my answer there is a hard cutoff at r=50. There are ways to overcome this but for now I leave it as it is.
Now you seem to also want to embed the points inside a 2D grid, much like a histogram. You can do that using
z, _, _ = np.histogram2d(x, y, [100, 100])

Plotting interpolated values using LinearNDInterpolator (Python)

I am using the LinearNDInterpolator on some (x, y, z) data, using the following script. However, I cannot figure out how to go from the interpolated data to plotting/showing the interpolation in heatmap form? Am I missing something like setting up a meshgrid based on the min and max of x and y? Any help or an example would be great!
import numpy as np
import scipy.interpolate
x = np.array([-4386795.73911443, -1239996.25110694, -3974316.43669208,
1560260.49911342, 4977361.53694849, -1996458.01768192,
5888021.46423068, 2969439.36068243, 562498.56468588,
4940040.00457585])
y = np.array([ -572081.11495993, -5663387.07621326, 3841976.34982795,
3761230.61316845, -942281.80271223, 5414546.28275767,
1320445.40098735, -4234503.89305636, 4621185.12249923,
1172328.8107458 ])
z = np.array([ 4579159.6898615 , 2649940.2481702 , 3171358.81564312,
4892740.54647532, 3862475.79651847, 2707177.605241 ,
2059175.83411223, 3720138.47529587, 4345385.04025412,
3847493.83999694])
# Create coordinate pairs
cartcoord = zip(x, y)
# Interpolate
interp = scipy.interpolate.LinearNDInterpolator(cartcoord, z)
Edit:
Based on #Spinor's solution, and using Python 2.7, the following code gives me what I'm looking for (approach 1). Is there a way to increase my density of the interpolated points?
The dataset yields the following plot:
Needless to say, I did not expect the results to be circular, since the (lat,lon) coordinates are taken from an equirectrangular projection map. On further investigation, I think this is simply mapped on a different projection.
I will assume that you are trying to interpolate values of z.
Now, what happens when you do call interpolation function? It creates the entire landscape of the inputs (x and y) and the outputs (z). In the code above, you didn't really ask for its value at any point. To use this function, you need to specify the inputs and it will give you the interpolated output.
You used the function scipy.interpolate.LinearNDInterpolator which is constructed by triangulating the input data and on each triangle performing linear barycentric interpolation. Depending on your inputs, there are likely to be regions where this breaks down and you get Nan. For instance, try this in your code
print interp(-4386790, 3720137)
This is within the limits of the min-max of your x and y. We could set the Nan to zero via the fill_value argument if that is acceptable to you.
Read up on the docs. Often people might find the following function acceptable as well, scipy.interpolate.interp2d. It uses spline interpolation instead. In the code below, I've implemented both functions (the former with nan values set to 0) and plotted them on a heatmap.
As for the heatmap, it is as you've suspected. You have to create a grid of values. Below is my the output graphs for LinearNDInterpolator with nan set to zero and interp2d as well as the codes.
Using LinearNDInterpolator(cartcoord, z, fill_value=0)
Using interp2d(x, y, z)
P.S. I am using Python3. If you run into issues in Python2, remove the list from cartcoord = list(zip(x, y)).
import matplotlib.pyplot as plt
import numpy as np
import scipy.interpolate
x = np.array([-4386795.73911443, -1239996.25110694, -3974316.43669208,
1560260.49911342, 4977361.53694849, -1996458.01768192,
5888021.46423068, 2969439.36068243, 562498.56468588,
4940040.00457585])
y = np.array([ -572081.11495993, -5663387.07621326, 3841976.34982795,
3761230.61316845, -942281.80271223, 5414546.28275767,
1320445.40098735, -4234503.89305636, 4621185.12249923,
1172328.8107458 ])
z = np.array([ 4579159.6898615 , 2649940.2481702 , 3171358.81564312,
4892740.54647532, 3862475.79651847, 2707177.605241 ,
2059175.83411223, 3720138.47529587, 4345385.04025412,
3847493.83999694])
# Create coordinate pairs
cartcoord = list(zip(x, y))
X = np.linspace(min(x), max(x))
Y = np.linspace(min(y), max(y))
X, Y = np.meshgrid(X, Y)
# Approach 1
interp = scipy.interpolate.LinearNDInterpolator(cartcoord, z, fill_value=0)
Z0 = interp(X, Y)
plt.figure()
plt.pcolormesh(X, Y, Z0)
plt.colorbar() # Color Bar
plt.show()
# Approach 2
func = scipy.interpolate.interp2d(x, y, z)
Z = func(X[0, :], Y[:, 0])
plt.figure()
plt.pcolormesh(X, Y, Z)
plt.colorbar() # Color Bar
plt.show()

Accelerate the SciPy Griddata Function

I have 3 numpy arrays:
X and Y are longitudes and latitudes, and Z are digital elevation values. All the X, Y, and Z covers the RED polygon.
However, when I make grid of the data as follows it covers the Blue polygon.
x = X.ravel()
y = Y.ravel()
z = Z.ravel()
xi = np.linspace(np.min(x), np.max(x), 5000) #As you see the size of data are so large (5000 by 5000)
yi = np.linspace(np.max(y), np.min(y), 5000)
zi = griddata((x, y), z, (xi.reshape(1,-1), yi.reshape(-1,1)), method='nearest') #from
scipy.interpolate import griddata
How can I make it to cover only RED polygon?
The computation is also very slow, if I make it possible to compute only for within the RED polygon it would be faster also.
Slowness, you are interpolating your data on a 5000x5000 grid. OK. on what device are you going to represent your interpolated data? Is it wort the effort? Possibly not...
.
The shape... this is a shot in the dark but is it possible that somewhere in your code you forgot to project the spherical coordinates over a plane? (aka mapping) --- another possibility is that your data is specified in a generic quadrilateral, but then you plot the z in terms of a matrix zi, defined on a rectangular grid. The two possibilities are not incompatible.

Categories