def __getattr__(self, key): """Return a grib key, or one of our extra keys.""" # is it in the grib message? try: # we just get <type 'float'> as the type of the "values" array...special case here... if key in ["values", "pv", "latitudes", "longitudes"]: res = gribapi.grib_get_double_array(self.grib_message, key) elif key in ('typeOfFirstFixedSurface', 'typeOfSecondFixedSurface'): res = np.int32(gribapi.grib_get_long(self.grib_message, key)) else: key_type = gribapi.grib_get_native_type(self.grib_message, key) if key_type == int: res = np.int32(gribapi.grib_get_long(self.grib_message, key)) elif key_type == float: # Because some computer keys are floats, like # longitudeOfFirstGridPointInDegrees, a float32 is not always enough... res = np.float64(gribapi.grib_get_double(self.grib_message, key)) elif key_type == str: res = gribapi.grib_get_string(self.grib_message, key) else: raise ValueError("Unknown type for %s : %s" % (key, str(key_type))) except gribapi.GribInternalError: res = None #...or is it in our list of extras? if res is None: if key in self.extra_keys: res = self.extra_keys[key] else: #must raise an exception for the hasattr() mechanism to work raise AttributeError("Cannot find GRIB key %s" % key) return res
def grid_definition_template_12(cube, grib): """ Set keys within the provided grib message based on Grid Definition Template 3.12. Template 3.12 is used to represent a Transverse Mercator grid. """ gribapi.grib_set(grib, "gridDefinitionTemplateNumber", 12) # Retrieve some information from the cube. y_coord = cube.coord(dimensions=[0]) x_coord = cube.coord(dimensions=[1]) cs = y_coord.coord_system # Normalise the coordinate values to centimetres - the resolution # used in the GRIB message. def points_in_cm(coord): points = coord.units.convert(coord.points, 'cm') points = np.around(points).astype(int) return points y_cm = points_in_cm(y_coord) x_cm = points_in_cm(x_coord) # Set some keys specific to GDT12. # Encode the horizontal points. # NB. Since we're already in centimetres, our tolerance for # discrepancy in the differences is 1. def step(points): diffs = points[1:] - points[:-1] mean_diff = np.mean(diffs).astype(points.dtype) if not np.allclose(diffs, mean_diff, atol=1): msg = ('Irregular coordinates not supported for transverse ' 'Mercator.') raise iris.exceptions.TranslationError(msg) return int(mean_diff) gribapi.grib_set(grib, 'Di', abs(step(x_cm))) gribapi.grib_set(grib, 'Dj', abs(step(y_cm))) horizontal_grid_common(cube, grib) # GRIBAPI expects unsigned ints in X1, X2, Y1, Y2 but it should accept # signed ints, so work around this. # See https://software.ecmwf.int/issues/browse/SUP-1101 ensure_set_int32_value(grib, 'Y1', int(y_cm[0])) ensure_set_int32_value(grib, 'Y2', int(y_cm[-1])) ensure_set_int32_value(grib, 'X1', int(x_cm[0])) ensure_set_int32_value(grib, 'X2', int(x_cm[-1])) # Lat and lon of reference point are measured in millionths of a degree. gribapi.grib_set(grib, "latitudeOfReferencePoint", cs.latitude_of_projection_origin / _DEFAULT_DEGREES_UNITS) gribapi.grib_set(grib, "longitudeOfReferencePoint", cs.longitude_of_central_meridian / _DEFAULT_DEGREES_UNITS) # Convert a value in metres into the closest integer number of # centimetres. def m_to_cm(value): return int(round(value * 100)) # False easting and false northing are measured in units of (10^-2)m. gribapi.grib_set(grib, 'XR', m_to_cm(cs.false_easting)) gribapi.grib_set(grib, 'YR', m_to_cm(cs.false_northing)) # GRIBAPI expects a signed int for scaleFactorAtReferencePoint # but it should accept a float, so work around this. # See https://software.ecmwf.int/issues/browse/SUP-1100 value = cs.scale_factor_at_central_meridian key_type = gribapi.grib_get_native_type(grib, "scaleFactorAtReferencePoint") if key_type is not float: value = fixup_float32_as_int32(value) gribapi.grib_set(grib, "scaleFactorAtReferencePoint", value)