Compare commits

..

No commits in common. "6cce05b03547949940165a217670f5b776319dc4" and "b3fdbc811e447f8acb1b72deff3a096e065c724c" have entirely different histories.

19 changed files with 325 additions and 671 deletions

157
PyLoT.py
View File

@ -25,7 +25,6 @@ https://www.iconfinder.com/iconsets/flavour
import argparse
import json
import logging
import os
import platform
import shutil
@ -61,7 +60,7 @@ except ImportError:
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
from pylot.core.analysis.magnitude import LocalMagnitude, MomentMagnitude
from pylot.core.analysis.magnitude import LocalMagnitude, MomentMagnitude, calcsourcespec
from pylot.core.io.data import Data
from pylot.core.io.inputs import FilterOptions, PylotParameter
from autoPyLoT import autoPyLoT
@ -85,7 +84,7 @@ from pylot.core.util.widgets import FilterOptionsDialog, NewEventDlg, \
PylotCanvas, WaveformWidgetPG, PropertiesDlg, HelpForm, createAction, PickDlg, \
ComparisonWidget, TuneAutopicker, PylotParaBox, AutoPickDlg, CanvasWidget, AutoPickWidget, \
CompareEventsWidget, ProgressBarWidget, AddMetadataWidget, SingleTextLineDialog, LogWidget, PickQualitiesFromXml, \
SpectrogramTab, SearchFileByExtensionDialog
SourceSpecWindow, ChooseWaveFormWindow, SpectrogramTab
from pylot.core.util.array_map import Array_map
from pylot.core.util.structure import DATASTRUCTURE
from pylot.core.util.thread import Thread, Worker
@ -114,7 +113,11 @@ class MainWindow(QMainWindow):
def __init__(self, parent=None, infile=None, reset_qsettings=False):
super(MainWindow, self).__init__(parent)
if infile and os.path.isfile(infile) is False:
# check for default pylot.in-file
if not infile:
infile = os.path.join(os.path.expanduser('~'), '.pylot', 'pylot.in')
print('Using default input file {}'.format(infile))
if os.path.isfile(infile) is False:
infile = QFileDialog().getOpenFileName(caption='Choose PyLoT-input file')[0]
if not os.path.exists(infile):
@ -178,7 +181,6 @@ class MainWindow(QMainWindow):
self.autodata = Data(self)
self.fnames = None
self.fnames_comp = None
self._stime = None
# track deleted picks for logging
@ -251,7 +253,7 @@ class MainWindow(QMainWindow):
self._inputs.reset_defaults()
# check for default pylot.in-file
infile = os.path.join(pylot_config_dir, '.pylot.in')
logging.warning('Using default input file {}'.format(infile))
print('Using default input file {}'.format(infile))
self._inputs.export2File(infile)
self.infile = infile
@ -506,8 +508,6 @@ class MainWindow(QMainWindow):
logAction = self.createAction(self, "&Show Log", self.showLogWidget,
tip="""Display Log""")
logAction.setEnabled(use_logwidget)
# create button group for component selection
componentGroup = QActionGroup(self)
@ -1004,17 +1004,18 @@ class MainWindow(QMainWindow):
return
refresh = False
events = self.project.eventlist
sld = SearchFileByExtensionDialog(label='Specify file extension: ', default_text='.xml',
events=events)
sld = SingleTextLineDialog(label='Specify file extension: ', default_text='.xml')
if not sld.exec_():
return
filenames = sld.getChecked()
fext = sld.lineEdit.text()
# fext = '.xml'
for event in events:
for filename in filenames:
if os.path.isfile(filename) and event.pylot_id in filename:
self.load_data(filename, draw=False, event=event, ask_user=True, merge_strategy=sld.merge_strategy)
refresh = True
path = event.path
eventname = path.split('/')[-1] # or event.pylot_id
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
if not refresh:
return
if self.get_current_event().pylot_picks:
@ -1022,8 +1023,8 @@ class MainWindow(QMainWindow):
self.fill_eventbox()
self.setDirty(True)
def load_data(self, fname=None, loc=False, draw=True, event=None, ask_user=False, merge_strategy='Overwrite'):
if not ask_user:
def load_data(self, fname=None, loc=False, draw=True, event=None, overwrite=False):
if not overwrite:
if not self.okToContinue():
return
if fname is None:
@ -1037,12 +1038,9 @@ class MainWindow(QMainWindow):
data = Data(self, event)
try:
data_new = Data(self, evtdata=str(fname))
if merge_strategy == 'Overwrite':
data = data_new
elif merge_strategy == 'Merge':
data += data_new
else:
raise NotImplementedError(f'Unknown merge strategy: {merge_strategy}')
# MP MP commented because adding several picks might cause inconsistencies
data = data_new
# data += data_new
except ValueError:
qmb = QMessageBox(self, icon=QMessageBox.Question,
text='Warning: Missmatch in event identifiers {} and {}. Continue?'.format(
@ -1130,19 +1128,16 @@ class MainWindow(QMainWindow):
else:
return
def getWFFnames_from_eventbox(self, eventbox: str = None, subpath: str = None) -> list:
def getWFFnames_from_eventbox(self, eventbox=None):
'''
Return waveform filenames from event in eventbox.
'''
# TODO: add dataStructure class for obspyDMT here, this is just a workaround!
eventpath = self.get_current_event_path(eventbox)
if subpath:
eventpath = os.path.join(eventpath, subpath)
if not os.path.isdir(eventpath):
return []
basepath = eventpath.split(os.path.basename(eventpath))[0]
if self.dataStructure:
if not eventpath:
return []
return
fnames = [os.path.join(eventpath, f) for f in os.listdir(eventpath)]
else:
raise DatastructureError('not specified')
@ -1387,7 +1382,7 @@ class MainWindow(QMainWindow):
index = eventBox.currentIndex()
tv = QtWidgets.QTableView()
header = tv.horizontalHeader()
header.setSectionResizeMode(QtWidgets.QHeaderView.ResizeToContents)
header.setResizeMode(QtWidgets.QHeaderView.ResizeToContents)
header.setStretchLastSection(True)
header.hide()
tv.verticalHeader().hide()
@ -1407,28 +1402,25 @@ class MainWindow(QMainWindow):
for id, event in enumerate(self.project.eventlist):
event_path = event.path
#phaseErrors = {'P': self._inputs['timeerrorsP'],
# 'S': self._inputs['timeerrorsS']}
phaseErrors = {'P': self._inputs['timeerrorsP'],
'S': self._inputs['timeerrorsS']}
man_au_picks = {'manual': event.pylot_picks,
'auto': event.pylot_autopicks}
npicks = {'manual': {'P': 0, 'S': 0},
'auto': {'P': 0, 'S': 0}}
npicks_total = {'manual': {'P': 0, 'S': 0},
'auto': {'P': 0, 'S': 0}}
ma_props = {'manual': event.pylot_picks,
'auto': event.pylot_autopicks}
ma_count = {'manual': 0,
'auto': 0}
ma_count_total = {'manual': 0,
'auto': 0}
for ma in man_au_picks.keys():
if man_au_picks[ma]:
for picks in man_au_picks[ma].values():
for ma in ma_props.keys():
if ma_props[ma]:
for picks in ma_props[ma].values():
for phasename, pick in picks.items():
if not type(pick) in [dict, AttribDict]:
continue
phase_ID = identifyPhaseID(phasename)
if not phase_ID in npicks[ma].keys():
continue
if pick.get('spe'):
npicks[ma][phase_ID] += 1
npicks_total[ma][phase_ID] += 1
ma_count[ma] += 1
ma_count_total[ma] += 1
event_ref = event.isRefEvent()
event_test = event.isTestEvent()
@ -1463,23 +1455,16 @@ class MainWindow(QMainWindow):
if event.dirty:
event_str += '*'
item_path = QStandardItem(event_str)
item_time = QStandardItem('{}'.format(time.strftime("%Y-%m-%d %H:%M:%S") if time else ''))
item_time = QStandardItem('{}'.format(time))
item_lat = QStandardItem('{}'.format(lat))
item_lon = QStandardItem('{}'.format(lon))
item_depth = QStandardItem('{}'.format(depth))
item_localmag = QStandardItem('{}'.format(localmag))
item_momentmag = QStandardItem('{}'.format(momentmag))
item_nmp = QStandardItem()
item_nap = QStandardItem()
item_nmp = QStandardItem('{}({})'.format(ma_count['manual'], ma_count_total['manual']))
item_nmp.setIcon(self.manupicksicon_small)
item_nap = QStandardItem('{}({})'.format(ma_count['auto'], ma_count_total['auto']))
item_nap.setIcon(self.autopicksicon_small)
for picktype, item_np in [('manual', item_nmp), ('auto', item_nap)]:
npicks_str = f"{npicks[picktype]['P']}|{npicks[picktype]['S']}"
#npicks_str += f"({npicks_total[picktype]['P']}/{npicks_total[picktype]['S']})"
item_np.setText(npicks_str)
item_ref = QStandardItem() # str(event_ref))
item_test = QStandardItem() # str(event_test))
if event_ref:
@ -1900,7 +1885,6 @@ class MainWindow(QMainWindow):
# which will read in data input twice. Therefore current tab is changed to 0
# in loadProject before calling this function.
self.fill_eventbox()
#print(f'{self.get_current_event()=}')
plotted = False
if self.tabs.currentIndex() == 2:
self.init_event_table()
@ -1935,6 +1919,7 @@ class MainWindow(QMainWindow):
self.spectro_layout.addWidget(newSpectroWidget)
self.spectroWidget = newSpectroWidget
def newWF(self, event=None, plot=True):
'''
Load new data and plot if necessary.
@ -1964,20 +1949,13 @@ class MainWindow(QMainWindow):
def prepareLoadWaveformData(self):
self.fnames = self.getWFFnames_from_eventbox()
self.fnames_comp = []
fnames_comp = self.getWFFnames_from_eventbox(subpath='compare')
self.dataPlot.activateCompareOptions(bool(fnames_comp))
if fnames_comp:
if self.dataPlot.comp_checkbox.isChecked():
self.fnames_comp = fnames_comp
self.fnames_syn = []
eventpath = self.get_current_event_path()
basepath = eventpath.split(os.path.basename(eventpath))[0]
self.obspy_dmt = check_obspydmt_structure(basepath)
self.dataPlot.activateObspyDMToptions(self.obspy_dmt)
if self.obspy_dmt:
self.prepareObspyDMT_data(eventpath)
self.dataPlot.activateCompareOptions(True)
def loadWaveformData(self):
'''
@ -1992,8 +1970,6 @@ class MainWindow(QMainWindow):
# ans = False
settings = QSettings()
# process application events to wait for event items to appear in event box
QApplication.processEvents()
curr_event = self.get_current_event()
if not curr_event:
print('Could not find current event. Try reload?')
@ -2010,7 +1986,7 @@ class MainWindow(QMainWindow):
tstop = None
self.data.setWFData(self.fnames,
self.fnames_comp,
self.fnames_syn,
checkRotated=True,
metadata=self.metadata,
tstart=tstart,
@ -2018,7 +1994,7 @@ class MainWindow(QMainWindow):
def prepareObspyDMT_data(self, eventpath):
qcbox_processed = self.dataPlot.qcombo_processed
qcheckb_syn = self.dataPlot.comp_checkbox
qcheckb_syn = self.dataPlot.syn_checkbox
qcbox_processed.setEnabled(False)
qcheckb_syn.setEnabled(False)
for fpath in os.listdir(eventpath):
@ -2026,8 +2002,8 @@ class MainWindow(QMainWindow):
if 'syngine' in fpath:
eventpath_syn = os.path.join(eventpath, fpath)
qcheckb_syn.setEnabled(True)
if self.dataPlot.comp_checkbox.isChecked():
self.fnames_comp = [os.path.join(eventpath_syn, filename) for filename in os.listdir(eventpath_syn)]
if self.dataPlot.syn_checkbox.isChecked():
self.fnames_syn = [os.path.join(eventpath_syn, filename) for filename in os.listdir(eventpath_syn)]
if 'processed' in fpath:
qcbox_processed.setEnabled(True)
if qcbox_processed.isEnabled():
@ -2149,7 +2125,7 @@ class MainWindow(QMainWindow):
def finish_pg_plot(self):
self.getPlotWidget().updateWidget()
plots = self.wfp_thread.data
plots, gaps = self.wfp_thread.data
# do not show plot if no data are given
self.wf_scroll_area.setVisible(len(plots) > 0)
self.no_data_label.setVisible(not len(plots) > 0)
@ -2308,7 +2284,7 @@ class MainWindow(QMainWindow):
comp = self.getComponent()
title = 'section: {0} components'.format(zne_text[comp])
wfst = self.get_data().getWFData()
wfsyn = self.get_data().getAltWFdata()
wfsyn = self.get_data().getSynWFData()
if self.filterActionP.isChecked() and filter:
self.filterWaveformData(plot=False, phase='P')
elif self.filterActionS.isChecked() and filter:
@ -2323,8 +2299,8 @@ class MainWindow(QMainWindow):
self.plot_method = 'normal'
rval = plotWidget.plotWFData(wfdata=wfst, wfsyn=wfsyn, title=title, mapping=False, component=comp,
nth_sample=int(nth_sample), method=self.plot_method, gain=self.gain)
plots = rval if rval else []
return plots
plots, gaps = rval if rval else ([], [])
return plots, gaps
def adjustPlotHeight(self):
if self.pg:
@ -2620,21 +2596,18 @@ class MainWindow(QMainWindow):
print("Warning! No network, station, and location info available!")
return
self.update_status('picking on station {0}'.format(station))
wfdata = self.get_data().getOriginalWFData().copy()
wfdata_comp = self.get_data().getAltWFdata().copy()
data = self.get_data().getOriginalWFData().copy()
event = self.get_current_event()
wftype = self.dataPlot.qcombo_processed.currentText() if self.obspy_dmt else None
pickDlg = PickDlg(self, parameter=self._inputs,
data=wfdata.select(station=station),
data_compare=wfdata_comp.select(station=station),
data=data.select(station=station),
station=station, network=network,
location=location,
picks=self.getPicksOnStation(station, 'manual'),
autopicks=self.getPicksOnStation(station, 'auto'),
metadata=self.metadata, event=event,
model=self.inputs.get('taup_model'),
filteroptions=self.filteroptions, wftype=wftype,
show_comp_data=self.dataPlot.comp_checkbox.isChecked())
filteroptions=self.filteroptions, wftype=wftype)
if self.filterActionP.isChecked():
pickDlg.currentPhase = "P"
pickDlg.filterWFData()
@ -3013,16 +2986,10 @@ class MainWindow(QMainWindow):
event = self.get_current_event()
event.pylot_picks = {}
event.pylot_autopicks = {}
picksdict = picksdict_from_picks(evt=self.get_data().get_evt_data(), parameter=self.getParameter())
picksdict = picksdict_from_picks(evt=self.get_data().get_evt_data())
event.addPicks(picksdict['manual'])
event.addAutopicks(picksdict['auto'])
def getParameter(self):
if hasattr(self.project, 'parameter') and isinstance(self.project.parameter, PylotParameter):
return self.project.parameter
else:
return self._inputs
def drawPicks(self, station=None, picktype=None, stime=None):
# if picktype not specified, draw both
if not stime:
@ -3490,7 +3457,7 @@ class MainWindow(QMainWindow):
self.event_table.setCellWidget(r_index, c_index, item)
header = self.event_table.horizontalHeader()
header.setSectionResizeMode(QtWidgets.QHeaderView.ResizeToContents)
header.setResizeMode(QtWidgets.QHeaderView.ResizeToContents)
header.setStretchLastSection(True)
self.event_table.cellChanged[int, int].connect(cell_changed)
self.event_table.cellClicked[int, int].connect(cell_clicked)
@ -3664,7 +3631,7 @@ class MainWindow(QMainWindow):
return True
return False
def update_status(self, message, duration=10000):
def update_status(self, message, duration=5000):
self.statusBar().showMessage(message, duration)
if self.get_data() is not None:
if not self.get_current_event() or not self.project.location:
@ -3717,13 +3684,10 @@ class MainWindow(QMainWindow):
if not self.okToContinue():
return
if not fnm:
settings = QSettings()
dir = settings.value('current_project_path')
dlg = QFileDialog(parent=self, directory=dir)
dlg = QFileDialog(parent=self)
fnm = dlg.getOpenFileName(self, 'Open project file...', filter='Pylot project (*.plp)')[0]
if not fnm:
return
settings.setValue('current_project_path', os.path.split(fnm)[0])
if not os.path.exists(fnm):
QMessageBox.warning(self, 'Could not open file',
'Could not open project file {}. File does not exist.'.format(fnm))
@ -3841,8 +3805,7 @@ class MainWindow(QMainWindow):
def closeEvent(self, event):
if self.okToContinue():
if hasattr(self, 'logwidget'):
self.logwidget.close()
self.logwidget.close()
event.accept()
else:
event.ignore()

View File

@ -99,7 +99,7 @@ We hope to solve these with the next release.
## Staff
Original author(s): M. Rische, S. Wehling-Benatelli, L. Kueperkoch, M. Bischoff (PILOT)
Original author(s): L. Kueperkoch, S. Wehling-Benatelli, M. Bischoff (PILOT)
Developer(s): S. Wehling-Benatelli, M. Paffrath, L. Kueperkoch, K. Olbert, M. Bischoff, C. Wollin, M. Rische, D. Arnold, K. Cökerim, S. Zimmermann

View File

@ -119,9 +119,13 @@ def autoPyLoT(input_dict=None, parameter=None, inputfile=None, fnames=None, even
obspyDMT_wfpath = input_dict['obspyDMT_wfpath']
if not parameter:
if not inputfile:
print('Using default input parameter')
parameter = PylotParameter(inputfile)
if inputfile:
parameter = PylotParameter(inputfile)
# iplot = parameter['iplot']
else:
infile = os.path.join(os.path.expanduser('~'), '.pylot', 'pylot.in')
print('Using default input file {}'.format(infile))
parameter = PylotParameter(infile)
else:
if not type(parameter) == PylotParameter:
print('Wrong input type for parameter: {}'.format(type(parameter)))

View File

@ -3,10 +3,8 @@
#$ -l low
#$ -cwd
#$ -pe smp 40
##$ -l mem=3G
#$ -l h_vmem=6G
#$ -l mem=2G
#$ -l h_vmem=2G
#$ -l os=*stretch
conda activate pylot_311
python ./autoPyLoT.py -i /home/marcel/.pylot/pylot_adriaarray.in -c 20 -dmt processed
python ./autoPyLoT.py -i /home/marcel/.pylot/pylot_alparray_mantle_corr_stack_0.03-0.5.in -dmt processed -c $NSLOTS

View File

@ -8,7 +8,7 @@ dependencies:
- numpy=1.22.3
- obspy=1.3.0
- pyqtgraph=0.12.4
- pyside2>=5.13.2
- pyside2=5.13.2
- python=3.8.12
- qt>=5.12.9
- qt=5.12.9
- scipy=1.8.0

View File

@ -418,10 +418,6 @@ class MomentMagnitude(Magnitude):
distance = degrees2kilometers(a.distance)
azimuth = a.azimuth
incidence = a.takeoff_angle
if not 0. <= incidence <= 360.:
if self.verbose:
print(f'WARNING: Incidence angle outside bounds - {incidence}')
return
w0, fc = calcsourcespec(scopy, onset, self.p_velocity, distance,
azimuth, incidence, self.p_attenuation,
self.plot_flag, self.verbose)

View File

@ -2,7 +2,6 @@
# -*- coding: utf-8 -*-
import copy
import logging
import os
from PySide2.QtWidgets import QMessageBox
@ -20,7 +19,7 @@ from pylot.core.util.errors import FormatError, OverwriteError
from pylot.core.util.event import Event
from pylot.core.util.obspyDMT_interface import qml_from_obspyDMT
from pylot.core.util.utils import fnConstructor, full_range, check4rotated, \
check_for_gaps_and_merge, trim_station_components, check_for_nan
check4gapsAndMerge, trim_station_components
class Data(object):
@ -65,7 +64,7 @@ class Data(object):
elif 'LOC' in evtdata:
raise NotImplementedError('PILOT location information '
'read support not yet '
'implemented.')
'implemeted.')
elif 'event.pkl' in evtdata:
evtdata = qml_from_obspyDMT(evtdata)
else:
@ -409,16 +408,18 @@ class Data(object):
not implemented: {1}'''.format(evtformat, e))
if fnext == '_focmec.in':
try:
parameter = PylotParameter()
logging.warning('Using default input parameter')
infile = os.path.join(os.path.expanduser('~'), '.pylot', 'pylot.in')
print('Using default input file {}'.format(infile))
parameter = PylotParameter(infile)
focmec.export(picks_copy, fnout + fnext, parameter, eventinfo=self.get_evt_data())
except KeyError as e:
raise KeyError('''{0} export format
not implemented: {1}'''.format(evtformat, e))
if fnext == '.pha':
try:
parameter = PylotParameter()
logging.warning('Using default input parameter')
infile = os.path.join(os.path.expanduser('~'), '.pylot', 'pylot.in')
print('Using default input file {}'.format(infile))
parameter = PylotParameter(infile)
hypodd.export(picks_copy, fnout + fnext, parameter, eventinfo=self.get_evt_data())
except KeyError as e:
raise KeyError('''{0} export format
@ -450,30 +451,20 @@ class Data(object):
data.filter(**kwargs)
self.dirty = True
def setWFData(self, fnames, fnames_alt=None, checkRotated=False, metadata=None, tstart=0, tstop=0):
def setWFData(self, fnames, fnames_syn=None, checkRotated=False, metadata=None, tstart=0, tstop=0):
"""
Clear current waveform data and set given waveform data
:param fnames: waveform data names to append
:param fnames_alt: alternative data to show (e.g. synthetic/processed)
:type fnames: list
"""
def check_fname_exists(filenames: list) -> list:
if filenames:
filenames = [fn for fn in filenames if os.path.isfile(fn)]
return filenames
self.wfdata = Stream()
self.wforiginal = None
self.wf_alt = Stream()
self.wfsyn = Stream()
if tstart == tstop:
tstart = tstop = None
self.tstart = tstart
self.tstop = tstop
# remove directories
fnames = check_fname_exists(fnames)
fnames_alt = check_fname_exists(fnames_alt)
# if obspy_dmt:
# wfdir = 'raw'
# self.processed = False
@ -491,8 +482,8 @@ class Data(object):
# wffnames = fnames
if fnames is not None:
self.appendWFData(fnames)
if fnames_alt is not None:
self.appendWFData(fnames_alt, alternative=True)
if fnames_syn is not None:
self.appendWFData(fnames_syn, synthetic=True)
else:
return False
@ -500,9 +491,7 @@ class Data(object):
# remove possible underscores in station names
# self.wfdata = remove_underscores(self.wfdata)
# check for gaps and merge
self.wfdata, _ = check_for_gaps_and_merge(self.wfdata)
# check for nans
check_for_nan(self.wfdata)
self.wfdata = check4gapsAndMerge(self.wfdata)
# check for stations with rotated components
if checkRotated and metadata is not None:
self.wfdata = check4rotated(self.wfdata, metadata, verbosity=0)
@ -514,7 +503,7 @@ class Data(object):
self.dirty = False
return True
def appendWFData(self, fnames, alternative=False):
def appendWFData(self, fnames, synthetic=False):
"""
Read waveform data from fnames and append it to current wf data
:param fnames: waveform data to append
@ -527,20 +516,20 @@ class Data(object):
if self.dirty:
self.resetWFData()
orig_or_alternative_data = {True: self.wf_alt,
False: self.wfdata}
real_or_syn_data = {True: self.wfsyn,
False: self.wfdata}
warnmsg = ''
for fname in set(fnames):
try:
orig_or_alternative_data[alternative] += read(fname, starttime=self.tstart, endtime=self.tstop)
real_or_syn_data[synthetic] += read(fname, starttime=self.tstart, endtime=self.tstop)
except TypeError:
try:
orig_or_alternative_data[alternative] += read(fname, format='GSE2', starttime=self.tstart, endtime=self.tstop)
real_or_syn_data[synthetic] += read(fname, format='GSE2', starttime=self.tstart, endtime=self.tstop)
except Exception as e:
try:
orig_or_alternative_data[alternative] += read(fname, format='SEGY', starttime=self.tstart,
endtime=self.tstop)
real_or_syn_data[synthetic] += read(fname, format='SEGY', starttime=self.tstart,
endtime=self.tstop)
except Exception as e:
warnmsg += '{0}\n{1}\n'.format(fname, e)
except SacIOError as se:
@ -555,8 +544,8 @@ class Data(object):
def getOriginalWFData(self):
return self.wforiginal
def getAltWFdata(self):
return self.wf_alt
def getSynWFData(self):
return self.wfsyn
def resetWFData(self):
"""

View File

@ -1,7 +1,6 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import glob
import logging
import os
import warnings
@ -218,7 +217,7 @@ def picksdict_from_obs(fn):
return picks
def picksdict_from_picks(evt, parameter=None):
def picksdict_from_picks(evt):
"""
Takes an Event object and return the pick dictionary commonly used within
PyLoT
@ -231,7 +230,6 @@ def picksdict_from_picks(evt, parameter=None):
'auto': {}
}
for pick in evt.picks:
errors = None
phase = {}
station = pick.waveform_id.station_code
if pick.waveform_id.channel_code is None:
@ -275,28 +273,31 @@ def picksdict_from_picks(evt, parameter=None):
phase['epp'] = epp
phase['lpp'] = lpp
phase['spe'] = spe
weight = phase.get('weight')
if not weight:
if not parameter:
logging.warning('Using ')
logging.warning('Using default input parameter')
parameter = PylotParameter()
pick.phase_hint = identifyPhase(pick.phase_hint)
try:
phase['weight'] = weight
except:
# get onset weight from uncertainty
infile = os.path.join(os.path.expanduser('~'), '.pylot', 'pylot.in')
print('Using default input file {}'.format(infile))
parameter = PylotParameter(infile)
if pick.phase_hint == 'P':
errors = parameter['timeerrorsP']
elif pick.phase_hint == 'S':
errors = parameter['timeerrorsS']
if errors:
weight = get_quality_class(spe, errors)
phase['weight'] = weight
weight = get_quality_class(spe, errors)
phase['weight'] = weight
phase['channel'] = channel
phase['network'] = network
phase['picker'] = pick_method
if pick.polarity == 'positive':
phase['fm'] = 'U'
elif pick.polarity == 'negative':
phase['fm'] = 'D'
else:
try:
if pick.polarity == 'positive':
phase['fm'] = 'U'
elif pick.polarity == 'negative':
phase['fm'] = 'D'
else:
phase['fm'] = 'N'
except:
print("No FM info available!")
phase['fm'] = 'N'
phase['filter_id'] = filter_id if filter_id is not None else ''

View File

@ -82,8 +82,8 @@ def locate(fnin, parameter=None):
:param fnin: external program name
:return: None
"""
exe_path = os.path.join(parameter['nllocbin'], 'NLLoc')
if not os.path.isfile(exe_path):
exe_path = parameter['nllocbin'] + '/bin/NLLoc'
if exe_path is None:
raise NLLocError('NonLinLoc executable not found; check your '
'environment variables')

View File

@ -20,7 +20,7 @@ from pylot.core.pick.charfuns import CharacteristicFunction
from pylot.core.pick.charfuns import HOScf, AICcf, ARZcf, ARHcf, AR3Ccf
from pylot.core.pick.picker import AICPicker, PragPicker
from pylot.core.pick.utils import checksignallength, checkZ4S, earllatepicker, \
getSNR, fmpicker, checkPonsets, wadaticheck, get_quality_class, PickingFailedException, MissingTraceException
getSNR, fmpicker, checkPonsets, wadaticheck, get_quality_class
from pylot.core.util.utils import getPatternLine, gen_Pool, \
get_bool, identifyPhaseID, get_none, correct_iplot
@ -232,6 +232,20 @@ class PickingContainer:
self.Sflag = 0
class MissingTraceException(ValueError):
"""
Used to indicate missing traces in a obspy.core.stream.Stream object
"""
pass
class PickingFailedException(Exception):
"""
Raised when picking fails due to missing values etc.
"""
pass
class AutopickStation(object):
def __init__(self, wfstream, pickparam, verbose, iplot=0, fig_dict=None, metadata=None, origin=None):
@ -646,7 +660,7 @@ class AutopickStation(object):
ax1.set_ylim([-1.5, 1.5])
ax1.set_ylabel('Normalized Counts')
if self.horizontal_traces_exist():# and self.s_data.Sflag == 1:
if self.horizontal_traces_exist() and self.s_data.Sflag == 1:
# plot E trace
ax2 = fig.add_subplot(3, 1, 2, sharex=ax1)
th1data = np.linspace(0, self.etrace.stats.endtime - self.etrace.stats.starttime,
@ -922,7 +936,7 @@ class AutopickStation(object):
"minFMSNR"]:
# if SNR is high enough, try to determine first motion of onset
self.set_current_figure('fm_picker')
self.p_results.fm = fmpicker(self.zstream.copy(), z_copy, self.pickparams["fmpickwin"], self.p_results.mpp,
self.p_results.fm = fmpicker(self.zstream, z_copy, self.pickparams["fmpickwin"], self.p_results.mpp,
self.iplot, self.current_figure, self.current_linecolor)
msg = "autopickstation: P-weight: {}, SNR: {}, SNR[dB]: {}, Polarity: {}"
msg = msg.format(self.p_results.weight, self.p_results.snr, self.p_results.snrdb, self.p_results.fm)

View File

@ -20,8 +20,6 @@ import numpy as np
from scipy import signal
from obspy.core import Stream
from pylot.core.pick.utils import PickingFailedException
class CharacteristicFunction(object):
"""
@ -295,7 +293,7 @@ class HOScf(CharacteristicFunction):
if j < 4:
LTA[j] = 0
STA[j] = 0
elif j <= ista and self.getOrder() == 2:
elif j <= ista:
lta = (y[j] + lta * (j - 1)) / j
if self.getOrder() == 2:
sta = (y[j] + sta * (j - 1)) / j
@ -490,9 +488,6 @@ class ARHcf(CharacteristicFunction):
print('Calculating AR-prediction error from both horizontal traces ...')
xnp = self.getDataArray(self.getCut())
if len(xnp[0]) == 0:
raise PickingFailedException('calcCF: Found empty data trace for cut times. Return')
n0 = np.isnan(xnp[0].data)
if len(n0) > 1:
xnp[0].data[n0] = 0

View File

@ -178,9 +178,7 @@ class AICPicker(AutoPicker):
aic = tap * self.cf + max(abs(self.cf))
# smooth AIC-CF
ismooth = int(round(self.Tsmooth / self.dt))
# MP MP better start with original data than zeros if array shall be smoothed, created artificial value before
# when starting with i in range(1...) loop below and subtracting offset afterwards
aicsmooth = np.copy(aic)
aicsmooth = np.zeros(len(aic))
if len(aic) < ismooth:
print('AICPicker: Tsmooth larger than CF!')
return
@ -190,7 +188,7 @@ class AICPicker(AutoPicker):
ii1 = i - ismooth
aicsmooth[i] = aicsmooth[i - 1] + (aic[i] - aic[ii1]) / ismooth
else:
aicsmooth[i] = np.mean(aic[0: i]) # MP MP created np.nan for i=1
aicsmooth[i] = np.mean(aic[1: i])
# remove offset in AIC function
offset = abs(min(aic) - min(aicsmooth))
aicsmooth = aicsmooth - offset
@ -199,7 +197,7 @@ class AICPicker(AutoPicker):
# minimum in AIC function
icfmax = np.argmax(cf)
# TODO: If this shall be kept, maybe add thresh_factor to pylot parameters
# MP MP testing threshold
thresh_hit = False
thresh_factor = 0.7
thresh = thresh_factor * cf[icfmax]
@ -211,6 +209,7 @@ class AICPicker(AutoPicker):
if sample <= cf[index - 1]:
icfmax = index - 1
break
# MP MP ---
# find minimum in AIC-CF front of maximum of HOS/AR-CF
lpickwindow = int(round(self.PickWindow / self.dt))
@ -337,7 +336,7 @@ class AICPicker(AutoPicker):
self.slope = 1 / (len(dataslope) * self.Data[0].stats.delta) * (datafit[-1] - datafit[0])
# normalize slope to maximum of cf to make it unit independent
self.slope /= aicsmooth[iaicmax]
except Exception as e:
except ValueError as e:
print("AICPicker: Problems with data fitting! {}".format(e))
else:

View File

@ -62,7 +62,7 @@ def earllatepicker(X, nfac, TSNR, Pick1, iplot=0, verbosity=1, fig=None, linecol
plt_flag = 0
try:
iplot = int(iplot)
except ValueError:
except:
if get_bool(iplot):
iplot = 2
else:
@ -890,8 +890,6 @@ def checksignallength(X, pick, minsiglength, pickparams, iplot=0, fig=None, line
input()
except SyntaxError:
pass
except EOFError:
pass
plt.close(fig)
return returnflag
@ -1334,6 +1332,22 @@ def get_quality_class(uncertainty, weight_classes):
return quality
def set_NaNs_to(data, nan_value):
"""
Replace all NaNs in data with nan_value
:param data: array holding data
:type data: `~numpy.ndarray`
:param nan_value: value which all NaNs are set to
:type nan_value: float, int
:return: data array with all NaNs replaced with nan_value
:rtype: `~numpy.ndarray`
"""
nn = np.isnan(data)
if np.any(nn):
data[nn] = nan_value
return data
def taper_cf(cf):
"""
Taper cf data to get rid off of side maximas
@ -1518,17 +1532,3 @@ if __name__ == '__main__':
import doctest
doctest.testmod()
class PickingFailedException(Exception):
"""
Raised when picking fails due to missing values etc.
"""
pass
class MissingTraceException(ValueError):
"""
Used to indicate missing traces in a obspy.core.stream.Stream object
"""
pass

View File

@ -13,7 +13,6 @@ import obspy
from PySide2 import QtWidgets
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from mpl_toolkits.axes_grid1.inset_locator import inset_axes
from pylot.core.util.utils import identifyPhaseID
from scipy.interpolate import griddata
from pylot.core.pick.utils import get_quality_class
@ -76,6 +75,7 @@ class Array_map(QtWidgets.QWidget):
self._style = None if not hasattr(parent, '_style') else parent._style
self.show()
def init_map(self):
self.init_colormap()
@ -124,8 +124,8 @@ class Array_map(QtWidgets.QWidget):
self.cmaps_box = QtWidgets.QComboBox()
self.cmaps_box.setMaxVisibleItems(20)
[self.cmaps_box.addItem(map_name) for map_name in sorted(plt.colormaps())]
# try to set to viridis as default
self.cmaps_box.setCurrentIndex(self.cmaps_box.findText('viridis'))
# try to set to hsv as default
self.cmaps_box.setCurrentIndex(self.cmaps_box.findText('hsv'))
self.top_row.addWidget(QtWidgets.QLabel('Select a phase: '))
self.top_row.addWidget(self.comboBox_phase)
@ -280,12 +280,9 @@ class Array_map(QtWidgets.QWidget):
self.canvas.axes.figure.canvas.draw_idle()
def onpick(self, event):
btn_msg = {1: ' in selection. Aborted', 2: ' to delete a pick on. Aborted', 3: ' to display info.'}
ind = event.ind
button = event.mouseevent.button
msg_reason = None
if len(ind) > 1:
self._parent.update_status(f'Found more than one station {btn_msg.get(button)}')
if ind == []:
return
if button == 1:
self.openPickDlg(ind)
@ -388,14 +385,7 @@ class Array_map(QtWidgets.QWidget):
try:
station_name = st_id.split('.')[-1]
# current_picks_dict: auto or manual
station_picks = self.current_picks_dict().get(station_name)
if not station_picks:
continue
for phase_hint, pick in station_picks.items():
if identifyPhaseID(phase_hint) == phase:
break
else:
continue
pick = self.current_picks_dict()[station_name][phase]
if pick['picker'] == 'auto':
if not pick['spe']:
continue
@ -474,19 +464,17 @@ class Array_map(QtWidgets.QWidget):
transform=ccrs.PlateCarree(), label='deleted'))
def openPickDlg(self, ind):
wfdata = self._parent.get_data().getWFData()
wfdata_comp = self._parent.get_data().getWFDataComp()
data = self._parent.get_data().getWFData()
for index in ind:
network, station = self._station_onpick_ids[index].split('.')[:2]
pyl_mw = self._parent
try:
wfdata = wfdata.select(station=station)
wfdata_comp = wfdata_comp.select(station=station)
if not wfdata:
data = data.select(station=station)
if not data:
self._warn('No data for station {}'.format(station))
return
pickDlg = PickDlg(self._parent, parameter=self.parameter,
data=wfdata.copy(), data_compare=wfdata_comp.copy(), network=network, station=station,
data=data, network=network, station=station,
picks=self._parent.get_current_event().getPick(station),
autopicks=self._parent.get_current_event().getAutopick(station),
filteroptions=self._parent.filteroptions, metadata=self.metadata,

View File

@ -2,7 +2,6 @@
# -*- coding: utf-8 -*-
import glob
import logging
import os
import sys
@ -190,11 +189,7 @@ class Metadata(object):
metadata = self.get_metadata(seed_id, time)
if not metadata:
return
try:
return metadata['data'].get_coordinates(seed_id, time)
# no specific exception defined in obspy inventory
except Exception as e:
logging.warning(f'Could not get metadata for {seed_id}')
return metadata['data'].get_coordinates(seed_id, time)
def get_all_coordinates(self):
def stat_info_from_parser(parser):
@ -276,7 +271,7 @@ class Metadata(object):
continue
invtype, robj = self._read_metadata_file(os.path.join(path_to_inventory, fname))
try:
# robj.get_coordinates(station_seed_id) # TODO: Commented out, failed with Parser, is this needed?
robj.get_coordinates(station_seed_id)
self.inventory_files[fname] = {'invtype': invtype,
'data': robj}
if station_seed_id in self.seed_ids.keys():

View File

@ -42,7 +42,7 @@ def main(project_file_path, manual=False, auto=True, file_format='png', f_ext=''
for item in input_list:
array_map_worker(item)
else:
pool = multiprocessing.Pool(ncores, maxtasksperchild=1000)
pool = multiprocessing.Pool(ncores)
pool.map(array_map_worker, input_list)
pool.close()
pool.join()

View File

@ -160,7 +160,7 @@ class MultiThread(QThread):
try:
if not self.ncores:
self.ncores = multiprocessing.cpu_count()
pool = multiprocessing.Pool(self.ncores, maxtasksperchild=1000)
pool = multiprocessing.Pool(self.ncores)
self.data = pool.map_async(self.func, self.args, callback=self.emitDone)
# self.data = pool.apply_async(self.func, self.shotlist, callback=self.emitDone) #emit each time returned
pool.close()

View File

@ -1,15 +1,13 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import glob
import hashlib
import logging
import os
import platform
import re
import subprocess
import warnings
from typing import Literal, Tuple, Type
from functools import lru_cache
import numpy as np
from obspy import UTCDateTime, read
@ -107,7 +105,7 @@ def gen_Pool(ncores=0):
print('gen_Pool: Generated multiprocessing Pool with {} cores\n'.format(ncores))
pool = multiprocessing.Pool(ncores, maxtasksperchild=100)
pool = multiprocessing.Pool(ncores)
return pool
@ -334,7 +332,7 @@ def get_none(value):
def get_bool(value):
"""
Convert string representations of bools to their true boolean value. Return value if it cannot be identified as bool.
Convert string representations of bools to their true boolean value
:param value:
:type value: str, bool, int, float
:return: true boolean value
@ -356,22 +354,17 @@ def get_bool(value):
False
>>> get_bool(-0.3)
False
>>> get_bool(None)
None
"""
if type(value) is bool:
if type(value) == bool:
return value
elif value in ['True', 'true']:
return True
elif value in ['False', 'false']:
return False
elif isinstance(value, float) or isinstance(value, int):
if value > 0. or value > 0:
return True
else:
return False
elif value > 0. or value > 0:
return True
else:
return value
return False
def four_digits(year):
@ -900,6 +893,19 @@ def trim_station_components(data, trim_start=True, trim_end=True):
return data
def merge_stream(stream):
gaps = stream.get_gaps()
if gaps:
# list of merged stations (seed_ids)
merged = ['{}.{}.{}.{}'.format(*gap[:4]) for gap in gaps]
stream.merge(method=1)
print('Merged the following stations because of gaps:')
for merged_station in merged:
print(merged_station)
return stream, gaps
def check4gapsAndRemove(data):
"""
check for gaps in Stream and remove them
@ -920,12 +926,12 @@ def check4gapsAndRemove(data):
return data
def check_for_gaps_and_merge(data):
def check4gapsAndMerge(data):
"""
check for gaps in Stream and merge if gaps are found
:param data: stream of seismic data
:type data: `~obspy.core.stream.Stream`
:return: data stream, gaps returned from obspy get_gaps
:return: data stream
:rtype: `~obspy.core.stream.Stream`
"""
gaps = data.get_gaps()
@ -936,7 +942,7 @@ def check_for_gaps_and_merge(data):
for merged_station in merged:
print(merged_station)
return data, gaps
return data
def check4doubled(data):
@ -966,46 +972,6 @@ def check4doubled(data):
return data
def check_for_nan(data, nan_value=0.):
"""
Replace all NaNs in data with nan_value (in place)
:param data: stream of seismic data
:type data: `~obspy.core.stream.Stream`
:param nan_value: value which all NaNs are set to
:type nan_value: float, int
:return: None
"""
if not data:
return
for trace in data:
np.nan_to_num(trace.data, copy=False, nan=nan_value)
def get_pylot_eventfile_with_extension(event, fext):
if hasattr(event, 'path'):
eventpath = event.path
else:
logging.warning('No attribute path found for event.')
return
eventname = event.pylot_id #path.split('/')[-1] # or event.pylot_id
filename = os.path.join(eventpath, 'PyLoT_' + eventname + fext)
if os.path.isfile(filename):
return filename
def get_possible_pylot_eventfile_extensions(event, fext):
if hasattr(event, 'path'):
eventpath = event.path
else:
logging.warning('No attribute path found for event.')
return []
eventname = event.pylot_id
filename = os.path.join(eventpath, 'PyLoT_' + eventname + fext)
filenames = glob.glob(filename)
extensions = [os.path.split(path)[-1].split('PyLoT_' + eventname)[-1] for path in filenames]
return extensions
def get_stations(data):
"""
Get list of all station names in data-stream
@ -1072,7 +1038,7 @@ def check4rotated(data, metadata=None, verbosity=1):
# check if any traces in this station need to be rotated
trace_ids = [trace.id for trace in wfs_in]
if not rotation_required(trace_ids):
logging.debug(f"Stream does not need any rotation: Traces are {trace_ids=}")
print(f"Stream does not need any rotation: Traces are {trace_ids=}")
return wfs_in
# check metadata quality
@ -1084,7 +1050,7 @@ def check4rotated(data, metadata=None, verbosity=1):
azimuths.append(metadata.get_coordinates(tr_id, t_start)['azimuth'])
dips.append(metadata.get_coordinates(tr_id, t_start)['dip'])
except (KeyError, TypeError) as err:
logging.error(f"{type(err)=} occurred: {err=} Rotating not possible, not all azimuth and dip information "
print(f"{type(err)=} occurred: {err=} Rotating not possible, not all azimuth and dip information "
f"available in metadata. Stream remains unchanged.")
return wfs_in
except Exception as err:
@ -1204,7 +1170,6 @@ def identifyPhase(phase):
return False
@lru_cache
def identifyPhaseID(phase):
"""
Returns phase id (capital P or S)

View File

@ -7,7 +7,6 @@ Created on Wed Mar 19 11:27:35 2014
import copy
import datetime
import getpass
import glob
import multiprocessing
import os
import subprocess
@ -17,7 +16,6 @@ import traceback
import matplotlib
import numpy as np
from pylot.core.io.phases import getQualitiesfromxml
matplotlib.use('QT5Agg')
@ -38,7 +36,7 @@ from PySide2.QtWidgets import QAction, QApplication, QCheckBox, QComboBox, \
QGridLayout, QLabel, QLineEdit, QMessageBox, \
QTabWidget, QToolBar, QVBoxLayout, QHBoxLayout, QWidget, \
QPushButton, QFileDialog, QInputDialog
from PySide2.QtCore import QSettings, Qt, QUrl, Signal, QTimer
from PySide2.QtCore import QSettings, Qt, QUrl, Signal
from PySide2.QtWebEngineWidgets import QWebEngineView as QWebView
from obspy import Stream, Trace, UTCDateTime
from obspy.core.util import AttribDict
@ -53,10 +51,10 @@ from pylot.core.pick.autopick import fmpicker
from pylot.core.util.defaults import OUTPUTFORMATS, FILTERDEFAULTS
from pylot.core.util.utils import prep_time_axis, full_range, demeanTrace, isSorted, findComboBoxIndex, clims, \
pick_linestyle_plt, pick_color_plt, \
check4rotated, check4doubled, check_for_gaps_and_merge, check_for_nan, identifyPhase, \
check4rotated, check4doubled, merge_stream, identifyPhase, \
loopIdentifyPhase, trim_station_components, transformFilteroptions2String, \
identifyPhaseID, get_bool, get_none, pick_color, getAutoFilteroptions, SetChannelComponents, \
station_id_remove_channel, get_pylot_eventfile_with_extension, get_possible_pylot_eventfile_extensions
station_id_remove_channel
from autoPyLoT import autoPyLoT
from pylot.core.util.thread import Thread
from pylot.core.util.dataprocessing import Metadata
@ -795,7 +793,7 @@ class WaveformWidgetPG(QtWidgets.QWidget):
def connect_signals(self):
self.qcombo_processed.activated.connect(self.parent().newWF)
self.comp_checkbox.clicked.connect(self.parent().newWF)
self.syn_checkbox.clicked.connect(self.parent().newWF)
def init_labels(self):
self.label_layout.addWidget(self.status_label)
@ -806,13 +804,13 @@ class WaveformWidgetPG(QtWidgets.QWidget):
# use widgets as placeholder, so that child widgets keep position when others are hidden
mid_layout = QHBoxLayout()
right_layout = QHBoxLayout()
mid_layout.addWidget(self.comp_checkbox)
mid_layout.addWidget(self.syn_checkbox)
right_layout.addWidget(self.qcombo_processed)
mid_widget.setLayout(mid_layout)
right_widget.setLayout(right_layout)
self.label_layout.addWidget(mid_widget)
self.label_layout.addWidget(right_widget)
self.comp_checkbox.setLayoutDirection(Qt.RightToLeft)
self.syn_checkbox.setLayoutDirection(Qt.RightToLeft)
self.label_layout.setStretch(0, 4)
self.label_layout.setStretch(1, 0)
self.label_layout.setStretch(2, 0)
@ -827,7 +825,7 @@ class WaveformWidgetPG(QtWidgets.QWidget):
label = QtWidgets.QLabel()
self.perm_labels.append(label)
self.qcombo_processed = QtWidgets.QComboBox()
self.comp_checkbox = QtWidgets.QCheckBox('Load comparison data')
self.syn_checkbox = QtWidgets.QCheckBox('synthetics')
self.addQCboxItem('processed', 'green')
self.addQCboxItem('raw', 'black')
# self.perm_qcbox_right.setAlignment(2)
@ -836,11 +834,9 @@ class WaveformWidgetPG(QtWidgets.QWidget):
def getPlotDict(self):
return self.plotdict
def activateObspyDMToptions(self, activate: bool) -> None:
self.qcombo_processed.setEnabled(activate)
def activateCompareOptions(self, activate: bool) -> None:
self.comp_checkbox.setEnabled(activate)
def activateObspyDMToptions(self, activate):
self.syn_checkbox.setVisible(activate)
self.qcombo_processed.setVisible(activate)
def setPermText(self, number, text=None, color='black'):
if not 0 <= number < len(self.perm_labels):
@ -864,26 +860,13 @@ class WaveformWidgetPG(QtWidgets.QWidget):
def clearPlotDict(self):
self.plotdict = dict()
def plotWFData(self, wfdata, wfsyn=None, title=None, scaleddata=False, mapping=True,
component='*', nth_sample=1, verbosity=0, method='normal', gain=1., shift_syn=0.2):
def station_sort(nslc):
"""Try to sort after station integer in case of a line array (e.g. active seismics)"""
try:
rval = sorted(nslc, key=lambda x: int(x.split('.')[1]))
return rval
except ValueError as e:
# this is the standard case for seismological stations
pass
except Exception as e:
print(f'Sorting by station integer failed with unknown exception: {e}')
# fallback to default sorting
return sorted(nslc)
def plotWFData(self, wfdata, wfsyn=None, title=None, zoomx=None, zoomy=None,
noiselevel=None, scaleddata=False, mapping=True,
component='*', nth_sample=1, iniPick=None, verbosity=0,
method='normal', gain=1.):
if not wfdata:
print('Nothing to plot.')
return
self.title = title
self.clearPlotDict()
self.wfstart, self.wfend = full_range(wfdata)
@ -901,14 +884,14 @@ class WaveformWidgetPG(QtWidgets.QWidget):
else:
st_select = wfdata
# st_select, gaps = check_for_gaps_and_merge(st_select) #MP MP commented because probably done twice
st_select, gaps = merge_stream(st_select)
# list containing tuples of network, station, channel (for sorting)
nslc = []
for trace in st_select:
nslc.append(
trace.get_id()) # (trace.stats.network, trace.stats.station, trace.stats.location trace.stats.channel))
nslc = station_sort(nslc)
nslc.sort()
nslc.reverse()
plots = []
@ -963,7 +946,7 @@ class WaveformWidgetPG(QtWidgets.QWidget):
[time for index, time in enumerate(time_ax_syn) if not index % nth_sample] if st_syn else [])
trace.data = np.array(
[datum * gain + n for index, datum in enumerate(trace.data) if not index % nth_sample])
trace_syn.data = np.array([datum + n + shift_syn for index, datum in enumerate(trace_syn.data)
trace_syn.data = np.array([datum + n for index, datum in enumerate(trace_syn.data)
if not index % nth_sample] if st_syn else [])
plots.append((times, trace.data,
times_syn, trace_syn.data))
@ -972,7 +955,7 @@ class WaveformWidgetPG(QtWidgets.QWidget):
self.ylabel = ''
self.setXLims([0, self.wfend - self.wfstart])
self.setYLims([0.5, nmax + 0.5])
return plots
return plots, gaps
def minMax(self, trace, time_ax):
'''
@ -994,7 +977,7 @@ class WaveformWidgetPG(QtWidgets.QWidget):
min_ = data.min(axis=1)
max_ = data.max(axis=1)
if remaining_samples:
extreme_values = np.empty((npixel + 1, 2), dtype=float)
extreme_values = np.empty((npixel + 1, 2), dtype=np.float)
extreme_values[:-1, 0] = min_
extreme_values[:-1, 1] = max_
extreme_values[-1, 0] = \
@ -1002,7 +985,7 @@ class WaveformWidgetPG(QtWidgets.QWidget):
extreme_values[-1, 1] = \
trace.data[-remaining_samples:].max()
else:
extreme_values = np.empty((npixel, 2), dtype=float)
extreme_values = np.empty((npixel, 2), dtype=np.float)
extreme_values[:, 0] = min_
extreme_values[:, 1] = max_
data = extreme_values.flatten()
@ -1152,12 +1135,12 @@ class PylotCanvas(FigureCanvas):
ax.set_xlim(self.cur_xlim)
ax.set_ylim(self.cur_ylim)
self.refreshPickDlgText()
ax.figure.canvas.draw_idle()
ax.figure.canvas.draw()
def panRelease(self, gui_event):
self.press = None
self.press_rel = None
self.figure.canvas.draw_idle()
self.figure.canvas.draw()
def panZoom(self, gui_event, threshold=2., factor=1.1):
if not gui_event.x and not gui_event.y:
@ -1375,15 +1358,11 @@ class PylotCanvas(FigureCanvas):
plot_positions[channel] = plot_pos
return plot_positions
def plotWFData(self, wfdata, wfdata_compare=None, title=None, zoomx=None, zoomy=None,
def plotWFData(self, wfdata, title=None, zoomx=None, zoomy=None,
noiselevel=None, scaleddata=False, mapping=True,
component='*', nth_sample=1, iniPick=None, verbosity=0,
plot_additional=False, additional_channel=None, scaleToChannel=None,
snr=None):
def get_wf_dict(data: Stream = Stream(), linecolor = 'k', offset: float = 0., **plot_kwargs):
return dict(data=data, linecolor=linecolor, offset=offset, plot_kwargs=plot_kwargs)
ax = self.axes[0]
ax.cla()
@ -1394,33 +1373,21 @@ class PylotCanvas(FigureCanvas):
settings = QSettings()
compclass = SetChannelComponents.from_qsettings(settings)
linecolor = (0., 0., 0., 1.) if not self.style else self.style['linecolor']['rgba_mpl']
plot_streams = dict(wfdata=get_wf_dict(linecolor=linecolor, linewidth=0.7),
wfdata_comp=get_wf_dict(offset=0.1, linecolor='b', alpha=0.7, linewidth=0.5))
if not component == '*':
alter_comp = compclass.getCompPosition(component)
# alter_comp = str(alter_comp[0])
plot_streams['wfdata']['data'] = wfdata.select(component=component)
plot_streams['wfdata']['data'] += wfdata.select(component=alter_comp)
if wfdata_compare:
plot_streams['wfdata_comp']['data'] = wfdata_compare.select(component=component)
plot_streams['wfdata_comp']['data'] += wfdata_compare.select(component=alter_comp)
st_select = wfdata.select(component=component)
st_select += wfdata.select(component=alter_comp)
else:
plot_streams['wfdata']['data'] = wfdata
if wfdata_compare:
plot_streams['wfdata_comp']['data'] = wfdata_compare
st_main = plot_streams['wfdata']['data']
st_select = wfdata
if mapping:
plot_positions = self.calcPlotPositions(st_main, compclass)
plot_positions = self.calcPlotPositions(st_select, compclass)
# list containing tuples of network, station, channel and plot position (for sorting)
nslc = []
for plot_pos, trace in enumerate(st_main):
for plot_pos, trace in enumerate(st_select):
if not trace.stats.channel[-1] in ['Z', 'N', 'E', '1', '2', '3']:
print('Warning: Unrecognized channel {}'.format(trace.stats.channel))
continue
@ -1428,48 +1395,44 @@ class PylotCanvas(FigureCanvas):
nslc.sort()
nslc.reverse()
linecolor = (0., 0., 0., 1.) if not self.style else self.style['linecolor']['rgba_mpl']
for n, seed_id in enumerate(nslc):
network, station, location, channel = seed_id.split('.')
for wf_name, wf_dict in plot_streams.items():
st_select = wf_dict.get('data')
if not st_select:
continue
st = st_select.select(id=seed_id)
trace = st[0].copy()
if mapping:
n = plot_positions[trace.stats.channel]
if n > nmax:
nmax = n
if verbosity:
msg = 'plotting %s channel of station %s' % (channel, station)
print(msg)
stime = trace.stats.starttime - wfstart
time_ax = prep_time_axis(stime, trace)
if time_ax is not None:
if scaleToChannel:
st_scale = wfdata.select(channel=scaleToChannel)
if st_scale:
tr = st_scale[0]
trace.detrend('constant')
trace.normalize(np.max(np.abs(tr.data)) * 2)
scaleddata = True
if not scaleddata:
st = st_select.select(id=seed_id)
trace = st[0].copy()
if mapping:
n = plot_positions[trace.stats.channel]
if n > nmax:
nmax = n
if verbosity:
msg = 'plotting %s channel of station %s' % (channel, station)
print(msg)
stime = trace.stats.starttime - wfstart
time_ax = prep_time_axis(stime, trace)
if time_ax is not None:
if scaleToChannel:
st_scale = wfdata.select(channel=scaleToChannel)
if st_scale:
tr = st_scale[0]
trace.detrend('constant')
trace.normalize(np.max(np.abs(trace.data)) * 2)
trace.normalize(np.max(np.abs(tr.data)) * 2)
scaleddata = True
if not scaleddata:
trace.detrend('constant')
trace.normalize(np.max(np.abs(trace.data)) * 2)
offset = wf_dict.get('offset')
times = [time for index, time in enumerate(time_ax) if not index % nth_sample]
data = [datum + n + offset for index, datum in enumerate(trace.data) if not index % nth_sample]
ax.axhline(n, color="0.5", lw=0.5)
ax.plot(times, data, color=wf_dict.get('linecolor'), **wf_dict.get('plot_kwargs'))
if noiselevel is not None:
for level in [-noiselevel[channel], noiselevel[channel]]:
ax.plot([time_ax[0], time_ax[-1]],
[n + level, n + level],
color=wf_dict.get('linecolor'),
linestyle='dashed')
self.setPlotDict(n, seed_id)
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]
ax.axhline(n, color="0.5", lw=0.5)
ax.plot(times, data, color=linecolor, linewidth=0.7)
if noiselevel is not None:
for level in [-noiselevel[channel], noiselevel[channel]]:
ax.plot([time_ax[0], time_ax[-1]],
[n + level, n + level],
color=linecolor,
linestyle='dashed')
self.setPlotDict(n, seed_id)
if plot_additional and additional_channel:
compare_stream = wfdata.select(channel=additional_channel)
if compare_stream:
@ -1592,178 +1555,6 @@ class PylotCanvas(FigureCanvas):
self.draw()
class SearchFileByExtensionDialog(QtWidgets.QDialog):
def __init__(self, parent=None, label='Text: ', default_text='.xml', events=None):
super(SearchFileByExtensionDialog, self).__init__(parent)
self.events = events
self.filepaths = []
self.file_extensions = []
self.check_all_state = True
self.merge_strategy = None
self.default_text = default_text
self.label = label
self.setButtons()
self.setupUi()
self.connectSignals()
self.showPaths()
self.refreshSelectionBox()
# self.refresh_timer = QTimer(self)
# self.refresh_timer.timeout.connect(self.showPaths)
# self.refresh_timer.start(10000)
self.resize(800, 450)
def setupUi(self):
ncol = 4
self.main_layout = QtWidgets.QVBoxLayout()
self.header_layout = QtWidgets.QHBoxLayout()
self.footer_layout = QtWidgets.QHBoxLayout()
#
self.setLayout(self.main_layout)
# widgets inside the dialog
self.textLabel = QtWidgets.QLabel(self.label)
self.comboBox = QtWidgets.QComboBox()
self.comboBox.addItem(self.default_text)
self.comboBox.setEditable(True)
# optional search button, currently disabled. List refreshed when text changes
self.searchButton = QtWidgets.QPushButton('Search')
self.searchButton.setVisible(False)
# check/uncheck button for table
self.checkAllButton = QtWidgets.QPushButton('Check/Uncheck all')
# radiobutton for merge selection
self.mergeRadioButtonGroup = QtWidgets.QButtonGroup()
self.merge_button = QtWidgets.QRadioButton('Merge')
self.overwrite_button = QtWidgets.QRadioButton('Overwrite')
self.mergeRadioButtonGroup.addButton(self.merge_button)
self.mergeRadioButtonGroup.addButton(self.overwrite_button)
self.merge_button.setChecked(True)
self.merge_strategy = self.merge_button.text()
# table
self.tableWidget = QtWidgets.QTableWidget()
tableWidget = self.tableWidget
tableWidget.setColumnCount(ncol)
tableWidget.setRowCount(len(self.events))
tableWidget.setHorizontalHeaderLabels(('', 'Event ID', 'Filename', 'Last modified'))
tableWidget.setEditTriggers(tableWidget.NoEditTriggers)
tableWidget.setSortingEnabled(True)
header = tableWidget.horizontalHeader()
header.setSectionResizeMode(QtWidgets.QHeaderView.ResizeToContents)
header.setStretchLastSection(True)
self.statusText = QtWidgets.QLabel()
self.header_layout.addWidget(self.textLabel)
self.header_layout.addWidget(self.comboBox)
self.header_layout.addWidget(self.searchButton)
self.footer_layout.addWidget(self.checkAllButton)
self.footer_layout.addWidget(self.statusText)
self.footer_layout.addWidget(self.merge_button)
self.footer_layout.addWidget(self.overwrite_button)
self.footer_layout.setStretch(0, 0)
self.footer_layout.setStretch(1, 1)
self.main_layout.addLayout(self.header_layout)
self.main_layout.addWidget(self.tableWidget)
self.main_layout.addLayout(self.footer_layout)
self.main_layout.addWidget(self._buttonbox)
def showPaths(self):
self.filepaths = []
fext = self.comboBox.currentText()
self.tableWidget.clearContents()
for index, event in enumerate(self.events):
filename = get_pylot_eventfile_with_extension(event, fext)
pf_selected_item = QtWidgets.QTableWidgetItem()
check_state = QtCore.Qt.Checked if filename else QtCore.Qt.Unchecked
pf_selected_item.setCheckState(check_state)
self.tableWidget.setItem(index, 0, pf_selected_item)
self.tableWidget.setItem(index, 1, QtWidgets.QTableWidgetItem(f'{event.pylot_id}'))
if filename:
self.filepaths.append(filename)
ts = int(os.path.getmtime(filename))
# create QTableWidgetItems of filepath and last modification time
fname_item = QtWidgets.QTableWidgetItem(f'{os.path.split(filename)[-1]}')
fname_item.setData(3, filename)
ts_item = QtWidgets.QTableWidgetItem(f'{datetime.datetime.fromtimestamp(ts)}')
self.tableWidget.setItem(index, 2, fname_item)
self.tableWidget.setItem(index, 3, ts_item)
self.update_status()
def refreshSelectionBox(self):
fext = self.comboBox.currentText()
self.file_extensions = [fext]
for event in self.events:
extensions = get_possible_pylot_eventfile_extensions(event, '*.xml')
for ext in extensions:
if not ext in self.file_extensions:
self.file_extensions.append(ext)
self.comboBox.clear()
for ext in sorted(self.file_extensions):
self.comboBox.addItem(ext)
def setButtons(self):
self._buttonbox = QDialogButtonBox(QDialogButtonBox.Ok |
QDialogButtonBox.Cancel)
def toggleCheckAll(self):
self.check_all_state = not self.check_all_state
self.checkAll(self.check_all_state)
def checkAll(self, state):
state = QtCore.Qt.Checked if state else QtCore.Qt.Unchecked
for row_ind in range(self.tableWidget.rowCount()):
item = self.tableWidget.item(row_ind, 0)
item.setCheckState(state)
def getChecked(self):
filepaths = []
for row_ind in range(self.tableWidget.rowCount()):
item_check = self.tableWidget.item(row_ind, 0)
if item_check.checkState() == QtCore.Qt.Checked:
item_fname = self.tableWidget.item(row_ind, 2)
if item_fname:
filepath = item_fname.data(3)
filepaths.append(filepath)
return filepaths
def update_status(self, row=None, col=None):
if col is not None and col != 0:
return
filepaths = self.getChecked()
if len(filepaths) > 0:
status_text = f"Found {len(filepaths)} eventfile{'s' if len(filepaths) > 1 else ''}. Do you want to load them?"
else:
status_text = 'Did not find/select any files for specified file mask.'
self.statusText.setText(status_text)
def update_merge_strategy(self):
for button in (self.merge_button, self.overwrite_button):
if button.isChecked():
self.merge_strategy = button.text()
def connectSignals(self):
self._buttonbox.accepted.connect(self.accept)
self._buttonbox.rejected.connect(self.reject)
self.comboBox.editTextChanged.connect(self.showPaths)
self.searchButton.clicked.connect(self.showPaths)
self.checkAllButton.clicked.connect(self.toggleCheckAll)
self.checkAllButton.clicked.connect(self.update_status)
self.tableWidget.cellClicked.connect(self.update_status)
self.merge_button.clicked.connect(self.update_merge_strategy)
self.overwrite_button.clicked.connect(self.update_merge_strategy)
class SingleTextLineDialog(QtWidgets.QDialog):
def __init__(self, parent=None, label='Text: ', default_text='.xml'):
super(SingleTextLineDialog, self).__init__(parent)
@ -1868,8 +1659,8 @@ class PhaseDefaults(QtWidgets.QDialog):
class PickDlg(QDialog):
update_picks = QtCore.Signal(dict)
def __init__(self, parent=None, data=None, data_compare=None, station=None, network=None, location=None, picks=None,
autopicks=None, rotate=False, parameter=None, embedded=False, metadata=None, show_comp_data=False,
def __init__(self, parent=None, data=None, station=None, network=None, location=None, picks=None,
autopicks=None, rotate=False, parameter=None, embedded=False, metadata=None,
event=None, filteroptions=None, model=None, wftype=None):
super(PickDlg, self).__init__(parent, Qt.Window)
self.orig_parent = parent
@ -1878,7 +1669,6 @@ class PickDlg(QDialog):
# initialize attributes
self.parameter = parameter
self._embedded = embedded
self.showCompData = show_comp_data
self.station = station
self.network = network
self.location = location
@ -1917,32 +1707,11 @@ class PickDlg(QDialog):
else:
self.filteroptions = FILTERDEFAULTS
self.pick_block = False
# set attribute holding data
if data is None or not data:
try:
data = parent.get_data().getWFData().copy()
self.data = data.select(station=station)
except AttributeError as e:
errmsg = 'You either have to put in a data or an appropriate ' \
'parent (PyLoT MainWindow) object: {0}'.format(e)
raise Exception(errmsg)
else:
self.data = data
self.data_compare = data_compare
self.nextStation = QtWidgets.QCheckBox('Continue with next station ')
# comparison channel
self.referenceChannel = QtWidgets.QComboBox()
self.referenceChannel.activated.connect(self.resetPlot)
# comparison channel
self.compareCB = QtWidgets.QCheckBox()
self.compareCB.setChecked(self.showCompData)
self.compareCB.clicked.connect(self.switchCompData)
self.compareCB.clicked.connect(self.resetPlot)
self.compareCB.setVisible(bool(self.data_compare))
self.compareChannel = QtWidgets.QComboBox()
self.compareChannel.activated.connect(self.resetPlot)
# scale channel
self.scaleChannel = QtWidgets.QComboBox()
@ -1955,6 +1724,18 @@ class PickDlg(QDialog):
self.cur_xlim = None
self.cur_ylim = None
# set attribute holding data
if data is None or not data:
try:
data = parent.get_data().getWFData().copy()
self.data = data.select(station=station)
except AttributeError as e:
errmsg = 'You either have to put in a data or an appropriate ' \
'parent (PyLoT MainWindow) object: {0}'.format(e)
raise Exception(errmsg)
else:
self.data = data
self.stime, self.etime = full_range(self.getWFData())
# initialize plotting widget
@ -1966,12 +1747,12 @@ class PickDlg(QDialog):
self.setupUi()
# fill compare and scale channels
self.referenceChannel.addItem('-', None)
self.compareChannel.addItem('-', None)
self.scaleChannel.addItem('individual', None)
for trace in self.getWFData():
channel = trace.stats.channel
self.referenceChannel.addItem(channel, trace)
self.compareChannel.addItem(channel, trace)
if not channel[-1] in ['Z', 'N', 'E', '1', '2', '3']:
print('Skipping unknown channel for scaling: {}'.format(channel))
continue
@ -1988,7 +1769,7 @@ class PickDlg(QDialog):
if self.wftype is not None:
title += ' | ({})'.format(self.wftype)
self.multicompfig.plotWFData(wfdata=self.getWFData(), wfdata_compare=self.getWFDataComp(),
self.multicompfig.plotWFData(wfdata=self.getWFData(),
title=title)
self.multicompfig.setZoomBorders2content()
@ -2005,7 +1786,7 @@ class PickDlg(QDialog):
# init expected picks using obspy Taup
try:
if self.metadata and model != "None":
if self.metadata and model is not None:
self.model = TauPyModel(model)
self.get_arrivals()
self.drawArrivals()
@ -2164,11 +1945,8 @@ class PickDlg(QDialog):
_dialtoolbar.addWidget(est_label)
_dialtoolbar.addWidget(self.plot_arrivals_button)
_dialtoolbar.addSeparator()
_dialtoolbar.addWidget(QtWidgets.QLabel('Plot reference channel: '))
_dialtoolbar.addWidget(self.referenceChannel)
_dialtoolbar.addSeparator()
_dialtoolbar.addWidget(QtWidgets.QLabel('Compare: '))
_dialtoolbar.addWidget(self.compareCB)
_dialtoolbar.addWidget(QtWidgets.QLabel('Compare to channel: '))
_dialtoolbar.addWidget(self.compareChannel)
_dialtoolbar.addSeparator()
_dialtoolbar.addWidget(QtWidgets.QLabel('Scaling: '))
_dialtoolbar.addWidget(self.scaleChannel)
@ -2253,12 +2031,10 @@ class PickDlg(QDialog):
station_id = trace.get_id()
starttime = trace.stats.starttime
station_coords = self.metadata.get_coordinates(station_id, starttime)
if not station_coords:
print('get_arrivals: No station coordinates found. Return!')
return
origins = self.pylot_event.origins
if phases == ['None', 'None']:
print("get_arrivals: Creation info (manual or auto) not available! Return!")
print("get_arrivals: Creation info (manual or auto) not available!")
print("Return!")
return
if origins:
source_origin = origins[0]
@ -2370,7 +2146,7 @@ class PickDlg(QDialog):
# create action and add to menu
# phase name transferred using lambda function
slot = lambda ph=phase, phID=phaseID: phaseSelect[phID](ph)
slot = lambda phase=phase, phaseID=phaseID: phaseSelect[phaseID](phase)
picksAction = createAction(parent=self, text=phase,
slot=slot,
shortcut=shortcut)
@ -2505,7 +2281,7 @@ class PickDlg(QDialog):
def activatePicking(self):
self.leave_rename_phase()
self.renamePhaseAction.setEnabled(False)
self.referenceChannel.setEnabled(False)
self.compareChannel.setEnabled(False)
self.scaleChannel.setEnabled(False)
phase = self.currentPhase
phaseID = self.getPhaseID(phase)
@ -2537,7 +2313,7 @@ class PickDlg(QDialog):
self.disconnectPressEvent()
self.multicompfig.connectEvents()
self.renamePhaseAction.setEnabled(True)
self.referenceChannel.setEnabled(True)
self.compareChannel.setEnabled(True)
self.scaleChannel.setEnabled(True)
self.connect_pick_delete()
self.draw()
@ -2610,12 +2386,6 @@ class PickDlg(QDialog):
def getWFData(self):
return self.data
def getWFDataComp(self):
if self.showCompData:
return self.data_compare
else:
return Stream()
def selectWFData(self, channel):
component = channel[-1].upper()
wfdata = Stream()
@ -2737,33 +2507,24 @@ class PickDlg(QDialog):
stime = self.getStartTime()
# copy wfdata for plotting
wfdata = self.getWFData().copy()
wfdata_comp = self.getWFDataComp().copy()
wfdata = self.getPickPhases(wfdata, phase)
wfdata_comp = self.getPickPhases(wfdata_comp, phase)
for wfd in [wfdata, wfdata_comp]:
if wfd:
wfd.normalize()
if not wfdata:
# copy data for plotting
data = self.getWFData().copy()
data = self.getPickPhases(data, phase)
data.normalize()
if not data:
QtWidgets.QMessageBox.warning(self, 'No channel to plot',
'No channel to plot for phase: {}. '
'Make sure to select the correct channels for P and S '
'in the menu in the top panel.'.format(phase))
'No channel to plot for phase: {}.'.format(phase))
self.leave_picking_mode()
return
# filter wfdata and trace on which is picked prior to determination of SNR
# filter data and trace on which is picked prior to determination of SNR
filterphase = self.currentFilterPhase()
if filterphase:
filteroptions = self.getFilterOptions(filterphase).parseFilterOptions()
try:
for wfd in [wfdata, wfdata_comp]:
if wfd:
wfd.detrend('linear')
wfd.filter(**filteroptions)
# wfdata.filter(**filteroptions)# MP MP removed filtering of original wfdata
data.detrend('linear')
data.filter(**filteroptions)
# wfdata.filter(**filteroptions)# MP MP removed filtering of original data
except ValueError as e:
self.qmb = QtWidgets.QMessageBox(QtWidgets.QMessageBox.Icon.Information,
'Denied',
@ -2773,8 +2534,8 @@ class PickDlg(QDialog):
snr = []
noiselevels = {}
# determine SNR and noiselevel
for trace in wfdata.traces:
st = wfdata.select(channel=trace.stats.channel)
for trace in data.traces:
st = data.select(channel=trace.stats.channel)
stime_diff = trace.stats.starttime - stime
result = getSNR(st, (noise_win, gap_win, signal_win), ini_pick - stime_diff)
snr.append(result[0])
@ -2785,25 +2546,23 @@ class PickDlg(QDialog):
noiselevel = nfac
noiselevels[trace.stats.channel] = noiselevel
# prepare plotting of wfdata
for wfd in [wfdata, wfdata_comp]:
if wfd:
for trace in wfd:
t = prep_time_axis(trace.stats.starttime - stime, trace)
inoise = getnoisewin(t, ini_pick, noise_win, gap_win)
trace = demeanTrace(trace, inoise)
# upscale trace wfdata in a way that each trace is vertically zoomed to noiselevel*factor
channel = trace.stats.channel
noiselevel = noiselevels[channel]
noiseScaleFactor = self.calcNoiseScaleFactor(noiselevel, zoomfactor=5.)
trace.data *= noiseScaleFactor
noiselevels[channel] *= noiseScaleFactor
# prepare plotting of data
for trace in data:
t = prep_time_axis(trace.stats.starttime - stime, trace)
inoise = getnoisewin(t, ini_pick, noise_win, gap_win)
trace = demeanTrace(trace, inoise)
# upscale trace data in a way that each trace is vertically zoomed to noiselevel*factor
channel = trace.stats.channel
noiselevel = noiselevels[channel]
noiseScaleFactor = self.calcNoiseScaleFactor(noiselevel, zoomfactor=5.)
trace.data *= noiseScaleFactor
noiselevels[channel] *= noiseScaleFactor
mean_snr = np.mean(snr)
x_res = getResolutionWindow(mean_snr, parameter.get('extent'))
xlims = [ini_pick - x_res, ini_pick + x_res]
ylims = list(np.array([-.5, .5]) + [0, len(wfdata) - 1])
ylims = list(np.array([-.5, .5]) + [0, len(data) - 1])
title = self.getStation() + ' picking mode'
title += ' | SNR: {}'.format(mean_snr)
@ -2811,10 +2570,9 @@ class PickDlg(QDialog):
filtops_str = transformFilteroptions2String(filteroptions)
title += ' | Filteroptions: {}'.format(filtops_str)
plot_additional = bool(self.referenceChannel.currentText())
additional_channel = self.referenceChannel.currentText()
self.multicompfig.plotWFData(wfdata=wfdata,
wfdata_compare=wfdata_comp,
plot_additional = bool(self.compareChannel.currentText())
additional_channel = self.compareChannel.currentText()
self.multicompfig.plotWFData(wfdata=data,
title=title,
zoomx=xlims,
zoomy=ylims,
@ -2895,7 +2653,7 @@ class PickDlg(QDialog):
minFMSNR = parameter.get('minFMSNR')
quality = get_quality_class(spe, parameter.get('timeerrorsP'))
if quality <= minFMweight and snr >= minFMSNR:
FM = fmpicker(self.getWFData().select(channel=channel).copy(), wfdata.copy(), parameter.get('fmpickwin'),
FM = fmpicker(self.getWFData().select(channel=channel), wfdata, parameter.get('fmpickwin'),
pick - stime_diff)
# save pick times for actual phase
@ -3205,8 +2963,7 @@ class PickDlg(QDialog):
self.cur_xlim = self.multicompfig.axes[0].get_xlim()
self.cur_ylim = self.multicompfig.axes[0].get_ylim()
# self.multicompfig.updateCurrentLimits()
wfdata = self.getWFData().copy()
wfdata_comp = self.getWFDataComp().copy()
data = self.getWFData().copy()
title = self.getStation()
if filter:
filtoptions = None
@ -3214,22 +2971,19 @@ class PickDlg(QDialog):
filtoptions = self.getFilterOptions(self.getPhaseID(phase), gui_filter=True).parseFilterOptions()
if filtoptions is not None:
for wfd in [wfdata, wfdata_comp]:
if wfd:
wfd.detrend('linear')
wfd.taper(0.02, type='cosine')
wfd.filter(**filtoptions)
data.detrend('linear')
data.taper(0.02, type='cosine')
data.filter(**filtoptions)
filtops_str = transformFilteroptions2String(filtoptions)
title += ' | Filteroptions: {}'.format(filtops_str)
if self.wftype is not None:
title += ' | ({})'.format(self.wftype)
plot_additional = bool(self.referenceChannel.currentText())
additional_channel = self.referenceChannel.currentText()
plot_additional = bool(self.compareChannel.currentText())
additional_channel = self.compareChannel.currentText()
scale_channel = self.scaleChannel.currentText()
self.multicompfig.plotWFData(wfdata=wfdata, wfdata_compare=wfdata_comp,
title=title,
self.multicompfig.plotWFData(wfdata=data, title=title,
zoomx=self.getXLims(),
zoomy=self.getYLims(),
plot_additional=plot_additional,
@ -3302,9 +3056,6 @@ class PickDlg(QDialog):
self.resetZoom()
self.refreshPlot()
def switchCompData(self):
self.showCompData = self.compareCB.isChecked()
def refreshPlot(self):
if self.autoFilterAction.isChecked():
self.filterActionP.setChecked(False)
@ -3815,9 +3566,8 @@ class TuneAutopicker(QWidget):
# wfdat = remove_underscores(wfdat)
# rotate misaligned stations to ZNE
# check for gaps and doubled channels
wfdat, _ = check_for_gaps_and_merge(wfdat)
# check for nans
check_for_nan(wfdat)
wfdat, gaps = merge_stream(wfdat)
# check4gaps(wfdat)
check4doubled(wfdat)
wfdat = check4rotated(wfdat, self.parent().metadata, verbosity=0)
# trim station components to same start value
@ -3861,14 +3611,14 @@ class TuneAutopicker(QWidget):
self.listWidget.scrollToBottom()
def get_current_event(self):
path = self.get_current_event_fp()
path = self.eventBox.currentText()
return self.parent().project.getEventFromPath(path)
def get_current_event_name(self):
return self.eventBox.currentText().split('/')[-1].rstrip('*')
return self.eventBox.currentText().split('/')[-1].split('*')[0]
def get_current_event_fp(self):
return self.eventBox.currentText().rstrip('*')
return self.eventBox.currentText().split('*')[0]
def get_current_event_picks(self, station):
event = self.get_current_event()
@ -3916,13 +3666,11 @@ class TuneAutopicker(QWidget):
location = None
wfdata = self.data.getWFData()
wfdata_comp = self.data.getWFDataComp()
metadata = self.parent().metadata
event = self.get_current_event()
filteroptions = self.parent().filteroptions
wftype = self.wftype if self.obspy_dmt else ''
self.pickDlg = PickDlg(self.parent(), data=wfdata.select(station=station).copy(),
data_comp=wfdata_comp.select(station=station).copy(),
station=station, network=network,
location=location, parameter=self.parameter,
picks=self.get_current_event_picks(station),
@ -3974,7 +3722,6 @@ class TuneAutopicker(QWidget):
st = self.data.getWFData()
tr = st.select(station=self.get_current_station())[0]
starttime = tr.stats.starttime
# create two lists with figure names and subindices (for subplots) to get the correct axes
p_axes = [
('mainFig', 0),
('aicFig', 0),
@ -4007,7 +3754,7 @@ class TuneAutopicker(QWidget):
self.plot_manual_pick_to_ax(ax=ax, picks=picks, phase='S',
starttime=starttime, quality=qualitySpick)
for canvas in self.parent().canvas_dict.values():
canvas.draw_idle()
canvas.draw()
def plot_manual_pick_to_ax(self, ax, picks, phase, starttime, quality):
mpp = picks[phase]['mpp'] - starttime
@ -5919,7 +5666,7 @@ class ChooseWaveFormWindow(QWidget):
#self.currentSpectro = self.traces[
# self.chooseBoxTraces.currentText()[3:]][self.chooseBoxComponent.currentText()].spectrogram(show=False, title=t)
#self.currentSpectro.show()
self.applyFFT()
applyFFT()
def applyFFT(self, trace):
tra = self.traces[self.chooseBoxTraces.currentText()[3:]]['Z']