diff --git a/QtPyLoT.py b/QtPyLoT.py index 745cce04..c02599e0 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -79,7 +79,8 @@ 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, \ PylotCanvas, WaveformWidgetPG, PropertiesDlg, HelpForm, createAction, PickDlg, \ - getDataType, ComparisonWidget, TuneAutopicker, PylotParaBox, AutoPickDlg, CanvasWidget, AutoPickWidget + getDataType, ComparisonWidget, TuneAutopicker, PylotParaBox, AutoPickDlg, CanvasWidget, AutoPickWidget, \ + CompareEventsWidget from pylot.core.util.map_projection import map_projection from pylot.core.util.structure import DATASTRUCTURE from pylot.core.util.thread import Thread, Worker @@ -1029,6 +1030,8 @@ class MainWindow(QMainWindow): # if pick widget is open, refresh tooltips as well if hasattr(self, 'apw'): self.apw.refresh_tooltips() + if hasattr(self, 'cmpw'): + self.cmpw.refresh_tooltips() if not eventBox: eventBox = self.eventBox @@ -1230,14 +1233,50 @@ class MainWindow(QMainWindow): return None def comparePicks(self): - if self.check4Comparison(): - autopicks = excludeQualityClasses(self.getPicks('auto'), [4], + comparisons = {} + eventdict = {} + for event in self.project.eventlist: + if not self.comparable[event.pylot_id]: + continue + autopicks = excludeQualityClasses(event.getAutopicks(), [4], self._inputs['timeerrorsP'], self._inputs['timeerrorsS']) - manupicks = excludeQualityClasses(self.getPicks('manual'), [4], + manupicks = excludeQualityClasses(event.getPicks(), [4], self._inputs['timeerrorsP'], self._inputs['timeerrorsS']) co = Comparison(auto=autopicks, manu=manupicks) - compare_dlg = ComparisonWidget(co, self) - compare_dlg.show() + comparisons[event.pylot_id] = co + eventdict[event.pylot_id] = event + if len(eventdict) < 1: + return + + # init event selection options for autopick + self.compareoptions =[('tune events', self.get_ref_events), + ('test events', self.get_test_events), + ('all (picked) events', self.get_manu_picked_events)] + + self.cmpw = CompareEventsWidget(self, self.compareoptions, eventdict, comparisons) + self.cmpw.start.connect(self.compareMulti) + self.cmpw.refresh_tooltips() + self.cmpw.show() + + def compareMulti(self): + for key, func in self.compareoptions: + if self.cmpw.rb_dict[key].isChecked(): + # if radio button is checked break for loop and use func + break + eventlist = func() + # use only events comparable + eventlist_overlap = [event for event in eventlist if self.comparable[event.pylot_id]] + compare_widget = self.buildMultiCompareWidget(eventlist_overlap) + compare_widget.show() + + + def buildMultiCompareWidget(self, eventlist): + global_comparison = Comparison(eventlist=eventlist) + compare_widget = ComparisonWidget(global_comparison, self) + compare_widget.setWindowTitle('Histograms for all selected events') + compare_widget.hideToolbar() + compare_widget.setHistboxChecked(True) + return compare_widget def getPlotWidget(self): return self.dataPlot @@ -1488,6 +1527,7 @@ class MainWindow(QMainWindow): self.dataPlot.plotWidget.showAxis('bottom') def finishWaveformDataPlot(self): + self.comparable = self.checkEvents4comparison() if self.pg: self.finish_pg_plot() else: @@ -1521,15 +1561,31 @@ class MainWindow(QMainWindow): self.locateEvent.setEnabled(True) if event.pylot_autopicks: self.drawPicks(picktype='auto') - if event.pylot_picks and event.pylot_autopicks: - for key in event.pylot_picks: - for akey in event.pylot_autopicks: - if (akey == key) and (event.pylot_autopicks[akey]['P']['spe'] is not None \ - or event.pylot_autopicks[akey]['S']['spe'] is not None): - self.compare_action.setEnabled(True) - break + if True in self.comparable.values(): + self.compare_action.setEnabled(True) self.draw() + def checkEvent4comparison(self, event): + if event.pylot_picks and event.pylot_autopicks: + for station in event.pylot_picks: + if station in event.pylot_autopicks: + autopick_p = event.pylot_autopicks[station]['P']['spe'] + manupick_p = event.pylot_picks[station]['P']['spe'] + autopick_s = event.pylot_autopicks[station]['S']['spe'] + manupick_s = event.pylot_picks[station]['S']['spe'] + if autopick_p and manupick_p: + return True + elif autopick_s and manupick_s: + return True + return False + + def checkEvents4comparison(self): + # init dict to keep track whether event can be compared + comparable = {} + for event in self.project.eventlist: + comparable[event.pylot_id] = self.checkEvent4comparison(event) + return comparable + def clearWaveformDataPlot(self): self.disconnectWFplotEvents() if self.pg: @@ -1799,11 +1855,14 @@ class MainWindow(QMainWindow): return self.update_status('picking on station {0}'.format(station)) data = self.get_data().getWFData() + event = self.get_current_event() pickDlg = PickDlg(self, parameter=self._inputs, data=data.select(station=station), station=station, network=network, picks=self.getPicksOnStation(station, 'manual'), - autopicks=self.getPicksOnStation(station, 'auto')) + autopicks=self.getPicksOnStation(station, 'auto'), + metadata=self.metadata, event=event, + filteroptions=self.filteroptions) pickDlg.nextStation.setChecked(nextStation) if pickDlg.exec_(): if pickDlg._dirty: @@ -1915,6 +1974,7 @@ class MainWindow(QMainWindow): "No autoPyLoT output declared!") return + # init event selection options for autopick self.pickoptions =[('current event', self.get_current_event), ('tune events', self.get_ref_events), ('test events', self.get_test_events), @@ -2092,7 +2152,6 @@ class MainWindow(QMainWindow): #event.picks.update(picks) MP MP idea elif type == 'auto': event.addAutopicks(picksdict['auto']) - self.check4Comparison() def drawPicks(self, station=None, picktype=None): # if picktype not specified, draw both @@ -2632,18 +2691,18 @@ class MainWindow(QMainWindow): def check4Loc(self): return self.picksNum() >= 4 - def check4Comparison(self): - mpicks = self.getPicks() - apicks = self.getPicks('auto') - for station, phases in mpicks.items(): - try: - aphases = apicks[station] - for phase in phases.keys(): - if phase in aphases.keys(): - return True - except KeyError: - continue - return False + # def check4Comparison(self): + # mpicks = self.getPicks() + # apicks = self.getPicks('auto') + # for station, phases in mpicks.items(): + # try: + # aphases = apicks[station] + # for phase in phases.keys(): + # if phase in aphases.keys(): + # return True + # except KeyError: + # continue + # return False def picksNum(self, type='manual'): num = 0 diff --git a/pylot/core/pick/autopick.py b/pylot/core/pick/autopick.py index 5b8c4d96..6932f620 100644 --- a/pylot/core/pick/autopick.py +++ b/pylot/core/pick/autopick.py @@ -131,6 +131,7 @@ def autopickstation(wfstream, pickparam, verbose=False, bpz1 = pickparam.get('bpz1') bpz2 = pickparam.get('bpz2') pickwinP = pickparam.get('pickwinP') + aictsmoothP = pickparam.get('aictsmooth') tsmoothP = pickparam.get('tsmoothP') ausP = pickparam.get('ausP') nfacP = pickparam.get('nfacP') @@ -323,7 +324,7 @@ def autopickstation(wfstream, pickparam, verbose=False, fig = fig_dict[key] else: fig = None - aicpick = AICPicker(aiccf, tsnrz, pickwinP, iplot, None, tsmoothP, fig=fig) + aicpick = AICPicker(aiccf, tsnrz, pickwinP, iplot, None, aictsmoothP, fig=fig) # add pstart and pstop to aic plot if fig: for ax in fig.axes: diff --git a/pylot/core/pick/compare.py b/pylot/core/pick/compare.py index 24caa92c..ee908a13 100644 --- a/pylot/core/pick/compare.py +++ b/pylot/core/pick/compare.py @@ -27,16 +27,8 @@ class Comparison(object): """ def __init__(self, **kwargs): - names = list() self._pdfs = dict() - for name, fn in kwargs.items(): - if isinstance(fn, PDFDictionary): - self._pdfs[name] = fn - elif isinstance(fn, dict) or isinstance(fn, AttribDict): - self._pdfs[name] = PDFDictionary(fn) - else: - self._pdfs[name] = PDFDictionary.from_quakeml(fn) - names.append(name) + names = self.iter_kwargs(kwargs) if len(names) > 2: raise ValueError('Comparison is only defined for two ' 'arguments!') @@ -48,6 +40,40 @@ class Comparison(object): return False return True + def iter_kwargs(self, kwargs): + names = list() + for name, fn in kwargs.items(): + if name == 'eventlist': + names = self.init_by_eventlist(fn) + break + if isinstance(fn, PDFDictionary): + self._pdfs[name] = fn + elif isinstance(fn, dict) or isinstance(fn, AttribDict): + self._pdfs[name] = PDFDictionary(fn) + else: + self._pdfs[name] = PDFDictionary.from_quakeml(fn) + names.append(name) + return names + + def init_by_eventlist(self, eventlist): + # create one dictionary containing all picks for all events (therefore modify station key) + global_picksdict = {} + for event in eventlist: + automanu = {'manu': event.pylot_picks, + 'auto': event.pylot_autopicks} + for method, picksdict in automanu.items(): + if not method in global_picksdict.keys(): + global_picksdict[method] = {} + for station, picks in picksdict.items(): + new_picksdict = global_picksdict[method] + # new id combining event and station in one dictionary for all events + id = '{}_{}'.format(event.pylot_id, station) + new_picksdict[id] = picks + for method, picksdict in global_picksdict.items(): + self._pdfs[method] = PDFDictionary(picksdict) + names = list(global_picksdict.keys()) + return names + def get(self, name): return self._pdfs[name] diff --git a/pylot/core/util/utils.py b/pylot/core/util/utils.py index 46a66eda..28dad3bb 100644 --- a/pylot/core/util/utils.py +++ b/pylot/core/util/utils.py @@ -9,6 +9,7 @@ import subprocess import numpy as np from obspy import UTCDateTime, read +from obspy.core import AttribDict from obspy.signal.rotate import rotate2zne from obspy.io.xseed.utils import SEEDParserException @@ -95,6 +96,8 @@ def excludeQualityClasses(picks, qClasses, timeerrorsP, timeerrorsS): for station, phases in picks.items(): for phase, pick in phases.items(): + if not type(pick) in [AttribDict, dict]: + continue pickerror = phaseError[identifyPhaseID(phase)] quality = getQualityFromUncertainty(pick['spe'], pickerror) if not quality in qClasses: diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index 376d1a66..3575eb1e 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -121,7 +121,7 @@ def createAction(parent, text, slot=None, shortcut=None, icon=None, class ComparisonWidget(QWidget): - def __init__(self, c, parent=None): + def __init__(self, c, parent=None, windowflag=1): self._data = c self._stats = c.stations self._canvas = PlotWidget(self) @@ -130,7 +130,7 @@ class ComparisonWidget(QWidget): histCheckBox=None) self._phases = 'PS' self._plotprops = dict(station=list(self.stations)[0], phase=list(self.phases)[0]) - super(ComparisonWidget, self).__init__(parent, 1) + super(ComparisonWidget, self).__init__(parent, windowflag) self.setupUI() self.resize(1280, 720) self.plotcomparison() @@ -154,19 +154,19 @@ class ComparisonWidget(QWidget): _phases_combobox.currentIndexChanged.connect(self.prepareplot) self.widgets = _phases_combobox - _hist_checkbox = QCheckBox('Show histograms', self) - _hist_checkbox.setObjectName('histCheckBox') - _hist_checkbox.stateChanged.connect(self.plothist) - self.widgets = _hist_checkbox + self._hist_checkbox = QCheckBox('Show histograms', self) + self._hist_checkbox.setObjectName('histCheckBox') + self._hist_checkbox.stateChanged.connect(self.plothist) + self.widgets = self._hist_checkbox - _toolbar = QToolBar(self) - _toolbar.addWidget(_stats_combobox) - _toolbar.addWidget(_phases_combobox) - _toolbar.addWidget(_hist_checkbox) + self._toolbar = QToolBar(self) + self._toolbar.addWidget(_stats_combobox) + self._toolbar.addWidget(_phases_combobox) + self._toolbar.addWidget(self._hist_checkbox) _innerlayout.addWidget(self.canvas) - _outerlayout.addWidget(_toolbar) + _outerlayout.addWidget(self._toolbar) _outerlayout.addLayout(_innerlayout) # finally layout the entire widget @@ -232,6 +232,15 @@ class ComparisonWidget(QWidget): if name in self.widgets.keys(): self._widgets[name] = widget + def showToolbar(self): + self._toolbar.show() + + def hideToolbar(self): + self._toolbar.hide() + + def setHistboxChecked(self, bool): + self._hist_checkbox.setChecked(bool) + def clf(self): self.canvas.figure.clf() @@ -994,7 +1003,8 @@ class PickDlg(QDialog): update_picks = QtCore.Signal(dict) def __init__(self, parent=None, data=None, station=None, network=None, picks=None, - autopicks=None, rotate=False, parameter=None, embedded=False, model='iasp91'): + autopicks=None, rotate=False, parameter=None, embedded=False, metadata=None, + event=None, filteroptions=None, model='iasp91'): super(PickDlg, self).__init__(parent) # initialize attributes @@ -1003,6 +1013,8 @@ class PickDlg(QDialog): self.station = station self.network = network self.rotate = rotate + self.metadata = metadata + self.pylot_event = event self.components = 'ZNE' self.currentPhase = None self.phaseText = [] @@ -1025,8 +1037,8 @@ class PickDlg(QDialog): else: self.autopicks = {} self._init_autopicks = {} - if hasattr(self.parent(), 'filteroptions'): - self.filteroptions = self.parent().filteroptions + if filteroptions: + self.filteroptions = filteroptions else: self.filteroptions = FILTERDEFAULTS self.pick_block = False @@ -1077,7 +1089,7 @@ class PickDlg(QDialog): # init expected picks using obspy Taup try: - if self.parent().metadata: + if self.metadata: self.model = TauPyModel(model) self.get_arrivals() self.drawArrivals() @@ -1242,13 +1254,16 @@ class PickDlg(QDialog): self._dirty = bool def get_arrivals(self, plot=False): + if not self.metadata: + print('get_arrivals: No metadata given. Return!') + return func = {True: self.model.get_ray_paths_geo, False: self.model.get_travel_times_geo} phases = self.prepare_phases() station_id = self.data.traces[0].get_id() - parser = self.parent().metadata[1] + parser = self.metadata[1] station_coords = parser.get_coordinates(station_id) - origins = self.parent().get_current_event().origins + origins = self.pylot_event.origins if origins: source_origin = origins[0] else: @@ -1279,7 +1294,7 @@ class PickDlg(QDialog): else: ylims = self.multicompfig.getYLims(ax) stime = self.getStartTime() - source_origin = self.parent().get_current_event().origins[0] + source_origin = self.pylot_event.origins[0] source_time = source_origin.time for arrival in self.arrivals: arrival_time_abs = source_time + arrival.time @@ -2071,18 +2086,16 @@ class CanvasWidget(QWidget): canvas.setZoomBorders2content() -class AutoPickWidget(QWidget): +class MultiEventWidget(QWidget): start = Signal() ''' - ''' - def __init__(self, parent, pickoptions): - QtGui.QWidget.__init__(self, parent, 1) - self.pickoptions = pickoptions + ''' + def __init__(self, options=None, parent=None, windowflag=1): + QtGui.QWidget.__init__(self, parent, windowflag) + + self.options = options self.setupUi() - self.connect_buttons() - self.reinitEvents2plot() - self.setWindowTitle('Autopick events interactively') # set initial size self.resize(1280, 720) @@ -2095,8 +2108,6 @@ class AutoPickWidget(QWidget): 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') @@ -2108,10 +2119,6 @@ class AutoPickWidget(QWidget): 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() @@ -2119,8 +2126,9 @@ class AutoPickWidget(QWidget): self.start_button = QtGui.QPushButton('Start') - for index, (key, func) in enumerate(self.pickoptions): + for index, (key, func) in enumerate(self.options): rb = QtGui.QRadioButton(key) + rb.toggled.connect(self.check_rb_selection) if index == 0: rb.setChecked(True) self.rb_dict[key] = rb @@ -2130,10 +2138,58 @@ class AutoPickWidget(QWidget): self.rb_layout.addWidget(self.start_button) self.rb_layout.addWidget(QtGui.QWidget()) - self.rb_layout.setStretch(len(self.pickoptions)+1, 1) + self.rb_layout.setStretch(len(self.options) + 1, 1) self.main_layout.insertLayout(0, self.rb_layout) + def refresh_tooltips(self): + for key, func in self.options: + 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) + self.check_rb_selection() + + def check_rb_selection(self): + for rb in self.rb_dict.values(): + if rb.isChecked(): + check_events = (rb.toolTip() == 'No events for this selection') + self.start_button.setEnabled(not(check_events)) + + 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 AutoPickWidget(MultiEventWidget): + ''' + ''' + + def __init__(self, parent, options): + MultiEventWidget.__init__(self, options, parent, 1) + self.connect_buttons() + self.init_plot_layout() + self.init_log_layout() + self.reinitEvents2plot() + self.setWindowTitle('Autopick events interactively') + self.set_main_stretch() + + def connect_buttons(self): + self.start_button.clicked.connect(self.run) + self.button_clear.clicked.connect(self.reinitEvents2plot) + def init_plot_layout(self): # init tab widget self.tab_plots = QtGui.QTabWidget() @@ -2185,6 +2241,10 @@ class AutoPickWidget(QWidget): self.eventbox_layout.setStretch(0, 1) self.plot_layout.insertLayout(0, self.eventbox_layout) + def set_main_stretch(self): + self.main_layout.setStretch(0, 0) + self.main_layout.setStretch(1, 1) + def reinitEvents2plot(self): self.events2plot = {} self.eventbox.clear() @@ -2193,32 +2253,69 @@ class AutoPickWidget(QWidget): 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): + def run(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 CompareEventsWidget(MultiEventWidget): + ''' + ''' + + def __init__(self, parent, options, eventdict, comparisons): + MultiEventWidget.__init__(self, options, parent, 1) + self.eventdict = eventdict + self.comparisons = comparisons + self.compare_widget = QtGui.QWidget() + self.init_eventbox() + self.init_event_area() + self.fill_eventbox() + self.connect_buttons() + self.setWindowTitle('Compare events') + self.set_main_stretch() + + def connect_buttons(self): + self.start_button.clicked.connect(self.run) + self.start_button.setText('Show Histograms') + + def init_event_area(self): + self.event_layout = QVBoxLayout() + self.event_layout.insertWidget(0, self.eventbox) + self.event_area = QGroupBox('Single Event') + self.event_area.setLayout(self.event_layout) + self.main_layout.insertWidget(1, self.event_area) + + def init_eventbox(self): + self.eventbox_layout = QtGui.QHBoxLayout() + self.eventbox_layout.addWidget(self.eventbox) + self.eventbox.currentIndexChanged.connect(self.update_comparison) + + def fill_eventbox(self): + event_ids = list(self.eventdict.keys()) + for event_id in sorted(event_ids): + self.eventbox.addItem(str(event_id)) + self.update_comparison() + + def update_eventbox(self): + self.eventbox.clear() + self.fill_eventbox() + + def update_comparison(self, index=0): + self.compare_widget.setParent(None) + self.compare_widget = ComparisonWidget( + self.comparisons[self.eventbox.currentText()], self, 0) + self.event_layout.insertWidget(1, self.compare_widget) + self.set_main_stretch() + + def set_main_stretch(self): + self.main_layout.setStretch(0, 0) + self.main_layout.setStretch(1, 1) + self.main_layout.setStretch(2, 0) + self.event_layout.setStretch(0, 0) + self.event_layout.setStretch(1, 1) + + def run(self): + self.start.emit() class TuneAutopicker(QWidget): @@ -2381,10 +2478,14 @@ class TuneAutopicker(QWidget): return station = self.get_current_station() data = self.data.getWFData() + metadata = self.parent.metadata + event = self.get_current_event() + filteroptions = self.parent.filteroptions pickDlg = PickDlg(self, data=data.select(station=station), station=station, parameter=self.parameter, picks=self.get_current_event_picks(station), autopicks=self.get_current_event_autopicks(station), + metadata=metadata, event=event, filteroptions=filteroptions, embedded=True) pickDlg.update_picks.connect(self.picks_from_pickdlg) pickDlg.update_picks.connect(self.fill_eventbox)