I'm trying to calculate vorticity in MetPy for multiple (consecutive) vertical levels. When I try to calculate it for a single level, everything works fine.
Here's the code; I've used the example for cross sections from https://unidata.github.io/MetPy/latest/examples/cross_section.html#sphx-glr-examples-cross-section-py.
import cartopy.crs as ccrs
import cartopy.feature as cfeature
import matplotlib.pyplot as plt
import numpy as np
import xarray as xr
import metpy.calc as mpcalc
from metpy.cbook import get_test_data
from metpy.interpolate import cross_section
from metpy.units import units
data = xr.open_dataset(get_test_data('narr_example.nc', False))
data = data.metpy.parse_cf().squeeze()
data_crs = data['Temperature'].metpy.cartopy_crs
lat = data['lat']
lon = data['lon']
f = mpcalc.coriolis_parameter(lat)
dx, dy = mpcalc.lat_lon_grid_deltas(lon, lat, initstring=data_crs.proj4_init)
Then the calculation of vorticity is performed.
vort = mpcalc.vorticity(data['u_wind'], data['v_wind'], dx, dy)
The traceback:
Traceback (most recent call last):
File "E:\Временные файлы\cross_section (1).py", line 63, in <module>
vort = mpcalc.vorticity(data['u_wind'], data['v_wind'], dx, dy)
File "C:\ProgramData\Miniconda3\lib\site-packages\metpy\xarray.py", line 436, in wrapper
return func(*args, **kwargs)
File "C:\ProgramData\Miniconda3\lib\site-packages\metpy\calc\kinematics.py", line 60, in wrapper
ret = func(*args, **kwargs)
File "C:\ProgramData\Miniconda3\lib\site-packages\metpy\calc\kinematics.py", line 121, in vorticity
dudy = first_derivative(u, delta=dy, axis=-2)
File "C:\ProgramData\Miniconda3\lib\site-packages\metpy\calc\tools.py", line 920, in wrapper
return preprocess_xarray(func)(f, **kwargs)
File "C:\ProgramData\Miniconda3\lib\site-packages\metpy\xarray.py", line 436, in wrapper
return func(*args, **kwargs)
File "C:\ProgramData\Miniconda3\lib\site-packages\metpy\calc\tools.py", line 1014, in first_derivative
combined_delta = delta[tuple(delta_slice0)] + delta[tuple(delta_slice1)]
File "C:\ProgramData\Miniconda3\lib\site-packages\pint\quantity.py", line 1400, in __getitem__
value = self._magnitude[key]
IndexError: too many indices for array
I'm absolutely stuck. Searching "metpy multiple levels calculations" (no actual quotes) gives no relevant results.
The doc says:
metpy.calc.vorticity(u, v, dx, dy)[source]
Calculate the vertical vorticity of the horizontal wind.
Parameters:
u ((M, N) ndarray) – x component of the wind
v ((M, N) ndarray) – y component of the wind
dx (float or ndarray) – The grid spacing(s) in the x-direction. If an array, there should be one item less than the size of u along the applicable axis.
dy (float or ndarray) – The grid spacing(s) in the y-direction. If an array, there should be one item less than the size of u along the applicable axis.
dim_order (str or None, optional) – The ordering of dimensions in passed in arrays. Can be one of None, 'xy', or 'yx'. 'xy' indicates that the dimension corresponding to x is the leading dimension, followed by y. 'yx' indicates that x is the last dimension, preceded by y. None indicates that the default ordering should be assumed, which is ‘yx’. Can only be passed as a keyword argument, i.e. func(…, dim_order=’xy’).
Returns:
(M, N) ndarray – vertical vorticity
I conclude that the input can have more than 2 dimensions, but 3-dimensional input (as it is in my case) gives errors. What can be done to fix them?
I'm absolutely new to Python, so I could've made a stupid mistake.
Unfortunately, the error message that comes up isn't that helpful in this case if you don't know what to look for!
The problem with the vorticity function call in your example is that the dimensionality of your input variables do not match. data['u_wind'] and data['v_wind'] are 3D arrays with shape (29, 118, 292), but dx and dy, since they were computed from lat_lon_grid_deltas, are 2D arrays with shapes (118, 291) and (117, 292) respectively. And so, we need to obtain arrays that broadcast appropriately...there are many different ways that you could do this, but here are two options that I would recommend:
Option 1: Manual Broadcasting
Since the "extra" dimension that dx and dy are missing is the first dimension (in the vertical), we can just make dx and dy into properly aligned 3D arrays by inserting a size-one leading dimension:
dx, dy = mpcalc.lat_lon_grid_deltas(lon, lat, initstring=data_crs.proj4_init)
dx = dx[None, :]
dy = dy[None, :]
vort = mpcalc.vorticity(data['u_wind'], data['v_wind'], dx, dy)
Option 2: Use the grid_deltas_from_dataarray() helper function
MetPy also has a helper function to make pulling the grid deltas from an xarray DataArray easy. It also ensures that the broadcasting occurs properly, so you don't have to do it yourself. Using it in your example, it would be:
dx, dy = mpcalc.grid_deltas_from_dataarray(data['u_wind'])
vort = mpcalc.vorticity(data['u_wind'], data['v_wind'], dx, dy)
Related
I would like to interpolate many xarray datasets containig global climate data to one common grid. xarray actually has an interp() method which works fine, but as far as I can tell does not take any periodic boundries into account, although this is necessary when interpolating on a sphere. Instead, datapoints which are outside of the old grid are extrapolated or filled with NaNs. The interpolation is based on the scipy package, and I think other interpolation methods from scipy also do not support periodic boundaries.
I am considering using xesmf, but was wondering if there is an easier solution for this just using xarray?
I would prefer linear interpolation but am flexible in this regard.
This is possible, if you are willing to wrap your data in the longitudinal direction. With some assumptions (DataArray has coords 'lon' and 'lat', 'lon' spans almost 0-360 and doesn't quite go to the boundaries), and borrowing some ideas from this answer, this should work:
import numpy as np
import xarray as xr
data = np.arange(360 * 180).reshape(360, 180)
lon = np.linspace(0.5, 359.5, 360)
lat = np.linspace(-89.5, 89.5, 180)
da = xr.DataArray(
coords=dict(
lon=lon,
lat=lat,
),
data=data,
)
# These will both print 'nan' as lon is outside 0.5-359.5
print(da.interp(lon=0.3, lat=32).values)
print(da.interp(lon=359.7, lat=32).values)
def xr_add_cyclic_points(da):
"""
Add cyclic points at start and end of `lon` dimension of data array.
Inputs
da: xr.DataArray including dimensions (lat,lon)
"""
# Borrows heavily from cartopy.util.add_cyclic point, but adds at start and end.
lon_idx = da.dims.index('lon')
start_slice = [slice(None)] * da.ndim
end_slice = [slice(None)] * da.ndim
start_slice[lon_idx] = slice(0, 1)
end_slice[lon_idx] = slice(-1, None)
wrap_data = np.concatenate([da.values[tuple(end_slice)], da.values, da.values[tuple(start_slice)]], axis=lon_idx)
wrap_lon = np.concatenate([da.lon.values[-1:] - 360, da.lon.values, da.lon.values[0:1] + 360])
# Generate output DataArray with new data but same structure as input
outp_da = xr.DataArray(data=wrap_data,
coords=dict(lat=da.lat, lon=wrap_lon),
dims=da.dims,
attrs=da.attrs)
return outp_da
da_wrapped = xr_add_cyclic_points(da)
# These will print interpolated values.
print(da_wrapped.interp(lon=0.3, lat=32).values)
print(da_wrapped.interp(lon=359.7, lat=32).values)
I have 974 data points located on the surface of the unit sphere. The points are not ordered in any particular way. I wish to read in these data points, along with their corresponding polar coordinates (phi, theta). I then wish to interpolate onto a regular latitude-longitude grid using scipy.interpolate.SmoothSphereBivariateSpline. So far I have the following code:
import numpy as np
from scipy.interpolate import SmoothSphereBivariateSpline
#Read in the unorganized grid points
# and also shift so that phi in [0,pi] and theta in [0,2*pi)
leb = np.genfromtxt('grid.txt')
u, v = np.hsplit(leb, 2)
phi, theta = u[:,0], v[:,0]
theta += np.pi
#Read in the unorganized data values
data1 = np.genfromtxt('0p0_97p03.txt')
#Create the interpolator object
lut = SmoothSphereBivariateSpline(phi, theta, data1, s=350)
#Generate a regular lat-long grid to interpolate onto
N = 100
lat = np.linspace(0.0, np.pi, N)
lon = np.linspace(0.0, 2.0*np.pi, N)
lat, lon = np.meshgrid(lat, lon)
#Now interpolate onto the regular grid
data_inerp = lut(lat, lon)
In the above code, the arrays phi, theta and data1 are the unorganized data points, with each of these three arrays having a shape (974,). When i run this code I get the error message:
Traceback (most recent call last):
File "spherebiv.py", line 25, in <module>
data_inerp = lut(lat, lon)
File "/usr/lib/python2.7/dist-packages/scipy/interpolate/fitpack2.py", line 958, in __call__
raise ValueError("Error code returned by bispev: %s" % ier)
ValueError: Error code returned by bispev: 10
Now I don't understand this error code - it seems to be something to do with the underlying Fortran routine. Is there anything obvious I'm doing wrong here?
I'm having small troubles to understand how to implement a cubic interpolation on a grid.
I have 25 magnitude values stored on a 1D array. Each values represents the number inside a cell. Thus, the values are being showed on a 5x5 imshow image where each of the 25 resulting cells represents my stored values.
I have successfully used built-in interpolation parameters on imshow but I would really like to return the result of the interpolation.
data = 1D numpy array with 25 values
imshow(data .reshape(5, 5), origin='upper', interpolation='lanczos', cmap=cm.jet)
I was trying to go with this built-in scipy interpolation function, however I do not know it it is really the best way.
from scipy.interpolate import griddata
grid_x, grid_y = np.mgrid[0:4:100j, 0:4:200j]
grid_z2 = griddata(np.array([arange(5), arange(5)]).T, data, (grid_x, grid_y), method='cubic')
I'm receiving a ValueError with different number of values and points. If this is not really the best way I'm able to use other approaches.
Traceback (most recent call last):
File "file.py", line 100, in <module>
grid_z2 = griddata(np.array([arange(5), arange(5)]).T, data, (grid_x, grid_y), method='cubic')
File "C:\Program Files\Anaconda\lib\site-packages\scipy\interpolate\ndgriddata.py", line 212, in griddata
rescale=rescale)
File "scipy\interpolate\interpnd.pyx", line 840, in scipy.interpolate.interpnd.CloughTocher2DInterpolator.__init__ (scipy\interpolate\interpnd.c:9953)
File "scipy\interpolate\interpnd.pyx", line 78, in scipy.interpolate.interpnd.NDInterpolatorBase.__init__ (scipy\interpolate\interpnd.c:2342)
File "scipy\interpolate\interpnd.pyx", line 121, in scipy.interpolate.interpnd.NDInterpolatorBase._check_init_shape (scipy\interpolate\interpnd.c:3085)
ValueError: different number of values and points
I think I manage to use interpolate.RectBivariateSpline to satisfy my ends.
Here's the full complete code:
from scipy import interpolate
x = arange(5)
y = arange(5)
sp_x = interpolate.RectBivariateSpline(x, y, data)
nx = linspace(0, 4, 50)
ny = linspace(0, 4, 50)
n_data = sp_x(nx, ny)
I have two pictures, one that was the original and another one that I have modified so that it's translated up and left a bit and then rotated 90 degrees (so the shape of the picture is transposed as well).
Now I'd like to determine how many pixels (or any distance unit) the modified picture is translated from the original, as well as the degrees of rotation relative to the original. Phase correlation is supposed to solve this problem by first converting the coordinates to logpolar coordinates, then doing a number of things so that in the end you get a correlation matrix. From that matrix I'm supposed to find the peak and the (x,y) combination will reveal the translation and rotation somehow. This link explains it much better:
Phase correlation
This is the following code I have:
import scipy as sp
from scipy import ndimage
from PIL import Image
from math import *
import numpy as np
def logpolar(input,silent=False):
# This takes a numpy array and returns it in Log-Polar coordinates.
if not silent: print("Creating log-polar coordinates...")
# Create a cartesian array which will be used to compute log-polar coordinates.
coordinates = sp.mgrid[0:max(input.shape)*2,0:360]
# Compute a normalized logarithmic gradient
log_r = 10**(coordinates[0,:]/(input.shape[0]*2.)*log10(input.shape[1]))
# Create a linear gradient going from 0 to 2*Pi
angle = 2.*pi*(coordinates[1,:]/360.)
# Using scipy's map_coordinates(), we map the input array on the log-polar
# coordinate. Do not forget to center the coordinates!
if not silent: print("Interpolation...")
lpinput = ndimage.interpolation.map_coordinates(input,
(log_r*sp.cos(angle)+input.shape[0]/2.,
log_r*sp.sin(angle)+input.shape[1]/2.),
order=3,mode='constant')
# Returning log-normal...
return lpinput
def load_image( infilename ) :
img = Image.open( infilename )
img.load()
data = np.asarray( img, dtype="int32" )
return data
def save_image( npdata, outfilename ) :
img = Image.fromarray( np.asarray( np.clip(npdata,0,255), dtype="uint8"), "L" )
img.save( outfilename )
image = load_image("C:/images/testing_image1.jpg")
target = load_image("C:/images/testing_otherimage.jpg")
# Conversion to log-polar coordinates
lpimage = logpolar(image)
lptarget = logpolar(target)
# Correlation through FFTs
Fcorr = np.fft.fft(lpimage)*np.fft.fft(lptarget)
correlation = np.fft.ifft(Fcorr)
The problem I have now is that this code will give as output:
Traceback (most recent call last):
File "./phase.py", line 44, in <module>
lpimage = logpolar(image)
File "./phase.py", line 24, in logpolar
order=3,mode='constant')
File "C:\Python27\lib\site-packages\scipy\ndimage\interpolation.py", line 295, in map_coordinates
raise RuntimeError('invalid shape for coordinate array')
RuntimeError: invalid shape for coordinate array
As I just have a very superficial understanding of what exactly is happening in the whole phase correlation process, I'm unclear on what the problem is about. I have tried to see if something's wrong with the input so I added save_image(image,"C:/testing.jpg") right after loading the image to see if there's something wrong with the numpy array from my images. And sure enough, the images I convert to np array, cannot be converted back to an image. This is the error I get:
Traceback (most recent call last):
File "./phase.py", line 41, in <module>
save_image(image,"C:/testing.jpg")
File "./phase.py", line 36, in save_image
img = Image.fromarray( np.asarray( np.clip(npdata,0,255), dtype="uint8"), "L" )
File "C:\Python27\lib\site-packages\PIL\Image.py", line 1917, in fromarray
raise ValueError("Too many dimensions.")
ValueError: Too many dimensions.
Taking a peek at the original documentation didn't give me much inspiration on what the problem could be. I don't think the code to convert images to numpy arrays are wrong as I've tested for the type with print type(image) and the results looked legit. Yet I can't convert it back to an image. Any help I can get would be greatly appreciated.
I think the problem is that you are trying to input a 3D image array (R,G,B,A?), into your function. Whereas the input only takes a 2D arrays. Try using a single channel to determine the transformation. E.g.
image = load_image("/path/to/image")[:,:,0]
I am trying to convert an image from cartesian to polar so that I can unravel the image, but I am getting a runtime error. If you are curious how this looks visually, see this example.
Code:
import scipy
import scipy.ndimage
import numpy as np
from math import *
import cv2
def logpolar(input):
# This takes a numpy array and returns it in Log-Polar coordinates.
coordinates = np.mgrid[0:max(input.shape[:])*2,0:360] # We create a cartesian array which will be used to compute log-polar coordinates.
log_r = 10**(coordinates[0,:]/(input.shape[0]*2.)*log10(input.shape[1])) # This contains a normalized logarithmic gradient
angle = 2.*pi*(coordinates[1,:]/360.) # This is a linear gradient going from 0 to 2*Pi
# Using scipy's map_coordinates(), we map the input array on the log-polar coordinate. Do not forget to center the coordinates!
lpinput = scipy.ndimage.interpolation.map_coordinates(input,(log_r*np.cos(angle)+input.shape[0]/2.,log_r*np.sin(angle)+input.shape[1]/2.),order=3,mode='constant')
# Returning log-normal...
return lpinput
# Load image
image = cv2.imread("test.jpg")
result = logpolar(image)
Error message in console:
Traceback (most recent call last):
File "test.py", line 23, in <module>
result = logpolar(image)
File "test.py", line 15, in logpolar
lpinput = scipy.ndimage.interpolation.map_coordinates(input,(log_r*np.cos(angle)+input.shape[0]/2.,log_r*np.sin(angle)+input.shape[1]/2.),order=3,mode='constant')
File "/Library/Python/2.7/site-packages/scipy-0.13.0.dev_c31f167_20130415-py2.7-macosx-10.8-intel.egg/scipy/ndimage/interpolation.py", line 295, in map_coordinates
raise RuntimeError('invalid shape for coordinate array')
RuntimeError: invalid shape for coordinate array
My first guess would be that you are passing in a colour image which is 3 dimensional. At first glance I don't think your code could handle that.
My guess was based off of the error you pasted, specifically
"invalid shape for coordinate array"
When using higher dimensional arrays like that usually you have to pass extra parameters around specifying which axis to repeat the operations over and even then sometimes it does not work. I didn't see a repeated extra integer at the end of your argument lists so I figured you weren't trying to handle that case explicitly and might have forgotten to check your array dimensions after reading in the image.
Glad it helped :)