diff --git a/QtPyLoT.py b/QtPyLoT.py index aa6aacec..4b5f869d 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -64,7 +64,7 @@ from pylot.core.io.data import Data from pylot.core.io.inputs import FilterOptions, PylotParameter from autoPyLoT import autoPyLoT from pylot.core.pick.compare import Comparison -from pylot.core.pick.utils import symmetrize_error, getQualityfromUncertainty +from pylot.core.pick.utils import symmetrize_error, getQualityFromUncertainty from pylot.core.io.phases import picksdict_from_picks import pylot.core.loc.nll as nll from pylot.core.util.defaults import FILTERDEFAULTS, SetChannelComponents @@ -74,12 +74,12 @@ from pylot.core.util.connection import checkurl from pylot.core.util.dataprocessing import read_metadata, restitute_data from pylot.core.util.utils import fnConstructor, getLogin, \ full_range, readFilterInformation, trim_station_components, check4gaps, make_pen, pick_color_plt, \ - pick_linestyle_plt, identifyPhase, loopIdentifyPhase, remove_underscores, check4doubled, check4rotated + pick_linestyle_plt, remove_underscores, check4doubled, identifyPhaseID, excludeQualityClasses, has_spe from pylot.core.util.event import Event from pylot.core.io.location import create_creation_info, create_event from pylot.core.util.widgets import FilterOptionsDialog, NewEventDlg, \ WaveformWidget, WaveformWidgetPG, PropertiesDlg, HelpForm, createAction, PickDlg, \ - getDataType, ComparisonDialog, TuneAutopicker, PylotParaBox, AutoPickDlg + getDataType, ComparisonWidget, TuneAutopicker, PylotParaBox, AutoPickDlg, CanvasWidget, AutoPickWidget from pylot.core.util.map_projection import map_projection from pylot.core.util.structure import DATASTRUCTURE from pylot.core.util.thread import Thread, Worker @@ -865,7 +865,7 @@ class MainWindow(QMainWindow): return fnames def getPhaseID(self, phase): - return identifyPhase(loopIdentifyPhase(phase)) + return identifyPhaseID(phase) def get_current_event(self, eventbox=None): ''' @@ -1025,6 +1025,11 @@ class MainWindow(QMainWindow): :param: select_events, can be 'all', 'ref' :type: str ''' + + # if pick widget is open, refresh tooltips as well + if hasattr(self, 'apw'): + self.apw.refresh_tooltips() + if not eventBox: eventBox = self.eventBox index = eventBox.currentIndex() @@ -1048,12 +1053,23 @@ class MainWindow(QMainWindow): for id, event in enumerate(self.project.eventlist): event_path = event.path - event_npicks = 0 - event_nautopicks = 0 - if event.pylot_picks: - event_npicks = len(event.pylot_picks) - if event.pylot_autopicks: - event_nautopicks = len(event.pylot_autopicks) + phaseErrors = {'P': self._inputs['timeerrorsP'], + 'S': self._inputs['timeerrorsS']} + + ma_props = {'manual': event.pylot_picks, + 'auto': event.pylot_autopicks} + ma_count = {'manual': 0, + 'auto': 0} + + for ma in ma_props.keys(): + if ma_props[ma]: + for picks in ma_props[ma].values(): + for phasename, pick in picks.items(): + if not type(pick) in [dict, AttribDict]: + continue + if getQualityFromUncertainty(has_spe(pick), phaseErrors[self.getPhaseID(phasename)]) < 4: + ma_count[ma] += 1 + event_ref = event.isRefEvent() event_test = event.isTestEvent() @@ -1064,9 +1080,9 @@ class MainWindow(QMainWindow): # a=event_nautopicks) item_path = QtGui.QStandardItem('{path:{plen}}'.format(path=event_path, plen=plmax)) - item_nmp = QtGui.QStandardItem(str(event_npicks)) + item_nmp = QtGui.QStandardItem(str(ma_count['manual'])) item_nmp.setIcon(self.manupicksicon_small) - item_nap = QtGui.QStandardItem(str(event_nautopicks)) + item_nap = QtGui.QStandardItem(str(ma_count['auto'])) item_nap.setIcon(self.autopicksicon_small) item_ref = QtGui.QStandardItem() # str(event_ref)) item_test = QtGui.QStandardItem() # str(event_test)) @@ -1215,9 +1231,13 @@ class MainWindow(QMainWindow): def comparePicks(self): if self.check4Comparison(): - co = Comparison(auto=self.getPicks('auto'), manu=self.getPicks()) - compare_dlg = ComparisonDialog(co, self) - compare_dlg.exec_() + autopicks = excludeQualityClasses(self.getPicks('auto'), [4], + self._inputs['timeerrorsP'], self._inputs['timeerrorsS']) + manupicks = excludeQualityClasses(self.getPicks('manual'), [4], + self._inputs['timeerrorsP'], self._inputs['timeerrorsS']) + co = Comparison(auto=autopicks, manu=manupicks) + compare_dlg = ComparisonWidget(co, self) + compare_dlg.show() def getPlotWidget(self): return self.dataPlot @@ -1813,16 +1833,8 @@ class MainWindow(QMainWindow): self.listWidget.addItem(text) self.listWidget.scrollToBottom() - def tune_autopicker(self): - ''' - Initiates TuneAutopicker widget use to interactively - tune parameters for autopicking algorithm. - ''' - # figures and canvas have to be iniated from the main GUI - # thread to prevent handling of QPixmap objects outside of - # the main thread + def init_fig_dict(self): self.fig_dict = {} - self.canvas_dict = {} self.fig_keys = [ 'mainFig', 'aicFig', @@ -1834,12 +1846,45 @@ class MainWindow(QMainWindow): 'el_S1pick', 'el_S2pick', 'refSpick', - 'aicARHfig', + 'aicARHfig' ] for key in self.fig_keys: fig = Figure() self.fig_dict[key] = fig + def init_canvas_dict(self): + self.canvas_dict = {} + for key in self.fig_keys: + self.canvas_dict[key] = FigureCanvas(self.fig_dict[key]) + + def init_fig_dict_wadatijack(self, eventIDs): + self.fig_dict_wadatijack = {} + self.fig_keys_wadatijack = [ + 'jackknife', + 'wadati' + ] + for eventID in eventIDs: + self.fig_dict_wadatijack[eventID] = {} + for key in self.fig_keys_wadatijack: + fig = Figure() + self.fig_dict_wadatijack[eventID][key] = fig + + def init_canvas_dict_wadatijack(self): + self.canvas_dict_wadatijack = {} + for eventID in self.fig_dict_wadatijack.keys(): + self.canvas_dict_wadatijack[eventID] = {} + for key in self.fig_keys_wadatijack: + self.canvas_dict_wadatijack[eventID][key] = FigureCanvas(self.fig_dict_wadatijack[eventID][key]) + + def tune_autopicker(self): + ''' + Initiates TuneAutopicker widget use to interactively + tune parameters for autopicking algorithm. + ''' + # figures and canvas have to be iniated from the main GUI + # thread to prevent handling of QPixmap objects outside of + # the main thread + self.init_fig_dict() #if not self.tap: # init TuneAutopicker object self.tap = TuneAutopicker(self) @@ -1858,8 +1903,7 @@ class MainWindow(QMainWindow): ''' Create and fill TuneAutopicker tabs with figure canvas. ''' - for key in self.fig_keys: - self.canvas_dict[key] = FigureCanvas(self.fig_dict[key]) + self.init_canvas_dict() self.tap.fill_tabs(picked=True) def autoPick(self): @@ -1868,36 +1912,79 @@ class MainWindow(QMainWindow): QMessageBox.warning(self, "PyLoT Warning", "No autoPyLoT output declared!") return - event = self.get_current_event() - self.saveData(event, event.path, outformats=['.xml']) + + self.pickoptions =[('current event', self.get_current_event), + ('tune events', self.get_ref_events), + ('test events', self.get_test_events), + ('all (picked) events', self.get_manu_picked_events), + ('all events', self.get_all_events)] + self.listWidget = QListWidget() self.setDirty(True) - self.logDockWidget = QDockWidget("AutoPickLog", self) - self.logDockWidget.setObjectName("LogDockWidget") - self.logDockWidget.setAllowedAreas( - Qt.LeftDockWidgetArea | Qt.RightDockWidgetArea) - self.logDockWidget.setWidget(self.listWidget) - self.addDockWidget(Qt.LeftDockWidgetArea, self.logDockWidget) + self.apw = AutoPickWidget(self, self.pickoptions) + self.apw.insert_log_widget(self.listWidget) + self.apw.refresh_tooltips() + + # self.logDockWidget = QDockWidget("AutoPickLog", self) + # self.logDockWidget.setObjectName("LogDockWidget") + # self.logDockWidget.setAllowedAreas( + # Qt.LeftDockWidgetArea | Qt.RightDockWidgetArea) + # self.logDockWidget.setWidget(self.listWidget) + # self.addDockWidget(Qt.LeftDockWidgetArea, self.logDockWidget) # self.addListItem('Loading default values from PyLoT-input file %s' # % self.infile) + self.apw.start.connect(self.start_autopick) + self.apw.show() + + def start_autopick(self): + for key, func in self.pickoptions: + if self.apw.rb_dict[key].isChecked(): + # if radio button is checked break for loop and use func + break + + events = func() + if not type(events) == list: + events = [events] + eventPaths = self.get_event_paths(events) + eventIDs = self.get_event_ids(events) + + self.init_fig_dict_wadatijack(eventIDs) + + if not eventPaths: + self.addListItem("No events found for '{}'".format(key)) + return + else: + self.addListItem("Picking the following events ({}):".format(key)) + for eventID in eventPaths: + self.addListItem(str(eventID)) + + self.apw.enable(False) + + # export current picks etc. + self.exportAllEvents(['.xml']) + + # define arguments for picker args = {'parameter': self._inputs, 'station': 'all', 'fnames': 'None', - 'eventid': self.get_current_event_path(), + 'eventid': eventPaths, 'iplot': 0, 'fig_dict': None, + 'fig_dict_wadatijack': self.fig_dict_wadatijack, 'locflag': 0} + # init pick thread self.mp_thread = QtCore.QThreadPool() self.mp_worker = Worker(autoPyLoT, args, redirect_stdout=True) - self.mp_thread.start(self.mp_worker) self.addListItem(str(self._inputs)) self.mp_worker.signals.message.connect(self.addListItem) self.mp_worker.signals.result.connect(self.finalizeAutoPick) + self.mp_thread.start(self.mp_worker) + def autoPickProject(self): if not self.apd_local: self.apd_local = AutoPickDlg(self, sge=False) @@ -1909,12 +1996,63 @@ class MainWindow(QMainWindow): self.apd_sge.show() def finalizeAutoPick(self, result): + self.apw.enable(True) if result: - event = self.get_current_event() - event.addAutopicks(result) + self.init_canvas_dict_wadatijack() + for eventID in result.keys(): + event = self.get_event_from_id(eventID) + if not event: + continue + event.addAutopicks(result[eventID]) + jkw = CanvasWidget(self, self.canvas_dict_wadatijack[eventID]['jackknife']) + wdw = CanvasWidget(self, self.canvas_dict_wadatijack[eventID]['wadati']) + self.apw.add_plot_widget(jkw, 'Jackknife', eventID) + self.apw.add_plot_widget(wdw, 'Wadati', eventID) + self.apw.update_plots() self.drawPicks(picktype='auto') self.draw() + def get_event_from_id(self, eventID): + for event in self.project.eventlist: + if event.pylot_id == eventID: + return event + + def get_event_paths(self, eventlist): + eventPaths = [] + for event in eventlist: + eventPaths.append(event.path) + return eventPaths + + def get_event_ids(self, eventlist): + eventIDs = [] + for event in eventlist: + eventIDs.append(event.pylot_id) + return eventIDs + + def get_all_events(self): + return self.project.eventlist + + def get_ref_events(self): + events = [] + for event in self.project.eventlist: + if event.isRefEvent(): + events.append(event) + return events + + def get_test_events(self): + events = [] + for event in self.project.eventlist: + if event.isTestEvent(): + events.append(event) + return events + + def get_manu_picked_events(self): + events = [] + for event in self.project.eventlist: + if len(event.pylot_picks) > 0: + events.append(event) + return events + def addPicks(self, station, picks, type='manual'): stat_picks = self.getPicksOnStation(station, type) if not stat_picks: @@ -1994,16 +2132,17 @@ class MainWindow(QMainWindow): stime = self.getStime() for phase in stat_picks: + if phase == 'SPt': continue # wadati SP time picks = stat_picks[phase] if type(stat_picks[phase]) is not dict and type(stat_picks[phase]) is not AttribDict: return # get quality classes if self.getPhaseID(phase) == 'P': - quality = getQualityfromUncertainty(picks['spe'], self._inputs['timeerrorsP']) + quality = getQualityFromUncertainty(picks['spe'], self._inputs['timeerrorsP']) phaseID = 'P' elif self.getPhaseID(phase) == 'S': - quality = getQualityfromUncertainty(picks['spe'], self._inputs['timeerrorsS']) + quality = getQualityFromUncertainty(picks['spe'], self._inputs['timeerrorsS']) phaseID = 'S' mpp = picks['mpp'] - stime @@ -2326,12 +2465,22 @@ class MainWindow(QMainWindow): # iterate through eventlist and generate items for table rows self.project._table = [] for index, event in enumerate(eventlist): - event_npicks = 0 - event_nautopicks = 0 - if event.pylot_picks: - event_npicks = len(event.pylot_picks) - if event.pylot_autopicks: - event_nautopicks = len(event.pylot_autopicks) + phaseErrors = {'P': self._inputs['timeerrorsP'], + 'S': self._inputs['timeerrorsS']} + + ma_props = {'manual': event.pylot_picks, + 'auto': event.pylot_autopicks} + ma_count = {'manual': 0, + 'auto': 0} + + for ma in ma_props.keys(): + if ma_props[ma]: + for picks in ma_props[ma].values(): + for phasename, pick in picks.items(): + if not type(pick) in [dict, AttribDict]: + continue + if getQualityFromUncertainty(has_spe(pick), phaseErrors[self.getPhaseID(phasename)]) < 4: + ma_count[ma] += 1 # init table items for current row item_delete = QtGui.QTableWidgetItem() @@ -2342,9 +2491,9 @@ class MainWindow(QMainWindow): item_lon = QtGui.QTableWidgetItem() item_depth = QtGui.QTableWidgetItem() item_mag = QtGui.QTableWidgetItem() - item_nmp = QtGui.QTableWidgetItem(str(event_npicks)) + item_nmp = QtGui.QTableWidgetItem(str(ma_count['manual'])) item_nmp.setIcon(self.manupicksicon_small) - item_nap = QtGui.QTableWidgetItem(str(event_nautopicks)) + item_nap = QtGui.QTableWidgetItem(str(ma_count['auto'])) item_nap.setIcon(self.autopicksicon_small) item_ref = QtGui.QTableWidgetItem() item_test = QtGui.QTableWidgetItem() diff --git a/autoPyLoT.py b/autoPyLoT.py index 5940d077..44b2bd4e 100755 --- a/autoPyLoT.py +++ b/autoPyLoT.py @@ -78,12 +78,17 @@ def autoPyLoT(input_dict=None, parameter=None, inputfile=None, fnames=None, even inputfile = real_None(inputfile) eventid = real_None(eventid) + fig_dict = None + fig_dict_wadatijack = None + locflag = 1 if input_dict and isinstance(input_dict, dict): if 'parameter' in input_dict: parameter = input_dict['parameter'] if 'fig_dict' in input_dict: fig_dict = input_dict['fig_dict'] + if 'fig_dict_wadatijack' in input_dict: + fig_dict_wadatijack = input_dict['fig_dict_wadatijack'] if 'station' in input_dict: station = input_dict['station'] if 'fnames' in input_dict: @@ -179,13 +184,14 @@ def autoPyLoT(input_dict=None, parameter=None, inputfile=None, fnames=None, even evID = os.path.split(eventid)[-1] locflag = 2 else: - # started in tune mode + # started in tune or interactive mode datapath = os.path.join(parameter['rootpath'], parameter['datapath']) events = [] - events.append(os.path.join(datapath, - parameter['database'], - eventid)) + for eventID in eventid: + events.append(os.path.join(datapath, + parameter['database'], + eventID)) if not events: print('autoPyLoT: No events given. Return!') @@ -196,6 +202,7 @@ def autoPyLoT(input_dict=None, parameter=None, inputfile=None, fnames=None, even eventpath = eventpath.replace(SEPARATOR, '/') events[index] = eventpath + allpicks = {} glocflag = locflag for eventpath in events: evID = os.path.split(eventpath)[-1] @@ -260,14 +267,12 @@ def autoPyLoT(input_dict=None, parameter=None, inputfile=None, fnames=None, even print(wfdat) ########################################################## # !automated picking starts here! - if input_dict: - if 'fig_dict' in input_dict: - fig_dict = input_dict['fig_dict'] - picks = autopickevent(wfdat, parameter, iplot=iplot, fig_dict=fig_dict, - ncores=ncores, metadata=metadata, origin=data.get_evt_data().origins) - else: - picks = autopickevent(wfdat, parameter, iplot=iplot, - ncores=ncores, metadata=metadata, origin=data.get_evt_data().origins) + fdwj = None + if fig_dict_wadatijack: + fdwj = fig_dict_wadatijack[evID] + picks = autopickevent(wfdat, parameter, iplot=iplot, fig_dict=fig_dict, + fig_dict_wadatijack=fdwj, + ncores=ncores, metadata=metadata, origin=data.get_evt_data().origins) ########################################################## # locating if locflag > 0: @@ -383,7 +388,8 @@ def autoPyLoT(input_dict=None, parameter=None, inputfile=None, fnames=None, even iplot) # update pick with moment property values (w0, fc, Mo) for stats, props in moment_mag.moment_props.items(): - picks[stats]['P'].update(props) + if picks.has_key(stats): + picks[stats]['P'].update(props) evt = moment_mag.updated_event() net_mw = moment_mag.net_magnitude() print("Network moment magnitude: %4.1f" % net_mw.mag) @@ -394,7 +400,8 @@ def autoPyLoT(input_dict=None, parameter=None, inputfile=None, fnames=None, even parameter.get('sstop'), WAscaling, True, iplot) for stats, amplitude in local_mag.amplitudes.items(): - picks[stats]['S']['Ao'] = amplitude.generic_amplitude + if picks.has_key(stats): + picks[stats]['S']['Ao'] = amplitude.generic_amplitude print("Local station magnitudes scaled with:") print("log(Ao) + %f * log(r) + %f * r + %f" % (WAscaling[0], WAscaling[1], @@ -452,13 +459,16 @@ def autoPyLoT(input_dict=None, parameter=None, inputfile=None, fnames=None, even if locflag == 0: print("autoPyLoT was running in non-location mode!") + # save picks for current event ID to dictionary with ALL picks + allpicks[evID] = picks + endsp = '''####################################\n ************************************\n *********autoPyLoT terminates*******\n The Python picking and Location Tool\n ************************************'''.format(version=_getVersionString()) print(endsp) - return picks + return allpicks if __name__ == "__main__": diff --git a/pylot/core/io/phases.py b/pylot/core/io/phases.py index c54c120a..fd89df4c 100644 --- a/pylot/core/io/phases.py +++ b/pylot/core/io/phases.py @@ -879,6 +879,9 @@ def getQualitiesfromxml(xmlnames, ErrorsP, ErrorsS, plotflag=1): Ludger Küperkoch, BESTEC GmbH, 07/2017 """ + from pylot.core.pick.utils import getQualityFromUncertainty + from pylot.core.util.utils import loopIdentifyPhase, identifyPhase + # read all onset weights Pw0 = [] Pw1 = [] @@ -902,14 +905,15 @@ def getQualitiesfromxml(xmlnames, ErrorsP, ErrorsS, plotflag=1): mstation = Pick.waveform_id.station_code mstation_ext = mstation + '_' for mpick in arrivals_copy: - if mpick.phase_hint[0] == 'P': + phase = identifyPhase(loopIdentifyPhase(Pick.phase_hint)) + if phase == 'P': if ((mpick.waveform_id.station_code == mstation) or \ (mpick.waveform_id.station_code == mstation_ext)) and \ ((mpick.method_id).split('/')[1] == 'auto') and \ (mpick.time_errors['uncertainty'] <= ErrorsP[3]): del mpick break - elif mpick.phase_hint[0] == 'S': + elif phase == 'S': if ((mpick.waveform_id.station_code == mstation) or \ (mpick.waveform_id.station_code == mstation_ext)) and \ ((mpick.method_id).split('/')[1] == 'auto') and \ @@ -921,38 +925,31 @@ def getQualitiesfromxml(xmlnames, ErrorsP, ErrorsS, plotflag=1): print("Found manual as well as automatic picks, prefered the {} manual ones!".format(lendiff)) for Pick in arrivals_copy: - if Pick.phase_hint[0] == 'P': - if Pick.time_errors.uncertainty <= ErrorsP[0]: + phase = identifyPhase(loopIdentifyPhase(Pick.phase_hint)) + if phase == 'P': + Pqual = getQualityFromUncertainty(Pick.time_errors.uncertainty, ErrorsP) + if Pqual == 0: Pw0.append(Pick.time_errors.uncertainty) - elif (Pick.time_errors.uncertainty > ErrorsP[0]) and \ - (Pick.time_errors.uncertainty <= ErrorsP[1]): + elif Pqual == 1: Pw1.append(Pick.time_errors.uncertainty) - elif (Pick.time_errors.uncertainty > ErrorsP[1]) and \ - (Pick.time_errors.uncertainty <= ErrorsP[2]): + elif Pqual == 2: Pw2.append(Pick.time_errors.uncertainty) - elif (Pick.time_errors.uncertainty > ErrorsP[2]) and \ - (Pick.time_errors.uncertainty <= ErrorsP[3]): + elif Pqual == 3: Pw3.append(Pick.time_errors.uncertainty) - elif Pick.time_errors.uncertainty > ErrorsP[3]: + elif Pqual == 4: Pw4.append(Pick.time_errors.uncertainty) - else: - pass - elif Pick.phase_hint[0] == 'S': - if Pick.time_errors.uncertainty <= ErrorsS[0]: + elif phase == 'S': + Squal = getQualityFromUncertainty(Pick.time_errors.uncertainty, ErrorsS) + if Squal == 0: Sw0.append(Pick.time_errors.uncertainty) - elif (Pick.time_errors.uncertainty > ErrorsS[0]) and \ - (Pick.time_errors.uncertainty <= ErrorsS[1]): + elif Squal == 1: Sw1.append(Pick.time_errors.uncertainty) - elif (Pick.time_errors.uncertainty > ErrorsS[1]) and \ - (Pick.time_errors.uncertainty <= ErrorsS[2]): + elif Squal == 2: Sw2.append(Pick.time_errors.uncertainty) - elif (Pick.time_errors.uncertainty > ErrorsS[2]) and \ - (Pick.time_errors.uncertainty <= ErrorsS[3]): + elif Squal == 3: Sw3.append(Pick.time_errors.uncertainty) - elif Pick.time_errors.uncertainty > ErrorsS[3]: + elif Squal == 4: Sw4.append(Pick.time_errors.uncertainty) - else: - pass else: print("Phase hint not defined for picking!") pass @@ -965,45 +962,45 @@ def getQualitiesfromxml(xmlnames, ErrorsP, ErrorsS, plotflag=1): # get percentage of weights numPweights = np.sum([len(Pw0), len(Pw1), len(Pw2), len(Pw3), len(Pw4)]) numSweights = np.sum([len(Sw0), len(Sw1), len(Sw2), len(Sw3), len(Sw4)]) - try: + if len(Pw0) > 0: P0perc = 100 / numPweights * len(Pw0) - except: + else: P0perc = 0 - try: + if len(Pw1) > 0: P1perc = 100 / numPweights * len(Pw1) - except: + else: P1perc = 0 - try: + if len(Pw2) > 0: P2perc = 100 / numPweights * len(Pw2) - except: + else: P2perc = 0 - try: + if len(Pw3) > 0: P3perc = 100 / numPweights * len(Pw3) - except: + else: P3perc = 0 - try: + if len(Pw4) > 0: P4perc = 100 / numPweights * len(Pw4) - except: + else: P4perc = 0 - try: + if len(Sw0) > 0: S0perc = 100 / numSweights * len(Sw0) - except: + else: S0perc = 0 - try: + if len(Sw1) > 0: S1perc = 100 / numSweights * len(Sw1) - except: + else: S1perc = 0 - try: + if len(Sw2) > 0: S2perc = 100 / numSweights * len(Sw2) - except: + else: S2perc = 0 - try: + if len(Sw3) > 0: S3perc = 100 / numSweights * len(Sw3) - except: + else: S3perc = 0 - try: + if len(Sw4) > 0: S4perc = 100 / numSweights * len(Sw4) - except: + else: S4perc = 0 weights = ('0', '1', '2', '3', '4') diff --git a/pylot/core/pick/autopick.py b/pylot/core/pick/autopick.py index e69d11c5..0b60f4f7 100644 --- a/pylot/core/pick/autopick.py +++ b/pylot/core/pick/autopick.py @@ -18,13 +18,13 @@ from pylot.core.pick.charfuns import HOScf, AICcf, ARZcf, ARHcf, AR3Ccf from pylot.core.pick.picker import AICPicker, PragPicker from pylot.core.pick.utils import checksignallength, checkZ4S, earllatepicker, \ getSNR, fmpicker, checkPonsets, wadaticheck -from pylot.core.util.utils import getPatternLine, gen_Pool, identifyPhase, loopIdentifyPhase, \ - real_Bool +from pylot.core.util.utils import getPatternLine, gen_Pool,\ + real_Bool, identifyPhaseID from obspy.taup import TauPyModel -def autopickevent(data, param, iplot=0, fig_dict=None, ncores=0, metadata=None, origin=None): +def autopickevent(data, param, iplot=0, fig_dict=None, fig_dict_wadatijack=None, ncores=0, metadata=None, origin=None): stations = [] all_onsets = {} input_tuples = [] @@ -76,14 +76,15 @@ def autopickevent(data, param, iplot=0, fig_dict=None, ncores=0, metadata=None, pick.pop('station') all_onsets[station] = pick - all_onsets = checkPonsets(all_onsets, mdttolerance, iplot) - return all_onsets + #return all_onsets # quality control # median check and jackknife on P-onset times - jk_checked_onsets = checkPonsets(all_onsets, mdttolerance, iplot) + jk_checked_onsets = checkPonsets(all_onsets, mdttolerance, 1, fig_dict_wadatijack) + #return jk_checked_onsets # check S-P times (Wadati) - return wadaticheck(jk_checked_onsets, wdttolerance, iplot) + wadationsets = wadaticheck(jk_checked_onsets, wdttolerance, 1, fig_dict_wadatijack) + return wadationsets def call_autopickstation(input_tuple): @@ -255,7 +256,7 @@ def autopickstation(wfstream, pickparam, verbose=False, phases = {'P': [], 'S': []} for arr in arrivals: - phases[identifyPhase(loopIdentifyPhase(arr.phase.name))].append(arr) + phases[identifyPhaseID(arr.phase.name)].append(arr) # get first P and S onsets from arrivals list arrP, estFirstP = min([(arr, arr.time) for arr in phases['P']], key = lambda t: t[1]) @@ -984,9 +985,9 @@ def autopickstation(wfstream, pickparam, verbose=False, ########################################################################## # calculate "real" onset times if lpickP is not None and lpickP == mpickP: - lpickP += timeerrorsP[0] + lpickP += zdat[0].stats.delta if epickP is not None and epickP == mpickP: - epickP -= timeerrorsP[0] + epickP -= zdat[0].stats.delta if mpickP is not None and epickP is not None and lpickP is not None: lpickP = zdat[0].stats.starttime + lpickP epickP = zdat[0].stats.starttime + epickP @@ -998,27 +999,27 @@ def autopickstation(wfstream, pickparam, verbose=False, epickP = zdat[0].stats.starttime - timeerrorsP[3] mpickP = zdat[0].stats.starttime + if edat: + hdat = edat[0] + elif ndat: + hdat = ndat[0] + else: + return + if lpickS is not None and lpickS == mpickS: - lpickS += timeerrorsS[0] + lpickS += hdat.stats.delta if epickS is not None and epickS == mpickS: - epickS -= timeerrorsS[0] + epickS -= hdat.stats.delta if mpickS is not None and epickS is not None and lpickS is not None: - lpickS = edat[0].stats.starttime + lpickS - epickS = edat[0].stats.starttime + epickS - mpickS = edat[0].stats.starttime + mpickS + lpickS = hdat.stats.starttime + lpickS + epickS = hdat.stats.starttime + epickS + mpickS = hdat.stats.starttime + mpickS else: # dummy values (start of seismic trace) in order to derive # theoretical onset times for iteratve picking - if edat: - lpickS = edat[0].stats.starttime + timeerrorsS[3] - epickS = edat[0].stats.starttime - timeerrorsS[3] - mpickS = edat[0].stats.starttime - elif ndat: - lpickS = ndat[0].stats.starttime + timeerrorsS[3] - epickS = ndat[0].stats.starttime - timeerrorsS[3] - mpickS = ndat[0].stats.starttime - else: - return + lpickS = hdat.stats.starttime + timeerrorsS[3] + epickS = hdat.stats.starttime - timeerrorsS[3] + mpickS = hdat.stats.starttime # create dictionary # for P phase @@ -1028,12 +1029,8 @@ def autopickstation(wfstream, pickparam, verbose=False, snrdb=SNRPdB, weight=Pweight, fm=FM, w0=None, fc=None, Mo=None, Mw=None, picker=picker, marked=Pmarker) # add S phase - try: - ccode = edat[0].stats.channel - ncode = edat[0].stats.network - except: - ccode = ndat[0].stats.channel - ncode = ndat[0].stats.network + ccode = hdat.stats.channel + ncode = hdat.stats.network spick = dict(channel=ccode, network=ncode, lpp=lpickS, epp=epickS, mpp=mpickS, spe=Serror, snr=SNRS, snrdb=SNRSdB, weight=Sweight, fm=None, picker=picker, Ao=Ao) # merge picks into returning dictionary diff --git a/pylot/core/pick/utils.py b/pylot/core/pick/utils.py index 873c228b..6a214b2e 100644 --- a/pylot/core/pick/utils.py +++ b/pylot/core/pick/utils.py @@ -566,7 +566,7 @@ def select_for_phase(st, phase): return sel_st -def wadaticheck(pickdic, dttolerance, iplot): +def wadaticheck(pickdic, dttolerance, iplot=0, fig_dict=None): ''' Function to calculate Wadati-diagram from given P and S onsets in order to detect S pick outliers. If a certain S-P time deviates by dttolerance @@ -620,7 +620,7 @@ def wadaticheck(pickdic, dttolerance, iplot): ii = 0 ibad = 0 for key in pickdic: - if pickdic[key].has_key('SPt'): + if 'SPt' in pickdic[key]: wddiff = abs(pickdic[key]['SPt'] - wdfit[ii]) ii += 1 # check, if deviation is larger than adjusted @@ -664,21 +664,28 @@ def wadaticheck(pickdic, dttolerance, iplot): # plot results if iplot > 0: - plt.figure() # iplot) - f1, = plt.plot(Ppicks, SPtimes, 'ro') - if wfitflag == 0: - f2, = plt.plot(Ppicks, wdfit, 'k') - f3, = plt.plot(checkedPpicks, checkedSPtimes, 'ko') - f4, = plt.plot(checkedPpicks, wdfit2, 'g') - plt.title('Wadati-Diagram, %d S-P Times, Vp/Vs(raw)=%5.2f,' \ - 'Vp/Vs(checked)=%5.2f' % (len(SPtimes), vpvsr, cvpvsr)) - plt.legend([f1, f2, f3, f4], ['Skipped S-Picks', 'Wadati 1', - 'Reliable S-Picks', 'Wadati 2'], loc='best') + if fig_dict: + fig = fig_dict['wadati'] + plt_flag = 0 else: - plt.title('Wadati-Diagram, %d S-P Times' % len(SPtimes)) + fig = plt.figure() + plt_flag = 1 + ax = fig.add_subplot(111) + ax.plot(Ppicks, SPtimes, 'ro', label='Skipped S-Picks') + if wfitflag == 0: + ax.plot(Ppicks, wdfit, 'k', label='Wadati 1') + ax.plot(checkedPpicks, checkedSPtimes, 'ko', label='Reliable S-Picks') + ax.plot(checkedPpicks, wdfit2, 'g', label='Wadati 2') + ax.set_title('Wadati-Diagram, %d S-P Times, Vp/Vs(raw)=%5.2f,' \ + 'Vp/Vs(checked)=%5.2f' % (len(SPtimes), vpvsr, cvpvsr)) + ax.legend() + else: + ax.set_title('Wadati-Diagram, %d S-P Times' % len(SPtimes)) - plt.ylabel('S-P Times [s]') - plt.xlabel('P Times [s]') + ax.set_ylabel('S-P Times [s]') + ax.set_xlabel('P Times [s]') + if plt_flag: + fig.show() return checkedonsets @@ -796,7 +803,7 @@ def checksignallength(X, pick, TSNR, minsiglength, nfac, minpercent, iplot=0, fi return returnflag -def checkPonsets(pickdic, dttolerance, iplot): +def checkPonsets(pickdic, dttolerance, iplot=0, fig_dict=None): ''' Function to check statistics of P-onset times: Control deviation from median (maximum adjusted deviation = dttolerance) and apply pseudo- @@ -818,17 +825,19 @@ def checkPonsets(pickdic, dttolerance, iplot): # search for good quality P picks Ppicks = [] stations = [] - for key in pickdic: - if pickdic[key]['P']['weight'] < 4: + for station in pickdic: + if pickdic[station]['P']['weight'] < 4: # add P onsets to list - UTCPpick = UTCDateTime(pickdic[key]['P']['mpp']) + UTCPpick = UTCDateTime(pickdic[station]['P']['mpp']) Ppicks.append(UTCPpick.timestamp) - stations.append(key) + stations.append(station) # apply jackknife bootstrapping on variance of P onsets print("###############################################") print("checkPonsets: Apply jackknife bootstrapping on P-onset times ...") [xjack, PHI_pseudo, PHI_sub] = jackknife(Ppicks, 'VAR', 1) + if not xjack: + return # get pseudo variances smaller than average variances # (times safety factor), these picks passed jackknife test ij = np.where(PHI_pseudo <= 5 * xjack) @@ -872,21 +881,30 @@ def checkPonsets(pickdic, dttolerance, iplot): checkedonsets = pickdic if iplot > 0: - p1, = plt.plot(np.arange(0, len(Ppicks)), Ppicks, 'ro', markersize=14) - if len(badstations) < 1 and len(badjkstations) < 1: - p2, = plt.plot(np.arange(0, len(Ppicks)), Ppicks, 'go', markersize=14) + if fig_dict: + fig = fig_dict['jackknife'] + plt_flag = 0 else: - p2, = plt.plot(igood, np.array(Ppicks)[igood], 'go', markersize=14) - p3, = plt.plot([0, len(Ppicks) - 1], [pmedian, pmedian], 'g', - linewidth=2) - for i in range(0, len(Ppicks)): - plt.text(i, Ppicks[i] + 0.01, '{0}'.format(stations[i])) + fig = plt.figure() + plt_flag = 1 + ax = fig.add_subplot(111) - plt.xlabel('Number of P Picks') - plt.ylabel('Onset Time [s] from 1.1.1970') - plt.legend([p1, p2, p3], ['Skipped P Picks', 'Good P Picks', 'Median'], - loc='best') - plt.title('Jackknifing and Median Tests on P Onsets') + ax.plot(np.arange(0, len(Ppicks)), Ppicks, 'ro', markersize=14) + if len(badstations) < 1 and len(badjkstations) < 1: + ax.plot(np.arange(0, len(Ppicks)), Ppicks, 'go', markersize=14, label='Skipped P Picks') + else: + ax.plot(igood, np.array(Ppicks)[igood], 'go', markersize=14, label='Good P Picks') + ax.plot([0, len(Ppicks) - 1], [pmedian, pmedian], 'g', + linewidth=2, label='Median') + for i in range(0, len(Ppicks)): + ax.text(i, Ppicks[i] + 0.01, '{0}'.format(stations[i])) + + ax.set_xlabel('Number of P Picks') + ax.set_ylabel('Onset Time [s] from 1.1.1970') + ax.legend() + ax.set_title('Jackknifing and Median Tests on P Onsets') + if plt_flag: + fig.show() return checkedonsets @@ -922,6 +940,7 @@ def jackknife(X, phi, h): print("Choose another size for subgroups!") return PHI_jack, PHI_pseudo, PHI_sub else: + g = int(len(X) / h) # estimator of undisturbed spot check if phi == 'MEA': phi_sc = np.mean(X) @@ -1100,7 +1119,7 @@ def checkZ4S(X, pick, zfac, checkwin, iplot, fig=None): return returnflag -def getQualityfromUncertainty(uncertainty, Errors): +def getQualityFromUncertainty(uncertainty, Errors): '''Script to transform uncertainty into quality classes 0-4 regarding adjusted time errors Errors. ''' diff --git a/pylot/core/util/utils.py b/pylot/core/util/utils.py index 6fcde6c0..46a66eda 100644 --- a/pylot/core/util/utils.py +++ b/pylot/core/util/utils.py @@ -74,6 +74,37 @@ def gen_Pool(ncores=0): return pool +def excludeQualityClasses(picks, qClasses, timeerrorsP, timeerrorsS): + ''' + takes PyLoT picks dictionary and returns a new dictionary with certain classes excluded. + :param picks: PyLoT picks dictionary + :param qClasses: list (or int) of quality classes (0-4) to exclude + :param timeerrorsP: time errors for classes (0-4) for P + :param timeerrorsS: time errors for classes (0-4) for S + :return: new picks dictionary + ''' + from pylot.core.pick.utils import getQualityFromUncertainty + + if type(qClasses) in [int, float]: + qClasses = [qClasses] + + picksdict_new = {} + + phaseError = {'P': timeerrorsP, + 'S': timeerrorsS} + + for station, phases in picks.items(): + for phase, pick in phases.items(): + pickerror = phaseError[identifyPhaseID(phase)] + quality = getQualityFromUncertainty(pick['spe'], pickerror) + if not quality in qClasses: + if not station in picksdict_new: + picksdict_new[station] = {} + picksdict_new[station][phase] = pick + + return picksdict_new + + def clims(lim1, lim2): """ takes two pairs of limits and returns one pair of common limts @@ -897,6 +928,15 @@ def identifyPhase(phase): return False +def identifyPhaseID(phase): + return identifyPhase(loopIdentifyPhase(phase)) + + +def has_spe(pick): + if not 'spe' in pick.keys(): + return None + else: + return pick['spe'] if __name__ == "__main__": diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index bffb1e9c..e7705475 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -12,6 +12,7 @@ import multiprocessing import os import subprocess import sys +import time import numpy as np @@ -22,7 +23,7 @@ except: from matplotlib.figure import Figure from pylot.core.util.utils import find_horizontals, identifyPhase, loopIdentifyPhase, trim_station_components, \ - check4rotated + identifyPhaseID try: from matplotlib.backends.backend_qt4agg import FigureCanvas @@ -45,7 +46,7 @@ from obspy.taup.utils import get_phase_names from pylot.core.io.data import Data from pylot.core.io.inputs import FilterOptions, PylotParameter from pylot.core.pick.utils import getSNR, earllatepicker, getnoisewin, \ - getResolutionWindow, getQualityfromUncertainty + getResolutionWindow, getQualityFromUncertainty from pylot.core.pick.compare import Comparison from pylot.core.util.defaults import OUTPUTFORMATS, FILTERDEFAULTS, \ SetChannelComponents @@ -119,7 +120,7 @@ def createAction(parent, text, slot=None, shortcut=None, icon=None, return action -class ComparisonDialog(QDialog): +class ComparisonWidget(QWidget): def __init__(self, c, parent=None): self._data = c self._stats = c.stations @@ -129,8 +130,9 @@ class ComparisonDialog(QDialog): histCheckBox=None) self._phases = 'PS' self._plotprops = dict(station=list(self.stations)[0], phase=list(self.phases)[0]) - super(ComparisonDialog, self).__init__(parent) + super(ComparisonWidget, self).__init__(parent, 1) self.setupUI() + self.resize(1280, 720) self.plotcomparison() def setupUI(self): @@ -162,17 +164,12 @@ class ComparisonDialog(QDialog): _toolbar.addWidget(_phases_combobox) _toolbar.addWidget(_hist_checkbox) - _buttonbox = QDialogButtonBox(QDialogButtonBox.Close) - _innerlayout.addWidget(self.canvas) - _innerlayout.addWidget(_buttonbox) _outerlayout.addWidget(_toolbar) _outerlayout.addLayout(_innerlayout) - _buttonbox.rejected.connect(self.reject) - - # finally layout the entire dialog + # finally layout the entire widget self.setLayout(_outerlayout) @property @@ -271,6 +268,10 @@ class ComparisonDialog(QDialog): # _axes.cla() station = self.plotprops['station'] phase = self.plotprops['phase'] + if not phase in self.data.comparison[station]: + _axes.set_title('No pick found for phase {}.'.format(phase)) + self.canvas.draw() + return pdf = self.data.comparison[station][phase] x, y, std, exp = pdf.axis, pdf.data, pdf.standard_deviation(), \ pdf.expectation() @@ -1286,7 +1287,7 @@ class PickDlg(QDialog): self.currentPhase = str(self.s_button.text()) def getPhaseID(self, phase): - return identifyPhase(loopIdentifyPhase(phase)) + return identifyPhaseID(phase) def set_button_color(self, button, color=None): if type(color) == QtGui.QColor: @@ -1700,7 +1701,7 @@ class PickDlg(QDialog): else: ylims = self.getPlotWidget().getYLims() if self.getPicks(picktype): - if phase is not None: + if phase is not None and not phase == 'SPt': if (type(self.getPicks(picktype)[phase]) is dict or type(self.getPicks(picktype)[phase]) is AttribDict): picks = self.getPicks(picktype)[phase] @@ -1715,10 +1716,10 @@ class PickDlg(QDialog): # get quality classes if self.getPhaseID(phase) == 'P': - quality = getQualityfromUncertainty(picks['spe'], self.parameter['timeerrorsP']) + quality = getQualityFromUncertainty(picks['spe'], self.parameter['timeerrorsP']) phaseID = 'P' elif self.getPhaseID(phase) == 'S': - quality = getQualityfromUncertainty(picks['spe'], self.parameter['timeerrorsS']) + quality = getQualityFromUncertainty(picks['spe'], self.parameter['timeerrorsS']) phaseID = 'S' mpp = picks['mpp'] - self.getStartTime() @@ -2011,6 +2012,167 @@ class PhasePlotWidget(FigureCanvas): super(PhasePlotWidget, self).__init__(self.fig) +class CanvasWidget(QWidget): + ''' + ''' + + def __init__(self, parent, canvas): + QtGui.QWidget.__init__(self, parent)#, 1) + self.main_layout = QtGui.QVBoxLayout() + self.setLayout(self.main_layout) + self.main_layout.addWidget(canvas) + + +class AutoPickWidget(QWidget): + start = Signal() + ''' + ''' + + def __init__(self, parent, pickoptions): + QtGui.QWidget.__init__(self, parent, 1) + self.pickoptions = pickoptions + self.setupUi() + self.connect_buttons() + self.reinitEvents2plot() + self.setWindowTitle('Autopick events interactively') + # set initial size + self.resize(1280, 720) + + def setupUi(self): + # init main layout + self.main_layout = QtGui.QVBoxLayout() + self.setLayout(self.main_layout) + # init main splitter + self.main_splitter = QtGui.QSplitter() + self.main_splitter.setChildrenCollapsible(False) + + self.init_checkboxes() + self.init_log_layout() + self.init_plot_layout() + + self.eventbox = QtGui.QComboBox() + self.button_clear = QtGui.QPushButton('Clear') + + self.main_layout.insertWidget(1, self.main_splitter) + + self.main_layout.setStretch(0, 0) + self.main_layout.setStretch(1, 1) + self.main_splitter.setStretchFactor(0, 1) + self.main_splitter.setStretchFactor(1, 2) + + def connect_buttons(self): + self.start_button.clicked.connect(self.start_picker) + self.button_clear.clicked.connect(self.reinitEvents2plot) + + def init_checkboxes(self): + self.rb_layout = QtGui.QHBoxLayout() + + self.rb_dict = {} + + self.start_button = QtGui.QPushButton('Start') + + for index, (key, func) in enumerate(self.pickoptions): + rb = QtGui.QRadioButton(key) + if index == 0: + rb.setChecked(True) + self.rb_dict[key] = rb + self.rb_layout.insertWidget(index, rb) + self.rb_layout.setStretch(index, 0) + + self.rb_layout.addWidget(self.start_button) + + self.rb_layout.addWidget(QtGui.QWidget()) + self.rb_layout.setStretch(len(self.pickoptions)+1, 1) + + self.main_layout.insertLayout(0, self.rb_layout) + + def init_plot_layout(self): + # init tab widget + self.tab_plots = QtGui.QTabWidget() + self.gb_plots = QtGui.QGroupBox('Plots') + self.gb_plots.setMinimumSize(100, 100) + self.main_splitter.insertWidget(1, self.gb_plots) + self.plot_layout = QtGui.QVBoxLayout() + self.plot_layout.insertWidget(1, self.tab_plots) + self.gb_plots.setLayout(self.plot_layout) + + def init_log_layout(self): + self.gb_log = QtGui.QGroupBox('Log') + self.gb_log.setMinimumSize(100, 100) + self.main_splitter.insertWidget(0, self.gb_log) + + def insert_log_widget(self, widget): + vl = QtGui.QVBoxLayout() + vl.addWidget(widget) + self.gb_log.setLayout(vl) + + def add_plot_widget(self, widget, key, eventID): + eventID += ' [picked: {}]'.format(time.strftime('%X %x %z')) + if not eventID in self.events2plot.keys(): + self.events2plot[eventID] = {} + self.events2plot[eventID][key] = widget + + def generate_combobox(self): + self.eventbox.clear() + for eventID, widgets in self.events2plot.items(): + self.eventbox.addItem(str(eventID), widgets) + self.eventbox.currentIndexChanged.connect(self.draw_plots) + self.draw_plots() + + def draw_plots(self, index=0): + self.refresh_plot_tabs() + widgets = self.eventbox.itemData(index) + if not widgets: + return + for key, widget in widgets.items(): + self.tab_plots.addTab(widget, str(key)) + + def update_plots(self): + self.refresh_plot_tabs() + if len(self.events2plot) > 0: + self.eventbox_layout = QtGui.QHBoxLayout() + self.generate_combobox() + self.eventbox_layout.addWidget(self.eventbox) + self.eventbox_layout.addWidget(self.button_clear) + self.eventbox_layout.setStretch(0, 1) + self.plot_layout.insertLayout(0, self.eventbox_layout) + + def reinitEvents2plot(self): + self.events2plot = {} + self.eventbox.clear() + self.refresh_plot_tabs() + + def refresh_plot_tabs(self): + self.tab_plots.clear() + + def refresh_tooltips(self): + for key, func in self.pickoptions: + eventlist = func() + if not type(eventlist) == list: + eventlist = [eventlist] + tooltip='' + for index, event in enumerate(eventlist): + if not event: + continue + tooltip += '{}'.format(event.pylot_id) + if not index + 1 == len(eventlist): + tooltip += '\n' + if not tooltip: + tooltip = 'No events for this selection' + self.rb_dict[key].setToolTip(tooltip) + + def start_picker(self): + self.refresh_plot_tabs() + self.start.emit() + + def enable(self, bool): + for rb in self.rb_dict.values(): + rb.setEnabled(bool) + self.start_button.setEnabled(bool) + self.eventbox.setEnabled(bool) + self.button_clear.setEnabled(bool) + + class TuneAutopicker(QWidget): update = QtCore.Signal(str) ''' @@ -2343,7 +2505,7 @@ class TuneAutopicker(QWidget): args = {'parameter': self.parameter, 'station': station, 'fnames': 'None', - 'eventid': self.get_current_event_fp(), + 'eventid': [self.get_current_event_fp()], 'iplot': 2, 'fig_dict': self.fig_dict, 'locflag': 0, @@ -2369,7 +2531,7 @@ class TuneAutopicker(QWidget): info = self.ap_thread._executedErrorInfo self._warn(msg, info) return - self.pylot_picks = self.ap_thread.data + self.pylot_picks = self.ap_thread.data[self.get_current_event_name()] if not self.pylot_picks: self._warn('No picks found. See terminal output.') return