diff --git a/src/wrf/api.py b/src/wrf/api.py index ae8d6bd..a5ed04b 100644 --- a/src/wrf/api.py +++ b/src/wrf/api.py @@ -17,6 +17,7 @@ from .util import (npvalues, extract_global_attrs, extract_dim, extract_vars, extract_times, combine_files, is_staggered, get_left_indexes, iter_left_indexes, get_right_slices, get_proj_params) +from .coordpair import CoordPair from .version import __version__ __all__ = [] @@ -39,5 +40,6 @@ __all__ += ["npvalues", "extract_global_attrs", "extract_dim", "extract_vars", "extract_times", "combine_files", "is_staggered", "get_left_indexes", "iter_left_indexes", "get_right_slices", "get_proj_params"] +__all__ += ["CoordPair"] __all__ += ["__version__"] diff --git a/src/wrf/coordpair.py b/src/wrf/coordpair.py new file mode 100644 index 0000000..787b358 --- /dev/null +++ b/src/wrf/coordpair.py @@ -0,0 +1,131 @@ +from __future__ import (absolute_import, division, print_function, + unicode_literals) + +from .py3compat import py2round + + +def _binary_operator(operator): + def func(self, other): + if isinstance(other, CoordPair): + args = [ + None if getattr(self, attr) is None or getattr(other, attr) is None + else getattr(getattr(self, attr), operator)(getattr(other, attr)) + for attr in ("x", "y", "lat", "lon")] + else: + args = [ + None if getattr(self, attr) is None + else getattr(getattr(self, attr), operator)(other) + for attr in ("x", "y", "lat", "lon")] + + return CoordPair(*args) + + return func + + +def _unary_operator(operator): + def func(self): + args = [None if getattr(self, attr) is None + else getattr(getattr(self, attr), operator)() + for attr in ("x", "y", "lat", "lon")] + + return CoordPair(*args) + + return func + + +def _cmp_operator(operator): + def func(self, other): + vals = [getattr(getattr(self, attr), operator)(getattr(other, attr)) + for attr in ("x", "y", "lat", "lon") + if getattr(self, attr) is not None] + + return all(vals) + + return func + + +class CoordPair(object): + def __init__(self, x=None, y=None, lat=None, lon=None): + self.x = x + self.y = y + self.lat = lat + self.lon = lon + + + def __repr__(self): + args = [] + if self.x is not None: + args.append("x={}".format(self.x)) + args.append("y={}".format(self.y)) + + if self.lat is not None: + args.append("lat={}".format(self.lat)) + args.append("lon={}".format(self.lon)) + + argstr = ", ".join(args) + + return "{}({})".format(self.__class__.__name__, argstr) + + + def __str__(self): + return self.__repr__() + + + def xy_str(self, fmt="{:.4f}, {:.4f}"): + if self.x is None or self.y is None: + return None + + return fmt.format(self.x, self.y) + + + def latlon_str(self, fmt="{:.4f}, {:.4f}"): + if self.lat is None or self.lon is None: + return None + + return fmt.format(self.lat, self.lon) + + + def __round__(self, d=None): + args = [None if getattr(self, attr) is None + else py2round(getattr(self, attr), d) + for attr in ("x", "y", "lat", "lon")] + + return CoordPair(*args) + + + def __pow__(self, other, modulo=None): + if isinstance(other, CoordPair): + args = [ + None if getattr(self, attr) is None or getattr(other, attr) is None + else getattr(getattr(self, attr), "__pow__")(getattr(other, attr), + modulo) + for attr in ("x", "y", "lat", "lon")] + else: + args = [ + None if getattr(self, attr) is None + else getattr(getattr(self, attr), "__pow__")(other, modulo) + for attr in ("x", "y", "lat", "lon")] + + return CoordPair(*args) + + + def __rpow__(self, other): + return self.__pow__(other) + + +for operator in ("__add__", "__divmod__", "__floordiv__", "__mod__", + "__mul__", "__sub__", "__truediv__", "__radd__", + "__rdivmod__", "__rsub__", "__rmul__", "__rtruediv__", + "__rfloordiv__", "__rmod__"): + setattr(CoordPair, operator, _binary_operator(operator)) + + +for operator in ("__neg__", "__pos__", "__abs__", "__invert__"): + setattr(CoordPair, operator, _unary_operator(operator)) + + +for operator in ("__lt__", "__le__", "__eq__", "__ne__", "__gt__", "__ge__"): + setattr(CoordPair, operator, _cmp_operator(operator)) + + + \ No newline at end of file diff --git a/src/wrf/metadecorators.py b/src/wrf/metadecorators.py index 996e1ce..f635f0b 100644 --- a/src/wrf/metadecorators.py +++ b/src/wrf/metadecorators.py @@ -8,8 +8,9 @@ import numpy.ma as ma from .extension import _interpline from .util import (extract_vars, combine_with, either, from_args, arg_location, - is_coordvar, latlon_coordvars, CoordPair, npvalues, + is_coordvar, latlon_coordvars, npvalues, from_var, iter_left_indexes) +from .coordpair import CoordPair from .py3compat import viewkeys, viewitems, py3range, ucode from .interputils import get_xy_z_params, get_xy from .config import xarray_enabled @@ -637,7 +638,7 @@ def _set_cross_meta(wrapped, instance, args, kwargs): ed_x = xy[-1,0] ed_y = xy[-1,1] - cross_str = "Cross-Section: ({0}, {1}) to ({2}, {3})".format(st_x, st_y, + cross_str = "({0}, {1}) to ({2}, {3})".format(st_x, st_y, ed_x, ed_y) if angle is not None: cross_str += " ; center={0} ; angle={1}".format(pivot_point, @@ -726,7 +727,7 @@ def _set_cross_meta(wrapped, instance, args, kwargs): outname = "field3d_cross" outattrs = OrderedDict() - outattrs["Orientation"] = cross_str + outattrs["orientation"] = cross_str outattrs["missing_value"] = missingval outattrs["_FillValue"] = missingval @@ -863,7 +864,7 @@ def _set_line_meta(wrapped, instance, args, kwargs): outname = "field2d_line" outattrs = OrderedDict() - outattrs["Orientation"] = cross_str + outattrs["orientation"] = cross_str return DataArray(result, name=outname, dims=outdimnames, coords=outcoords, attrs=outattrs) @@ -933,6 +934,11 @@ def _set_2dxy_meta(wrapped, instance, args, kwargs): cross_str = "({0},{1}) to ({2},{3})".format(st_x, st_y, ed_x, ed_y) + outname = None + outdimnames = None + outcoords = None + outattrs = None + # Dims are (...,xy,z) if isinstance(field3d, DataArray): outcoords = OrderedDict() @@ -976,56 +982,59 @@ def _set_2dxy_meta(wrapped, instance, args, kwargs): else: outname = "field3d_2dxy" - outattrs["Orientation"] = cross_str + outattrs["orientation"] = cross_str return DataArray(result, name=outname, dims=outdimnames, coords=outcoords, attrs=outattrs) def _set_1d_meta(wrapped, instance, args, kwargs): - argvars = from_args(wrapped, ("v_in", "z_in", "z_out", "missingval"), + argvars = from_args(wrapped, ("field", "z_in", "z_out", "missingval"), *args, **kwargs) - v_in = argvars["v_in"] + field = argvars["field"] z_in = argvars["z_in"] z_out = argvars["z_out"] missingval = argvars["missingval"] result = wrapped(*args, **kwargs) + outname = None + outdimnames = None + outcoords = None + outattrs = None + # Dims are (...,xy,z) - if isinstance(v_in, DataArray): + if isinstance(field, DataArray): outcoords = OrderedDict() outattrs = OrderedDict() - outdimnames = list(v_in.dims) - #outcoords.update(v_in.coords) + outdimnames = list(field.dims) outdimnames.pop(-1) for name in outdimnames: try: - outcoords[name] = v_in.coords[name] + outcoords[name] = field.coords[name] except KeyError: continue outdimnames.append("z") - outname = "{0}_z".format(v_in.name) + outname = "{0}_z".format(field.name) outcoords["z"] = z_out - #outattrs.update(v_in.attrs) outattrs["_FillValue"] = missingval outattrs["missing_value"] = missingval - desc = v_in.attrs.get("description", None) + desc = field.attrs.get("description", None) if desc is not None: outattrs["description"] = desc - units = v_in.attrs.get("units", None) + units = field.attrs.get("units", None) if units is not None: outattrs["units"] = units else: - outname = "v_in_z" + outname = "field_z" return DataArray(result, name=outname, dims=outdimnames, @@ -1049,7 +1058,7 @@ def _set_xy_meta(wrapped, instance, args, kwargs): outname = "{0}_xy".format(field.name) else: outname = "xy" - + outdimnames = ["line_idx", "x_y"] outcoords = OrderedDict() outattrs = OrderedDict() diff --git a/src/wrf/py3compat.py b/src/wrf/py3compat.py index 737ade3..d8004fc 100644 --- a/src/wrf/py3compat.py +++ b/src/wrf/py3compat.py @@ -1,3 +1,6 @@ +from __future__ import (absolute_import, division, print_function, + unicode_literals) + from sys import version_info from math import floor, copysign @@ -30,12 +33,13 @@ def isstr(s): # Python 2 rounding behavior -def _round2(x, d=0): +def _round2(x, d=None): + d = 0 if d is None else d p = 10 ** d return float(floor((x * p) + copysign(0.5, x)))/p -def py2round(x, d=0): +def py2round(x, d=None): if version_info >= (3,): return _round2(x, d) @@ -53,4 +57,4 @@ def ucode(*args, **kwargs): if version_info >= (3, ): return str(*args, **kwargs) - return unicode(*args, **kwargs) \ No newline at end of file + return unicode(*args, **kwargs) diff --git a/src/wrf/util.py b/src/wrf/util.py index 742b16d..55cfd5d 100644 --- a/src/wrf/util.py +++ b/src/wrf/util.py @@ -1507,56 +1507,7 @@ def get_proj_params(wrfnc, timeidx=0, varname=None): return (wrfnc.variables[lat_coord][time_idx_or_slice,:], wrfnc.variables[lon_coord][time_idx_or_slice,:], proj_params) - - -class CoordPair(object): - def __init__(self, x=None, y=None, i=None, j=None, lat=None, lon=None): - self.x = x - self.y = y - self.i = i - self.j = j - self.lat = lat - self.lon = lon - - def __repr__(self): - args = [] - if self.x is not None: - args.append("x={}".format(self.x)) - args.append("y={}".format(self.y)) - - if self.i is not None: - args.append("i={}".format(self.i)) - args.append("j={}".format(self.j)) - - if self.lat is not None: - args.append("lat={}".format(self.lat)) - args.append("lon={}".format(self.lon)) - - argstr = ", ".join(args) - - return "{}({})".format(self.__class__.__name__, argstr) - - def __str__(self): - return self.__repr__() - - def xy_str(self, fmt="{:.4f}, {:.4f}"): - if self.x is None or self.y is None: - return None - - return fmt.format(self.x, self.y) - - def latlon_str(self, fmt="{:.4f}, {:.4f}"): - if self.lat is None or self.lon is None: - return None - return fmt.format(self.lat, self.lon) - - def ij_str(self, fmt="{:.4f}, {:.4f}"): - if self.i is None or self.j is None: - return None - - return fmt.format(self.i, self.j) - def from_args(func, argnames, *args, **kwargs): """Parses the function args and kargs looking for the desired argument @@ -1569,7 +1520,7 @@ def from_args(func, argnames, *args, **kwargs): else: arglist = argnames - result = {} + result = OrderedDict() for argname in arglist: arg_loc = arg_location(func, argname, args, kwargs)