Skip to content

Commit

Permalink
Merge pull request #1349 from jthielen/0-12-patch-xarray-0-15-1
Browse files Browse the repository at this point in the history
Backport convert_coordinate_units (xarray 0.15.1 fix) to 0.12.x
  • Loading branch information
dopplershift authored Apr 17, 2020
2 parents 0653de8 + 76fc20f commit 3f6e5f8
Show file tree
Hide file tree
Showing 17 changed files with 69 additions and 35 deletions.
3 changes: 2 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ matrix:
include:
- python: 3.6
env:
- VERSIONS="numpy==1.13.0 matplotlib==2.1.0 scipy==1.0.0 pint==0.8 xarray==0.10.7 pandas==0.22.0"
- VERSIONS="numpy==1.16.0 matplotlib==2.1.0 scipy==1.0.0 pint==0.10.1 xarray==0.13.0 pandas==0.22.0"
- TASK="coverage"
- TEST_OUTPUT_CONTROL=""
- python: "3.8-dev"
Expand Down Expand Up @@ -126,6 +126,7 @@ before_script:

script:
- export TEST_DATA_DIR=${TRAVIS_BUILD_DIR}/staticdata;
- export NUMPY_EXPERIMENTAL_ARRAY_FUNCTION=1;
- if [[ $TASK == "docs" ]]; then
pushd docs;
make clean overridecheck html linkcheck O=-W;
Expand Down
6 changes: 3 additions & 3 deletions docs/installguide.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@ In general, MetPy tries to support minor versions of dependencies released withi
years. For Python itself, that means supporting the last two minor releases.

* matplotlib >= 2.1.0
* numpy >= 1.13.0
* numpy >= 1.16.0
* scipy >= 1.0.0
* pint >= 0.8
* pint >= 0.10.1
* pandas >= 0.22.0
* xarray >= 0.10.7
* xarray >= 0.13.0
* traitlets >= 4.3.0
* pooch >= 0.1

Expand Down
6 changes: 3 additions & 3 deletions examples/sigma_to_pressure_interpolation.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,9 @@
lon = data.variables['lon'][:]
time = data.variables['time']
vtimes = num2date(time[:], time.units)
temperature = data.variables['temperature'][:] * units.celsius
pres = data.variables['pressure'][:] * units.pascal
hgt = data.variables['height'][:] * units.meter
temperature = units.Quantity(data.variables['temperature'][:], 'degC')
pres = units.Quantity(data.variables['pressure'][:], 'Pa')
hgt = units.Quantity(data.variables['height'][:], 'meter')

####################################
# Array of desired pressure levels
Expand Down
8 changes: 4 additions & 4 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,10 @@ setup_requires = setuptools_scm
python_requires = >=3.6
install_requires =
matplotlib>=2.1.0
numpy>=1.13.0
numpy>=1.16.0
scipy>=1.0
pint>=0.8
xarray>=0.10.7
pint>=0.10.1
xarray>=0.13.0
pooch>=0.1
traitlets>=4.3.0
pandas>=0.22.0
Expand All @@ -52,7 +52,7 @@ where = src

[options.extras_require]
dev = ipython[all]>=3.1
doc = sphinx>=2.1; sphinx-gallery>=0.4; doc8; m2r; netCDF4
doc = sphinx<3.0; sphinx-gallery>=0.4; doc8; m2r; netCDF4
examples = cartopy>=0.15.0; matplotlib>=2.2.0; pyproj>=1.9.4,!=2.0.0
test = pytest>=2.4; pytest-mpl; pytest-flake8; cartopy>=0.16.0; flake8>3.2.0; flake8-builtins!=1.4.0; flake8-comprehensions; flake8-copyright; flake8-docstrings; flake8-import-order; flake8-mutable; flake8-pep3101; flake8-print; flake8-quotes; flake8-rst-docstrings; pep8-naming; netCDF4; pyproj>=1.9.4,!=2.0.0

Expand Down
2 changes: 1 addition & 1 deletion src/metpy/calc/thermo.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ def potential_temperature(pressure, temperature):
--------
>>> from metpy.units import units
>>> metpy.calc.potential_temperature(800. * units.mbar, 273. * units.kelvin)
<Quantity(290.96653180346203, 'kelvin')>
<Quantity(290.9665329591884, 'kelvin')>
"""
return temperature / exner_function(pressure)
Expand Down
4 changes: 2 additions & 2 deletions src/metpy/plots/station_plot.py
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,8 @@ def plot_parameter(self, location, parameter, formatter='.0f', **kwargs):
plot_barb, plot_symbol, plot_text
"""
if hasattr(parameter, 'units'):
parameter = parameter.magnitude
text = self._to_string_list(parameter, formatter)
return self.plot_text(location, text, **kwargs)

Expand Down Expand Up @@ -353,8 +355,6 @@ def _to_string_list(vals, fmt):
if not callable(fmt):
def formatter(s):
"""Turn a format string into a callable."""
if hasattr(s, 'units'):
s = s.item()
return format(s, fmt)
else:
formatter = fmt
Expand Down
42 changes: 32 additions & 10 deletions src/metpy/xarray.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,13 +137,30 @@ def unit_array(self):
@unit_array.setter
def unit_array(self, values):
"""Set data values from a `pint.Quantity`."""
self._data_array.values = values.magnitude
try:
self._data_array.values = values.magnitude
except ValueError:
raise ValueError('Unable to set unit array to a dimension coordinate. Use the '
'assign_coords method instead')
self._units = self._data_array.attrs['units'] = str(values.units)

def convert_units(self, units):
"""Convert the data values to different units in-place."""
self.unit_array = self.unit_array.to(units)

def convert_coordinate_units(self, coord, units):
"""Return new DataArray with coordinate converted to different units.
Notes
-----
Backported from MetPy 1.0.
"""
new_coord_var = self._data_array[coord].copy(
data=self._data_array[coord].metpy.unit_array.m_as(units)
)
new_coord_var.attrs['units'] = str(units)
return self._data_array.assign_coords(coords={coord: new_coord_var})

@property
def crs(self):
"""Return the coordinate reference system (CRS) as a CFProjection object."""
Expand Down Expand Up @@ -517,7 +534,7 @@ def parse_cf(self, varname=None, coordinates=None):
else:
var.coords['crs'] = CFProjection(proj_var.attrs)

self._fixup_coords(var)
var = self._fixup_coords(var)

# Trying to guess whether we should be adding a crs to this variable's coordinates
# First make sure it's missing CRS but isn't lat/lon itself
Expand All @@ -542,18 +559,23 @@ def parse_cf(self, varname=None, coordinates=None):

def _fixup_coords(self, var):
"""Clean up the units on the coordinate variables."""
for coord_name, data_array in var.coords.items():
if (check_axis(data_array, 'x', 'y')
and not check_axis(data_array, 'longitude', 'latitude')):
for coord_name, coord_var in var.coords.items():
if (check_axis(coord_var, 'x', 'y')
and not check_axis(coord_var, 'longitude', 'latitude')):
try:
var.coords[coord_name].metpy.convert_units('meters')
var = var.metpy.convert_coordinate_units(coord_name, 'meter')
except DimensionalityError: # Radians!
if 'crs' in var.coords:
new_data_array = data_array.copy()
height = var.coords['crs'].item()['perspective_point_height']
scaled_vals = new_data_array.metpy.unit_array * (height * units.meters)
new_data_array.metpy.unit_array = scaled_vals.to('meters')
var.coords[coord_name] = new_data_array
new_coord_var = coord_var.copy(
data=(
coord_var.metpy.unit_array * (height * units.meters)
).m_as('meter')
)
new_coord_var.attrs['units'] = 'meter'
var = var.assign_coords(coords={coord_name: new_coord_var})

return var

class _LocIndexer:
"""Provide the unit-wrapped .loc indexer for datasets."""
Expand Down
Binary file modified tests/plots/baseline/test_declarative_sfc_obs_full.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified tests/plots/baseline/test_simple_layout.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified tests/plots/baseline/test_skewt_default_aspect_empty.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified tests/plots/baseline/test_skewt_tight_bbox.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified tests/plots/baseline/test_stationlayout_api.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 2 additions & 1 deletion tests/plots/test_declarative.py
Original file line number Diff line number Diff line change
Expand Up @@ -485,7 +485,8 @@ def test_declarative_sfc_obs_changes():
return pc.figure


@pytest.mark.mpl_image_compare(remove_text=True, tolerance=0.022)
@pytest.mark.mpl_image_compare(remove_text=True,
tolerance={'3.1': 9.771, '2.1': 9.771}.get(MPL_VERSION, 0.))
def test_declarative_sfc_obs_full():
"""Test making a full surface observation plot."""
data = pd.read_csv(get_test_data('SFC_obs.csv', as_file_obj=False),
Expand Down
9 changes: 6 additions & 3 deletions tests/plots/test_skewt.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
from metpy.units import units


@pytest.mark.mpl_image_compare(tolerance=.02, remove_text=True, style='default')
@pytest.mark.mpl_image_compare(tolerance=.0202, remove_text=True, style='default')
def test_skewt_api():
"""Test the SkewT API."""
with matplotlib.rc_context({'axes.autolimit_mode': 'data'}):
Expand Down Expand Up @@ -47,7 +47,7 @@ def test_skewt_api():
return fig


@pytest.mark.mpl_image_compare(tolerance=.027 if matplotlib.__version__ < '3.2' else 34.4,
@pytest.mark.mpl_image_compare(tolerance=.0272 if matplotlib.__version__ < '3.2' else 34.4,
remove_text=True, style='default')
def test_skewt_api_units():
"""#Test the SkewT API when units are provided."""
Expand All @@ -66,6 +66,9 @@ def test_skewt_api_units():
skew.plot_moist_adiabats()
skew.plot_mixing_lines()

# This works around the fact that newer pint versions default to degrees_Celsius
skew.ax.set_xlabel('degC')

return fig


Expand All @@ -82,7 +85,7 @@ def test_skewt_default_aspect_empty():
return fig


@pytest.mark.skipif(matplotlib.__version__ < '3' or matplotlib.__version__ >= '3.2',
@pytest.mark.skipif(matplotlib.__version__ < '3.2',
reason='Matplotlib versions generate different image sizes.')
@pytest.mark.mpl_image_compare(tolerance=0., remove_text=False, style='default',
savefig_kwargs={'bbox_inches': 'tight'})
Expand Down
8 changes: 4 additions & 4 deletions tests/plots/test_station_plot.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ def test_station_plot_replace():
return fig


@pytest.mark.mpl_image_compare(tolerance=0.00413, savefig_kwargs={'dpi': 300},
@pytest.mark.mpl_image_compare(tolerance=0, savefig_kwargs={'dpi': 300},
remove_text=True)
def test_stationlayout_api():
"""Test the StationPlot API."""
Expand All @@ -92,7 +92,7 @@ def test_stationlayout_api():
# testing data
x = np.array([1, 5])
y = np.array([2, 4])
data = {'temp': np.array([32., 212.]) * units.degF, 'u': np.array([2, 0]) * units.knots,
data = {'temp': np.array([33., 212.]) * units.degF, 'u': np.array([2, 0]) * units.knots,
'v': np.array([0, 5]) * units.knots, 'stid': ['KDEN', 'KSHV'], 'cover': [3, 8]}

# Set up the layout
Expand Down Expand Up @@ -151,15 +151,15 @@ def test_station_layout_names():
assert sorted(layout.names()) == ['cover', 'stid', 'temp', 'u', 'v']


@pytest.mark.mpl_image_compare(tolerance=0.0072, savefig_kwargs={'dpi': 300}, remove_text=True)
@pytest.mark.mpl_image_compare(tolerance=0, savefig_kwargs={'dpi': 300}, remove_text=True)
def test_simple_layout():
"""Test metpy's simple layout for station plots."""
fig = plt.figure(figsize=(9, 9))

# testing data
x = np.array([1, 5])
y = np.array([2, 4])
data = {'air_temperature': np.array([32., 212.]) * units.degF,
data = {'air_temperature': np.array([33., 212.]) * units.degF,
'dew_point_temperature': np.array([28., 80.]) * units.degF,
'air_pressure_at_sea_level': np.array([29.92, 28.00]) * units.inHg,
'eastward_wind': np.array([2, 0]) * units.knots,
Expand Down
11 changes: 9 additions & 2 deletions tests/test_xarray.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,13 @@ def test_convert_units(test_var):
assert_almost_equal(test_var[0, 0, 0, 0], 18.44 * units.degC, 2)


def test_convert_coordinate_units(test_ds_generic):
"""Test conversion of coordinate units."""
result = test_ds_generic['test'].metpy.convert_coordinate_units('b', 'percent')
assert result['b'].data[1] == 100.
assert result['b'].metpy.units == units.percent


def test_radian_projection_coords():
"""Test fallback code for radian units in projection coordinate variables."""
proj = xr.DataArray(0, attrs={'grid_mapping_name': 'geostationary',
Expand Down Expand Up @@ -670,8 +677,8 @@ def test_coordinate_identification_shared_but_not_equal_coords():

def test_check_no_quantification_of_xarray_data(test_ds_generic):
"""Test that .unit_array setter does not insert a `pint.Quantity` into the DataArray."""
var = test_ds_generic['e']
var.metpy.unit_array = [1000, 925, 850, 700, 500] * units.hPa
var = test_ds_generic['test']
var.metpy.unit_array = np.zeros(var.shape) * units.hPa
assert not isinstance(var.data, units.Quantity)


Expand Down
2 changes: 1 addition & 1 deletion tutorials/upperair_soundings.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
problems. In this tutorial we will gather weather balloon data, plot it,
perform a series of thermodynamic calculations, and summarize the results.
To learn more about the Skew-T diagram and its use in weather analysis and
forecasting, checkout `this <https://homes.comet.ucar.edu/~alanbol/aws-tr-79-006.pdf>`_
forecasting, checkout `this <http://www.pmarshwx.com/research/manuals/AF_skewt_manual.pdf>`_
air weather service guide.
"""

Expand Down

0 comments on commit 3f6e5f8

Please sign in to comment.