diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8952a210..6275cf90 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -14,7 +14,7 @@ Getting started 2. Fork the iris-grib repository, create your new fix/feature branch, and start commiting code. - The main - [Iris development guide](http://scitools.org.uk/iris/docs/latest/developers_guide/gitwash/git_development.html) + [Iris development guide](https://scitools-iris.readthedocs.io/en/latest/developers_guide/gitwash/index.html) has more detail. 3. Remember to add appropriate documentation and tests to supplement any new or changed functionality. diff --git a/README.rst b/README.rst index ee3aa9a6..6e379cb0 100644 --- a/README.rst +++ b/README.rst @@ -1,7 +1,7 @@ iris-grib ========= -|CirrusCI|_ |Coveralls|_ +|CirrusCI|_ |Coveralls|_ |conda-forge|_ |pypi|_ |License|_ |Commits|_ |Contributors|_ GRIB interface for `Iris `_. @@ -24,3 +24,18 @@ terms of its `GNU LGPLv3 license `_. .. |Coveralls| image:: https://coveralls.io/repos/github/SciTools/iris-grib/badge.svg?branch=master .. _Coveralls: https://coveralls.io/github/SciTools/iris-grib?branch=master + +.. |conda-forge| image:: https://img.shields.io/conda/vn/conda-forge/iris-grib?color=orange&label=conda-forge&logo=conda-forge&logoColor=white +.. _conda-forge: https://anaconda.org/conda-forge/iris-grib + +.. |pypi| image:: https://img.shields.io/pypi/v/iris-grib?color=orange&label=pypi&logo=python&logoColor=white +.. _pypi: https://pypi.org/project/iris-grib + +.. |License| image:: https://img.shields.io/github/license/SciTools/iris-grib?style=plastic +.. _License: https://github.com/SciTools/iris-grib/blob/master/COPYING + +.. |Contributors| image:: https://img.shields.io/github/contributors/SciTools/iris-grib?style=plastic +.. _Contributors: https://github.com/SciTools/iris-grib/graphs/contributors + +.. |Commits| image:: https://img.shields.io/github/commits-since/SciTools/iris-grib/latest.svg?style=plastic +.. _Commits: https://github.com/SciTools/iris-grib/commits/master \ No newline at end of file diff --git a/iris_grib/__init__.py b/iris_grib/__init__.py index ae718c6e..84658ac5 100644 --- a/iris_grib/__init__.py +++ b/iris_grib/__init__.py @@ -25,7 +25,6 @@ from iris._lazy_data import as_lazy_data import iris.coord_systems as coord_systems from iris.exceptions import TranslationError, NotYetImplementedError -from iris.util import _array_slice_ifempty from . import grib_phenom_translation as gptx from . import _save_rules @@ -98,19 +97,13 @@ def ndim(self): return len(self.shape) def __getitem__(self, keys): - # Avoid fetching file data just to return an 'empty' result. - # Needed because of how dask.array.from_array behaves since Dask v2.0. - result = _array_slice_ifempty(keys, self.shape, self.dtype) - if result is None: - with open(self.path, 'rb') as grib_fh: - grib_fh.seek(self.offset) - grib_message = gribapi.grib_new_from_file(grib_fh) - data = _message_values(grib_message, self.shape) - gribapi.grib_release(grib_message) - - result = data.__getitem__(keys) - - return result + with open(self.path, 'rb') as grib_fh: + grib_fh.seek(self.offset) + grib_message = gribapi.grib_new_from_file(grib_fh) + data = _message_values(grib_message, self.shape) + gribapi.grib_release(grib_message) + + return data.__getitem__(keys) def __repr__(self): msg = '<{self.__class__.__name__} shape={self.shape} ' \ diff --git a/iris_grib/message.py b/iris_grib/message.py index 503d4cd6..d202d08b 100644 --- a/iris_grib/message.py +++ b/iris_grib/message.py @@ -12,7 +12,6 @@ import re import gribapi -from iris_grib import _array_slice_ifempty import numpy as np import numpy.ma as ma @@ -232,35 +231,28 @@ def _bitmap(self, bitmap_section): def __getitem__(self, keys): # NB. Currently assumes that the validity of this interpretation # is checked before this proxy is created. + message = self.recreate_raw() + sections = message.sections + bitmap_section = sections[6] + bitmap = self._bitmap(bitmap_section) + data = sections[7]['codedValues'] + + if bitmap is not None: + # Note that bitmap and data are both 1D arrays at this point. + if np.count_nonzero(bitmap) == data.shape[0]: + # Only the non-masked values are included in codedValues. + _data = np.empty(shape=bitmap.shape) + _data[bitmap.astype(bool)] = data + # `ma.masked_array` masks where input = 1, the opposite of + # the behaviour specified by the GRIB spec. + data = ma.masked_array(_data, mask=np.logical_not(bitmap), + fill_value=np.nan) + else: + msg = 'Shapes of data and bitmap do not match.' + raise TranslationError(msg) - # Avoid fetching file data just to return an 'empty' result. - # Needed because of how dask.array.from_array behaves since Dask v2.0. - result = _array_slice_ifempty(keys, self.shape, self.dtype) - if result is None: - message = self.recreate_raw() - sections = message.sections - bitmap_section = sections[6] - bitmap = self._bitmap(bitmap_section) - data = sections[7]['codedValues'] - - if bitmap is not None: - # Note that bitmap and data are both 1D arrays at this point. - if np.count_nonzero(bitmap) == data.shape[0]: - # Only the non-masked values are included in codedValues. - _data = np.empty(shape=bitmap.shape) - _data[bitmap.astype(bool)] = data - # `ma.masked_array` masks where input = 1, the opposite of - # the behaviour specified by the GRIB spec. - data = ma.masked_array(_data, mask=np.logical_not(bitmap), - fill_value=np.nan) - else: - msg = 'Shapes of data and bitmap do not match.' - raise TranslationError(msg) - - data = data.reshape(self.shape) - result = data.__getitem__(keys) - - return result + data = data.reshape(self.shape) + return data.__getitem__(keys) def __repr__(self): msg = '<{self.__class__.__name__} shape={self.shape} ' \ diff --git a/iris_grib/tests/unit/message/test__DataProxy.py b/iris_grib/tests/unit/message/test__DataProxy.py index d7494d5e..b828c97c 100644 --- a/iris_grib/tests/unit/message/test__DataProxy.py +++ b/iris_grib/tests/unit/message/test__DataProxy.py @@ -43,31 +43,5 @@ def test_bitmap__invalid_indicator(self): data_proxy._bitmap(section_6) -class Test_emptyfetch(tests.IrisGribTest): - # See : - # iris.tests.unit.fileformats.pp.test_PPDataProxy.Test__getitem__slicing - # In this case, test *only* the no-data-read effect, not the method which - # is part of Iris. - def test_empty_slice(self): - # Check behaviour of the getitem call with an 'empty' slicing. - # This is necessary because, since Dask 2.0, the "from_array" function - # takes a zero-length slice of its array argument, to capture array - # metadata, and in those cases we want to avoid file access. - test_dtype = np.dtype(np.float32) - mock_datafetch = mock.MagicMock() - proxy = _DataProxy(shape=(3, 4), - dtype=np.dtype(np.float32), - recreate_raw=mock_datafetch) - - # Test the special no-data indexing operation. - result = proxy[0:0, 0:0] - - # Check the behaviour and results were as expected. - self.assertEqual(mock_datafetch.call_count, 0) - self.assertIsInstance(result, np.ndarray) - self.assertEqual(result.dtype, test_dtype) - self.assertEqual(result.shape, (0, 0)) - - if __name__ == '__main__': tests.main()