Merge branch 'develop' into feature/additional_channel_display

This commit is contained in:
Marcel Paffrath 2018-01-15 14:43:26 +01:00
commit 975bd64266
7 changed files with 339 additions and 112 deletions

112
PyLoT.py
View File

@ -274,6 +274,10 @@ class MainWindow(QMainWindow):
print_icon.addPixmap(QPixmap(':/icons/printer.png'))
self.filter_icon = QIcon()
self.filter_icon.addPixmap(QPixmap(':/icons/filter.png'))
self.filter_icon_p = QIcon()
self.filter_icon_p.addPixmap(QPixmap(':/icons/filter_p.png'))
self.filter_icon_s = QIcon()
self.filter_icon_s.addPixmap(QPixmap(':/icons/filter_s.png'))
z_icon = QIcon()
z_icon.addPixmap(QPixmap(':/icons/key_Z.png'))
n_icon = QIcon()
@ -368,15 +372,23 @@ class MainWindow(QMainWindow):
self.setParameter,
None, paraIcon,
"Modify Parameter")
self.filterAction = self.createAction(self, "&Filter ...",
self.filterWaveformData,
"Ctrl+F", self.filter_icon,
"""Toggle un-/filtered waveforms
to be displayed, according to the
desired seismic phase.""", True)
self.filterActionP = createAction(parent=self, text='Apply P Filter',
slot=self.filterP,
icon=self.filter_icon_p,
tip='Toggle filtered/original'
' waveforms',
checkable=True,
shortcut='P')
self.filterActionS = createAction(parent=self, text='Apply S Filter',
slot=self.filterS,
icon=self.filter_icon_s,
tip='Toggle filtered/original'
' waveforms',
checkable=True,
shortcut='S')
filterEditAction = self.createAction(self, "&Filter parameter ...",
self.adjustFilterOptions,
"Alt+F", self.filter_icon,
"Ctrl+F", self.filter_icon,
"""Adjust filter parameters.""")
self.inventoryAction = self.createAction(self, "Select &Inventory ...",
self.get_new_metadata,
@ -502,8 +514,8 @@ class MainWindow(QMainWindow):
self.updateFileMenu()
self.editMenu = self.menuBar().addMenu('&Edit')
editActions = (self.filterAction, filterEditAction, None,
self.selectPAction, self.selectSAction, None,
editActions = (self.filterActionP, self.filterActionS, filterEditAction, None,
#self.selectPAction, self.selectSAction, None,
self.inventoryAction, self.initMapAction, None,
prefsEventAction)
#printAction) #TODO: print event?
@ -1415,7 +1427,8 @@ class MainWindow(QMainWindow):
return None
def getStime(self):
return self._stime
if self.get_data():
return full_range(self.get_data().getWFData())[0]
def addActions(self, target, actions):
for action in actions:
@ -1589,18 +1602,9 @@ class MainWindow(QMainWindow):
# else:
# ans = False
self.fnames = self.getWFFnames_from_eventbox()
self.data.setWFData(self.fnames)
wfdat = self.data.getWFData() # all available streams
# remove possible underscores in station names
wfdat = remove_underscores(wfdat)
# check for gaps and doubled channels
check4gaps(wfdat)
check4doubled(wfdat)
# check for stations with rotated components
wfdat = check4rotated(wfdat, self.metadata, verbosity=0)
# trim station components to same start value
trim_station_components(wfdat, trim_start=True, trim_end=False)
self._stime = full_range(self.get_data().getWFData())[0]
self.data.setWFData(self.fnames,
checkRotated=True,
metadata=self.metadata)
def connectWFplotEvents(self):
'''
@ -1759,18 +1763,19 @@ class MainWindow(QMainWindow):
self.disableSaveEventAction()
self.draw()
def plotWaveformDataThread(self):
def plotWaveformDataThread(self, filter=True):
'''
Open a modal thread to plot current waveform data.
'''
self.clearWaveformDataPlot()
self.wfp_thread = Thread(self, self.plotWaveformData,
arg=filter,
progressText='Plotting waveform data...',
pb_widget=self.mainProgressBarWidget)
self.wfp_thread.finished.connect(self.finishWaveformDataPlot)
self.wfp_thread.start()
def plotWaveformData(self):
def plotWaveformData(self, filter=True):
'''
Plot waveform data to current plotWidget.
'''
@ -1782,8 +1787,10 @@ class MainWindow(QMainWindow):
comp = self.getComponent()
title = 'section: {0} components'.format(zne_text[comp])
wfst = self.get_data().getWFData()
if self.filterAction.isChecked():
self.filterWaveformData(plot=False)
if self.filterActionP.isChecked() and filter:
self.filterWaveformData(plot=False, phase='P')
elif self.filterActionS.isChecked() and filter:
self.filterWaveformData(plot=False, phase='S')
# wfst = self.get_data().getWFData().select(component=comp)
# wfst += self.get_data().getWFData().select(component=alter_comp)
plotWidget = self.getPlotWidget()
@ -1823,19 +1830,48 @@ class MainWindow(QMainWindow):
def pushFilterWF(self, param_args):
self.get_data().filterWFData(param_args)
def filterWaveformData(self, plot=True):
def filterP(self):
self.filterActionS.setChecked(False)
if self.filterActionP.isChecked():
self.filterWaveformData(phase='P')
else:
self.resetWFData()
def filterS(self):
self.filterActionP.setChecked(False)
if self.filterActionS.isChecked():
self.filterWaveformData(phase='S')
else:
self.resetWFData()
def resetWFData(self):
self.get_data().resetWFData()
self.plotWaveformDataThread()
def filterWaveformData(self, plot=True, phase=None):
if not self.get_current_event():
return
if self.get_data():
if self.getFilterOptions() and self.filterAction.isChecked():
kwargs = self.getFilterOptions()[self.getSeismicPhase()].parseFilterOptions()
self.pushFilterWF(kwargs)
elif self.filterAction.isChecked():
if not phase:
if self.filterActionP.isChecked():
phase = 'P'
elif self.filterActionS.isChecked():
phase = 'S'
if self.getFilterOptions():
if (phase == 'P' and self.filterActionP.isChecked()) or (phase == 'S' and self.filterActionS.isChecked()):
kwargs = self.getFilterOptions()[phase].parseFilterOptions()
self.pushFilterWF(kwargs)
else:
self.get_data().resetWFData()
elif self.filterActionP.isChecked() or self.filterActionS.isChecked():
self.adjustFilterOptions()
else:
self.get_data().resetWFData()
if plot:
self.plotWaveformDataThread()
self.drawPicks()
self.draw()
if plot:
self.plotWaveformDataThread(filter=False)
#self.drawPicks()
#self.draw()
def adjustFilterOptions(self):
fstring = "Filter Options"
@ -1844,7 +1880,7 @@ class MainWindow(QMainWindow):
if self.filterDlg.exec_():
filteroptions = self.filterDlg.getFilterOptions()
self.setFilterOptions(filteroptions)
if self.filterAction.isChecked():
if self.filterActionP.isChecked() or self.filterActionS.isChecked():
kwargs = self.getFilterOptions()[self.getSeismicPhase()].parseFilterOptions()
self.pushFilterWF(kwargs)
self.plotWaveformDataThread()
@ -1925,7 +1961,7 @@ class MainWindow(QMainWindow):
# '[{0}: {1} Hz]'.format(
# self.getFilterOptions().getFilterType(),
# self.getFilterOptions().getFreq()))
# if self.filterAction.isChecked():
# if self.filterActionP.isChecked() or self.filterActionS.isChecked():
# self.filterWaveformData()
def getSeismicPhase(self):
@ -2018,7 +2054,7 @@ class MainWindow(QMainWindow):
autopicks=self.getPicksOnStation(station, 'auto'),
metadata=self.metadata, event=event,
filteroptions=self.filteroptions)
if self.filterAction.isChecked():
if self.filterActionP.isChecked() or self.filterActionS.isChecked():
pickDlg.currentPhase = self.getSeismicPhase()
pickDlg.filterWFData()
pickDlg.nextStation.setChecked(nextStation)

View File

@ -1 +1 @@
0ebc-dirty
f483-dirty

View File

@ -12,7 +12,8 @@ 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.event import Event
from pylot.core.util.utils import fnConstructor, full_range
from pylot.core.util.utils import fnConstructor, full_range, remove_underscores, check4gaps, check4doubled, \
check4rotated, trim_station_components
import pylot.core.loc.velest as velest
@ -367,7 +368,7 @@ class Data(object):
data.filter(**kwargs)
self.dirty = True
def setWFData(self, fnames):
def setWFData(self, fnames, checkRotated=False, metadata=None):
"""
Clear current waveform data and set given waveform data
:param fnames: waveform data names to append
@ -379,10 +380,26 @@ class Data(object):
self.appendWFData(fnames)
else:
return False
# various pre-processing steps:
# remove possible underscores in station names
self.wfdata = remove_underscores(self.wfdata)
# check for gaps and doubled channels
check4gaps(self.wfdata)
check4doubled(self.wfdata)
# check for stations with rotated components
if checkRotated and metadata is not None:
self.wfdata = check4rotated(self.wfdata, metadata, verbosity=0)
# trim station components to same start value
trim_station_components(self.wfdata, trim_start=True, trim_end=False)
# make a copy of original data
self.wforiginal = self.getWFData().copy()
self.dirty = False
return True
def appendWFData(self, fnames):
"""
Read waveform data from fnames and append it to current wf data

View File

@ -16,7 +16,8 @@ from pylot.core.io.inputs import PylotParameter
from pylot.core.io.location import create_event, \
create_magnitude
from pylot.core.pick.utils import select_for_phase
from pylot.core.util.utils import getOwner, full_range, four_digits
from pylot.core.util.utils import getOwner, full_range, four_digits, transformFilteroptions2String, \
transformFilterString4Export, backtransformFilterString
def add_amplitudes(event, amplitudes):
@ -235,6 +236,10 @@ def picksdict_from_picks(evt):
network = pick.waveform_id.network_code
mpp = pick.time
spe = pick.time_errors.uncertainty
if pick.filter_id:
filter_id = backtransformFilterString(str(pick.filter_id.id))
else:
filter_id = None
try:
picker = str(pick.method_id)
if picker.startswith('smi:local/'):
@ -261,6 +266,7 @@ def picksdict_from_picks(evt):
phase['channel'] = channel
phase['network'] = network
phase['picker'] = picker
phase['filter_id'] = filter_id if filter_id is not None else ''
onsets[pick.phase_hint] = phase.copy()
picksdict[picker][station] = onsets.copy()
@ -312,6 +318,13 @@ def picks_from_picksdict(picks, creation_info=None):
pick.waveform_id = ope.WaveformStreamID(station_code=station,
channel_code=ccode,
network_code=ncode)
try:
filter_id = phase['filteroptions']
filter_id = transformFilterString4Export(filter_id)
except KeyError as e:
warnings.warn(e.message, RuntimeWarning)
filter_id = ''
pick.filter_id = filter_id
try:
polarity = phase['fm']
if polarity == 'U' or '+':
@ -328,7 +341,6 @@ def picks_from_picksdict(picks, creation_info=None):
picks_list.append(pick)
return picks_list
def reassess_pilot_db(root_dir, db_dir, out_dir=None, fn_param=None, verbosity=0):
import glob

View File

@ -24,7 +24,7 @@ class Thread(QThread):
if self.redirect_stdout:
sys.stdout = self
try:
if self.arg:
if self.arg is not None:
self.data = self.func(self.arg)
else:
self.data = self.func()

View File

@ -391,6 +391,38 @@ def full_range(stream):
return min_start, max_end
def transformFilteroptions2String(filtopts):
st = ''
if not filtopts:
return st
if 'type' in filtopts.keys():
st += '{}'.format(filtopts['type'])
if 'freq' in filtopts.keys():
st += ' | freq: {}'.format(filtopts['freq'])
elif 'freqmin' in filtopts.keys() and 'freqmax' in filtopts.keys():
st += ' | freqmin: {} | freqmax: {}'.format(filtopts['freqmin'], filtopts['freqmax'])
for key, value in filtopts.items():
if key in ['type', 'freq', 'freqmin', 'freqmax']:
continue
st += ' | {}: {}'.format(key, value)
return st
def transformFilterString4Export(st):
st = st.replace('|', '//')
st = st.replace(':', '/')
st = st.replace(' ', '')
return st
def backtransformFilterString(st):
st = st.split('smi:local/')
st = st[1] if len(st) > 1 else st[0]
st = st.replace('//', ' | ')
st = st.replace('/', ': ')
return st
def getHash(time):
"""
takes a time object and returns the corresponding SHA1 hash of the formatted date string
@ -1137,6 +1169,8 @@ def identifyPhase(phase):
# common phase suffix for P and S
common_P = ['P', 'p', 'R']
common_S = ['S', 's']
if phase is None:
return False
if phase[-1] in common_P:
return 'P'
if phase[-1] in common_S:

View File

@ -17,8 +17,6 @@ import time
import numpy as np
from matplotlib.figure import Figure
from pylot.core.util.utils import find_horizontals, identifyPhase, loopIdentifyPhase, trim_station_components, \
identifyPhaseID, check4rotated, real_Bool, pick_color
try:
from matplotlib.backends.backend_qt4agg import FigureCanvas
@ -49,7 +47,9 @@ from pylot.core.util.defaults import OUTPUTFORMATS, FILTERDEFAULTS, \
SetChannelComponents
from pylot.core.util.utils import prepTimeAxis, full_range, scaleWFData, \
demeanTrace, isSorted, findComboBoxIndex, clims, pick_linestyle_plt, pick_color_plt, \
check4rotated, check4doubled, check4gaps, remove_underscores
check4rotated, check4doubled, check4gaps, remove_underscores, find_horizontals, identifyPhase, \
loopIdentifyPhase, trim_station_components, transformFilteroptions2String, \
identifyPhaseID, real_Bool, pick_color
from autoPyLoT import autoPyLoT
from pylot.core.util.thread import Thread
@ -375,8 +375,8 @@ class ComparisonWidget(QWidget):
ax = axes_dict[phase]['exp']
xlims = ax.get_xlim()
ylims = ax.get_ylim()
ax.fill_between([xlims[0], 0], ylims[0], ylims[1], color=(0.9, 1.0, 0.9, 0.5), label='earlier than manual')
ax.fill_between([0, xlims[1]], ylims[0], ylims[1], color=(1.0, 0.9, 0.9, 0.5), label='later than manual')
#ax.fill_between([xlims[0], 0], ylims[0], ylims[1], color=(0.9, 1.0, 0.9, 0.5), label='earlier than manual')
#ax.fill_between([0, xlims[1]], ylims[0], ylims[1], color=(1.0, 0.9, 0.9, 0.5), label='later than manual')
legend = ax.legend()
legend.draggable()
@ -1152,9 +1152,11 @@ class PickDlg(QDialog):
self.components = 'ZNE'
self.currentPhase = None
self.phaseText = []
self.phaseLines = []
self.arrivals = []
self.arrivalsText = []
self.cidpick = []
self.cidpress = None
settings = QSettings()
pylot_user = getpass.getuser()
self._user = settings.value('user/Login', pylot_user)
@ -1242,6 +1244,7 @@ class PickDlg(QDialog):
# init pick delete (with right click)
self.connect_pick_delete()
self.connect_mouse_motion()
self.setWindowTitle('Pickwindow on station: {}'.format(self.getStation()))
self.setWindowState(QtCore.Qt.WindowMaximized)
@ -1270,6 +1273,8 @@ class PickDlg(QDialog):
home_icon.addPixmap(QPixmap(':/icons/zoom_0.png'))
del_icon = QIcon()
del_icon.addPixmap(QPixmap(':/icons/delete.png'))
sync_icon = QIcon()
sync_icon.addPixmap(QPixmap(':/icons/sync.png'))
# create actions
self.filterActionP = createAction(parent=self, text='Apply P Filter',
@ -1278,14 +1283,14 @@ class PickDlg(QDialog):
tip='Toggle filtered/original'
' waveforms',
checkable=True,
shortcut='Ctrl+F')
shortcut='P')
self.filterActionS = createAction(parent=self, text='Apply S Filter',
slot=self.filterS,
icon=filter_icon_s,
tip='Toggle filtered/original'
' waveforms',
checkable=True,
shortcut='Shift+F')
shortcut='S')
self.autoFilterAction = createAction(parent=self, text='Automatic Filtering',
slot=self.toggleAutoFilter,
icon=key_a_icon,
@ -1302,6 +1307,10 @@ class PickDlg(QDialog):
self.resetPicksAction = createAction(parent=self, text='Delete Picks',
slot=self.delPicks, icon=del_icon,
tip='Delete current picks.')
self.renamePhaseAction = createAction(parent=self, text='Rename Phase',
slot=self.initRenamePhase, icon=sync_icon,
tip='Rename a Phase.', checkable=True,
shortcut='R')
self.addPickPhases(menuBar)
@ -1330,6 +1339,8 @@ class PickDlg(QDialog):
self.reject_button = QPushButton('&Reject')
self.disable_ar_buttons()
self.statusbar = QtGui.QStatusBar(self)
# add hotkeys
self._shortcut_space = QtGui.QShortcut(QtGui.QKeySequence(' '), self)
self._shortcut_space.activated.connect(self.accept_button.clicked)
@ -1352,6 +1363,7 @@ class PickDlg(QDialog):
_dialtoolbar.addAction(self.resetZoomAction)
_dialtoolbar.addSeparator()
_dialtoolbar.addAction(self.resetPicksAction)
_dialtoolbar.addAction(self.renamePhaseAction)
_dialtoolbar.addSeparator()
if self._embedded:
manu_label = QLabel('Manual Onsets:')
@ -1375,9 +1387,12 @@ class PickDlg(QDialog):
# layout the innermost widget
_innerlayout = QVBoxLayout()
_innerinnerlayout = QtGui.QHBoxLayout()
_lowerlayout = QHBoxLayout()
_innerinnerlayout.addWidget(self.multicompfig)
_innerinnerlayout.addWidget(self.phaseplot)
_innerlayout.addLayout(_innerinnerlayout)
_innerlayout.addLayout(_lowerlayout)
_lowerlayout.addWidget(self.statusbar)
# add button box to the dialog
_buttonbox = QDialogButtonBox(QDialogButtonBox.Ok |
@ -1385,13 +1400,17 @@ class PickDlg(QDialog):
# merge widgets and layouts to establish the dialog
if not self._embedded:
_innerlayout.addWidget(_buttonbox)
_lowerlayout.addWidget(_buttonbox)
_outerlayout.addWidget(menuBar)
_outerlayout.addWidget(_dialtoolbar)
_outerlayout.addLayout(_innerlayout)
_outerlayout.setStretch(0, 0)
_outerlayout.setStretch(1, 0)
_outerlayout.setStretch(2, 1)
_lowerlayout.setStretch(0, 5)
_lowerlayout.setStretch(1, 1)
_innerlayout.setStretch(0, 1)
_innerlayout.setStretch(1, 0)
# connect widget element signals with slots (methods to the dialog
# object
@ -1555,7 +1574,7 @@ class PickDlg(QDialog):
filterOptionsAction = createAction(parent=self, text="&Filter parameter ...",
slot=self.filterOptions,
shortcut='Alt+F',
shortcut='Ctrl+F',
icon=self.orig_parent.filter_icon)
filterMenu = menuBar.addMenu('Filter')
filterMenu.addAction(self.filterActionP)
@ -1675,8 +1694,15 @@ class PickDlg(QDialog):
self.deactivatePicking()
def activatePicking(self):
self.leave_rename_phase()
self.renamePhaseAction.setEnabled(False)
phase = self.currentPhase
color = pick_color_plt('manual', self.getPhaseID(phase))
phaseID = self.getPhaseID(phase)
if not phaseID:
self.warn_unknown_phase(phase)
self.leave_picking_mode()
return
color = pick_color_plt('manual', phaseID)
self.multicompfig.set_frame_color(color)
self.multicompfig.set_frame_linewidth(1.5)
if self.zoomAction.isChecked():
@ -1700,6 +1726,7 @@ class PickDlg(QDialog):
self.disconnectPressEvent()
self.multicompfig.connectEvents()
self.renamePhaseAction.setEnabled(True)
self.connect_pick_delete()
self.draw()
@ -1799,6 +1826,17 @@ class PickDlg(QDialog):
self.resetPicks()
self.refreshPlot()
def initRenamePhase(self):
if self.renamePhaseAction.isChecked():
self.multicompfig.disconnectEvents()
self.multicompfig.set_frame_color('orange')
self.draw()
self.statusbar.showMessage('Click on a phase you want to rename.')
else:
self.multicompfig.set_frame_color()
self.multicompfig.connectEvents()
self.draw()
def setIniPick(self, gui_event):
self.multicompfig.set_frame_color('green')
trace_number = round(gui_event.ydata)
@ -2006,41 +2044,30 @@ class PickDlg(QDialog):
# save pick times for actual phase
phasepicks = dict(epp=epp, lpp=lpp, mpp=mpp, spe=spe,
picker='manual', channel=channel,
network=wfdata[0].stats.network)
network=wfdata[0].stats.network,
filteroptions=transformFilteroptions2String(filteroptions))
try:
oldphasepick = self.picks[phase]
except KeyError:
self.picks[phase] = phasepicks
else:
self.picks[phase] = phasepicks
oepp = oldphasepick['epp']
ompp = oldphasepick['mpp']
olpp = oldphasepick['lpp']
msg = """Warning old phase information for phase {phase} has been
altered.\n
New phase times:\n
earliest possible pick: {epp}\n
most probable pick: {mpp}\n
latest possible pick: {lpp}\n
\n
Old phase times (overwritten):\n
earliest possible pick: {oepp}\n
most probable pick: {ompp}\n
latest possible pick: {olpp}\n""".format(phase=phase,
epp=epp,
mpp=pick,
lpp=lpp,
oepp=oepp,
ompp=ompp,
olpp=olpp)
saved = self.savePick(phase, phasepicks)
if saved:
self.setDirty(True)
self.disconnectPressEvent()
self.enable_ar_buttons()
self.zoomAction.setEnabled(True)
#self.pick_block = self.togglePickBlocker()
#self.pick_block = self.togglPickBlocker()
self.leave_picking_mode()
self.setDirty(True)
def savePick(self, phase, phasepicks):
if not self.getPhaseID(phase):
self.warn_unknown_phase(phase)
return
self.picks[phase] = phasepicks
return True
def warn_unknown_phase(self, phase=None):
QtGui.QMessageBox.warning(self, 'Unknown phase ID',
'Could not identify phase ID: {}.'.format(phase))
def disconnectPressEvent(self):
self.multicompfig.mpl_disconnect(self.cidpress)
@ -2050,6 +2077,7 @@ class PickDlg(QDialog):
self.removePhaseText()
self.drawPicks(picktype='manual')
self.drawPicks(picktype='auto')
self.draw()
def drawPicks(self, phase=None, picktype='manual', textOnly=False, picks=None):
# plotting picks
@ -2091,19 +2119,20 @@ class PickDlg(QDialog):
color = pick_color_plt(picktype, phaseID, quality)
if not textOnly:
linestyle_mpp, width_mpp = pick_linestyle_plt(picktype, 'mpp')
ax.plot([mpp, mpp], ylims, color=color, linestyle=linestyle_mpp, linewidth=width_mpp,
label='{}-Pick (quality: {})'.format(phase, quality), picker=5)
vl = ax.axvline(mpp, ylims[0], ylims[1], color=color, linestyle=linestyle_mpp, linewidth=width_mpp,
label='{}-Pick (quality: {})'.format(phase, quality), picker=5)
self.phaseLines.append(vl)
if spe:
ax.fill_between([mpp-spe, mpp+spe], ylims[0], ylims[1],
alpha=.25, color=color, label='{}-SPE'.format(phase))
if picks['epp']:
linestyle_epp, width_epp = pick_linestyle_plt(picktype, 'epp')
ax.plot([epp, epp], ylims, color=color, linestyle=linestyle_epp,
linewidth=width_epp, label='{}-EPP'.format(phase))
ax.axvline(epp, ylims[0], ylims[1], color=color, linestyle=linestyle_epp,
linewidth=width_epp, label='{}-EPP'.format(phase))
if picks['lpp']:
linestyle_lpp, width_lpp = pick_linestyle_plt(picktype, 'lpp')
ax.plot([lpp, lpp], ylims, color=color, linestyle=linestyle_lpp,
linewidth=width_lpp, label='{}-LPP'.format(phase))
ax.axvline(lpp, ylims[0], ylims[1], color=color, linestyle=linestyle_lpp,
linewidth=width_lpp, label='{}-LPP'.format(phase))
# else:
# ax.plot([mpp, mpp], ylims, color=color, linestyle=linestyle_mpp, linewidth=width_mpp,
# label='{}-Pick (NO PICKERROR)'.format(phase), picker=5)
@ -2115,8 +2144,9 @@ class PickDlg(QDialog):
if not textOnly:
ax.plot(mpp, ylims[1], color=color, marker='v')
ax.plot(mpp, ylims[0], color=color, marker='^')
ax.vlines(mpp, ylims[0], ylims[1], color=color, linestyle=linestyle_mpp, linewidth=width_mpp,
picker=5, label='{}-Autopick (quality: {})'.format(phase, quality))
vl = ax.axvline(mpp, ylims[0], ylims[1], color=color, linestyle=linestyle_mpp, linewidth=width_mpp,
picker=5, label='{}-Autopick (quality: {})'.format(phase, quality))
self.phaseLines.append(vl)
# append phase text (if textOnly: draw with current ylims)
self.phaseText.append(ax.text(mpp, ylims[1], phase, color=color))
else:
@ -2124,24 +2154,121 @@ class PickDlg(QDialog):
ax.legend(loc=1)
def connect_mouse_motion(self):
self.cidmotion = self.multicompfig.mpl_connect(
'motion_notify_event', self.on_motion)
def connect_pick_delete(self):
self.cidpick = self.multicompfig.mpl_connect('pick_event', self.onpick_delete)
self.cidpick = self.multicompfig.mpl_connect('pick_event', self.onpick)
self.cidpick = self.multicompfig.mpl_connect('motion_notify_event', self.on_hover_info)
def disconnect_pick_delete(self):
if hasattr(self, 'cidpick'):
self.multicompfig.mpl_disconnect(self.cidpick)
def on_motion(self, event):
x = event.xdata
if x is not None:
time_code = 'T = {}, t = {} [s]'.format(self.stime+x, x)
user_help = ' - Left-Click to Drag | Right-Click to Pan-Zoom |' \
' Mousewheel to Zoom | Middle-Click to Delete Pick'
self.statusbar.showMessage(time_code + user_help)
def onpick(self, event):
if event.mouseevent.button == 1:
self.onpick_info(event)
elif event.mouseevent.button == 2:
self.onpick_delete(event)
def on_hover_info(self, event):
if not any([phase.contains(event)[0] for phase in self.phaseLines]):
return
x = event.xdata
if not x:
return
allpicks, pick_rel, phase, picktype = self.identify_selected_picks(x)
pick = allpicks[picktype][phase]
message = '{} {}-pick'.format(picktype, phase)
if 'mpp' in pick:
message += ', MPP: {}'.format(pick['mpp'])
if 'spe' in pick:
message += ', SPE: {} [s]'.format(pick['spe'])
if 'filteroptions' in pick:
message += ', FILTER: {}'.format(pick['filteroptions'])
x = event.x
y = event.y
y = self.size().height() - y
pt = self.mapToGlobal(QtCore.QPoint(x, y))
QtGui.QToolTip.showText(pt, message)
def onpick_info(self, event):
if not event.mouseevent.button == 1:
return
x = event.mouseevent.xdata
allpicks, pick_rel, phase, picktype = self.identify_selected_picks(x)
pick = allpicks[picktype][phase]
message = '{} {}-pick'.format(picktype, phase)
if 'mpp' in pick:
message += ', MPP: {}'.format(pick['mpp'])
if 'spe' in pick:
message += ', SPE: {}'.format(pick['spe'])
if 'filteroptions' in pick:
message += ', FILTER: {}'.format(pick['filteroptions'])
if self.renamePhaseAction.isChecked():
self.renamePhase(picktype, phase)
self.statusbar.showMessage(message, 10e3)
def onpick_delete(self, event):
if not event.mouseevent.button == 3:
if not event.mouseevent.button == 2:
return
x = event.mouseevent.xdata
self.remove_pick_by_x(x)
self.refreshPlot()
def renamePhase(self, picktype, phase):
allpicks = {'manual': self.picks,
'auto': self.autopicks}
picks = allpicks[picktype]
dialog = QtGui.QInputDialog(parent=self)
new_phase, executed = dialog.getText(self, 'Rename phase', 'Rename phase {} to:'.format(phase))
if executed:
try:
self.renamePhaseInDict(picks, phase, new_phase)
except KeyError as e:
QtGui.QMessageBox.warning(self, 'Could not rename phase',
'Could not rename phase {} to {}: {}'.format(phase, new_phase, e))
self.leave_rename_phase()
self.refreshPlot()
def renamePhaseInDict(self, picks, phase_old, phase_new):
if phase_new in picks:
raise KeyError('New phase ID already assigned.')
picks_new = picks[phase_old].copy()
saved = self.savePick(phase_new, picks_new)
if saved:
picks.pop(phase_old)
self.setDirty(True)
def leave_rename_phase(self):
self.renamePhaseAction.setChecked(False)
self.multicompfig.set_frame_color()
self.multicompfig.connectEvents()
def remove_pick_by_x(self, x):
if not self.picks and not self.autopicks:
return
# init empty list and get station starttime
allpicks, pick_rel, phase, picktype = self.identify_selected_picks(x)
# delete the value from corresponding dictionary
allpicks[picktype].pop(phase)
# information output
msg = 'Deleted {} pick for phase {}, at timestamp {} (relative time: {} s)'
print(msg.format(picktype, phase, self.getStartTime()+pick_rel, pick_rel))
self.setDirty(True)
def identify_selected_picks(self, x):
# init empty list and get stat5ion starttime
X = []
starttime = self.getStartTime()
# init dictionaries to iterate through and iterate over them
@ -2159,12 +2286,9 @@ class PickDlg(QDialog):
index, value = min(enumerate([val[0] for val in X]), key=lambda y: abs(y[1] - x))
# unpack the found value
pick_rel, phase, picktype = X[index]
# delete the value from corresponding dictionary
allpicks[picktype].pop(phase)
# information output
msg = 'Deleted {} pick for phase {}, at timestamp {} (relative time: {} s)'
print(msg.format(picktype, phase, starttime+pick_rel, pick_rel))
self.setDirty(True)
return allpicks, pick_rel, phase, picktype
def drawPhaseText(self):
self.drawPicks(picktype='manual', textOnly=True)
@ -2219,11 +2343,8 @@ class PickDlg(QDialog):
data.detrend('linear')
data.taper(0.02, type='cosine')
data.filter(**filtoptions)
title += ' | {} filtered |'.format(filtoptions['type'])
for key, value in filtoptions.items():
if key == 'type':
continue
title += ' {}: {} |'.format(key, value)
filtops_str = transformFilteroptions2String(filtoptions)
title += ' | Filteroptions: {}'.format(filtops_str)
self.multicompfig.plotWFData(wfdata=data, title=title,
zoomx=self.getXLims(),
zoomy=self.getYLims(),
@ -2258,10 +2379,17 @@ class PickDlg(QDialog):
if self.autoFilterAction.isChecked():
self.filterActionP.setChecked(False)
self.filterActionS.setChecked(False)
data = self.getWFData().copy()
title = self.getStation()
filter = self.filterActionP.isChecked or self.filterActionS.isChecked()
self.plotWFData(filter=filter)
# data = self.getWFData().copy()
# title = self.getStation()
filter = False
phase = None
if self.filterActionP.isChecked():
phase = 'P'
filter = True
if self.filterActionS.isChecked():
phase = 'S'
filter = True
self.plotWFData(phase=phase, filter=filter)
def resetZoom(self):
ax = self.multicompfig.axes[0]
@ -2866,9 +2994,9 @@ class TuneAutopicker(QWidget):
y_top = 0.9 * ax.get_ylim()[1]
y_bot = 0.9 * ax.get_ylim()[0]
self._manual_pick_plots.append(ax.vlines(mpp, y_bot, y_top,
color=color, linewidth=2,
label='manual {} Onset (quality: {})'.format(phase, quality)))
self._manual_pick_plots.append(ax.axvline(mpp, y_bot, y_top,
color=color, linewidth=2,
label='manual {} Onset (quality: {})'.format(phase, quality)))
self._manual_pick_plots.append(ax.plot([mpp - 0.5, mpp + 0.5],
[y_bot, y_bot], linewidth=2,
color=color))