# Copyright (c) 2014,Vienna University of Technology, Department of Geodesy and Geoinformation
# All rights reserved.
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
# * Neither the name of the Vienna University of Technology, Department of
# Geodesy and Geoinformation nor the names of its contributors may be
# used to endorse or promote products derived from this software without
# specific prior written permission.
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL VIENNA UNIVERSITY OF TECHNOLOGY,
# DEPARTMENT OF GEODESY AND GEOINFORMATION BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
'''
Created on May 21, 2014
@author: Christoph Paulik christoph.paulik@geo.tuwien.ac.at
'''
import warnings
try:
from pybufr_ecmwf import raw_bufr_file
from pybufr_ecmwf import ecmwfbufr
from pybufr_ecmwf import ecmwfbufr_parameters
except ImportError:
warnings.warn('pybufr-ecmwf can not be imported, H08 and H07 images can not be read.')
import os
import numpy as np
[docs]class BUFRReader(object):
"""
BUFR reader based on the pybufr-ecmwf package but faster
Parameters
----------
filename : string
filename of the bufr file
kelem_guess : int, optional
if the elements per variable in as message are known
please specify here.
Otherwise the elements will be found out via trial and error
This works most of the time but is not 100 percent failsafe
Default: 500
max_tries : int, optional
the Reader will try max_tries times to unpack a bufr message.
Some messages can not be read even if the array sizes are ok.
Most of the time these files are corrupt.
"""
def __init__(self, filename, kelem_guess=500, max_tries=10):
self.bufr = raw_bufr_file.RawBUFRFile()
self.bufr.open(filename, 'rb')
self.nr_messages = self.bufr.get_num_bufr_msgs()
self.max_tries = max_tries
if 'BUFR_TABLES' not in os.environ:
path = os.path.split(ecmwfbufr.__file__)[0]
os.environ["BUFR_TABLES"] = os.path.join(path, 'ecmwf_bufrtables' + os.sep)
# os.environ['PRINT_TABLE_NAMES'] = "false"
self.size_ksup = ecmwfbufr_parameters.JSUP
self.size_ksec0 = ecmwfbufr_parameters.JSEC0
self.size_ksec1 = ecmwfbufr_parameters.JSEC1
self.size_ksec2 = ecmwfbufr_parameters.JSEC2
self.size_key = ecmwfbufr_parameters.JKEY
self.size_ksec3 = ecmwfbufr_parameters.JSEC3
self.size_ksec4 = ecmwfbufr_parameters.JSEC4
self.kelem_guess = kelem_guess
[docs] def messages(self):
"""
Raises
------
IOError:
if a message cannot be unpacked after max_tries tries
Returns
-------
data : yield results of messages
"""
for i in np.arange(self.nr_messages) + 1:
tries = 0
ksup = np.zeros(self.size_ksup, dtype=np.int)
ksec0 = np.zeros(self.size_ksec0, dtype=np.int)
ksec1 = np.zeros(self.size_ksec1, dtype=np.int)
ksec2 = np.zeros(self.size_ksec2, dtype=np.int)
key = np.zeros(self.size_key, dtype=np.int)
ksec3 = np.zeros(self.size_ksec3, dtype=np.int)
ksec4 = np.zeros(self.size_ksec4, dtype=np.int)
kerr = 0
data = self.bufr.get_raw_bufr_msg(i)
ecmwfbufr.bus012(data[0], # input
ksup, # output
ksec0, # output
ksec1, # output
ksec2, # output
kerr) # output
kelem = self.kelem_guess
ksup_first = ksup[5]
kvals = ksup_first * kelem
max_kelem = 500000
self.init_values = np.zeros(kvals, dtype=np.float64)
self.cvals = np.zeros((kvals, 80), dtype=np.character)
# try to expand bufr message with the first guess for
# kelem
increment_arraysize = True
while increment_arraysize:
cnames = np.zeros((kelem, 64), dtype='|S1')
cunits = np.zeros((kelem, 24), dtype='|S1')
ecmwfbufr.bufrex(data[0], # input
ksup, # output
ksec0, # output
ksec1, # output
ksec2, # output
ksec3, # output
ksec4, # output
cnames, # output
cunits, # output
self.init_values, # output
self.cvals, # output
kerr) # output
# no error - stop loop
if kerr == 0 and ksec4[0] != 0:
increment_arraysize = False
# error increase array size and try to unpack again
else:
tries += 1
if tries >= self.max_tries:
raise IOError('This file seems corrupt')
kelem = kelem * 5
kvals = ksup_first * kelem
if kelem > max_kelem:
kelem = kvals / 2
max_kelem = kvals
self.init_values = np.zeros(kvals, dtype=np.float64)
self.cvals = np.zeros((kvals, 80), dtype=np.character)
decoded_values = ksup[4]
# set kelem_guess to decoded values of last message
# only increases reading speed if all messages are the same
# not sure if this is the best option
self.kelem_guess = decoded_values
decoded_msg = ksup[5]
# calculate first dimension of 2D array
factor = kvals / kelem
# reshape and trim the array to the actual size of the data
values = self.init_values.reshape((factor, kelem))
values = values[:decoded_msg, :decoded_values]
yield values
def __enter__(self):
return self
def __exit__(self, exc, val, trace):
self.bufr.close()