Compare commits
43 Commits
b3fdbc811e
...
6cce05b035
Author | SHA1 | Date | |
---|---|---|---|
6cce05b035 | |||
7326f061e5 | |||
1a18401fe3 | |||
ec930dbc12 | |||
b991f771af | |||
2c3b1876ab | |||
0acd23d4d0 | |||
f349c8bc7e | |||
6688ef845d | |||
5b18e9ab71 | |||
c79e886d77 | |||
76f2d5d972 | |||
2d08fd029d | |||
8f22d438d3 | |||
93b7de3baa | |||
05642e775b | |||
47205ca493 | |||
5c7f0b56eb | |||
c574031931 | |||
e1a0fde619 | |||
48d196df11 | |||
6cc9cb4a96 | |||
5eab686445 | |||
b12e7937ac | |||
78f2dbcab2 | |||
8b95c7a0fe | |||
65dbaad446 | |||
5b97d51517 | |||
f03ace75e7 | |||
9c78471d20 | |||
09d2fb1022 | |||
3cae6d3a78 | |||
2e85d083a3 | |||
ba4e6cfe50 | |||
1f16d01648 | |||
3069e7d526 | |||
a9aeb7aaa3 | |||
b9adb182ad | |||
a823eb2440 | |||
486e3dc9c3 | |||
8d356050d7 | |||
43cab3767f | |||
0634d24814 |
157
PyLoT.py
157
PyLoT.py
@ -25,6 +25,7 @@ https://www.iconfinder.com/iconsets/flavour
|
|||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
import json
|
import json
|
||||||
|
import logging
|
||||||
import os
|
import os
|
||||||
import platform
|
import platform
|
||||||
import shutil
|
import shutil
|
||||||
@ -60,7 +61,7 @@ except ImportError:
|
|||||||
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
|
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
|
||||||
from matplotlib.figure import Figure
|
from matplotlib.figure import Figure
|
||||||
|
|
||||||
from pylot.core.analysis.magnitude import LocalMagnitude, MomentMagnitude, calcsourcespec
|
from pylot.core.analysis.magnitude import LocalMagnitude, MomentMagnitude
|
||||||
from pylot.core.io.data import Data
|
from pylot.core.io.data import Data
|
||||||
from pylot.core.io.inputs import FilterOptions, PylotParameter
|
from pylot.core.io.inputs import FilterOptions, PylotParameter
|
||||||
from autoPyLoT import autoPyLoT
|
from autoPyLoT import autoPyLoT
|
||||||
@ -84,7 +85,7 @@ from pylot.core.util.widgets import FilterOptionsDialog, NewEventDlg, \
|
|||||||
PylotCanvas, WaveformWidgetPG, PropertiesDlg, HelpForm, createAction, PickDlg, \
|
PylotCanvas, WaveformWidgetPG, PropertiesDlg, HelpForm, createAction, PickDlg, \
|
||||||
ComparisonWidget, TuneAutopicker, PylotParaBox, AutoPickDlg, CanvasWidget, AutoPickWidget, \
|
ComparisonWidget, TuneAutopicker, PylotParaBox, AutoPickDlg, CanvasWidget, AutoPickWidget, \
|
||||||
CompareEventsWidget, ProgressBarWidget, AddMetadataWidget, SingleTextLineDialog, LogWidget, PickQualitiesFromXml, \
|
CompareEventsWidget, ProgressBarWidget, AddMetadataWidget, SingleTextLineDialog, LogWidget, PickQualitiesFromXml, \
|
||||||
SourceSpecWindow, ChooseWaveFormWindow, SpectrogramTab
|
SpectrogramTab, SearchFileByExtensionDialog
|
||||||
from pylot.core.util.array_map import Array_map
|
from pylot.core.util.array_map import Array_map
|
||||||
from pylot.core.util.structure import DATASTRUCTURE
|
from pylot.core.util.structure import DATASTRUCTURE
|
||||||
from pylot.core.util.thread import Thread, Worker
|
from pylot.core.util.thread import Thread, Worker
|
||||||
@ -113,11 +114,7 @@ class MainWindow(QMainWindow):
|
|||||||
def __init__(self, parent=None, infile=None, reset_qsettings=False):
|
def __init__(self, parent=None, infile=None, reset_qsettings=False):
|
||||||
super(MainWindow, self).__init__(parent)
|
super(MainWindow, self).__init__(parent)
|
||||||
|
|
||||||
# check for default pylot.in-file
|
if infile and os.path.isfile(infile) is False:
|
||||||
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]
|
infile = QFileDialog().getOpenFileName(caption='Choose PyLoT-input file')[0]
|
||||||
|
|
||||||
if not os.path.exists(infile):
|
if not os.path.exists(infile):
|
||||||
@ -181,6 +178,7 @@ class MainWindow(QMainWindow):
|
|||||||
self.autodata = Data(self)
|
self.autodata = Data(self)
|
||||||
|
|
||||||
self.fnames = None
|
self.fnames = None
|
||||||
|
self.fnames_comp = None
|
||||||
self._stime = None
|
self._stime = None
|
||||||
|
|
||||||
# track deleted picks for logging
|
# track deleted picks for logging
|
||||||
@ -253,7 +251,7 @@ class MainWindow(QMainWindow):
|
|||||||
self._inputs.reset_defaults()
|
self._inputs.reset_defaults()
|
||||||
# check for default pylot.in-file
|
# check for default pylot.in-file
|
||||||
infile = os.path.join(pylot_config_dir, '.pylot.in')
|
infile = os.path.join(pylot_config_dir, '.pylot.in')
|
||||||
print('Using default input file {}'.format(infile))
|
logging.warning('Using default input file {}'.format(infile))
|
||||||
self._inputs.export2File(infile)
|
self._inputs.export2File(infile)
|
||||||
self.infile = infile
|
self.infile = infile
|
||||||
|
|
||||||
@ -508,6 +506,8 @@ class MainWindow(QMainWindow):
|
|||||||
logAction = self.createAction(self, "&Show Log", self.showLogWidget,
|
logAction = self.createAction(self, "&Show Log", self.showLogWidget,
|
||||||
tip="""Display Log""")
|
tip="""Display Log""")
|
||||||
|
|
||||||
|
logAction.setEnabled(use_logwidget)
|
||||||
|
|
||||||
# create button group for component selection
|
# create button group for component selection
|
||||||
|
|
||||||
componentGroup = QActionGroup(self)
|
componentGroup = QActionGroup(self)
|
||||||
@ -1004,18 +1004,17 @@ class MainWindow(QMainWindow):
|
|||||||
return
|
return
|
||||||
refresh = False
|
refresh = False
|
||||||
events = self.project.eventlist
|
events = self.project.eventlist
|
||||||
sld = SingleTextLineDialog(label='Specify file extension: ', default_text='.xml')
|
sld = SearchFileByExtensionDialog(label='Specify file extension: ', default_text='.xml',
|
||||||
|
events=events)
|
||||||
if not sld.exec_():
|
if not sld.exec_():
|
||||||
return
|
return
|
||||||
fext = sld.lineEdit.text()
|
|
||||||
# fext = '.xml'
|
filenames = sld.getChecked()
|
||||||
for event in events:
|
for event in events:
|
||||||
path = event.path
|
for filename in filenames:
|
||||||
eventname = path.split('/')[-1] # or event.pylot_id
|
if os.path.isfile(filename) and event.pylot_id in filename:
|
||||||
filename = os.path.join(path, 'PyLoT_' + eventname + fext)
|
self.load_data(filename, draw=False, event=event, ask_user=True, merge_strategy=sld.merge_strategy)
|
||||||
if os.path.isfile(filename):
|
refresh = True
|
||||||
self.load_data(filename, draw=False, event=event, overwrite=True)
|
|
||||||
refresh = True
|
|
||||||
if not refresh:
|
if not refresh:
|
||||||
return
|
return
|
||||||
if self.get_current_event().pylot_picks:
|
if self.get_current_event().pylot_picks:
|
||||||
@ -1023,8 +1022,8 @@ class MainWindow(QMainWindow):
|
|||||||
self.fill_eventbox()
|
self.fill_eventbox()
|
||||||
self.setDirty(True)
|
self.setDirty(True)
|
||||||
|
|
||||||
def load_data(self, fname=None, loc=False, draw=True, event=None, overwrite=False):
|
def load_data(self, fname=None, loc=False, draw=True, event=None, ask_user=False, merge_strategy='Overwrite'):
|
||||||
if not overwrite:
|
if not ask_user:
|
||||||
if not self.okToContinue():
|
if not self.okToContinue():
|
||||||
return
|
return
|
||||||
if fname is None:
|
if fname is None:
|
||||||
@ -1038,9 +1037,12 @@ class MainWindow(QMainWindow):
|
|||||||
data = Data(self, event)
|
data = Data(self, event)
|
||||||
try:
|
try:
|
||||||
data_new = Data(self, evtdata=str(fname))
|
data_new = Data(self, evtdata=str(fname))
|
||||||
# MP MP commented because adding several picks might cause inconsistencies
|
if merge_strategy == 'Overwrite':
|
||||||
data = data_new
|
data = data_new
|
||||||
# data += data_new
|
elif merge_strategy == 'Merge':
|
||||||
|
data += data_new
|
||||||
|
else:
|
||||||
|
raise NotImplementedError(f'Unknown merge strategy: {merge_strategy}')
|
||||||
except ValueError:
|
except ValueError:
|
||||||
qmb = QMessageBox(self, icon=QMessageBox.Question,
|
qmb = QMessageBox(self, icon=QMessageBox.Question,
|
||||||
text='Warning: Missmatch in event identifiers {} and {}. Continue?'.format(
|
text='Warning: Missmatch in event identifiers {} and {}. Continue?'.format(
|
||||||
@ -1128,16 +1130,19 @@ class MainWindow(QMainWindow):
|
|||||||
else:
|
else:
|
||||||
return
|
return
|
||||||
|
|
||||||
def getWFFnames_from_eventbox(self, eventbox=None):
|
def getWFFnames_from_eventbox(self, eventbox: str = None, subpath: str = None) -> list:
|
||||||
'''
|
'''
|
||||||
Return waveform filenames from event in eventbox.
|
Return waveform filenames from event in eventbox.
|
||||||
'''
|
'''
|
||||||
# TODO: add dataStructure class for obspyDMT here, this is just a workaround!
|
# TODO: add dataStructure class for obspyDMT here, this is just a workaround!
|
||||||
eventpath = self.get_current_event_path(eventbox)
|
eventpath = self.get_current_event_path(eventbox)
|
||||||
basepath = eventpath.split(os.path.basename(eventpath))[0]
|
if subpath:
|
||||||
|
eventpath = os.path.join(eventpath, subpath)
|
||||||
|
if not os.path.isdir(eventpath):
|
||||||
|
return []
|
||||||
if self.dataStructure:
|
if self.dataStructure:
|
||||||
if not eventpath:
|
if not eventpath:
|
||||||
return
|
return []
|
||||||
fnames = [os.path.join(eventpath, f) for f in os.listdir(eventpath)]
|
fnames = [os.path.join(eventpath, f) for f in os.listdir(eventpath)]
|
||||||
else:
|
else:
|
||||||
raise DatastructureError('not specified')
|
raise DatastructureError('not specified')
|
||||||
@ -1382,7 +1387,7 @@ class MainWindow(QMainWindow):
|
|||||||
index = eventBox.currentIndex()
|
index = eventBox.currentIndex()
|
||||||
tv = QtWidgets.QTableView()
|
tv = QtWidgets.QTableView()
|
||||||
header = tv.horizontalHeader()
|
header = tv.horizontalHeader()
|
||||||
header.setResizeMode(QtWidgets.QHeaderView.ResizeToContents)
|
header.setSectionResizeMode(QtWidgets.QHeaderView.ResizeToContents)
|
||||||
header.setStretchLastSection(True)
|
header.setStretchLastSection(True)
|
||||||
header.hide()
|
header.hide()
|
||||||
tv.verticalHeader().hide()
|
tv.verticalHeader().hide()
|
||||||
@ -1402,25 +1407,28 @@ class MainWindow(QMainWindow):
|
|||||||
|
|
||||||
for id, event in enumerate(self.project.eventlist):
|
for id, event in enumerate(self.project.eventlist):
|
||||||
event_path = event.path
|
event_path = event.path
|
||||||
phaseErrors = {'P': self._inputs['timeerrorsP'],
|
#phaseErrors = {'P': self._inputs['timeerrorsP'],
|
||||||
'S': self._inputs['timeerrorsS']}
|
# 'S': self._inputs['timeerrorsS']}
|
||||||
|
|
||||||
ma_props = {'manual': event.pylot_picks,
|
man_au_picks = {'manual': event.pylot_picks,
|
||||||
'auto': event.pylot_autopicks}
|
'auto': event.pylot_autopicks}
|
||||||
ma_count = {'manual': 0,
|
npicks = {'manual': {'P': 0, 'S': 0},
|
||||||
'auto': 0}
|
'auto': {'P': 0, 'S': 0}}
|
||||||
ma_count_total = {'manual': 0,
|
npicks_total = {'manual': {'P': 0, 'S': 0},
|
||||||
'auto': 0}
|
'auto': {'P': 0, 'S': 0}}
|
||||||
|
|
||||||
for ma in ma_props.keys():
|
for ma in man_au_picks.keys():
|
||||||
if ma_props[ma]:
|
if man_au_picks[ma]:
|
||||||
for picks in ma_props[ma].values():
|
for picks in man_au_picks[ma].values():
|
||||||
for phasename, pick in picks.items():
|
for phasename, pick in picks.items():
|
||||||
if not type(pick) in [dict, AttribDict]:
|
if not type(pick) in [dict, AttribDict]:
|
||||||
continue
|
continue
|
||||||
|
phase_ID = identifyPhaseID(phasename)
|
||||||
|
if not phase_ID in npicks[ma].keys():
|
||||||
|
continue
|
||||||
if pick.get('spe'):
|
if pick.get('spe'):
|
||||||
ma_count[ma] += 1
|
npicks[ma][phase_ID] += 1
|
||||||
ma_count_total[ma] += 1
|
npicks_total[ma][phase_ID] += 1
|
||||||
|
|
||||||
event_ref = event.isRefEvent()
|
event_ref = event.isRefEvent()
|
||||||
event_test = event.isTestEvent()
|
event_test = event.isTestEvent()
|
||||||
@ -1455,16 +1463,23 @@ class MainWindow(QMainWindow):
|
|||||||
if event.dirty:
|
if event.dirty:
|
||||||
event_str += '*'
|
event_str += '*'
|
||||||
item_path = QStandardItem(event_str)
|
item_path = QStandardItem(event_str)
|
||||||
item_time = QStandardItem('{}'.format(time))
|
item_time = QStandardItem('{}'.format(time.strftime("%Y-%m-%d %H:%M:%S") if time else ''))
|
||||||
item_lat = QStandardItem('{}'.format(lat))
|
item_lat = QStandardItem('{}'.format(lat))
|
||||||
item_lon = QStandardItem('{}'.format(lon))
|
item_lon = QStandardItem('{}'.format(lon))
|
||||||
item_depth = QStandardItem('{}'.format(depth))
|
item_depth = QStandardItem('{}'.format(depth))
|
||||||
item_localmag = QStandardItem('{}'.format(localmag))
|
item_localmag = QStandardItem('{}'.format(localmag))
|
||||||
item_momentmag = QStandardItem('{}'.format(momentmag))
|
item_momentmag = QStandardItem('{}'.format(momentmag))
|
||||||
item_nmp = QStandardItem('{}({})'.format(ma_count['manual'], ma_count_total['manual']))
|
|
||||||
|
item_nmp = QStandardItem()
|
||||||
|
item_nap = QStandardItem()
|
||||||
item_nmp.setIcon(self.manupicksicon_small)
|
item_nmp.setIcon(self.manupicksicon_small)
|
||||||
item_nap = QStandardItem('{}({})'.format(ma_count['auto'], ma_count_total['auto']))
|
|
||||||
item_nap.setIcon(self.autopicksicon_small)
|
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_ref = QStandardItem() # str(event_ref))
|
||||||
item_test = QStandardItem() # str(event_test))
|
item_test = QStandardItem() # str(event_test))
|
||||||
if event_ref:
|
if event_ref:
|
||||||
@ -1885,6 +1900,7 @@ class MainWindow(QMainWindow):
|
|||||||
# which will read in data input twice. Therefore current tab is changed to 0
|
# which will read in data input twice. Therefore current tab is changed to 0
|
||||||
# in loadProject before calling this function.
|
# in loadProject before calling this function.
|
||||||
self.fill_eventbox()
|
self.fill_eventbox()
|
||||||
|
#print(f'{self.get_current_event()=}')
|
||||||
plotted = False
|
plotted = False
|
||||||
if self.tabs.currentIndex() == 2:
|
if self.tabs.currentIndex() == 2:
|
||||||
self.init_event_table()
|
self.init_event_table()
|
||||||
@ -1919,7 +1935,6 @@ class MainWindow(QMainWindow):
|
|||||||
self.spectro_layout.addWidget(newSpectroWidget)
|
self.spectro_layout.addWidget(newSpectroWidget)
|
||||||
self.spectroWidget = newSpectroWidget
|
self.spectroWidget = newSpectroWidget
|
||||||
|
|
||||||
|
|
||||||
def newWF(self, event=None, plot=True):
|
def newWF(self, event=None, plot=True):
|
||||||
'''
|
'''
|
||||||
Load new data and plot if necessary.
|
Load new data and plot if necessary.
|
||||||
@ -1949,13 +1964,20 @@ class MainWindow(QMainWindow):
|
|||||||
|
|
||||||
def prepareLoadWaveformData(self):
|
def prepareLoadWaveformData(self):
|
||||||
self.fnames = self.getWFFnames_from_eventbox()
|
self.fnames = self.getWFFnames_from_eventbox()
|
||||||
self.fnames_syn = []
|
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
|
||||||
|
|
||||||
eventpath = self.get_current_event_path()
|
eventpath = self.get_current_event_path()
|
||||||
basepath = eventpath.split(os.path.basename(eventpath))[0]
|
basepath = eventpath.split(os.path.basename(eventpath))[0]
|
||||||
self.obspy_dmt = check_obspydmt_structure(basepath)
|
self.obspy_dmt = check_obspydmt_structure(basepath)
|
||||||
self.dataPlot.activateObspyDMToptions(self.obspy_dmt)
|
self.dataPlot.activateObspyDMToptions(self.obspy_dmt)
|
||||||
if self.obspy_dmt:
|
if self.obspy_dmt:
|
||||||
self.prepareObspyDMT_data(eventpath)
|
self.prepareObspyDMT_data(eventpath)
|
||||||
|
self.dataPlot.activateCompareOptions(True)
|
||||||
|
|
||||||
def loadWaveformData(self):
|
def loadWaveformData(self):
|
||||||
'''
|
'''
|
||||||
@ -1970,6 +1992,8 @@ class MainWindow(QMainWindow):
|
|||||||
# ans = False
|
# ans = False
|
||||||
|
|
||||||
settings = QSettings()
|
settings = QSettings()
|
||||||
|
# process application events to wait for event items to appear in event box
|
||||||
|
QApplication.processEvents()
|
||||||
curr_event = self.get_current_event()
|
curr_event = self.get_current_event()
|
||||||
if not curr_event:
|
if not curr_event:
|
||||||
print('Could not find current event. Try reload?')
|
print('Could not find current event. Try reload?')
|
||||||
@ -1986,7 +2010,7 @@ class MainWindow(QMainWindow):
|
|||||||
tstop = None
|
tstop = None
|
||||||
|
|
||||||
self.data.setWFData(self.fnames,
|
self.data.setWFData(self.fnames,
|
||||||
self.fnames_syn,
|
self.fnames_comp,
|
||||||
checkRotated=True,
|
checkRotated=True,
|
||||||
metadata=self.metadata,
|
metadata=self.metadata,
|
||||||
tstart=tstart,
|
tstart=tstart,
|
||||||
@ -1994,7 +2018,7 @@ class MainWindow(QMainWindow):
|
|||||||
|
|
||||||
def prepareObspyDMT_data(self, eventpath):
|
def prepareObspyDMT_data(self, eventpath):
|
||||||
qcbox_processed = self.dataPlot.qcombo_processed
|
qcbox_processed = self.dataPlot.qcombo_processed
|
||||||
qcheckb_syn = self.dataPlot.syn_checkbox
|
qcheckb_syn = self.dataPlot.comp_checkbox
|
||||||
qcbox_processed.setEnabled(False)
|
qcbox_processed.setEnabled(False)
|
||||||
qcheckb_syn.setEnabled(False)
|
qcheckb_syn.setEnabled(False)
|
||||||
for fpath in os.listdir(eventpath):
|
for fpath in os.listdir(eventpath):
|
||||||
@ -2002,8 +2026,8 @@ class MainWindow(QMainWindow):
|
|||||||
if 'syngine' in fpath:
|
if 'syngine' in fpath:
|
||||||
eventpath_syn = os.path.join(eventpath, fpath)
|
eventpath_syn = os.path.join(eventpath, fpath)
|
||||||
qcheckb_syn.setEnabled(True)
|
qcheckb_syn.setEnabled(True)
|
||||||
if self.dataPlot.syn_checkbox.isChecked():
|
if self.dataPlot.comp_checkbox.isChecked():
|
||||||
self.fnames_syn = [os.path.join(eventpath_syn, filename) for filename in os.listdir(eventpath_syn)]
|
self.fnames_comp = [os.path.join(eventpath_syn, filename) for filename in os.listdir(eventpath_syn)]
|
||||||
if 'processed' in fpath:
|
if 'processed' in fpath:
|
||||||
qcbox_processed.setEnabled(True)
|
qcbox_processed.setEnabled(True)
|
||||||
if qcbox_processed.isEnabled():
|
if qcbox_processed.isEnabled():
|
||||||
@ -2125,7 +2149,7 @@ class MainWindow(QMainWindow):
|
|||||||
|
|
||||||
def finish_pg_plot(self):
|
def finish_pg_plot(self):
|
||||||
self.getPlotWidget().updateWidget()
|
self.getPlotWidget().updateWidget()
|
||||||
plots, gaps = self.wfp_thread.data
|
plots = self.wfp_thread.data
|
||||||
# do not show plot if no data are given
|
# do not show plot if no data are given
|
||||||
self.wf_scroll_area.setVisible(len(plots) > 0)
|
self.wf_scroll_area.setVisible(len(plots) > 0)
|
||||||
self.no_data_label.setVisible(not len(plots) > 0)
|
self.no_data_label.setVisible(not len(plots) > 0)
|
||||||
@ -2284,7 +2308,7 @@ class MainWindow(QMainWindow):
|
|||||||
comp = self.getComponent()
|
comp = self.getComponent()
|
||||||
title = 'section: {0} components'.format(zne_text[comp])
|
title = 'section: {0} components'.format(zne_text[comp])
|
||||||
wfst = self.get_data().getWFData()
|
wfst = self.get_data().getWFData()
|
||||||
wfsyn = self.get_data().getSynWFData()
|
wfsyn = self.get_data().getAltWFdata()
|
||||||
if self.filterActionP.isChecked() and filter:
|
if self.filterActionP.isChecked() and filter:
|
||||||
self.filterWaveformData(plot=False, phase='P')
|
self.filterWaveformData(plot=False, phase='P')
|
||||||
elif self.filterActionS.isChecked() and filter:
|
elif self.filterActionS.isChecked() and filter:
|
||||||
@ -2299,8 +2323,8 @@ class MainWindow(QMainWindow):
|
|||||||
self.plot_method = 'normal'
|
self.plot_method = 'normal'
|
||||||
rval = plotWidget.plotWFData(wfdata=wfst, wfsyn=wfsyn, title=title, mapping=False, component=comp,
|
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)
|
nth_sample=int(nth_sample), method=self.plot_method, gain=self.gain)
|
||||||
plots, gaps = rval if rval else ([], [])
|
plots = rval if rval else []
|
||||||
return plots, gaps
|
return plots
|
||||||
|
|
||||||
def adjustPlotHeight(self):
|
def adjustPlotHeight(self):
|
||||||
if self.pg:
|
if self.pg:
|
||||||
@ -2596,18 +2620,21 @@ class MainWindow(QMainWindow):
|
|||||||
print("Warning! No network, station, and location info available!")
|
print("Warning! No network, station, and location info available!")
|
||||||
return
|
return
|
||||||
self.update_status('picking on station {0}'.format(station))
|
self.update_status('picking on station {0}'.format(station))
|
||||||
data = self.get_data().getOriginalWFData().copy()
|
wfdata = self.get_data().getOriginalWFData().copy()
|
||||||
|
wfdata_comp = self.get_data().getAltWFdata().copy()
|
||||||
event = self.get_current_event()
|
event = self.get_current_event()
|
||||||
wftype = self.dataPlot.qcombo_processed.currentText() if self.obspy_dmt else None
|
wftype = self.dataPlot.qcombo_processed.currentText() if self.obspy_dmt else None
|
||||||
pickDlg = PickDlg(self, parameter=self._inputs,
|
pickDlg = PickDlg(self, parameter=self._inputs,
|
||||||
data=data.select(station=station),
|
data=wfdata.select(station=station),
|
||||||
|
data_compare=wfdata_comp.select(station=station),
|
||||||
station=station, network=network,
|
station=station, network=network,
|
||||||
location=location,
|
location=location,
|
||||||
picks=self.getPicksOnStation(station, 'manual'),
|
picks=self.getPicksOnStation(station, 'manual'),
|
||||||
autopicks=self.getPicksOnStation(station, 'auto'),
|
autopicks=self.getPicksOnStation(station, 'auto'),
|
||||||
metadata=self.metadata, event=event,
|
metadata=self.metadata, event=event,
|
||||||
model=self.inputs.get('taup_model'),
|
model=self.inputs.get('taup_model'),
|
||||||
filteroptions=self.filteroptions, wftype=wftype)
|
filteroptions=self.filteroptions, wftype=wftype,
|
||||||
|
show_comp_data=self.dataPlot.comp_checkbox.isChecked())
|
||||||
if self.filterActionP.isChecked():
|
if self.filterActionP.isChecked():
|
||||||
pickDlg.currentPhase = "P"
|
pickDlg.currentPhase = "P"
|
||||||
pickDlg.filterWFData()
|
pickDlg.filterWFData()
|
||||||
@ -2986,10 +3013,16 @@ class MainWindow(QMainWindow):
|
|||||||
event = self.get_current_event()
|
event = self.get_current_event()
|
||||||
event.pylot_picks = {}
|
event.pylot_picks = {}
|
||||||
event.pylot_autopicks = {}
|
event.pylot_autopicks = {}
|
||||||
picksdict = picksdict_from_picks(evt=self.get_data().get_evt_data())
|
picksdict = picksdict_from_picks(evt=self.get_data().get_evt_data(), parameter=self.getParameter())
|
||||||
event.addPicks(picksdict['manual'])
|
event.addPicks(picksdict['manual'])
|
||||||
event.addAutopicks(picksdict['auto'])
|
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):
|
def drawPicks(self, station=None, picktype=None, stime=None):
|
||||||
# if picktype not specified, draw both
|
# if picktype not specified, draw both
|
||||||
if not stime:
|
if not stime:
|
||||||
@ -3457,7 +3490,7 @@ class MainWindow(QMainWindow):
|
|||||||
self.event_table.setCellWidget(r_index, c_index, item)
|
self.event_table.setCellWidget(r_index, c_index, item)
|
||||||
|
|
||||||
header = self.event_table.horizontalHeader()
|
header = self.event_table.horizontalHeader()
|
||||||
header.setResizeMode(QtWidgets.QHeaderView.ResizeToContents)
|
header.setSectionResizeMode(QtWidgets.QHeaderView.ResizeToContents)
|
||||||
header.setStretchLastSection(True)
|
header.setStretchLastSection(True)
|
||||||
self.event_table.cellChanged[int, int].connect(cell_changed)
|
self.event_table.cellChanged[int, int].connect(cell_changed)
|
||||||
self.event_table.cellClicked[int, int].connect(cell_clicked)
|
self.event_table.cellClicked[int, int].connect(cell_clicked)
|
||||||
@ -3631,7 +3664,7 @@ class MainWindow(QMainWindow):
|
|||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def update_status(self, message, duration=5000):
|
def update_status(self, message, duration=10000):
|
||||||
self.statusBar().showMessage(message, duration)
|
self.statusBar().showMessage(message, duration)
|
||||||
if self.get_data() is not None:
|
if self.get_data() is not None:
|
||||||
if not self.get_current_event() or not self.project.location:
|
if not self.get_current_event() or not self.project.location:
|
||||||
@ -3684,10 +3717,13 @@ class MainWindow(QMainWindow):
|
|||||||
if not self.okToContinue():
|
if not self.okToContinue():
|
||||||
return
|
return
|
||||||
if not fnm:
|
if not fnm:
|
||||||
dlg = QFileDialog(parent=self)
|
settings = QSettings()
|
||||||
|
dir = settings.value('current_project_path')
|
||||||
|
dlg = QFileDialog(parent=self, directory=dir)
|
||||||
fnm = dlg.getOpenFileName(self, 'Open project file...', filter='Pylot project (*.plp)')[0]
|
fnm = dlg.getOpenFileName(self, 'Open project file...', filter='Pylot project (*.plp)')[0]
|
||||||
if not fnm:
|
if not fnm:
|
||||||
return
|
return
|
||||||
|
settings.setValue('current_project_path', os.path.split(fnm)[0])
|
||||||
if not os.path.exists(fnm):
|
if not os.path.exists(fnm):
|
||||||
QMessageBox.warning(self, 'Could not open file',
|
QMessageBox.warning(self, 'Could not open file',
|
||||||
'Could not open project file {}. File does not exist.'.format(fnm))
|
'Could not open project file {}. File does not exist.'.format(fnm))
|
||||||
@ -3805,7 +3841,8 @@ class MainWindow(QMainWindow):
|
|||||||
|
|
||||||
def closeEvent(self, event):
|
def closeEvent(self, event):
|
||||||
if self.okToContinue():
|
if self.okToContinue():
|
||||||
self.logwidget.close()
|
if hasattr(self, 'logwidget'):
|
||||||
|
self.logwidget.close()
|
||||||
event.accept()
|
event.accept()
|
||||||
else:
|
else:
|
||||||
event.ignore()
|
event.ignore()
|
||||||
|
@ -99,7 +99,7 @@ We hope to solve these with the next release.
|
|||||||
|
|
||||||
## Staff
|
## Staff
|
||||||
|
|
||||||
Original author(s): L. Kueperkoch, S. Wehling-Benatelli, M. Bischoff (PILOT)
|
Original author(s): M. Rische, S. Wehling-Benatelli, L. Kueperkoch, 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
|
Developer(s): S. Wehling-Benatelli, M. Paffrath, L. Kueperkoch, K. Olbert, M. Bischoff, C. Wollin, M. Rische, D. Arnold, K. Cökerim, S. Zimmermann
|
||||||
|
|
||||||
|
10
autoPyLoT.py
10
autoPyLoT.py
@ -119,13 +119,9 @@ def autoPyLoT(input_dict=None, parameter=None, inputfile=None, fnames=None, even
|
|||||||
obspyDMT_wfpath = input_dict['obspyDMT_wfpath']
|
obspyDMT_wfpath = input_dict['obspyDMT_wfpath']
|
||||||
|
|
||||||
if not parameter:
|
if not parameter:
|
||||||
if inputfile:
|
if not inputfile:
|
||||||
parameter = PylotParameter(inputfile)
|
print('Using default input parameter')
|
||||||
# iplot = parameter['iplot']
|
parameter = PylotParameter(inputfile)
|
||||||
else:
|
|
||||||
infile = os.path.join(os.path.expanduser('~'), '.pylot', 'pylot.in')
|
|
||||||
print('Using default input file {}'.format(infile))
|
|
||||||
parameter = PylotParameter(infile)
|
|
||||||
else:
|
else:
|
||||||
if not type(parameter) == PylotParameter:
|
if not type(parameter) == PylotParameter:
|
||||||
print('Wrong input type for parameter: {}'.format(type(parameter)))
|
print('Wrong input type for parameter: {}'.format(type(parameter)))
|
||||||
|
@ -3,8 +3,10 @@
|
|||||||
#$ -l low
|
#$ -l low
|
||||||
#$ -cwd
|
#$ -cwd
|
||||||
#$ -pe smp 40
|
#$ -pe smp 40
|
||||||
#$ -l mem=2G
|
##$ -l mem=3G
|
||||||
#$ -l h_vmem=2G
|
#$ -l h_vmem=6G
|
||||||
#$ -l os=*stretch
|
#$ -l os=*stretch
|
||||||
|
|
||||||
python ./autoPyLoT.py -i /home/marcel/.pylot/pylot_alparray_mantle_corr_stack_0.03-0.5.in -dmt processed -c $NSLOTS
|
conda activate pylot_311
|
||||||
|
|
||||||
|
python ./autoPyLoT.py -i /home/marcel/.pylot/pylot_adriaarray.in -c 20 -dmt processed
|
||||||
|
@ -8,7 +8,7 @@ dependencies:
|
|||||||
- numpy=1.22.3
|
- numpy=1.22.3
|
||||||
- obspy=1.3.0
|
- obspy=1.3.0
|
||||||
- pyqtgraph=0.12.4
|
- pyqtgraph=0.12.4
|
||||||
- pyside2=5.13.2
|
- pyside2>=5.13.2
|
||||||
- python=3.8.12
|
- python=3.8.12
|
||||||
- qt=5.12.9
|
- qt>=5.12.9
|
||||||
- scipy=1.8.0
|
- scipy=1.8.0
|
@ -418,6 +418,10 @@ class MomentMagnitude(Magnitude):
|
|||||||
distance = degrees2kilometers(a.distance)
|
distance = degrees2kilometers(a.distance)
|
||||||
azimuth = a.azimuth
|
azimuth = a.azimuth
|
||||||
incidence = a.takeoff_angle
|
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,
|
w0, fc = calcsourcespec(scopy, onset, self.p_velocity, distance,
|
||||||
azimuth, incidence, self.p_attenuation,
|
azimuth, incidence, self.p_attenuation,
|
||||||
self.plot_flag, self.verbose)
|
self.plot_flag, self.verbose)
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
import copy
|
import copy
|
||||||
|
import logging
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from PySide2.QtWidgets import QMessageBox
|
from PySide2.QtWidgets import QMessageBox
|
||||||
@ -19,7 +20,7 @@ from pylot.core.util.errors import FormatError, OverwriteError
|
|||||||
from pylot.core.util.event import Event
|
from pylot.core.util.event import Event
|
||||||
from pylot.core.util.obspyDMT_interface import qml_from_obspyDMT
|
from pylot.core.util.obspyDMT_interface import qml_from_obspyDMT
|
||||||
from pylot.core.util.utils import fnConstructor, full_range, check4rotated, \
|
from pylot.core.util.utils import fnConstructor, full_range, check4rotated, \
|
||||||
check4gapsAndMerge, trim_station_components
|
check_for_gaps_and_merge, trim_station_components, check_for_nan
|
||||||
|
|
||||||
|
|
||||||
class Data(object):
|
class Data(object):
|
||||||
@ -64,7 +65,7 @@ class Data(object):
|
|||||||
elif 'LOC' in evtdata:
|
elif 'LOC' in evtdata:
|
||||||
raise NotImplementedError('PILOT location information '
|
raise NotImplementedError('PILOT location information '
|
||||||
'read support not yet '
|
'read support not yet '
|
||||||
'implemeted.')
|
'implemented.')
|
||||||
elif 'event.pkl' in evtdata:
|
elif 'event.pkl' in evtdata:
|
||||||
evtdata = qml_from_obspyDMT(evtdata)
|
evtdata = qml_from_obspyDMT(evtdata)
|
||||||
else:
|
else:
|
||||||
@ -408,18 +409,16 @@ class Data(object):
|
|||||||
not implemented: {1}'''.format(evtformat, e))
|
not implemented: {1}'''.format(evtformat, e))
|
||||||
if fnext == '_focmec.in':
|
if fnext == '_focmec.in':
|
||||||
try:
|
try:
|
||||||
infile = os.path.join(os.path.expanduser('~'), '.pylot', 'pylot.in')
|
parameter = PylotParameter()
|
||||||
print('Using default input file {}'.format(infile))
|
logging.warning('Using default input parameter')
|
||||||
parameter = PylotParameter(infile)
|
|
||||||
focmec.export(picks_copy, fnout + fnext, parameter, eventinfo=self.get_evt_data())
|
focmec.export(picks_copy, fnout + fnext, parameter, eventinfo=self.get_evt_data())
|
||||||
except KeyError as e:
|
except KeyError as e:
|
||||||
raise KeyError('''{0} export format
|
raise KeyError('''{0} export format
|
||||||
not implemented: {1}'''.format(evtformat, e))
|
not implemented: {1}'''.format(evtformat, e))
|
||||||
if fnext == '.pha':
|
if fnext == '.pha':
|
||||||
try:
|
try:
|
||||||
infile = os.path.join(os.path.expanduser('~'), '.pylot', 'pylot.in')
|
parameter = PylotParameter()
|
||||||
print('Using default input file {}'.format(infile))
|
logging.warning('Using default input parameter')
|
||||||
parameter = PylotParameter(infile)
|
|
||||||
hypodd.export(picks_copy, fnout + fnext, parameter, eventinfo=self.get_evt_data())
|
hypodd.export(picks_copy, fnout + fnext, parameter, eventinfo=self.get_evt_data())
|
||||||
except KeyError as e:
|
except KeyError as e:
|
||||||
raise KeyError('''{0} export format
|
raise KeyError('''{0} export format
|
||||||
@ -451,20 +450,30 @@ class Data(object):
|
|||||||
data.filter(**kwargs)
|
data.filter(**kwargs)
|
||||||
self.dirty = True
|
self.dirty = True
|
||||||
|
|
||||||
def setWFData(self, fnames, fnames_syn=None, checkRotated=False, metadata=None, tstart=0, tstop=0):
|
def setWFData(self, fnames, fnames_alt=None, checkRotated=False, metadata=None, tstart=0, tstop=0):
|
||||||
"""
|
"""
|
||||||
Clear current waveform data and set given waveform data
|
Clear current waveform data and set given waveform data
|
||||||
:param fnames: waveform data names to append
|
:param fnames: waveform data names to append
|
||||||
|
:param fnames_alt: alternative data to show (e.g. synthetic/processed)
|
||||||
:type fnames: list
|
: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.wfdata = Stream()
|
||||||
self.wforiginal = None
|
self.wforiginal = None
|
||||||
self.wfsyn = Stream()
|
self.wf_alt = Stream()
|
||||||
if tstart == tstop:
|
if tstart == tstop:
|
||||||
tstart = tstop = None
|
tstart = tstop = None
|
||||||
self.tstart = tstart
|
self.tstart = tstart
|
||||||
self.tstop = tstop
|
self.tstop = tstop
|
||||||
|
|
||||||
|
# remove directories
|
||||||
|
fnames = check_fname_exists(fnames)
|
||||||
|
fnames_alt = check_fname_exists(fnames_alt)
|
||||||
|
|
||||||
# if obspy_dmt:
|
# if obspy_dmt:
|
||||||
# wfdir = 'raw'
|
# wfdir = 'raw'
|
||||||
# self.processed = False
|
# self.processed = False
|
||||||
@ -482,8 +491,8 @@ class Data(object):
|
|||||||
# wffnames = fnames
|
# wffnames = fnames
|
||||||
if fnames is not None:
|
if fnames is not None:
|
||||||
self.appendWFData(fnames)
|
self.appendWFData(fnames)
|
||||||
if fnames_syn is not None:
|
if fnames_alt is not None:
|
||||||
self.appendWFData(fnames_syn, synthetic=True)
|
self.appendWFData(fnames_alt, alternative=True)
|
||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@ -491,7 +500,9 @@ class Data(object):
|
|||||||
# remove possible underscores in station names
|
# remove possible underscores in station names
|
||||||
# self.wfdata = remove_underscores(self.wfdata)
|
# self.wfdata = remove_underscores(self.wfdata)
|
||||||
# check for gaps and merge
|
# check for gaps and merge
|
||||||
self.wfdata = check4gapsAndMerge(self.wfdata)
|
self.wfdata, _ = check_for_gaps_and_merge(self.wfdata)
|
||||||
|
# check for nans
|
||||||
|
check_for_nan(self.wfdata)
|
||||||
# check for stations with rotated components
|
# check for stations with rotated components
|
||||||
if checkRotated and metadata is not None:
|
if checkRotated and metadata is not None:
|
||||||
self.wfdata = check4rotated(self.wfdata, metadata, verbosity=0)
|
self.wfdata = check4rotated(self.wfdata, metadata, verbosity=0)
|
||||||
@ -503,7 +514,7 @@ class Data(object):
|
|||||||
self.dirty = False
|
self.dirty = False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def appendWFData(self, fnames, synthetic=False):
|
def appendWFData(self, fnames, alternative=False):
|
||||||
"""
|
"""
|
||||||
Read waveform data from fnames and append it to current wf data
|
Read waveform data from fnames and append it to current wf data
|
||||||
:param fnames: waveform data to append
|
:param fnames: waveform data to append
|
||||||
@ -516,20 +527,20 @@ class Data(object):
|
|||||||
if self.dirty:
|
if self.dirty:
|
||||||
self.resetWFData()
|
self.resetWFData()
|
||||||
|
|
||||||
real_or_syn_data = {True: self.wfsyn,
|
orig_or_alternative_data = {True: self.wf_alt,
|
||||||
False: self.wfdata}
|
False: self.wfdata}
|
||||||
|
|
||||||
warnmsg = ''
|
warnmsg = ''
|
||||||
for fname in set(fnames):
|
for fname in set(fnames):
|
||||||
try:
|
try:
|
||||||
real_or_syn_data[synthetic] += read(fname, starttime=self.tstart, endtime=self.tstop)
|
orig_or_alternative_data[alternative] += read(fname, starttime=self.tstart, endtime=self.tstop)
|
||||||
except TypeError:
|
except TypeError:
|
||||||
try:
|
try:
|
||||||
real_or_syn_data[synthetic] += read(fname, format='GSE2', starttime=self.tstart, endtime=self.tstop)
|
orig_or_alternative_data[alternative] += read(fname, format='GSE2', starttime=self.tstart, endtime=self.tstop)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
try:
|
try:
|
||||||
real_or_syn_data[synthetic] += read(fname, format='SEGY', starttime=self.tstart,
|
orig_or_alternative_data[alternative] += read(fname, format='SEGY', starttime=self.tstart,
|
||||||
endtime=self.tstop)
|
endtime=self.tstop)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
warnmsg += '{0}\n{1}\n'.format(fname, e)
|
warnmsg += '{0}\n{1}\n'.format(fname, e)
|
||||||
except SacIOError as se:
|
except SacIOError as se:
|
||||||
@ -544,8 +555,8 @@ class Data(object):
|
|||||||
def getOriginalWFData(self):
|
def getOriginalWFData(self):
|
||||||
return self.wforiginal
|
return self.wforiginal
|
||||||
|
|
||||||
def getSynWFData(self):
|
def getAltWFdata(self):
|
||||||
return self.wfsyn
|
return self.wf_alt
|
||||||
|
|
||||||
def resetWFData(self):
|
def resetWFData(self):
|
||||||
"""
|
"""
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
import glob
|
import glob
|
||||||
|
import logging
|
||||||
import os
|
import os
|
||||||
import warnings
|
import warnings
|
||||||
|
|
||||||
@ -217,7 +218,7 @@ def picksdict_from_obs(fn):
|
|||||||
return picks
|
return picks
|
||||||
|
|
||||||
|
|
||||||
def picksdict_from_picks(evt):
|
def picksdict_from_picks(evt, parameter=None):
|
||||||
"""
|
"""
|
||||||
Takes an Event object and return the pick dictionary commonly used within
|
Takes an Event object and return the pick dictionary commonly used within
|
||||||
PyLoT
|
PyLoT
|
||||||
@ -230,6 +231,7 @@ def picksdict_from_picks(evt):
|
|||||||
'auto': {}
|
'auto': {}
|
||||||
}
|
}
|
||||||
for pick in evt.picks:
|
for pick in evt.picks:
|
||||||
|
errors = None
|
||||||
phase = {}
|
phase = {}
|
||||||
station = pick.waveform_id.station_code
|
station = pick.waveform_id.station_code
|
||||||
if pick.waveform_id.channel_code is None:
|
if pick.waveform_id.channel_code is None:
|
||||||
@ -273,31 +275,28 @@ def picksdict_from_picks(evt):
|
|||||||
phase['epp'] = epp
|
phase['epp'] = epp
|
||||||
phase['lpp'] = lpp
|
phase['lpp'] = lpp
|
||||||
phase['spe'] = spe
|
phase['spe'] = spe
|
||||||
try:
|
weight = phase.get('weight')
|
||||||
phase['weight'] = weight
|
if not weight:
|
||||||
except:
|
if not parameter:
|
||||||
# get onset weight from uncertainty
|
logging.warning('Using ')
|
||||||
infile = os.path.join(os.path.expanduser('~'), '.pylot', 'pylot.in')
|
logging.warning('Using default input parameter')
|
||||||
print('Using default input file {}'.format(infile))
|
parameter = PylotParameter()
|
||||||
parameter = PylotParameter(infile)
|
pick.phase_hint = identifyPhase(pick.phase_hint)
|
||||||
if pick.phase_hint == 'P':
|
if pick.phase_hint == 'P':
|
||||||
errors = parameter['timeerrorsP']
|
errors = parameter['timeerrorsP']
|
||||||
elif pick.phase_hint == 'S':
|
elif pick.phase_hint == 'S':
|
||||||
errors = parameter['timeerrorsS']
|
errors = parameter['timeerrorsS']
|
||||||
weight = get_quality_class(spe, errors)
|
if errors:
|
||||||
phase['weight'] = weight
|
weight = get_quality_class(spe, errors)
|
||||||
|
phase['weight'] = weight
|
||||||
phase['channel'] = channel
|
phase['channel'] = channel
|
||||||
phase['network'] = network
|
phase['network'] = network
|
||||||
phase['picker'] = pick_method
|
phase['picker'] = pick_method
|
||||||
try:
|
if pick.polarity == 'positive':
|
||||||
if pick.polarity == 'positive':
|
phase['fm'] = 'U'
|
||||||
phase['fm'] = 'U'
|
elif pick.polarity == 'negative':
|
||||||
elif pick.polarity == 'negative':
|
phase['fm'] = 'D'
|
||||||
phase['fm'] = 'D'
|
else:
|
||||||
else:
|
|
||||||
phase['fm'] = 'N'
|
|
||||||
except:
|
|
||||||
print("No FM info available!")
|
|
||||||
phase['fm'] = 'N'
|
phase['fm'] = 'N'
|
||||||
phase['filter_id'] = filter_id if filter_id is not None else ''
|
phase['filter_id'] = filter_id if filter_id is not None else ''
|
||||||
|
|
||||||
|
@ -82,8 +82,8 @@ def locate(fnin, parameter=None):
|
|||||||
:param fnin: external program name
|
:param fnin: external program name
|
||||||
:return: None
|
:return: None
|
||||||
"""
|
"""
|
||||||
exe_path = parameter['nllocbin'] + '/bin/NLLoc'
|
exe_path = os.path.join(parameter['nllocbin'], 'NLLoc')
|
||||||
if exe_path is None:
|
if not os.path.isfile(exe_path):
|
||||||
raise NLLocError('NonLinLoc executable not found; check your '
|
raise NLLocError('NonLinLoc executable not found; check your '
|
||||||
'environment variables')
|
'environment variables')
|
||||||
|
|
||||||
|
@ -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.charfuns import HOScf, AICcf, ARZcf, ARHcf, AR3Ccf
|
||||||
from pylot.core.pick.picker import AICPicker, PragPicker
|
from pylot.core.pick.picker import AICPicker, PragPicker
|
||||||
from pylot.core.pick.utils import checksignallength, checkZ4S, earllatepicker, \
|
from pylot.core.pick.utils import checksignallength, checkZ4S, earllatepicker, \
|
||||||
getSNR, fmpicker, checkPonsets, wadaticheck, get_quality_class
|
getSNR, fmpicker, checkPonsets, wadaticheck, get_quality_class, PickingFailedException, MissingTraceException
|
||||||
from pylot.core.util.utils import getPatternLine, gen_Pool, \
|
from pylot.core.util.utils import getPatternLine, gen_Pool, \
|
||||||
get_bool, identifyPhaseID, get_none, correct_iplot
|
get_bool, identifyPhaseID, get_none, correct_iplot
|
||||||
|
|
||||||
@ -232,20 +232,6 @@ class PickingContainer:
|
|||||||
self.Sflag = 0
|
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):
|
class AutopickStation(object):
|
||||||
|
|
||||||
def __init__(self, wfstream, pickparam, verbose, iplot=0, fig_dict=None, metadata=None, origin=None):
|
def __init__(self, wfstream, pickparam, verbose, iplot=0, fig_dict=None, metadata=None, origin=None):
|
||||||
@ -660,7 +646,7 @@ class AutopickStation(object):
|
|||||||
ax1.set_ylim([-1.5, 1.5])
|
ax1.set_ylim([-1.5, 1.5])
|
||||||
ax1.set_ylabel('Normalized Counts')
|
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
|
# plot E trace
|
||||||
ax2 = fig.add_subplot(3, 1, 2, sharex=ax1)
|
ax2 = fig.add_subplot(3, 1, 2, sharex=ax1)
|
||||||
th1data = np.linspace(0, self.etrace.stats.endtime - self.etrace.stats.starttime,
|
th1data = np.linspace(0, self.etrace.stats.endtime - self.etrace.stats.starttime,
|
||||||
@ -936,7 +922,7 @@ class AutopickStation(object):
|
|||||||
"minFMSNR"]:
|
"minFMSNR"]:
|
||||||
# if SNR is high enough, try to determine first motion of onset
|
# if SNR is high enough, try to determine first motion of onset
|
||||||
self.set_current_figure('fm_picker')
|
self.set_current_figure('fm_picker')
|
||||||
self.p_results.fm = fmpicker(self.zstream, z_copy, self.pickparams["fmpickwin"], self.p_results.mpp,
|
self.p_results.fm = fmpicker(self.zstream.copy(), z_copy, self.pickparams["fmpickwin"], self.p_results.mpp,
|
||||||
self.iplot, self.current_figure, self.current_linecolor)
|
self.iplot, self.current_figure, self.current_linecolor)
|
||||||
msg = "autopickstation: P-weight: {}, SNR: {}, SNR[dB]: {}, Polarity: {}"
|
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)
|
msg = msg.format(self.p_results.weight, self.p_results.snr, self.p_results.snrdb, self.p_results.fm)
|
||||||
|
@ -20,6 +20,8 @@ import numpy as np
|
|||||||
from scipy import signal
|
from scipy import signal
|
||||||
from obspy.core import Stream
|
from obspy.core import Stream
|
||||||
|
|
||||||
|
from pylot.core.pick.utils import PickingFailedException
|
||||||
|
|
||||||
|
|
||||||
class CharacteristicFunction(object):
|
class CharacteristicFunction(object):
|
||||||
"""
|
"""
|
||||||
@ -293,7 +295,7 @@ class HOScf(CharacteristicFunction):
|
|||||||
if j < 4:
|
if j < 4:
|
||||||
LTA[j] = 0
|
LTA[j] = 0
|
||||||
STA[j] = 0
|
STA[j] = 0
|
||||||
elif j <= ista:
|
elif j <= ista and self.getOrder() == 2:
|
||||||
lta = (y[j] + lta * (j - 1)) / j
|
lta = (y[j] + lta * (j - 1)) / j
|
||||||
if self.getOrder() == 2:
|
if self.getOrder() == 2:
|
||||||
sta = (y[j] + sta * (j - 1)) / j
|
sta = (y[j] + sta * (j - 1)) / j
|
||||||
@ -488,6 +490,9 @@ class ARHcf(CharacteristicFunction):
|
|||||||
print('Calculating AR-prediction error from both horizontal traces ...')
|
print('Calculating AR-prediction error from both horizontal traces ...')
|
||||||
|
|
||||||
xnp = self.getDataArray(self.getCut())
|
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)
|
n0 = np.isnan(xnp[0].data)
|
||||||
if len(n0) > 1:
|
if len(n0) > 1:
|
||||||
xnp[0].data[n0] = 0
|
xnp[0].data[n0] = 0
|
||||||
|
@ -178,7 +178,9 @@ class AICPicker(AutoPicker):
|
|||||||
aic = tap * self.cf + max(abs(self.cf))
|
aic = tap * self.cf + max(abs(self.cf))
|
||||||
# smooth AIC-CF
|
# smooth AIC-CF
|
||||||
ismooth = int(round(self.Tsmooth / self.dt))
|
ismooth = int(round(self.Tsmooth / self.dt))
|
||||||
aicsmooth = np.zeros(len(aic))
|
# 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)
|
||||||
if len(aic) < ismooth:
|
if len(aic) < ismooth:
|
||||||
print('AICPicker: Tsmooth larger than CF!')
|
print('AICPicker: Tsmooth larger than CF!')
|
||||||
return
|
return
|
||||||
@ -188,7 +190,7 @@ class AICPicker(AutoPicker):
|
|||||||
ii1 = i - ismooth
|
ii1 = i - ismooth
|
||||||
aicsmooth[i] = aicsmooth[i - 1] + (aic[i] - aic[ii1]) / ismooth
|
aicsmooth[i] = aicsmooth[i - 1] + (aic[i] - aic[ii1]) / ismooth
|
||||||
else:
|
else:
|
||||||
aicsmooth[i] = np.mean(aic[1: i])
|
aicsmooth[i] = np.mean(aic[0: i]) # MP MP created np.nan for i=1
|
||||||
# remove offset in AIC function
|
# remove offset in AIC function
|
||||||
offset = abs(min(aic) - min(aicsmooth))
|
offset = abs(min(aic) - min(aicsmooth))
|
||||||
aicsmooth = aicsmooth - offset
|
aicsmooth = aicsmooth - offset
|
||||||
@ -197,7 +199,7 @@ class AICPicker(AutoPicker):
|
|||||||
# minimum in AIC function
|
# minimum in AIC function
|
||||||
icfmax = np.argmax(cf)
|
icfmax = np.argmax(cf)
|
||||||
|
|
||||||
# MP MP testing threshold
|
# TODO: If this shall be kept, maybe add thresh_factor to pylot parameters
|
||||||
thresh_hit = False
|
thresh_hit = False
|
||||||
thresh_factor = 0.7
|
thresh_factor = 0.7
|
||||||
thresh = thresh_factor * cf[icfmax]
|
thresh = thresh_factor * cf[icfmax]
|
||||||
@ -209,7 +211,6 @@ class AICPicker(AutoPicker):
|
|||||||
if sample <= cf[index - 1]:
|
if sample <= cf[index - 1]:
|
||||||
icfmax = index - 1
|
icfmax = index - 1
|
||||||
break
|
break
|
||||||
# MP MP ---
|
|
||||||
|
|
||||||
# find minimum in AIC-CF front of maximum of HOS/AR-CF
|
# find minimum in AIC-CF front of maximum of HOS/AR-CF
|
||||||
lpickwindow = int(round(self.PickWindow / self.dt))
|
lpickwindow = int(round(self.PickWindow / self.dt))
|
||||||
@ -336,7 +337,7 @@ class AICPicker(AutoPicker):
|
|||||||
self.slope = 1 / (len(dataslope) * self.Data[0].stats.delta) * (datafit[-1] - datafit[0])
|
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
|
# normalize slope to maximum of cf to make it unit independent
|
||||||
self.slope /= aicsmooth[iaicmax]
|
self.slope /= aicsmooth[iaicmax]
|
||||||
except ValueError as e:
|
except Exception as e:
|
||||||
print("AICPicker: Problems with data fitting! {}".format(e))
|
print("AICPicker: Problems with data fitting! {}".format(e))
|
||||||
|
|
||||||
else:
|
else:
|
||||||
|
@ -62,7 +62,7 @@ def earllatepicker(X, nfac, TSNR, Pick1, iplot=0, verbosity=1, fig=None, linecol
|
|||||||
plt_flag = 0
|
plt_flag = 0
|
||||||
try:
|
try:
|
||||||
iplot = int(iplot)
|
iplot = int(iplot)
|
||||||
except:
|
except ValueError:
|
||||||
if get_bool(iplot):
|
if get_bool(iplot):
|
||||||
iplot = 2
|
iplot = 2
|
||||||
else:
|
else:
|
||||||
@ -890,6 +890,8 @@ def checksignallength(X, pick, minsiglength, pickparams, iplot=0, fig=None, line
|
|||||||
input()
|
input()
|
||||||
except SyntaxError:
|
except SyntaxError:
|
||||||
pass
|
pass
|
||||||
|
except EOFError:
|
||||||
|
pass
|
||||||
plt.close(fig)
|
plt.close(fig)
|
||||||
|
|
||||||
return returnflag
|
return returnflag
|
||||||
@ -1332,22 +1334,6 @@ def get_quality_class(uncertainty, weight_classes):
|
|||||||
return quality
|
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):
|
def taper_cf(cf):
|
||||||
"""
|
"""
|
||||||
Taper cf data to get rid off of side maximas
|
Taper cf data to get rid off of side maximas
|
||||||
@ -1532,3 +1518,17 @@ if __name__ == '__main__':
|
|||||||
import doctest
|
import doctest
|
||||||
|
|
||||||
doctest.testmod()
|
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
|
||||||
|
@ -13,6 +13,7 @@ import obspy
|
|||||||
from PySide2 import QtWidgets
|
from PySide2 import QtWidgets
|
||||||
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
|
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
|
||||||
from mpl_toolkits.axes_grid1.inset_locator import inset_axes
|
from mpl_toolkits.axes_grid1.inset_locator import inset_axes
|
||||||
|
from pylot.core.util.utils import identifyPhaseID
|
||||||
from scipy.interpolate import griddata
|
from scipy.interpolate import griddata
|
||||||
|
|
||||||
from pylot.core.pick.utils import get_quality_class
|
from pylot.core.pick.utils import get_quality_class
|
||||||
@ -75,7 +76,6 @@ class Array_map(QtWidgets.QWidget):
|
|||||||
|
|
||||||
self._style = None if not hasattr(parent, '_style') else parent._style
|
self._style = None if not hasattr(parent, '_style') else parent._style
|
||||||
|
|
||||||
self.show()
|
|
||||||
|
|
||||||
def init_map(self):
|
def init_map(self):
|
||||||
self.init_colormap()
|
self.init_colormap()
|
||||||
@ -124,8 +124,8 @@ class Array_map(QtWidgets.QWidget):
|
|||||||
self.cmaps_box = QtWidgets.QComboBox()
|
self.cmaps_box = QtWidgets.QComboBox()
|
||||||
self.cmaps_box.setMaxVisibleItems(20)
|
self.cmaps_box.setMaxVisibleItems(20)
|
||||||
[self.cmaps_box.addItem(map_name) for map_name in sorted(plt.colormaps())]
|
[self.cmaps_box.addItem(map_name) for map_name in sorted(plt.colormaps())]
|
||||||
# try to set to hsv as default
|
# try to set to viridis as default
|
||||||
self.cmaps_box.setCurrentIndex(self.cmaps_box.findText('hsv'))
|
self.cmaps_box.setCurrentIndex(self.cmaps_box.findText('viridis'))
|
||||||
|
|
||||||
self.top_row.addWidget(QtWidgets.QLabel('Select a phase: '))
|
self.top_row.addWidget(QtWidgets.QLabel('Select a phase: '))
|
||||||
self.top_row.addWidget(self.comboBox_phase)
|
self.top_row.addWidget(self.comboBox_phase)
|
||||||
@ -280,9 +280,12 @@ class Array_map(QtWidgets.QWidget):
|
|||||||
self.canvas.axes.figure.canvas.draw_idle()
|
self.canvas.axes.figure.canvas.draw_idle()
|
||||||
|
|
||||||
def onpick(self, event):
|
def onpick(self, event):
|
||||||
|
btn_msg = {1: ' in selection. Aborted', 2: ' to delete a pick on. Aborted', 3: ' to display info.'}
|
||||||
ind = event.ind
|
ind = event.ind
|
||||||
button = event.mouseevent.button
|
button = event.mouseevent.button
|
||||||
if ind == []:
|
msg_reason = None
|
||||||
|
if len(ind) > 1:
|
||||||
|
self._parent.update_status(f'Found more than one station {btn_msg.get(button)}')
|
||||||
return
|
return
|
||||||
if button == 1:
|
if button == 1:
|
||||||
self.openPickDlg(ind)
|
self.openPickDlg(ind)
|
||||||
@ -385,7 +388,14 @@ class Array_map(QtWidgets.QWidget):
|
|||||||
try:
|
try:
|
||||||
station_name = st_id.split('.')[-1]
|
station_name = st_id.split('.')[-1]
|
||||||
# current_picks_dict: auto or manual
|
# current_picks_dict: auto or manual
|
||||||
pick = self.current_picks_dict()[station_name][phase]
|
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
|
||||||
if pick['picker'] == 'auto':
|
if pick['picker'] == 'auto':
|
||||||
if not pick['spe']:
|
if not pick['spe']:
|
||||||
continue
|
continue
|
||||||
@ -464,17 +474,19 @@ class Array_map(QtWidgets.QWidget):
|
|||||||
transform=ccrs.PlateCarree(), label='deleted'))
|
transform=ccrs.PlateCarree(), label='deleted'))
|
||||||
|
|
||||||
def openPickDlg(self, ind):
|
def openPickDlg(self, ind):
|
||||||
data = self._parent.get_data().getWFData()
|
wfdata = self._parent.get_data().getWFData()
|
||||||
|
wfdata_comp = self._parent.get_data().getWFDataComp()
|
||||||
for index in ind:
|
for index in ind:
|
||||||
network, station = self._station_onpick_ids[index].split('.')[:2]
|
network, station = self._station_onpick_ids[index].split('.')[:2]
|
||||||
pyl_mw = self._parent
|
pyl_mw = self._parent
|
||||||
try:
|
try:
|
||||||
data = data.select(station=station)
|
wfdata = wfdata.select(station=station)
|
||||||
if not data:
|
wfdata_comp = wfdata_comp.select(station=station)
|
||||||
|
if not wfdata:
|
||||||
self._warn('No data for station {}'.format(station))
|
self._warn('No data for station {}'.format(station))
|
||||||
return
|
return
|
||||||
pickDlg = PickDlg(self._parent, parameter=self.parameter,
|
pickDlg = PickDlg(self._parent, parameter=self.parameter,
|
||||||
data=data, network=network, station=station,
|
data=wfdata.copy(), data_compare=wfdata_comp.copy(), network=network, station=station,
|
||||||
picks=self._parent.get_current_event().getPick(station),
|
picks=self._parent.get_current_event().getPick(station),
|
||||||
autopicks=self._parent.get_current_event().getAutopick(station),
|
autopicks=self._parent.get_current_event().getAutopick(station),
|
||||||
filteroptions=self._parent.filteroptions, metadata=self.metadata,
|
filteroptions=self._parent.filteroptions, metadata=self.metadata,
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
import glob
|
import glob
|
||||||
|
import logging
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
@ -189,7 +190,11 @@ class Metadata(object):
|
|||||||
metadata = self.get_metadata(seed_id, time)
|
metadata = self.get_metadata(seed_id, time)
|
||||||
if not metadata:
|
if not metadata:
|
||||||
return
|
return
|
||||||
return metadata['data'].get_coordinates(seed_id, time)
|
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}')
|
||||||
|
|
||||||
def get_all_coordinates(self):
|
def get_all_coordinates(self):
|
||||||
def stat_info_from_parser(parser):
|
def stat_info_from_parser(parser):
|
||||||
@ -271,7 +276,7 @@ class Metadata(object):
|
|||||||
continue
|
continue
|
||||||
invtype, robj = self._read_metadata_file(os.path.join(path_to_inventory, fname))
|
invtype, robj = self._read_metadata_file(os.path.join(path_to_inventory, fname))
|
||||||
try:
|
try:
|
||||||
robj.get_coordinates(station_seed_id)
|
# robj.get_coordinates(station_seed_id) # TODO: Commented out, failed with Parser, is this needed?
|
||||||
self.inventory_files[fname] = {'invtype': invtype,
|
self.inventory_files[fname] = {'invtype': invtype,
|
||||||
'data': robj}
|
'data': robj}
|
||||||
if station_seed_id in self.seed_ids.keys():
|
if station_seed_id in self.seed_ids.keys():
|
||||||
|
@ -42,7 +42,7 @@ def main(project_file_path, manual=False, auto=True, file_format='png', f_ext=''
|
|||||||
for item in input_list:
|
for item in input_list:
|
||||||
array_map_worker(item)
|
array_map_worker(item)
|
||||||
else:
|
else:
|
||||||
pool = multiprocessing.Pool(ncores)
|
pool = multiprocessing.Pool(ncores, maxtasksperchild=1000)
|
||||||
pool.map(array_map_worker, input_list)
|
pool.map(array_map_worker, input_list)
|
||||||
pool.close()
|
pool.close()
|
||||||
pool.join()
|
pool.join()
|
||||||
|
@ -160,7 +160,7 @@ class MultiThread(QThread):
|
|||||||
try:
|
try:
|
||||||
if not self.ncores:
|
if not self.ncores:
|
||||||
self.ncores = multiprocessing.cpu_count()
|
self.ncores = multiprocessing.cpu_count()
|
||||||
pool = multiprocessing.Pool(self.ncores)
|
pool = multiprocessing.Pool(self.ncores, maxtasksperchild=1000)
|
||||||
self.data = pool.map_async(self.func, self.args, callback=self.emitDone)
|
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
|
# self.data = pool.apply_async(self.func, self.shotlist, callback=self.emitDone) #emit each time returned
|
||||||
pool.close()
|
pool.close()
|
||||||
|
@ -1,13 +1,15 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
import glob
|
||||||
import hashlib
|
import hashlib
|
||||||
|
import logging
|
||||||
import os
|
import os
|
||||||
import platform
|
import platform
|
||||||
import re
|
import re
|
||||||
import subprocess
|
import subprocess
|
||||||
import warnings
|
import warnings
|
||||||
from typing import Literal, Tuple, Type
|
from typing import Literal, Tuple, Type
|
||||||
|
from functools import lru_cache
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from obspy import UTCDateTime, read
|
from obspy import UTCDateTime, read
|
||||||
@ -105,7 +107,7 @@ def gen_Pool(ncores=0):
|
|||||||
|
|
||||||
print('gen_Pool: Generated multiprocessing Pool with {} cores\n'.format(ncores))
|
print('gen_Pool: Generated multiprocessing Pool with {} cores\n'.format(ncores))
|
||||||
|
|
||||||
pool = multiprocessing.Pool(ncores)
|
pool = multiprocessing.Pool(ncores, maxtasksperchild=100)
|
||||||
return pool
|
return pool
|
||||||
|
|
||||||
|
|
||||||
@ -332,7 +334,7 @@ def get_none(value):
|
|||||||
|
|
||||||
def get_bool(value):
|
def get_bool(value):
|
||||||
"""
|
"""
|
||||||
Convert string representations of bools to their true boolean value
|
Convert string representations of bools to their true boolean value. Return value if it cannot be identified as bool.
|
||||||
:param value:
|
:param value:
|
||||||
:type value: str, bool, int, float
|
:type value: str, bool, int, float
|
||||||
:return: true boolean value
|
:return: true boolean value
|
||||||
@ -354,17 +356,22 @@ def get_bool(value):
|
|||||||
False
|
False
|
||||||
>>> get_bool(-0.3)
|
>>> get_bool(-0.3)
|
||||||
False
|
False
|
||||||
|
>>> get_bool(None)
|
||||||
|
None
|
||||||
"""
|
"""
|
||||||
if type(value) == bool:
|
if type(value) is bool:
|
||||||
return value
|
return value
|
||||||
elif value in ['True', 'true']:
|
elif value in ['True', 'true']:
|
||||||
return True
|
return True
|
||||||
elif value in ['False', 'false']:
|
elif value in ['False', 'false']:
|
||||||
return False
|
return False
|
||||||
elif value > 0. or value > 0:
|
elif isinstance(value, float) or isinstance(value, int):
|
||||||
return True
|
if value > 0. or value > 0:
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
else:
|
else:
|
||||||
return False
|
return value
|
||||||
|
|
||||||
|
|
||||||
def four_digits(year):
|
def four_digits(year):
|
||||||
@ -893,19 +900,6 @@ def trim_station_components(data, trim_start=True, trim_end=True):
|
|||||||
return data
|
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):
|
def check4gapsAndRemove(data):
|
||||||
"""
|
"""
|
||||||
check for gaps in Stream and remove them
|
check for gaps in Stream and remove them
|
||||||
@ -926,12 +920,12 @@ def check4gapsAndRemove(data):
|
|||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
def check4gapsAndMerge(data):
|
def check_for_gaps_and_merge(data):
|
||||||
"""
|
"""
|
||||||
check for gaps in Stream and merge if gaps are found
|
check for gaps in Stream and merge if gaps are found
|
||||||
:param data: stream of seismic data
|
:param data: stream of seismic data
|
||||||
:type data: `~obspy.core.stream.Stream`
|
:type data: `~obspy.core.stream.Stream`
|
||||||
:return: data stream
|
:return: data stream, gaps returned from obspy get_gaps
|
||||||
:rtype: `~obspy.core.stream.Stream`
|
:rtype: `~obspy.core.stream.Stream`
|
||||||
"""
|
"""
|
||||||
gaps = data.get_gaps()
|
gaps = data.get_gaps()
|
||||||
@ -942,7 +936,7 @@ def check4gapsAndMerge(data):
|
|||||||
for merged_station in merged:
|
for merged_station in merged:
|
||||||
print(merged_station)
|
print(merged_station)
|
||||||
|
|
||||||
return data
|
return data, gaps
|
||||||
|
|
||||||
|
|
||||||
def check4doubled(data):
|
def check4doubled(data):
|
||||||
@ -972,6 +966,46 @@ def check4doubled(data):
|
|||||||
return 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):
|
def get_stations(data):
|
||||||
"""
|
"""
|
||||||
Get list of all station names in data-stream
|
Get list of all station names in data-stream
|
||||||
@ -1038,7 +1072,7 @@ def check4rotated(data, metadata=None, verbosity=1):
|
|||||||
# check if any traces in this station need to be rotated
|
# check if any traces in this station need to be rotated
|
||||||
trace_ids = [trace.id for trace in wfs_in]
|
trace_ids = [trace.id for trace in wfs_in]
|
||||||
if not rotation_required(trace_ids):
|
if not rotation_required(trace_ids):
|
||||||
print(f"Stream does not need any rotation: Traces are {trace_ids=}")
|
logging.debug(f"Stream does not need any rotation: Traces are {trace_ids=}")
|
||||||
return wfs_in
|
return wfs_in
|
||||||
|
|
||||||
# check metadata quality
|
# check metadata quality
|
||||||
@ -1050,7 +1084,7 @@ def check4rotated(data, metadata=None, verbosity=1):
|
|||||||
azimuths.append(metadata.get_coordinates(tr_id, t_start)['azimuth'])
|
azimuths.append(metadata.get_coordinates(tr_id, t_start)['azimuth'])
|
||||||
dips.append(metadata.get_coordinates(tr_id, t_start)['dip'])
|
dips.append(metadata.get_coordinates(tr_id, t_start)['dip'])
|
||||||
except (KeyError, TypeError) as err:
|
except (KeyError, TypeError) as err:
|
||||||
print(f"{type(err)=} occurred: {err=} Rotating not possible, not all azimuth and dip information "
|
logging.error(f"{type(err)=} occurred: {err=} Rotating not possible, not all azimuth and dip information "
|
||||||
f"available in metadata. Stream remains unchanged.")
|
f"available in metadata. Stream remains unchanged.")
|
||||||
return wfs_in
|
return wfs_in
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
@ -1170,6 +1204,7 @@ def identifyPhase(phase):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
@lru_cache
|
||||||
def identifyPhaseID(phase):
|
def identifyPhaseID(phase):
|
||||||
"""
|
"""
|
||||||
Returns phase id (capital P or S)
|
Returns phase id (capital P or S)
|
||||||
|
@ -7,6 +7,7 @@ Created on Wed Mar 19 11:27:35 2014
|
|||||||
import copy
|
import copy
|
||||||
import datetime
|
import datetime
|
||||||
import getpass
|
import getpass
|
||||||
|
import glob
|
||||||
import multiprocessing
|
import multiprocessing
|
||||||
import os
|
import os
|
||||||
import subprocess
|
import subprocess
|
||||||
@ -16,6 +17,7 @@ import traceback
|
|||||||
|
|
||||||
import matplotlib
|
import matplotlib
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
from pylot.core.io.phases import getQualitiesfromxml
|
||||||
|
|
||||||
matplotlib.use('QT5Agg')
|
matplotlib.use('QT5Agg')
|
||||||
|
|
||||||
@ -36,7 +38,7 @@ from PySide2.QtWidgets import QAction, QApplication, QCheckBox, QComboBox, \
|
|||||||
QGridLayout, QLabel, QLineEdit, QMessageBox, \
|
QGridLayout, QLabel, QLineEdit, QMessageBox, \
|
||||||
QTabWidget, QToolBar, QVBoxLayout, QHBoxLayout, QWidget, \
|
QTabWidget, QToolBar, QVBoxLayout, QHBoxLayout, QWidget, \
|
||||||
QPushButton, QFileDialog, QInputDialog
|
QPushButton, QFileDialog, QInputDialog
|
||||||
from PySide2.QtCore import QSettings, Qt, QUrl, Signal
|
from PySide2.QtCore import QSettings, Qt, QUrl, Signal, QTimer
|
||||||
from PySide2.QtWebEngineWidgets import QWebEngineView as QWebView
|
from PySide2.QtWebEngineWidgets import QWebEngineView as QWebView
|
||||||
from obspy import Stream, Trace, UTCDateTime
|
from obspy import Stream, Trace, UTCDateTime
|
||||||
from obspy.core.util import AttribDict
|
from obspy.core.util import AttribDict
|
||||||
@ -51,10 +53,10 @@ from pylot.core.pick.autopick import fmpicker
|
|||||||
from pylot.core.util.defaults import OUTPUTFORMATS, FILTERDEFAULTS
|
from pylot.core.util.defaults import OUTPUTFORMATS, FILTERDEFAULTS
|
||||||
from pylot.core.util.utils import prep_time_axis, full_range, demeanTrace, isSorted, findComboBoxIndex, clims, \
|
from pylot.core.util.utils import prep_time_axis, full_range, demeanTrace, isSorted, findComboBoxIndex, clims, \
|
||||||
pick_linestyle_plt, pick_color_plt, \
|
pick_linestyle_plt, pick_color_plt, \
|
||||||
check4rotated, check4doubled, merge_stream, identifyPhase, \
|
check4rotated, check4doubled, check_for_gaps_and_merge, check_for_nan, identifyPhase, \
|
||||||
loopIdentifyPhase, trim_station_components, transformFilteroptions2String, \
|
loopIdentifyPhase, trim_station_components, transformFilteroptions2String, \
|
||||||
identifyPhaseID, get_bool, get_none, pick_color, getAutoFilteroptions, SetChannelComponents, \
|
identifyPhaseID, get_bool, get_none, pick_color, getAutoFilteroptions, SetChannelComponents, \
|
||||||
station_id_remove_channel
|
station_id_remove_channel, get_pylot_eventfile_with_extension, get_possible_pylot_eventfile_extensions
|
||||||
from autoPyLoT import autoPyLoT
|
from autoPyLoT import autoPyLoT
|
||||||
from pylot.core.util.thread import Thread
|
from pylot.core.util.thread import Thread
|
||||||
from pylot.core.util.dataprocessing import Metadata
|
from pylot.core.util.dataprocessing import Metadata
|
||||||
@ -793,7 +795,7 @@ class WaveformWidgetPG(QtWidgets.QWidget):
|
|||||||
|
|
||||||
def connect_signals(self):
|
def connect_signals(self):
|
||||||
self.qcombo_processed.activated.connect(self.parent().newWF)
|
self.qcombo_processed.activated.connect(self.parent().newWF)
|
||||||
self.syn_checkbox.clicked.connect(self.parent().newWF)
|
self.comp_checkbox.clicked.connect(self.parent().newWF)
|
||||||
|
|
||||||
def init_labels(self):
|
def init_labels(self):
|
||||||
self.label_layout.addWidget(self.status_label)
|
self.label_layout.addWidget(self.status_label)
|
||||||
@ -804,13 +806,13 @@ class WaveformWidgetPG(QtWidgets.QWidget):
|
|||||||
# use widgets as placeholder, so that child widgets keep position when others are hidden
|
# use widgets as placeholder, so that child widgets keep position when others are hidden
|
||||||
mid_layout = QHBoxLayout()
|
mid_layout = QHBoxLayout()
|
||||||
right_layout = QHBoxLayout()
|
right_layout = QHBoxLayout()
|
||||||
mid_layout.addWidget(self.syn_checkbox)
|
mid_layout.addWidget(self.comp_checkbox)
|
||||||
right_layout.addWidget(self.qcombo_processed)
|
right_layout.addWidget(self.qcombo_processed)
|
||||||
mid_widget.setLayout(mid_layout)
|
mid_widget.setLayout(mid_layout)
|
||||||
right_widget.setLayout(right_layout)
|
right_widget.setLayout(right_layout)
|
||||||
self.label_layout.addWidget(mid_widget)
|
self.label_layout.addWidget(mid_widget)
|
||||||
self.label_layout.addWidget(right_widget)
|
self.label_layout.addWidget(right_widget)
|
||||||
self.syn_checkbox.setLayoutDirection(Qt.RightToLeft)
|
self.comp_checkbox.setLayoutDirection(Qt.RightToLeft)
|
||||||
self.label_layout.setStretch(0, 4)
|
self.label_layout.setStretch(0, 4)
|
||||||
self.label_layout.setStretch(1, 0)
|
self.label_layout.setStretch(1, 0)
|
||||||
self.label_layout.setStretch(2, 0)
|
self.label_layout.setStretch(2, 0)
|
||||||
@ -825,7 +827,7 @@ class WaveformWidgetPG(QtWidgets.QWidget):
|
|||||||
label = QtWidgets.QLabel()
|
label = QtWidgets.QLabel()
|
||||||
self.perm_labels.append(label)
|
self.perm_labels.append(label)
|
||||||
self.qcombo_processed = QtWidgets.QComboBox()
|
self.qcombo_processed = QtWidgets.QComboBox()
|
||||||
self.syn_checkbox = QtWidgets.QCheckBox('synthetics')
|
self.comp_checkbox = QtWidgets.QCheckBox('Load comparison data')
|
||||||
self.addQCboxItem('processed', 'green')
|
self.addQCboxItem('processed', 'green')
|
||||||
self.addQCboxItem('raw', 'black')
|
self.addQCboxItem('raw', 'black')
|
||||||
# self.perm_qcbox_right.setAlignment(2)
|
# self.perm_qcbox_right.setAlignment(2)
|
||||||
@ -834,9 +836,11 @@ class WaveformWidgetPG(QtWidgets.QWidget):
|
|||||||
def getPlotDict(self):
|
def getPlotDict(self):
|
||||||
return self.plotdict
|
return self.plotdict
|
||||||
|
|
||||||
def activateObspyDMToptions(self, activate):
|
def activateObspyDMToptions(self, activate: bool) -> None:
|
||||||
self.syn_checkbox.setVisible(activate)
|
self.qcombo_processed.setEnabled(activate)
|
||||||
self.qcombo_processed.setVisible(activate)
|
|
||||||
|
def activateCompareOptions(self, activate: bool) -> None:
|
||||||
|
self.comp_checkbox.setEnabled(activate)
|
||||||
|
|
||||||
def setPermText(self, number, text=None, color='black'):
|
def setPermText(self, number, text=None, color='black'):
|
||||||
if not 0 <= number < len(self.perm_labels):
|
if not 0 <= number < len(self.perm_labels):
|
||||||
@ -860,13 +864,26 @@ class WaveformWidgetPG(QtWidgets.QWidget):
|
|||||||
def clearPlotDict(self):
|
def clearPlotDict(self):
|
||||||
self.plotdict = dict()
|
self.plotdict = dict()
|
||||||
|
|
||||||
def plotWFData(self, wfdata, wfsyn=None, title=None, zoomx=None, zoomy=None,
|
def plotWFData(self, wfdata, wfsyn=None, title=None, scaleddata=False, mapping=True,
|
||||||
noiselevel=None, scaleddata=False, mapping=True,
|
component='*', nth_sample=1, verbosity=0, method='normal', gain=1., shift_syn=0.2):
|
||||||
component='*', nth_sample=1, iniPick=None, verbosity=0,
|
def station_sort(nslc):
|
||||||
method='normal', gain=1.):
|
"""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)
|
||||||
|
|
||||||
if not wfdata:
|
if not wfdata:
|
||||||
print('Nothing to plot.')
|
print('Nothing to plot.')
|
||||||
return
|
return
|
||||||
|
|
||||||
self.title = title
|
self.title = title
|
||||||
self.clearPlotDict()
|
self.clearPlotDict()
|
||||||
self.wfstart, self.wfend = full_range(wfdata)
|
self.wfstart, self.wfend = full_range(wfdata)
|
||||||
@ -884,14 +901,14 @@ class WaveformWidgetPG(QtWidgets.QWidget):
|
|||||||
else:
|
else:
|
||||||
st_select = wfdata
|
st_select = wfdata
|
||||||
|
|
||||||
st_select, gaps = merge_stream(st_select)
|
# st_select, gaps = check_for_gaps_and_merge(st_select) #MP MP commented because probably done twice
|
||||||
|
|
||||||
# list containing tuples of network, station, channel (for sorting)
|
# list containing tuples of network, station, channel (for sorting)
|
||||||
nslc = []
|
nslc = []
|
||||||
for trace in st_select:
|
for trace in st_select:
|
||||||
nslc.append(
|
nslc.append(
|
||||||
trace.get_id()) # (trace.stats.network, trace.stats.station, trace.stats.location trace.stats.channel))
|
trace.get_id()) # (trace.stats.network, trace.stats.station, trace.stats.location trace.stats.channel))
|
||||||
nslc.sort()
|
nslc = station_sort(nslc)
|
||||||
nslc.reverse()
|
nslc.reverse()
|
||||||
plots = []
|
plots = []
|
||||||
|
|
||||||
@ -946,7 +963,7 @@ class WaveformWidgetPG(QtWidgets.QWidget):
|
|||||||
[time for index, time in enumerate(time_ax_syn) if not index % nth_sample] if st_syn else [])
|
[time for index, time in enumerate(time_ax_syn) if not index % nth_sample] if st_syn else [])
|
||||||
trace.data = np.array(
|
trace.data = np.array(
|
||||||
[datum * gain + n for index, datum in enumerate(trace.data) if not index % nth_sample])
|
[datum * gain + n for index, datum in enumerate(trace.data) if not index % nth_sample])
|
||||||
trace_syn.data = np.array([datum + n for index, datum in enumerate(trace_syn.data)
|
trace_syn.data = np.array([datum + n + shift_syn for index, datum in enumerate(trace_syn.data)
|
||||||
if not index % nth_sample] if st_syn else [])
|
if not index % nth_sample] if st_syn else [])
|
||||||
plots.append((times, trace.data,
|
plots.append((times, trace.data,
|
||||||
times_syn, trace_syn.data))
|
times_syn, trace_syn.data))
|
||||||
@ -955,7 +972,7 @@ class WaveformWidgetPG(QtWidgets.QWidget):
|
|||||||
self.ylabel = ''
|
self.ylabel = ''
|
||||||
self.setXLims([0, self.wfend - self.wfstart])
|
self.setXLims([0, self.wfend - self.wfstart])
|
||||||
self.setYLims([0.5, nmax + 0.5])
|
self.setYLims([0.5, nmax + 0.5])
|
||||||
return plots, gaps
|
return plots
|
||||||
|
|
||||||
def minMax(self, trace, time_ax):
|
def minMax(self, trace, time_ax):
|
||||||
'''
|
'''
|
||||||
@ -977,7 +994,7 @@ class WaveformWidgetPG(QtWidgets.QWidget):
|
|||||||
min_ = data.min(axis=1)
|
min_ = data.min(axis=1)
|
||||||
max_ = data.max(axis=1)
|
max_ = data.max(axis=1)
|
||||||
if remaining_samples:
|
if remaining_samples:
|
||||||
extreme_values = np.empty((npixel + 1, 2), dtype=np.float)
|
extreme_values = np.empty((npixel + 1, 2), dtype=float)
|
||||||
extreme_values[:-1, 0] = min_
|
extreme_values[:-1, 0] = min_
|
||||||
extreme_values[:-1, 1] = max_
|
extreme_values[:-1, 1] = max_
|
||||||
extreme_values[-1, 0] = \
|
extreme_values[-1, 0] = \
|
||||||
@ -985,7 +1002,7 @@ class WaveformWidgetPG(QtWidgets.QWidget):
|
|||||||
extreme_values[-1, 1] = \
|
extreme_values[-1, 1] = \
|
||||||
trace.data[-remaining_samples:].max()
|
trace.data[-remaining_samples:].max()
|
||||||
else:
|
else:
|
||||||
extreme_values = np.empty((npixel, 2), dtype=np.float)
|
extreme_values = np.empty((npixel, 2), dtype=float)
|
||||||
extreme_values[:, 0] = min_
|
extreme_values[:, 0] = min_
|
||||||
extreme_values[:, 1] = max_
|
extreme_values[:, 1] = max_
|
||||||
data = extreme_values.flatten()
|
data = extreme_values.flatten()
|
||||||
@ -1135,12 +1152,12 @@ class PylotCanvas(FigureCanvas):
|
|||||||
ax.set_xlim(self.cur_xlim)
|
ax.set_xlim(self.cur_xlim)
|
||||||
ax.set_ylim(self.cur_ylim)
|
ax.set_ylim(self.cur_ylim)
|
||||||
self.refreshPickDlgText()
|
self.refreshPickDlgText()
|
||||||
ax.figure.canvas.draw()
|
ax.figure.canvas.draw_idle()
|
||||||
|
|
||||||
def panRelease(self, gui_event):
|
def panRelease(self, gui_event):
|
||||||
self.press = None
|
self.press = None
|
||||||
self.press_rel = None
|
self.press_rel = None
|
||||||
self.figure.canvas.draw()
|
self.figure.canvas.draw_idle()
|
||||||
|
|
||||||
def panZoom(self, gui_event, threshold=2., factor=1.1):
|
def panZoom(self, gui_event, threshold=2., factor=1.1):
|
||||||
if not gui_event.x and not gui_event.y:
|
if not gui_event.x and not gui_event.y:
|
||||||
@ -1358,11 +1375,15 @@ class PylotCanvas(FigureCanvas):
|
|||||||
plot_positions[channel] = plot_pos
|
plot_positions[channel] = plot_pos
|
||||||
return plot_positions
|
return plot_positions
|
||||||
|
|
||||||
def plotWFData(self, wfdata, title=None, zoomx=None, zoomy=None,
|
def plotWFData(self, wfdata, wfdata_compare=None, title=None, zoomx=None, zoomy=None,
|
||||||
noiselevel=None, scaleddata=False, mapping=True,
|
noiselevel=None, scaleddata=False, mapping=True,
|
||||||
component='*', nth_sample=1, iniPick=None, verbosity=0,
|
component='*', nth_sample=1, iniPick=None, verbosity=0,
|
||||||
plot_additional=False, additional_channel=None, scaleToChannel=None,
|
plot_additional=False, additional_channel=None, scaleToChannel=None,
|
||||||
snr=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 = self.axes[0]
|
||||||
ax.cla()
|
ax.cla()
|
||||||
|
|
||||||
@ -1373,21 +1394,33 @@ class PylotCanvas(FigureCanvas):
|
|||||||
settings = QSettings()
|
settings = QSettings()
|
||||||
compclass = SetChannelComponents.from_qsettings(settings)
|
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 == '*':
|
if not component == '*':
|
||||||
alter_comp = compclass.getCompPosition(component)
|
alter_comp = compclass.getCompPosition(component)
|
||||||
# alter_comp = str(alter_comp[0])
|
# alter_comp = str(alter_comp[0])
|
||||||
|
|
||||||
st_select = wfdata.select(component=component)
|
plot_streams['wfdata']['data'] = wfdata.select(component=component)
|
||||||
st_select += wfdata.select(component=alter_comp)
|
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)
|
||||||
else:
|
else:
|
||||||
st_select = wfdata
|
plot_streams['wfdata']['data'] = wfdata
|
||||||
|
if wfdata_compare:
|
||||||
|
plot_streams['wfdata_comp']['data'] = wfdata_compare
|
||||||
|
|
||||||
|
st_main = plot_streams['wfdata']['data']
|
||||||
|
|
||||||
if mapping:
|
if mapping:
|
||||||
plot_positions = self.calcPlotPositions(st_select, compclass)
|
plot_positions = self.calcPlotPositions(st_main, compclass)
|
||||||
|
|
||||||
# list containing tuples of network, station, channel and plot position (for sorting)
|
# list containing tuples of network, station, channel and plot position (for sorting)
|
||||||
nslc = []
|
nslc = []
|
||||||
for plot_pos, trace in enumerate(st_select):
|
for plot_pos, trace in enumerate(st_main):
|
||||||
if not trace.stats.channel[-1] in ['Z', 'N', 'E', '1', '2', '3']:
|
if not trace.stats.channel[-1] in ['Z', 'N', 'E', '1', '2', '3']:
|
||||||
print('Warning: Unrecognized channel {}'.format(trace.stats.channel))
|
print('Warning: Unrecognized channel {}'.format(trace.stats.channel))
|
||||||
continue
|
continue
|
||||||
@ -1395,44 +1428,48 @@ class PylotCanvas(FigureCanvas):
|
|||||||
nslc.sort()
|
nslc.sort()
|
||||||
nslc.reverse()
|
nslc.reverse()
|
||||||
|
|
||||||
linecolor = (0., 0., 0., 1.) if not self.style else self.style['linecolor']['rgba_mpl']
|
|
||||||
|
|
||||||
for n, seed_id in enumerate(nslc):
|
for n, seed_id in enumerate(nslc):
|
||||||
network, station, location, channel = seed_id.split('.')
|
network, station, location, channel = seed_id.split('.')
|
||||||
st = st_select.select(id=seed_id)
|
for wf_name, wf_dict in plot_streams.items():
|
||||||
trace = st[0].copy()
|
st_select = wf_dict.get('data')
|
||||||
if mapping:
|
if not st_select:
|
||||||
n = plot_positions[trace.stats.channel]
|
continue
|
||||||
if n > nmax:
|
st = st_select.select(id=seed_id)
|
||||||
nmax = n
|
trace = st[0].copy()
|
||||||
if verbosity:
|
if mapping:
|
||||||
msg = 'plotting %s channel of station %s' % (channel, station)
|
n = plot_positions[trace.stats.channel]
|
||||||
print(msg)
|
if n > nmax:
|
||||||
stime = trace.stats.starttime - wfstart
|
nmax = n
|
||||||
time_ax = prep_time_axis(stime, trace)
|
if verbosity:
|
||||||
if time_ax is not None:
|
msg = 'plotting %s channel of station %s' % (channel, station)
|
||||||
if scaleToChannel:
|
print(msg)
|
||||||
st_scale = wfdata.select(channel=scaleToChannel)
|
stime = trace.stats.starttime - wfstart
|
||||||
if st_scale:
|
time_ax = prep_time_axis(stime, trace)
|
||||||
tr = st_scale[0]
|
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:
|
||||||
trace.detrend('constant')
|
trace.detrend('constant')
|
||||||
trace.normalize(np.max(np.abs(tr.data)) * 2)
|
trace.normalize(np.max(np.abs(trace.data)) * 2)
|
||||||
scaleddata = True
|
|
||||||
if not scaleddata:
|
|
||||||
trace.detrend('constant')
|
|
||||||
trace.normalize(np.max(np.abs(trace.data)) * 2)
|
|
||||||
|
|
||||||
times = [time for index, time in enumerate(time_ax) if not index % nth_sample]
|
offset = wf_dict.get('offset')
|
||||||
data = [datum + n for index, datum in enumerate(trace.data) if not index % nth_sample]
|
|
||||||
ax.axhline(n, color="0.5", lw=0.5)
|
times = [time for index, time in enumerate(time_ax) if not index % nth_sample]
|
||||||
ax.plot(times, data, color=linecolor, linewidth=0.7)
|
data = [datum + n + offset for index, datum in enumerate(trace.data) if not index % nth_sample]
|
||||||
if noiselevel is not None:
|
ax.axhline(n, color="0.5", lw=0.5)
|
||||||
for level in [-noiselevel[channel], noiselevel[channel]]:
|
ax.plot(times, data, color=wf_dict.get('linecolor'), **wf_dict.get('plot_kwargs'))
|
||||||
ax.plot([time_ax[0], time_ax[-1]],
|
if noiselevel is not None:
|
||||||
[n + level, n + level],
|
for level in [-noiselevel[channel], noiselevel[channel]]:
|
||||||
color=linecolor,
|
ax.plot([time_ax[0], time_ax[-1]],
|
||||||
linestyle='dashed')
|
[n + level, n + level],
|
||||||
self.setPlotDict(n, seed_id)
|
color=wf_dict.get('linecolor'),
|
||||||
|
linestyle='dashed')
|
||||||
|
self.setPlotDict(n, seed_id)
|
||||||
if plot_additional and additional_channel:
|
if plot_additional and additional_channel:
|
||||||
compare_stream = wfdata.select(channel=additional_channel)
|
compare_stream = wfdata.select(channel=additional_channel)
|
||||||
if compare_stream:
|
if compare_stream:
|
||||||
@ -1555,6 +1592,178 @@ class PylotCanvas(FigureCanvas):
|
|||||||
self.draw()
|
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):
|
class SingleTextLineDialog(QtWidgets.QDialog):
|
||||||
def __init__(self, parent=None, label='Text: ', default_text='.xml'):
|
def __init__(self, parent=None, label='Text: ', default_text='.xml'):
|
||||||
super(SingleTextLineDialog, self).__init__(parent)
|
super(SingleTextLineDialog, self).__init__(parent)
|
||||||
@ -1659,8 +1868,8 @@ class PhaseDefaults(QtWidgets.QDialog):
|
|||||||
class PickDlg(QDialog):
|
class PickDlg(QDialog):
|
||||||
update_picks = QtCore.Signal(dict)
|
update_picks = QtCore.Signal(dict)
|
||||||
|
|
||||||
def __init__(self, parent=None, data=None, station=None, network=None, location=None, picks=None,
|
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,
|
autopicks=None, rotate=False, parameter=None, embedded=False, metadata=None, show_comp_data=False,
|
||||||
event=None, filteroptions=None, model=None, wftype=None):
|
event=None, filteroptions=None, model=None, wftype=None):
|
||||||
super(PickDlg, self).__init__(parent, Qt.Window)
|
super(PickDlg, self).__init__(parent, Qt.Window)
|
||||||
self.orig_parent = parent
|
self.orig_parent = parent
|
||||||
@ -1669,6 +1878,7 @@ class PickDlg(QDialog):
|
|||||||
# initialize attributes
|
# initialize attributes
|
||||||
self.parameter = parameter
|
self.parameter = parameter
|
||||||
self._embedded = embedded
|
self._embedded = embedded
|
||||||
|
self.showCompData = show_comp_data
|
||||||
self.station = station
|
self.station = station
|
||||||
self.network = network
|
self.network = network
|
||||||
self.location = location
|
self.location = location
|
||||||
@ -1707,22 +1917,6 @@ class PickDlg(QDialog):
|
|||||||
else:
|
else:
|
||||||
self.filteroptions = FILTERDEFAULTS
|
self.filteroptions = FILTERDEFAULTS
|
||||||
self.pick_block = False
|
self.pick_block = False
|
||||||
self.nextStation = QtWidgets.QCheckBox('Continue with next station ')
|
|
||||||
|
|
||||||
# comparison channel
|
|
||||||
self.compareChannel = QtWidgets.QComboBox()
|
|
||||||
self.compareChannel.activated.connect(self.resetPlot)
|
|
||||||
|
|
||||||
# scale channel
|
|
||||||
self.scaleChannel = QtWidgets.QComboBox()
|
|
||||||
self.scaleChannel.activated.connect(self.resetPlot)
|
|
||||||
|
|
||||||
# initialize panning attributes
|
|
||||||
self.press = None
|
|
||||||
self.xpress = None
|
|
||||||
self.ypress = None
|
|
||||||
self.cur_xlim = None
|
|
||||||
self.cur_ylim = None
|
|
||||||
|
|
||||||
# set attribute holding data
|
# set attribute holding data
|
||||||
if data is None or not data:
|
if data is None or not data:
|
||||||
@ -1735,6 +1929,31 @@ class PickDlg(QDialog):
|
|||||||
raise Exception(errmsg)
|
raise Exception(errmsg)
|
||||||
else:
|
else:
|
||||||
self.data = data
|
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))
|
||||||
|
|
||||||
|
# scale channel
|
||||||
|
self.scaleChannel = QtWidgets.QComboBox()
|
||||||
|
self.scaleChannel.activated.connect(self.resetPlot)
|
||||||
|
|
||||||
|
# initialize panning attributes
|
||||||
|
self.press = None
|
||||||
|
self.xpress = None
|
||||||
|
self.ypress = None
|
||||||
|
self.cur_xlim = None
|
||||||
|
self.cur_ylim = None
|
||||||
|
|
||||||
self.stime, self.etime = full_range(self.getWFData())
|
self.stime, self.etime = full_range(self.getWFData())
|
||||||
|
|
||||||
@ -1747,12 +1966,12 @@ class PickDlg(QDialog):
|
|||||||
self.setupUi()
|
self.setupUi()
|
||||||
|
|
||||||
# fill compare and scale channels
|
# fill compare and scale channels
|
||||||
self.compareChannel.addItem('-', None)
|
self.referenceChannel.addItem('-', None)
|
||||||
self.scaleChannel.addItem('individual', None)
|
self.scaleChannel.addItem('individual', None)
|
||||||
|
|
||||||
for trace in self.getWFData():
|
for trace in self.getWFData():
|
||||||
channel = trace.stats.channel
|
channel = trace.stats.channel
|
||||||
self.compareChannel.addItem(channel, trace)
|
self.referenceChannel.addItem(channel, trace)
|
||||||
if not channel[-1] in ['Z', 'N', 'E', '1', '2', '3']:
|
if not channel[-1] in ['Z', 'N', 'E', '1', '2', '3']:
|
||||||
print('Skipping unknown channel for scaling: {}'.format(channel))
|
print('Skipping unknown channel for scaling: {}'.format(channel))
|
||||||
continue
|
continue
|
||||||
@ -1769,7 +1988,7 @@ class PickDlg(QDialog):
|
|||||||
if self.wftype is not None:
|
if self.wftype is not None:
|
||||||
title += ' | ({})'.format(self.wftype)
|
title += ' | ({})'.format(self.wftype)
|
||||||
|
|
||||||
self.multicompfig.plotWFData(wfdata=self.getWFData(),
|
self.multicompfig.plotWFData(wfdata=self.getWFData(), wfdata_compare=self.getWFDataComp(),
|
||||||
title=title)
|
title=title)
|
||||||
|
|
||||||
self.multicompfig.setZoomBorders2content()
|
self.multicompfig.setZoomBorders2content()
|
||||||
@ -1786,7 +2005,7 @@ class PickDlg(QDialog):
|
|||||||
|
|
||||||
# init expected picks using obspy Taup
|
# init expected picks using obspy Taup
|
||||||
try:
|
try:
|
||||||
if self.metadata and model is not None:
|
if self.metadata and model != "None":
|
||||||
self.model = TauPyModel(model)
|
self.model = TauPyModel(model)
|
||||||
self.get_arrivals()
|
self.get_arrivals()
|
||||||
self.drawArrivals()
|
self.drawArrivals()
|
||||||
@ -1945,8 +2164,11 @@ class PickDlg(QDialog):
|
|||||||
_dialtoolbar.addWidget(est_label)
|
_dialtoolbar.addWidget(est_label)
|
||||||
_dialtoolbar.addWidget(self.plot_arrivals_button)
|
_dialtoolbar.addWidget(self.plot_arrivals_button)
|
||||||
_dialtoolbar.addSeparator()
|
_dialtoolbar.addSeparator()
|
||||||
_dialtoolbar.addWidget(QtWidgets.QLabel('Compare to channel: '))
|
_dialtoolbar.addWidget(QtWidgets.QLabel('Plot reference channel: '))
|
||||||
_dialtoolbar.addWidget(self.compareChannel)
|
_dialtoolbar.addWidget(self.referenceChannel)
|
||||||
|
_dialtoolbar.addSeparator()
|
||||||
|
_dialtoolbar.addWidget(QtWidgets.QLabel('Compare: '))
|
||||||
|
_dialtoolbar.addWidget(self.compareCB)
|
||||||
_dialtoolbar.addSeparator()
|
_dialtoolbar.addSeparator()
|
||||||
_dialtoolbar.addWidget(QtWidgets.QLabel('Scaling: '))
|
_dialtoolbar.addWidget(QtWidgets.QLabel('Scaling: '))
|
||||||
_dialtoolbar.addWidget(self.scaleChannel)
|
_dialtoolbar.addWidget(self.scaleChannel)
|
||||||
@ -2031,10 +2253,12 @@ class PickDlg(QDialog):
|
|||||||
station_id = trace.get_id()
|
station_id = trace.get_id()
|
||||||
starttime = trace.stats.starttime
|
starttime = trace.stats.starttime
|
||||||
station_coords = self.metadata.get_coordinates(station_id, 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
|
origins = self.pylot_event.origins
|
||||||
if phases == ['None', 'None']:
|
if phases == ['None', 'None']:
|
||||||
print("get_arrivals: Creation info (manual or auto) not available!")
|
print("get_arrivals: Creation info (manual or auto) not available! Return!")
|
||||||
print("Return!")
|
|
||||||
return
|
return
|
||||||
if origins:
|
if origins:
|
||||||
source_origin = origins[0]
|
source_origin = origins[0]
|
||||||
@ -2146,7 +2370,7 @@ class PickDlg(QDialog):
|
|||||||
|
|
||||||
# create action and add to menu
|
# create action and add to menu
|
||||||
# phase name transferred using lambda function
|
# phase name transferred using lambda function
|
||||||
slot = lambda phase=phase, phaseID=phaseID: phaseSelect[phaseID](phase)
|
slot = lambda ph=phase, phID=phaseID: phaseSelect[phID](ph)
|
||||||
picksAction = createAction(parent=self, text=phase,
|
picksAction = createAction(parent=self, text=phase,
|
||||||
slot=slot,
|
slot=slot,
|
||||||
shortcut=shortcut)
|
shortcut=shortcut)
|
||||||
@ -2281,7 +2505,7 @@ class PickDlg(QDialog):
|
|||||||
def activatePicking(self):
|
def activatePicking(self):
|
||||||
self.leave_rename_phase()
|
self.leave_rename_phase()
|
||||||
self.renamePhaseAction.setEnabled(False)
|
self.renamePhaseAction.setEnabled(False)
|
||||||
self.compareChannel.setEnabled(False)
|
self.referenceChannel.setEnabled(False)
|
||||||
self.scaleChannel.setEnabled(False)
|
self.scaleChannel.setEnabled(False)
|
||||||
phase = self.currentPhase
|
phase = self.currentPhase
|
||||||
phaseID = self.getPhaseID(phase)
|
phaseID = self.getPhaseID(phase)
|
||||||
@ -2313,7 +2537,7 @@ class PickDlg(QDialog):
|
|||||||
self.disconnectPressEvent()
|
self.disconnectPressEvent()
|
||||||
self.multicompfig.connectEvents()
|
self.multicompfig.connectEvents()
|
||||||
self.renamePhaseAction.setEnabled(True)
|
self.renamePhaseAction.setEnabled(True)
|
||||||
self.compareChannel.setEnabled(True)
|
self.referenceChannel.setEnabled(True)
|
||||||
self.scaleChannel.setEnabled(True)
|
self.scaleChannel.setEnabled(True)
|
||||||
self.connect_pick_delete()
|
self.connect_pick_delete()
|
||||||
self.draw()
|
self.draw()
|
||||||
@ -2386,6 +2610,12 @@ class PickDlg(QDialog):
|
|||||||
def getWFData(self):
|
def getWFData(self):
|
||||||
return self.data
|
return self.data
|
||||||
|
|
||||||
|
def getWFDataComp(self):
|
||||||
|
if self.showCompData:
|
||||||
|
return self.data_compare
|
||||||
|
else:
|
||||||
|
return Stream()
|
||||||
|
|
||||||
def selectWFData(self, channel):
|
def selectWFData(self, channel):
|
||||||
component = channel[-1].upper()
|
component = channel[-1].upper()
|
||||||
wfdata = Stream()
|
wfdata = Stream()
|
||||||
@ -2507,24 +2737,33 @@ class PickDlg(QDialog):
|
|||||||
|
|
||||||
stime = self.getStartTime()
|
stime = self.getStartTime()
|
||||||
|
|
||||||
# copy data for plotting
|
# copy wfdata for plotting
|
||||||
data = self.getWFData().copy()
|
wfdata = self.getWFData().copy()
|
||||||
data = self.getPickPhases(data, phase)
|
wfdata_comp = self.getWFDataComp().copy()
|
||||||
data.normalize()
|
wfdata = self.getPickPhases(wfdata, phase)
|
||||||
if not data:
|
wfdata_comp = self.getPickPhases(wfdata_comp, phase)
|
||||||
|
for wfd in [wfdata, wfdata_comp]:
|
||||||
|
if wfd:
|
||||||
|
wfd.normalize()
|
||||||
|
|
||||||
|
if not wfdata:
|
||||||
QtWidgets.QMessageBox.warning(self, 'No channel to plot',
|
QtWidgets.QMessageBox.warning(self, 'No channel to plot',
|
||||||
'No channel to plot for phase: {}.'.format(phase))
|
'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))
|
||||||
self.leave_picking_mode()
|
self.leave_picking_mode()
|
||||||
return
|
return
|
||||||
|
|
||||||
# filter data and trace on which is picked prior to determination of SNR
|
# filter wfdata and trace on which is picked prior to determination of SNR
|
||||||
filterphase = self.currentFilterPhase()
|
filterphase = self.currentFilterPhase()
|
||||||
if filterphase:
|
if filterphase:
|
||||||
filteroptions = self.getFilterOptions(filterphase).parseFilterOptions()
|
filteroptions = self.getFilterOptions(filterphase).parseFilterOptions()
|
||||||
try:
|
try:
|
||||||
data.detrend('linear')
|
for wfd in [wfdata, wfdata_comp]:
|
||||||
data.filter(**filteroptions)
|
if wfd:
|
||||||
# wfdata.filter(**filteroptions)# MP MP removed filtering of original data
|
wfd.detrend('linear')
|
||||||
|
wfd.filter(**filteroptions)
|
||||||
|
# wfdata.filter(**filteroptions)# MP MP removed filtering of original wfdata
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
self.qmb = QtWidgets.QMessageBox(QtWidgets.QMessageBox.Icon.Information,
|
self.qmb = QtWidgets.QMessageBox(QtWidgets.QMessageBox.Icon.Information,
|
||||||
'Denied',
|
'Denied',
|
||||||
@ -2534,8 +2773,8 @@ class PickDlg(QDialog):
|
|||||||
snr = []
|
snr = []
|
||||||
noiselevels = {}
|
noiselevels = {}
|
||||||
# determine SNR and noiselevel
|
# determine SNR and noiselevel
|
||||||
for trace in data.traces:
|
for trace in wfdata.traces:
|
||||||
st = data.select(channel=trace.stats.channel)
|
st = wfdata.select(channel=trace.stats.channel)
|
||||||
stime_diff = trace.stats.starttime - stime
|
stime_diff = trace.stats.starttime - stime
|
||||||
result = getSNR(st, (noise_win, gap_win, signal_win), ini_pick - stime_diff)
|
result = getSNR(st, (noise_win, gap_win, signal_win), ini_pick - stime_diff)
|
||||||
snr.append(result[0])
|
snr.append(result[0])
|
||||||
@ -2546,23 +2785,25 @@ class PickDlg(QDialog):
|
|||||||
noiselevel = nfac
|
noiselevel = nfac
|
||||||
noiselevels[trace.stats.channel] = noiselevel
|
noiselevels[trace.stats.channel] = noiselevel
|
||||||
|
|
||||||
# prepare plotting of data
|
# prepare plotting of wfdata
|
||||||
for trace in data:
|
for wfd in [wfdata, wfdata_comp]:
|
||||||
t = prep_time_axis(trace.stats.starttime - stime, trace)
|
if wfd:
|
||||||
inoise = getnoisewin(t, ini_pick, noise_win, gap_win)
|
for trace in wfd:
|
||||||
trace = demeanTrace(trace, inoise)
|
t = prep_time_axis(trace.stats.starttime - stime, trace)
|
||||||
# upscale trace data in a way that each trace is vertically zoomed to noiselevel*factor
|
inoise = getnoisewin(t, ini_pick, noise_win, gap_win)
|
||||||
channel = trace.stats.channel
|
trace = demeanTrace(trace, inoise)
|
||||||
noiselevel = noiselevels[channel]
|
# upscale trace wfdata in a way that each trace is vertically zoomed to noiselevel*factor
|
||||||
noiseScaleFactor = self.calcNoiseScaleFactor(noiselevel, zoomfactor=5.)
|
channel = trace.stats.channel
|
||||||
trace.data *= noiseScaleFactor
|
noiselevel = noiselevels[channel]
|
||||||
noiselevels[channel] *= noiseScaleFactor
|
noiseScaleFactor = self.calcNoiseScaleFactor(noiselevel, zoomfactor=5.)
|
||||||
|
trace.data *= noiseScaleFactor
|
||||||
|
noiselevels[channel] *= noiseScaleFactor
|
||||||
|
|
||||||
mean_snr = np.mean(snr)
|
mean_snr = np.mean(snr)
|
||||||
x_res = getResolutionWindow(mean_snr, parameter.get('extent'))
|
x_res = getResolutionWindow(mean_snr, parameter.get('extent'))
|
||||||
|
|
||||||
xlims = [ini_pick - x_res, ini_pick + x_res]
|
xlims = [ini_pick - x_res, ini_pick + x_res]
|
||||||
ylims = list(np.array([-.5, .5]) + [0, len(data) - 1])
|
ylims = list(np.array([-.5, .5]) + [0, len(wfdata) - 1])
|
||||||
|
|
||||||
title = self.getStation() + ' picking mode'
|
title = self.getStation() + ' picking mode'
|
||||||
title += ' | SNR: {}'.format(mean_snr)
|
title += ' | SNR: {}'.format(mean_snr)
|
||||||
@ -2570,9 +2811,10 @@ class PickDlg(QDialog):
|
|||||||
filtops_str = transformFilteroptions2String(filteroptions)
|
filtops_str = transformFilteroptions2String(filteroptions)
|
||||||
title += ' | Filteroptions: {}'.format(filtops_str)
|
title += ' | Filteroptions: {}'.format(filtops_str)
|
||||||
|
|
||||||
plot_additional = bool(self.compareChannel.currentText())
|
plot_additional = bool(self.referenceChannel.currentText())
|
||||||
additional_channel = self.compareChannel.currentText()
|
additional_channel = self.referenceChannel.currentText()
|
||||||
self.multicompfig.plotWFData(wfdata=data,
|
self.multicompfig.plotWFData(wfdata=wfdata,
|
||||||
|
wfdata_compare=wfdata_comp,
|
||||||
title=title,
|
title=title,
|
||||||
zoomx=xlims,
|
zoomx=xlims,
|
||||||
zoomy=ylims,
|
zoomy=ylims,
|
||||||
@ -2653,7 +2895,7 @@ class PickDlg(QDialog):
|
|||||||
minFMSNR = parameter.get('minFMSNR')
|
minFMSNR = parameter.get('minFMSNR')
|
||||||
quality = get_quality_class(spe, parameter.get('timeerrorsP'))
|
quality = get_quality_class(spe, parameter.get('timeerrorsP'))
|
||||||
if quality <= minFMweight and snr >= minFMSNR:
|
if quality <= minFMweight and snr >= minFMSNR:
|
||||||
FM = fmpicker(self.getWFData().select(channel=channel), wfdata, parameter.get('fmpickwin'),
|
FM = fmpicker(self.getWFData().select(channel=channel).copy(), wfdata.copy(), parameter.get('fmpickwin'),
|
||||||
pick - stime_diff)
|
pick - stime_diff)
|
||||||
|
|
||||||
# save pick times for actual phase
|
# save pick times for actual phase
|
||||||
@ -2963,7 +3205,8 @@ class PickDlg(QDialog):
|
|||||||
self.cur_xlim = self.multicompfig.axes[0].get_xlim()
|
self.cur_xlim = self.multicompfig.axes[0].get_xlim()
|
||||||
self.cur_ylim = self.multicompfig.axes[0].get_ylim()
|
self.cur_ylim = self.multicompfig.axes[0].get_ylim()
|
||||||
# self.multicompfig.updateCurrentLimits()
|
# self.multicompfig.updateCurrentLimits()
|
||||||
data = self.getWFData().copy()
|
wfdata = self.getWFData().copy()
|
||||||
|
wfdata_comp = self.getWFDataComp().copy()
|
||||||
title = self.getStation()
|
title = self.getStation()
|
||||||
if filter:
|
if filter:
|
||||||
filtoptions = None
|
filtoptions = None
|
||||||
@ -2971,19 +3214,22 @@ class PickDlg(QDialog):
|
|||||||
filtoptions = self.getFilterOptions(self.getPhaseID(phase), gui_filter=True).parseFilterOptions()
|
filtoptions = self.getFilterOptions(self.getPhaseID(phase), gui_filter=True).parseFilterOptions()
|
||||||
|
|
||||||
if filtoptions is not None:
|
if filtoptions is not None:
|
||||||
data.detrend('linear')
|
for wfd in [wfdata, wfdata_comp]:
|
||||||
data.taper(0.02, type='cosine')
|
if wfd:
|
||||||
data.filter(**filtoptions)
|
wfd.detrend('linear')
|
||||||
|
wfd.taper(0.02, type='cosine')
|
||||||
|
wfd.filter(**filtoptions)
|
||||||
filtops_str = transformFilteroptions2String(filtoptions)
|
filtops_str = transformFilteroptions2String(filtoptions)
|
||||||
title += ' | Filteroptions: {}'.format(filtops_str)
|
title += ' | Filteroptions: {}'.format(filtops_str)
|
||||||
|
|
||||||
if self.wftype is not None:
|
if self.wftype is not None:
|
||||||
title += ' | ({})'.format(self.wftype)
|
title += ' | ({})'.format(self.wftype)
|
||||||
|
|
||||||
plot_additional = bool(self.compareChannel.currentText())
|
plot_additional = bool(self.referenceChannel.currentText())
|
||||||
additional_channel = self.compareChannel.currentText()
|
additional_channel = self.referenceChannel.currentText()
|
||||||
scale_channel = self.scaleChannel.currentText()
|
scale_channel = self.scaleChannel.currentText()
|
||||||
self.multicompfig.plotWFData(wfdata=data, title=title,
|
self.multicompfig.plotWFData(wfdata=wfdata, wfdata_compare=wfdata_comp,
|
||||||
|
title=title,
|
||||||
zoomx=self.getXLims(),
|
zoomx=self.getXLims(),
|
||||||
zoomy=self.getYLims(),
|
zoomy=self.getYLims(),
|
||||||
plot_additional=plot_additional,
|
plot_additional=plot_additional,
|
||||||
@ -3056,6 +3302,9 @@ class PickDlg(QDialog):
|
|||||||
self.resetZoom()
|
self.resetZoom()
|
||||||
self.refreshPlot()
|
self.refreshPlot()
|
||||||
|
|
||||||
|
def switchCompData(self):
|
||||||
|
self.showCompData = self.compareCB.isChecked()
|
||||||
|
|
||||||
def refreshPlot(self):
|
def refreshPlot(self):
|
||||||
if self.autoFilterAction.isChecked():
|
if self.autoFilterAction.isChecked():
|
||||||
self.filterActionP.setChecked(False)
|
self.filterActionP.setChecked(False)
|
||||||
@ -3566,8 +3815,9 @@ class TuneAutopicker(QWidget):
|
|||||||
# wfdat = remove_underscores(wfdat)
|
# wfdat = remove_underscores(wfdat)
|
||||||
# rotate misaligned stations to ZNE
|
# rotate misaligned stations to ZNE
|
||||||
# check for gaps and doubled channels
|
# check for gaps and doubled channels
|
||||||
wfdat, gaps = merge_stream(wfdat)
|
wfdat, _ = check_for_gaps_and_merge(wfdat)
|
||||||
# check4gaps(wfdat)
|
# check for nans
|
||||||
|
check_for_nan(wfdat)
|
||||||
check4doubled(wfdat)
|
check4doubled(wfdat)
|
||||||
wfdat = check4rotated(wfdat, self.parent().metadata, verbosity=0)
|
wfdat = check4rotated(wfdat, self.parent().metadata, verbosity=0)
|
||||||
# trim station components to same start value
|
# trim station components to same start value
|
||||||
@ -3611,14 +3861,14 @@ class TuneAutopicker(QWidget):
|
|||||||
self.listWidget.scrollToBottom()
|
self.listWidget.scrollToBottom()
|
||||||
|
|
||||||
def get_current_event(self):
|
def get_current_event(self):
|
||||||
path = self.eventBox.currentText()
|
path = self.get_current_event_fp()
|
||||||
return self.parent().project.getEventFromPath(path)
|
return self.parent().project.getEventFromPath(path)
|
||||||
|
|
||||||
def get_current_event_name(self):
|
def get_current_event_name(self):
|
||||||
return self.eventBox.currentText().split('/')[-1].split('*')[0]
|
return self.eventBox.currentText().split('/')[-1].rstrip('*')
|
||||||
|
|
||||||
def get_current_event_fp(self):
|
def get_current_event_fp(self):
|
||||||
return self.eventBox.currentText().split('*')[0]
|
return self.eventBox.currentText().rstrip('*')
|
||||||
|
|
||||||
def get_current_event_picks(self, station):
|
def get_current_event_picks(self, station):
|
||||||
event = self.get_current_event()
|
event = self.get_current_event()
|
||||||
@ -3666,11 +3916,13 @@ class TuneAutopicker(QWidget):
|
|||||||
location = None
|
location = None
|
||||||
|
|
||||||
wfdata = self.data.getWFData()
|
wfdata = self.data.getWFData()
|
||||||
|
wfdata_comp = self.data.getWFDataComp()
|
||||||
metadata = self.parent().metadata
|
metadata = self.parent().metadata
|
||||||
event = self.get_current_event()
|
event = self.get_current_event()
|
||||||
filteroptions = self.parent().filteroptions
|
filteroptions = self.parent().filteroptions
|
||||||
wftype = self.wftype if self.obspy_dmt else ''
|
wftype = self.wftype if self.obspy_dmt else ''
|
||||||
self.pickDlg = PickDlg(self.parent(), data=wfdata.select(station=station).copy(),
|
self.pickDlg = PickDlg(self.parent(), data=wfdata.select(station=station).copy(),
|
||||||
|
data_comp=wfdata_comp.select(station=station).copy(),
|
||||||
station=station, network=network,
|
station=station, network=network,
|
||||||
location=location, parameter=self.parameter,
|
location=location, parameter=self.parameter,
|
||||||
picks=self.get_current_event_picks(station),
|
picks=self.get_current_event_picks(station),
|
||||||
@ -3722,6 +3974,7 @@ class TuneAutopicker(QWidget):
|
|||||||
st = self.data.getWFData()
|
st = self.data.getWFData()
|
||||||
tr = st.select(station=self.get_current_station())[0]
|
tr = st.select(station=self.get_current_station())[0]
|
||||||
starttime = tr.stats.starttime
|
starttime = tr.stats.starttime
|
||||||
|
# create two lists with figure names and subindices (for subplots) to get the correct axes
|
||||||
p_axes = [
|
p_axes = [
|
||||||
('mainFig', 0),
|
('mainFig', 0),
|
||||||
('aicFig', 0),
|
('aicFig', 0),
|
||||||
@ -3754,7 +4007,7 @@ class TuneAutopicker(QWidget):
|
|||||||
self.plot_manual_pick_to_ax(ax=ax, picks=picks, phase='S',
|
self.plot_manual_pick_to_ax(ax=ax, picks=picks, phase='S',
|
||||||
starttime=starttime, quality=qualitySpick)
|
starttime=starttime, quality=qualitySpick)
|
||||||
for canvas in self.parent().canvas_dict.values():
|
for canvas in self.parent().canvas_dict.values():
|
||||||
canvas.draw()
|
canvas.draw_idle()
|
||||||
|
|
||||||
def plot_manual_pick_to_ax(self, ax, picks, phase, starttime, quality):
|
def plot_manual_pick_to_ax(self, ax, picks, phase, starttime, quality):
|
||||||
mpp = picks[phase]['mpp'] - starttime
|
mpp = picks[phase]['mpp'] - starttime
|
||||||
@ -5666,7 +5919,7 @@ class ChooseWaveFormWindow(QWidget):
|
|||||||
#self.currentSpectro = self.traces[
|
#self.currentSpectro = self.traces[
|
||||||
# self.chooseBoxTraces.currentText()[3:]][self.chooseBoxComponent.currentText()].spectrogram(show=False, title=t)
|
# self.chooseBoxTraces.currentText()[3:]][self.chooseBoxComponent.currentText()].spectrogram(show=False, title=t)
|
||||||
#self.currentSpectro.show()
|
#self.currentSpectro.show()
|
||||||
applyFFT()
|
self.applyFFT()
|
||||||
|
|
||||||
def applyFFT(self, trace):
|
def applyFFT(self, trace):
|
||||||
tra = self.traces[self.chooseBoxTraces.currentText()[3:]]['Z']
|
tra = self.traces[self.chooseBoxTraces.currentText()[3:]]['Z']
|
||||||
|
Loading…
Reference in New Issue
Block a user