pylot/pylot/core/read/data.py

357 lines
12 KiB
Python

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import os
import numpy as np
from obspy.core import (read, Stream, UTCDateTime)
from obspy import readEvents
from obspy.core.event import (Event, Catalog)
from pylot.core.read import readPILOTEvent
from pylot.core.util import fnConstructor, createEvent, FormatError
class Data(object):
'''
Data container with attributes wfdata holding ~obspy.core.stream.
:type parent: PySide.QtGui.QWidget object, optional
:param parent: A PySide.QtGui.QWidget object utilized when
called by a GUI to display a PySide.QtGui.QMessageBox instead of printing
to standard out.
:type wfdata: ~obspy.core.stream.Stream object, optional
:param wfdata: ~obspy.core.stream.Stream object containing all available
waveform data for the actual event
:type evtdata: ~obspy.core.event.Event object, optional
:param evtdata ~obspy.core.event.Event object containing all derived or
loaded event. Container object holding, e.g. phase arrivals, etc.
'''
def __init__(self, parent=None, evtdata=None):
if parent:
self.comp = parent.getComponent()
else:
self.comp = 'Z'
self.wfdata = Stream()
self.newevent = False
if evtdata is not None and isinstance(evtdata, Event):
self.evtdata = evtdata
elif evtdata is not None and not isinstance(evtdata, dict):
cat = readEvents(evtdata)
self.evtdata = cat[0]
elif evtdata is not None:
cat = readPILOTEvent(**evtdata)
else: # create an empty Event object
self.newevent = True
self.evtdata = Event()
self.wforiginal = None
self.cuttimes = None
self.dirty = False
def isNew(self):
return self.newevent
def getCutTimes(self):
if self.cuttimes is None:
self.updateCutTimes()
return self.cuttimes
def updateCutTimes(self):
min_start = UTCDateTime()
max_end = None
for trace in self.getWFData().select(component=self.getComp()):
if trace.stats.starttime < min_start:
min_start = trace.stats.starttime
if max_end is None or trace.stats.endtime > max_end:
max_end = trace.stats.endtime
self.cuttimes = [min_start, max_end]
def exportEvent(self, fnout=None, evtformat='QUAKEML'):
from pylot.core.util.defaults import OUTPUTFORMATS
if evtformat.strip() not in OUTPUTFORMATS.values():
errmsg = 'selected format {0} not available'.format(evtformat)
raise FormatError(errmsg)
if fnout is None:
ID = self.getID()
# handle forbidden filenames especially on windows systems
fnout = fnConstructor(ID)
else:
fnout = fnConstructor(fnout)
evtformat = evtformat.upper().strip()
# establish catalog object (event object has no write method)
cat = Catalog()
cat.append(self.getEvtData())
# try exporting event via ObsPy
try:
cat.write(fnout + evtformat.lower(), format=evtformat)
except KeyError, e:
raise KeyError('''{0} export format
not implemented: {1}'''.format(evtformat, e))
def plotData(self, widget):
wfst = self.getWFData().select(component=self.getComp())
for n, trace in enumerate(wfst):
stime = trace.stats.starttime - self.getCutTimes()[0]
etime = trace.stats.endtime - self.getCutTimes()[1]
srate = trace.stats.sampling_rate
nsamp = len(trace.data)
tincr = trace.stats.delta
time_ax = np.arange(stime, nsamp / srate, tincr)
trace.normalize()
widget.axes.plot(time_ax, trace.data + n, 'k')
xlabel = 'seconds since {0}'.format(self.getCutTimes()[0])
ylabel = ''
zne_text = {'Z': 'vertical', 'N': 'north-south', 'E': 'east-west'}
title = 'overview: {0} components'.format(zne_text[self.getComp()])
widget.updateWidget(xlabel, ylabel, title)
def getComp(self):
return self.comp
def getID(self):
try:
return self.evtdata.get('resource_id').id
except:
return 'smi:bug/pylot/1234'
def filter(self, kwargs):
self.getWFData().filter(**kwargs)
self.dirty = True
def setWFData(self, fnames):
self.wfdata = Stream()
self.wforiginal = None
if fnames is not None:
self.appendWFData(fnames)
self.orig = self.getWFData().copy()
self.dirty = False
def appendWFData(self, fnames):
if self.dirty is not False:
self.resetWFData()
for fname in fnames:
try:
self.wfdata += read(fname)
except TypeError:
self.wfdata += read(fname, format='GSE2')
def getWFData(self):
return self.wfdata
def getOriginalWFData(self):
return self.wforiginal
def resetWFData(self):
self.wfdata = self.getOriginalWFData().copy()
self.dirty = False
def getEvtData(self):
return self.evtdata
class GenericDataStructure(object):
'''
GenericDataBase type holds all information about the current data-
base working on.
'''
def __init__(self, structexp='$R/$D/$E', folderdepth=2, **kwargs):
structureOptions = ('$R', '$D', '$E')
structExpression = []
depth = 0
while structexp is not os.path.sep:
try:
[structexp, tlexp] = os.path.split(structexp)
except AttributeError:
rootExpression = None
structExpression = None
break
structExpression.append(tlexp)
depth += 1
if depth is folderdepth:
rootExpression = structexp
break
structExpression.reverse()
self.folderDepth = folderdepth
self.__gdsFields = {'ROOT': rootExpression}
self.modifyFields(**kwargs)
def modifyFields(self, **kwargs):
for key, value in kwargs.iteritems():
key = str(key).upper()
self.__gdsFields[key] = value
def getFields(self):
return self.__gdsFields
def expandDataPath(self):
return os.path.join(*self.getFields().values())
class PilotDataStructure(object):
'''
Object containing the data access information for the old PILOT data
structure.
'''
def __init__(self, dataformat='GSE2', fsuffix='gse',
root='/data/Egelados/EVENT_DATA/LOCAL/', database='2006.01',
**kwargs):
self.dataType = dataformat
self.__pdsFields = {'ROOT': root,
'DATABASE': database,
'SUFFIX': fsuffix
}
self.modifiyFields(**kwargs)
def modifiyFields(self, **kwargs):
if kwargs and isinstance(kwargs, dict):
for key, value in kwargs.iteritems():
key = str(key)
if type(value) not in (str, int, float):
for n, val in enumerate(value):
value[n] = str(val)
else:
value = str(value)
try:
if key in self.getFields().keys():
self.getFields()[key] = value
else:
raise KeyError('unknown PDS wildcard: %s.' % key)
except KeyError, e:
errmsg = ''
errmsg += 'WARNING:\n'
errmsg += 'unable to set values for PDS fields\n'
errmsg += '%s; desired value was: %s\n' % (e, value)
print errmsg
def getType(self):
return self.dataType
def getFields(self):
return self.__pdsFields
def expandDataPath(self):
datapath = os.path.join(self.getFields()['ROOT'],
self.getFields()['DATABASE'])
return datapath
class SeiscompDataStructure(object):
'''
Dictionary containing the data access information for an SDS data archive:
:param str dataType: Desired data type. Default: ``'waveform'``
:param sdate, edate: Either date string or an instance of
:class:`obspy.core.utcdatetime.UTCDateTime. Default: ``None``
:type sdate, edate: str or UTCDateTime or None
'''
# Data type options
__typeOptions = {'waveform': 'D', # Waveform data
'detect': 'E', # Detection data
'log': 'L', # Log data
'timing': 'T', # Timing data
'calib': 'C', # Calibration data
'resp': 'R', # Response data
'opaque': 'O' # Opaque data
}
def __init__(self, dataType='waveform', sdate=None, edate=None, **kwargs):
# imports
from obspy.core import UTCDateTime
def checkDate(date):
if not isinstance(date, UTCDateTime):
return True
return False
try:
if checkDate(sdate):
sdate = UTCDateTime(sdate)
if checkDate(edate):
edate = UTCDateTime(edate)
except TypeError:
edate = UTCDateTime()
halfyear = UTCDateTime('1970-07-01')
sdate = UTCDateTime(edate - halfyear)
del halfyear
year = ''
if not edate.year == sdate.year:
nyears = edate.year - sdate.year
for yr in range(nyears):
year += '{0:04d},'.format(sdate.year + yr)
year = '{' + year[:-1] + '}'
else:
year = '{0:04d}'.format(sdate.year)
if dataType in self.__typeOptions.keys():
self.dataType = dataType
else:
self.dataType = 'waveform' # default value for dataType
print '''Warning: Selected datatype ('%s') not available.\n
Using 'waveform' instead!'''.format(dataType)
# SDS fields' default values
# definitions from
# http://www.seiscomp3.org/wiki/doc/applications/slarchive/SDS
self.__sdsFields = {'SDSdir': '/data/SDS', # base directory
'YEAR': year, # 4 digits
'NET': '??', # up to 8 characters
'STA': '????', # up to 8 characters
'CHAN': 'HH?', # up to 8 characters
'TYPE': self.getType(), # 1 character
'LOC': '', # up to 8 characters
'DAY': '{0:03d}'.format(sdate.julday) # 3 digits
}
self.modifiyFields(**kwargs)
def modifiyFields(self, **kwargs):
if kwargs and isinstance(kwargs, dict):
for key, value in kwargs.iteritems():
key = str(key)
if type(value) not in (str, int, float):
for n, val in enumerate(value):
value[n] = str(val)
else:
value = str(value)
try:
if key in self.getFields().keys():
self.getFields()[key] = value
else:
raise KeyError('unknown SDS wildcard: %s.' % key)
except KeyError, e:
errmsg = ''
errmsg += 'WARNING:\n'
errmsg += 'unable to set values for SDS fields\n'
errmsg += '%s; desired value was: %s\n' % (e, value)
print errmsg
def getType(self):
return self.__typeOptions[self.dataType]
def getFields(self):
return self.__sdsFields
def expandDataPath(self):
fullChan = '{0}.{1}'.format(self.getFields()['CHAN'], self.getType())
dataPath = os.path.join(self.getFields()['SDSdir'],
self.getFields()['YEAR'],
self.getFields()['NET'],
self.getFields()['STA'],
fullChan,
'*{0}'.format(self.getFields()['DAY'])
)
return dataPath