def create_bufr_template(): # #[ create the template ''' now use these definitions to create a BUFR template ''' from pybufr_ecmwf.bufr_template import BufrTemplate variable3 = 0 template = BufrTemplate() template.add_descriptor(D_301192) # 4 items template.add_descriptor(D_301193) # 6 items if USE_DELAYED_REPLICATION: max_nr = 5 # max. 5*1 items template.add_delayed_replic_descriptors(max_nr, [variable3,]) return template
def create_bufr_template(): # #[ create the template ''' now use these definitions to create a BUFR template ''' from pybufr_ecmwf.bufr_template import BufrTemplate variable3 = 0 template = BufrTemplate(verbose=True) template.add_descriptor(D_301192) # 4 items template.add_descriptor(D_301193) # 6 items if USE_DELAYED_REPLICATION: max_nr = 5 # max. 5*1 items template.add_delayed_replic_descriptors(max_nr, [variable3,]) return template
print('='*50) bufr_table_set.print_D_table() print('='*50) # define the table name without preceding 'B' or 'D' character # (which will be prepended by the below write method) table_name = '_test_table.txt' bufr_table_set.write_tables(table_name) # now use these definitions to create a BUFR template max_nr_of_repeats = 5 num_subsets = 2 num_repetitions = [3, 5] # actual number to be used template = BufrTemplate(verbose=True) template.add_descriptor(D_363192) # 1 item template.del_repl_max_nr_of_repeats_list = [max_nr_of_repeats,]*num_subsets # and use this BUFR template to create a test BUFR message bufr = BUFRInterfaceECMWF(verbose=True) # fill sections 0, 1, 2 and 3 bufr.fill_sections_0123(bufr_code_centre=0, bufr_obstype=0, bufr_subtype=0, bufr_table_local_version=0, bufr_table_master=0, bufr_table_master_version=0, bufr_code_subcentre=0, num_subsets=num_subsets, bufr_compression_flag=0)
class BUFRMessage_W: # #[ bufr msg class for writing """ a class that implements iteration over the data in a given bufr message for reading """ def __init__(self, parent, num_subsets=1, verbose=False, do_range_check=False): # #[ initialise a message for writing self.parent = parent self.num_subsets = num_subsets self.verbose = verbose self.do_range_check = do_range_check self._bufr_obj = BUFRInterfaceECMWF(verbose=verbose) # fill sections 0, 1, 2 and 3 with default values self._bufr_obj.fill_sections_0123( bufr_code_centre=0, # use official WMO tables bufr_obstype=3, # sounding bufr_subtype=253, # L2B bufr_table_local_version=0, # dont use local tables bufr_table_master=0, bufr_table_master_version=26, # use latest WMO version bufr_code_subcentre=0, # L2B processing facility num_subsets=num_subsets, bufr_compression_flag=64) # 64=compression/0=no compression #table_name = 'default' # self._bufr_obj.setup_tables(table_b_to_use='B'+table_name, # table_d_to_use='D'+table_name) # use information from sections 0123 to construct the BUFR table # names expected by the ECMWF BUFR library self._bufr_obj.setup_tables() # init to None self.template = None self.values = None self.cvals = None # #] def set_template(self, *args, **kwargs): # #[ set the template self.template = BufrTemplate() # todo: see if it is possible to also allow # a bufr_template instance as input for descr in args: # inputs may be integer, string or a Descriptor instance # print('adding descriptor: ', descr, ' of type ', type(descr)) self.template.add_descriptor(descr) if 'max_repl' in kwargs: #print('max_repl = ', kwargs['max_repl']) self.template.del_repl_max_nr_of_repeats_list = kwargs['max_repl'] self._bufr_obj.register_and_expand_descriptors(self.template) # retrieve the length of the expanded descriptor list exp_descr_list_length = self._bufr_obj.ktdexl if self.verbose: print("exp_descr_list_length = ", exp_descr_list_length) # ensure zeros at the end are removed, so explicitely # define the end of the slice exp_descr_list = self._bufr_obj.ktdexp[:exp_descr_list_length] if self.verbose: print("exp_descr_list = ", self._bufr_obj.ktdexp) self.num_fields = exp_descr_list_length # ensure all descriptors are instances of bufr_table.Descriptor self.normalised_descriptor_list = \ self._bufr_obj.bt.normalise_descriptor_list(exp_descr_list) # allocate the needed values and cvalues arrays self.num_values = self.num_subsets * self.num_fields self.values = numpy.zeros(self.num_values, dtype=numpy.float64) # note: float64 is the default but it doesnt hurt to be explicit if self.verbose: print("self.num_values = ", self.num_values) # note: these two must be identical for now, otherwise the # python to fortran interface breaks down. This also ofcourse is the # cause of the huge memory use of cvals in case num_values is large. self.num_cvalues = self.num_values self.cvals = numpy.zeros((self.num_cvalues, 80), dtype=numpy.character) self.cvals_index = 0 # dont use this, it is not compatible to python 2.6: # from collections import OrderedDict # since I cannot use an orderddict due to missing compatibility # to python 2.6, I'll use an additional (ordered) list of keys # fill an ordered dict with field properties for convenience self.field_properties = {} self.field_properties_keys = [] for idx, descr in enumerate(self.normalised_descriptor_list): if descr.unit == 'CCITTIA5': (min_allowed_num_chars, max_allowed_num_chars, dummy_var) = descr.get_min_max_step() p = { 'index': idx, 'name': descr.name, 'min_allowed_num_chars': min_allowed_num_chars, 'max_allowed_num_chars': max_allowed_num_chars } else: (min_allowed_value, max_allowed_value, step) = descr.get_min_max_step() p = { 'index': idx, 'name': descr.name, 'min_allowed_value': min_allowed_value, 'max_allowed_value': max_allowed_value, 'step': step } self.field_properties[descr.reference] = p self.field_properties_keys.append(descr.reference) # #] def copy_template_from_bufr_msg(self, msg): pass def get_field_names(self): # #[ request field names names = [] for key in self.field_properties_keys: p = self.field_properties[key] names.append(p['name']) return names # #] def add_subset_data(self, data): pass def write_msg_to_file(self): # #[ write out the current message # do the encoding to binary format self._bufr_obj.encode_data(self.values, self.cvals) # check if file was properly opened if not self.parent.is_open: errtxt = 'please open the bufr file before writing data to it!' raise IncorrectUsageError(errtxt) # write the encoded BUFR message self.parent.raw_bf.write_raw_bufr_msg(self._bufr_obj.encoded_message) # #] def str_get_index_to_use(self, this_key): # #[ convert string input for key to index in exp. descr. list # see if an index is provided index = -1 if '[' in this_key: parts = this_key.split('[') this_key = parts[0] index_str = parts[1][:-1] index = int(index_str) possible_matches = [] names_of_possible_matches = [] try: reference = int(this_key) p = self.field_properties[reference] descr_name = p['name'] except: # this appears to be not an integer number, so assume # (part of) the name is given descr_name = this_key for key in self.field_properties_keys: p = self.field_properties[key] if descr_name in p['name']: possible_matches.append(key) names_of_possible_matches.append(p['name']) # print('possible matches for key: ', possible_matches) if len(possible_matches) == 1: # ok, proper location found key = possible_matches[0] p = self.field_properties[key] index_to_use = p['index'] # print('filling row:', p) elif len(possible_matches) == 0: errtxt = ('ERROR: the current BUFRmessage does not contain any ' + 'fields that have [{}] in their name.'.format(this_key)) raise IncorrectUsageError(errtxt) elif index >= 0: # ok, proper location found since an index was supplied try: key = possible_matches[index] except: # invalid index errtxt = ('ERROR: the index on the requested descriptor ' + 'is out of the possible range. ' + 'Only {0} '.format(len(possible_matches)) + 'possible matches are present in this template. ' + 'while the requested index was {} '.format(index) + 'for key {0}.'.format(this_key)) raise IncorrectUsageError(errtxt) p = self.field_properties[key] index_to_use = p['index'] # print('filling row:', p) else: errtxt = ('ERROR: the current BUFRmessage has multiple ' + 'fields that have [{}] in their name.'.format(this_key) + ' Please add an index to indicate which ' + 'field should be used. Key [{}] matches with {}.'.format( this_key, names_of_possible_matches)) raise IncorrectUsageError(errtxt) return index_to_use, p # #] def num_get_index_to_use(self, this_key): # #[ get properties for direct index # print('self.field_properties_keys = ', self.field_properties_keys) # print('self.field_properties = ', self.field_properties) index_to_use = this_key reference = self.field_properties_keys[this_key] p = self.field_properties[reference] return index_to_use, p # #] def fill(self, values): # #[ fill all elements for all subsets using a 2d array np_values = numpy.array(values) # check array shape nfields_data, nsubsets_data = numpy.shape(np_values) if ((nsubsets_data != self.num_subsets) or (nfields_data != self.num_fields)): errtxt = ( 'input values array has wrong shape! ' + 'values shape: {0}:{1} '.format(nsubsets_data, nfields_data) + 'but expected shape is: {0}:{1}'.format( self.num_subsets, self.num_fields)) raise IncorrectUsageError(errtxt) # fill the requested row with data for subset in range(self.num_subsets): i = subset * self.num_fields for j in range(self.num_fields): self.values[i + j] = np_values[j, subset] # #] def fill_subset(self, isubset, values): # #[ fill all elements for a given subset np_values = numpy.array(values) # check subset index nfields_data = len(np_values) if ((isubset < 0) or (isubset >= self.num_subsets)): errtxt = ('incorrect subset number: {0} '.format(isubset) + 'The subset index must be between {0} and {1}.'.format( 0, self.num_subsets - 1)) raise IncorrectUsageError(errtxt) # check array length if (nfields_data != self.num_fields): errtxt = ('input values array has wrong length! ' + 'values length: {0} '.format(nfields_data) + 'but expected length is: {0}'.format(self.num_fields)) raise IncorrectUsageError(errtxt) # fill the requested row with data i = isubset * self.num_fields for j in range(self.num_fields): self.values[i + j] = np_values[j] # #] def check_and_assign_val(self, this_value, p, j): # #[ chack range and assign if self.do_range_check: # optional, since this may make the code slower check_range(p, this_value) self.values[j] = this_value # #] def check_and_assign_ascii_val(self, this_value, p, j): # #[ check length of input string and assign to cvals array # no need to check this one I guess #p['min_allowed_num_chars'] max_len = p['max_allowed_num_chars'] if len(this_value) > max_len: print('WARNING: string is too long and will be truncated', file=sys.stderr) print('during encoding of: [{0}]'.format(this_value), file=sys.stderr) print( 'Maximum allowed lenght in the current template is: {}'.format( max_len), file=sys.stderr) print('but this string has length: {}'.format(len(this_value)), file=sys.stderr) # truncate string this_value = this_value[:max_len] # ensure input string has correct length # and is left aligned # (if optional right alignment is needed, change < in >) this_value = '{0:<{width}s}'.format(this_value, width=max_len) self.cvals[self.cvals_index, :] = ' ' # init with spaces for ic, c in enumerate(this_value): self.cvals[self.cvals_index, ic] = c # copy characters # store the cvals_index for the cvals array in the values # array, this is needed so the software can find the the # text string self.values[j] = ((self.cvals_index + 1) * 1000 + len(this_value)) self.cvals_index = self.cvals_index + 1 # #] def __setitem__(self, this_key, this_value): # #[ allow addition of date with dict like interface # print('searching for: ', this_key) if type(this_key) is int: # a direct index to the expanded list of descriptors # should be given in this case index_to_use, p = self.num_get_index_to_use(this_key) elif type(this_key) is str: index_to_use, p = self.str_get_index_to_use(this_key) else: errtxt = 'key has unknown type: {}'.format(type(this_key)) raise IncorrectUsageError(errtxt) # check if input value is character string input_is_ccittia5 = False if type(this_value) is str: input_is_ccittia5 = True n = 1 else: # check length of input (scalar or array?) try: n = len(this_value) try: if type(this_value[0]) is str: input_is_ccittia5 = True except IndexError: pass except TypeError: n = 1 if n != 1: if n != self.num_subsets: errtxt = ('Please provide an array of size num_subsets! ' + 'Current array has size {0} '.format(n) + 'but num_subsets is {0}'.format(self.num_subsets)) raise IncorrectUsageError(errtxt) # fill the requested row with data for subset in range(self.num_subsets): i = subset * self.num_fields j = i + index_to_use if not input_is_ccittia5: if n == 1: self.check_and_assign_val(this_value, p, j) else: self.check_and_assign_val(this_value[subset], p, j) else: # special case for character strings if n == 1: self.check_and_assign_ascii_val(this_value, p, j) else: self.check_and_assign_ascii_val(this_value[subset], p, j)
table_name = '_test_table.txt' bufr_table_set.write_tables(table_name) # now use these definitions to create a BUFR template max_nr_of_replications = 3 num_subsets = 3 num_replications1 = [1, 2, 3] num_replications2 = [[ 1, ], [ 2, 2, ], [3, 3, 3]] template = BufrTemplate(verbose=True) template.add_descriptor(D_363192) # 1 item template.del_repl_max_nr_of_repeats_list = ([ max_nr_of_replications, ] * max_nr_of_replications * num_subsets) # and use this BUFR template to create a test BUFR message bufr = BUFRInterfaceECMWF(verbose=True) # fill sections 0, 1, 2 and 3 bufr.fill_sections_0123(bufr_code_centre=0, bufr_obstype=0, bufr_subtype=0, bufr_table_local_version=0, bufr_table_master=0, bufr_table_master_version=0, bufr_code_subcentre=0,
bufr_table_set.print_B_table() print('='*50) print("D-table:") print('='*50) bufr_table_set.print_D_table() print('='*50) # define the table name without preceding 'B' or 'D' character # (which will be prepended by the below write method) table_name = '_test_table.txt' bufr_table_set.write_tables(table_name) # now use these definitions to create a BUFR template max_nr_of_repeats = 5 template = BufrTemplate(verbose=True) template.add_descriptor(var1) # 1 item template.add_delayed_replic_descriptors(max_nr_of_repeats, D_363192) # max. 1 + 5*5 items # and use this BUFR template to create a test BUFR message bufr = BUFRInterfaceECMWF(verbose=True) # fill sections 0, 1, 2 and 3 num_subsets = 3 bufr.fill_sections_0123(bufr_code_centre=0, bufr_obstype=0, bufr_subtype=0, bufr_table_local_version=0, bufr_table_master=0, bufr_table_master_version=0, bufr_code_subcentre=0,