def contains(self, lon, lat): """ Return a boolean array of same shape as lon and lat with points contained in the patch """ lon = np.asarray(lon) lat = np.asarray(lat) mlon, mlat = self.coords assert lon.shape == lat.shape, "lon, lat must have the same shape" #lon = rectify_longitude(lon, lon0=self.lon0, sort=False) # first fix lon0 to match new data lon0 = get_lon0(*lon.flatten()) if lon0 != self.lon0: self = copy(self) mlon, self.mask = rectify_longitude_data(mlon, self.mask, lon0) if not (np.all(lon == mlon) and np.all(lat == mlat)): self = copy(self) # interpolate the mask onto the input grid maskf = interp(np.asarray(self.mask, dtype=float), mlon, mlat, lon, lat) mask = maskf >= 0.5 else: mask = self.mask return mask
def interp2(lon, lat, data, lon2, lat2, order=1): """ Equivalent to matlab interp2 order: - 0 : nearest neighbout - 1 : linear (default) """ from dimarray.compat.basemap import interp if np.ndim(lon2) == 1: lon2, lat2 = np.meshgrid(lon2, lat2) return interp(data, lon, lat, lon2, lat2, order=order)
def interpolate(self, lon, lat): """ interpolate the mask lon, lat: 1-D arrays to be passed to meshgrid """ assert lon.ndim == 1 and lat.ndim == 1, "must be 1-D coordinates compatible with meshgrid" # first fix lon0 to match new data lon0 = get_lon0(*lon.flatten()) mlon, mlat = self.coords mlon, mask = rectify_longitude_data(mlon, self.mask, lon0) # make input coords is 2-D #if lon.ndim == 1 and meshgrid: lon2, lat2 = np.meshgrid(lon, lat) maskf = interp(np.asarray(self.mask, dtype=float), mlon, mlat, lon2, lat2) mask = maskf >= 0.5 return Mask(lon, lat, mask, lon0)
def interp2d(dim_array, newaxes, dims=(-2, -1), order=1, clip=False): """ bilinear interpolation Parameters ---------- dim_array : DimArray instance newaxes : sequence of two array-like, or dict. axes on which to interpolate dims : sequence of two axis names or integer rank, optional Indicate dimensions which match `newaxes`. By default (-2, -1) (last two dimensions). order : int, optional order of the interpolation (default 1 for linear) clip : bool, optional if True, values in newaxes outside the range are clipped to closest values Returns ------- dim_array_int : DimArray instance interpolated array Examples -------- >>> from dimarray import DimArray, interp2d >>> x = np.array([0, 1, 2]) >>> y = np.array([0, 10]) >>> a = DimArray([[0,0,1],[1,0.,0.]], [('y',y),('x',x)]) >>> a dimarray: 6 non-null elements (0 null) 0 / y (2): 0 to 10 1 / x (3): 0 to 2 array([[ 0., 0., 1.], [ 1., 0., 0.]]) >>> newx = [0.5, 1.5] >>> newy = np.linspace(0,10,5) >>> ai = interp2d(a, [newy, newx]) >>> ai dimarray: 10 non-null elements (0 null) 0 / y (5): 0.0 to 10.0 1 / x (2): 0.5 to 1.5 array([[ 0. , 0.5 ], [ 0.125, 0.375], [ 0.25 , 0.25 ], [ 0.375, 0.125], [ 0.5 , 0. ]]) Use dims keyword argument if new axes order does not match array dimensions >>> (ai == interp2d(a, [newx, newy], dims=('x','y'))).all() True >>> newx = [-1, 1] >>> newy = [-5, 0, 10] >>> interp2d(a, [newy, newx]) dimarray: 2 non-null elements (4 null) 0 / y (3): -5 to 10 1 / x (2): -1 to 1 array([[ nan, nan], [ nan, 0.], [ nan, 0.]]) >>> interp2d(a, [newy, newx], clip=True) dimarray: 6 non-null elements (0 null) 0 / y (3): -5 to 10 1 / x (2): -1 to 1 array([[ 0., 0.], [ 0., 0.], [ 1., 0.]]) """ # provided as a dictionary if isinstance(newaxes, dict): dims = newaxes.keys() newaxes = newaxes.values() if not isinstance(newaxes, list) or isinstance(newaxes, tuple): raise TypeError("newaxes must be a sequence of axes to interpolate on") if len(newaxes) != 2: raise ValueError("must provide two axis values to interpolate on") if len(dims) != 2: raise ValueError("must provide two axis names to interpolate on") x0 = dim_array.axes[dims[0]] y0 = dim_array.axes[dims[1]] # new axes xi, yi = newaxes xi2, yi2 = np.meshgrid(xi, yi) # requires 2-D grid xi = Axis(xi, x0.name) # convert to Axis yi = Axis(yi, y0.name) # transpose the array to shape .., y0, x0 (cartesian convention needed for interp) dims_orig = dim_array.dims dims_new = [d for d in dim_array.dims if d not in [x0.name, y0.name]] + [y0.name, x0.name] dim_array = dim_array.transpose(dims_new) if dim_array.ndim == 2: newvalues = interp(dim_array.values, x0.values, y0.values, xi2, yi2, masked=not clip) dim_array_int = dim_array._constructor(newvalues, [yi, xi]) else: # first reshape to 3-D, flattening everything except horizontal_coordinates coordinates # TODO: optimize by computing and re-using weights? dim_array = dim_array.group((x0.name, y0.name), reverse=True, insert=0) newvalues = [] for k, suba in dim_array.iter(axis=0): # iterate over the first dimension newval = interp(suba.values, x0.values, y0.values, xi2, yi2, masked=not clip) newvalues.append(newval) # stack the arrays together newvalues = np.array(newvalues) grouped_dim_array = dim_array._constructor(newvalues, [dim_array.axes[0], yi, xi]) dim_array_int = grouped_dim_array.ungroup(axis=0) # reshape back # ...replace old axis names by new ones of the projection dims_orig = list(dims_orig) # ...transpose dim_array_int = dim_array_int.transpose(dims_orig) # add metadata dim_array_int._metadata(dim_array._metadata()) return dim_array_int
def transform_vectors(u, v, to_crs, from_crs=None, \ xt=None, yt=None, masked=np.nan): """ Transform vector field array into a new coordinate system and \ interpolate values onto a new regular grid Assume the vector field is represented by an array of shape (2, Ny, Nx) Parameters ---------- u, v : GeoArray or other DimArray instances x- and y- vector components to_crs : str or dict or cartopy.crs.CRS instance grid mapping onto which the transformation should be done str : PROJ.4 str or cartopy.crs.CRS class name dict : CF parameters from_crs : idem, optional original grid mapping. Can be omitted if the grid_mapping attribute already contains the appropriate information, or if the horizontal coordinates are longitude and latitude. xt, yt : array-like (1-D), optional new coordinates to interpolate the array on will be deduced as min and max of new coordinates if not provided masked : bool or number, optional If False, interpolated values outside the range of input grid will be clipped to values on boundary of input grid If True, points outside the range of input grid are masked (set to NaN) If masked is set to a number, then points outside the range of xin and yin will be set to that number. Default is nan. Returns ------- transformed : GeoArray new 3-D GeoArray transformed and interpolated """ if not isinstance(u, DimArray) or not isinstance(v, DimArray): raise TypeError("u and v must be DimArray instances") if not isinstance(u, GeoArray): u = GeoArray(u) if not isinstance(v, GeoArray): v = GeoArray(v) # consistency check between u and v assert u.axes == v.axes , "u and v must have the same axes" if from_crs is None and hasattr(u, 'grid_mapping'): assert hasattr(v, 'grid_mapping') and u.grid_mapping == v.grid_mapping, 'u and v must have the same grid mapping' # get grid mapping instances from_crs = _get_crs(from_crs, u) to_crs = _get_crs(to_crs) # find horizontal coordinates x0, y0 = _check_horizontal_coordinates(u) # Transform coordinates and prepare regular grid for interpolation x0_interp, y0_interp, xt, yt = _inverse_transform_coords(from_crs, to_crs, xt, yt, x0, y0) # Transform vector components x0_2d, y0_2d = np.meshgrid(x0, y0) if masked is True: masked = np.nan # use NaN instead of MaskedArray _constructor = u._constructor if u.ndim == 2: # First transform vector components onto the new coordinate system _ut, _vt = to_crs.transform_vectors(from_crs, x0_2d, y0_2d, u.values, v.values) # Then interpolate onto regular grid _ui = interp(_ut, x0.values, y0.values, x0_interp, y0_interp, masked=masked) ut = _constructor(_ui, [yt, xt]) _vi = interp(_vt, x0.values, y0.values, x0_interp, y0_interp, masked=masked) vt = _constructor(_vi, [yt, xt]) else: # first reshape to 3-D components, flattening everything except horizontal coordinates # TODO: optimize by computing and re-using weights? obj = stack([u, v], axis='vector_components', keys=['u','v']) obj = obj.group(('vector_components', x0.name, y0.name), reverse=True, insert=0) # newvalues = [] for k, suba in obj.iter(axis=0): # iterate over the first dimension # First transform vector components onto the new coordinate system _ut, _vt = to_crs.transform_vectors(from_crs, x0_2d, y0_2d, suba.values[0], suba.values[1]) # Then interpolate onto regular grid _ui = interp(_ut, x0.values, y0.values, x0_interp, y0_interp, masked=masked) _vi = interp(_vt, x0.values, y0.values, x0_interp, y0_interp, masked=masked) newvalues.append(np.array([_ui, _vi])) # stack the arrays together newvalues = np.array(newvalues) # 4-D : grouped, vector_components, y, x grouped_obj = _constructor(newvalues, [obj.axes[0], obj.axes[1], yt, xt]) ut, vt = grouped_obj.ungroup(axis=0).swapaxes('vector_components',0) # add metadata ut._metadata(u._metadata()) vt._metadata(v._metadata()) _add_grid_mapping_metadata(ut, to_crs) _add_grid_mapping_metadata(vt, to_crs) return ut, vt
def transform(geo_array, to_crs, from_crs=None, \ xt=None, yt=None, masked=np.nan): """ Transform scalar field array into a new coordinate system and \ interpolate values onto a new regular grid Parameters ---------- geo_array : GeoArray or other DimArray instance to_crs : str or dict or cartopy.crs.CRS instance grid mapping onto which the transformation should be done str : PROJ.4 str or cartopy.crs.CRS class name dict : CF parameters from_crs : idem, optional original grid mapping. Can be omitted if the grid_mapping attribute already contains the appropriate information, or if the horizontal coordinates are longitude and latitude. xt, yt : array-like (1-D), optional new coordinates to interpolate the array on will be deduced as min and max of new coordinates if not provided masked : bool or number, optional If False, interpolated values outside the range of input grid will be clipped to values on boundary of input grid If True, points outside the range of input grid are set to NaN If masked is set to a number, then points outside the range of xin and yin will be set to that number. Default is nan. Returns ------- transformed : GeoArray new GeoArray transformed Attempt is made to document the projection with CF-conform metadata Examples -------- """ # local import since it's quite heavy if not isinstance(geo_array, DimArray): raise TypeError("geo_array must be a DimArray instance") if not isinstance(geo_array, GeoArray): geo_array = GeoArray(geo_array) # find horizontal coordinates x0, y0 = _check_horizontal_coordinates(geo_array) # transpose the array to shape .., y0, x0 (cartesian convention needed for meshgrid) dims_orig = geo_array.dims dims_new = [d for d in geo_array.dims if d not in [x0.name, y0.name]] + [y0.name, x0.name] geo_array = geo_array.transpose(dims_new) #assert geo_array.dims.index(x0.name) > geo_array.axes[ # get cartopy.crs.CRS instances from_crs = _get_crs(from_crs, geo_array) to_crs = _get_crs(to_crs) # Transform coordinates and prepare regular grid for interpolation x0_interp, y0_interp, xt, yt = _inverse_transform_coords(from_crs, to_crs, xt, yt, x0, y0) if masked is True: masked = np.nan # use NaN instead of MaskedArray if geo_array.ndim == 2: #newvalues = interp(geo_array.values, xt_2d, yt_2d, xt_2dr, yt_2dr) newvalues = interp(geo_array.values, x0.values, y0.values, x0_interp, y0_interp, masked=masked) transformed = geo_array._constructor(newvalues, [yt, xt]) else: # first reshape to 3-D, flattening everything except horizontal_coordinates coordinates # TODO: optimize by computing and re-using weights? obj = geo_array.group((x0.name, y0.name), reverse=True, insert=0) newvalues = [] for k, suba in obj.iter(axis=0): # iterate over the first dimension #newval = interp(suba.values, xt_2d, yt_2d, xt_2dr, yt_2dr) newval = interp(suba.values, x0.values, y0.values, x0_interp, y0_interp, masked=masked) newvalues.append(newval) # stack the arrays together newvalues = np.array(newvalues) grouped_obj = geo_array._constructor(newvalues, [obj.axes[0], yt, xt]) transformed = grouped_obj.ungroup(axis=0) # reshape back # ...replace old axis names by new ones of the projection dims_orig = list(dims_orig) dims_orig[dims_orig.index(x0.name)] = xt.name dims_orig[dims_orig.index(y0.name)] = yt.name # ...transpose transformed = transformed.transpose(dims_orig) # add metadata transformed._metadata(geo_array._metadata()) _add_grid_mapping_metadata(transformed, to_crs) return transformed
def interp2d(obj, order=1): """ 2-D interpolation function appled recursively on the object """ x0, y0 = obj.axes[x.name].values, obj.axes[y.name].values res = interp(obj.values, x0, y0, x1, y1, order=order) return obj._constructor(res, newaxes, **obj._metadata)