def getSEED(self, compact=False): """ Returns a SEED representation of the current Parser object. """ self.compact = compact # Nothing to write if not all necessary data is available. if not self.volume or not self.abbreviations or not self.stations: msg = 'No data to be written available.' raise SEEDParserException(msg) # Check blockettes: if not self._checkBlockettes(): msg = 'Not all necessary blockettes are available.' raise SEEDParserException(msg) # String to be written to: seed_string = '' cur_count = 1 volume, abbreviations, stations = self._createBlockettes11and12() # Delete Blockette 11 again. self._deleteBlockettes11and12() # Finally write the actual SEED String. for _i in volume: seed_string += '%06i' % cur_count + _i cur_count += 1 for _i in abbreviations: seed_string += '%06i' % cur_count + _i cur_count += 1 # Remove name of the stations. stations = [_i[1:] for _i in stations] for _i in stations: for _j in _i: seed_string += '%06i' % cur_count + _j cur_count += 1 return seed_string
def _select(self, seed_id, datetime=None): """ Selects all blockettes related to given SEED id and datetime. """ old_format = self._format # parse blockettes if not SEED. Needed foe XSEED to be intialized. # XXX: Should potentially be fixed at some point. if self._format != 'SEED': self.__init__(self.getSEED()) if old_format == "XSEED": self._format = "XSEED" # split id if '.' in seed_id: net, sta, loc, cha = seed_id.split('.') else: cha = seed_id net = sta = loc = None # create a copy of station list stations = list(self.stations) # filter blockettes list by given SEED id station_flag = False channel_flag = False blockettes = [] for station in stations: for blk in station: if blk.id == 50: station_flag = False if net is not None and blk.network_code != net: continue if sta is not None and blk.station_call_letters != sta: continue station_flag = True tmpb50 = blk elif blk.id == 52 and station_flag: channel_flag = False if loc is not None and blk.location_identifier != loc: continue if blk.channel_identifier != cha: continue if datetime is not None: if blk.start_date > datetime: continue if blk.end_date and blk.end_date < datetime: continue channel_flag = True blockettes.append(tmpb50) blockettes.append(blk) elif channel_flag and station_flag: blockettes.append(blk) # check number of selected channels (equals number of blockette 52) b50s = [b for b in blockettes if b.id == 50] b52s = [b for b in blockettes if b.id == 52] if len(b50s) == 0 or len(b52s) == 0: msg = 'No channel found with the given SEED id: %s' raise SEEDParserException(msg % (seed_id)) elif len(b50s) > 1 or len(b52s) > 1: msg = 'More than one channel found with the given SEED id: %s' raise SEEDParserException(msg % (seed_id)) return blockettes
def _parseXMLBlockette(self, XML_blockette, record_type, xseed_version): """ Takes the lxml tree of any blockette and returns a blockette object. """ # Get blockette number. blockette_id = int(XML_blockette.values()[0]) if blockette_id in HEADER_INFO[record_type].get('blockettes', []): class_name = 'Blockette%03d' % blockette_id if not hasattr(blockette, class_name): raise SEEDParserException('Blockette %d not implemented!' % blockette_id) blockette_class = getattr(blockette, class_name) blockette_obj = blockette_class(debug=self.debug, strict=self.strict, compact=self.compact, version=self.version, record_type=record_type, xseed_version=xseed_version) blockette_obj.parseXML(XML_blockette) return blockette_obj elif blockette_id != 0: msg = "Unknown blockette type %d found" % blockette_id raise SEEDParserException(msg)
def _parseSEED(self, data): """ Parses through a whole SEED volume. It will always parse the whole file and skip any time span data. :type data: File pointer or StringIO object. """ # Jump to the beginning of the file. data.seek(0) # Retrieve some basic data like version and record length. temp = data.read(8) # Check whether it starts with record sequence number 1 and a volume # index control header. if temp != '000001V ': raise SEEDParserException("Expecting 000001V ") # The first blockette has to be Blockette 10. temp = data.read(3) if temp not in ['010', '008', '005']: raise SEEDParserException("Expecting blockette 010, 008 or 005") # Skip the next four bytes containing the length of the blockette. data.seek(4, 1) # Set the version. self.version = float(data.read(4)) # Get the record length. length = pow(2, int(data.read(2))) # Test record length. data.seek(length) temp = data.read(6) if temp != '000002': msg = "Got an invalid logical record length %d" % length raise SEEDParserException(msg) self.record_length = length if self.debug: print("RECORD LENGTH: %d" % (self.record_length)) # Set all temporary attributes. self.temp = {'volume': [], 'abbreviations': [], 'stations': []} # Jump back to beginning. data.seek(0) # Read the first record. record = data.read(self.record_length) merged_data = '' record_type = None # Loop through file and pass merged records to _parseMergedData. while record: record_continuation = (record[7] == CONTINUE_FROM_LAST_RECORD) same_record_type = (record[6] == record_type) if record_type == 'S' and record[8:11] != '050': record_continuation = True if record_continuation and same_record_type: # continued record merged_data += record[8:] else: self._parseMergedData(merged_data.strip(), record_type) # first or new type of record record_type = record[6] merged_data = record[8:] if record_type not in HEADERS: # only parse headers, no data merged_data = '' record_type = None break if self.debug: if not record_continuation: print("========") print(record[0:8]) record = data.read(self.record_length) # Use parse once again. self._parseMergedData(merged_data.strip(), record_type) # Update the internal structure to finish parsing. self._updateInternalSEEDStructure()
def getXSEED(self, version=DEFAULT_XSEED_VERSION, split_stations=False): """ Returns a XSEED representation of the current Parser object. :type version: float, optional :param version: XSEED version string (default is ``1.1``). :type split_stations: boolean, optional :param split_stations: Splits stations containing multiple channels into multiple documents. :rtype: str or dict :return: Returns either a string or a dict of strings depending on the flag ``split_stations``. """ if version not in XSEED_VERSIONS: raise SEEDParserException("Unknown XML-SEED version!") doc = Element("xseed", version=version) # Nothing to write if not all necessary data is available. if not self.volume or not self.abbreviations or \ len(self.stations) == 0: msg = 'No data to be written available.' raise SEEDParserException(msg) # Check blockettes: if not self._checkBlockettes(): msg = 'Not all necessary blockettes are available.' raise SEEDParserException(msg) # Add blockettes 11 and 12 only for XSEED version 1.0. if version == '1.0': self._createBlockettes11and12(blockette12=True) # Now start actually filling the XML tree. # Volume header: sub = SubElement(doc, utils.toTag('Volume Index Control Header')) for blkt in self.volume: sub.append(blkt.getXML(xseed_version=version)) # Delete blockettes 11 and 12 if necessary. if version == '1.0': self._deleteBlockettes11and12() # Abbreviations: sub = SubElement(doc, utils.toTag('Abbreviation Dictionary Control Header')) for blkt in self.abbreviations: sub.append(blkt.getXML(xseed_version=version)) if not split_stations: # Don't split stations for station in self.stations: sub = SubElement(doc, utils.toTag('Station Control Header')) for blkt in station: sub.append(blkt.getXML(xseed_version=version)) if version == '1.0': # To pass the XSD schema test an empty time span control header # is added to the end of the file. SubElement(doc, utils.toTag('Timespan Control Header')) # Also no data is present in all supported SEED files. SubElement(doc, utils.toTag('Data Records')) # Return single XML String. return tostring(doc, pretty_print=True, xml_declaration=True, encoding='UTF-8') else: # generate a dict of XML resources for each station result = {} for station in self.stations: cdoc = copy.copy(doc) sub = SubElement(cdoc, utils.toTag('Station Control Header')) for blkt in station: sub.append(blkt.getXML(xseed_version=version)) if version == '1.0': # To pass the XSD schema test an empty time span control # header is added to the end of the file. SubElement(doc, utils.toTag('Timespan Control Header')) # Also no data is present in all supported SEED files. SubElement(doc, utils.toTag('Data Records')) try: id = station[0].end_effective_date.datetime except AttributeError: id = '' result[id] = tostring(cdoc, pretty_print=True, xml_declaration=True, encoding='UTF-8') return result
def _parseMergedData(self, data, record_type): """ This method takes any merged SEED record and writes its blockettes in the corresponding dictionary entry of self.temp. """ if not data: return # Create StringIO for easier access. data = StringIO(data) # Do not do anything if no data is passed or if a time series header # is passed. if record_type not in HEADERS: return # Set standard values. blockette_length = 0 blockette_id = -1 # Find out what kind of record is being parsed. if record_type == 'S': # Create new station blockettes list. self.temp['stations'].append([]) root_attribute = self.temp['stations'][-1] elif record_type == 'V': # Just one Volume header per file allowed. if len(self.temp['volume']): msg = 'More than one Volume index control header found!' raise SEEDParserException(msg) root_attribute = self.temp['volume'] else: # Just one abbreviations header allowed! if len(self.temp['abbreviations']): msg = 'More than one Abbreviation Dictionary Control ' + \ 'Headers found!' warnings.warn(msg, UserWarning) root_attribute = self.temp['abbreviations'] # Loop over all blockettes in data. while blockette_id != 0: # remove spaces between blockettes while data.read(1) == ' ': continue data.seek(-1, 1) try: blockette_id = int(data.read(3)) blockette_length = int(data.read(4)) except: break data.seek(-7, 1) if blockette_id in HEADER_INFO[record_type].get('blockettes', []): class_name = 'Blockette%03d' % blockette_id if not hasattr(blockette, class_name): raise SEEDParserException('Blockette %d not implemented!' % blockette_id) blockette_class = getattr(blockette, class_name) blockette_obj = blockette_class(debug=self.debug, strict=self.strict, compact=self.compact, version=self.version, record_type=record_type) blockette_obj.parseSEED(data, blockette_length) root_attribute.append(blockette_obj) self.blockettes.setdefault(blockette_id, []).append(blockette_obj) elif blockette_id != 0: msg = "Unknown blockette type %d found" % blockette_id raise SEEDParserException(msg) # check if everything is parsed if data.len != data.tell(): warnings.warn("There exist unparsed elements!")