From 485bdd7b273e640b6da302ce4bca949978befcc2 Mon Sep 17 00:00:00 2001 From: bblay Date: Wed, 15 Aug 2012 17:01:55 +0100 Subject: [PATCH 1/2] NIMROD loading --- lib/iris/coord_systems.py | 14 +- lib/iris/fileformats/__init__.py | 8 + lib/iris/fileformats/nimrod.py | 357 ++++++++++++++++++ .../results/file_load/format_associations.txt | 3 +- .../tests/results/file_load/known_loaders.txt | 1 + lib/iris/tests/results/nimrod/load.cml | 37 ++ lib/iris/tests/test_io_init.py | 2 +- lib/iris/tests/test_nimrod.py | 43 +++ 8 files changed, 461 insertions(+), 4 deletions(-) create mode 100644 lib/iris/fileformats/nimrod.py create mode 100644 lib/iris/tests/results/nimrod/load.cml create mode 100644 lib/iris/tests/test_nimrod.py diff --git a/lib/iris/coord_systems.py b/lib/iris/coord_systems.py index edbee82f44..14761f2e90 100644 --- a/lib/iris/coord_systems.py +++ b/lib/iris/coord_systems.py @@ -276,8 +276,8 @@ def __init__(self, latitude_of_projection_origin, longitude_of_central_meridian, Example:: - airy1830 = GeogCS(6377563.396, 6356256.910) - osgb = TransverseMercator(49, -2, 40000, -10000, 0.9996012717, ellipsoid=airy1830) + airy1830 = GeogCS(6377563.396, 6356256.909) + osgb = TransverseMercator(49, -2, 400000, -100000, 0.9996012717, ellipsoid=airy1830) """ self.latitude_of_projection_origin = float(latitude_of_projection_origin) @@ -305,3 +305,13 @@ def __repr__(self): (self.latitude_of_projection_origin, self.longitude_of_central_meridian, self.false_easting, self.false_northing, self.scale_factor_at_central_meridian, self.ellipsoid) + + +class OSGB(TransverseMercator): + """A Specific transverse mercator projection on a specific ellipsoid.""" + def __init__(self): + TransverseMercator.__init__(self, 49, -2, -400000, 100000, 0.9996012717, + GeogCS(6377563.396, 6356256.909)) + + def _as_cartopy_projection(self): + return cartopy.crs.OSGB() diff --git a/lib/iris/fileformats/__init__.py b/lib/iris/fileformats/__init__.py index fda5101879..17fa988eac 100644 --- a/lib/iris/fileformats/__init__.py +++ b/lib/iris/fileformats/__init__.py @@ -24,6 +24,7 @@ import ff import grib import netcdf +import nimrod __all__ = ['FORMAT_AGENT'] @@ -99,3 +100,10 @@ def _pp_little_endian(filename, *args, **kwargs): priority=4, ) FORMAT_AGENT.add_spec(_FF_5p2_spec) + +_NIMROD_spec = fp.FormatSpecification('NIMROD', + fp.MAGIC_NUMBER_32_BIT, 0x00000200, + nimrod.load_cubes, + priority=5, + ) +FORMAT_AGENT.add_spec(_NIMROD_spec) diff --git a/lib/iris/fileformats/nimrod.py b/lib/iris/fileformats/nimrod.py new file mode 100644 index 0000000000..1a5e95836f --- /dev/null +++ b/lib/iris/fileformats/nimrod.py @@ -0,0 +1,357 @@ +# (C) British Crown Copyright 2010 - 2012, Met Office +# +# This file is part of Iris. +# +# Iris is free software: you can redistribute it and/or modify it under +# the terms of the GNU Lesser General Public License as published by the +# Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Iris is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Iris. If not, see . + + +import glob +import netcdftime +import numpy +import os +import struct +import sys +import warnings + + +import iris +from iris.coord_systems import OSGB +from iris.coords import DimCoord, AuxCoord +from iris.exceptions import TranslationError + + +# general header (int16) elements 1-31 (bytes 1-62) +general_header_int16s = ("vt_year", "vt_month", "vt_day", "vt_hour", "vt_minute", "vt_second", + "dt_year", "dt_month", "dt_day", "dt_hour", "dt_minute", + "datum_type", "datum_len", "experiment_num", "horizontal_grid_type", + "num_rows", "num_cols", "nimrod_version", "field_code", + "vertical_coord_type", "reference_vertical_coord_type", + "data_specific_float32_len", "data_specific_int16_len", + "origin_corner", "int_mdi", "period_minutes", "num_model_levels", + "proj_biaxial_ellipsoid", "ensemble_member", "spare1", "spare2") + + +# general header (float32) elements 32-59 (bytes 63-174) +general_header_float32s = ("vertical_coord", "reference_vertical_coord", + "y_origin", "row_step", "x_origin", "column_step", + "float32_mdi", "MKS_data_scaling", "data_offset", + "x_offset", "y_offset", "true_origin_latitude", + "true_origin_longitude", "true_origin_easting", + "true_origin_northing", "tm_meridian_scaling") + + +# data specific header (float32) elements 60-104 (bytes 175-354) +data_header_float32s = ("tl_y", "tl_x", "tr_y", "ty_x", "br_y", "br_x", "bl_y", "bl_x", + "sat_calib", "sat_space_count", "ducting_index", "elevation_angle") + + +# data specific header (int16) elements 108- (bytes 411-512) +data_header_int16s = ("radar_num", "radars_bitmask", "more_radars_bitmask", + "clutter_map_num", "calibration_type", "bright_band_height", + "bright_band_intensity", "bright_band_test1", "bright_band_test2", + "infill_flag", "stop_elevation", "int16_vertical_coord", + "int16_reference_vertical_coord", "int16_y_origin", + "int16_row_step", "int16_x_origin", "int16_column_step", + "int16_float32_mdi", "int16_data_scaling", "int16_data_offset", + "int16_x_offset", "int16_y_offset", "int16_true_origin_latitude", + "int16_true_origin_longitude", "int16_tl_y", "int16_tl_x", + "int16_tr_y", "int16_ty_x", "int16_br_y", "int16_br_x", + "int16_bl_y", "int16_bl_x", "sensor_id", "meteosat_id", + "alphas_available") + + +def _read_chars(infile, num): + """Read characters from the (big-endian) file.""" + instr = infile.read(num) + return struct.unpack(">%ds" % num, instr)[0] + + +class NimrodField(object): + """ + A data field from a NIMROD file. + + Capable of converting itself into a :class:`~iris.cube.Cube` + + """ + def __init__(self, from_file=None): + """ + Create a NimrodField object and optionally read from an open file. + + Example:: + + with open("nimrod_file", "rb") as infile: + field = NimrodField(infile) + + """ + if from_file is not None: + self.read(from_file) + + def read(self, infile): + """Read the next field from the given file object.""" + self._read_header(infile) + self._read_data(infile) + + def _read_header_subset(self, infile, names, dtype): + # Read contiguous header items of the same data type. + values = numpy.fromfile(infile, dtype=dtype, count=len(names)) + if sys.byteorder == "little": + values.byteswap(True) + for i, name in enumerate(names): + setattr(self, name, values[i]) + + def _read_header(self, infile): + """Load the 512 byte header (surrounded by 4-byte length, at start and end).""" + # TODO: For future optimisation, use numpy.fromfile or similar block loading + + leading_length = struct.unpack(">L", infile.read(4))[0] + if leading_length != 512: + raise TranslationError("Expected header leading_length of 512") + + # general header (int16) elements 1-31 (bytes 1-62) + self._read_header_subset(infile, general_header_int16s, numpy.int16) + + # general header (float32) elements 32-59 (bytes 63-174) + self._read_header_subset(infile, general_header_float32s, numpy.float32) + infile.seek(4 * (28 - len(general_header_float32s)), os.SEEK_CUR) # skip unnamed floats + + # data specific header (float32) elements 60-104 (bytes 175-354) + self._read_header_subset(infile, data_header_float32s, numpy.float32) + infile.seek(4 * (45 - len(data_header_float32s)), os.SEEK_CUR) # skip unnamed floats + + # data specific header (char) elements 105-107 (bytes 355-410) + self.units = _read_chars(infile, 8) + self.source = _read_chars(infile, 24) + self.title = _read_chars(infile, 24) + + # data specific header (int16) elements 108- (bytes 411-512) + self._read_header_subset(infile, data_header_int16s, numpy.int16) + infile.seek(2 * (51 - len(data_header_int16s)), os.SEEK_CUR) # skip unnamed int16s + + trailing_length = struct.unpack(">L", infile.read(4))[0] + if trailing_length != leading_length: + raise TranslationError('Expected header trailing_length of {}, got {}.'\ + .format(leading_length, trailing_length)) + + def _read_data(self, infile): + """ + Read the data array: int8, int16, int32 or float32 + + (surrounded by 4-byte length, at start and end) + + """ + # what are we expecting? + num_data = int(self.num_rows) * int(self.num_cols) + num_data_bytes = int(num_data) * int(self.datum_len) + + # format string for unpacking the file.read() + # 0:real + if self.datum_type == 0: + numpy_dtype = numpy.float32 + data_format_string = ">%df" % num_data + # 1:int + elif self.datum_type == 1: + if self.datum_len == 1: + numpy_dtype = numpy.int8 + data_format_string = ">%db" % num_data + elif self.datum_len == 2: + numpy_dtype = numpy.int16 + data_format_string = ">%dh" % num_data + elif self.datum_len == 4: + numpy_dtype = numpy.int32 + data_format_string = ">%di" % num_data + else: + raise TranslationError("Undefined datum length %d" % self.datum_type) + # 2:byte + elif self.datum_type == 2: + numpy_dtype = numpy.byte + data_format_string = ">%db" % num_data + else: + raise TranslationError("Undefined data type") + + leading_length = struct.unpack(">L", infile.read(4))[0] + if leading_length != num_data_bytes: + raise TranslationError("Expected data leading_length of %d" % num_data_bytes) + + self.data = struct.unpack(data_format_string, infile.read(num_data_bytes)) + self.data = numpy.array(self.data, dtype=numpy_dtype) + + trailing_length = struct.unpack(">L", infile.read(4))[0] + if trailing_length != leading_length: + raise TranslationError("Expected data trailing_length of %d" % num_data_bytes) + + # TODO: Confirm the MKS scaling factor does not need to be applied. + # TODO: Confiem the data offset only applies when converting to MKS too. + + # form the correct shape + self.data = self.data.reshape(self.num_rows, self.num_cols) + + # flip the data to get origin in bottom left + if self.origin_corner == 0: # top left + self.data = self.data[::-1, :] + else: + raise TranslationError("Corner {0} not yet implemented".format(self.origin_corner)) + + def to_cube(self): + """Return a new :class:`~iris.cube.Cube`, created from this NimrodField.""" + MISSING_INT = self.int_mdi + MISSING_FLOAT = self.float32_mdi + + # name + cube = iris.cube.Cube(self.data) + cube.rename(self.title.strip()) + + # Can we set the units? + units = self.units.strip() + try: + cube.units = units + except ValueError: + # Just add it as an attribute. + warnings.warn("Unhandled units '{0}' recorded in cube attributes.".format(units)) + cube.attributes["invalid_units"] = units + + # time + valid_date = netcdftime.datetime(self.vt_year, self.vt_month, self.vt_day, + self.vt_hour, self.vt_minute, self.vt_second) + time_coord = DimCoord(iris.unit.date2num(valid_date, 'hours since 1970-01-01 00:00:00', + iris.unit.CALENDAR_STANDARD), + standard_name='time', units="hours") + cube.add_aux_coord(time_coord) + + data_date = netcdftime.datetime(self.dt_year, self.dt_month, self.dt_day, + self.dt_hour, self.dt_minute) + ref_time_coord = DimCoord(iris.unit.date2num(data_date, 'hours since 1970-01-01 00:00:00', + iris.unit.CALENDAR_STANDARD), + standard_name='forecast_reference_time', units="hours") + cube.add_aux_coord(ref_time_coord) + + if self.period_minutes != MISSING_INT: + if self.period_minutes != 0: + raise TranslationError("Period_minutes not yet handled") + + # experiment + if self.experiment_num != MISSING_INT: + cube.add_aux_coord(DimCoord(self.experiment_num, long_name="experiment_number", + units="1")) + + # horizontal grid + if self.proj_biaxial_ellipsoid != MISSING_INT: + raise TranslationError("Biaxial ellipoid %d not yet handled" % \ + self.proj_biaxial_ellipsoid) + + if self.tm_meridian_scaling != MISSING_INT: + raise TranslationError("tm_meridian_scaling not yet handled") + + if self.horizontal_grid_type == 0: + # "NG", means osgb grid. + osgb_cs = iris.coord_systems.OSGB() + cube.add_dim_coord( + DimCoord(numpy.arange(self.num_cols)*self.column_step+self.x_origin, + long_name="x", units="m", coord_system=osgb_cs), 1) + cube.add_dim_coord( + DimCoord(numpy.arange(self.num_rows)*self.row_step+self.y_origin, + long_name="y", units="m", coord_system=osgb_cs), 0) + else: + raise TranslationError("Grid type %d not yet implemented" % \ + self.horizontal_grid_type) + + # vertical + if self.vertical_coord_type != MISSING_INT: + if self.vertical_coord_type == 0: + if self.reference_vertical_coord_type == MISSING_INT or \ + self.reference_vertical_coord == MISSING_FLOAT: + cube.add_aux_coord(DimCoord(self.vertical_coord, + long_name="height", units="m")) + else: + raise TranslationError("Bounded vertical not yet implemented") + else: + raise TranslationError("Vertical coord type %d currently unhanded" % \ + self.vertical_coord_type) + + # add other stuff, if present + ensemble_member = getattr(self, "ensemble_member") + if ensemble_member != MISSING_INT: + cube.add_aux_coord(DimCoord(ensemble_member, "realization", units="1")) + + def add_attr(name): + value = getattr(self, name) + if value not in [MISSING_INT, MISSING_FLOAT]: + cube.attributes[name] = value + + add_attr("nimrod_version") + add_attr("field_code") + add_attr("num_model_levels") + add_attr("sat_calib") + add_attr("sat_space_count") + add_attr("ducting_index") + add_attr("elevation_angle") + add_attr("radar_num") + add_attr("radars_bitmask") + add_attr("more_radars_bitmask") + add_attr("clutter_map_num") + add_attr("calibration_type") + add_attr("bright_band_height") + add_attr("bright_band_intensity") + add_attr("bright_band_test1") + add_attr("bright_band_test2") + add_attr("infill_flag") + add_attr("stop_elevation") + add_attr("sensor_id") + add_attr("meteosat_id") + add_attr("alphas_available") + + cube.attributes["source"] = self.source.strip() + + return cube + + +def load_cubes(filespecs, callback=None): + """ + Loads cubes from a list of NIMROD filenames. + + Args: + + * filenames - list of NIMROD filenames to load + + Kwargs: + + * callback - a function which can be passed on to :func:`iris.io.run_callback` + + .. note:: + The resultant cubes may not be in the same order as in the file. + + """ + if isinstance(filespecs, basestring): + filespecs=[filespecs] + + cubes = [] + for filespec in filespecs: + for filename in glob.glob(filespec): + with open(filename, "rb") as infile: + + try: + field = NimrodField(infile) + except struct.error: + # End of file. Move on to the next file. + continue + + cube = field.to_cube() + + # Were we given a callback? + if callback is not None: + cube = iris.io.run_callback(callback, cube, field, filename) + if cube is None: + continue + + yield cube + diff --git a/lib/iris/tests/results/file_load/format_associations.txt b/lib/iris/tests/results/file_load/format_associations.txt index 78bb82a640..d20cae43a0 100644 --- a/lib/iris/tests/results/file_load/format_associations.txt +++ b/lib/iris/tests/results/file_load/format_associations.txt @@ -5,4 +5,5 @@ NetCDF_v4 - NetCDF/global/xyt/SMALL_total_column_co2.nc4.k4 UM Fields file (FF) pre v3.1 - ssps/qtgl.ssps_006 GRIB - GRIB/grib1_second_order_packing/GRIB_00008_FRANX01 GRIB - GRIB/jpeg2000/file.grib2 -UM Post Processing file (PP) - PP/uk4/uk4par09.pp \ No newline at end of file +UM Post Processing file (PP) - PP/uk4/uk4par09.pp +NIMROD - NIMROD/uk2km/WO0000000003452/201007020900_u1096_ng_ey00_visibility0180_screen_2km \ No newline at end of file diff --git a/lib/iris/tests/results/file_load/known_loaders.txt b/lib/iris/tests/results/file_load/known_loaders.txt index bd6bb572db..0c6db19f80 100644 --- a/lib/iris/tests/results/file_load/known_loaders.txt +++ b/lib/iris/tests/results/file_load/known_loaders.txt @@ -4,5 +4,6 @@ * NetCDF (priority 5) * NetCDF 64 bit offset format (priority 5) * NetCDF_v4 (priority 5) + * NIMROD (priority 5) * UM Fields file (FF) pre v3.1 (priority 4) * UM Fields file (FF) post v5.2 (priority 4) \ No newline at end of file diff --git a/lib/iris/tests/results/nimrod/load.cml b/lib/iris/tests/results/nimrod/load.cml new file mode 100644 index 0000000000..4548fb40fa --- /dev/null +++ b/lib/iris/tests/results/nimrod/load.cml @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/iris/tests/test_io_init.py b/lib/iris/tests/test_io_init.py index 4fb2a3bf0c..01054d80bc 100644 --- a/lib/iris/tests/test_io_init.py +++ b/lib/iris/tests/test_io_init.py @@ -66,7 +66,7 @@ def test_format_picker(self): ['GRIB', 'jpeg2000', 'file.grib2'], # GRIB2 ['PP', 'uk4', 'uk4par09.pp'], # PP # ['BUFR', 'mss', 'BUFR_Samples', 'JUPV78_EGRR_121200_00002501'], # BUFFR -# ['NIMROD', 'uk2km', 'WO0000000003452', '201007021200_u1096_ng_ey08_visibility0360_screen_2km'], # nimrod + ['NIMROD', 'uk2km', 'WO0000000003452', '201007020900_u1096_ng_ey00_visibility0180_screen_2km'], # nimrod # ['NAME', '20100509_18Z_variablesource_12Z_VAAC', 'Fields_grid1_201005110000.txt'], # NAME ] diff --git a/lib/iris/tests/test_nimrod.py b/lib/iris/tests/test_nimrod.py new file mode 100644 index 0000000000..45598bd7c7 --- /dev/null +++ b/lib/iris/tests/test_nimrod.py @@ -0,0 +1,43 @@ +# (C) British Crown Copyright 2010 - 2012, Met Office +# +# This file is part of Iris. +# +# Iris is free software: you can redistribute it and/or modify it under +# the terms of the GNU Lesser General Public License as published by the +# Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# Iris is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with Iris. If not, see . + + +# import iris tests first so that some things can be initialised before importing anything else +import iris.tests as tests + +import matplotlib.pyplot as plt + +import iris +import iris.plot as iplt + + +@iris.tests.skip_data +class TestGribLoad(tests.GraphicsTest): + + def test_load(self): + cube = iris.load(tests.get_data_path(('NIMROD', 'uk2km', 'WO0000000003452', '201007020900_u1096_ng_ey00_visibility0180_screen_2km')))[0] + self.assertCML(cube, ("nimrod", "load.cml")) + + # TODO: #84 +# iplt.contourf(cube) +# iplt.gcm(cube).drawcoastlines() +# self.check_graphic() + + + +if __name__ == "__main__": + tests.main() From b7a4e09e3c5fe4006b9896cb6a7f6a1c4fd45752 Mon Sep 17 00:00:00 2001 From: bblay Date: Wed, 12 Sep 2012 15:14:42 +0100 Subject: [PATCH 2/2] review actions --- lib/iris/fileformats/nimrod.py | 43 +++++++++---------- .../test_nimrod.TestGribLoad.test_load.0.txt | 1 + lib/iris/tests/results/nimrod/load.cml | 2 +- lib/iris/tests/test_nimrod.py | 6 ++- 4 files changed, 27 insertions(+), 25 deletions(-) create mode 100644 lib/iris/tests/results/image_checksums/redhat6.2_64bit/test_nimrod.TestGribLoad.test_load.0.txt diff --git a/lib/iris/fileformats/nimrod.py b/lib/iris/fileformats/nimrod.py index 1a5e95836f..8390cf36ce 100644 --- a/lib/iris/fileformats/nimrod.py +++ b/lib/iris/fileformats/nimrod.py @@ -14,7 +14,7 @@ # # You should have received a copy of the GNU Lesser General Public License # along with Iris. If not, see . - +"""Provides NIMROD file format capabilities.""" import glob import netcdftime @@ -24,14 +24,13 @@ import sys import warnings - import iris from iris.coord_systems import OSGB -from iris.coords import DimCoord, AuxCoord +from iris.coords import DimCoord from iris.exceptions import TranslationError -# general header (int16) elements 1-31 (bytes 1-62) +# general header (int16) elements general_header_int16s = ("vt_year", "vt_month", "vt_day", "vt_hour", "vt_minute", "vt_second", "dt_year", "dt_month", "dt_day", "dt_hour", "dt_minute", "datum_type", "datum_len", "experiment_num", "horizontal_grid_type", @@ -42,7 +41,7 @@ "proj_biaxial_ellipsoid", "ensemble_member", "spare1", "spare2") -# general header (float32) elements 32-59 (bytes 63-174) +# general header (float32) elements general_header_float32s = ("vertical_coord", "reference_vertical_coord", "y_origin", "row_step", "x_origin", "column_step", "float32_mdi", "MKS_data_scaling", "data_offset", @@ -51,12 +50,12 @@ "true_origin_northing", "tm_meridian_scaling") -# data specific header (float32) elements 60-104 (bytes 175-354) +# data specific header (float32) elements data_header_float32s = ("tl_y", "tl_x", "tr_y", "ty_x", "br_y", "br_x", "bl_y", "bl_x", "sat_calib", "sat_space_count", "ducting_index", "elevation_angle") -# data specific header (int16) elements 108- (bytes 411-512) +# data specific header (int16) elements data_header_int16s = ("radar_num", "radars_bitmask", "more_radars_bitmask", "clutter_map_num", "calibration_type", "bright_band_height", "bright_band_intensity", "bright_band_test1", "bright_band_test2", @@ -112,7 +111,6 @@ def _read_header_subset(self, infile, names, dtype): def _read_header(self, infile): """Load the 512 byte header (surrounded by 4-byte length, at start and end).""" - # TODO: For future optimisation, use numpy.fromfile or similar block loading leading_length = struct.unpack(">L", infile.read(4))[0] if leading_length != 512: @@ -183,15 +181,16 @@ def _read_data(self, infile): if leading_length != num_data_bytes: raise TranslationError("Expected data leading_length of %d" % num_data_bytes) - self.data = struct.unpack(data_format_string, infile.read(num_data_bytes)) - self.data = numpy.array(self.data, dtype=numpy_dtype) + self.data = numpy.fromfile(infile, dtype=numpy_dtype, count=num_data) + if sys.byteorder == "little": + self.data.byteswap(True) trailing_length = struct.unpack(">L", infile.read(4))[0] if trailing_length != leading_length: raise TranslationError("Expected data trailing_length of %d" % num_data_bytes) # TODO: Confirm the MKS scaling factor does not need to be applied. - # TODO: Confiem the data offset only applies when converting to MKS too. + # TODO: Confirm the data offset only applies when converting to MKS too. # form the correct shape self.data = self.data.reshape(self.num_rows, self.num_cols) @@ -225,14 +224,14 @@ def to_cube(self): self.vt_hour, self.vt_minute, self.vt_second) time_coord = DimCoord(iris.unit.date2num(valid_date, 'hours since 1970-01-01 00:00:00', iris.unit.CALENDAR_STANDARD), - standard_name='time', units="hours") + standard_name='time', units='hours') cube.add_aux_coord(time_coord) data_date = netcdftime.datetime(self.dt_year, self.dt_month, self.dt_day, self.dt_hour, self.dt_minute) ref_time_coord = DimCoord(iris.unit.date2num(data_date, 'hours since 1970-01-01 00:00:00', iris.unit.CALENDAR_STANDARD), - standard_name='forecast_reference_time', units="hours") + standard_name='forecast_reference_time', units='hours') cube.add_aux_coord(ref_time_coord) if self.period_minutes != MISSING_INT: @@ -241,12 +240,11 @@ def to_cube(self): # experiment if self.experiment_num != MISSING_INT: - cube.add_aux_coord(DimCoord(self.experiment_num, long_name="experiment_number", - units="1")) + cube.add_aux_coord(DimCoord(self.experiment_num, long_name="experiment_number")) # horizontal grid if self.proj_biaxial_ellipsoid != MISSING_INT: - raise TranslationError("Biaxial ellipoid %d not yet handled" % \ + raise TranslationError("Biaxial ellipoid %d not yet handled" % self.proj_biaxial_ellipsoid) if self.tm_meridian_scaling != MISSING_INT: @@ -256,13 +254,13 @@ def to_cube(self): # "NG", means osgb grid. osgb_cs = iris.coord_systems.OSGB() cube.add_dim_coord( - DimCoord(numpy.arange(self.num_cols)*self.column_step+self.x_origin, + DimCoord(numpy.arange(self.num_cols) * self.column_step + self.x_origin, long_name="x", units="m", coord_system=osgb_cs), 1) cube.add_dim_coord( - DimCoord(numpy.arange(self.num_rows)*self.row_step+self.y_origin, + DimCoord(numpy.arange(self.num_rows) * self.row_step + self.y_origin, long_name="y", units="m", coord_system=osgb_cs), 0) else: - raise TranslationError("Grid type %d not yet implemented" % \ + raise TranslationError("Grid type %d not yet implemented" % self.horizontal_grid_type) # vertical @@ -271,17 +269,17 @@ def to_cube(self): if self.reference_vertical_coord_type == MISSING_INT or \ self.reference_vertical_coord == MISSING_FLOAT: cube.add_aux_coord(DimCoord(self.vertical_coord, - long_name="height", units="m")) + standard_name="height", units="m")) else: raise TranslationError("Bounded vertical not yet implemented") else: - raise TranslationError("Vertical coord type %d currently unhanded" % \ + raise TranslationError("Vertical coord type %d currently unhanded" % self.vertical_coord_type) # add other stuff, if present ensemble_member = getattr(self, "ensemble_member") if ensemble_member != MISSING_INT: - cube.add_aux_coord(DimCoord(ensemble_member, "realization", units="1")) + cube.add_aux_coord(DimCoord(ensemble_member, "realization")) def add_attr(name): value = getattr(self, name) @@ -334,7 +332,6 @@ def load_cubes(filespecs, callback=None): if isinstance(filespecs, basestring): filespecs=[filespecs] - cubes = [] for filespec in filespecs: for filename in glob.glob(filespec): with open(filename, "rb") as infile: diff --git a/lib/iris/tests/results/image_checksums/redhat6.2_64bit/test_nimrod.TestGribLoad.test_load.0.txt b/lib/iris/tests/results/image_checksums/redhat6.2_64bit/test_nimrod.TestGribLoad.test_load.0.txt new file mode 100644 index 0000000000..4ced72f82d --- /dev/null +++ b/lib/iris/tests/results/image_checksums/redhat6.2_64bit/test_nimrod.TestGribLoad.test_load.0.txt @@ -0,0 +1 @@ +-1845015673 \ No newline at end of file diff --git a/lib/iris/tests/results/nimrod/load.cml b/lib/iris/tests/results/nimrod/load.cml index 4548fb40fa..de6769738a 100644 --- a/lib/iris/tests/results/nimrod/load.cml +++ b/lib/iris/tests/results/nimrod/load.cml @@ -13,7 +13,7 @@ - + diff --git a/lib/iris/tests/test_nimrod.py b/lib/iris/tests/test_nimrod.py index 45598bd7c7..ce98950a18 100644 --- a/lib/iris/tests/test_nimrod.py +++ b/lib/iris/tests/test_nimrod.py @@ -29,9 +29,13 @@ class TestGribLoad(tests.GraphicsTest): def test_load(self): - cube = iris.load(tests.get_data_path(('NIMROD', 'uk2km', 'WO0000000003452', '201007020900_u1096_ng_ey00_visibility0180_screen_2km')))[0] + cube = iris.load(tests.get_data_path(('NIMROD', 'uk2km', 'WO0000000003452', + '201007020900_u1096_ng_ey00_visibility0180_screen_2km')))[0] self.assertCML(cube, ("nimrod", "load.cml")) + plt.contourf(cube.data) + self.check_graphic() + # TODO: #84 # iplt.contourf(cube) # iplt.gcm(cube).drawcoastlines()