def _regularise_shape(grib_message): """ Calculate the regularised shape of the reduced message and push dummy regularised values into the message to force the gribapi to update the message grid type from reduced to regular. """ # Make sure to read any missing values as NaN. gribapi.grib_set_double(grib_message, "missingValue", np.nan) # Get full longitude values, these describe the longitude value of # *every* point in the grid, they are not 1d monotonic coordinates. lons = gribapi.grib_get_double_array(grib_message, "longitudes") # Compute the new longitude coordinate for the regular grid. new_nx = max(gribapi.grib_get_long_array(grib_message, "pl")) new_x_step = (max(lons) - min(lons)) / (new_nx - 1) if gribapi.grib_get_long(grib_message, "iScansNegatively"): new_x_step *= -1 gribapi.grib_set_long(grib_message, "Nx", int(new_nx)) gribapi.grib_set_double(grib_message, "iDirectionIncrementInDegrees", float(new_x_step)) # Spoof gribapi with false regularised values. nj = gribapi.grib_get_long(grib_message, 'Nj') temp = np.zeros((nj * new_nx,), dtype=np.float) gribapi.grib_set_double_array(grib_message, 'values', temp) gribapi.grib_set_long(grib_message, "jPointsAreConsecutive", 0) gribapi.grib_set_long(grib_message, "PLPresent", 0)
def set_data(source, grib): """ Sets the actual data of a grib message. """ var_name = get_varible_name(source) # treat masked arrays differently if isinstance(source[var_name].values, np.ma.core.MaskedArray): gribapi.grib_set(grib, "bitmapPresent", 1) # use the missing value from the masked array as default missing_value = source[var_name].values.get_fill_value() # but give the netCDF specified missing value preference missing_value = source[var_name].attrs.get('missing_value', missing_value) gribapi.grib_set_double(grib, "missingValue", float(missing_value)) data = source[var_name].values.filled() else: gribapi.grib_set_double(grib, "missingValue", 9999) data = source[var_name].values[:] gribapi.grib_set_long(grib, "bitsPerValue", 12) #gribapi.grib_set_long(grib, "bitsPerValueAndRepack", 12) gribapi.grib_set_long(grib, "decimalPrecision", 2) gribapi.grib_set_long(grib, "decimalScaleFactor", 2) #gribapi.grib_set_long(grib, "binaryScaleFactor", 0) gribapi.grib_set_long(grib, "dataRepresentationType", 0) # get the grib code for the variable code = reverse_codes[conv.to_grib1[var_name]] _, grib_unit = codes[code] # default to the grib default unit unit = source[var_name].attrs.get('units', grib_unit) mult = 1. if not unit == grib_unit: mult = units._speed[unit] / units._speed[grib_unit] # add the data gribapi.grib_set_double_array(grib, "values", mult * data.flatten())
def dx_dy(x_coord, y_coord, grib): x_step = regular_step(x_coord) y_step = regular_step(y_coord) # TODO: THIS USED BE "Dx" and "Dy"!!! DID THE API CHANGE AGAIN??? gribapi.grib_set_double(grib, "DxInDegrees", float(abs(x_step))) gribapi.grib_set_double(grib, "DyInDegrees", float(abs(y_step)))
def system_test_grib_patch(self): import gribapi gm = gribapi.grib_new_from_samples("GRIB2") result = gribapi.grib_get_double(gm, "missingValue") new_missing_value = 123456.0 gribapi.grib_set_double(gm, "missingValue", new_missing_value) new_result = gribapi.grib_get_double(gm, "missingValue") self.assertEqual(new_result, new_missing_value)
def _message_values(grib_message, shape): gribapi.grib_set_double(grib_message, 'missingValue', np.nan) data = gribapi.grib_get_double_array(grib_message, 'values') data = data.reshape(shape) # Handle missing values in a sensible way. mask = np.isnan(data) if mask.any(): data = ma.array(data, mask=mask, fill_value=np.nan) return data
def data(cube, grib): # mdi if isinstance(cube.data, ma.core.MaskedArray): gribapi.grib_set_double(grib, "missingValue", float(cube.data.fill_value)) data = cube.data.filled() else: gribapi.grib_set_double(grib, "missingValue", float(-1e9)) data = cube.data # values gribapi.grib_set_double_array(grib, "values", data.flatten())
def data_section(cube, grib): # Masked data? if isinstance(cube.data, ma.core.MaskedArray): # What missing value shall we use? if not np.isnan(cube.data.fill_value): # Use the data's fill value. fill_value = float(cube.data.fill_value) else: # We can't use the data's fill value if it's NaN, # the GRIB API doesn't like it. # Calculate an MDI outside the data range. min, max = cube.data.min(), cube.data.max() fill_value = min - (max - min) * 0.1 # Prepare the unmaksed data array, using fill_value as the MDI. data = cube.data.filled(fill_value) else: fill_value = None data = cube.data # units scaling grib2_info = gptx.cf_phenom_to_grib2_info(cube.standard_name, cube.long_name) if grib2_info is None: # for now, just allow this warnings.warn('Unable to determine Grib2 parameter code for cube.\n' 'Message data may not be correctly scaled.') else: if cube.units != grib2_info.units: data = cube.units.convert(data, grib2_info.units) if fill_value is not None: fill_value = cube.units.convert(fill_value, grib2_info.units) if fill_value is None: # Disable missing values in the grib message. gribapi.grib_set(grib, "bitmapPresent", 0) else: # Enable missing values in the grib message. gribapi.grib_set(grib, "bitmapPresent", 1) gribapi.grib_set_double(grib, "missingValue", fill_value) gribapi.grib_set_double_array(grib, "values", data.flatten())
def data(cube, grib): # mdi if isinstance(cube.data, ma.core.MaskedArray): gribapi.grib_set(grib, "bitmapPresent", 1) gribapi.grib_set_double(grib, "missingValue", float(cube.data.fill_value)) data = cube.data.filled() else: gribapi.grib_set_double(grib, "missingValue", float(-1e9)) data = cube.data # units scaling grib2_info = gptx.cf_phenom_to_grib2_info(cube.standard_name, cube.long_name) if grib2_info is None: # for now, just allow this warnings.warn('Unable to determine Grib2 parameter code for cube.\n' 'Message data may not be correctly scaled.') else: if cube.units != grib2_info.units: data = cube.units.convert(data, grib2_info.units) # values gribapi.grib_set_double_array(grib, "values", data.flatten())
def _message_values(grib_message, shape): gribapi.grib_set_double(grib_message, 'missingValue', np.nan) data = gribapi.grib_get_double_array(grib_message, 'values') data = data.reshape(shape) return data
def _regularise(grib_message): """ Transform a reduced grid to a regular grid using interpolation. Uses 1d linear interpolation at constant latitude to make the grid regular. If the longitude dimension is circular then this is taken into account by the interpolation. If the longitude dimension is not circular then extrapolation is allowed to make sure all end regular grid points get a value. In practice this extrapolation is likely to be minimal. """ # Make sure to read any missing values as NaN. gribapi.grib_set_double(grib_message, "missingValue", np.nan) # Get full longitude values, these describe the longitude value of # *every* point in the grid, they are not 1d monotonic coordinates. lons = gribapi.grib_get_double_array(grib_message, "longitudes") # Compute the new longitude coordinate for the regular grid. new_nx = max(gribapi.grib_get_long_array(grib_message, "pl")) new_x_step = (max(lons) - min(lons)) / (new_nx - 1) if gribapi.grib_get_long(grib_message, "iScansNegatively"): new_x_step *= -1 new_lons = np.arange(new_nx) * new_x_step + lons[0] # Get full latitude and data values, these describe the latitude and # data values of *every* point in the grid, they are not 1d monotonic # coordinates. lats = gribapi.grib_get_double_array(grib_message, "latitudes") values = gribapi.grib_get_double_array(grib_message, "values") # Retrieve the distinct latitudes from the GRIB message. GRIBAPI docs # don't specify if these points are guaranteed to be oriented correctly so # the safe option is to sort them into ascending (south-to-north) order # and then reverse the order if necessary. new_lats = gribapi.grib_get_double_array(grib_message, "distinctLatitudes") new_lats.sort() if not gribapi.grib_get_long(grib_message, "jScansPositively"): new_lats = new_lats[::-1] ny = new_lats.shape[0] # Use 1d linear interpolation along latitude circles to regularise the # reduced data. cyclic = _longitude_is_cyclic(new_lons) new_values = np.empty([ny, new_nx], dtype=values.dtype) for ilat, lat in enumerate(new_lats): idx = np.where(lats == lat) llons = lons[idx] vvalues = values[idx] if cyclic: # For cyclic data we insert dummy points at each end to ensure # we can interpolate to all output longitudes using pure # interpolation. cgap = (360 - llons[-1] - llons[0]) llons = np.concatenate( (llons[0:1] - cgap, llons, llons[-1:] + cgap)) vvalues = np.concatenate( (vvalues[-1:], vvalues, vvalues[0:1])) fixed_latitude_interpolator = scipy.interpolate.interp1d( llons, vvalues) else: # Allow extrapolation for non-cyclic data sets to ensure we can # interpolate to all output longitudes. fixed_latitude_interpolator = Linear1dExtrapolator( scipy.interpolate.interp1d(llons, vvalues)) new_values[ilat] = fixed_latitude_interpolator(new_lons) new_values = new_values.flatten() # Set flags for the regularised data. if np.isnan(new_values).any(): # Account for any missing data. gribapi.grib_set_double(grib_message, "missingValue", np.inf) gribapi.grib_set(grib_message, "bitmapPresent", 1) new_values = np.where(np.isnan(new_values), np.inf, new_values) gribapi.grib_set_long(grib_message, "Nx", int(new_nx)) gribapi.grib_set_double(grib_message, "iDirectionIncrementInDegrees", float(new_x_step)) gribapi.grib_set_double_array(grib_message, "values", new_values) gribapi.grib_set_long(grib_message, "jPointsAreConsecutive", 0) gribapi.grib_set_long(grib_message, "PLPresent", 0)