From a07162d668eeb39623e347dfc675d77826760bfa Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Wed, 7 Jun 2017 17:05:48 +0200 Subject: [PATCH 01/79] first semi-working plot of all traces --- QtPyLoT.py | 38 ++++++-- pylot/RELEASE-VERSION | 2 +- pylot/core/util/widgets.py | 178 +++++++++++++++++++++++++++++++++++++ 3 files changed, 211 insertions(+), 7 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index 998a6c72..b653bfe8 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -42,6 +42,11 @@ from PySide.QtGui import QMainWindow, QInputDialog, QIcon, QFileDialog, \ import numpy as np from obspy import UTCDateTime +try: + import pyqtgraph as pg +except: + pg = None + try: from matplotlib.backends.backend_qt4agg import FigureCanvas except ImportError: @@ -66,7 +71,7 @@ from pylot.core.util.utils import fnConstructor, getLogin, \ full_range from pylot.core.io.location import create_creation_info, create_event from pylot.core.util.widgets import FilterOptionsDialog, NewEventDlg, \ - WaveformWidget, PropertiesDlg, HelpForm, createAction, PickDlg, \ + WaveformWidget, WaveformWidgetPG, PropertiesDlg, HelpForm, createAction, PickDlg, \ getDataType, ComparisonDialog, TuneAutopicker, AutoPickParaBox from pylot.core.util.map_projection import map_projection from pylot.core.util.structure import DATASTRUCTURE @@ -210,8 +215,14 @@ class MainWindow(QMainWindow): # create central matplotlib figure canvas widget plottitle = "Overview: {0} components ".format(self.getComponent()) - self.dataPlot = WaveformWidget(parent=self, xlabel=xlab, ylabel=None, - title=plottitle) + if pg: + self.pg = True + self.dataPlot = WaveformWidgetPG(parent=self, xlabel=xlab, ylabel=None, + title=plottitle) + else: + self.pg = False + self.dataPlot = WaveformWidget(parent=self, xlabel=xlab, ylabel=None, + title=plottitle) self.dataPlot.setCursor(Qt.CrossCursor) # add scroll area used in case number of traces gets too high @@ -1187,6 +1198,12 @@ class MainWindow(QMainWindow): ''' Connect signals refering to WF-Dataplot (select station, tutor_user, scrolling) ''' + if self.pg: + pass + else: + self.connect_mpl() + + def connect_mpl(self): if not self.poS_id: self.poS_id = self.dataPlot.mpl_connect('button_press_event', self.pickOnStation) @@ -1198,11 +1215,16 @@ class MainWindow(QMainWindow): self.scroll_id = self.dataPlot.mpl_connect('scroll_event', self.scrollPlot) - def disconnectWFplotEvents(self): ''' Disconnect all signals refering to WF-Dataplot (select station, tutor_user, scrolling) ''' + if self.pg: + pass + else: + self.disconnect_mpl() + + def disconnect_mpl(self): if self.poS_id: self.dataPlot.mpl_disconnect(self.poS_id) if self.ae_id: @@ -1237,7 +1259,10 @@ class MainWindow(QMainWindow): def clearWaveformDataPlot(self): self.disconnectWFplotEvents() - self.dataPlot.getAxes().cla() + if self.pg: + self.dataPlot.plotitem.clear() + else: + self.dataPlot.getAxes().cla() self.loadlocationaction.setEnabled(False) self.auto_tune.setEnabled(False) self.auto_pick.setEnabled(False) @@ -1254,6 +1279,7 @@ class MainWindow(QMainWindow): ''' Open a modal thread to plot current waveform data. ''' + #self.plotWaveformData() wfp_thread = Thread(self, self.plotWaveformData, progressText='Plotting waveform data...') wfp_thread.finished.connect(self.finishWaveformDataPlot) @@ -1284,7 +1310,7 @@ class MainWindow(QMainWindow): plotWidget.figure.tight_layout() except: pass - self._max_xlims = self.dataPlot.getXLims() + #self._max_xlims = self.dataPlot.getXLims() def adjustPlotHeight(self): height_need = len(self.data.getWFData())*self.height_factor diff --git a/pylot/RELEASE-VERSION b/pylot/RELEASE-VERSION index 8fc5044c..b603f317 100644 --- a/pylot/RELEASE-VERSION +++ b/pylot/RELEASE-VERSION @@ -1 +1 @@ -0af79-dirty +be8035-dirty diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index f28d2679..68b637dd 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -12,6 +12,11 @@ import copy import datetime import numpy as np +try: + import pyqtgraph as pg +except: + pg = None + from matplotlib.figure import Figure from pylot.core.util.utils import find_horizontals @@ -42,6 +47,12 @@ from autoPyLoT import autoPyLoT from pylot.core.util.thread import Thread import icons_rc +if pg: + pg.setConfigOption('background', 'w') + pg.setConfigOption('foreground', 'k') + pg.setConfigOptions(antialias=True) + #pg.setConfigOption('leftButtonPan', False) + def getDataType(parent): type = QInputDialog().getItem(parent, "Select phases type", "Type:", ["manual", "automatic"]) @@ -393,6 +404,173 @@ class PlotWidget(FigureCanvas): return self._parent +class WaveformWidgetPG(QtGui.QWidget): + def __init__(self, parent=None, xlabel='x', ylabel='y', title='Title'): + QtGui.QWidget.__init__(self, parent)#, 1) + self.setParent(parent) + self._parent = parent + # attribute plotdict is a dictionary connecting position and a name + self.plotdict = dict() + # create plot + self.main_layout = QtGui.QVBoxLayout() + self.setLayout(self.main_layout) + #self.win = pg.GraphicsWindow(title="Window") + self.plot = pg.PlotWidget(title=title) + self.main_layout.addWidget(self.plot) + #self.plot = self.win.addPlot(title=title) + self.plot.setMouseEnabled(False, False) + self.plot.showGrid(x=False, y=True, alpha=0.2) + # update labels of the entire widget + #self.updateWidget(xlabel, ylabel, title) + + self.vLine = pg.InfiniteLine(angle=90, movable=False) + self.hLine = pg.InfiniteLine(angle=0, movable=False) + self.plot.addItem(self.vLine, ignoreBounds=True) + self.plot.addItem(self.hLine, ignoreBounds=True) + self._proxy = pg.SignalProxy(self.plot.scene().sigMouseMoved, rateLimit=60, slot=self.mouseMoved) + + def mouseMoved(self, evt): + pos = evt[0] ## using signal proxy turns original arguments into a tuple + if self.plot.sceneBoundingRect().contains(pos): + mousePoint = self.plot.getPlotItem().vb.mapSceneToView(pos) + index = int(mousePoint.x()) + # if index > 0 and index < len(data1): + # label.setText("x=%0.1f, y1=%0.1f, y2=%0.1f" % (mousePoint.x(), data1[index], data2[index])) + self.vLine.setPos(mousePoint.x()) + self.hLine.setPos(mousePoint.y()) + + def getPlotDict(self): + return self.plotdict + + def setPlotDict(self, key, value): + self.plotdict[key] = value + + def clearPlotDict(self): + self.plotdict = dict() + + def getParent(self): + return self._parent + + def setParent(self, parent): + self._parent = parent + + def plotWFData(self, wfdata, title=None, zoomx=None, zoomy=None, + noiselevel=None, scaleddata=False, mapping=True, + component='*', nth_sample=1, iniPick=None): + #self.getAxes().cla() + self.clearPlotDict() + wfstart, wfend = full_range(wfdata) + nmax = 0 + + settings = QSettings() + compclass = settings.value('compclass') + if not compclass: + print('Warning: No settings for channel components found. Using default') + compclass = SetChannelComponents() + + if not component == '*': + alter_comp = compclass.getCompPosition(component) + #alter_comp = str(alter_comp[0]) + + wfdata = wfdata.select(component=component) + wfdata += wfdata.select(component=alter_comp) + + # list containing tuples of network, station, channel (for sorting) + nsc = [] + for trace in wfdata: + nsc.append((trace.stats.network, trace.stats.station, trace.stats.channel)) + nsc.sort() + nsc.reverse() + + for n, (network, station, channel) in enumerate(nsc): + st = wfdata.select(network=network, station=station, channel=channel) + trace = st[0] + if mapping: + comp = channel[-1] + n = compclass.getPlotPosition(str(comp)) + #n = n[0] + if n > nmax: + nmax = n + msg = 'plotting %s channel of station %s' % (channel, station) + print(msg) + stime = trace.stats.starttime - wfstart + time_ax = prepTimeAxis(stime, trace) + if time_ax is not None: + if not scaleddata: + trace.detrend('constant') + trace.normalize(np.max(np.abs(trace.data)) * 2) + times = [time for index, time in enumerate(time_ax) if not index%nth_sample] + data = [datum + n for index, datum in enumerate(trace.data) if not index%nth_sample] + self.plot.plot(times, data, pen=(0, 0, 0)) + if noiselevel is not None: + for level in noiselevel: + self.plot.plot([time_ax[0], time_ax[-1]], + [level, level], pen=(0, 0, 0)) + # if iniPick: + # ax = self.getAxes() + # ax.vlines(iniPick, ax.get_ylim()[0], ax.get_ylim()[1], + # colors='m', linestyles='dashed', + # linewidth=2) + self.setPlotDict(n, (station, channel, network)) + xlabel = 'seconds since {0}'.format(wfstart) + ylabel = '' + #self.updateWidget(xlabel, ylabel, title) + # self.setXLims([0, wfend - wfstart]) + # self.setYLims([-0.5, nmax + 0.5]) + # if zoomx is not None: + # self.setXLims(zoomx) + # if zoomy is not None: + # self.setYLims(zoomy) + # self.draw() + + # def getAxes(self): + # return self.axes + + # def getXLims(self): + # return self.getAxes().get_xlim() + + # def getYLims(self): + # return self.getAxes().get_ylim() + + # def setXLims(self, lims): + # self.getAxes().set_xlim(lims) + + # def setYLims(self, lims): + # self.getAxes().set_ylim(lims) + + def setYTickLabels(self, pos, labels): + ticks = zip(pos, labels) + leftAx = self.plot.getPlotItem().axes['left']['item'] + # leftAx.tickLength = 5 + # leftAx.orientation = 'right' + leftAx.setTicks([ticks, []]) + + # def updateXLabel(self, text): + # self.getAxes().set_xlabel(text) + # self.draw() + + # def updateYLabel(self, text): + # self.getAxes().set_ylabel(text) + # self.draw() + + # def updateTitle(self, text): + # self.getAxes().set_title(text) + # self.draw() + + # def updateWidget(self, xlabel, ylabel, title): + # self.updateXLabel(xlabel) + # self.updateYLabel(ylabel) + # self.updateTitle(title) + + # def insertLabel(self, pos, text): + # pos = pos / max(self.getAxes().ylim) + # axann = self.getAxes().annotate(text, xy=(.03, pos), + # xycoords='axes fraction') + # axann.set_bbox(dict(facecolor='lightgrey', alpha=.6)) + def draw(self): + pass + + class WaveformWidget(FigureCanvas): def __init__(self, parent=None, xlabel='x', ylabel='y', title='Title'): From 909e2241a7209f6d6608a5c975afc68080ad3e22 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Thu, 8 Jun 2017 14:09:01 +0200 Subject: [PATCH 02/79] [change] working version of pyqtgraph for dataPlot, WIP: fill not working on versions lower 0.9.9 --- QtPyLoT.py | 119 +++++++++++++++++++++++++++---------- pylot/RELEASE-VERSION | 2 +- pylot/core/util/widgets.py | 98 ++++++++++++++---------------- 3 files changed, 135 insertions(+), 84 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index b653bfe8..b47bbbd2 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -1009,10 +1009,7 @@ class MainWindow(QMainWindow): return self.dataPlot @staticmethod - def getWFID(gui_event): - - ycoord = gui_event.ydata - + def getWFID(ycoord): try: statID = int(round(ycoord)) except TypeError as e: @@ -1199,10 +1196,13 @@ class MainWindow(QMainWindow): Connect signals refering to WF-Dataplot (select station, tutor_user, scrolling) ''' if self.pg: - pass + self.connect_pg() else: self.connect_mpl() + def connect_pg(self): + self.poS_id = self.dataPlot.plotWidget.scene().sigMouseClicked.connect(self.pickOnStation) + def connect_mpl(self): if not self.poS_id: self.poS_id = self.dataPlot.mpl_connect('button_press_event', @@ -1220,10 +1220,14 @@ class MainWindow(QMainWindow): Disconnect all signals refering to WF-Dataplot (select station, tutor_user, scrolling) ''' if self.pg: - pass + self.disconnect_pg() else: self.disconnect_mpl() + def disconnect_pg(self): + if self.poS_id: + self.dataPlot.plotWidget.scene().sigMouseClicked.disconnect(self.poS_id) + def disconnect_mpl(self): if self.poS_id: self.dataPlot.mpl_disconnect(self.poS_id) @@ -1236,6 +1240,8 @@ class MainWindow(QMainWindow): self.scroll_id = None def finishWaveformDataPlot(self): + if pg: + self.getPlotWidget().updateWidget() self.connectWFplotEvents() self.loadlocationaction.setEnabled(True) self.auto_tune.setEnabled(True) @@ -1260,7 +1266,7 @@ class MainWindow(QMainWindow): def clearWaveformDataPlot(self): self.disconnectWFplotEvents() if self.pg: - self.dataPlot.plotitem.clear() + self.dataPlot.plotWidgetitem.clear() else: self.dataPlot.getAxes().cla() self.loadlocationaction.setEnabled(False) @@ -1313,6 +1319,8 @@ class MainWindow(QMainWindow): #self._max_xlims = self.dataPlot.getXLims() def adjustPlotHeight(self): + if self.pg: + return height_need = len(self.data.getWFData())*self.height_factor plotWidget = self.getPlotWidget() if self.tabs.widget(0).frameSize().height() < height_need: @@ -1408,7 +1416,9 @@ class MainWindow(QMainWindow): return self.seismicPhase def getStationName(self, wfID): - return self.getPlotWidget().getPlotDict()[wfID][0] + plot_dict = self.getPlotWidget().getPlotDict() + if wfID in plot_dict.keys(): + return plot_dict[wfID][0] def alterPhase(self): pass @@ -1455,14 +1465,25 @@ class MainWindow(QMainWindow): self.dataPlot.draw() def pickOnStation(self, gui_event): - if not gui_event.button == 1: - return - - wfID = self.getWFID(gui_event) + if self.pg: + if not gui_event.button() == 1: + return + else: + if not gui_event.button == 1: + return + + if self.pg: + ycoord = self.dataPlot.plotWidget.getPlotItem().vb.mapSceneToView(gui_event.scenePos()).y() + else: + ycoord = gui_event.ydata + + wfID = self.getWFID(ycoord) if wfID is None: return station = self.getStationName(wfID) + if not station: + return self.update_status('picking on station {0}'.format(station)) data = self.get_data().getWFData() pickDlg = PickDlg(self, parameter=self._inputs, @@ -1629,12 +1650,23 @@ class MainWindow(QMainWindow): plotID = self.getStationID(station) if plotID is None: return - ax = self.getPlotWidget().axes + if pg: + pw = self.getPlotWidget().plotWidget + else: + ax = self.getPlotWidget().axes ylims = np.array([-.5, +.5]) + plotID - phase_col = { - 'P': ('c', 'c--', 'b-', 'bv', 'b^', 'b'), - 'S': ('m', 'm--', 'r-', 'rv', 'r^', 'r') - } + if pg: + dashed = QtCore.Qt.DashLine + dotted = QtCore.Qt.DotLine + phase_col = { + 'P': (pg.mkPen('c'), pg.mkPen((0, 255, 255, 100), style=dashed), pg.mkPen('b', style=dashed), pg.mkPen('b', style=dotted)), + 'S': (pg.mkPen('m'), pg.mkPen((255, 0, 255, 100), style=dashed), pg.mkPen('r', style=dashed), pg.mkPen('r', style=dotted)) + } + else: + phase_col = { + 'P': ('c', 'c--', 'b-', 'bv', 'b^', 'b'), + 'S': ('m', 'm--', 'r-', 'rv', 'r^', 'r') + } stat_picks = self.getPicks(type=picktype)[station] @@ -1655,22 +1687,47 @@ class MainWindow(QMainWindow): if not spe and epp and lpp: spe = symmetrize_error(mpp - epp, lpp - mpp) - if picktype == 'manual': - if picks['epp'] and picks['lpp']: - ax.fill_between([epp, lpp], ylims[0], ylims[1], - alpha=.25, color=colors[0], label='EPP, LPP') - if spe: - ax.plot([mpp - spe, mpp - spe], ylims, colors[1], label='{}-SPE'.format(phase)) - ax.plot([mpp + spe, mpp + spe], ylims, colors[1]) - ax.plot([mpp, mpp], ylims, colors[2], label='{}-Pick'.format(phase)) + if pg: + if picktype == 'manual': + if picks['epp'] and picks['lpp']: + pw.plot([epp, epp], ylims, + alpha=.25, pen=colors[0], name='EPP') + pw.plot([lpp, lpp], ylims, + alpha=.25, pen=colors[0], name='LPP') + if spe: + spe_l = pg.PlotDataItem([mpp - spe, mpp - spe], ylims, pen=colors[1], name='{}-SPE'.format(phase)) + spe_r = pg.PlotDataItem([mpp + spe, mpp + spe], ylims, pen=colors[1]) + pw.addItem(spe_l) + pw.addItem(spe_r) + try: + fill = pg.FillBetweenItem(spe_l, spe_r, brush=colors[1].brush()) + fb = pw.addItem(fill) + except: + print('Could not create fill for SPE.') + pw.plot([mpp, mpp], ylims, pen=colors[2], name='{}-Pick'.format(phase)) + else: + pw.plot([mpp, mpp], ylims, pen=colors[0], name='{}-Pick (NO PICKERROR)'.format(phase)) + elif picktype == 'auto': + pw.plot([mpp, mpp], ylims, pen=colors[3]) else: - ax.plot([mpp, mpp], ylims, colors[6], label='{}-Pick (NO PICKERROR)'.format(phase)) - elif picktype == 'auto': - ax.plot(mpp, ylims[1], colors[3], - mpp, ylims[0], colors[4]) - ax.vlines(mpp, ylims[0], ylims[1], colors[5], linestyles='dotted') + raise TypeError('Unknown picktype {0}'.format(picktype)) else: - raise TypeError('Unknown picktype {0}'.format(picktype)) + if picktype == 'manual': + if picks['epp'] and picks['lpp']: + ax.fill_between([epp, lpp], ylims[0], ylims[1], + alpha=.25, color=colors[0], label='EPP, LPP') + if spe: + ax.plot([mpp - spe, mpp - spe], ylims, colors[1], label='{}-SPE'.format(phase)) + ax.plot([mpp + spe, mpp + spe], ylims, colors[1]) + ax.plot([mpp, mpp], ylims, colors[2], label='{}-Pick'.format(phase)) + else: + ax.plot([mpp, mpp], ylims, colors[6], label='{}-Pick (NO PICKERROR)'.format(phase)) + elif picktype == 'auto': + ax.plot(mpp, ylims[1], colors[3], + mpp, ylims[0], colors[4]) + ax.vlines(mpp, ylims[0], ylims[1], colors[5], linestyles='dotted') + else: + raise TypeError('Unknown picktype {0}'.format(picktype)) def locate_event(self): """ diff --git a/pylot/RELEASE-VERSION b/pylot/RELEASE-VERSION index b603f317..47c1eedd 100644 --- a/pylot/RELEASE-VERSION +++ b/pylot/RELEASE-VERSION @@ -1 +1 @@ -be8035-dirty +a071-dirty diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index 68b637dd..c0b285eb 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -414,26 +414,23 @@ class WaveformWidgetPG(QtGui.QWidget): # create plot self.main_layout = QtGui.QVBoxLayout() self.setLayout(self.main_layout) - #self.win = pg.GraphicsWindow(title="Window") - self.plot = pg.PlotWidget(title=title) - self.main_layout.addWidget(self.plot) - #self.plot = self.win.addPlot(title=title) - self.plot.setMouseEnabled(False, False) - self.plot.showGrid(x=False, y=True, alpha=0.2) - # update labels of the entire widget - #self.updateWidget(xlabel, ylabel, title) + self.plotWidget = pg.PlotWidget(title=title, autoDownsample=True) + self.main_layout.addWidget(self.plotWidget) + #self.plotWidget.setMouseEnabled(False, False) + self.plotWidget.showGrid(x=False, y=True, alpha=0.2) + self._proxy = pg.SignalProxy(self.plotWidget.scene().sigMouseMoved, rateLimit=60, slot=self.mouseMoved) + def reinitMoveProxy(self): self.vLine = pg.InfiniteLine(angle=90, movable=False) self.hLine = pg.InfiniteLine(angle=0, movable=False) - self.plot.addItem(self.vLine, ignoreBounds=True) - self.plot.addItem(self.hLine, ignoreBounds=True) - self._proxy = pg.SignalProxy(self.plot.scene().sigMouseMoved, rateLimit=60, slot=self.mouseMoved) - + self.plotWidget.addItem(self.vLine, ignoreBounds=True) + self.plotWidget.addItem(self.hLine, ignoreBounds=True) + def mouseMoved(self, evt): pos = evt[0] ## using signal proxy turns original arguments into a tuple - if self.plot.sceneBoundingRect().contains(pos): - mousePoint = self.plot.getPlotItem().vb.mapSceneToView(pos) - index = int(mousePoint.x()) + if self.plotWidget.sceneBoundingRect().contains(pos): + mousePoint = self.plotWidget.getPlotItem().vb.mapSceneToView(pos) + # index = int(mousePoint.x()) # if index > 0 and index < len(data1): # label.setText("x=%0.1f, y1=%0.1f, y2=%0.1f" % (mousePoint.x(), data1[index], data2[index])) self.vLine.setPos(mousePoint.x()) @@ -457,7 +454,8 @@ class WaveformWidgetPG(QtGui.QWidget): def plotWFData(self, wfdata, title=None, zoomx=None, zoomy=None, noiselevel=None, scaleddata=False, mapping=True, component='*', nth_sample=1, iniPick=None): - #self.getAxes().cla() + self.title = title + self.plotWidget.getPlotItem().clear() self.clearPlotDict() wfstart, wfend = full_range(wfdata) nmax = 0 @@ -501,10 +499,10 @@ class WaveformWidgetPG(QtGui.QWidget): trace.normalize(np.max(np.abs(trace.data)) * 2) times = [time for index, time in enumerate(time_ax) if not index%nth_sample] data = [datum + n for index, datum in enumerate(trace.data) if not index%nth_sample] - self.plot.plot(times, data, pen=(0, 0, 0)) + self.plotWidget.plot(times, data, pen=(0, 0, 0)) if noiselevel is not None: for level in noiselevel: - self.plot.plot([time_ax[0], time_ax[-1]], + self.plotWidget.plot([time_ax[0], time_ax[-1]], [level, level], pen=(0, 0, 0)) # if iniPick: # ax = self.getAxes() @@ -512,16 +510,12 @@ class WaveformWidgetPG(QtGui.QWidget): # colors='m', linestyles='dashed', # linewidth=2) self.setPlotDict(n, (station, channel, network)) - xlabel = 'seconds since {0}'.format(wfstart) - ylabel = '' - #self.updateWidget(xlabel, ylabel, title) - # self.setXLims([0, wfend - wfstart]) - # self.setYLims([-0.5, nmax + 0.5]) - # if zoomx is not None: - # self.setXLims(zoomx) - # if zoomy is not None: - # self.setYLims(zoomy) - # self.draw() + self.reinitMoveProxy() + self.xlabel = 'seconds since {0}'.format(wfstart) + self.ylabel = '' + self.setXLims([0, wfend - wfstart]) + self.setYLims([-0.5, nmax + 0.5]) + self.draw() # def getAxes(self): # return self.axes @@ -532,41 +526,41 @@ class WaveformWidgetPG(QtGui.QWidget): # def getYLims(self): # return self.getAxes().get_ylim() - # def setXLims(self, lims): - # self.getAxes().set_xlim(lims) + def setXLims(self, lims): + vb = self.plotWidget.getPlotItem().getViewBox() + vb.setXRange(lims[0], lims[1], padding=0) - # def setYLims(self, lims): - # self.getAxes().set_ylim(lims) + def setYLims(self, lims): + vb = self.plotWidget.getPlotItem().getViewBox() + vb.setYRange(lims[0], lims[1], padding=0) def setYTickLabels(self, pos, labels): ticks = zip(pos, labels) - leftAx = self.plot.getPlotItem().axes['left']['item'] + minorTicks = [(0, 0) for item in labels] # leftAx.tickLength = 5 # leftAx.orientation = 'right' - leftAx.setTicks([ticks, []]) + self.getAxItem('left').setTicks([ticks, minorTicks]) - # def updateXLabel(self, text): - # self.getAxes().set_xlabel(text) - # self.draw() + def updateXLabel(self, text): + self.getAxItem('bottom').setLabel(text) + self.draw() - # def updateYLabel(self, text): - # self.getAxes().set_ylabel(text) - # self.draw() + def updateYLabel(self, text): + self.getAxItem('left').setLabel(text) + self.draw() - # def updateTitle(self, text): - # self.getAxes().set_title(text) - # self.draw() + def getAxItem(self, position): + return self.plotWidget.getPlotItem().axes[position]['item'] - # def updateWidget(self, xlabel, ylabel, title): - # self.updateXLabel(xlabel) - # self.updateYLabel(ylabel) - # self.updateTitle(title) + def updateTitle(self, text): + self.plotWidget.getPlotItem().setTitle(text) + self.draw() + + def updateWidget(self):#, xlabel, ylabel, title): + self.updateXLabel(self.xlabel) + self.updateYLabel(self.ylabel) + self.updateTitle(self.title) - # def insertLabel(self, pos, text): - # pos = pos / max(self.getAxes().ylim) - # axann = self.getAxes().annotate(text, xy=(.03, pos), - # xycoords='axes fraction') - # axann.set_bbox(dict(facecolor='lightgrey', alpha=.6)) def draw(self): pass From d6dd8bfaa8ffec0100865fb5414aeb14b63aa3bb Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Thu, 8 Jun 2017 16:14:37 +0200 Subject: [PATCH 03/79] [bugfix] several bugfixes mostly refering to plotting from plotWaveformDataThread, now only plot data are generated from within thread --- QtPyLoT.py | 58 +++++++++++++++++++++----------------- pylot/RELEASE-VERSION | 2 +- pylot/core/util/widgets.py | 18 ++++-------- 3 files changed, 38 insertions(+), 40 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index b47bbbd2..cc00fc74 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -1201,7 +1201,7 @@ class MainWindow(QMainWindow): self.connect_mpl() def connect_pg(self): - self.poS_id = self.dataPlot.plotWidget.scene().sigMouseClicked.connect(self.pickOnStation) + self.poS_id = self.dataPlot.plotWidget.scene().sigMouseClicked.connect(self.pickOnStation) def connect_mpl(self): if not self.poS_id: @@ -1226,7 +1226,7 @@ class MainWindow(QMainWindow): def disconnect_pg(self): if self.poS_id: - self.dataPlot.plotWidget.scene().sigMouseClicked.disconnect(self.poS_id) + self.dataPlot.plotWidget.scene().sigMouseClicked.disconnect() def disconnect_mpl(self): if self.poS_id: @@ -1240,8 +1240,22 @@ class MainWindow(QMainWindow): self.scroll_id = None def finishWaveformDataPlot(self): - if pg: - self.getPlotWidget().updateWidget() + if self.pg: + self.getPlotWidget().updateWidget() + plots = self.wfp_thread.data + for times, data in plots: + self.dataPlot.plotWidget.getPlotItem().plot(times, data, pen='k') + self.dataPlot.reinitMoveProxy() + plotWidget = self.getPlotWidget() + plotDict = plotWidget.getPlotDict() + pos = plotDict.keys() + labels = [plotDict[n][2]+'.'+plotDict[n][0] for n in pos] + plotWidget.setYTickLabels(pos, labels) + try: + plotWidget.figure.tight_layout() + except: + pass + #self._max_xlims = self.dataPlot.getXLims() self.connectWFplotEvents() self.loadlocationaction.setEnabled(True) self.auto_tune.setEnabled(True) @@ -1266,7 +1280,7 @@ class MainWindow(QMainWindow): def clearWaveformDataPlot(self): self.disconnectWFplotEvents() if self.pg: - self.dataPlot.plotWidgetitem.clear() + self.dataPlot.plotWidget.getPlotItem().clear() else: self.dataPlot.getAxes().cla() self.loadlocationaction.setEnabled(False) @@ -1285,11 +1299,11 @@ class MainWindow(QMainWindow): ''' Open a modal thread to plot current waveform data. ''' - #self.plotWaveformData() - wfp_thread = Thread(self, self.plotWaveformData, - progressText='Plotting waveform data...') - wfp_thread.finished.connect(self.finishWaveformDataPlot) - wfp_thread.start() + self.clearWaveformDataPlot() + self.wfp_thread = Thread(self, self.plotWaveformData, + progressText='Plotting waveform data...') + self.wfp_thread.finished.connect(self.finishWaveformDataPlot) + self.wfp_thread.start() def plotWaveformData(self): ''' @@ -1307,16 +1321,8 @@ class MainWindow(QMainWindow): # wfst += self.get_data().getWFData().select(component=alter_comp) plotWidget = self.getPlotWidget() self.adjustPlotHeight() - plotWidget.plotWFData(wfdata=wfst, title=title, mapping=False, component=comp, nth_sample=int(nth_sample)) - plotDict = plotWidget.getPlotDict() - pos = plotDict.keys() - labels = [plotDict[n][2]+'.'+plotDict[n][0] for n in pos] - plotWidget.setYTickLabels(pos, labels) - try: - plotWidget.figure.tight_layout() - except: - pass - #self._max_xlims = self.dataPlot.getXLims() + plots = plotWidget.plotWFData(wfdata=wfst, title=title, mapping=False, component=comp, nth_sample=int(nth_sample)) + return plots def adjustPlotHeight(self): if self.pg: @@ -1331,20 +1337,20 @@ class MainWindow(QMainWindow): def plotZ(self): self.setComponent('Z') self.plotWaveformDataThread() - self.drawPicks() - self.draw() + # self.drawPicks() + # self.draw() def plotN(self): self.setComponent('N') self.plotWaveformDataThread() - self.drawPicks() - self.draw() + # self.drawPicks() + # self.draw() def plotE(self): self.setComponent('E') self.plotWaveformDataThread() - self.drawPicks() - self.draw() + # self.drawPicks() + # self.draw() def pushFilterWF(self, param_args): self.get_data().filterWFData(param_args) diff --git a/pylot/RELEASE-VERSION b/pylot/RELEASE-VERSION index 47c1eedd..df648b9f 100644 --- a/pylot/RELEASE-VERSION +++ b/pylot/RELEASE-VERSION @@ -1 +1 @@ -a071-dirty +909e-dirty diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index c0b285eb..bb4bbbe8 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -418,6 +418,7 @@ class WaveformWidgetPG(QtGui.QWidget): self.main_layout.addWidget(self.plotWidget) #self.plotWidget.setMouseEnabled(False, False) self.plotWidget.showGrid(x=False, y=True, alpha=0.2) + self.reinitMoveProxy() self._proxy = pg.SignalProxy(self.plotWidget.scene().sigMouseMoved, rateLimit=60, slot=self.mouseMoved) def reinitMoveProxy(self): @@ -455,7 +456,7 @@ class WaveformWidgetPG(QtGui.QWidget): noiselevel=None, scaleddata=False, mapping=True, component='*', nth_sample=1, iniPick=None): self.title = title - self.plotWidget.getPlotItem().clear() + #self.plotWidget.clear() self.clearPlotDict() wfstart, wfend = full_range(wfdata) nmax = 0 @@ -479,6 +480,7 @@ class WaveformWidgetPG(QtGui.QWidget): nsc.append((trace.stats.network, trace.stats.station, trace.stats.channel)) nsc.sort() nsc.reverse() + plots = [] for n, (network, station, channel) in enumerate(nsc): st = wfdata.select(network=network, station=station, channel=channel) @@ -499,23 +501,13 @@ class WaveformWidgetPG(QtGui.QWidget): trace.normalize(np.max(np.abs(trace.data)) * 2) times = [time for index, time in enumerate(time_ax) if not index%nth_sample] data = [datum + n for index, datum in enumerate(trace.data) if not index%nth_sample] - self.plotWidget.plot(times, data, pen=(0, 0, 0)) - if noiselevel is not None: - for level in noiselevel: - self.plotWidget.plot([time_ax[0], time_ax[-1]], - [level, level], pen=(0, 0, 0)) - # if iniPick: - # ax = self.getAxes() - # ax.vlines(iniPick, ax.get_ylim()[0], ax.get_ylim()[1], - # colors='m', linestyles='dashed', - # linewidth=2) + plots.append((times, data)) self.setPlotDict(n, (station, channel, network)) - self.reinitMoveProxy() self.xlabel = 'seconds since {0}'.format(wfstart) self.ylabel = '' self.setXLims([0, wfend - wfstart]) self.setYLims([-0.5, nmax + 0.5]) - self.draw() + return plots # def getAxes(self): # return self.axes From b061c81b2cc7de4b2e90a8da9d3d8f19f9bad661 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Thu, 8 Jun 2017 16:58:44 +0200 Subject: [PATCH 04/79] [change] removed axes on startup --- QtPyLoT.py | 22 ++++++++++++++-------- pylot/RELEASE-VERSION | 2 +- pylot/core/util/widgets.py | 23 ++++++++++++++++++----- 3 files changed, 33 insertions(+), 14 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index cc00fc74..16039abd 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -214,7 +214,7 @@ class MainWindow(QMainWindow): self.tabs.currentChanged.connect(self.refreshTabs) # create central matplotlib figure canvas widget - plottitle = "Overview: {0} components ".format(self.getComponent()) + plottitle = None#"Overview: {0} components ".format(self.getComponent()) if pg: self.pg = True self.dataPlot = WaveformWidgetPG(parent=self, xlabel=xlab, ylabel=None, @@ -1239,13 +1239,20 @@ class MainWindow(QMainWindow): self.ae_id = None self.scroll_id = None + def finish_pg_plot(self): + self.getPlotWidget().updateWidget() + plots = self.wfp_thread.data + for times, data in plots: + self.dataPlot.plotWidget.getPlotItem().plot(times, data, pen='k') + self.dataPlot.reinitMoveProxy() + self.dataPlot.plotWidget.showAxis('left') + self.dataPlot.plotWidget.showAxis('bottom') + def finishWaveformDataPlot(self): if self.pg: - self.getPlotWidget().updateWidget() - plots = self.wfp_thread.data - for times, data in plots: - self.dataPlot.plotWidget.getPlotItem().plot(times, data, pen='k') - self.dataPlot.reinitMoveProxy() + self.finish_pg_plot() + else: + self._max_xlims = self.dataPlot.getXLims() plotWidget = self.getPlotWidget() plotDict = plotWidget.getPlotDict() pos = plotDict.keys() @@ -1255,7 +1262,6 @@ class MainWindow(QMainWindow): plotWidget.figure.tight_layout() except: pass - #self._max_xlims = self.dataPlot.getXLims() self.connectWFplotEvents() self.loadlocationaction.setEnabled(True) self.auto_tune.setEnabled(True) @@ -1709,7 +1715,7 @@ class MainWindow(QMainWindow): fill = pg.FillBetweenItem(spe_l, spe_r, brush=colors[1].brush()) fb = pw.addItem(fill) except: - print('Could not create fill for SPE.') + print('Warning: drawPicks: Could not create fill for symmetric pick error.') pw.plot([mpp, mpp], ylims, pen=colors[2], name='{}-Pick'.format(phase)) else: pw.plot([mpp, mpp], ylims, pen=colors[0], name='{}-Pick (NO PICKERROR)'.format(phase)) diff --git a/pylot/RELEASE-VERSION b/pylot/RELEASE-VERSION index df648b9f..603fa123 100644 --- a/pylot/RELEASE-VERSION +++ b/pylot/RELEASE-VERSION @@ -1 +1 @@ -909e-dirty +d6dd-dirty diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index bb4bbbe8..ee426643 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -413,11 +413,14 @@ class WaveformWidgetPG(QtGui.QWidget): self.plotdict = dict() # create plot self.main_layout = QtGui.QVBoxLayout() + self.label = QtGui.QLabel() self.setLayout(self.main_layout) self.plotWidget = pg.PlotWidget(title=title, autoDownsample=True) self.main_layout.addWidget(self.plotWidget) - #self.plotWidget.setMouseEnabled(False, False) + self.main_layout.addWidget(self.label) self.plotWidget.showGrid(x=False, y=True, alpha=0.2) + self.plotWidget.hideAxis('bottom') + self.plotWidget.hideAxis('left') self.reinitMoveProxy() self._proxy = pg.SignalProxy(self.plotWidget.scene().sigMouseMoved, rateLimit=60, slot=self.mouseMoved) @@ -431,9 +434,12 @@ class WaveformWidgetPG(QtGui.QWidget): pos = evt[0] ## using signal proxy turns original arguments into a tuple if self.plotWidget.sceneBoundingRect().contains(pos): mousePoint = self.plotWidget.getPlotItem().vb.mapSceneToView(pos) - # index = int(mousePoint.x()) - # if index > 0 and index < len(data1): - # label.setText("x=%0.1f, y1=%0.1f, y2=%0.1f" % (mousePoint.x(), data1[index], data2[index])) + x, y, = (mousePoint.x(), mousePoint.y()) + #if x > 0:# and index < len(data1): + wfID = self._parent.getWFID(y) + station = self._parent.getStationName(wfID) + if self._parent.get_current_event(): + self.label.setText("station = {}, t = {} [s]".format(station, x)) self.vLine.setPos(mousePoint.x()) self.hLine.setPos(mousePoint.y()) @@ -456,7 +462,6 @@ class WaveformWidgetPG(QtGui.QWidget): noiselevel=None, scaleddata=False, mapping=True, component='*', nth_sample=1, iniPick=None): self.title = title - #self.plotWidget.clear() self.clearPlotDict() wfstart, wfend = full_range(wfdata) nmax = 0 @@ -482,6 +487,14 @@ class WaveformWidgetPG(QtGui.QWidget): nsc.reverse() plots = [] + try: + self.plotWidget.getPlotItem().vb.setLimits(xMin=wfstart, + xMax=wfend, + yMin=-0.5, + yMax=len(nsc)+0.5) + except: + print('Warning: Could not set zoom limits') + for n, (network, station, channel) in enumerate(nsc): st = wfdata.select(network=network, station=station, channel=channel) trace = st[0] From 8e839df718bd6197e0b6c768bda4de0189da0e07 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Fri, 9 Jun 2017 13:23:16 +0200 Subject: [PATCH 05/79] [add] pyqtgraph now part of Settings object; if installed will be used by default but can be deactivated --- QtPyLoT.py | 51 ++++++++++++++++++++++---------------- pylot/RELEASE-VERSION | 2 +- pylot/core/util/widgets.py | 23 ++++++++++++++--- 3 files changed, 50 insertions(+), 26 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index 16039abd..1136a29d 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -45,6 +45,7 @@ from obspy import UTCDateTime try: import pyqtgraph as pg except: + print('QtPyLoT: Could not import pyqtgraph.') pg = None try: @@ -144,9 +145,6 @@ class MainWindow(QMainWindow): self.dirty = False - # setup UI - self.setupUi() - if settings.value("user/FullName", None) is None: fulluser = QInputDialog.getText(self, "Enter Name:", "Full name") settings.setValue("user/FullName", fulluser) @@ -170,6 +168,9 @@ class MainWindow(QMainWindow): settings.setValue('compclass', SetChannelComponents()) settings.sync() + # setup UI + self.setupUi() + self.filteroptions = {} self.pickDlgs = {} self.picks = {} @@ -189,8 +190,6 @@ class MainWindow(QMainWindow): self.setWindowTitle("PyLoT - do seismic processing the python way") self.setWindowIcon(pylot_icon) - xlab = self.startTime.strftime('seconds since %Y/%m/%d %H:%M:%S (%Z)') - _widget = QWidget() self._main_layout = QVBoxLayout() @@ -213,21 +212,13 @@ class MainWindow(QMainWindow): self._main_layout.addWidget(self.tabs) self.tabs.currentChanged.connect(self.refreshTabs) - # create central matplotlib figure canvas widget - plottitle = None#"Overview: {0} components ".format(self.getComponent()) - if pg: - self.pg = True - self.dataPlot = WaveformWidgetPG(parent=self, xlabel=xlab, ylabel=None, - title=plottitle) - else: - self.pg = False - self.dataPlot = WaveformWidget(parent=self, xlabel=xlab, ylabel=None, - title=plottitle) - self.dataPlot.setCursor(Qt.CrossCursor) - # add scroll area used in case number of traces gets too high self.wf_scroll_area = QtGui.QScrollArea() + # create central matplotlib figure canvas widget + self.pg = pg + self.init_wfWidget() + # init main widgets for main tabs wf_tab = QtGui.QWidget() array_tab = QtGui.QWidget() @@ -247,7 +238,6 @@ class MainWindow(QMainWindow): self.tabs.addTab(events_tab, 'Eventlist') self.wf_layout.addWidget(self.wf_scroll_area) - self.wf_scroll_area.setWidget(self.dataPlot) self.wf_scroll_area.setWidgetResizable(True) self.init_array_tab() self.init_event_table() @@ -521,6 +511,24 @@ class MainWindow(QMainWindow): self.setCentralWidget(_widget) + def init_wfWidget(self): + settings = QSettings() + xlab = self.startTime.strftime('seconds since %Y/%m/%d %H:%M:%S (%Z)') + plottitle = None#"Overview: {0} components ".format(self.getComponent()) + self.disconnectWFplotEvents() + if str(settings.value('pyqtgraphic')) == 'false' or not pg: + self.pg = False + self.dataPlot = WaveformWidget(parent=self, xlabel=xlab, ylabel=None, + title=plottitle) + else: + self.pg = True + self.dataPlot = WaveformWidgetPG(parent=self, xlabel=xlab, ylabel=None, + title=plottitle) + self.dataPlot.setCursor(Qt.CrossCursor) + self.wf_scroll_area.setWidget(self.dataPlot) + if self.get_current_event(): + self.plotWaveformDataThread() + def init_ref_test_buttons(self): ''' Initiate/create buttons for assigning events containing manual picks to reference or test set. @@ -1662,12 +1670,12 @@ class MainWindow(QMainWindow): plotID = self.getStationID(station) if plotID is None: return - if pg: + if self.pg: pw = self.getPlotWidget().plotWidget else: ax = self.getPlotWidget().axes ylims = np.array([-.5, +.5]) + plotID - if pg: + if self.pg: dashed = QtCore.Qt.DashLine dotted = QtCore.Qt.DotLine phase_col = { @@ -1699,7 +1707,7 @@ class MainWindow(QMainWindow): if not spe and epp and lpp: spe = symmetrize_error(mpp - epp, lpp - mpp) - if pg: + if self.pg: if picktype == 'manual': if picks['epp'] and picks['lpp']: pw.plot([epp, epp], ylims, @@ -2252,6 +2260,7 @@ class MainWindow(QMainWindow): self._props = PropertiesDlg(self, infile=self.infile) if self._props.exec_(): + self.init_wfWidget() return def helpHelp(self): diff --git a/pylot/RELEASE-VERSION b/pylot/RELEASE-VERSION index 603fa123..dc5ac6e8 100644 --- a/pylot/RELEASE-VERSION +++ b/pylot/RELEASE-VERSION @@ -1 +1 @@ -d6dd-dirty +b061-dirty diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index ee426643..33f4a79c 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -2373,6 +2373,7 @@ class PropTab(QWidget): def resetValues(self, infile=None): return None + class InputsTab(PropTab): def __init__(self, parent, infile=None): @@ -2484,6 +2485,7 @@ class GraphicsTab(PropTab): def __init__(self, parent=None): super(GraphicsTab, self).__init__(parent) self.init_layout() + self.add_pg_cb() self.add_nth_sample() self.setLayout(self.main_layout) @@ -2498,15 +2500,28 @@ class GraphicsTab(PropTab): self.spinbox_nth_sample = QtGui.QSpinBox() label = QLabel('nth sample') + label.setToolTip('Plot every nth sample (to speed up plotting)') self.spinbox_nth_sample.setMinimum(1) self.spinbox_nth_sample.setMaximum(10e3) self.spinbox_nth_sample.setValue(int(nth_sample)) - label.setToolTip('Plot every nth sample (to speed up plotting)') - self.main_layout.addWidget(label, 0, 0) - self.main_layout.addWidget(self.spinbox_nth_sample, 0, 1) + self.main_layout.addWidget(label, 1, 0) + self.main_layout.addWidget(self.spinbox_nth_sample, 1, 1) + def add_pg_cb(self): + text = {True: 'Use pyqtgraphic library for plotting', + False: 'Cannot use library: pyqtgraphic not found on system'} + label = QLabel('PyQt graphic') + label.setToolTip(text[bool(pg)]) + label.setEnabled(bool(pg)) + self.checkbox_pg = QtGui.QCheckBox() + self.checkbox_pg.setEnabled(bool(pg)) + self.checkbox_pg.setChecked(bool(pg)) + self.main_layout.addWidget(label, 0, 0) + self.main_layout.addWidget(self.checkbox_pg, 0, 1) + def getValues(self): - values = {'nth_sample': self.spinbox_nth_sample.value()} + values = {'nth_sample': self.spinbox_nth_sample.value(), + 'pyqtgraphic': self.checkbox_pg.isChecked()} return values From 01c556562e617431f338ca3e6450ae3fb4e485c2 Mon Sep 17 00:00:00 2001 From: marcel Date: Fri, 9 Jun 2017 13:51:20 +0200 Subject: [PATCH 06/79] [bugfix] newer version of pyqtgraph could not cope with UTCDatetime, now min/max_range for axes working --- pylot/RELEASE-VERSION | 2 +- pylot/core/util/widgets.py | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pylot/RELEASE-VERSION b/pylot/RELEASE-VERSION index dc5ac6e8..9515eb1e 100644 --- a/pylot/RELEASE-VERSION +++ b/pylot/RELEASE-VERSION @@ -1 +1 @@ -b061-dirty +8e83-dirty diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index 33f4a79c..60257c90 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -488,8 +488,8 @@ class WaveformWidgetPG(QtGui.QWidget): plots = [] try: - self.plotWidget.getPlotItem().vb.setLimits(xMin=wfstart, - xMax=wfend, + self.plotWidget.getPlotItem().vb.setLimits(xMin=float(0), + xMax=float(wfend-wfstart), yMin=-0.5, yMax=len(nsc)+0.5) except: @@ -533,11 +533,11 @@ class WaveformWidgetPG(QtGui.QWidget): def setXLims(self, lims): vb = self.plotWidget.getPlotItem().getViewBox() - vb.setXRange(lims[0], lims[1], padding=0) + vb.setXRange(float(lims[0]), float(lims[1]), padding=0) def setYLims(self, lims): vb = self.plotWidget.getPlotItem().getViewBox() - vb.setYRange(lims[0], lims[1], padding=0) + vb.setYRange(float(lims[0]), float(lims[1]), padding=0) def setYTickLabels(self, pos, labels): ticks = zip(pos, labels) From ddedd3ed7a9f32bed87a0c5ca5efd78638474cac Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Mon, 12 Jun 2017 10:42:44 +0200 Subject: [PATCH 07/79] [hotfix] ignoring error when mouseclick signal cannot be disconnected --- QtPyLoT.py | 5 ++++- pylot/RELEASE-VERSION | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index 1136a29d..8e84cccb 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -1234,7 +1234,10 @@ class MainWindow(QMainWindow): def disconnect_pg(self): if self.poS_id: - self.dataPlot.plotWidget.scene().sigMouseClicked.disconnect() + try: + self.dataPlot.plotWidget.scene().sigMouseClicked.disconnect() + except: + pass def disconnect_mpl(self): if self.poS_id: diff --git a/pylot/RELEASE-VERSION b/pylot/RELEASE-VERSION index 9515eb1e..401c7fd7 100644 --- a/pylot/RELEASE-VERSION +++ b/pylot/RELEASE-VERSION @@ -1 +1 @@ -8e83-dirty +a2c7-dirty From 5412de82490f8cf0c20c94c057289c53d5304397 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Mon, 12 Jun 2017 11:34:45 +0200 Subject: [PATCH 08/79] [add] notes can be read from event folders --- QtPyLoT.py | 13 ++++++++++++- pylot/RELEASE-VERSION | 2 +- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index 8e84cccb..9f271d8d 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -2371,9 +2371,20 @@ class Event(object): self.notes = '' self._testEvent = False self._refEvent = False + try: + self.get_notes() + except: + pass + + def get_notes(self): + notesfile = os.path.join(self.path, 'notes.txt') + if os.path.isfile(notesfile): + with open(notesfile) as infile: + text = '[eventInfo: '+str(infile.readlines()[0].split('\n')[0])+']' + self.addNotes(text) def addNotes(self, notes): - self.notes = notes + self.notes = str(notes) def clearNotes(self): self.notes = None diff --git a/pylot/RELEASE-VERSION b/pylot/RELEASE-VERSION index 401c7fd7..6d6e8843 100644 --- a/pylot/RELEASE-VERSION +++ b/pylot/RELEASE-VERSION @@ -1 +1 @@ -a2c7-dirty +dded-dirty From d2d8498739eadf9c9f22136fb34ec1860f05502c Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Mon, 12 Jun 2017 14:36:38 +0200 Subject: [PATCH 09/79] [bugfix] stream oabject overwritten when using alter_comp causing traces to disappear in plot --- pylot/RELEASE-VERSION | 2 +- pylot/core/util/widgets.py | 22 +++++++++++++--------- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/pylot/RELEASE-VERSION b/pylot/RELEASE-VERSION index 6d6e8843..038cd01d 100644 --- a/pylot/RELEASE-VERSION +++ b/pylot/RELEASE-VERSION @@ -1 +1 @@ -dded-dirty +5412-dirty diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index 60257c90..e49f9ca7 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -476,12 +476,14 @@ class WaveformWidgetPG(QtGui.QWidget): alter_comp = compclass.getCompPosition(component) #alter_comp = str(alter_comp[0]) - wfdata = wfdata.select(component=component) - wfdata += wfdata.select(component=alter_comp) - + st_select = wfdata.select(component=component) + st_select += wfdata.select(component=alter_comp) + else: + st_select = wfdata + # list containing tuples of network, station, channel (for sorting) nsc = [] - for trace in wfdata: + for trace in st_select: nsc.append((trace.stats.network, trace.stats.station, trace.stats.channel)) nsc.sort() nsc.reverse() @@ -496,7 +498,7 @@ class WaveformWidgetPG(QtGui.QWidget): print('Warning: Could not set zoom limits') for n, (network, station, channel) in enumerate(nsc): - st = wfdata.select(network=network, station=station, channel=channel) + st = st_select.select(network=network, station=station, channel=channel) trace = st[0] if mapping: comp = channel[-1] @@ -623,18 +625,20 @@ class WaveformWidget(FigureCanvas): alter_comp = compclass.getCompPosition(component) #alter_comp = str(alter_comp[0]) - wfdata = wfdata.select(component=component) - wfdata += wfdata.select(component=alter_comp) + st_select = wfdata.select(component=component) + st_select += wfdata.select(component=alter_comp) + else: + st_select = wfdata # list containing tuples of network, station, channel (for sorting) nsc = [] - for trace in wfdata: + for trace in st_select: nsc.append((trace.stats.network, trace.stats.station, trace.stats.channel)) nsc.sort() nsc.reverse() for n, (network, station, channel) in enumerate(nsc): - st = wfdata.select(network=network, station=station, channel=channel) + st = st_select.select(network=network, station=station, channel=channel) trace = st[0] if mapping: comp = channel[-1] From 754883c5c97e329d656b10dfe9398547eedd9f7a Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Wed, 14 Jun 2017 16:22:26 +0200 Subject: [PATCH 10/79] [refs #204] adding application name/organisation/domain when building app before calling settings --- QtPyLoT.py | 3 +++ pylot/RELEASE-VERSION | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index 9f271d8d..a18a6cb4 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -2461,6 +2461,9 @@ def create_window(): if app is None: app = QApplication(sys.argv) app_created = True + app.setOrganizationName("QtPyLoT"); + app.setOrganizationDomain("rub.de"); + app.setApplicationName("RUB"); app.references = set() #app.references.add(window) #window.show() diff --git a/pylot/RELEASE-VERSION b/pylot/RELEASE-VERSION index 038cd01d..f31df83e 100644 --- a/pylot/RELEASE-VERSION +++ b/pylot/RELEASE-VERSION @@ -1 +1 @@ -5412-dirty +d2d8-dirty From 7257d4315536080b0adfc937baa3e755e201f331 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Mon, 19 Jun 2017 10:23:39 +0200 Subject: [PATCH 11/79] [fixes #205] call replot thread after manual picking --- QtPyLoT.py | 2 +- pylot/RELEASE-VERSION | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index a18a6cb4..71ac5e8b 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -1523,7 +1523,7 @@ class MainWindow(QMainWindow): self.get_current_event().setPick(station, pickDlg.getPicks()) self.enableSaveManualPicksAction() if replot: - self.plotWaveformData() + self.plotWaveformDataThread() self.drawPicks() self.draw() else: diff --git a/pylot/RELEASE-VERSION b/pylot/RELEASE-VERSION index f31df83e..304c35ec 100644 --- a/pylot/RELEASE-VERSION +++ b/pylot/RELEASE-VERSION @@ -1 +1 @@ -d2d8-dirty +7548-dirty From aea77cd49c13812b8dac9638a2c21640a710d73a Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Mon, 19 Jun 2017 10:30:08 +0200 Subject: [PATCH 12/79] [change] removed redundant propmpt because new picks have to be accepted in the first place when closing PickDlg --- QtPyLoT.py | 51 +++++++++++++++++++++++-------------------- pylot/RELEASE-VERSION | 2 +- 2 files changed, 28 insertions(+), 25 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index 71ac5e8b..97c2f11e 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -1626,30 +1626,33 @@ class MainWindow(QMainWindow): self.thread.quit() def addPicks(self, station, picks, type='manual'): - stat_picks = self.getPicksOnStation(station, type) - rval = False - if not stat_picks: - stat_picks = picks - else: - msgBox = QMessageBox(self) - msgBox.setText("The picks for station {0} have been " - "changed.".format(station)) - msgBox.setDetailedText("Old picks:\n" - "{old_picks}\n\n" - "New picks:\n" - "{new_picks}".format(old_picks=stat_picks, - new_picks=picks)) - msgBox.setInformativeText("Do you want to save your changes?") - msgBox.setStandardButtons(QMessageBox.Save | QMessageBox.Cancel) - msgBox.setDefaultButton(QMessageBox.Save) - ret = msgBox.exec_() - if ret == QMessageBox.Save: - stat_picks = picks - rval = True - elif ret == QMessageBox.Cancel: - pass - else: - raise Exception('FATAL: Should never occur!') + # stat_picks = self.getPicksOnStation(station, type) + # rval = False + # if not stat_picks: + # stat_picks = picks + # else: + # msgBox = QMessageBox(self) + # msgBox.setText("The picks for station {0} have been " + # "changed.".format(station)) + # msgBox.setDetailedText("Old picks:\n" + # "{old_picks}\n\n" + # "New picks:\n" + # "{new_picks}".format(old_picks=stat_picks, + # new_picks=picks)) + # msgBox.setInformativeText("Do you want to save your changes?") + # msgBox.setStandardButtons(QMessageBox.Save | QMessageBox.Cancel) + # msgBox.setDefaultButton(QMessageBox.Save) + # ret = msgBox.exec_() + # if ret == QMessageBox.Save: + # stat_picks = picks + # rval = True + # elif ret == QMessageBox.Cancel: + # pass + # else: + # raise Exception('FATAL: Should never occur!') + # MP MP prompt redundant because new picks have to be accepted in the first place closing PickDlg + stat_picks = picks + rval = True self.getPicks(type=type)[station] = stat_picks return rval diff --git a/pylot/RELEASE-VERSION b/pylot/RELEASE-VERSION index 304c35ec..0238003d 100644 --- a/pylot/RELEASE-VERSION +++ b/pylot/RELEASE-VERSION @@ -1 +1 @@ -7548-dirty +7257-dirty From 053caa5cf6727b26bf92ece77351bcd77e29ff9b Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Mon, 19 Jun 2017 10:37:24 +0200 Subject: [PATCH 13/79] [change] replot after manual picking only if necessary (picks changed) --- QtPyLoT.py | 14 ++++++++------ pylot/RELEASE-VERSION | 2 +- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index 97c2f11e..2a82046e 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -1626,8 +1626,14 @@ class MainWindow(QMainWindow): self.thread.quit() def addPicks(self, station, picks, type='manual'): - # stat_picks = self.getPicksOnStation(station, type) - # rval = False + stat_picks = self.getPicksOnStation(station, type) + if not stat_picks: + rval = False + else: + #set picks (ugly syntax?) + self.getPicks(type=type)[station] = picks + rval = True + return rval # if not stat_picks: # stat_picks = picks # else: @@ -1651,10 +1657,6 @@ class MainWindow(QMainWindow): # else: # raise Exception('FATAL: Should never occur!') # MP MP prompt redundant because new picks have to be accepted in the first place closing PickDlg - stat_picks = picks - rval = True - self.getPicks(type=type)[station] = stat_picks - return rval def updatePicks(self, type='manual'): picks = picksdict_from_picks(evt=self.get_data(type).get_evt_data()) diff --git a/pylot/RELEASE-VERSION b/pylot/RELEASE-VERSION index 0238003d..321e9d20 100644 --- a/pylot/RELEASE-VERSION +++ b/pylot/RELEASE-VERSION @@ -1 +1 @@ -7257-dirty +aea7-dirty From 08b9cec6747ea2af28132100e105ce71a7a77262 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Mon, 19 Jun 2017 11:28:11 +0200 Subject: [PATCH 14/79] [refs #206] savepicks XML now opens current event folder by default, also gives default filename [bugfix] could not save XML files with . in name [change] removed user prompt when changing picks on different event (redundant) --- QtPyLoT.py | 61 +++++++++++++++++++++++++------------------ pylot/RELEASE-VERSION | 2 +- 2 files changed, 37 insertions(+), 26 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index 2a82046e..ab7af35d 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -63,7 +63,7 @@ from pylot.core.pick.compare import Comparison from pylot.core.pick.utils import symmetrize_error from pylot.core.io.phases import picksdict_from_picks import pylot.core.loc.nll as nll -from pylot.core.util.defaults import FILTERDEFAULTS, SetChannelComponents +from pylot.core.util.defaults import FILTERDEFAULTS, OUTPUTFORMATS, SetChannelComponents from pylot.core.util.errors import FormatError, DatastructureError, \ OverwriteError, ProcessingError from pylot.core.util.connection import checkurl @@ -650,6 +650,8 @@ class MainWindow(QMainWindow): data[type] += Data(self, evtdata=fname) if not loc: self.updatePicks(type=type) + if self.get_current_event.picks: + self.plotWaveformDataThread() self.drawPicks(picktype=type) self.draw() self.setDirty(True) @@ -732,10 +734,14 @@ class MainWindow(QMainWindow): ''' Return event path of event (type QtPylot.Event) currently selected in eventbox. ''' - if not eventbox: - eventbox = self.eventBox - return str(eventbox.currentText().split('|')[0]).strip() - + return self.get_current_event(eventbox).path + + def get_current_event_name(self, eventbox=None): + ''' + Return event path of event (type QtPylot.Event) currently selected in eventbox. + ''' + return self.get_current_event_path(eventbox).split('/')[-1] + def getLastEvent(self): return self.recentfiles[0] @@ -908,41 +914,46 @@ class MainWindow(QMainWindow): def getSavePath(e): print('warning: {0}'.format(e)) - directory = os.path.realpath(self.getRoot()) + directory = self.get_current_event_path() + eventname = self.get_current_event_name() + filename = 'picks_'+eventname + outpath = os.path.join(directory, filename) file_filter = "QuakeML file (*.xml);;VELEST observation file " \ "format (*.cnv);;NonLinLoc observation file (*.obs)" title = 'Save pick data ...' fname, selected_filter = QFileDialog.getSaveFileName(self, title, - directory, + outpath, file_filter) fbasename, exform = os.path.splitext(fname) - if not exform and selected_filter: + if not exform and selected_filter or not exform in OUTPUTFORMATS: exform = selected_filter.split('*')[1][:-1] - + if not exform in OUTPUTFORMATS: + return fname, exform return fbasename, exform settings = QSettings() fbasename = self.getEventFileName() exform = settings.value('data/exportFormat', 'QUAKEML') - try: - self.get_data().applyEVTData(self.getPicks()) - except OverwriteError: - msgBox = QMessageBox() - msgBox.setText("Picks have been modified!") - msgBox.setInformativeText( - "Do you want to save the changes and overwrite the picks?") - msgBox.setDetailedText(self.get_data().getPicksStr()) - msgBox.setStandardButtons(QMessageBox.Save | QMessageBox.Cancel) - msgBox.setDefaultButton(QMessageBox.Save) - ret = msgBox.exec_() - if ret == QMessageBox.Save: - self.get_data().resetPicks() - return self.saveData() - elif ret == QMessageBox.Cancel: - return False + # try: + # self.get_data().applyEVTData(self.getPicks()) + # except OverwriteError: + # msgBox = QMessageBox() + # msgBox.setText("Picks have been modified!") + # msgBox.setInformativeText( + # "Do you want to save the changes and overwrite the picks?") + # msgBox.setDetailedText(self.get_data().getPicksStr()) + # msgBox.setStandardButtons(QMessageBox.Save | QMessageBox.Cancel) + # msgBox.setDefaultButton(QMessageBox.Save) + # ret = msgBox.exec_() + # if ret == QMessageBox.Save: + # self.get_data().resetPicks() + # return self.saveData() + # elif ret == QMessageBox.Cancel: + # return False + # MP MP changed due to new event structure not uniquely refering to data object try: self.get_data().exportEvent(fbasename, exform) except FormatError as e: diff --git a/pylot/RELEASE-VERSION b/pylot/RELEASE-VERSION index 321e9d20..785fc992 100644 --- a/pylot/RELEASE-VERSION +++ b/pylot/RELEASE-VERSION @@ -1 +1 @@ -aea7-dirty +053c-dirty From 9aba69686df83f4f83d11c216b8af706e679842e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Mon, 19 Jun 2017 11:37:08 +0200 Subject: [PATCH 15/79] Richter magnitude scaling is hard-wired, should not be handled internal. First step to flexible local- magnitude estimation. --- pylot/core/analysis/magnitude.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/pylot/core/analysis/magnitude.py b/pylot/core/analysis/magnitude.py index dd5e2d6b..5e568fbc 100644 --- a/pylot/core/analysis/magnitude.py +++ b/pylot/core/analysis/magnitude.py @@ -17,10 +17,14 @@ from pylot.core.pick.utils import getsignalwin, crossings_nonzero_all, \ from pylot.core.util.utils import common_range, fit_curve def richter_magnitude_scaling(delta): - relation = np.loadtxt(os.path.join(os.path.expanduser('~'), - '.pylot', 'richter_scaling.data')) + distance = np.arange(0, 1000, 10) + richter_scaling = array([1.4, 1.5, 1.7, 1.9, 2.1, 2.3, 2.4, 2.5, 2.6, 2.8, 2.8, 2.9, + 2.9, 3.0, 3.1, 3.1, 3.2, 3.2, 3.3, 3.3, 3.4, 3.4, 3.5, 3.5, + 3.6, 3.7, 3.7, 3.8, 3.8, 3.9, 3.9, 4.0, 4.0, 4.1, 4.2, 4.2, + 4.2, 4.2, 4.3, 4.3, 4.3, 4.4, 4.4, 4.5, 4.6, 4.7, 4.8, 4.9, + 5.1, 5.2, 5.4, 5.5, 5.7]) # prepare spline interpolation to calculate return value - func, params = fit_curve(relation[:, 0], relation[:, 1]) + func, params = fit_curve(distance, richter_scaling) return func(delta, params) From 0717f491aa72a4c02ad554d74433950f26e7cf80 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Mon, 19 Jun 2017 13:54:43 +0200 Subject: [PATCH 16/79] [closes #203] events can be saved as pickle now, not intended to save back event notes to notes.txt file as it is only used for automatically generated event folders --- QtPyLoT.py | 38 ++++++++++++++++++++++++++++++++++++-- pylot/RELEASE-VERSION | 2 +- 2 files changed, 37 insertions(+), 3 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index ab7af35d..52280f6d 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -2392,8 +2392,12 @@ class Event(object): except: pass - def get_notes(self): + def get_notes_path(self): notesfile = os.path.join(self.path, 'notes.txt') + return notesfile + + def get_notes(self): + notesfile = self.get_notes_path() if os.path.isfile(notesfile): with open(notesfile) as infile: text = '[eventInfo: '+str(infile.readlines()[0].split('\n')[0])+']' @@ -2454,8 +2458,38 @@ class Event(object): def getAutopicks(self): return self.autopicks + + def save(self, filename): + ''' + Save PyLoT Event to a file. + Can be loaded by using event.load(filename). + ''' + try: + import cPickle + except ImportError: + import _pickle as cPickle + + try: + outfile = open(filename, 'wb') + cPickle.dump(self, outfile, -1) + except Exception as e: + print('Could not pickle PyLoT event. Reason: {}'.format(e)) + + @staticmethod + def load(filename): + ''' + Load project from filename. + ''' + try: + import cPickle + except ImportError: + import _pickle as cPickle + infile = open(filename, 'rb') + event = cPickle.load(infile) + print('Loaded %s' % filename) + return event + - class getExistingDirectories(QFileDialog): ''' File dialog with possibility to select multiple folders. diff --git a/pylot/RELEASE-VERSION b/pylot/RELEASE-VERSION index 785fc992..5cf22012 100644 --- a/pylot/RELEASE-VERSION +++ b/pylot/RELEASE-VERSION @@ -1 +1 @@ -053c-dirty +9aba-dirty From 1b17842f6327803bd0266506b3b6152f4340a0fc Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Mon, 19 Jun 2017 16:30:39 +0200 Subject: [PATCH 17/79] [fixes #201] test events will be skipped when using TuneAutopicker --- QtPyLoT.py | 12 +++++++++--- pylot/RELEASE-VERSION | 2 +- pylot/core/util/widgets.py | 25 ++++++++++++++++++++++--- 3 files changed, 32 insertions(+), 7 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index 52280f6d..0cd648ca 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -716,6 +716,8 @@ class MainWindow(QMainWindow): ''' if self.dataStructure: directory = self.get_current_event_path(eventbox) + if not directory: + return fnames = [os.path.join(directory, f) for f in os.listdir(directory)] else: raise DatastructureError('not specified') @@ -734,13 +736,17 @@ class MainWindow(QMainWindow): ''' Return event path of event (type QtPylot.Event) currently selected in eventbox. ''' - return self.get_current_event(eventbox).path - + event = self.get_current_event(eventbox) + if event: + return event.path + def get_current_event_name(self, eventbox=None): ''' Return event path of event (type QtPylot.Event) currently selected in eventbox. ''' - return self.get_current_event_path(eventbox).split('/')[-1] + path = self.get_current_event_path(eventbox) + if path: + return path.split('/')[-1] def getLastEvent(self): return self.recentfiles[0] diff --git a/pylot/RELEASE-VERSION b/pylot/RELEASE-VERSION index 5cf22012..9bcd299b 100644 --- a/pylot/RELEASE-VERSION +++ b/pylot/RELEASE-VERSION @@ -1 +1 @@ -9aba-dirty +0717-dirty diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index e49f9ca7..46984de9 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -1610,7 +1610,6 @@ class TuneAutopicker(QWidget): self.eventBox = self.parent.createEventBox() self.eventBox.setMaxVisibleItems(20) self.fill_eventbox() - self.eventBox.setCurrentIndex(0) self.trace_layout.addWidget(self.eventBox) def init_stationlist(self): @@ -1712,6 +1711,9 @@ class TuneAutopicker(QWidget): return widget def gen_pick_dlg(self): + if not self.get_current_event(): + self.pickDlg = None + return station = self.get_current_station() data = self.data.getWFData() pickDlg = PickDlg(self, data=data.select(station=station), @@ -1800,7 +1802,7 @@ class TuneAutopicker(QWidget): id1 = self.figure_tabs.insertTab(1, self.overview, 'Overview') id2 = self.figure_tabs.insertTab(2, self.p_tabs, 'P') id3 = self.figure_tabs.insertTab(3, self.s_tabs, 'S') - if picked: + if picked and self.get_current_event(): self.fill_p_tabs(canvas_dict) self.fill_s_tabs(canvas_dict) self.toggle_autopickTabs(bool(self.fig_dict['mainFig'].axes)) @@ -1839,7 +1841,23 @@ class TuneAutopicker(QWidget): self.init_tab_names() def fill_eventbox(self): + # update own list self.parent.fill_eventbox(eventBox=self.eventBox, select_events='ref') + index_start = self.eventBox.currentIndex() + index = index_start + if index == -1: + index += 1 + nevents = self.eventBox.model().rowCount() + if self.eventBox.itemData(index).isTestEvent(): + for index in range(nevents): + if not self.eventBox.itemData(index).isTestEvent(): + break + elif index == nevents - 1: + index = -1 + self.eventBox.setCurrentIndex(index) + if not index == index_start: + self.eventBox.activated.emit(index) + # update parent self.parent.fill_eventbox() def update_eventID(self): @@ -1910,7 +1928,8 @@ class TuneAutopicker(QWidget): def clear_all(self): if hasattr(self, 'pickDlg'): - self.pickDlg.setParent(None) + if self.pickDlg: + self.pickDlg.setParent(None) del(self.pickDlg) if hasattr(self, 'overview'): self.overview.setParent(None) From 2601ec2865aeef675389afec406c93e9643a819e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Mon, 19 Jun 2017 16:35:51 +0200 Subject: [PATCH 18/79] Bugfix: Empty polyfit-object is avoided. --- pylot/core/pick/picker.py | 39 ++++++++++++++++++++------------------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/pylot/core/pick/picker.py b/pylot/core/pick/picker.py index c0b0eedb..3c27e221 100644 --- a/pylot/core/pick/picker.py +++ b/pylot/core/pick/picker.py @@ -223,28 +223,29 @@ class AICPicker(AutoPicker): # find maximum within slope determination window # 'cause slope should be calculated up to first local minimum only! imax = np.argmax(self.Data[0].data[islope]) - if imax == 0: - print('AICPicker: Maximum for slope determination right at the beginning of the window!') - print('Choose longer slope determination window!') - if self.iplot > 1: - if not self.fig: - fig = plt.figure() #self.iplot) ### WHY? MP MP - else: - fig = self.fig - ax = fig.add_subplot(111) - x = self.Data[0].data - ax.plot(self.Tcf, x / max(x), 'k', legend='(HOS-/AR-) Data') - ax.plot(self.Tcf, aicsmooth / max(aicsmooth), 'r', legend='Smoothed AIC-CF') - ax.legend() - ax.set_xlabel('Time [s] since %s' % self.Data[0].stats.starttime) - ax.set_yticks([]) - ax.set_title(self.Data[0].stats.station) - return - iislope = islope[0][0:imax] - if len(iislope) <= 3: + if len(iislope) <= 2: # calculate slope from initial onset to maximum of AIC function + print("AICPicker: Not enough data samples left for slope calculation!") + print("Calculating slope from initial onset to maximum of AIC function ...") imax = np.argmax(aicsmooth[islope]) + if imax == 0: + print("AICPicker: Maximum for slope determination right at the beginning of the window!") + print("Choose longer slope determination window!") + if self.iplot > 1: + if not self.fig: + fig = plt.figure() #self.iplot) ### WHY? MP MP + else: + fig = self.fig + ax = fig.add_subplot(111) + x = self.Data[0].data + ax.plot(self.Tcf, x / max(x), 'k', legend='(HOS-/AR-) Data') + ax.plot(self.Tcf, aicsmooth / max(aicsmooth), 'r', legend='Smoothed AIC-CF') + ax.legend() + ax.set_xlabel('Time [s] since %s' % self.Data[0].stats.starttime) + ax.set_yticks([]) + ax.set_title(self.Data[0].stats.station) + return iislope = islope[0][0:imax] dataslope = self.Data[0].data[iislope] # calculate slope as polynomal fit of order 1 From 16003b63539d8115f3df4b4af41bc3a5c71d4b24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Mon, 19 Jun 2017 18:21:38 +0200 Subject: [PATCH 19/79] Bugfix: Different number of elements in used arrays. --- pylot/RELEASE-VERSION | 2 +- pylot/core/analysis/magnitude.py | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/pylot/RELEASE-VERSION b/pylot/RELEASE-VERSION index 9bcd299b..ff6a7e93 100644 --- a/pylot/RELEASE-VERSION +++ b/pylot/RELEASE-VERSION @@ -1 +1 @@ -0717-dirty +2601-dirty diff --git a/pylot/core/analysis/magnitude.py b/pylot/core/analysis/magnitude.py index 5e568fbc..06b540a4 100644 --- a/pylot/core/analysis/magnitude.py +++ b/pylot/core/analysis/magnitude.py @@ -17,8 +17,11 @@ from pylot.core.pick.utils import getsignalwin, crossings_nonzero_all, \ from pylot.core.util.utils import common_range, fit_curve def richter_magnitude_scaling(delta): - distance = np.arange(0, 1000, 10) - richter_scaling = array([1.4, 1.5, 1.7, 1.9, 2.1, 2.3, 2.4, 2.5, 2.6, 2.8, 2.8, 2.9, + distance = np.array([0, 10, 20, 25, 30, 35,40, 45, 50, 60, 70, 75, 85, 90, 100, 110, + 120, 130, 140, 150, 160, 170, 180, 190, 200, 210, 230, 240, 250, + 260, 270, 280, 290, 300, 310, 320, 330, 340, 350, 360, 370, 380, + 390, 400, 430, 470, 510, 560, 600, 700, 800, 900, 1000]) + richter_scaling = np.array([1.4, 1.5, 1.7, 1.9, 2.1, 2.3, 2.4, 2.5, 2.6, 2.8, 2.8, 2.9, 2.9, 3.0, 3.1, 3.1, 3.2, 3.2, 3.3, 3.3, 3.4, 3.4, 3.5, 3.5, 3.6, 3.7, 3.7, 3.8, 3.8, 3.9, 3.9, 4.0, 4.0, 4.1, 4.2, 4.2, 4.2, 4.2, 4.3, 4.3, 4.3, 4.4, 4.4, 4.5, 4.6, 4.7, 4.8, 4.9, From 300ff9215be758592dc8f388692c609111f1bf66 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Tue, 20 Jun 2017 09:54:23 +0200 Subject: [PATCH 20/79] [fixes #210] missing paranthesis in function call --- QtPyLoT.py | 2 +- pylot/RELEASE-VERSION | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index 0cd648ca..7afd2991 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -650,7 +650,7 @@ class MainWindow(QMainWindow): data[type] += Data(self, evtdata=fname) if not loc: self.updatePicks(type=type) - if self.get_current_event.picks: + if self.get_current_event().picks: self.plotWaveformDataThread() self.drawPicks(picktype=type) self.draw() diff --git a/pylot/RELEASE-VERSION b/pylot/RELEASE-VERSION index 9bcd299b..85433583 100644 --- a/pylot/RELEASE-VERSION +++ b/pylot/RELEASE-VERSION @@ -1 +1 @@ -0717-dirty +1b17-dirty From a8544dfb93c2d3ccb3ed54ad6b1ab42192f87c13 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Tue, 20 Jun 2017 10:35:31 +0200 Subject: [PATCH 21/79] [bugfix] save XML does not set project clean --- QtPyLoT.py | 1 - 1 file changed, 1 deletion(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index 7afd2991..4a3bcd07 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -985,7 +985,6 @@ class MainWindow(QMainWindow): # export to given path self.get_data().exportEvent(fbasename, exform) # all files save (ui clean) - self.setDirty(False) self.update_status('Picks saved as %s' % (fbasename + exform)) self.disableSaveManualPicksAction() return True From 62a7d94816da6e50852b79e6d784f47b292b5894 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Tue, 20 Jun 2017 12:10:17 +0200 Subject: [PATCH 22/79] [bugfix] expecting deprecated return parameter of autoPyLoT --- autoPyLoT.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/autoPyLoT.py b/autoPyLoT.py index dc597110..b83f78af 100755 --- a/autoPyLoT.py +++ b/autoPyLoT.py @@ -385,9 +385,5 @@ if __name__ == "__main__": cla = parser.parse_args() - try: - picks, mainFig = autoPyLoT(inputfile=str(cla.inputfile), fnames=str(cla.fnames), - eventid=str(cla.eventid), savepath=str(cla.spath)) - except ValueError: - print("autoPyLoT was running in production mode.") - + picks = autoPyLoT(inputfile=str(cla.inputfile), fnames=str(cla.fnames), + eventid=str(cla.eventid), savepath=str(cla.spath)) From 0ff6f78a6cac1192e21daf4270ee2ed8a8095694 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Tue, 20 Jun 2017 12:10:56 +0200 Subject: [PATCH 23/79] [add] parameter localMag added --- pylot/RELEASE-VERSION | 2 +- pylot/core/io/default_parameters.py | 9 +++++++-- pylot/core/util/widgets.py | 2 ++ 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/pylot/RELEASE-VERSION b/pylot/RELEASE-VERSION index 85433583..7bb5d74d 100644 --- a/pylot/RELEASE-VERSION +++ b/pylot/RELEASE-VERSION @@ -1 +1 @@ -1b17-dirty +a8544-dirty diff --git a/pylot/core/io/default_parameters.py b/pylot/core/io/default_parameters.py index f304c8e0..1ae7caad 100644 --- a/pylot/core/io/default_parameters.py +++ b/pylot/core/io/default_parameters.py @@ -275,7 +275,11 @@ defaults = {'rootpath': {'type': str, 'wdttolerance': {'type': float, 'tooltip': 'maximum allowed deviation from Wadati-diagram', - 'value': 1.0} + 'value': 1.0}, + + 'localMag': {'type': float, + 'tooltip': 'maximum allowed deviation from Wadati-diagram', + 'value': 1.0} } settings_main={ @@ -297,7 +301,8 @@ settings_main={ 'smoment':[ 'vp', 'rho', - 'Qp'], + 'Qp', + 'localMag'], 'pick':[ 'extent', 'pstart', diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index 46984de9..a92a7940 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -2228,6 +2228,8 @@ class AutoPickParaBox(QtGui.QWidget): if type(box) == QtGui.QLineEdit: box.setText(str(value)) elif type(box) == QtGui.QSpinBox or type(box) == QtGui.QDoubleSpinBox: + if not value: + value = 0. box.setValue(value) elif type(box) == QtGui.QCheckBox: if value == 'True': From 0be5c4b694142ff6f25ca810c9dead406a7fae09 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Tue, 20 Jun 2017 13:22:29 +0200 Subject: [PATCH 24/79] [add] verbosity on import pyqtgraph exception --- QtPyLoT.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index 4a3bcd07..ceab88ef 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -44,8 +44,8 @@ from obspy import UTCDateTime try: import pyqtgraph as pg -except: - print('QtPyLoT: Could not import pyqtgraph.') +except Exception as e: + print('QtPyLoT: Could not import pyqtgraph. {}'.format(e)) pg = None try: From ab979e9b1094e8c9475271e639f4a401a4fb432e Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Tue, 20 Jun 2017 14:32:21 +0200 Subject: [PATCH 25/79] [bugfix] events not properly provided to autoPyLoT by TuneAutopicker, wrong parameter name in plotting routine --- autoPyLoT.py | 38 ++++++++++++++++++++++++++------------ pylot/RELEASE-VERSION | 2 +- pylot/core/pick/picker.py | 4 ++-- pylot/core/util/thread.py | 6 ++++-- 4 files changed, 33 insertions(+), 17 deletions(-) diff --git a/autoPyLoT.py b/autoPyLoT.py index b83f78af..0df1e2cb 100755 --- a/autoPyLoT.py +++ b/autoPyLoT.py @@ -130,21 +130,35 @@ def autoPyLoT(input_dict=None, parameter=None, inputfile=None, fnames=None, even print("!!No source parameter estimation possible!!") print(" !!! ") - datapath = datastructure.expandDataPath() - if fnames == 'None' and not parameter.hasParam('eventID'): - # multiple event processing - # read each event in database - events = [events for events in glob.glob(os.path.join(datapath, '*')) if os.path.isdir(events)] - elif fnames == 'None' and parameter.hasParam('eventID'): - # single event processing - events = glob.glob(os.path.join(datapath, parameter.get('eventID'))) + if not input_dict: + # started in production mode + datapath = datastructure.expandDataPath() + if fnames == 'None' and not parameter['eventID']: + # multiple event processing + # read each event in database + events = [events for events in glob.glob(os.path.join(datapath, '*')) if os.path.isdir(events)] + elif fnames == 'None' and parameter['eventID']: + # single event processing + events = glob.glob(os.path.join(datapath, parameter.get('eventID'))) + else: + # autoPyLoT was initialized from GUI + events = [] + events.append(eventid) + evID = os.path.split(eventid)[-1] + locflag = 2 else: - # autoPyLoT was initialized from GUI + # started in tune mode + datapath = os.path.join(parameter['rootpath'], + parameter['datapath']) events = [] - events.append(eventid) - evID = os.path.split(eventid)[-1] - locflag = 2 + events.append(os.path.join(datapath, + parameter['database'], + parameter['eventID'])) + if not events: + print('autoPyLoT: No events given. Return!') + return + for event in events: if fnames == 'None': data.setWFData(glob.glob(os.path.join(datapath, event, '*'))) diff --git a/pylot/RELEASE-VERSION b/pylot/RELEASE-VERSION index 7bb5d74d..2c8398ed 100644 --- a/pylot/RELEASE-VERSION +++ b/pylot/RELEASE-VERSION @@ -1 +1 @@ -a8544-dirty +0be5-dirty diff --git a/pylot/core/pick/picker.py b/pylot/core/pick/picker.py index 3c27e221..a6cd7452 100644 --- a/pylot/core/pick/picker.py +++ b/pylot/core/pick/picker.py @@ -239,8 +239,8 @@ class AICPicker(AutoPicker): fig = self.fig ax = fig.add_subplot(111) x = self.Data[0].data - ax.plot(self.Tcf, x / max(x), 'k', legend='(HOS-/AR-) Data') - ax.plot(self.Tcf, aicsmooth / max(aicsmooth), 'r', legend='Smoothed AIC-CF') + ax.plot(self.Tcf, x / max(x), 'k', label='(HOS-/AR-) Data') + ax.plot(self.Tcf, aicsmooth / max(aicsmooth), 'r', label='Smoothed AIC-CF') ax.legend() ax.set_xlabel('Time [s] since %s' % self.Data[0].stats.starttime) ax.set_yticks([]) diff --git a/pylot/core/util/thread.py b/pylot/core/util/thread.py index 609cf9df..5928ace3 100644 --- a/pylot/core/util/thread.py +++ b/pylot/core/util/thread.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -import sys +import sys, os from PySide.QtCore import QThread, Signal, Qt from PySide.QtGui import QDialog, QProgressBar, QLabel, QHBoxLayout @@ -64,7 +64,9 @@ class Thread(QThread): except Exception as e: self._executed = False self._executedError = e - print(e) + exc_type, exc_obj, exc_tb = sys.exc_info() + fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1] + print('Exception: {}, file: {}, line: {}'.format(exc_type, fname, exc_tb.tb_lineno)) sys.stdout = sys.__stdout__ def __del__(self): From 1b512a0864e2bfe427c390dc39057a2865d31074 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Tue, 20 Jun 2017 16:23:54 +0200 Subject: [PATCH 26/79] [closes #207, closes #208] when supressing user prompt, applyEVTData to data object was accidently removed --- QtPyLoT.py | 12 ++++++------ pylot/RELEASE-VERSION | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index ceab88ef..8e558b5c 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -943,9 +943,9 @@ class MainWindow(QMainWindow): settings = QSettings() fbasename = self.getEventFileName() exform = settings.value('data/exportFormat', 'QUAKEML') - # try: - # self.get_data().applyEVTData(self.getPicks()) - # except OverwriteError: + try: + self.get_data().applyEVTData(self.getPicks()) + except OverwriteError: # msgBox = QMessageBox() # msgBox.setText("Picks have been modified!") # msgBox.setInformativeText( @@ -955,11 +955,11 @@ class MainWindow(QMainWindow): # msgBox.setDefaultButton(QMessageBox.Save) # ret = msgBox.exec_() # if ret == QMessageBox.Save: - # self.get_data().resetPicks() - # return self.saveData() + self.get_data().resetPicks() + return self.saveData() # elif ret == QMessageBox.Cancel: # return False - # MP MP changed due to new event structure not uniquely refering to data object + # MP MP changed to suppress unnecessary user prompt try: self.get_data().exportEvent(fbasename, exform) except FormatError as e: diff --git a/pylot/RELEASE-VERSION b/pylot/RELEASE-VERSION index 2c8398ed..e1085601 100644 --- a/pylot/RELEASE-VERSION +++ b/pylot/RELEASE-VERSION @@ -1 +1 @@ -0be5-dirty +ab97-dirty From 6daccad341e1c21f54433075eafd71f288562aff Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Tue, 20 Jun 2017 16:30:13 +0200 Subject: [PATCH 27/79] [add] testing: saveProjectAs option 'always' enabled when project is active --- QtPyLoT.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index 8e558b5c..f8ec2e4e 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -2196,6 +2196,7 @@ class MainWindow(QMainWindow): self.project.parameter=self._inputs self.project.save(filename) self.setDirty(False) + self.saveProjectAsAction.setEnabled(True) self.update_status('Creating new project...', duration=1000) return True @@ -2260,7 +2261,7 @@ class MainWindow(QMainWindow): def setDirty(self, value): self.saveProjectAction.setEnabled(value) - self.saveProjectAsAction.setEnabled(value) + self.saveProjectAsAction.setEnabled(True) self.project.setDirty(value) self.dirty = value From be463c2adf807b137d0d20a0015c5cf109e6c286 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Wed, 21 Jun 2017 10:36:22 +0200 Subject: [PATCH 28/79] [add] functionality to display one groupbox of AutoPickParaBox exclusively --- pylot/core/util/widgets.py | 62 ++++++++++++++++++++++++++++++-------- 1 file changed, 49 insertions(+), 13 deletions(-) diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index a92a7940..6fa70376 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -1979,7 +1979,8 @@ class AutoPickParaBox(QtGui.QWidget): self.add_special_pick_parameters_tab() self.params_to_gui() self._toggle_advanced_settings() - self.resize(720, 1280) + self.resize(720, 1280) + self.setWindowModality(QtCore.Qt.WindowModality.ApplicationModal) def _init_sublayouts(self): self._main_layout = QtGui.QVBoxLayout() @@ -2107,31 +2108,29 @@ class AutoPickParaBox(QtGui.QWidget): scrollA = QtGui.QScrollArea() scrollA.setWidgetResizable(True) scrollA.setWidget(widget) - widget.setLayout(layout) - self.tabs.addTab(scrollA, name) def add_main_parameters_tab(self): self.add_to_layout(self._main_layout, 'Directories', - self.parameter.get_main_para_names()['dirs']) + self.parameter.get_main_para_names()['dirs'], 0) self.add_to_layout(self._main_layout, 'NLLoc', - self.parameter.get_main_para_names()['nlloc']) + self.parameter.get_main_para_names()['nlloc'], 1) self.add_to_layout(self._main_layout, 'Seismic Moment', - self.parameter.get_main_para_names()['smoment']) + self.parameter.get_main_para_names()['smoment'], 2) self.add_to_layout(self._main_layout, 'Common Settings Characteristic Function', - self.parameter.get_main_para_names()['pick']) + self.parameter.get_main_para_names()['pick'], 3) self.add_tab(self._main_layout, 'Main Settings') def add_special_pick_parameters_tab(self): self.add_to_layout(self._advanced_layout, 'Z-component', - self.parameter.get_special_para_names()['z']) + self.parameter.get_special_para_names()['z'], 0) self.add_to_layout(self._advanced_layout, 'H-components', - self.parameter.get_special_para_names()['h']) + self.parameter.get_special_para_names()['h'], 1) self.add_to_layout(self._advanced_layout, 'First-motion picker', - self.parameter.get_special_para_names()['fm']) + self.parameter.get_special_para_names()['fm'], 2) self.add_to_layout(self._advanced_layout, 'Quality assessment', - self.parameter.get_special_para_names()['quality']) + self.parameter.get_special_para_names()['quality'], 3) self.add_tab(self._advanced_layout, 'Advanced Settings') # def gen_h_seperator(self): @@ -2145,12 +2144,32 @@ class AutoPickParaBox(QtGui.QWidget): # font.setBold(True) # label.setFont(font) # return label + + def refresh(self): + for groupbox in self.groupboxes.values(): + layout = groupbox._parentLayout + position = groupbox._position + layout.insertWidget(position, groupbox) + + def get_groupbox_exclusive(self, name): + widget = QtGui.QWidget(self, 1) + self._exclusive_widget = widget + layout = QtGui.QVBoxLayout() + button = QtGui.QPushButton('Okay') + widget.setLayout(layout) + layout.addWidget(self.groupboxes[name]) + layout.addWidget(button) + button.clicked.connect(widget.close) + button.clicked.connect(self.refresh) + return widget - def add_to_layout(self, layout, name, items): + def add_to_layout(self, layout, name, items, position): groupbox = QtGui.QGroupBox(name) + groupbox._position = position + groupbox._parentLayout = layout self.groupboxes[name] = groupbox groupbox.setLayout(self.init_boxes(items)) - layout.addWidget(groupbox) + layout.insertWidget(position, groupbox) def show_groupboxes(self): for name in self.groupboxes.keys(): @@ -2174,6 +2193,16 @@ class AutoPickParaBox(QtGui.QWidget): else: print('Groupbox {} not part of object.'.format(name)) + def show_file_buttons(self): + self.saveButton.show() + self.loadButton.show() + self.defaultsButton.show() + + def hide_file_buttons(self): + self.saveButton.hide() + self.loadButton.hide() + self.defaultsButton.hide() + def show_parameter(self, name=None): if not name: for name in self.boxes.keys(): @@ -2286,6 +2315,13 @@ class AutoPickParaBox(QtGui.QWidget): except Exception as e: self._warn('Could not restore defaults:\n{}'.format(e)) return + + def show(self): + self.refresh() + self.show_parameter() + if hasattr(self, '_exclusive_widget'): + self._exclusive_widget.close() + QtGui.QWidget.show(self) def _warn(self, message): self.qmb = QtGui.QMessageBox(QtGui.QMessageBox.Icon.Warning, From 0a6ad8b663b01921df59cfcaa44f64eb8028c53d Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Wed, 21 Jun 2017 11:43:01 +0200 Subject: [PATCH 29/79] [add] folder structure automatically recognized when adding events --- QtPyLoT.py | 48 +++++++++++++++++++++++++++++++++----- pylot/RELEASE-VERSION | 2 +- pylot/core/util/widgets.py | 25 ++++++++++++-------- 3 files changed, 58 insertions(+), 17 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index f8ec2e4e..e2dfcf55 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -362,10 +362,10 @@ class MainWindow(QMainWindow): QCoreApplication.instance().quit, QKeySequence.Close, quitIcon, "Close event and quit PyLoT") - self.parameterAction = self.createAction(self, "Pick Parameter", - self.pickParameter, + self.parameterAction = self.createAction(self, "Parameter", + self.setParameter, None, QIcon(None), - "Modify Picking Parameter") + "Modify Parameter") self.filterAction = self.createAction(self, "&Filter ...", self.filterWaveformData, "Ctrl+F", filter_icon, @@ -764,11 +764,46 @@ class MainWindow(QMainWindow): eventlist = [item for item in eventlist if item.split('/')[-1].startswith('e') and len(item.split('/')[-1].split('.')) == 3 and len(item.split('/')[-1]) == 12] + if not eventlist: + print('No events found! Expected structure for event folders: [evID.DOY.YR]') + return else: return if not self.project: print('No project found.') return + + #get path from first event in list and split them + path = eventlist[0] + try: + dirs = { + 'database': path.split('/')[-2], + 'datapath': path.split('/')[-3], + 'rootpath': os.path.join(*path.split('/')[:-3]) + } + except Exception as e: + dirs = { + 'database': '' + 'datapath': '' + 'rootpath': '' + } + print('Warning: Could not automatically init folder structure. ({})'.format(e)) + + if not self.project.eventlist: + #init parameter object + self.setParameter(show=False) + #hide all parameter (show all needed parameter later) + self.paraBox.hide_parameter() + for directory in dirs.keys(): + #set parameter + box = self.paraBox.boxes[directory] + self.paraBox.setValue(box, dirs[directory]) + #show needed parameter in box + self.paraBox.show_parameter(directory) + dirs_box = self.paraBox.get_groupbox_exclusive('Directories') + if not dirs_box.exec_(): + return + self.project.add_eventlist(eventlist) self.init_events() self.setDirty(True) @@ -2273,12 +2308,13 @@ class MainWindow(QMainWindow): # self.closing.emit() # QMainWindow.closeEvent(self, event) - def pickParameter(self): + def setParameter(self, show=True): if not self.paraBox: self.paraBox = AutoPickParaBox(self._inputs) self.paraBox._apply.clicked.connect(self._setDirty) - self.paraBox._okay.clicked.connect(self._setDirty) - self.paraBox.show() + self.paraBox._okay.clicked.connect(self._setDirty) + if show: + self.paraBox.show() def PyLoTprefs(self): if not self._props: diff --git a/pylot/RELEASE-VERSION b/pylot/RELEASE-VERSION index e1085601..d0508819 100644 --- a/pylot/RELEASE-VERSION +++ b/pylot/RELEASE-VERSION @@ -1 +1 @@ -ab97-dirty +be46-dirty diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index 6fa70376..dcef3254 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -2152,16 +2152,21 @@ class AutoPickParaBox(QtGui.QWidget): layout.insertWidget(position, groupbox) def get_groupbox_exclusive(self, name): - widget = QtGui.QWidget(self, 1) - self._exclusive_widget = widget + dialog = QtGui.QDialog(self.parent()) + buttonbox = QtGui.QDialogButtonBox(QDialogButtonBox.Ok | + QDialogButtonBox.Cancel) + self._exclusive_dialog = dialog layout = QtGui.QVBoxLayout() - button = QtGui.QPushButton('Okay') - widget.setLayout(layout) + dialog.setLayout(layout) layout.addWidget(self.groupboxes[name]) - layout.addWidget(button) - button.clicked.connect(widget.close) - button.clicked.connect(self.refresh) - return widget + layout.addWidget(buttonbox) + buttonbox.accepted.connect(dialog.accept) + buttonbox.accepted.connect(self.refresh) + buttonbox.accepted.connect(self.params_from_gui) + buttonbox.rejected.connect(dialog.reject) + buttonbox.rejected.connect(self.refresh) + buttonbox.rejected.connect(self.params_to_gui) + return dialog def add_to_layout(self, layout, name, items, position): groupbox = QtGui.QGroupBox(name) @@ -2319,8 +2324,8 @@ class AutoPickParaBox(QtGui.QWidget): def show(self): self.refresh() self.show_parameter() - if hasattr(self, '_exclusive_widget'): - self._exclusive_widget.close() + if hasattr(self, '_exclusive_dialog'): + self._exclusive_dialog.close() QtGui.QWidget.show(self) def _warn(self, message): From 51be558560dd0543f8e54e9b9934a4233f92469a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Wed, 21 Jun 2017 13:01:14 +0200 Subject: [PATCH 30/79] New parameters added for local magnitude estimation and scaling. --- pylot/core/io/default_parameters.py | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/pylot/core/io/default_parameters.py b/pylot/core/io/default_parameters.py index 1ae7caad..68c09a46 100644 --- a/pylot/core/io/default_parameters.py +++ b/pylot/core/io/default_parameters.py @@ -277,9 +277,13 @@ defaults = {'rootpath': {'type': str, 'tooltip': 'maximum allowed deviation from Wadati-diagram', 'value': 1.0}, - 'localMag': {'type': float, - 'tooltip': 'maximum allowed deviation from Wadati-diagram', - 'value': 1.0} + 'WAscaling': {'type': float, + 'tooltip': 'Scaling relation of Wood-Anderson amplitude [nm]', + 'value': []}, + + 'magscaling': {'type': (float), + 'tooltip': 'Scaling relation for derived local magnitude', + 'value': []} } settings_main={ @@ -301,8 +305,10 @@ settings_main={ 'smoment':[ 'vp', 'rho', - 'Qp', - 'localMag'], + 'Qp'], + 'localmag':[ + 'WAscaling', + 'magscaling'], 'pick':[ 'extent', 'pstart', From 9dc1545b6314415dd1c4a0c3c8be47ee57d8ce15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Wed, 21 Jun 2017 13:03:17 +0200 Subject: [PATCH 31/79] Renamed/added new main parameter sequence Seismic Moment and Local Magnitude. --- pylot/core/util/widgets.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index a92a7940..96bd03ed 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -2119,6 +2119,8 @@ class AutoPickParaBox(QtGui.QWidget): self.parameter.get_main_para_names()['nlloc']) self.add_to_layout(self._main_layout, 'Seismic Moment', self.parameter.get_main_para_names()['smoment']) + self.add_to_layout(self._main_layout, 'Local Magnitude', + self.parameter.get_main_para_names()['localmag']) self.add_to_layout(self._main_layout, 'Common Settings Characteristic Function', self.parameter.get_main_para_names()['pick']) self.add_tab(self._main_layout, 'Main Settings') From 43c314a1f82ff45625b330d777060b7568298f11 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Wed, 21 Jun 2017 13:24:45 +0200 Subject: [PATCH 32/79] [add/change] dataroot added to project file, improvements in behavior of create/save project --- QtPyLoT.py | 83 ++++++++++++++++++++++++++++--------------- pylot/RELEASE-VERSION | 2 +- 2 files changed, 55 insertions(+), 30 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index e2dfcf55..5c0478f1 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -106,7 +106,9 @@ class MainWindow(QMainWindow): self._inputs = AutoPickParameter(infile) self._props = None + self.dirty = False self.project = Project() + self.project.parameter = self._inputs self.tap = None self.paraBox = None self.array_map = None @@ -143,8 +145,6 @@ class MainWindow(QMainWindow): self.data = Data(self) self.autodata = Data(self) - self.dirty = False - if settings.value("user/FullName", None) is None: fulluser = QInputDialog.getText(self, "Enter Name:", "Full name") settings.setValue("user/FullName", fulluser) @@ -756,7 +756,7 @@ class MainWindow(QMainWindow): Creates and adds events by user selection of event folders to GUI. ''' if not self.project: - self.project = Project() + self.createNewProject() ed = getExistingDirectories(self, 'Select event directories...') if ed.exec_(): eventlist = ed.selectedFiles() @@ -783,8 +783,8 @@ class MainWindow(QMainWindow): } except Exception as e: dirs = { - 'database': '' - 'datapath': '' + 'database': '', + 'datapath': '', 'rootpath': '' } print('Warning: Could not automatically init folder structure. ({})'.format(e)) @@ -803,7 +803,16 @@ class MainWindow(QMainWindow): dirs_box = self.paraBox.get_groupbox_exclusive('Directories') if not dirs_box.exec_(): return - + self.project.rootpath = dirs['rootpath'] + else: + if hasattr(self.project, 'rootpath'): + if not self.project.rootpath == dirs['rootpath']: + QMessageBox.warning(self, "PyLoT Warning", + 'Rootpath missmatch to current project!') + return + else: + self.project.rootpath = dirs['rootpath'] + self.project.add_eventlist(eventlist) self.init_events() self.setDirty(True) @@ -1349,6 +1358,8 @@ class MainWindow(QMainWindow): self.disconnectWFplotEvents() if self.pg: self.dataPlot.plotWidget.getPlotItem().clear() + self.dataPlot.plotWidget.hideAxis('bottom') + self.dataPlot.plotWidget.hideAxis('left') else: self.dataPlot.getAxes().cla() self.loadlocationaction.setEnabled(False) @@ -2210,29 +2221,18 @@ class MainWindow(QMainWindow): self.data = Data(self, evtdata=event) self.setDirty(True) - def createNewProject(self, exists=False): + def createNewProject(self): ''' Create new project file. ''' - if not exists: - if not self.okToContinue(): - return - dlg = QFileDialog() - fnm = dlg.getSaveFileName(self, 'Create a new project file...', filter='Pylot project (*.plp)') - filename = fnm[0] - if not len(fnm[0]): - return False - if not filename.split('.')[-1] == 'plp': - filename = fnm[0] + '.plp' - if not exists: - self.project = Project() - self.init_events(new=True) - self.setDirty(True) - self.project.parameter=self._inputs - self.project.save(filename) + if not self.okToContinue(): + return + self.project = Project() + self.init_events(new=True) self.setDirty(False) + self.project.parameter=self._inputs self.saveProjectAsAction.setEnabled(True) - self.update_status('Creating new project...', duration=1000) + self.update_status('Created new project...', duration=1000) return True def loadProject(self, fnm=None): @@ -2261,8 +2261,26 @@ class MainWindow(QMainWindow): return self.init_array_tab() - def saveProjectAs(self): - self.saveProject(new=True) + def saveProjectAs(self, exists=False): + ''' + Save back project to new pickle file. + ''' + if not exists: + if not self.okToContinue(): + return + dlg = QFileDialog() + fnm = dlg.getSaveFileName(self, 'Create a new project file...', filter='Pylot project (*.plp)') + filename = fnm[0] + if not len(fnm[0]): + return False + if not filename.split('.')[-1] == 'plp': + filename = fnm[0] + '.plp' + self.project.parameter=self._inputs + self.project.save(filename) + self.setDirty(False) + self.saveProjectAsAction.setEnabled(True) + self.update_status('Saved new project to {}'.format(filename), duration=5000) + return True def saveProject(self, new=False): ''' @@ -2270,14 +2288,14 @@ class MainWindow(QMainWindow): ''' if self.project and not new: if not self.project.location: - if not self.createNewProject(exists=True): + if not self.saveProjectAs(exists=True): self.setDirty(True) return False else: self.project.parameter=self._inputs self.project.save() if not self.project.dirty: - print('Saved back project to file:\n{}'.format(self.project.location)) + self.update_status('Saved back project to file:\n{}'.format(self.project.location), duration=5000) self.setDirty(False) return True else: @@ -2285,7 +2303,7 @@ class MainWindow(QMainWindow): qmb = QMessageBox.warning(self,'Could not save project', 'Could not save back to original file.\nChoose new file') self.setDirty(True) - return self.createNewProject(exists=True) + return self.saveProjectAs(exists=True) def draw(self): self.fill_eventbox() @@ -2340,6 +2358,7 @@ class Project(object): def __init__(self): self.eventlist = [] self.location = None + self.rootpath = None self.dirty = False self.parameter = None self._table = None @@ -2353,6 +2372,9 @@ class Project(object): return for item in eventlist: event = Event(item) + event.rootpath = self.parameter['rootpath'] + event.database = self.parameter['database'] + event.datapath = self.parameter['datapath'] if not event.path in self.getPaths(): self.eventlist.append(event) self.setDirty() @@ -2424,6 +2446,9 @@ class Event(object): ''' def __init__(self, path): self.path = path + self.database = path.split('/')[-2] + self.datapath = path.split('/')[-3] + self.rootpath = os.path.join(*path.split('/')[:-3]) self.autopicks = {} self.picks = {} self.notes = '' diff --git a/pylot/RELEASE-VERSION b/pylot/RELEASE-VERSION index d0508819..cc4db1e3 100644 --- a/pylot/RELEASE-VERSION +++ b/pylot/RELEASE-VERSION @@ -1 +1 @@ -be46-dirty +0a6a-dirty From b96af4132457b181c6582eced4eb0e86432726f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Wed, 21 Jun 2017 13:53:53 +0200 Subject: [PATCH 33/79] In progress: new parametters for magnitude estimation --- pylot/RELEASE-VERSION | 2 +- pylot/core/io/default_parameters.py | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pylot/RELEASE-VERSION b/pylot/RELEASE-VERSION index cc4db1e3..ab13596e 100644 --- a/pylot/RELEASE-VERSION +++ b/pylot/RELEASE-VERSION @@ -1 +1 @@ -0a6a-dirty +c106-dirty diff --git a/pylot/core/io/default_parameters.py b/pylot/core/io/default_parameters.py index 68c09a46..563476b8 100644 --- a/pylot/core/io/default_parameters.py +++ b/pylot/core/io/default_parameters.py @@ -279,11 +279,11 @@ defaults = {'rootpath': {'type': str, 'WAscaling': {'type': float, 'tooltip': 'Scaling relation of Wood-Anderson amplitude [nm]', - 'value': []}, + 'value': 1.0}, - 'magscaling': {'type': (float), - 'tooltip': 'Scaling relation for derived local magnitude', - 'value': []} + 'magscaling': {'type': float, + 'tooltip': 'Scaling relation for derived local magnitude [a*Ml+b]', + 'value': 1.0} } settings_main={ From 5214d3190893b5590d965e6b29262e9c4f5db6f8 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Wed, 21 Jun 2017 14:09:39 +0200 Subject: [PATCH 34/79] [add] space hotkey to close window --- pylot/RELEASE-VERSION | 2 +- pylot/core/util/widgets.py | 12 ++++++++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/pylot/RELEASE-VERSION b/pylot/RELEASE-VERSION index cc4db1e3..ab13596e 100644 --- a/pylot/RELEASE-VERSION +++ b/pylot/RELEASE-VERSION @@ -1 +1 @@ -0a6a-dirty +c106-dirty diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index 51eb6908..d00ec3de 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -845,17 +845,21 @@ class PickDlg(QDialog): self.s_button = QPushButton('S', self) self.p_button.setCheckable(True) self.s_button.setCheckable(True) - # button shortcuts (1 for P-button, 2 for S-button) - self.p_button.setShortcut(QKeySequence('1')) - self.s_button.setShortcut(QKeySequence('2')) # set button tooltips self.p_button.setToolTip('Hotkey: "1"') self.s_button.setToolTip('Hotkey: "2"') - + # create accept/reject button self.accept_button = QPushButton('&Accept Picks') self.reject_button = QPushButton('&Reject Picks') self.disable_ar_buttons() + + # add hotkeys + self._shortcut_space = QtGui.QShortcut(QtGui.QKeySequence(' '), self) + self._shortcut_space.activated.connect(self.accept_button.clicked) + # button shortcuts (1 for P-button, 2 for S-button) + self.p_button.setShortcut(QKeySequence('1')) + self.s_button.setShortcut(QKeySequence('2')) # layout the outermost appearance of the Pick Dialog _outerlayout = QVBoxLayout() From b0dcf5ff4b4d8073d204197d411e1e1b1e35d9f0 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Wed, 21 Jun 2017 14:20:22 +0200 Subject: [PATCH 35/79] [change] local magnitude section added --- pylot/RELEASE-VERSION | 2 +- pylot/core/io/inputs.py | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/pylot/RELEASE-VERSION b/pylot/RELEASE-VERSION index ab13596e..5991d614 100644 --- a/pylot/RELEASE-VERSION +++ b/pylot/RELEASE-VERSION @@ -1 +1 @@ -c106-dirty +d77a-dirty diff --git a/pylot/core/io/inputs.py b/pylot/core/io/inputs.py index f70b8caf..cd62b96a 100644 --- a/pylot/core/io/inputs.py +++ b/pylot/core/io/inputs.py @@ -140,7 +140,8 @@ class AutoPickParameter(object): all_names += self.get_main_para_names()['dirs'] all_names += self.get_main_para_names()['nlloc'] all_names += self.get_main_para_names()['smoment'] - all_names += self.get_main_para_names()['pick'] + all_names += self.get_main_para_names()['localmag'] + all_names += self.get_main_para_names()['pick'] all_names += self.get_special_para_names()['z'] all_names += self.get_special_para_names()['h'] all_names += self.get_special_para_names()['fm'] @@ -234,6 +235,8 @@ class AutoPickParameter(object): 'NLLoc settings', seperator) self.write_section(fid_out, self.get_main_para_names()['smoment'], 'parameters for seismic moment estimation', seperator) + self.write_section(fid_out, self.get_main_para_names()['localmag'], + 'settings local magnitude', seperator) self.write_section(fid_out, self.get_main_para_names()['pick'], 'common settings picker', seperator) fid_out.write(('#special settings for calculating CF#\n'+ From 6feffaeadbd3e184afd7a346d08621c2698ec2b5 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Wed, 21 Jun 2017 15:20:36 +0200 Subject: [PATCH 36/79] [closes #209] spacebar can now be used to accept pickDlg, also added a checkbox to automatically open next station (experimental) --- QtPyLoT.py | 35 +++++++++++++++++++---------------- pylot/RELEASE-VERSION | 2 +- pylot/core/util/widgets.py | 14 ++++++++++++-- 3 files changed, 32 insertions(+), 19 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index 5c0478f1..d8ea085b 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -172,7 +172,6 @@ class MainWindow(QMainWindow): self.setupUi() self.filteroptions = {} - self.pickDlgs = {} self.picks = {} self.autopicks = {} self.loc = False @@ -1565,7 +1564,9 @@ class MainWindow(QMainWindow): wfID = self.getWFID(ycoord) if wfID is None: return - + self.pickDialog(wfID) + + def pickDialog(self, wfID, nextStation=False): station = self.getStationName(wfID) if not station: return @@ -1576,21 +1577,23 @@ class MainWindow(QMainWindow): station=station, picks=self.getPicksOnStation(station, 'manual'), autopicks=self.getPicksOnStation(station, 'auto')) + pickDlg.nextStation.setChecked(nextStation) if pickDlg.exec_(): - if not pickDlg.getPicks(): - return - self.setDirty(True) - self.update_status('picks accepted ({0})'.format(station)) - replot = self.addPicks(station, pickDlg.getPicks()) - self.get_current_event().setPick(station, pickDlg.getPicks()) - self.enableSaveManualPicksAction() - if replot: - self.plotWaveformDataThread() - self.drawPicks() - self.draw() - else: - self.drawPicks(station) - self.draw() + if pickDlg.getPicks(): + self.setDirty(True) + self.update_status('picks accepted ({0})'.format(station)) + replot = self.addPicks(station, pickDlg.getPicks()) + self.get_current_event().setPick(station, pickDlg.getPicks()) + self.enableSaveManualPicksAction() + if replot: + self.plotWaveformDataThread() + self.drawPicks() + self.draw() + else: + self.drawPicks(station) + self.draw() + if pickDlg.nextStation.isChecked(): + self.pickDialog(wfID - 1, nextStation=pickDlg.nextStation.isChecked()) else: self.update_status('picks discarded ({0})'.format(station)) if not self.get_loc_flag() and self.check4Loc(): diff --git a/pylot/RELEASE-VERSION b/pylot/RELEASE-VERSION index 5991d614..19b2644c 100644 --- a/pylot/RELEASE-VERSION +++ b/pylot/RELEASE-VERSION @@ -1 +1 @@ -d77a-dirty +b0dc-dirty diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index d00ec3de..0bd05283 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -752,6 +752,7 @@ class PickDlg(QDialog): self._init_autopicks = {} self.filteroptions = FILTERDEFAULTS self.pick_block = False + self.nextStation = QtGui.QCheckBox('Continue with next station.') # initialize panning attributes self.press = None @@ -876,7 +877,9 @@ class PickDlg(QDialog): _dialtoolbar.addAction(self.resetPicksAction) if self._embedded: _dialtoolbar.addWidget(self.accept_button) - _dialtoolbar.addWidget(self.reject_button) + _dialtoolbar.addWidget(self.reject_button) + else: + _dialtoolbar.addWidget(self.nextStation) # layout the innermost widget _innerlayout = QVBoxLayout() @@ -1728,7 +1731,6 @@ class TuneAutopicker(QWidget): pickDlg.update_picks.connect(self.picks_from_pickdlg) pickDlg.update_picks.connect(self.fill_eventbox) pickDlg.update_picks.connect(self.fill_stationbox) - pickDlg.update_picks.connect(self.parent.drawPicks) pickDlg.update_picks.connect(lambda: self.parent.setDirty(True)) pickDlg.update_picks.connect(self.parent.enableSaveManualPicksAction) self.pickDlg = QtGui.QWidget() @@ -1738,7 +1740,15 @@ class TuneAutopicker(QWidget): def picks_from_pickdlg(self, picks=None): station = self.get_current_station() + replot = self.parent.addPicks(station, picks) self.get_current_event().setPick(station, picks) + if self.get_current_event() == self.parent.get_current_event(): + if replot: + self.parent.plotWaveformDataThread() + self.parent.drawPicks() + else: + self.parent.drawPicks(station) + self.parent.draw() def plot_manual_picks_to_figs(self): picks = self.get_current_event_picks(self.get_current_station()) From 5928967e28855cf24188cf935f98f496602c9868 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Wed, 21 Jun 2017 15:33:49 +0200 Subject: [PATCH 37/79] [closes #212] tuneAutopicker always tries to load current event in mainwindow --- pylot/RELEASE-VERSION | 2 +- pylot/core/util/widgets.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pylot/RELEASE-VERSION b/pylot/RELEASE-VERSION index 19b2644c..a281ef17 100644 --- a/pylot/RELEASE-VERSION +++ b/pylot/RELEASE-VERSION @@ -1 +1 @@ -b0dc-dirty +6feff-dirty diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index 0bd05283..9a051139 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -1857,7 +1857,7 @@ class TuneAutopicker(QWidget): def fill_eventbox(self): # update own list self.parent.fill_eventbox(eventBox=self.eventBox, select_events='ref') - index_start = self.eventBox.currentIndex() + index_start = self.parent.eventBox.currentIndex() index = index_start if index == -1: index += 1 From cf200e142570765eb1a2f57238367b99705ac6ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Wed, 21 Jun 2017 15:41:12 +0200 Subject: [PATCH 38/79] Preparation of flexible local magnitude estimation, renamed module. --- QtPyLoT.py | 4 ++-- autoPyLoT.py | 6 +++--- pylot/core/analysis/magnitude.py | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index d8ea085b..3b5a525a 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -55,7 +55,7 @@ except ImportError: from matplotlib.backends.backend_qt4agg import NavigationToolbar2QT as NavigationToolbar from matplotlib.figure import Figure -from pylot.core.analysis.magnitude import RichterMagnitude, MomentMagnitude +from pylot.core.analysis.magnitude import LocalMagnitude, MomentMagnitude from pylot.core.io.data import Data from pylot.core.io.inputs import FilterOptions, AutoPickParameter from autoPyLoT import autoPyLoT @@ -2150,7 +2150,7 @@ class MainWindow(QMainWindow): # if not rest_flag: # raise ProcessingError('Restitution of waveform data failed!') if type == 'ML': - local_mag = RichterMagnitude(corr_wf, self.get_data().get_evt_data(), self.inputs.get('sstop'), verbosity = True) + local_mag = LocalMagnitude(corr_wf, self.get_data().get_evt_data(), self.inputs.get('sstop'), verbosity = True) return local_mag.updated_event() elif type == 'Mw': moment_mag = MomentMagnitude(corr_wf, self.get_data().get_evt_data(), self.inputs.get('vp'), self.inputs.get('Qp'), self.inputs.get('rho'), verbosity = True) diff --git a/autoPyLoT.py b/autoPyLoT.py index 0df1e2cb..2b571dac 100755 --- a/autoPyLoT.py +++ b/autoPyLoT.py @@ -16,7 +16,7 @@ import pylot.core.loc.focmec as focmec import pylot.core.loc.hash as hash import pylot.core.loc.nll as nll #from PySide.QtGui import QWidget, QInputDialog -from pylot.core.analysis.magnitude import MomentMagnitude, RichterMagnitude +from pylot.core.analysis.magnitude import MomentMagnitude, LocalMagnitude from pylot.core.io.data import Data from pylot.core.io.inputs import AutoPickParameter from pylot.core.pick.autopick import autopickevent, iteratepicker @@ -252,7 +252,7 @@ def autoPyLoT(input_dict=None, parameter=None, inputfile=None, fnames=None, even for station, props in moment_mag.moment_props.items(): picks[station]['P'].update(props) evt = moment_mag.updated_event() - local_mag = RichterMagnitude(corr_dat, evt, + local_mag = LocalMagnitude(corr_dat, evt, parameter.get('sstop'), True,\ iplot) for station, amplitude in local_mag.amplitudes.items(): @@ -310,7 +310,7 @@ def autoPyLoT(input_dict=None, parameter=None, inputfile=None, fnames=None, even for station, props in moment_mag.moment_props.items(): picks[station]['P'].update(props) evt = moment_mag.updated_event() - local_mag = RichterMagnitude(corr_dat, evt, + local_mag = LocalMagnitude(corr_dat, evt, parameter.get('sstop'), True, \ iplot) for station, amplitude in local_mag.amplitudes.items(): diff --git a/pylot/core/analysis/magnitude.py b/pylot/core/analysis/magnitude.py index 06b540a4..88659cff 100644 --- a/pylot/core/analysis/magnitude.py +++ b/pylot/core/analysis/magnitude.py @@ -136,7 +136,7 @@ class Magnitude(object): return None -class RichterMagnitude(Magnitude): +class LocalMagnitude(Magnitude): """ Method to derive peak-to-peak amplitude as seen on a Wood-Anderson- seismograph. Has to be derived from instrument corrected traces! @@ -154,7 +154,7 @@ class RichterMagnitude(Magnitude): _amplitudes = dict() def __init__(self, stream, event, calc_win, verbosity=False, iplot=0): - super(RichterMagnitude, self).__init__(stream, event, verbosity, iplot) + super(LocalMagnitude, self).__init__(stream, event, verbosity, iplot) self._calc_win = calc_win self._type = 'ML' From 477aacbb4ae5eda8537ceb84215865a75d2fc8c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Wed, 21 Jun 2017 15:43:12 +0200 Subject: [PATCH 39/79] Preparation of flexible local magnitude estimation. --- pylot/core/io/default_parameters.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pylot/core/io/default_parameters.py b/pylot/core/io/default_parameters.py index 563476b8..93d55ae4 100644 --- a/pylot/core/io/default_parameters.py +++ b/pylot/core/io/default_parameters.py @@ -277,13 +277,13 @@ defaults = {'rootpath': {'type': str, 'tooltip': 'maximum allowed deviation from Wadati-diagram', 'value': 1.0}, - 'WAscaling': {'type': float, - 'tooltip': 'Scaling relation of Wood-Anderson amplitude [nm]', - 'value': 1.0}, + 'WAscaling': {'type': (float, float, float), + 'tooltip': 'Scaling relation (log(Ao)+Alog(r)+Br+C) of Wood-Anderson amplitude Ao [nm]', + 'value': (1.0, 1.0, 1.0)}, - 'magscaling': {'type': float, + 'magscaling': {'type': (float, float), 'tooltip': 'Scaling relation for derived local magnitude [a*Ml+b]', - 'value': 1.0} + 'value': (1.0, 1.0)} } settings_main={ From 9f164fc075e4284b32e337a4e9237aca9b0a6330 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Wed, 21 Jun 2017 16:42:26 +0200 Subject: [PATCH 40/79] Still in progress: flexible local magnitude estimation. --- autoPyLoT.py | 8 ++++---- pylot/core/analysis/magnitude.py | 23 +++++++++++++++++------ 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/autoPyLoT.py b/autoPyLoT.py index 2b571dac..a7d76002 100755 --- a/autoPyLoT.py +++ b/autoPyLoT.py @@ -253,8 +253,8 @@ def autoPyLoT(input_dict=None, parameter=None, inputfile=None, fnames=None, even picks[station]['P'].update(props) evt = moment_mag.updated_event() local_mag = LocalMagnitude(corr_dat, evt, - parameter.get('sstop'), True,\ - iplot) + parameter.get('sstop'), parameter.get('WAscaling'), \ + True, iplot) for station, amplitude in local_mag.amplitudes.items(): picks[station]['S']['Ao'] = amplitude.generic_amplitude evt = local_mag.updated_event() @@ -311,8 +311,8 @@ def autoPyLoT(input_dict=None, parameter=None, inputfile=None, fnames=None, even picks[station]['P'].update(props) evt = moment_mag.updated_event() local_mag = LocalMagnitude(corr_dat, evt, - parameter.get('sstop'), True, \ - iplot) + parameter.get('sstop'), parameter.get('WAscaling'), \ + True, iplot) for station, amplitude in local_mag.amplitudes.items(): picks[station]['S']['Ao'] = amplitude.generic_amplitude evt = local_mag.updated_event() diff --git a/pylot/core/analysis/magnitude.py b/pylot/core/analysis/magnitude.py index 88659cff..ff4dca18 100644 --- a/pylot/core/analysis/magnitude.py +++ b/pylot/core/analysis/magnitude.py @@ -38,10 +38,10 @@ class Magnitude(object): def __init__(self, stream, event, verbosity=False, iplot=0): self._type = "M" + self._stream = stream self._plot_flag = iplot self._verbosity = verbosity self._event = event - self._stream = stream self._magnitudes = dict() def __str__(self): @@ -153,10 +153,11 @@ class LocalMagnitude(Magnitude): _amplitudes = dict() - def __init__(self, stream, event, calc_win, verbosity=False, iplot=0): + def __init__(self, stream, event, calc_win, wascaling=None, verbosity=False, iplot=0): super(LocalMagnitude, self).__init__(stream, event, verbosity, iplot) self._calc_win = calc_win + self._wascaling = wascaling self._type = 'ML' self.calc() @@ -168,6 +169,10 @@ class LocalMagnitude(Magnitude): def calc_win(self, value): self._calc_win = value + @property + def wascaling(self): + return self._wascaling + @property def amplitudes(self): return self._amplitudes @@ -251,10 +256,16 @@ class LocalMagnitude(Magnitude): self.event.amplitudes.append(amplitude) self.amplitudes = (station, amplitude) # using standard Gutenberg-Richter relation - # TODO make the ML calculation more flexible by allowing - # use of custom relation functions - magnitude = ope.StationMagnitude( - mag=np.log10(a0) + richter_magnitude_scaling(delta)) + # or scale WA amplitude with given scaling relation + if self.wascaling == None: + print("Calculating original Richter magnitude ...") + magnitude = ope.StationMagnitude(mag=np.log10(a0) \ + + richter_magnitude_scaling(delta)) + else: + print("Calculating scaled local magnitude ...") + magnitude = ope.StationMagnitude(mag=np.log10(a0) \ + + self.wascaling[0] * np.log10(delta) + self.wascaling[1] + * delta + self.wascaling[2]) magnitude.origin_id = self.origin_id magnitude.waveform_id = pick.waveform_id magnitude.amplitude_id = amplitude.resource_id From f6a20c4ea2d3d388723dc484c0dd810d83cb6457 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Thu, 22 Jun 2017 10:38:57 +0200 Subject: [PATCH 41/79] [bugfix] advanced_cb acciddently pushed to bottom of layout --- pylot/RELEASE-VERSION | 2 +- pylot/core/util/widgets.py | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pylot/RELEASE-VERSION b/pylot/RELEASE-VERSION index a281ef17..4499c851 100644 --- a/pylot/RELEASE-VERSION +++ b/pylot/RELEASE-VERSION @@ -1 +1 @@ -6feff-dirty +9f16-dirty diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index 9a051139..b9e35d9c 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -2034,7 +2034,7 @@ class AutoPickParaBox(QtGui.QWidget): def _create_advanced_cb(self): self._advanced_cb = QtGui.QCheckBox('Enable Advanced Settings') - self._advanced_layout.addWidget(self._advanced_cb) + self._advanced_layout.insertWidget(self._advanced_cb, 0) self._advanced_cb.toggled.connect(self._toggle_advanced_settings) def _toggle_advanced_settings(self): @@ -2140,13 +2140,13 @@ class AutoPickParaBox(QtGui.QWidget): def add_special_pick_parameters_tab(self): self.add_to_layout(self._advanced_layout, 'Z-component', - self.parameter.get_special_para_names()['z'], 0) + self.parameter.get_special_para_names()['z'], 1) self.add_to_layout(self._advanced_layout, 'H-components', - self.parameter.get_special_para_names()['h'], 1) + self.parameter.get_special_para_names()['h'], 2) self.add_to_layout(self._advanced_layout, 'First-motion picker', - self.parameter.get_special_para_names()['fm'], 2) + self.parameter.get_special_para_names()['fm'], 3) self.add_to_layout(self._advanced_layout, 'Quality assessment', - self.parameter.get_special_para_names()['quality'], 3) + self.parameter.get_special_para_names()['quality'], 4) self.add_tab(self._advanced_layout, 'Advanced Settings') # def gen_h_seperator(self): From 203974cd83c71129154dc7b3026a6f1cd9cb502b Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Thu, 22 Jun 2017 10:54:17 +0200 Subject: [PATCH 42/79] [bugfix] parameters interchanged --- pylot/core/util/widgets.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index b9e35d9c..7236a7a4 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -2034,7 +2034,7 @@ class AutoPickParaBox(QtGui.QWidget): def _create_advanced_cb(self): self._advanced_cb = QtGui.QCheckBox('Enable Advanced Settings') - self._advanced_layout.insertWidget(self._advanced_cb, 0) + self._advanced_layout.insertWidget(0, self._advanced_cb) self._advanced_cb.toggled.connect(self._toggle_advanced_settings) def _toggle_advanced_settings(self): From f91e1cde6fbfe864d4eecf61b2cbd385c4669585 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Thu, 22 Jun 2017 11:03:24 +0200 Subject: [PATCH 43/79] [change] structure changed to make parabox more flexible --- QtPyLoT.py | 2 +- pylot/RELEASE-VERSION | 2 +- pylot/core/util/widgets.py | 21 ++++++++++++++++----- 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index 3b5a525a..89159ee6 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -799,7 +799,7 @@ class MainWindow(QMainWindow): self.paraBox.setValue(box, dirs[directory]) #show needed parameter in box self.paraBox.show_parameter(directory) - dirs_box = self.paraBox.get_groupbox_exclusive('Directories') + dirs_box = self.paraBox.get_groupbox_dialog('Directories') if not dirs_box.exec_(): return self.project.rootpath = dirs['rootpath'] diff --git a/pylot/RELEASE-VERSION b/pylot/RELEASE-VERSION index 4499c851..8742de16 100644 --- a/pylot/RELEASE-VERSION +++ b/pylot/RELEASE-VERSION @@ -1 +1 @@ -9f16-dirty +2039-dirty diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index 7236a7a4..be4af5c3 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -1987,6 +1987,7 @@ class AutoPickParaBox(QtGui.QWidget): self.labels = {} self.boxes = {} self.groupboxes = {} + self._exclusive_widgets = [] self._init_sublayouts() self.setLayout(self.layout) self.add_main_parameters_tab() @@ -2168,20 +2169,29 @@ class AutoPickParaBox(QtGui.QWidget): layout.insertWidget(position, groupbox) def get_groupbox_exclusive(self, name): + widget = QtGui.QWidget(self, 1) + layout = QtGui.QVBoxLayout() + widget.setLayout(layout) + layout.addWidget(self.groupboxes[name]) + self._exclusive_widgets.append(widget) + return widget + + def get_groupbox_dialog(self, name): + widget = self.get_groupbox_exclusive(name) dialog = QtGui.QDialog(self.parent()) - buttonbox = QtGui.QDialogButtonBox(QDialogButtonBox.Ok | - QDialogButtonBox.Cancel) - self._exclusive_dialog = dialog layout = QtGui.QVBoxLayout() dialog.setLayout(layout) - layout.addWidget(self.groupboxes[name]) - layout.addWidget(buttonbox) + buttonbox = QtGui.QDialogButtonBox(QDialogButtonBox.Ok | + QDialogButtonBox.Cancel) buttonbox.accepted.connect(dialog.accept) buttonbox.accepted.connect(self.refresh) buttonbox.accepted.connect(self.params_from_gui) buttonbox.rejected.connect(dialog.reject) buttonbox.rejected.connect(self.refresh) buttonbox.rejected.connect(self.params_to_gui) + layout.addWidget(widget) + layout.addWidget(buttonbox) + self._exclusive_dialog = dialog return dialog def add_to_layout(self, layout, name, items, position): @@ -2342,6 +2352,7 @@ class AutoPickParaBox(QtGui.QWidget): self.show_parameter() if hasattr(self, '_exclusive_dialog'): self._exclusive_dialog.close() + self._exclusive_widgets = [] QtGui.QWidget.show(self) def _warn(self, message): From 8e8b3e0462324dfd385593d2177ce253b1c9e6e5 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Thu, 22 Jun 2017 11:18:55 +0200 Subject: [PATCH 44/79] [rename] AutoPickPara ---> PylotPara --- QtPyLoT.py | 8 ++++---- autoPyLoT.py | 8 ++++---- pylot/RELEASE-VERSION | 2 +- pylot/core/io/inputs.py | 6 +++--- pylot/core/io/phases.py | 8 ++++---- pylot/core/pick/autopick.py | 4 ++-- pylot/core/util/utils.py | 4 ++-- pylot/core/util/widgets.py | 12 ++++++------ 8 files changed, 26 insertions(+), 26 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index 89159ee6..51e3dd93 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -57,7 +57,7 @@ from matplotlib.figure import Figure from pylot.core.analysis.magnitude import LocalMagnitude, MomentMagnitude from pylot.core.io.data import Data -from pylot.core.io.inputs import FilterOptions, AutoPickParameter +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 @@ -73,7 +73,7 @@ from pylot.core.util.utils import fnConstructor, getLogin, \ 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, AutoPickParaBox + getDataType, ComparisonDialog, TuneAutopicker, PylotParaBox from pylot.core.util.map_projection import map_projection from pylot.core.util.structure import DATASTRUCTURE from pylot.core.util.thread import AutoPickThread, Thread @@ -103,7 +103,7 @@ class MainWindow(QMainWindow): self.infile = infile[0] else: self.infile = infile - self._inputs = AutoPickParameter(infile) + self._inputs = PylotParameter(infile) self._props = None self.dirty = False @@ -2331,7 +2331,7 @@ class MainWindow(QMainWindow): def setParameter(self, show=True): if not self.paraBox: - self.paraBox = AutoPickParaBox(self._inputs) + self.paraBox = PylotParaBox(self._inputs) self.paraBox._apply.clicked.connect(self._setDirty) self.paraBox._okay.clicked.connect(self._setDirty) if show: diff --git a/autoPyLoT.py b/autoPyLoT.py index a7d76002..66db76a6 100755 --- a/autoPyLoT.py +++ b/autoPyLoT.py @@ -18,7 +18,7 @@ import pylot.core.loc.nll as nll #from PySide.QtGui import QWidget, QInputDialog from pylot.core.analysis.magnitude import MomentMagnitude, LocalMagnitude from pylot.core.io.data import Data -from pylot.core.io.inputs import AutoPickParameter +from pylot.core.io.inputs import PylotParameter from pylot.core.pick.autopick import autopickevent, iteratepicker from pylot.core.util.dataprocessing import restitute_data, read_metadata, \ remove_underscores @@ -35,7 +35,7 @@ def autoPyLoT(input_dict=None, parameter=None, inputfile=None, fnames=None, even :param inputfile: path to the input file containing all parameter information for automatic picking (for formatting details, see. - `~pylot.core.io.inputs.AutoPickParameter` + `~pylot.core.io.inputs.PylotParameter` :type inputfile: str :return: @@ -71,13 +71,13 @@ def autoPyLoT(input_dict=None, parameter=None, inputfile=None, fnames=None, even if not parameter: if inputfile: - parameter = AutoPickParameter(inputfile) + parameter = PylotParameter(inputfile) iplot = parameter['iplot'] else: print('No parameters set and no input file given. Choose either of both.') return else: - if not type(parameter) == AutoPickParameter: + if not type(parameter) == PylotParameter: print('Wrong input type for parameter: {}'.format(type(parameter))) return if inputfile: diff --git a/pylot/RELEASE-VERSION b/pylot/RELEASE-VERSION index 8742de16..6bf29797 100644 --- a/pylot/RELEASE-VERSION +++ b/pylot/RELEASE-VERSION @@ -1 +1 @@ -2039-dirty +f91e1-dirty diff --git a/pylot/core/io/inputs.py b/pylot/core/io/inputs.py index cd62b96a..3653b828 100644 --- a/pylot/core/io/inputs.py +++ b/pylot/core/io/inputs.py @@ -4,9 +4,9 @@ from pylot.core.util.errors import ParameterError import default_parameters -class AutoPickParameter(object): +class PylotParameter(object): ''' - AutoPickParameters is a parameter type object capable to read and/or write + PylotParameter is a parameter type object capable to read and/or write parameter ASCII. :param fn str: Filename of the input file @@ -78,7 +78,7 @@ class AutoPickParameter(object): # String representation of the object def __repr__(self): - return "AutoPickParameter('%s')" % self.__filename + return "PylotParameter('%s')" % self.__filename # Boolean test def __nonzero__(self): diff --git a/pylot/core/io/phases.py b/pylot/core/io/phases.py index 54f30695..eda56cf7 100644 --- a/pylot/core/io/phases.py +++ b/pylot/core/io/phases.py @@ -8,7 +8,7 @@ import scipy.io as sio import warnings from obspy.core import UTCDateTime -from pylot.core.io.inputs import AutoPickParameter +from pylot.core.io.inputs import PylotParameter from pylot.core.io.location import create_arrival, create_event, \ create_magnitude, create_origin, create_pick from pylot.core.pick.utils import select_for_phase @@ -116,7 +116,7 @@ def picksdict_from_pilot(fn): picks = dict() phases_pilot = sio.loadmat(fn) stations = stations_from_pilot(phases_pilot['stat']) - params = AutoPickParameter(TIMEERROR_DEFAULTS) + params = PylotParameter(TIMEERROR_DEFAULTS) timeerrors = dict(P=params.get('timeerrorsP'), S=params.get('timeerrorsS')) for n, station in enumerate(stations): @@ -295,14 +295,14 @@ def reassess_pilot_db(root_dir, db_dir, out_dir=None, fn_param=None, verbosity=0 def reassess_pilot_event(root_dir, db_dir, event_id, out_dir=None, fn_param=None, verbosity=0): from obspy import read - from pylot.core.io.inputs import AutoPickParameter + from pylot.core.io.inputs import PylotParameter from pylot.core.pick.utils import earllatepicker if fn_param is None: import pylot.core.util.defaults as defaults fn_param = defaults.AUTOMATIC_DEFAULTS - default = AutoPickParameter(fn_param, verbosity) + default = PylotParameter(fn_param, verbosity) search_base = os.path.join(root_dir, db_dir, event_id) phases_file = glob.glob(os.path.join(search_base, 'PHASES.mat')) diff --git a/pylot/core/pick/autopick.py b/pylot/core/pick/autopick.py index 8f43444b..a8437763 100644 --- a/pylot/core/pick/autopick.py +++ b/pylot/core/pick/autopick.py @@ -11,7 +11,7 @@ function conglomerate utils. import matplotlib.pyplot as plt import numpy as np -from pylot.core.io.inputs import AutoPickParameter +from pylot.core.io.inputs import PylotParameter from pylot.core.pick.picker import AICPicker, PragPicker from pylot.core.pick.charfuns import CharacteristicFunction from pylot.core.pick.charfuns import HOScf, AICcf, ARZcf, ARHcf, AR3Ccf @@ -81,7 +81,7 @@ def autopickstation(wfstream, pickparam, verbose=False, iplot=0, fig_dict=None): :param pickparam: container of picking parameters from input file, usually autoPyLoT.in - :type pickparam: AutoPickParameter + :type pickparam: PylotParameter :param verbose: :type verbose: bool diff --git a/pylot/core/util/utils.py b/pylot/core/util/utils.py index 905073e1..46fa699a 100644 --- a/pylot/core/util/utils.py +++ b/pylot/core/util/utils.py @@ -10,7 +10,7 @@ import re import warnings import subprocess from obspy import UTCDateTime, read -from pylot.core.io.inputs import AutoPickParameter +from pylot.core.io.inputs import PylotParameter def _pickle_method(m): @@ -497,7 +497,7 @@ def which(program, infile=None): bpath = os.path.join(os.path.expanduser('~'), '.pylot', infile) if os.path.exists(bpath): - nllocpath = ":" + AutoPickParameter(bpath).get('nllocbin') + nllocpath = ":" + PylotParameter(bpath).get('nllocbin') os.environ['PATH'] += nllocpath except ImportError as e: print(e.message) diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index be4af5c3..8fa59b71 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -36,7 +36,7 @@ from PySide.QtCore import QSettings, Qt, QUrl, Signal, Slot from PySide.QtWebKit import QWebView from obspy import Stream, UTCDateTime from pylot.core.io.data import Data -from pylot.core.io.inputs import FilterOptions, AutoPickParameter +from pylot.core.io.inputs import FilterOptions, PylotParameter from pylot.core.pick.utils import getSNR, earllatepicker, getnoisewin, \ getResolutionWindow from pylot.core.pick.compare import Comparison @@ -1662,7 +1662,7 @@ class TuneAutopicker(QWidget): self.stb_names = ['aicARHfig', 'refSpick', 'el_S1pick', 'el_S2pick'] def add_parameters(self): - self.paraBox = AutoPickParaBox(self.parameter) + self.paraBox = PylotParaBox(self.parameter) self.paraBox.set_tune_mode(True) self.update_eventID() self.parameter_layout.addWidget(self.paraBox) @@ -1968,13 +1968,13 @@ class TuneAutopicker(QWidget): self.qmb.show() -class AutoPickParaBox(QtGui.QWidget): +class PylotParaBox(QtGui.QWidget): def __init__(self, parameter, parent=None): ''' Generate Widget containing parameters for automatic picking algorithm. :param: parameter - :type: AutoPickParameter (object) + :type: PylotParameter (object) ''' QtGui.QWidget.__init__(self, parent) @@ -2521,7 +2521,7 @@ class InputsTab(PropTab): return values def resetValues(self, infile): - para = AutoPickParameter(infile) + para = PylotParameter(infile) datstruct = para.get('datastructure') if datstruct == 'SeisComp': index = 0 @@ -2777,7 +2777,7 @@ class LocalisationTab(PropTab): return values def resetValues(self, infile): - para = AutoPickParameter(infile) + para = PylotParameter(infile) nllocroot = para.get('nllocroot') nllocbin = para.get('nllocbin') loctool = self.locToolComboBox.setCurrentIndex(3) From 71876638c8811743f81868f0b1cd722c5d10195c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Thu, 22 Jun 2017 15:04:16 +0200 Subject: [PATCH 45/79] Flexible calculation of local magnitude including station magntiude scaling as well as network magnitude scaling. auoPyLot-output-file names have prefix event ID. --- autoPyLoT.py | 38 +++++++++++++++----------- pylot/RELEASE-VERSION | 2 +- pylot/core/analysis/magnitude.py | 41 ++++++++++++++++++----------- pylot/core/io/default_parameters.py | 10 ++++--- 4 files changed, 55 insertions(+), 36 deletions(-) diff --git a/autoPyLoT.py b/autoPyLoT.py index 66db76a6..db3a83f6 100755 --- a/autoPyLoT.py +++ b/autoPyLoT.py @@ -18,7 +18,7 @@ import pylot.core.loc.nll as nll #from PySide.QtGui import QWidget, QInputDialog from pylot.core.analysis.magnitude import MomentMagnitude, LocalMagnitude from pylot.core.io.data import Data -from pylot.core.io.inputs import PylotParameter +from pylot.core.io.inputs import AutoPickParameter from pylot.core.pick.autopick import autopickevent, iteratepicker from pylot.core.util.dataprocessing import restitute_data, read_metadata, \ remove_underscores @@ -35,7 +35,7 @@ def autoPyLoT(input_dict=None, parameter=None, inputfile=None, fnames=None, even :param inputfile: path to the input file containing all parameter information for automatic picking (for formatting details, see. - `~pylot.core.io.inputs.PylotParameter` + `~pylot.core.io.inputs.AutoPickParameter` :type inputfile: str :return: @@ -71,13 +71,13 @@ def autoPyLoT(input_dict=None, parameter=None, inputfile=None, fnames=None, even if not parameter: if inputfile: - parameter = PylotParameter(inputfile) + parameter = AutoPickParameter(inputfile) iplot = parameter['iplot'] else: print('No parameters set and no input file given. Choose either of both.') return else: - if not type(parameter) == PylotParameter: + if not type(parameter) == AutoPickParameter: print('Wrong input type for parameter: {}'.format(type(parameter))) return if inputfile: @@ -252,12 +252,17 @@ def autoPyLoT(input_dict=None, parameter=None, inputfile=None, fnames=None, even for station, props in moment_mag.moment_props.items(): picks[station]['P'].update(props) evt = moment_mag.updated_event() + net_mw = moment_mag.net_magnitude() + print("Network moment magnitude: %4.1f" % net_mw.mag) + # calculate local (Richter) magntiude local_mag = LocalMagnitude(corr_dat, evt, parameter.get('sstop'), parameter.get('WAscaling'), \ True, iplot) for station, amplitude in local_mag.amplitudes.items(): picks[station]['S']['Ao'] = amplitude.generic_amplitude - evt = local_mag.updated_event() + evt = local_mag.updated_event(parameter.get('magscaling')) + net_ml = local_mag.net_magnitude() + print("Network local magnitude: %4.1f" % net_ml.mag) else: print("autoPyLoT: No NLLoc-location file available!") print("No source parameter estimation possible!") @@ -310,14 +315,17 @@ def autoPyLoT(input_dict=None, parameter=None, inputfile=None, fnames=None, even for station, props in moment_mag.moment_props.items(): picks[station]['P'].update(props) evt = moment_mag.updated_event() + net_mw = moment_mag.net_magnitude() + print("Network moment magnitude: %4.1f" % net_mw.mag) + # calculate local (Richter) magntiude local_mag = LocalMagnitude(corr_dat, evt, parameter.get('sstop'), parameter.get('WAscaling'), \ True, iplot) for station, amplitude in local_mag.amplitudes.items(): picks[station]['S']['Ao'] = amplitude.generic_amplitude - evt = local_mag.updated_event() - net_mw = moment_mag.net_magnitude() - print("Network moment magnitude: %4.1f" % net_mw.mag) + evt = local_mag.updated_event(parameter.get('magscaling')) + net_ml = local_mag.net_magnitude(parameter.get('magscaling')) + print("Network local magnitude: %4.1f" % net_ml.mag) else: print("autoPyLoT: No NLLoc-location file available! Stop iteration!") locflag = 9 @@ -328,26 +336,26 @@ def autoPyLoT(input_dict=None, parameter=None, inputfile=None, fnames=None, even data.applyEVTData(picks) if evt is not None: data.applyEVTData(evt, 'event') - fnqml = '%s/autoPyLoT' % event + fnqml = '%s/picks_%s' % (event, evID) data.exportEvent(fnqml) # HYPO71 - hypo71file = '%s/autoPyLoT_HYPO71_phases' % event + hypo71file = '%s/%s_HYPO71_phases' % (event, evID) hypo71.export(picks, hypo71file, parameter) # HYPOSAT - hyposatfile = '%s/autoPyLoT_HYPOSAT_phases' % event + hyposatfile = '%s/%s_HYPOSAT_phases' % (event, evID) hyposat.export(picks, hyposatfile, parameter) if locflag == 1: # VELEST - velestfile = '%s/autoPyLoT_VELEST_phases.cnv' % event + velestfile = '%s/%s_VELEST_phases.cnv' % (event, evID) velest.export(picks, velestfile, parameter, evt) # hypoDD - hypoddfile = '%s/autoPyLoT_hypoDD_phases.pha' % event + hypoddfile = '%s/%s_hypoDD_phases.pha' % (event, evID) hypodd.export(picks, hypoddfile, parameter, evt) # FOCMEC - focmecfile = '%s/autoPyLoT_FOCMEC.in' % event + focmecfile = '%s/%s_FOCMEC.in' % (event, evID) focmec.export(picks, focmecfile, parameter, evt) # HASH - hashfile = '%s/autoPyLoT_HASH' % event + hashfile = '%s/%s_HASH' % (event, evID) hash.export(picks, hashfile, parameter, evt) endsplash = '''------------------------------------------\n' diff --git a/pylot/RELEASE-VERSION b/pylot/RELEASE-VERSION index 6bf29797..31812905 100644 --- a/pylot/RELEASE-VERSION +++ b/pylot/RELEASE-VERSION @@ -1 +1 @@ -f91e1-dirty +8e8b-dirty diff --git a/pylot/core/analysis/magnitude.py b/pylot/core/analysis/magnitude.py index ff4dca18..867a302d 100644 --- a/pylot/core/analysis/magnitude.py +++ b/pylot/core/analysis/magnitude.py @@ -2,6 +2,7 @@ # -*- coding: utf-8 -*- """ Created autumn/winter 2015. +Revised/extended summer 2017. :author: Ludger Küperkoch / MAGS2 EP3 working group """ @@ -115,23 +116,30 @@ class Magnitude(object): def calc(self): pass - def updated_event(self): - self.event.magnitudes.append(self.net_magnitude()) + def updated_event(self, magscaling=None): + self.event.magnitudes.append(self.net_magnitude(magscaling)) return self.event - def net_magnitude(self): + def net_magnitude(self, magscaling=None): if self: - # TODO if an average Magnitude instead of the median is calculated - # StationMagnitudeContributions should be added to the returned - # Magnitude object - # mag_error => weights (magnitude error estimate from peak_to_peak, calcsourcespec?) - # weights => StationMagnitdeContribution - mag = ope.Magnitude( - mag=np.median([M.mag for M in self.magnitudes.values()]), - magnitude_type=self.type, - origin_id=self.origin_id, - station_count=len(self.magnitudes), - azimuthal_gap=self.origin_id.get_referred_object().quality.azimuthal_gap) + if magscaling is not None and str(magscaling) is not '[0.0, 0.0]': + # scaling necessary + print("Scaling network magnitude ...") + mag = ope.Magnitude( + mag=np.median([M.mag for M in self.magnitudes.values()]) *\ + magscaling[0] + magscaling[1], + magnitude_type=self.type, + origin_id=self.origin_id, + station_count=len(self.magnitudes), + azimuthal_gap=self.origin_id.get_referred_object().quality.azimuthal_gap) + else: + # no saling necessary + mag = ope.Magnitude( + mag=np.median([M.mag for M in self.magnitudes.values()]), + magnitude_type=self.type, + origin_id=self.origin_id, + station_count=len(self.magnitudes), + azimuthal_gap=self.origin_id.get_referred_object().quality.azimuthal_gap) return mag return None @@ -153,7 +161,7 @@ class LocalMagnitude(Magnitude): _amplitudes = dict() - def __init__(self, stream, event, calc_win, wascaling=None, verbosity=False, iplot=0): + def __init__(self, stream, event, calc_win, wascaling, verbosity=False, iplot=0): super(LocalMagnitude, self).__init__(stream, event, verbosity, iplot) self._calc_win = calc_win @@ -257,12 +265,13 @@ class LocalMagnitude(Magnitude): self.amplitudes = (station, amplitude) # using standard Gutenberg-Richter relation # or scale WA amplitude with given scaling relation - if self.wascaling == None: + if str(self.wascaling) == '[0.0, 0.0, 0.0]': print("Calculating original Richter magnitude ...") magnitude = ope.StationMagnitude(mag=np.log10(a0) \ + richter_magnitude_scaling(delta)) else: print("Calculating scaled local magnitude ...") + a0 = a0 * 1e03 # mm to nm (see Havskov & Ottemöller, 2010) magnitude = ope.StationMagnitude(mag=np.log10(a0) \ + self.wascaling[0] * np.log10(delta) + self.wascaling[1] * delta + self.wascaling[2]) diff --git a/pylot/core/io/default_parameters.py b/pylot/core/io/default_parameters.py index 93d55ae4..22d0268b 100644 --- a/pylot/core/io/default_parameters.py +++ b/pylot/core/io/default_parameters.py @@ -278,12 +278,14 @@ defaults = {'rootpath': {'type': str, 'value': 1.0}, 'WAscaling': {'type': (float, float, float), - 'tooltip': 'Scaling relation (log(Ao)+Alog(r)+Br+C) of Wood-Anderson amplitude Ao [nm]', - 'value': (1.0, 1.0, 1.0)}, + 'tooltip': 'Scaling relation (log(Ao)+Alog(r)+Br+C) of Wood-Anderson amplitude Ao [nm] \ + If zeros are set, original Richter magnitude is calculated!', + 'value': (0., 0., 0.)}, 'magscaling': {'type': (float, float), - 'tooltip': 'Scaling relation for derived local magnitude [a*Ml+b]', - 'value': (1.0, 1.0)} + 'tooltip': 'Scaling relation for derived local magnitude [a*Ml+b]. \ + If zeros are set, no scaling is of network magnitude is applied.', + 'value': (0., 0.)} } settings_main={ From 9f9d492b8b92bd997408c69c6685fbc711d5cab4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Thu, 22 Jun 2017 15:09:40 +0200 Subject: [PATCH 46/79] Restored former changes of Marcel (AutoPickParameter - PyLoTParameter). --- autoPyLoT.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/autoPyLoT.py b/autoPyLoT.py index db3a83f6..41db89ca 100755 --- a/autoPyLoT.py +++ b/autoPyLoT.py @@ -18,7 +18,7 @@ import pylot.core.loc.nll as nll #from PySide.QtGui import QWidget, QInputDialog from pylot.core.analysis.magnitude import MomentMagnitude, LocalMagnitude from pylot.core.io.data import Data -from pylot.core.io.inputs import AutoPickParameter +from pylot.core.io.inputs import PyLoTParameter from pylot.core.pick.autopick import autopickevent, iteratepicker from pylot.core.util.dataprocessing import restitute_data, read_metadata, \ remove_underscores @@ -35,7 +35,7 @@ def autoPyLoT(input_dict=None, parameter=None, inputfile=None, fnames=None, even :param inputfile: path to the input file containing all parameter information for automatic picking (for formatting details, see. - `~pylot.core.io.inputs.AutoPickParameter` + `~pylot.core.io.inputs.PyLoTParameter` :type inputfile: str :return: @@ -71,13 +71,13 @@ def autoPyLoT(input_dict=None, parameter=None, inputfile=None, fnames=None, even if not parameter: if inputfile: - parameter = AutoPickParameter(inputfile) + parameter = PyLoTParameter(inputfile) iplot = parameter['iplot'] else: print('No parameters set and no input file given. Choose either of both.') return else: - if not type(parameter) == AutoPickParameter: + if not type(parameter) == PyLoTParameter: print('Wrong input type for parameter: {}'.format(type(parameter))) return if inputfile: From 33d81eb5382af7ca207e94ed7758ffe651987a40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Thu, 22 Jun 2017 15:18:19 +0200 Subject: [PATCH 47/79] Bugfix: Wrong naming of module in previous commit: PyLoTParameter - PylotParameter. --- autoPyLoT.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/autoPyLoT.py b/autoPyLoT.py index 41db89ca..7841487d 100755 --- a/autoPyLoT.py +++ b/autoPyLoT.py @@ -18,7 +18,7 @@ import pylot.core.loc.nll as nll #from PySide.QtGui import QWidget, QInputDialog from pylot.core.analysis.magnitude import MomentMagnitude, LocalMagnitude from pylot.core.io.data import Data -from pylot.core.io.inputs import PyLoTParameter +from pylot.core.io.inputs import PylotParameter from pylot.core.pick.autopick import autopickevent, iteratepicker from pylot.core.util.dataprocessing import restitute_data, read_metadata, \ remove_underscores @@ -35,7 +35,7 @@ def autoPyLoT(input_dict=None, parameter=None, inputfile=None, fnames=None, even :param inputfile: path to the input file containing all parameter information for automatic picking (for formatting details, see. - `~pylot.core.io.inputs.PyLoTParameter` + `~pylot.core.io.inputs.PylotParameter` :type inputfile: str :return: @@ -71,13 +71,13 @@ def autoPyLoT(input_dict=None, parameter=None, inputfile=None, fnames=None, even if not parameter: if inputfile: - parameter = PyLoTParameter(inputfile) + parameter = PylotParameter(inputfile) iplot = parameter['iplot'] else: print('No parameters set and no input file given. Choose either of both.') return else: - if not type(parameter) == PyLoTParameter: + if not type(parameter) == PylotParameter: print('Wrong input type for parameter: {}'.format(type(parameter))) return if inputfile: From c44f3516360f0385110a1134290f57aa9086245f Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Thu, 22 Jun 2017 16:30:22 +0200 Subject: [PATCH 48/79] [add] new method to load multiple XML files at once for all events, WIP: picks loaded from XML files cannot be saved afterwards --- QtPyLoT.py | 47 +++++++++++++++++++++++++++++++++---------- pylot/RELEASE-VERSION | 2 +- 2 files changed, 37 insertions(+), 12 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index 51e3dd93..d15be5af 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -637,9 +637,31 @@ class MainWindow(QMainWindow): fname_dict = dict(phasfn=fn_phases, locfn=fn_loc) self.load_data(fname_dict, type=type) - def load_data(self, fname=None, type='manual', loc=False): + def load_multiple_data(self, type='manual'): if not self.okToContinue(): return + refresh=False + events = self.project.eventlist + fext = '.xml' + for event in events: + path = event.path + eventname = path.split('/')[-1] + filename = os.path.join(path, 'picks_'+eventname+fext) + if os.path.isfile(filename): + self.load_data(filename, draw=False, event=event, overwrite=True) + refresh=True + if not refresh: + return + if self.get_current_event().picks: + self.plotWaveformDataThread() + self.drawPicks(picktype=type) + self.draw() + self.setDirty(True) + + def load_data(self, fname=None, type='manual', loc=False, draw=True, event=None, overwrite=False): + if not overwrite: + if not self.okToContinue(): + return if fname is None: action = self.sender() if isinstance(action, QAction): @@ -647,13 +669,15 @@ class MainWindow(QMainWindow): self.set_fname(fname, type) data = dict(auto=self.autodata, manual=self.data) data[type] += Data(self, evtdata=fname) + print('Loading {} picks from file {}.'.format(type, fname)) if not loc: - self.updatePicks(type=type) - if self.get_current_event().picks: - self.plotWaveformDataThread() - self.drawPicks(picktype=type) - self.draw() - self.setDirty(True) + self.updatePicks(type=type, event=event) + if draw: + if self.get_current_event().picks: + self.plotWaveformDataThread() + self.drawPicks(picktype=type) + self.draw() + self.setDirty(True) def add_recentfile(self, event): self.recentfiles.insert(0, event) @@ -979,7 +1003,6 @@ class MainWindow(QMainWindow): if not exform and selected_filter or not exform in OUTPUTFORMATS: exform = selected_filter.split('*')[1][:-1] - if not exform in OUTPUTFORMATS: return fname, exform return fbasename, exform @@ -1723,13 +1746,15 @@ class MainWindow(QMainWindow): # raise Exception('FATAL: Should never occur!') # MP MP prompt redundant because new picks have to be accepted in the first place closing PickDlg - def updatePicks(self, type='manual'): + def updatePicks(self, type='manual', event=None): + if not event: + event = self.get_current_event() picks = picksdict_from_picks(evt=self.get_data(type).get_evt_data()) if type == 'manual': - self.get_current_event().addPicks(picks) + event.addPicks(picks) self.picks.update(picks) elif type == 'auto': - self.get_current_event().addAutopicks(picks) + event.addAutopicks(picks) self.autopicks.update(picks) self.check4Comparison() diff --git a/pylot/RELEASE-VERSION b/pylot/RELEASE-VERSION index 6bf29797..31812905 100644 --- a/pylot/RELEASE-VERSION +++ b/pylot/RELEASE-VERSION @@ -1 +1 @@ -f91e1-dirty +8e8b-dirty From 6ebad273a9c380837483a3acf6eedb1063e717ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Fri, 23 Jun 2017 10:10:45 +0200 Subject: [PATCH 49/79] Improved screen output. --- autoPyLoT.py | 36 ++++++++++++++++++++++++++---------- pylot/RELEASE-VERSION | 2 +- 2 files changed, 27 insertions(+), 11 deletions(-) diff --git a/autoPyLoT.py b/autoPyLoT.py index 7841487d..7569d814 100755 --- a/autoPyLoT.py +++ b/autoPyLoT.py @@ -243,7 +243,7 @@ def autoPyLoT(input_dict=None, parameter=None, inputfile=None, fnames=None, even # get latest NLLoc-location file if several are available nllocfile = max(glob.glob(locsearch), key=os.path.getctime) evt = read_events(nllocfile)[0] - # calculating seismic moment Mo and moment magnitude Mw + # calculate seismic moment Mo and moment magnitude Mw moment_mag = MomentMagnitude(corr_dat, evt, parameter.get('vp'), parameter.get('Qp'), parameter.get('rho'), True, \ @@ -255,14 +255,22 @@ def autoPyLoT(input_dict=None, parameter=None, inputfile=None, fnames=None, even net_mw = moment_mag.net_magnitude() print("Network moment magnitude: %4.1f" % net_mw.mag) # calculate local (Richter) magntiude + WAscaling = parameter.get('WAscaling') + magscaling = parameter.get('magscaling') local_mag = LocalMagnitude(corr_dat, evt, - parameter.get('sstop'), parameter.get('WAscaling'), \ - True, iplot) + parameter.get('sstop'), + WAscaling, True, iplot) for station, amplitude in local_mag.amplitudes.items(): picks[station]['S']['Ao'] = amplitude.generic_amplitude - evt = local_mag.updated_event(parameter.get('magscaling')) - net_ml = local_mag.net_magnitude() + print("Local station magnitudes scaled with:") + print("log(Ao) + %f * log(r) + %f * r + %f" % (WAscaling[0], + WAscaling[1], + WAscaling[2])) + evt = local_mag.updated_event(magscaling) + net_ml = local_mag.net_magnitude(magscaling) print("Network local magnitude: %4.1f" % net_ml.mag) + print("Network local magnitude scaled with:") + print("%f * Ml + %f" % (magscaling[0], magscaling[1])) else: print("autoPyLoT: No NLLoc-location file available!") print("No source parameter estimation possible!") @@ -306,7 +314,7 @@ def autoPyLoT(input_dict=None, parameter=None, inputfile=None, fnames=None, even nlloccounter = maxnumit evt = read_events(nllocfile)[0] if locflag < 2: - # calculating seismic moment Mo and moment magnitude Mw + # calculate seismic moment Mo and moment magnitude Mw moment_mag = MomentMagnitude(corr_dat, evt, parameter.get('vp'), parameter.get('Qp'), parameter.get('rho'), True, \ @@ -318,14 +326,22 @@ def autoPyLoT(input_dict=None, parameter=None, inputfile=None, fnames=None, even net_mw = moment_mag.net_magnitude() print("Network moment magnitude: %4.1f" % net_mw.mag) # calculate local (Richter) magntiude + WAscaling = parameter.get('WAscaling') + magscaling = parameter.get('magscaling') local_mag = LocalMagnitude(corr_dat, evt, - parameter.get('sstop'), parameter.get('WAscaling'), \ - True, iplot) + parameter.get('sstop'), + WAscaling, True, iplot) for station, amplitude in local_mag.amplitudes.items(): picks[station]['S']['Ao'] = amplitude.generic_amplitude - evt = local_mag.updated_event(parameter.get('magscaling')) - net_ml = local_mag.net_magnitude(parameter.get('magscaling')) + print("Local station magnitudes scaled with:") + print("log(Ao) + %f * log(r) + %f * r + %f" % (WAscaling[0], + WAscaling[1], + WAscaling[2])) + evt = local_mag.updated_event(magscaling) + net_ml = local_mag.net_magnitude(magscaling) print("Network local magnitude: %4.1f" % net_ml.mag) + print("Network local magnitude scaled with:") + print("%f * Ml + %f" % (magscaling[0], magscaling[1])) else: print("autoPyLoT: No NLLoc-location file available! Stop iteration!") locflag = 9 diff --git a/pylot/RELEASE-VERSION b/pylot/RELEASE-VERSION index 31812905..0d836703 100644 --- a/pylot/RELEASE-VERSION +++ b/pylot/RELEASE-VERSION @@ -1 +1 @@ -8e8b-dirty +33d8-dirty From 5958e05f3bdab30be1b30fea5afda941cf734c21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Fri, 23 Jun 2017 10:26:49 +0200 Subject: [PATCH 50/79] Cosmetics. --- pylot/core/io/default_parameters.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pylot/core/io/default_parameters.py b/pylot/core/io/default_parameters.py index 22d0268b..18baca6c 100644 --- a/pylot/core/io/default_parameters.py +++ b/pylot/core/io/default_parameters.py @@ -284,7 +284,7 @@ defaults = {'rootpath': {'type': str, 'magscaling': {'type': (float, float), 'tooltip': 'Scaling relation for derived local magnitude [a*Ml+b]. \ - If zeros are set, no scaling is of network magnitude is applied.', + If zeros are set, no scaling of network magnitude is applied!', 'value': (0., 0.)} } From 27597e6b4b4909e53506862c81d2ed8dd6c68a23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Fri, 23 Jun 2017 11:32:08 +0200 Subject: [PATCH 51/79] Take into account parameter extent when writing header for PyLoT-input file. --- pylot/RELEASE-VERSION | 2 +- pylot/core/io/inputs.py | 21 ++++++++++----------- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/pylot/RELEASE-VERSION b/pylot/RELEASE-VERSION index 0d836703..cd4206e8 100644 --- a/pylot/RELEASE-VERSION +++ b/pylot/RELEASE-VERSION @@ -1 +1 @@ -33d8-dirty +5958-dirty diff --git a/pylot/core/io/inputs.py b/pylot/core/io/inputs.py index 3653b828..9aa9b5d2 100644 --- a/pylot/core/io/inputs.py +++ b/pylot/core/io/inputs.py @@ -221,24 +221,23 @@ class PylotParameter(object): # for key, value in self.iteritems(): # lines.append('{key}\t{value}\n'.format(key=key, value=value)) # fid_out.writelines(lines) - header = ('%This is a parameter input file for PyLoT/autoPyLoT.\n'+ '%All main and special settings regarding data handling\n'+ '%and picking are to be set here!\n'+ - '%Parameters are optimized for local data sets!\n') - seperator = '%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n' + '%Parameters are optimized for %{} data sets!\n'.format(self.get_main_para_names()['pick'][0])) + separator = '%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n' fid_out.write(header) self.write_section(fid_out, self.get_main_para_names()['dirs'], - 'main settings', seperator) + 'main settings', separator) self.write_section(fid_out, self.get_main_para_names()['nlloc'], - 'NLLoc settings', seperator) + 'NLLoc settings', separator) self.write_section(fid_out, self.get_main_para_names()['smoment'], - 'parameters for seismic moment estimation', seperator) + 'parameters for seismic moment estimation', separator) self.write_section(fid_out, self.get_main_para_names()['localmag'], - 'settings local magnitude', seperator) + 'settings local magnitude', separator) self.write_section(fid_out, self.get_main_para_names()['pick'], - 'common settings picker', seperator) + 'common settings picker', separator) fid_out.write(('#special settings for calculating CF#\n'+ '%!!Edit the following only if you know what you are doing!!%\n')) self.write_section(fid_out, self.get_special_para_names()['z'], @@ -250,9 +249,9 @@ class PylotParameter(object): self.write_section(fid_out, self.get_special_para_names()['quality'], 'quality assessment', None) - def write_section(self, fid, names, title, seperator): - if seperator: - fid.write(seperator) + def write_section(self, fid, names, title, separator): + if separator: + fid.write(separator) fid.write('#{}#\n'.format(title)) l_val = 50 l_name = 15 From 7bb2d54f6e0ef62899322d471e091de0b08715e9 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Fri, 23 Jun 2017 12:04:26 +0200 Subject: [PATCH 52/79] [change] Event class now bases on ObsPy Event class which should be more efficient in future, origin time will be read from notes file if possible --- QtPyLoT.py | 29 +++++++++++++++++++---------- pylot/RELEASE-VERSION | 2 +- pylot/core/util/widgets.py | 14 ++++++++++---- 3 files changed, 30 insertions(+), 15 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index d15be5af..6acaa2b9 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -41,6 +41,8 @@ from PySide.QtGui import QMainWindow, QInputDialog, QIcon, QFileDialog, \ QTreeView, QComboBox, QTabWidget, QPushButton, QGridLayout import numpy as np from obspy import UTCDateTime +from obspy.core.event import Event as ObsPyEvent +from obspy.core.event import Origin try: import pyqtgraph as pg @@ -752,8 +754,8 @@ class MainWindow(QMainWindow): ''' if not eventbox: eventbox = self.eventBox - index = eventbox.currentIndex() - return eventbox.itemData(index) + path = eventbox.currentText() + return self.project.getEventFromPath(path) def get_current_event_path(self, eventbox=None): ''' @@ -953,7 +955,8 @@ class MainWindow(QMainWindow): '{} unequal {}.' .format(event.path, self.eventBox.itemText(id))) raise ValueError(message) - eventBox.setItemData(id, event) + #not working with obspy events + #eventBox.setItemData(id, event) eventBox.setCurrentIndex(index) self.refreshRefTestButtons() @@ -2468,11 +2471,13 @@ class Project(object): return project -class Event(object): +class Event(ObsPyEvent): ''' - Pickable class containing information on a single event. + Pickable class derived from ~obspy.core.event.Event containing information on a single event. ''' def __init__(self, path): + # initialize super class + super(Event, self).__init__() self.path = path self.database = path.split('/')[-2] self.datapath = path.split('/')[-3] @@ -2482,10 +2487,7 @@ class Event(object): self.notes = '' self._testEvent = False self._refEvent = False - try: - self.get_notes() - except: - pass + self.get_notes() def get_notes_path(self): notesfile = os.path.join(self.path, 'notes.txt') @@ -2495,8 +2497,15 @@ class Event(object): notesfile = self.get_notes_path() if os.path.isfile(notesfile): with open(notesfile) as infile: - text = '[eventInfo: '+str(infile.readlines()[0].split('\n')[0])+']' + path = str(infile.readlines()[0].split('\n')[0]) + text = '[eventInfo: '+path+']' self.addNotes(text) + try: + datetime = UTCDateTime(path.split('/')[-1]) + origin = Origin(time=datetime, latitude=0, longitude=0) + self.origins.append(origin) + except: + pass def addNotes(self, notes): self.notes = str(notes) diff --git a/pylot/RELEASE-VERSION b/pylot/RELEASE-VERSION index 31812905..5f414d75 100644 --- a/pylot/RELEASE-VERSION +++ b/pylot/RELEASE-VERSION @@ -1 +1 @@ -8e8b-dirty +410fe-dirty diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index 8fa59b71..23c168c7 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -1687,8 +1687,8 @@ class TuneAutopicker(QWidget): self.listWidget.scrollToBottom() def get_current_event(self): - index = self.eventBox.currentIndex() - return self.eventBox.itemData(index) + path = self.eventBox.currentText() + return self.parent.project.getEventFromPath(path) def get_current_event_name(self): return self.eventBox.currentText().split('/')[-1] @@ -1855,6 +1855,9 @@ class TuneAutopicker(QWidget): self.init_tab_names() def fill_eventbox(self): + project = self.parent.project + if not project: + return # update own list self.parent.fill_eventbox(eventBox=self.eventBox, select_events='ref') index_start = self.parent.eventBox.currentIndex() @@ -1862,10 +1865,13 @@ class TuneAutopicker(QWidget): if index == -1: index += 1 nevents = self.eventBox.model().rowCount() - if self.eventBox.itemData(index).isTestEvent(): + path = self.eventBox.itemText(index) + if project.getEventFromPath(path).isTestEvent(): for index in range(nevents): - if not self.eventBox.itemData(index).isTestEvent(): + path = self.eventBox.itemText(index) + if not project.getEventFromPath(index).isTestEvent(): break + #in case all events are marked as test events elif index == nevents - 1: index = -1 self.eventBox.setCurrentIndex(index) From c9f0b126dfb1ad748b41e45bccea0b142b3dea95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Fri, 23 Jun 2017 14:11:35 +0200 Subject: [PATCH 53/79] Equal xml-file name for both automatic and manual picks, i.e. PyLoT_evID.xml. --- autoPyLoT.py | 14 +++++++------- pylot/core/io/phases.py | 4 ++-- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/autoPyLoT.py b/autoPyLoT.py index 7569d814..84d24dea 100755 --- a/autoPyLoT.py +++ b/autoPyLoT.py @@ -352,26 +352,26 @@ def autoPyLoT(input_dict=None, parameter=None, inputfile=None, fnames=None, even data.applyEVTData(picks) if evt is not None: data.applyEVTData(evt, 'event') - fnqml = '%s/picks_%s' % (event, evID) + fnqml = '%s/PyLoT_%s' % (event, evID) data.exportEvent(fnqml) # HYPO71 - hypo71file = '%s/%s_HYPO71_phases' % (event, evID) + hypo71file = '%s/PyLoT_%s_HYPO71_phases' % (event, evID) hypo71.export(picks, hypo71file, parameter) # HYPOSAT - hyposatfile = '%s/%s_HYPOSAT_phases' % (event, evID) + hyposatfile = '%s/PyLoT_%s_HYPOSAT_phases' % (event, evID) hyposat.export(picks, hyposatfile, parameter) if locflag == 1: # VELEST - velestfile = '%s/%s_VELEST_phases.cnv' % (event, evID) + velestfile = '%s/PyLoT_%s_VELEST_phases.cnv' % (event, evID) velest.export(picks, velestfile, parameter, evt) # hypoDD - hypoddfile = '%s/%s_hypoDD_phases.pha' % (event, evID) + hypoddfile = '%s/PyLoT_%s_hypoDD_phases.pha' % (event, evID) hypodd.export(picks, hypoddfile, parameter, evt) # FOCMEC - focmecfile = '%s/%s_FOCMEC.in' % (event, evID) + focmecfile = '%s/PyLoT_%s_FOCMEC.in' % (event, evID) focmec.export(picks, focmecfile, parameter, evt) # HASH - hashfile = '%s/%s_HASH' % (event, evID) + hashfile = '%s/PyLoT_%s_HASH' % (event, evID) hash.export(picks, hashfile, parameter, evt) endsplash = '''------------------------------------------\n' diff --git a/pylot/core/io/phases.py b/pylot/core/io/phases.py index eda56cf7..43ff2de3 100644 --- a/pylot/core/io/phases.py +++ b/pylot/core/io/phases.py @@ -382,12 +382,12 @@ def reassess_pilot_event(root_dir, db_dir, event_id, out_dir=None, fn_param=None evt.picks = picks_from_picksdict(picks_dict) # write phase information to file if not out_dir: - fnout_prefix = os.path.join(root_dir, db_dir, event_id, '{0}.'.format(event_id)) + fnout_prefix = os.path.join(root_dir, db_dir, event_id, 'PyLoT_{0}.'.format(event_id)) else: out_dir = os.path.join(out_dir, db_dir) if not os.path.isdir(out_dir): os.makedirs(out_dir) - fnout_prefix = os.path.join(out_dir, '{0}.'.format(event_id)) + fnout_prefix = os.path.join(out_dir, 'PyLoT_{0}.'.format(event_id)) evt.write(fnout_prefix + 'xml', format='QUAKEML') #evt.write(fnout_prefix + 'cnv', format='VELEST') From 103ae716a25b02a339887532c3cfcece8b708c17 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Fri, 23 Jun 2017 14:14:42 +0200 Subject: [PATCH 54/79] [add] additional event information can now be read by project from a simple textfile and added to each event (time, location, magnitude) --- QtPyLoT.py | 66 +++++++++++++++++++++++++++++++++++++++---- pylot/RELEASE-VERSION | 2 +- 2 files changed, 62 insertions(+), 6 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index 6acaa2b9..298d6c6f 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -42,7 +42,7 @@ from PySide.QtGui import QMainWindow, QInputDialog, QIcon, QFileDialog, \ import numpy as np from obspy import UTCDateTime from obspy.core.event import Event as ObsPyEvent -from obspy.core.event import Origin +from obspy.core.event import Origin, Magnitude, ResourceIdentifier try: import pyqtgraph as pg @@ -804,7 +804,7 @@ class MainWindow(QMainWindow): dirs = { 'database': path.split('/')[-2], 'datapath': path.split('/')[-3], - 'rootpath': os.path.join(*path.split('/')[:-3]) + 'rootpath': '/'+os.path.join(*path.split('/')[:-3]) } except Exception as e: dirs = { @@ -2411,6 +2411,62 @@ class Project(object): self.setDirty() else: print('Skipping event with path {}. Already part of project.'.format(event.path)) + self.search_eventfile_info() + + def read_eventfile_info(self, filename, separator=','): + ''' + Try to read event information from file (:param:filename) comparing specific event datetimes. + File structure (each row): event, date, time, magnitude, latitude, longitude, depth + separated by :param:separator each. + ''' + infile = open(filename, 'r') + for line in infile.readlines(): + event, date, time, mag, lat, lon, depth = line.split(separator)[:7] + #skip first line + try: + month, day, year = date.split('/') + except: + continue + year = int(year) + #hardcoded, if year only consists of 2 digits (e.g. 16 instead of 2016) + if year<100: + year += 2000 + datetime = '{}-{}-{}T{}'.format(year, month, day, time) + try: + datetime = UTCDateTime(datetime) + except Exception as e: + print(e, datetime, filename) + continue + for event in self.eventlist: + if not event.origins: + continue + origin = event.origins[0] #should have only one origin + if origin.time == datetime: + origin.latitude = float(lat) + origin.longitude = float(lon) + origin.depth = float(depth) + event.magnitudes.append(Magnitude(resource_id=event.resource_id, + mag=float(mag), + mag_type='M')) + + def search_eventfile_info(self): + ''' + Search all datapaths in rootpath for filenames with given file extension fext + and try to read event info from it + ''' + datapaths = [] + fext='.csv' + for event in self.eventlist: + if not event.datapath in datapaths: + datapaths.append(event.datapath) + for datapath in datapaths: + datapath = os.path.join(self.rootpath, datapath) + for filename in os.listdir(datapath): + if os.path.isfile(os.path.join(datapath, filename)) and filename.endswith(fext): + try: + self.read_eventfile_info(filename) + except Exception as e: + print('Failed on reading eventfile info from file {}: {}'.format(filename, e)) def getPaths(self): ''' @@ -2477,11 +2533,11 @@ class Event(ObsPyEvent): ''' def __init__(self, path): # initialize super class - super(Event, self).__init__() + super(Event, self).__init__(resource_id=ResourceIdentifier(path.split('/')[-1])) self.path = path self.database = path.split('/')[-2] self.datapath = path.split('/')[-3] - self.rootpath = os.path.join(*path.split('/')[:-3]) + self.rootpath = '/' + os.path.join(*path.split('/')[:-3]) self.autopicks = {} self.picks = {} self.notes = '' @@ -2502,7 +2558,7 @@ class Event(ObsPyEvent): self.addNotes(text) try: datetime = UTCDateTime(path.split('/')[-1]) - origin = Origin(time=datetime, latitude=0, longitude=0) + origin = Origin(resource_id=self.resource_id, time=datetime, latitude=0, longitude=0) self.origins.append(origin) except: pass diff --git a/pylot/RELEASE-VERSION b/pylot/RELEASE-VERSION index 5f414d75..04102365 100644 --- a/pylot/RELEASE-VERSION +++ b/pylot/RELEASE-VERSION @@ -1 +1 @@ -410fe-dirty +929f-dirty From ed4c112e992dc7490dd4b1ea1f476e9c89fd2fc0 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Fri, 23 Jun 2017 14:56:56 +0200 Subject: [PATCH 55/79] [add] additional event information will be displayed in eventlist tab [bugfix] project.search_eventfile_info was using relative path for filename --- QtPyLoT.py | 60 +++++++++++++++++++++++++++++++------------ pylot/RELEASE-VERSION | 2 +- 2 files changed, 45 insertions(+), 17 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index 298d6c6f..6303e78e 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -43,6 +43,7 @@ import numpy as np from obspy import UTCDateTime from obspy.core.event import Event as ObsPyEvent from obspy.core.event import Origin, Magnitude, ResourceIdentifier +from obspy.core.util import AttribDict try: import pyqtgraph as pg @@ -1795,7 +1796,7 @@ class MainWindow(QMainWindow): for phase in stat_picks: picks = stat_picks[phase] - if type(stat_picks[phase]) is not dict: + if type(stat_picks[phase]) is not dict and type(stat_picks[phase]) is not AttribDict: return colors = phase_col[phase[0].upper()] @@ -2021,19 +2022,19 @@ class MainWindow(QMainWindow): # changes attributes of the corresponding event table = self.project._table event = self.project.getEventFromPath(table[row][0].text()) - if column == 3 or column == 4: + if column == 8 or column == 9: #toggle checked states (exclusive) - item_ref = table[row][3] - item_test = table[row][4] - if column == 3 and item_ref.checkState(): + item_ref = table[row][8] + item_test = table[row][9] + if column == 8 and item_ref.checkState(): item_test.setCheckState(QtCore.Qt.Unchecked) event.setRefEvent(True) - elif column == 3 and not item_ref.checkState(): + elif column == 8 and not item_ref.checkState(): event.setRefEvent(False) - elif column == 4 and item_test.checkState(): + elif column == 9 and item_test.checkState(): item_ref.setCheckState(QtCore.Qt.Unchecked) event.setTestEvent(True) - elif column == 4 and not item_test.checkState(): + elif column == 9 and not item_test.checkState(): event.setTestEvent(False) self.fill_eventbox() elif column == 5: @@ -2049,11 +2050,19 @@ class MainWindow(QMainWindow): # init new qtable self.event_table = QtGui.QTableWidget() - self.event_table.setColumnCount(6) + self.event_table.setColumnCount(11) self.event_table.setRowCount(len(eventlist)) - self.event_table.setHorizontalHeaderLabels(['Event', '[N] MP', - '[N] AP', 'Tuning Set', - 'Test Set', 'Notes']) + self.event_table.setHorizontalHeaderLabels(['Event', + 'Time', + 'Lat', + 'Lon', + 'Depth', + 'Mag', + '[N] MP', + '[N] AP', + 'Tuning Set', + 'Test Set', + 'Notes']) # iterate through eventlist and generate items for table rows self.project._table = [] @@ -2065,6 +2074,11 @@ class MainWindow(QMainWindow): if event.autopicks: event_nautopicks = len(event.autopicks) item_path = QtGui.QTableWidgetItem() + item_time = QtGui.QTableWidgetItem() + item_lat = QtGui.QTableWidgetItem() + item_lon = QtGui.QTableWidgetItem() + item_depth = QtGui.QTableWidgetItem() + item_mag = QtGui.QTableWidgetItem() item_nmp = QtGui.QTableWidgetItem(str(event_npicks)) item_nmp.setIcon(self.manupicksicon_small) item_nap = QtGui.QTableWidgetItem(str(event_nautopicks)) @@ -2076,7 +2090,19 @@ class MainWindow(QMainWindow): item_ref.setBackground(self._colors['ref']) item_test.setBackground(self._colors['test']) item_path.setText(event.path) - item_notes.setText(event.notes) + if hasattr(event, 'origins'): + if event.origins: + origin = event.origins[0] + item_time.setText(str(origin.time).split('.')[0]) + item_lon.setText(str(origin.longitude)) + item_lat.setText(str(origin.latitude)) + item_depth.setText(str(origin.depth)) + if hasattr(event, 'magnitudes'): + if event.magnitudes: + magnitude = event.magnitudes[0] + item_mag.setText(str(magnitude.mag)) + item_notes.setText(event.notes) + set_enabled(item_path, True, False) set_enabled(item_nmp, True, False) set_enabled(item_nap, True, False) @@ -2096,7 +2122,8 @@ class MainWindow(QMainWindow): else: item_test.setCheckState(QtCore.Qt.Unchecked) - column=[item_path, item_nmp, item_nap, item_ref, item_test, item_notes] + column=[item_path, item_time, item_lat, item_lon, item_depth, item_mag, + item_nmp, item_nap, item_ref, item_test, item_notes] self.project._table.append(column) for r_index, row in enumerate(self.project._table): @@ -2462,7 +2489,8 @@ class Project(object): for datapath in datapaths: datapath = os.path.join(self.rootpath, datapath) for filename in os.listdir(datapath): - if os.path.isfile(os.path.join(datapath, filename)) and filename.endswith(fext): + filename = os.path.join(datapath, filename) + if os.path.isfile(filename) and filename.endswith(fext): try: self.read_eventfile_info(filename) except Exception as e: @@ -2558,7 +2586,7 @@ class Event(ObsPyEvent): self.addNotes(text) try: datetime = UTCDateTime(path.split('/')[-1]) - origin = Origin(resource_id=self.resource_id, time=datetime, latitude=0, longitude=0) + origin = Origin(resource_id=self.resource_id, time=datetime, latitude=0, longitude=0, depth=0) self.origins.append(origin) except: pass diff --git a/pylot/RELEASE-VERSION b/pylot/RELEASE-VERSION index 04102365..cabd06a0 100644 --- a/pylot/RELEASE-VERSION +++ b/pylot/RELEASE-VERSION @@ -1 +1 @@ -929f-dirty +f409-dirty From 8e6c4d06ce13d4bcfe75eddffbe12a29a52ab2b9 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Fri, 23 Jun 2017 15:40:13 +0200 Subject: [PATCH 56/79] [add] array_map now plotting event location if given --- QtPyLoT.py | 6 ++++++ pylot/RELEASE-VERSION | 2 +- pylot/core/util/map_projection.py | 11 +++++++++-- 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index 6303e78e..f70573ae 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -1996,6 +1996,12 @@ class MainWindow(QMainWindow): if not self.array_map: return # refresh with new picks here!!! + event = self.get_current_event() + if hasattr(event, 'origins'): + if event.origins: + lat = event.origins[0].latitude + lon = event.origins[0].longitude + self.array_map.eventLoc = (lat, lon) self.array_map.refresh_drawings(self.get_current_event().getPicks()) self._eventChanged[1] = False diff --git a/pylot/RELEASE-VERSION b/pylot/RELEASE-VERSION index cabd06a0..d7691880 100644 --- a/pylot/RELEASE-VERSION +++ b/pylot/RELEASE-VERSION @@ -1 +1 @@ -f409-dirty +ed4c1-dirty diff --git a/pylot/core/util/map_projection.py b/pylot/core/util/map_projection.py index 64fd0444..8a577c83 100644 --- a/pylot/core/util/map_projection.py +++ b/pylot/core/util/map_projection.py @@ -22,6 +22,7 @@ class map_projection(QtGui.QWidget): self.parser = parent.metadata[1] self.picks = None self.picks_dict = None + self.eventLoc = None self.figure = figure self.init_graphics() self.init_stations() @@ -244,6 +245,10 @@ class map_projection(QtGui.QWidget): self.sc = self.basemap.scatter(self.lon, self.lat, s=50, facecolor='none', latlon=True, zorder=10, picker=True, edgecolor='m', label='Not Picked') self.cid = self.canvas.mpl_connect('pick_event', self.onpick) + if self.eventLoc: + lat, lon = self.eventLoc + self.sc_event = self.basemap.scatter(lon, lat, s=100, facecolor='red', + latlon=True, zorder=11, label='Event (might be outside map region)') def scatter_picked_stations(self): lon = self.lon_no_nan @@ -274,8 +279,7 @@ class map_projection(QtGui.QWidget): def refresh_drawings(self, picks=None): self.picks_dict = picks - self.remove_drawings() - self.draw_everything() + self._refresh_drawings() def _refresh_drawings(self): self.remove_drawings() @@ -303,6 +307,9 @@ class map_projection(QtGui.QWidget): if hasattr(self, 'sc_picked'): self.sc_picked.remove() del(self.sc_picked) + if hasattr(self, 'sc_event'): + self.sc_event.remove() + del(self.sc_event) if hasattr(self, 'cbar'): self.cbar.remove() del(self.cbar) From 9746913f8b737eb3130cda3e81dd9360b9cdc515 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Fri, 23 Jun 2017 15:45:45 +0200 Subject: [PATCH 57/79] Consistent prefix for all PyLoT-output. --- QtPyLoT.py | 4 ++-- pylot/RELEASE-VERSION | 2 +- pylot/core/io/data.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index 6acaa2b9..ea5af579 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -648,7 +648,7 @@ class MainWindow(QMainWindow): for event in events: path = event.path eventname = path.split('/')[-1] - filename = os.path.join(path, 'picks_'+eventname+fext) + filename = os.path.join(path, 'PyLoT_'+eventname+fext) if os.path.isfile(filename): self.load_data(filename, draw=False, event=event, overwrite=True) refresh=True @@ -992,7 +992,7 @@ class MainWindow(QMainWindow): print('warning: {0}'.format(e)) directory = self.get_current_event_path() eventname = self.get_current_event_name() - filename = 'picks_'+eventname + filename = 'PyLoT_'+eventname outpath = os.path.join(directory, filename) file_filter = "QuakeML file (*.xml);;VELEST observation file " \ "format (*.cnv);;NonLinLoc observation file (*.obs)" diff --git a/pylot/RELEASE-VERSION b/pylot/RELEASE-VERSION index 5f414d75..496682cc 100644 --- a/pylot/RELEASE-VERSION +++ b/pylot/RELEASE-VERSION @@ -1 +1 @@ -410fe-dirty +c9f0b-dirty diff --git a/pylot/core/io/data.py b/pylot/core/io/data.py index 358c597f..fb729132 100644 --- a/pylot/core/io/data.py +++ b/pylot/core/io/data.py @@ -95,7 +95,7 @@ class Data(object): def getPicksStr(self): picks_str = '' for pick in self.get_evt_data().picks: - picks_str += str(pick) + '\n' + picks_str += str(PyLoT) + '\n' return picks_str def getParent(self): From acaf592590e602b5180037239572bf1a112af928 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Mon, 26 Jun 2017 10:47:40 +0200 Subject: [PATCH 58/79] [move] class Event moved to pylot.core.util.utils --- QtPyLoT.py | 128 +-------------------------------------- pylot/core/io/data.py | 10 +-- pylot/core/util/utils.py | 125 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 132 insertions(+), 131 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index 5f005b9a..71a9a0d8 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -41,8 +41,7 @@ from PySide.QtGui import QMainWindow, QInputDialog, QIcon, QFileDialog, \ QTreeView, QComboBox, QTabWidget, QPushButton, QGridLayout import numpy as np from obspy import UTCDateTime -from obspy.core.event import Event as ObsPyEvent -from obspy.core.event import Origin, Magnitude, ResourceIdentifier +from obspy.core.event import Magnitude from obspy.core.util import AttribDict try: @@ -71,7 +70,7 @@ from pylot.core.util.errors import FormatError, DatastructureError, \ OverwriteError, ProcessingError 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, \ +from pylot.core.util.utils import Event, fnConstructor, getLogin, \ full_range from pylot.core.io.location import create_creation_info, create_event from pylot.core.util.widgets import FilterOptionsDialog, NewEventDlg, \ @@ -2561,129 +2560,6 @@ class Project(object): return project -class Event(ObsPyEvent): - ''' - Pickable class derived from ~obspy.core.event.Event containing information on a single event. - ''' - def __init__(self, path): - # initialize super class - super(Event, self).__init__(resource_id=ResourceIdentifier(path.split('/')[-1])) - self.path = path - self.database = path.split('/')[-2] - self.datapath = path.split('/')[-3] - self.rootpath = '/' + os.path.join(*path.split('/')[:-3]) - self.autopicks = {} - self.picks = {} - self.notes = '' - self._testEvent = False - self._refEvent = False - self.get_notes() - - def get_notes_path(self): - notesfile = os.path.join(self.path, 'notes.txt') - return notesfile - - def get_notes(self): - notesfile = self.get_notes_path() - if os.path.isfile(notesfile): - with open(notesfile) as infile: - path = str(infile.readlines()[0].split('\n')[0]) - text = '[eventInfo: '+path+']' - self.addNotes(text) - try: - datetime = UTCDateTime(path.split('/')[-1]) - origin = Origin(resource_id=self.resource_id, time=datetime, latitude=0, longitude=0, depth=0) - self.origins.append(origin) - except: - pass - - def addNotes(self, notes): - self.notes = str(notes) - - def clearNotes(self): - self.notes = None - - def isRefEvent(self): - return self._refEvent - - def isTestEvent(self): - return self._testEvent - - def setRefEvent(self, bool): - self._refEvent = bool - if bool: self._testEvent = False - - def setTestEvent(self, bool): - self._testEvent = bool - if bool: self._refEvent = False - - def addPicks(self, picks): - for station in picks: - self.picks[station] = picks[station] - - def addAutopicks(self, autopicks): - for station in autopicks: - self.autopicks[station] = autopicks[station] - - def setPick(self, station, pick): - if pick: - self.picks[station] = pick - - def setPicks(self, picks): - self.picks = picks - - def getPick(self, station): - if station in self.picks.keys(): - return self.picks[station] - - def getPicks(self): - return self.picks - - def setAutopick(self, station, autopick): - if autopick: - self.autopicks[station] = autopick - - def setAutopicks(self, autopicks): - self.autopicks = autopicks - - def getAutopick(self, station): - if station in self.autopicks.keys(): - return self.autopicks[station] - - def getAutopicks(self): - return self.autopicks - - def save(self, filename): - ''' - Save PyLoT Event to a file. - Can be loaded by using event.load(filename). - ''' - try: - import cPickle - except ImportError: - import _pickle as cPickle - - try: - outfile = open(filename, 'wb') - cPickle.dump(self, outfile, -1) - except Exception as e: - print('Could not pickle PyLoT event. Reason: {}'.format(e)) - - @staticmethod - def load(filename): - ''' - Load project from filename. - ''' - try: - import cPickle - except ImportError: - import _pickle as cPickle - infile = open(filename, 'rb') - event = cPickle.load(infile) - print('Loaded %s' % filename) - return event - - class getExistingDirectories(QFileDialog): ''' File dialog with possibility to select multiple folders. diff --git a/pylot/core/io/data.py b/pylot/core/io/data.py index fb729132..14ed32eb 100644 --- a/pylot/core/io/data.py +++ b/pylot/core/io/data.py @@ -6,11 +6,11 @@ import os from obspy import read_events from obspy.core import read, Stream, UTCDateTime from obspy.io.sac import SacIOError -from obspy.core.event import Event +from obspy.core.event import Event as ObsPyEvent from pylot.core.io.phases import readPILOTEvent, picks_from_picksdict, \ picksdict_from_pilot, merge_picks from pylot.core.util.errors import FormatError, OverwriteError -from pylot.core.util.utils import fnConstructor, full_range +from pylot.core.util.utils import Event, fnConstructor, full_range class Data(object): """ @@ -33,7 +33,7 @@ class Data(object): self.comp = 'Z' self.wfdata = Stream() self._new = False - if isinstance(evtdata, Event): + if isinstance(evtdata, ObsPyEvent) or isinstance(evtdata, Event): pass elif isinstance(evtdata, dict): evt = readPILOTEvent(**evtdata) @@ -49,7 +49,7 @@ class Data(object): if 'Unknown format for file' in e.message: if 'PHASES' in evtdata: picks = picksdict_from_pilot(evtdata) - evtdata = Event() + evtdata = ObsPyEvent() evtdata.picks = picks_from_picksdict(picks) elif 'LOC' in evtdata: raise NotImplementedError('PILOT location information ' @@ -61,7 +61,7 @@ class Data(object): raise e else: # create an empty Event object self.setNew() - evtdata = Event() + evtdata = ObsPyEvent() evtdata.picks = [] self.evtdata = evtdata self.wforiginal = None diff --git a/pylot/core/util/utils.py b/pylot/core/util/utils.py index 46fa699a..06d1d48c 100644 --- a/pylot/core/util/utils.py +++ b/pylot/core/util/utils.py @@ -10,9 +10,134 @@ import re import warnings import subprocess from obspy import UTCDateTime, read +from obspy.core.event import Event as ObsPyEvent +from obspy.core.event import Origin, Magnitude, ResourceIdentifier from pylot.core.io.inputs import PylotParameter +class Event(ObsPyEvent): + ''' + Pickable class derived from ~obspy.core.event.Event containing information on a single event. + ''' + def __init__(self, path): + # initialize super class + super(Event, self).__init__(resource_id=ResourceIdentifier(path.split('/')[-1])) + self.path = path + self.database = path.split('/')[-2] + self.datapath = path.split('/')[-3] + self.rootpath = '/' + os.path.join(*path.split('/')[:-3]) + self.autopicks = {} + self.picks = {} + self.notes = '' + self._testEvent = False + self._refEvent = False + self.get_notes() + + def get_notes_path(self): + notesfile = os.path.join(self.path, 'notes.txt') + return notesfile + + def get_notes(self): + notesfile = self.get_notes_path() + if os.path.isfile(notesfile): + with open(notesfile) as infile: + path = str(infile.readlines()[0].split('\n')[0]) + text = '[eventInfo: '+path+']' + self.addNotes(text) + try: + datetime = UTCDateTime(path.split('/')[-1]) + origin = Origin(resource_id=self.resource_id, time=datetime, latitude=0, longitude=0, depth=0) + self.origins.append(origin) + except: + pass + + def addNotes(self, notes): + self.notes = str(notes) + + def clearNotes(self): + self.notes = None + + def isRefEvent(self): + return self._refEvent + + def isTestEvent(self): + return self._testEvent + + def setRefEvent(self, bool): + self._refEvent = bool + if bool: self._testEvent = False + + def setTestEvent(self, bool): + self._testEvent = bool + if bool: self._refEvent = False + + def addPicks(self, picks): + for station in picks: + self.picks[station] = picks[station] + + def addAutopicks(self, autopicks): + for station in autopicks: + self.autopicks[station] = autopicks[station] + + def setPick(self, station, pick): + if pick: + self.picks[station] = pick + + def setPicks(self, picks): + self.picks = picks + + def getPick(self, station): + if station in self.picks.keys(): + return self.picks[station] + + def getPicks(self): + return self.picks + + def setAutopick(self, station, autopick): + if autopick: + self.autopicks[station] = autopick + + def setAutopicks(self, autopicks): + self.autopicks = autopicks + + def getAutopick(self, station): + if station in self.autopicks.keys(): + return self.autopicks[station] + + def getAutopicks(self): + return self.autopicks + + def save(self, filename): + ''' + Save PyLoT Event to a file. + Can be loaded by using event.load(filename). + ''' + try: + import cPickle + except ImportError: + import _pickle as cPickle + + try: + outfile = open(filename, 'wb') + cPickle.dump(self, outfile, -1) + except Exception as e: + print('Could not pickle PyLoT event. Reason: {}'.format(e)) + + @staticmethod + def load(filename): + ''' + Load project from filename. + ''' + try: + import cPickle + except ImportError: + import _pickle as cPickle + infile = open(filename, 'rb') + event = cPickle.load(infile) + print('Loaded %s' % filename) + return event + + def _pickle_method(m): if m.im_self is None: return getattr, (m.im_class, m.im_func.func_name) From d08bd4cc9ef96fdb73d2a7c8cbd60c4849ac35e5 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Mon, 26 Jun 2017 12:03:04 +0200 Subject: [PATCH 59/79] [move] class Event finally moved to new module event --- pylot/core/util/event.py | 131 +++++++++++++++++++++++++++++++++++++++ pylot/core/util/utils.py | 125 ------------------------------------- 2 files changed, 131 insertions(+), 125 deletions(-) create mode 100644 pylot/core/util/event.py diff --git a/pylot/core/util/event.py b/pylot/core/util/event.py new file mode 100644 index 00000000..98d26e9a --- /dev/null +++ b/pylot/core/util/event.py @@ -0,0 +1,131 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import os + +from obspy import UTCDateTime +from obspy.core.event import Event as ObsPyEvent +from obspy.core.event import Origin, Magnitude, ResourceIdentifier + + +class Event(ObsPyEvent): + ''' + Pickable class derived from ~obspy.core.event.Event containing information on a single event. + ''' + def __init__(self, path): + # initialize super class + super(Event, self).__init__(resource_id=ResourceIdentifier(path.split('/')[-1])) + self.path = path + self.database = path.split('/')[-2] + self.datapath = path.split('/')[-3] + self.rootpath = '/' + os.path.join(*path.split('/')[:-3]) + self.pylot_autopicks = {} + self.pylot_picks = {} + self.notes = '' + self._testEvent = False + self._refEvent = False + self.get_notes() + + def get_notes_path(self): + notesfile = os.path.join(self.path, 'notes.txt') + return notesfile + + def get_notes(self): + notesfile = self.get_notes_path() + if os.path.isfile(notesfile): + with open(notesfile) as infile: + path = str(infile.readlines()[0].split('\n')[0]) + text = '[eventInfo: '+path+']' + self.addNotes(text) + try: + datetime = UTCDateTime(path.split('/')[-1]) + origin = Origin(resource_id=self.resource_id, time=datetime, latitude=0, longitude=0, depth=0) + self.origins.append(origin) + except: + pass + + def addNotes(self, notes): + self.notes = str(notes) + + def clearNotes(self): + self.notes = None + + def isRefEvent(self): + return self._refEvent + + def isTestEvent(self): + return self._testEvent + + def setRefEvent(self, bool): + self._refEvent = bool + if bool: self._testEvent = False + + def setTestEvent(self, bool): + self._testEvent = bool + if bool: self._refEvent = False + + def addPicks(self, picks): + for station in picks: + self.pylot_picks[station] = picks[station] + + def addAutopicks(self, autopicks): + for station in autopicks: + self.pylot_autopicks[station] = autopicks[station] + + def setPick(self, station, pick): + if pick: + self.pylot_picks[station] = pick + + def setPicks(self, picks): + self.pylot_picks = picks + + def getPick(self, station): + if station in self.pylot_picks.keys(): + return self.pylot_picks[station] + + def getPicks(self): + return self.pylot_picks + + def setAutopick(self, station, autopick): + if autopick: + self.pylot_autopicks[station] = autopick + + def setAutopicks(self, autopicks): + self.pylot_autopicks = autopicks + + def getAutopick(self, station): + if station in self.pylot_autopicks.keys(): + return self.pylot_autopicks[station] + + def getAutopicks(self): + return self.pylot_autopicks + + def save(self, filename): + ''' + Save PyLoT Event to a file. + Can be loaded by using event.load(filename). + ''' + try: + import cPickle + except ImportError: + import _pickle as cPickle + + try: + outfile = open(filename, 'wb') + cPickle.dump(self, outfile, -1) + except Exception as e: + print('Could not pickle PyLoT event. Reason: {}'.format(e)) + + @staticmethod + def load(filename): + ''' + Load project from filename. + ''' + try: + import cPickle + except ImportError: + import _pickle as cPickle + infile = open(filename, 'rb') + event = cPickle.load(infile) + print('Loaded %s' % filename) + return event diff --git a/pylot/core/util/utils.py b/pylot/core/util/utils.py index 06d1d48c..586a54c3 100644 --- a/pylot/core/util/utils.py +++ b/pylot/core/util/utils.py @@ -10,133 +10,8 @@ import re import warnings import subprocess from obspy import UTCDateTime, read -from obspy.core.event import Event as ObsPyEvent -from obspy.core.event import Origin, Magnitude, ResourceIdentifier from pylot.core.io.inputs import PylotParameter - -class Event(ObsPyEvent): - ''' - Pickable class derived from ~obspy.core.event.Event containing information on a single event. - ''' - def __init__(self, path): - # initialize super class - super(Event, self).__init__(resource_id=ResourceIdentifier(path.split('/')[-1])) - self.path = path - self.database = path.split('/')[-2] - self.datapath = path.split('/')[-3] - self.rootpath = '/' + os.path.join(*path.split('/')[:-3]) - self.autopicks = {} - self.picks = {} - self.notes = '' - self._testEvent = False - self._refEvent = False - self.get_notes() - - def get_notes_path(self): - notesfile = os.path.join(self.path, 'notes.txt') - return notesfile - - def get_notes(self): - notesfile = self.get_notes_path() - if os.path.isfile(notesfile): - with open(notesfile) as infile: - path = str(infile.readlines()[0].split('\n')[0]) - text = '[eventInfo: '+path+']' - self.addNotes(text) - try: - datetime = UTCDateTime(path.split('/')[-1]) - origin = Origin(resource_id=self.resource_id, time=datetime, latitude=0, longitude=0, depth=0) - self.origins.append(origin) - except: - pass - - def addNotes(self, notes): - self.notes = str(notes) - - def clearNotes(self): - self.notes = None - - def isRefEvent(self): - return self._refEvent - - def isTestEvent(self): - return self._testEvent - - def setRefEvent(self, bool): - self._refEvent = bool - if bool: self._testEvent = False - - def setTestEvent(self, bool): - self._testEvent = bool - if bool: self._refEvent = False - - def addPicks(self, picks): - for station in picks: - self.picks[station] = picks[station] - - def addAutopicks(self, autopicks): - for station in autopicks: - self.autopicks[station] = autopicks[station] - - def setPick(self, station, pick): - if pick: - self.picks[station] = pick - - def setPicks(self, picks): - self.picks = picks - - def getPick(self, station): - if station in self.picks.keys(): - return self.picks[station] - - def getPicks(self): - return self.picks - - def setAutopick(self, station, autopick): - if autopick: - self.autopicks[station] = autopick - - def setAutopicks(self, autopicks): - self.autopicks = autopicks - - def getAutopick(self, station): - if station in self.autopicks.keys(): - return self.autopicks[station] - - def getAutopicks(self): - return self.autopicks - - def save(self, filename): - ''' - Save PyLoT Event to a file. - Can be loaded by using event.load(filename). - ''' - try: - import cPickle - except ImportError: - import _pickle as cPickle - - try: - outfile = open(filename, 'wb') - cPickle.dump(self, outfile, -1) - except Exception as e: - print('Could not pickle PyLoT event. Reason: {}'.format(e)) - - @staticmethod - def load(filename): - ''' - Load project from filename. - ''' - try: - import cPickle - except ImportError: - import _pickle as cPickle - infile = open(filename, 'rb') - event = cPickle.load(infile) - print('Loaded %s' % filename) - return event - def _pickle_method(m): if m.im_self is None: From b10b79ea792e8132f2985fc051acf8e96cd653ce Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Mon, 26 Jun 2017 12:19:06 +0200 Subject: [PATCH 60/79] [change] renamed picks attribute for pylot to pylot_picks, picks attribute of events will now be used exclusively for ObsPy picks --- QtPyLoT.py | 69 +++++++++++++++++++------------------- pylot/core/io/data.py | 9 ++--- pylot/core/io/phases.py | 3 +- pylot/core/util/event.py | 18 ++++++++-- pylot/core/util/widgets.py | 14 ++++---- 5 files changed, 64 insertions(+), 49 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index 71a9a0d8..567d57e0 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -70,8 +70,9 @@ from pylot.core.util.errors import FormatError, DatastructureError, \ OverwriteError, ProcessingError from pylot.core.util.connection import checkurl from pylot.core.util.dataprocessing import read_metadata, restitute_data -from pylot.core.util.utils import Event, fnConstructor, getLogin, \ +from pylot.core.util.utils import fnConstructor, getLogin, \ full_range +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, \ @@ -174,8 +175,8 @@ class MainWindow(QMainWindow): self.setupUi() self.filteroptions = {} - self.picks = {} - self.autopicks = {} + self.pylot_picks = {} + self.pylot_autopicks = {} self.loc = False def setupUi(self): @@ -654,7 +655,7 @@ class MainWindow(QMainWindow): refresh=True if not refresh: return - if self.get_current_event().picks: + if self.get_current_event().pylot_picks: self.plotWaveformDataThread() self.drawPicks(picktype=type) self.draw() @@ -675,7 +676,7 @@ class MainWindow(QMainWindow): if not loc: self.updatePicks(type=type, event=event) if draw: - if self.get_current_event().picks: + if self.get_current_event().pylot_picks: self.plotWaveformDataThread() self.drawPicks(picktype=type) self.draw() @@ -702,8 +703,8 @@ class MainWindow(QMainWindow): def getWFFnames(self): try: evt = self.get_data().get_evt_data() - if evt.picks: - for pick in evt.picks: + if evt.pylot_picks: + for pick in evt.pylot_picks: try: if pick.waveform_id is not None: fname = pick.waveform_id.getSEEDstring() @@ -906,10 +907,10 @@ class MainWindow(QMainWindow): event_path = event.path event_npicks = 0 event_nautopicks = 0 - if event.picks: - event_npicks = len(event.picks) - if event.autopicks: - event_nautopicks = len(event.autopicks) + if event.pylot_picks: + event_npicks = len(event.pylot_picks) + if event.pylot_autopicks: + event_nautopicks = len(event.pylot_autopicks) event_ref = event.isRefEvent() event_test = event.isTestEvent() @@ -1013,7 +1014,7 @@ class MainWindow(QMainWindow): fbasename = self.getEventFileName() exform = settings.value('data/exportFormat', 'QUAKEML') try: - self.get_data().applyEVTData(self.getPicks()) + self.get_data().applyEVTData(self.get_current_event(), typ='event')#getPicks()) except OverwriteError: # msgBox = QMessageBox() # msgBox.setText("Picks have been modified!") @@ -1083,7 +1084,7 @@ class MainWindow(QMainWindow): return self.get_current_event().getPicks() if type == 'auto': return self.get_current_event().getAutopicks() - # rdict = dict(auto=self.autopicks, manual=self.picks) + # rdict = dict(auto=self.pylot_autopicks, manual=self.pylot_picks) # return rdict[type] def getPicksOnStation(self, station, type='manual'): @@ -1157,7 +1158,7 @@ class MainWindow(QMainWindow): if event: self.ref_event_button.setChecked(event.isRefEvent()) self.test_event_button.setChecked(event.isTestEvent()) - self.enableRefTestButtons(bool(self.get_current_event().picks)) + self.enableRefTestButtons(bool(self.get_current_event().pylot_picks)) return self.ref_event_button.setChecked(False) self.test_event_button.setChecked(False) @@ -1216,14 +1217,14 @@ class MainWindow(QMainWindow): if not event: return # update picks saved in GUI mainwindow (to be changed in future!!) MP MP - if not event.picks: - self.picks = {} + if not event.pylot_picks: + self.pylot_picks = {} else: - self.picks = event.picks - if not event.autopicks: - self.autopicks = {} + self.pylot_picks = event.pylot_picks + if not event.pylot_autopicks: + self.pylot_autopicks = {} else: - self.autopicks = event.autopicks + self.pylot_autopicks = event.pylot_autopicks # if current tab is waveformPlot-tab and the data in this tab was not yet refreshed if self.tabs.currentIndex() == 0: if self._eventChanged[0]: @@ -1369,12 +1370,12 @@ class MainWindow(QMainWindow): self.openautopicksaction.setEnabled(True) self.loadpilotevent.setEnabled(True) event = self.get_current_event() - if event.picks: - self.picks = event.picks + if event.pylot_picks: + self.pylot_picks = event.pylot_picks self.drawPicks(picktype='manual') self.enableSaveManualPicksAction() - if event.autopicks: - self.autopicks = event.autopicks + if event.pylot_autopicks: + self.pylot_autopicks = event.pylot_autopicks self.drawPicks(picktype='auto') self.compare_action.setEnabled(True) self.draw() @@ -1755,10 +1756,10 @@ class MainWindow(QMainWindow): picks = picksdict_from_picks(evt=self.get_data(type).get_evt_data()) if type == 'manual': event.addPicks(picks) - self.picks.update(picks) + self.pylot_picks.update(picks) elif type == 'auto': event.addAutopicks(picks) - self.autopicks.update(picks) + self.pylot_autopicks.update(picks) self.check4Comparison() def drawPicks(self, station=None, picktype='manual'): @@ -1909,8 +1910,8 @@ class MainWindow(QMainWindow): finally: os.remove(phasepath) - self.get_data().applyEVTData(lt.read_location(locpath), type='event') - self.get_data().applyEVTData(self.calc_magnitude(), type='event') + self.get_data().applyEVTData(lt.read_location(locpath), typ='event') + self.get_data().applyEVTData(self.calc_magnitude(), typ='event') def init_array_tab(self): ''' @@ -2074,10 +2075,10 @@ class MainWindow(QMainWindow): for index, event in enumerate(eventlist): event_npicks = 0 event_nautopicks = 0 - if event.picks: - event_npicks = len(event.picks) - if event.autopicks: - event_nautopicks = len(event.autopicks) + if event.pylot_picks: + event_npicks = len(event.pylot_picks) + if event.pylot_autopicks: + event_nautopicks = len(event.pylot_autopicks) item_path = QtGui.QTableWidgetItem() item_time = QtGui.QTableWidgetItem() item_lat = QtGui.QTableWidgetItem() @@ -2111,7 +2112,7 @@ class MainWindow(QMainWindow): set_enabled(item_path, True, False) set_enabled(item_nmp, True, False) set_enabled(item_nap, True, False) - if event.picks: + if event.pylot_picks: set_enabled(item_ref, True, True) set_enabled(item_test, True, True) else: @@ -2376,7 +2377,7 @@ class MainWindow(QMainWindow): self.setDirty(True) def setDirty(self, value): - self.saveProjectAction.setEnabled(value) + self.saveProjectAction.setEnabled(bool(self.get_current_event().picks)) self.saveProjectAsAction.setEnabled(True) self.project.setDirty(value) self.dirty = value diff --git a/pylot/core/io/data.py b/pylot/core/io/data.py index 14ed32eb..98ff0e39 100644 --- a/pylot/core/io/data.py +++ b/pylot/core/io/data.py @@ -10,7 +10,8 @@ from obspy.core.event import Event as ObsPyEvent from pylot.core.io.phases import readPILOTEvent, picks_from_picksdict, \ picksdict_from_pilot, merge_picks from pylot.core.util.errors import FormatError, OverwriteError -from pylot.core.util.utils import Event, fnConstructor, full_range +from pylot.core.util.utils import fnConstructor, full_range +from pylot.core.util.event import Event class Data(object): """ @@ -279,12 +280,12 @@ class Data(object): def setEvtData(self, event): self.evtdata = event - def applyEVTData(self, data, type='pick', authority_id='rub'): + def applyEVTData(self, data, typ='pick', authority_id='rub'): """ :param data: - :param type: + :param typ: :param authority_id: :raise OverwriteError: """ @@ -338,7 +339,7 @@ class Data(object): applydata = {'pick': applyPicks, 'event': applyEvent} - applydata[type](data) + applydata[typ](data) class GenericDataStructure(object): diff --git a/pylot/core/io/phases.py b/pylot/core/io/phases.py index 43ff2de3..30f8950c 100644 --- a/pylot/core/io/phases.py +++ b/pylot/core/io/phases.py @@ -7,6 +7,7 @@ import os import scipy.io as sio import warnings from obspy.core import UTCDateTime +from obspy.core.util import AttribDict from pylot.core.io.inputs import PylotParameter from pylot.core.io.location import create_arrival, create_event, \ @@ -231,7 +232,7 @@ def picks_from_picksdict(picks, creation_info=None): picks_list = list() for station, onsets in picks.items(): for label, phase in onsets.items(): - if not isinstance(phase, dict): + if not isinstance(phase, dict) and not isinstance(phase, AttribDict): continue onset = phase['mpp'] try: diff --git a/pylot/core/util/event.py b/pylot/core/util/event.py index 98d26e9a..48667a07 100644 --- a/pylot/core/util/event.py +++ b/pylot/core/util/event.py @@ -7,6 +7,8 @@ from obspy import UTCDateTime from obspy.core.event import Event as ObsPyEvent from obspy.core.event import Origin, Magnitude, ResourceIdentifier +from pylot.core.io.phases import picks_from_picksdict + class Event(ObsPyEvent): ''' @@ -65,8 +67,13 @@ class Event(ObsPyEvent): if bool: self._refEvent = False def addPicks(self, picks): + ''' + add pylot picks and overwrite existing + ''' for station in picks: self.pylot_picks[station] = picks[station] + #add ObsPy picks + self.picks = picks_from_picksdict(self.pylot_picks) def addAutopicks(self, autopicks): for station in autopicks: @@ -75,10 +82,15 @@ class Event(ObsPyEvent): def setPick(self, station, pick): if pick: self.pylot_picks[station] = pick - - def setPicks(self, picks): - self.pylot_picks = picks + self.picks = picks_from_picksdict(self.pylot_picks) + def setPicks(self, picks): + ''' + set pylot picks and delete and overwrite all existing + ''' + self.pylot_picks = picks + self.picks = picks_from_picksdict(self.pylot_picks) + def getPick(self, station): if station in self.pylot_picks.keys(): return self.pylot_picks[station] diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index 23c168c7..7cf891ed 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -1646,7 +1646,7 @@ class TuneAutopicker(QWidget): model = self.stationBox.model() for network, station in stations: item = QtGui.QStandardItem(network+'.'+station) - if station in self.get_current_event().picks: + if station in self.get_current_event().pylot_picks: item.setBackground(self.parent._colors['ref']) model.appendRow(item) @@ -1698,13 +1698,13 @@ class TuneAutopicker(QWidget): def get_current_event_picks(self, station): event = self.get_current_event() - if station in event.picks.keys(): - return event.picks[station] + if station in event.pylot_picks.keys(): + return event.pylot_picks[station] def get_current_event_autopicks(self, station): event = self.get_current_event() - if event.autopicks: - return event.autopicks[station] + if event.pylot_autopicks: + return event.pylot_autopicks[station] def get_current_station(self): return str(self.stationBox.currentText()).split('.')[-1] @@ -1917,8 +1917,8 @@ class TuneAutopicker(QWidget): self._warn('Could not execute picker:\n{}'.format( self.ap_thread._executedError)) return - self.picks = self.ap_thread.data - if not self.picks: + self.pylot_picks = self.ap_thread.data + if not self.pylot_picks: self._warn('No picks found. See terminal output.') return #renew tabs From 581a85b81f01f41560fe6c8ee6f8854224ab8518 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Mon, 26 Jun 2017 13:58:26 +0200 Subject: [PATCH 61/79] [bugfix] missing network code in picksdict_from_picks triggered silent Exception using picks generated by picks_from_picksdict --- pylot/core/io/data.py | 2 +- pylot/core/io/phases.py | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/pylot/core/io/data.py b/pylot/core/io/data.py index 98ff0e39..eb424094 100644 --- a/pylot/core/io/data.py +++ b/pylot/core/io/data.py @@ -61,7 +61,7 @@ class Data(object): else: raise e else: # create an empty Event object - self.setNew() + #self.setNew() evtdata = ObsPyEvent() evtdata.picks = [] self.evtdata = evtdata diff --git a/pylot/core/io/phases.py b/pylot/core/io/phases.py index 30f8950c..e513f4b6 100644 --- a/pylot/core/io/phases.py +++ b/pylot/core/io/phases.py @@ -196,6 +196,7 @@ def picksdict_from_picks(evt): phase = {} station = pick.waveform_id.station_code channel = pick.waveform_id.channel_code + network = pick.waveform_id.network_code try: onsets = picks[station] except KeyError as e: @@ -216,6 +217,7 @@ def picksdict_from_picks(evt): phase['lpp'] = lpp phase['spe'] = spe phase['channel'] = channel + phase['network'] = network try: picker = str(pick.method_id) if picker.startswith('smi:local/'): From c07642653d0b7bd424ed46f90f5a22092080daac Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Mon, 26 Jun 2017 14:08:17 +0200 Subject: [PATCH 62/79] [change] default open path for load_data is event folder --- QtPyLoT.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index 567d57e0..9a51873b 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -968,7 +968,7 @@ class MainWindow(QMainWindow): caption = "Open an event file" fname = QFileDialog().getOpenFileName(self, caption=caption, filter=filt, - dir=self.getRoot()) + dir=self.get_current_event_path()) fname = fname[0] else: fname = str(action.data().toString()) From 42c288ad82950ed856788b6d15db4fab8016e45b Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Mon, 26 Jun 2017 14:10:40 +0200 Subject: [PATCH 63/79] [bugfix] return when no file is selected on load_data --- QtPyLoT.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/QtPyLoT.py b/QtPyLoT.py index 9a51873b..e608cc75 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -669,6 +669,8 @@ class MainWindow(QMainWindow): action = self.sender() if isinstance(action, QAction): fname = self.filename_from_action(action) + if not fname: + return self.set_fname(fname, type) data = dict(auto=self.autodata, manual=self.data) data[type] += Data(self, evtdata=fname) From b0405cf2139bb6ce7328dac24fc69bc77d6b9615 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Mon, 26 Jun 2017 14:22:00 +0200 Subject: [PATCH 64/79] [change] load AUTO picks button will be reused now (old function deprecated as there is only one file for both types of picks) to automatically load all event information for all events given (TODO: currently only XML files!) --- QtPyLoT.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index e608cc75..1917265f 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -314,21 +314,21 @@ class MainWindow(QMainWindow): # self.createNewEvent, # QKeySequence.New, newIcon, # "Create a new event.") - self.openmanualpicksaction = self.createAction(self, "Load &manual picks ...", + self.openmanualpicksaction = self.createAction(self, "Load event ...", self.load_data, "Ctrl+M", manupicksicon, - "Load manual picks for " + "Load event information for " "the displayed event.") self.openmanualpicksaction.setEnabled(False) self.openmanualpicksaction.setData(None) - self.openautopicksaction = self.createAction(self, "Load &automatic picks ... ", - self.load_autopicks, + self.openautopicksaction = self.createAction(self, "Load event information &automatically ... ", + self.load_multiple_data, "Ctrl+A", autopicksicon, - "Load automatic picks " - "for the displayed event.") + "Load event data automatically " + "for for all events.") self.openautopicksaction.setEnabled(False) self.openautopicksaction.setData(None) From bec86049049393e8a0b638a14bda73859e4af69e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Mon, 26 Jun 2017 14:47:36 +0200 Subject: [PATCH 65/79] autoPyLoT appends manual picks to xml-file, and manualPyLoT appends automatic picks to xml-file. --- autoPyLoT.py | 2 +- pylot/core/io/data.py | 35 +++++++++++++++++++++++++++++------ 2 files changed, 30 insertions(+), 7 deletions(-) diff --git a/autoPyLoT.py b/autoPyLoT.py index 84d24dea..a5ca4035 100755 --- a/autoPyLoT.py +++ b/autoPyLoT.py @@ -353,7 +353,7 @@ def autoPyLoT(input_dict=None, parameter=None, inputfile=None, fnames=None, even if evt is not None: data.applyEVTData(evt, 'event') fnqml = '%s/PyLoT_%s' % (event, evID) - data.exportEvent(fnqml) + data.exportEvent(fnqml, fnext='.xml', fcheck='manual') # HYPO71 hypo71file = '%s/PyLoT_%s_HYPO71_phases' % (event, evID) hypo71.export(picks, hypo71file, parameter) diff --git a/pylot/core/io/data.py b/pylot/core/io/data.py index eb424094..4b1f5018 100644 --- a/pylot/core/io/data.py +++ b/pylot/core/io/data.py @@ -145,12 +145,13 @@ class Data(object): # handle forbidden filenames especially on windows systems return fnConstructor(str(ID)) - def exportEvent(self, fnout, fnext='.xml'): + def exportEvent(self, fnout, fnext='.xml', fcheck='auto'): """ :param fnout: :param fnext: + :param fcheck: :raise KeyError: """ from pylot.core.util.defaults import OUTPUTFORMATS @@ -161,13 +162,35 @@ class Data(object): errmsg = '{0}; selected file extension {1} not ' \ 'supported'.format(e, fnext) raise FormatError(errmsg) + + # check for already existing xml-file + if fnext == '.xml': + if os.path.isfile(fnout + fnext): + print("xml-file already exists! Check content ...") + cat_old = read_events(fnout + fnext) + checkflag = 0 + for j in range(len(cat_old.events[0].picks)): + if cat_old.events[0].picks[j].method_id.id.split('/')[1] == fcheck: + print("Found %s pick(s), append to new catalog." % fcheck) + checkflag = 1 + break + if checkflag == 1: + self.get_evt_data().write(fnout + fnext, format=evtformat) + cat_new = read_events(fnout + fnext) + cat_new.append(cat_old.events[0]) + cat_new.write(fnout + fnext, format=evtformat) + else: + self.get_evt_data().write(fnout + fnext, format=evtformat) + else: + self.get_evt_data().write(fnout + fnext, format=evtformat) # try exporting event via ObsPy - try: - self.get_evt_data().write(fnout + fnext, format=evtformat) - except KeyError as e: - raise KeyError('''{0} export format - not implemented: {1}'''.format(evtformat, e)) + else: + try: + self.get_evt_data().write(fnout + fnext, format=evtformat) + except KeyError as e: + raise KeyError('''{0} export format + not implemented: {1}'''.format(evtformat, e)) def getComp(self): """ From c56a11634a90d9027ff703653d0ba00d60c35cb0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Mon, 26 Jun 2017 16:10:18 +0200 Subject: [PATCH 66/79] Uncomment setNew, though still not sure, what its all about. --- pylot/core/io/data.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pylot/core/io/data.py b/pylot/core/io/data.py index 4b1f5018..f1838abf 100644 --- a/pylot/core/io/data.py +++ b/pylot/core/io/data.py @@ -61,7 +61,7 @@ class Data(object): else: raise e else: # create an empty Event object - #self.setNew() + self.setNew() evtdata = ObsPyEvent() evtdata.picks = [] self.evtdata = evtdata From a0633cafa41b96ba1f29be57dd9227eb960f0b8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Mon, 26 Jun 2017 16:19:57 +0200 Subject: [PATCH 67/79] Prevent export of phases when tuning picker. --- autoPyLoT.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/autoPyLoT.py b/autoPyLoT.py index a5ca4035..61634842 100755 --- a/autoPyLoT.py +++ b/autoPyLoT.py @@ -354,13 +354,13 @@ def autoPyLoT(input_dict=None, parameter=None, inputfile=None, fnames=None, even data.applyEVTData(evt, 'event') fnqml = '%s/PyLoT_%s' % (event, evID) data.exportEvent(fnqml, fnext='.xml', fcheck='manual') - # HYPO71 - hypo71file = '%s/PyLoT_%s_HYPO71_phases' % (event, evID) - hypo71.export(picks, hypo71file, parameter) - # HYPOSAT - hyposatfile = '%s/PyLoT_%s_HYPOSAT_phases' % (event, evID) - hyposat.export(picks, hyposatfile, parameter) if locflag == 1: + # HYPO71 + hypo71file = '%s/PyLoT_%s_HYPO71_phases' % (event, evID) + hypo71.export(picks, hypo71file, parameter) + # HYPOSAT + hyposatfile = '%s/PyLoT_%s_HYPOSAT_phases' % (event, evID) + hyposat.export(picks, hyposatfile, parameter) # VELEST velestfile = '%s/PyLoT_%s_VELEST_phases.cnv' % (event, evID) velest.export(picks, velestfile, parameter, evt) From 71da6ded2b16cb6aeda491fbe0d05c74461578f0 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Mon, 26 Jun 2017 17:40:36 +0200 Subject: [PATCH 68/79] [bugfix] Error when getEventFromPath returned None --- pylot/core/util/widgets.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index 7cf891ed..6eda72b8 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -1868,11 +1868,12 @@ class TuneAutopicker(QWidget): path = self.eventBox.itemText(index) if project.getEventFromPath(path).isTestEvent(): for index in range(nevents): - path = self.eventBox.itemText(index) - if not project.getEventFromPath(index).isTestEvent(): - break - #in case all events are marked as test events - elif index == nevents - 1: + path = self.eventBox.itemText(index) + if project.getEventFromPath(index): + if not project.getEventFromPath(index).isTestEvent(): + break + #in case all events are marked as test events and last event is reached + if index == nevents - 1: index = -1 self.eventBox.setCurrentIndex(index) if not index == index_start: From 30e2e1df84d6856b5a327f3b9b44ae99ae312c02 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Mon, 26 Jun 2017 17:41:08 +0200 Subject: [PATCH 69/79] [bugfix] query for data flag/attribute isNew changed --- QtPyLoT.py | 1 + pylot/core/io/data.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index 1917265f..3426fa51 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -146,6 +146,7 @@ class MainWindow(QMainWindow): self.data = Data(self, lastEvent) else: self.data = Data(self) + self.data._new = False self.autodata = Data(self) if settings.value("user/FullName", None) is None: diff --git a/pylot/core/io/data.py b/pylot/core/io/data.py index f1838abf..fc8387fc 100644 --- a/pylot/core/io/data.py +++ b/pylot/core/io/data.py @@ -350,7 +350,7 @@ class Data(object): information on the event to the actual data :param event: """ - if not self.isNew(): + if self.isNew(): self.setEvtData(event) else: # prevent overwriting original pick information From 53089a32795adf86104b027b9490dc9997906851 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Mon, 26 Jun 2017 18:02:07 +0200 Subject: [PATCH 70/79] [add] rootpath will be applied to QSettings as well --- QtPyLoT.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/QtPyLoT.py b/QtPyLoT.py index 3426fa51..a889d278 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -818,6 +818,10 @@ class MainWindow(QMainWindow): } print('Warning: Could not automatically init folder structure. ({})'.format(e)) + settings = QSettings() + settings.setValue("data/dataRoot", dirs['rootpath']) + settings.sync() + if not self.project.eventlist: #init parameter object self.setParameter(show=False) From a8bfc3c68fa61be26aec4d8ce901600f14d94edb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Tue, 27 Jun 2017 09:44:38 +0200 Subject: [PATCH 71/79] Parameter eventID uses wildcard (*) if all events of database supposed to be processed. --- autoPyLoT.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/autoPyLoT.py b/autoPyLoT.py index 61634842..8348a14f 100755 --- a/autoPyLoT.py +++ b/autoPyLoT.py @@ -98,8 +98,8 @@ def autoPyLoT(input_dict=None, parameter=None, inputfile=None, fnames=None, even exf = ['root', 'dpath', 'dbase'] - if parameter.hasParam('eventID') and fnames == 'None': - dsfields['eventID'] = parameter.get('eventID') + if parameter['eventID'] is not '*' and fnames == 'None': + dsfields['eventID'] = parameter['eventID'] exf.append('eventID') datastructure.modifyFields(**dsfields) @@ -133,13 +133,13 @@ def autoPyLoT(input_dict=None, parameter=None, inputfile=None, fnames=None, even if not input_dict: # started in production mode datapath = datastructure.expandDataPath() - if fnames == 'None' and not parameter['eventID']: + if fnames == 'None' and parameter['eventID'] is '*': # multiple event processing # read each event in database events = [events for events in glob.glob(os.path.join(datapath, '*')) if os.path.isdir(events)] - elif fnames == 'None' and parameter['eventID']: + elif fnames == 'None' and parameter['eventID'] is not '*': # single event processing - events = glob.glob(os.path.join(datapath, parameter.get('eventID'))) + events = glob.glob(os.path.join(datapath, parameter['eventID'])) else: # autoPyLoT was initialized from GUI events = [] From 4e735b3672a3524617eeb23386c7ed0ff7fd3f3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Tue, 27 Jun 2017 09:57:00 +0200 Subject: [PATCH 72/79] [Bugfix] If too few onsets are available, no export of phases (except xml). --- autoPyLoT.py | 1 + 1 file changed, 1 insertion(+) diff --git a/autoPyLoT.py b/autoPyLoT.py index 8348a14f..96e26e73 100755 --- a/autoPyLoT.py +++ b/autoPyLoT.py @@ -274,6 +274,7 @@ def autoPyLoT(input_dict=None, parameter=None, inputfile=None, fnames=None, even else: print("autoPyLoT: No NLLoc-location file available!") print("No source parameter estimation possible!") + locflag = 9 else: # get theoretical P-onset times from NLLoc-location file locsearch = '%s/loc/%s.????????.??????.grid?.loc.hyp' % (nllocroot, nllocout) From 987f5c4d6219b3c5e270ad220f70d33501b64da7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Tue, 27 Jun 2017 10:11:11 +0200 Subject: [PATCH 73/79] Additional info for user. --- pylot/core/io/default_parameters.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pylot/core/io/default_parameters.py b/pylot/core/io/default_parameters.py index 18baca6c..35a3a619 100644 --- a/pylot/core/io/default_parameters.py +++ b/pylot/core/io/default_parameters.py @@ -14,7 +14,7 @@ defaults = {'rootpath': {'type': str, 'value': ''}, 'eventID': {'type': str, - 'tooltip': 'event ID for single event processing', + 'tooltip': 'event ID for single event processing (* for all events found in database)', 'value': ''}, 'extent': {'type': str, From ab260e45f270e5e3150e4f93c96ce72b2c49bfe4 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Tue, 27 Jun 2017 10:39:44 +0200 Subject: [PATCH 74/79] [add/bugfix] data._new = False after first picks application --- autoPyLoT.py | 1 + 1 file changed, 1 insertion(+) diff --git a/autoPyLoT.py b/autoPyLoT.py index 96e26e73..55aed248 100755 --- a/autoPyLoT.py +++ b/autoPyLoT.py @@ -351,6 +351,7 @@ def autoPyLoT(input_dict=None, parameter=None, inputfile=None, fnames=None, even # and fault mechanism calculation routines # ObsPy event object data.applyEVTData(picks) + data._new = False if evt is not None: data.applyEVTData(evt, 'event') fnqml = '%s/PyLoT_%s' % (event, evID) From a90e0bd78b83e3d5280b2ebb4ef1fcd73ad0b7ac Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Tue, 27 Jun 2017 10:49:11 +0200 Subject: [PATCH 75/79] [change] data._new moved to applyEVTData --- autoPyLoT.py | 1 - pylot/core/io/data.py | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/autoPyLoT.py b/autoPyLoT.py index 55aed248..96e26e73 100755 --- a/autoPyLoT.py +++ b/autoPyLoT.py @@ -351,7 +351,6 @@ def autoPyLoT(input_dict=None, parameter=None, inputfile=None, fnames=None, even # and fault mechanism calculation routines # ObsPy event object data.applyEVTData(picks) - data._new = False if evt is not None: data.applyEVTData(evt, 'event') fnqml = '%s/PyLoT_%s' % (event, evID) diff --git a/pylot/core/io/data.py b/pylot/core/io/data.py index fc8387fc..2efcb4c9 100644 --- a/pylot/core/io/data.py +++ b/pylot/core/io/data.py @@ -363,6 +363,8 @@ class Data(object): 'event': applyEvent} applydata[typ](data) + self._new = False + class GenericDataStructure(object): From 68ef1276174a19bf08c5a069d819154ebeb017a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Tue, 27 Jun 2017 11:46:21 +0200 Subject: [PATCH 76/79] [Bugfix] Network code was missing. --- pylot/core/io/phases.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pylot/core/io/phases.py b/pylot/core/io/phases.py index e513f4b6..f737e630 100644 --- a/pylot/core/io/phases.py +++ b/pylot/core/io/phases.py @@ -838,9 +838,10 @@ def merge_picks(event, picks): err = pick.time_errors phase = pick.phase_hint station = pick.waveform_id.station_code + network = pick.waveform_id.network_code method = pick.method_id for p in event.picks: if p.waveform_id.station_code == station and p.phase_hint == phase: - p.time, p.time_errors, p.method_id = time, err, method - del time, err, phase, station, method + p.time, p.time_errors, p.waveform_id.network_code, p.method_id = time, err, network, method + del time, err, phase, station, network, method return event From 02da277f04aee86036a298d4857927b146ec6b13 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Tue, 27 Jun 2017 13:54:13 +0200 Subject: [PATCH 77/79] [bugfix] loading data from XML used same event multiple times as it was still part of current data object --- QtPyLoT.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index a889d278..dacfa693 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -657,9 +657,7 @@ class MainWindow(QMainWindow): if not refresh: return if self.get_current_event().pylot_picks: - self.plotWaveformDataThread() - self.drawPicks(picktype=type) - self.draw() + self.refreshEvents() self.setDirty(True) def load_data(self, fname=None, type='manual', loc=False, draw=True, event=None, overwrite=False): @@ -673,16 +671,18 @@ class MainWindow(QMainWindow): if not fname: return self.set_fname(fname, type) - data = dict(auto=self.autodata, manual=self.data) - data[type] += Data(self, evtdata=fname) + #data = dict(auto=self.autodata, manual=self.data) + if not event: + event = self.get_current_event() + data = Data(self, event) + data += Data(self, evtdata=fname) + self.data = data print('Loading {} picks from file {}.'.format(type, fname)) if not loc: self.updatePicks(type=type, event=event) if draw: if self.get_current_event().pylot_picks: - self.plotWaveformDataThread() - self.drawPicks(picktype=type) - self.draw() + self.refreshEvents() self.setDirty(True) def add_recentfile(self, event): From a3487b66eab1aef2b65df7edc03c73d32fc03762 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Tue, 27 Jun 2017 13:59:00 +0200 Subject: [PATCH 78/79] [Bugfix] Take care that chosen window lengths fit to length of time series. --- pylot/core/analysis/magnitude.py | 2 ++ pylot/core/pick/picker.py | 8 ++++++++ 2 files changed, 10 insertions(+) diff --git a/pylot/core/analysis/magnitude.py b/pylot/core/analysis/magnitude.py index 867a302d..c1eee9cd 100644 --- a/pylot/core/analysis/magnitude.py +++ b/pylot/core/analysis/magnitude.py @@ -215,6 +215,8 @@ class LocalMagnitude(Magnitude): th = np.arange(0, len(sqH) * dt, dt) # get maximum peak within pick window iwin = getsignalwin(th, t0 - stime, self.calc_win) + ii = min([iwin[len(iwin)-1], len(th)]) + iwin = iwin[0:ii] wapp = np.max(sqH[iwin]) if self.verbose: print("Determined Wood-Anderson peak-to-peak amplitude for station {0}: {1} " diff --git a/pylot/core/pick/picker.py b/pylot/core/pick/picker.py index a6cd7452..ebf737a5 100644 --- a/pylot/core/pick/picker.py +++ b/pylot/core/pick/picker.py @@ -212,6 +212,14 @@ class AICPicker(AutoPicker): self.Data[0].data = self.Data[0].data * 1000000 # get signal window isignal = getsignalwin(self.Tcf, self.Pick, self.TSNR[2]) + ii = min([isignal[len(isignal)-1], len(self.Tcf)]) + isignal = isignal[0:ii] + try: + aic[isignal] + except IndexError as e: + msg = "Time series out of bounds! {}".format(e) + print(msg) + return # calculate SNR from CF self.SNR = max(abs(aic[isignal] - np.mean(aic[isignal]))) / \ max(abs(aic[inoise] - np.mean(aic[inoise]))) From f1f929537002065c37cda1dfb67d3e7cf8948ca4 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Tue, 27 Jun 2017 15:06:28 +0200 Subject: [PATCH 79/79] [add] user prompt when event_id not matching on load_data --- QtPyLoT.py | 19 ++++++++++++++++++- pylot/core/io/data.py | 14 +++++++++++--- pylot/core/util/event.py | 3 ++- 3 files changed, 31 insertions(+), 5 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index dacfa693..e429307e 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -675,7 +675,24 @@ class MainWindow(QMainWindow): if not event: event = self.get_current_event() data = Data(self, event) - data += Data(self, evtdata=fname) + try: + data_new = Data(self, evtdata=fname) + data += data_new + except ValueError: + qmb = QMessageBox(self, icon=QMessageBox.Question, + text='Warning: Missmatch in event identifiers {} and {}. Continue?'.format( + data_new.get_evt_data().resource_id, + data.get_evt_data().resource_id), + windowTitle='PyLoT - Load data warning') + qmb.setStandardButtons(QMessageBox.Yes | QMessageBox.No) + qmb.setDefaultButton(QMessageBox.No) + ret = qmb.exec_() + if ret == qmb.Yes: + data_new.setNew() + data += data_new + else: + return + self.data = data print('Loading {} picks from file {}.'.format(type, fname)) if not loc: diff --git a/pylot/core/io/data.py b/pylot/core/io/data.py index 2efcb4c9..47e74053 100644 --- a/pylot/core/io/data.py +++ b/pylot/core/io/data.py @@ -74,6 +74,8 @@ class Data(object): def __add__(self, other): assert isinstance(other, Data), "operands must be of same type 'Data'" + rs_id = self.get_evt_data().get('resource_id') + rs_id_other = other.get_evt_data().get('resource_id') if other.isNew() and not self.isNew(): picks_to_add = other.get_evt_data().picks old_picks = self.get_evt_data().picks @@ -85,7 +87,7 @@ class Data(object): self.evtdata = new.get_evt_data() elif self.isNew() and other.isNew(): pass - elif self.get_evt_data().get('id') == other.get_evt_data().get('id'): + elif rs_id == rs_id_other: other.setNew() return self + other else: @@ -354,10 +356,16 @@ class Data(object): self.setEvtData(event) else: # prevent overwriting original pick information - picks = copy.deepcopy(self.get_evt_data().picks) + event_old = self.get_evt_data() + print(event_old.resource_id, event.resource_id) + if not event_old.resource_id == event.resource_id: + print("WARNING: Missmatch in event resource id's: {} and {}".format( + event_old.resource_id, + event.resource_id)) + picks = copy.deepcopy(event_old.picks) event = merge_picks(event, picks) # apply event information from location - self.get_evt_data().update(event) + event_old.update(event) applydata = {'pick': applyPicks, 'event': applyEvent} diff --git a/pylot/core/util/event.py b/pylot/core/util/event.py index 48667a07..1db5fac7 100644 --- a/pylot/core/util/event.py +++ b/pylot/core/util/event.py @@ -15,8 +15,9 @@ class Event(ObsPyEvent): Pickable class derived from ~obspy.core.event.Event containing information on a single event. ''' def __init__(self, path): + self.pylot_id = path.split('/')[-1] # initialize super class - super(Event, self).__init__(resource_id=ResourceIdentifier(path.split('/')[-1])) + super(Event, self).__init__(resource_id=ResourceIdentifier('smi:local/'+self.pylot_id)) self.path = path self.database = path.split('/')[-2] self.datapath = path.split('/')[-3]