diff --git a/QtPyLoT.py b/QtPyLoT.py index cdf88629..3d34ac8d 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -23,7 +23,7 @@ https://www.iconfinder.com/iconsets/flavour (http://www.gnu.org/copyleft/lesser.html) """ -import sys +import os, sys import matplotlib matplotlib.use('Qt4Agg') @@ -64,7 +64,6 @@ class MainWindow(QMainWindow): super(MainWindow, self).__init__(parent) self.createAction = createAction - self.dirty = False settings = QSettings() if settings.value("user/FullName", None) is None: fulluser = QInputDialog.getText(self, "Enter Name:", "Full name") @@ -102,8 +101,8 @@ class MainWindow(QMainWindow): self.data = Data(self) # load and display waveform data - self.loadWaveformData() self.dirty = False + self.loadWaveformData() self.loadData() self.updateFilterOptions() @@ -333,6 +332,10 @@ class MainWindow(QMainWindow): self.fileMenu.addSeparator() self.fileMenu.addAction(self.fileMenuActions[-1]) + def getRoot(self): + settings = QSettings() + return settings.value("data/dataRoot") + def loadData(self, fname=None): if fname is None: try: @@ -390,18 +393,27 @@ class MainWindow(QMainWindow): else: return + def getEventFileName(self): + return self.getData().getEventFileName() + def saveData(self): settings = QSettings() exform = settings.value('data/exportFormat', 'QUAKEML') + self.getData().applyEVTData(self.getPicks()) try: - self.data.exportEvent(self.fname, exform) + self.getData().exportEvent(self.fname, exform) except FormatError: return False except AttributeError, e: print 'warning: {0}'.format(e) - fname = QFileDialog.getSaveFileName(self, 'Save event') - fname = fname[0] - self.getData().exportEvent(fname, exform) + directory = os.path.join(self.getRoot(), self.getEventFileName()) + file_filter = "Seismic observation files (*.cnv *.obs *.xml)" + fname = QFileDialog.getSaveFileName(self, 'Save event data ...', + directory, file_filter) + fbasename, exform = os.path.splitext(fname[0]) + if not fbasename: + return False + self.getData().exportEvent(fbasename, exform) return True def getComponent(self): @@ -455,7 +467,7 @@ class MainWindow(QMainWindow): def loadWaveformData(self): if self.fnames and self.okToContinue(): - self.dirty = True + self.setDirty(True) self.data.setWFData(self.fnames) elif self.fnames is None and self.okToContinue(): self.data.setWFData(self.getWFFnames()) @@ -585,6 +597,7 @@ class MainWindow(QMainWindow): station=station, picks=self.getPicksOnStation(station)) if pickDlg.exec_(): + self.setDirty(True) self.updateStatus('picks accepted ({0})'.format(station)) self.addPicks(station, pickDlg.getPicks()) self.drawPicks(station) @@ -593,6 +606,7 @@ class MainWindow(QMainWindow): def autoPick(self): list = QListWidget() + self.setDirty(True) logDockWidget = QDockWidget("AutoPickLog", self) logDockWidget.setObjectName("LogDockWidget") logDockWidget.setAllowedAreas(Qt.LeftDockWidgetArea) @@ -700,11 +714,14 @@ class MainWindow(QMainWindow): cinfo = createCreationInfo(agency_id=self.agency) event = createEvent(evtpar['origintime'], cinfo) self.data = Data(self, evtdata=event) - self.dirty = True + self.setDirty(True) def draw(self): self.getPlotWidget().draw() + def setDirty(self, value): + self.dirty = value + def closeEvent(self, event): if self.okToContinue(): self.closing.emit() diff --git a/pylot/core/read/data.py b/pylot/core/read/data.py index 2ca981ed..dad3bf56 100644 --- a/pylot/core/read/data.py +++ b/pylot/core/read/data.py @@ -3,9 +3,9 @@ import os -from obspy.core import (read, Stream, UTCDateTime) +from obspy.core import read, Stream, UTCDateTime from obspy import readEvents, read_inventory -from obspy.core.event import (Event, Catalog) +from obspy.core.event import Event, ResourceIdentifier, Pick, WaveformStreamID from pylot.core.read.io import readPILOTEvent from pylot.core.util.utils import fnConstructor, getGlobalTimes @@ -46,6 +46,7 @@ class Data(object): else: # create an empty Event object self.newevent = True self.evtdata = Event() + self.getEvtData().picks = [] self.wforiginal = None self.cuttimes = None self.dirty = False @@ -67,29 +68,25 @@ class Data(object): def updateCutTimes(self): self.cuttimes = getGlobalTimes(self.getWFData()) - def exportEvent(self, fnout=None, evtformat='QUAKEML'): + def getEventFileName(self): + ID = self.getID() + # handle forbidden filenames especially on windows systems + return fnConstructor(str(ID)) + + def exportEvent(self, fnout, fnext='.xml'): from pylot.core.util.defaults import OUTPUTFORMATS - if evtformat.strip() not in OUTPUTFORMATS.values(): - errmsg = 'selected format {0} not available'.format(evtformat) + try: + evtformat = OUTPUTFORMATS[fnext] + except KeyError, e: + errmsg = '{0}; selected file extension {1} not ' \ + 'supported'.format(e, fnext) raise FormatError(errmsg) - if fnout is None: - ID = self.getID() - # handle forbidden filenames especially on windows systems - fnout = fnConstructor(str(ID)) - else: - fnout = fnConstructor(str(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) + self.getEvtData().write(fnout + fnext, format=evtformat) except KeyError, e: raise KeyError('''{0} export format not implemented: {1}'''.format(evtformat, e)) @@ -119,7 +116,7 @@ class Data(object): assert isinstance(fnames, list), "input parameter 'fnames' is " \ "supposed to be of type 'list' " \ "but is actually {0}".format(type( - fnames)) + fnames)) if self.dirty: self.resetWFData() @@ -150,27 +147,57 @@ class Data(object): st = self.getWFData() inv = read_inventory(fninventory) st.attach_response(inv) - pre_filt = (0.005, 0.006, 30.0, 35.0) # set in autoPyLoT.in + pre_filt = (0.005, 0.006, 30.0, 35.0) # set in autoPyLoT.in st.remove_response(output='VEL', pre_filt=pre_filt) def getEvtData(self): return self.evtdata - def applyEVTData(self, data, type='pick'): + def applyEVTData(self, data, type='pick', authority_id='rub'): def applyPicks(picks): - pass + firstonset = None + for station, onsets in picks.items(): + print 'Reading picks on station %s' % station + for label, phase in onsets.items(): + onset = phase['mpp'] + epp = phase['epp'] + lpp = phase['lpp'] + error = phase['spe'] + pick = Pick() + pick.time = onset + pick.time_errors.lower_uncertainty = onset - epp + pick.time_errors.upper_uncertainty = lpp - onset + pick.time_errors.uncertainty = error + pick.phase_hint = label + pick.waveform_id = WaveformStreamID(station_code=station) + self.getEvtData().picks.append(pick) + try: + polarity = phase['fm'] + except KeyError, e: + print 'No polarity information found for %s' % phase + if firstonset is None or firstonset > onset: + firstonset = onset + + if 'smi:local' in self.getID(): + fonset_str = firstonset.strftime('%Y_%m_%d_%H_%M_%S') + ID = ResourceIdentifier('event/' + fonset_str) + ID.convertIDToQuakeMLURI(authority_id=authority_id) + self.getEvtData().resource_id = ID + def applyArrivals(arrivals): pass + def applyEvent(event): pass - applydata = {'pick':applyPicks, - 'arrival':applyArrivals, - 'event':applyEvent} + applydata = {'pick': applyPicks, + 'arrival': applyArrivals, + 'event': applyEvent} applydata[type](data) + class GenericDataStructure(object): ''' GenericDataBase type holds all information about the current data- @@ -280,14 +307,13 @@ class PilotDataStructure(GenericDataStructure): ''' def __init__(self, **fields): - if not fields: - fields = {'database':'2006.01', - 'root':'/data/Egelados/EVENT_DATA/LOCAL'} + fields = {'database': '2006.01', + 'root': '/data/Egelados/EVENT_DATA/LOCAL'} GenericDataStructure.__init__(self, **fields) - self.setExpandFields(['root','database']) + self.setExpandFields(['root', 'database']) class SeiscompDataStructure(GenericDataStructure): @@ -304,7 +330,6 @@ class SeiscompDataStructure(GenericDataStructure): filesuffix=None, **kwargs): super(GenericDataStructure, self).__init__() - edate = UTCDateTime() halfyear = UTCDateTime('1970-07-01') sdate = UTCDateTime(edate - halfyear) @@ -324,9 +349,9 @@ class SeiscompDataStructure(GenericDataStructure): # http://www.seiscomp3.org/wiki/doc/applications/slarchive/SDS self.dsFields = {'root': '/data/SDS', 'YEAR': year, 'NET': '??', - 'STA': '????', 'CHAN': 'HH?', 'TYPE': 'D', 'LOC': '', - 'DAY': '{0:03d}'.format(sdate.julday) - } + 'STA': '????', 'CHAN': 'HH?', 'TYPE': 'D', 'LOC': '', + 'DAY': '{0:03d}'.format(sdate.julday) + } self.modifiyFields(**kwargs) def modifiyFields(self, **kwargs): diff --git a/pylot/core/util/defaults.py b/pylot/core/util/defaults.py index 1fde1c3a..b2bf2a6d 100644 --- a/pylot/core/util/defaults.py +++ b/pylot/core/util/defaults.py @@ -13,6 +13,6 @@ FILTERDEFAULTS = {'P': {'filtertype': None, 'order': 4, 'freq': [.5, 5]}} -OUTPUTFORMATS = {'QuakeML':'QUAKEML', - 'VelEst':'CNV', - 'NonLinLoc':'NLLOC_OBS'} +OUTPUTFORMATS = {'.xml':'QUAKEML', + '.cnv':'CNV', + '.obs':'NLLOC_OBS'}