Author SHA1 Message Date
marcel 6cce05b035 Merge branch 'feature/dae' into develop
# Conflicts:
#	pylot/core/io/data.py
#	pylot/core/util/widgets.py
2024-06-12 16:19:21 +02:00
marcel 7326f061e5 [minor] inform if station coordinates were not found in metadata 2024-06-12 16:11:53 +02:00
marcel 1a18401fe3 [bugfix] added missing Parameter object in call for picksdict_from_picks 2024-06-12 13:44:02 +02:00
marcel ec930dbc12 [minor] removed unneeded imports 2024-06-07 15:56:05 +02:00
marcel b991f771af [bugfix] removing redundancy and wrong bullsh.. try-except code 2024-06-07 15:04:16 +02:00
marcel 2c3b1876ab [minor] switch default cmap for array_map to 'viridis' 2024-06-07 14:34:27 +02:00
marcel 0acd23d4d0 [update] further improved Pickfile selection dialog, now providing methods "overwrite" or "merge" 2024-06-07 14:32:57 +02:00
marcel f349c8bc7e [update] improve pickfile selection, give the ability to select only specific files 2024-06-07 13:09:34 +02:00
marcel 6688ef845d [bugfix] re-implement ability of get_bool to return unidentifiable input 2024-06-07 13:08:51 +02:00
marcel 5b18e9ab71 [merge] merge branch 'improve-util-utils' of pull request #35 into develop 2024-06-07 10:29:39 +02:00
marcel c79e886d77 [minor] update default shell script for SGE 2024-06-06 15:55:47 +02:00
marcel 76f2d5d972 [update] improve SearchForFileExtensionDialog now proposing available file endings 2024-06-06 15:54:36 +02:00
marcel 2d08fd029d [hotfix] datetime formatting caused error when time not set 2024-06-06 13:55:11 +02:00
marcel 8f22d438d3 [update] changed eventbox overview to show P and S onsets separately 2024-06-05 16:18:25 +02:00
marcel 93b7de3baa [update] raising PickingFailedException when CF cannot be calculated due to missing signal (too short waveform)
[update] raising PickingFailedException when CF cannot be calculated due to missing signal (too short waveform)
2024-06-05 14:31:09 +02:00
marcel 05642e775b [minor] some tweaks (convenience)
[update] raising PickingFailedException when CF cannot be calculated due to missing signal (too short waveform)
2024-06-05 14:31:07 +02:00
marcel 47205ca493 [update] improved calculation of smoothed AIC. Old code always created an artificial value and a np.nan at the array start 2024-06-05 14:31:07 +02:00
marcel 5c7f0b56eb [update] improved SearchFileByExtensionDialog widget 2024-06-05 14:19:17 +02:00
marcel c574031931 [bugfix] the implementation approach of STA/LTA inside characteristic function calculation (skewness/kurtosis) corrupted the old, working code due to a mistake in the logic 2024-06-05 14:17:57 +02:00
marcel e1a0fde619 [update] improved array map to identify and display other phase names than P / S 2024-05-29 11:43:31 +02:00
marcel 48d196df11 [update] improved SearchFileByExtensionDialog widget (table, auto refresh) 2024-05-29 11:42:10 +02:00
marcel 6cc9cb4a96 [minor] reduced maxtasksperchild for multiprocessing 2024-05-29 11:40:34 +02:00
marcel 5eab686445 [bugfix] accidentally removed return value 2024-05-24 16:10:32 +02:00
marcel b12e7937ac [update] added a widget for loading pick files that lists available files depending on chosen filemask 2024-05-22 10:58:07 +02:00
marcel 78f2dbcab2 [update] added check for nan values in waveforms which crashed obspy filter routines
[minor] some tweaks and optimisations
2024-04-30 15:48:54 +02:00
marcel 8b95c7a0fe [update] changed sorting of traces overview if all station names are numeric (e.g. active experiments) 2024-04-09 16:02:31 +02:00
marcel 65dbaad446 [update] adding possibility to display other waveform data (e.g. denoised/synthetic) together with genuine data for comparison 2024-03-22 17:12:04 +01:00
marcel 5b97d51517 [minor] mpl.figure.canvas.draw -> draw_idle 2024-03-22 17:12:04 +01:00
marcel f03ace75e7 [bugfix] QWidget.show() killed figure axis dimensions creating unexpected error of fig.aspect=0 when creating colorbar inset_axes in Python 3.11 2024-03-22 17:10:04 +01:00
marcel 9c78471d20 [bugfix] header resize method renamed in QT5 2024-03-22 15:34:05 +01:00
marcel 09d2fb1022 [bugfix] pt2 of fmpicker fix, make sure to also copy stream in autoPyLoT
closes #24
2023-08-24 12:55:30 +02:00
marcel 3cae6d3a78 [bugfix] use copies of wfdata when calling fmpicker to prevent modification of actual data used inside GUI 2023-08-24 11:28:30 +02:00
marcel 2e85d083a3 [bugfix] do not call calcsourcespec if incidence angle is outside bounds (for whatever reason) 2023-08-24 11:27:30 +02:00
marcel ba4e6cfe50 [bugfix] bin directory + /bin creates "/bin/bin". Also it is not taken care of os compatibility and also compatibility with existing code (line 86ff was useless after recent change in line 85) 2023-08-23 14:48:21 +02:00
marcel 1f16d01648 [minor] give more precise user warning if no pick channel was selected 2023-08-23 09:38:16 +02:00
marcel 3069e7d526 [minor] commented - possibly unnecessary - line of code that created an error when using old metadata Parser 2023-08-22 15:53:49 +02:00
marcel a9aeb7aaa3 [bugfix] set simple phase hint (P or S) 2023-08-22 15:53:49 +02:00
marcel b9adb182ad [bugfix] could not handle asterisk-marked events when opening tune-autopicker 2023-08-22 15:53:49 +02:00
marcel a823eb2440 [workaround] using explicit Exception definition without a special handling does not make sense. Function broke on other errors in polyfit. Still might need fixes in the two lines above the "except" block(s). 2023-08-22 15:53:49 +02:00
marcel 486e3dc9c3 Merge pull request 'Disabled button in case flag is false' (#31) from disable-show-log-widget into develop
Reviewed-on: #31
2023-08-22 12:05:33 +02:00
marcel 8d356050d7 [update] corrected original authors of PILOT 2023-08-22 12:01:51 +02:00
jeldrik 43cab3767f [Bugfix] fixxed wrong check for taupymodel 2023-06-27 08:04:00 +02:00
sebastianw b3fdbc811e Merge branch 'develop' into improve-util-utils 2023-06-23 09:37:54 +02:00
jeldrik a1f6c5ffca Bugfixxes, spectogram tab wip 2023-06-14 13:11:54 +02:00
jeldrik e4e7afa996 Minor changes to adjust to python 3. Temporary Fix for file exporting not working properly. WIP spectrogram view. 2023-04-27 10:24:55 +02:00
sebastianw 0634d24814 fix: disabled button in case flag is false
The button was not disabled in case the flag variable was false. The get_Bool function was renamed and improved to also work in case in the input variable is of type int or float.

Additionally, the environment file was corrected to also work for macOS installations with ARM architecture.
2023-04-06 16:40:20 +02:00
jeldrik 43c2b97b3d Small changes 2023-01-24 11:45:12 +01:00
19 changed files with 860 additions and 371 deletions
+143 -75
View File
@@ -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, 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
@@ -496,7 +494,6 @@ class MainWindow(QMainWindow):
icon=eventlist_xml_icon, icon=eventlist_xml_icon,
tip='Create an Eventlist from a XML File') tip='Create an Eventlist from a XML File')
self.eventlist_xml_action.setEnabled(False) self.eventlist_xml_action.setEnabled(False)
printAction = self.createAction(self, "&Print event ...", printAction = self.createAction(self, "&Print event ...",
self.show_event_information, QKeySequence.Print, self.show_event_information, QKeySequence.Print,
print_icon, print_icon,
@@ -509,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)
@@ -698,14 +697,17 @@ class MainWindow(QMainWindow):
wf_tab = QtWidgets.QWidget(self) wf_tab = QtWidgets.QWidget(self)
array_tab = QtWidgets.QWidget(self) array_tab = QtWidgets.QWidget(self)
events_tab = QtWidgets.QWidget(self) events_tab = QtWidgets.QWidget(self)
spectro_tab = QtWidgets.QWidget(self)
# init main widgets layouts # init main widgets layouts
self.wf_layout = QtWidgets.QVBoxLayout() self.wf_layout = QtWidgets.QVBoxLayout()
self.array_layout = QtWidgets.QVBoxLayout() self.array_layout = QtWidgets.QVBoxLayout()
self.events_layout = QtWidgets.QVBoxLayout() self.events_layout = QtWidgets.QVBoxLayout()
self.spectro_layout = QtWidgets.QVBoxLayout()
wf_tab.setLayout(self.wf_layout) wf_tab.setLayout(self.wf_layout)
array_tab.setLayout(self.array_layout) array_tab.setLayout(self.array_layout)
events_tab.setLayout(self.events_layout) events_tab.setLayout(self.events_layout)
spectro_tab.setLayout(self.spectro_layout)
# tighten up layouts inside tabs # tighten up layouts inside tabs
for layout in [self.wf_layout, self.array_layout, self.events_layout]: for layout in [self.wf_layout, self.array_layout, self.events_layout]:
@@ -716,12 +718,14 @@ class MainWindow(QMainWindow):
self.tabs.addTab(wf_tab, 'Waveform Plot') self.tabs.addTab(wf_tab, 'Waveform Plot')
self.tabs.addTab(array_tab, 'Array Map') self.tabs.addTab(array_tab, 'Array Map')
self.tabs.addTab(events_tab, 'Eventlist') self.tabs.addTab(events_tab, 'Eventlist')
self.tabs.addTab(spectro_tab, 'Spectro')
self.wf_layout.addWidget(self.no_data_label) self.wf_layout.addWidget(self.no_data_label)
self.wf_layout.addWidget(self.wf_scroll_area) self.wf_layout.addWidget(self.wf_scroll_area)
self.wf_scroll_area.setWidgetResizable(True) self.wf_scroll_area.setWidgetResizable(True)
self.init_array_tab() self.init_array_tab()
self.init_event_table() self.init_event_table()
self.init_spectro_tab()
self.tabs.setCurrentIndex(0) self.tabs.setCurrentIndex(0)
self.eventLabel = QLabel() self.eventLabel = QLabel()
@@ -734,16 +738,13 @@ class MainWindow(QMainWindow):
_widget.setLayout(self._main_layout) _widget.setLayout(self._main_layout)
_widget.showFullScreen() _widget.showFullScreen()
self.logwidget = LogWidget(parent=None)
if use_logwidget: if use_logwidget:
self.logwidget = LogWidget(parent=None)
self.logwidget.show() self.logwidget.show()
self.stdout = self.logwidget.stdout self.stdout = self.logwidget.stdout
self.stderr = self.logwidget.stderr self.stderr = self.logwidget.stderr
# Not sure why but the lines above kept messing with the Ouput even with use_logwidget disabled
sys.stdout = self.stdout
sys.stderr = self.stderr
self.setCentralWidget(_widget) self.setCentralWidget(_widget)
# Need to store PickQualities Window somewhere so it doesnt disappear # Need to store PickQualities Window somewhere so it doesnt disappear
@@ -1003,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:
@@ -1022,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:
@@ -1037,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(
@@ -1127,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')
@@ -1381,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()
@@ -1401,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()
@@ -1454,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:
@@ -1678,20 +1694,20 @@ class MainWindow(QMainWindow):
def pickQualities(self): def pickQualities(self):
path = self.get_current_event_path() path = self.get_current_event_path()
(_, _, plot) = getQualitiesfromxml(path, self._inputs.get('timeerrorsP'), self._inputs.get('timeerrorsS'),plotflag=1) (_, plot) = getQualitiesfromxml(path, self._inputs.get('timeerrorsP'), self._inputs.get('timeerrorsS'),plotflag=1)
self.pickQualitiesWindow = PickQualitiesFromXml(figure=plot, path=self.get_current_event_path(),inputVar=self._inputs) self.pickQualitiesWindow = PickQualitiesFromXml(figure=plot, path=self.get_current_event_path(),inputVar=self._inputs)
self.pickQualitiesWindow.showUI() self.pickQualitiesWindow.showUI()
return return
# WIP JG # WIP JG
def eventlistXml2(self): def eventlistXml(self):
path = self._inputs['rootpath'] + '/' + self._inputs['datapath'] + '/' + self._inputs['database'] path = self._inputs['rootpath'] + '/' + self._inputs['datapath'] + '/' + self._inputs['database']
outpath = self.project.location[:self.project.location.rfind('/')] outpath = self.project.location[:self.project.location.rfind('/')]
geteventlistfromxml(path, outpath) geteventlistfromxml(path, outpath)
return return
# WIP JG # WIP JG
def eventlistXml(self): def spectogramView(self):
global test global test
stations = [] stations = []
names = [] names = []
@@ -1706,12 +1722,25 @@ class MainWindow(QMainWindow):
for tr in self.get_data().wfdata.select(component=ch).traces: for tr in self.get_data().wfdata.select(component=ch).traces:
traces[tr.stats.station][ch] = tr traces[tr.stats.station][ch] = tr
names.sort() names.sort()
a = self.get_current_event() a = self.get_current_event()
test = ChooseWaveFormWindow(WaveForms=names, traces=traces, stream=self.get_data())
#self.get_data().wfdata.spectrogram() print (self.get_data().wfdata.traces[0])
test.show()
test = SpectrogramTab(traces, self.get_data().wfdata)
height = self.tabs.widget(0).height()
width = self.tabs.widget(0).width()
self.tabs.setCurrentIndex(3)
figCanvas = test.makeSpecFig(direction=self.dispComponent, height = height, width = width, parent = self.tabs.widget)
return figCanvas
#self.spectro_layout.addWidget()
# self.get_data().wfdata.spectrogram()
# self.tabs.addTab(figCanvas, 'Spectrogram')
# self.tabs[3] = figCanvas
# self.refreshTabs()
# test.show()
def compareMulti(self): def compareMulti(self):
if not self.compareoptions: if not self.compareoptions:
@@ -1871,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()
@@ -1898,6 +1928,12 @@ class MainWindow(QMainWindow):
self.newWF(plot=False) self.newWF(plot=False)
self.update_obspy_dmt() self.update_obspy_dmt()
self.refresh_array_map() self.refresh_array_map()
if self.tabs.currentIndex() == 3:
if self.spectroWidget != None:
self.spectro_layout.removeWidget(self.spectroWidget)
newSpectroWidget = self.spectogramView()
self.spectro_layout.addWidget(newSpectroWidget)
self.spectroWidget = newSpectroWidget
def newWF(self, event=None, plot=True): def newWF(self, event=None, plot=True):
''' '''
@@ -1928,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):
''' '''
@@ -1949,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?')
@@ -1965,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,
@@ -1973,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):
@@ -1981,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():
@@ -2104,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)
@@ -2155,6 +2200,7 @@ class MainWindow(QMainWindow):
self.locateEventAction.setEnabled(True) self.locateEventAction.setEnabled(True)
self.qualities_action.setEnabled(True) self.qualities_action.setEnabled(True)
self.eventlist_xml_action.setEnabled(True) self.eventlist_xml_action.setEnabled(True)
if True in self.comparable.values(): if True in self.comparable.values():
self.compare_action.setEnabled(True) self.compare_action.setEnabled(True)
self.draw() self.draw()
@@ -2262,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:
@@ -2277,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:
@@ -2574,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()
@@ -2964,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:
@@ -3128,7 +3183,7 @@ class MainWindow(QMainWindow):
phasefile = os.path.join(obsdir, filename + '.obs') phasefile = os.path.join(obsdir, filename + '.obs')
lt.modify_inputs(ctrfile, locroot, filename, phasefile, ttt) lt.modify_inputs(ctrfile, locroot, filename, phasefile, ttt)
try: try:
lt.locate(ctrfile, self.obspy_dmt) lt.locate(ctrfile, self._inputs)
except RuntimeError as e: except RuntimeError as e:
print(e.message) print(e.message)
# finally: # finally:
@@ -3182,7 +3237,7 @@ class MainWindow(QMainWindow):
''' '''
if checked: pass # dummy argument for QAction trigger signal if checked: pass # dummy argument for QAction trigger signal
self.tabs.setCurrentIndex(1) self.tabs.setCurrentIndex(1)
# if there is no metadata (invetories is an empty list), just initialize the default empty tab # if there is no metadata (inventories is an empty list), just initialize the default empty tab
if not self.metadata.inventories: if not self.metadata.inventories:
self.init_array_tab() self.init_array_tab()
return return
@@ -3204,6 +3259,15 @@ class MainWindow(QMainWindow):
self.tabs.setCurrentIndex(index) self.tabs.setCurrentIndex(index)
self.refresh_array_map() self.refresh_array_map()
def init_spectro_tab(self):
'''
Init spectrogram tab with currently selected event.
'''
self.spectroWidget = None
#self.spectro_layout.addWidget( self.spectogramView() )
pass
def array_map_thread(self): def array_map_thread(self):
''' '''
Start modal thread to init the array_map object. Start modal thread to init the array_map object.
@@ -3426,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)
@@ -3600,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:
@@ -3653,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))
@@ -3774,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()
+1 -1
View File
@@ -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
+3 -7
View File
@@ -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)))
+5 -3
View File
@@ -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
+2 -2
View File
@@ -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
+4
View File
@@ -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)
+65 -29
View File
@@ -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:
@@ -260,7 +261,6 @@ class Data(object):
can be a str or a list of strings of ['manual', 'auto', 'origin', 'magnitude'] can be a str or a list of strings of ['manual', 'auto', 'origin', 'magnitude']
""" """
from pylot.core.util.defaults import OUTPUTFORMATS from pylot.core.util.defaults import OUTPUTFORMATS
if not type(fcheck) == list: if not type(fcheck) == list:
fcheck = [fcheck] fcheck = [fcheck]
@@ -321,35 +321,61 @@ class Data(object):
if lendiff != 0: if lendiff != 0:
print("Manual as well as automatic picks available. Prefered the {} manual ones!".format(lendiff)) print("Manual as well as automatic picks available. Prefered the {} manual ones!".format(lendiff))
no_uncertainties_p = []
no_uncertainties_s = []
if upperErrors: if upperErrors:
# check for pick uncertainties exceeding adjusted upper errors # check for pick uncertainties exceeding adjusted upper errors
# Picks with larger uncertainties will not be saved in output file! # Picks with larger uncertainties will not be saved in output file!
for j in range(len(picks)): for j in range(len(picks)):
for i in range(len(picks_copy)): for i in range(len(picks_copy)):
if picks_copy[i].phase_hint[0] == 'P': if picks_copy[i].phase_hint[0] == 'P':
if (picks_copy[i].time_errors['upper_uncertainty'] >= upperErrors[0]) or \ # Skipping pick if no upper_uncertainty is found and warning user
(picks_copy[i].time_errors['uncertainty'] is None): if picks_copy[i].time_errors['upper_uncertainty'] is None:
#print("{1} P-Pick of station {0} does not have upper_uncertainty and cant be checked".format(
# picks_copy[i].waveform_id.station_code,
# picks_copy[i].method_id))
if not picks_copy[i].waveform_id.station_code in no_uncertainties_p:
no_uncertainties_p.append(picks_copy[i].waveform_id.station_code)
continue
#print ("checking for upper_uncertainty")
if (picks_copy[i].time_errors['uncertainty'] is None) or \
(picks_copy[i].time_errors['upper_uncertainty'] >= upperErrors[0]):
print("Uncertainty exceeds or equal adjusted upper time error!") print("Uncertainty exceeds or equal adjusted upper time error!")
print("Adjusted uncertainty: {}".format(upperErrors[0])) print("Adjusted uncertainty: {}".format(upperErrors[0]))
print("Pick uncertainty: {}".format(picks_copy[i].time_errors['uncertainty'])) print("Pick uncertainty: {}".format(picks_copy[i].time_errors['uncertainty']))
print("{1} P-Pick of station {0} will not be saved in outputfile".format( print("{1} P-Pick of station {0} will not be saved in outputfile".format(
picks_copy[i].waveform_id.station_code, picks_copy[i].waveform_id.station_code,
picks_copy[i].method_id)) picks_copy[i].method_id))
print("#")
del picks_copy[i] del picks_copy[i]
break break
if picks_copy[i].phase_hint[0] == 'S': if picks_copy[i].phase_hint[0] == 'S':
if (picks_copy[i].time_errors['upper_uncertainty'] >= upperErrors[1]) or \
(picks_copy[i].time_errors['uncertainty'] is None): # Skipping pick if no upper_uncertainty is found and warning user
if picks_copy[i].time_errors['upper_uncertainty'] is None:
#print("{1} S-Pick of station {0} does not have upper_uncertainty and cant be checked".format(
#picks_copy[i].waveform_id.station_code,
#picks_copy[i].method_id))
if not picks_copy[i].waveform_id.station_code in no_uncertainties_s:
no_uncertainties_s.append(picks_copy[i].waveform_id.station_code)
continue
if (picks_copy[i].time_errors['uncertainty'] is None) or \
(picks_copy[i].time_errors['upper_uncertainty'] >= upperErrors[1]):
print("Uncertainty exceeds or equal adjusted upper time error!") print("Uncertainty exceeds or equal adjusted upper time error!")
print("Adjusted uncertainty: {}".format(upperErrors[1])) print("Adjusted uncertainty: {}".format(upperErrors[1]))
print("Pick uncertainty: {}".format(picks_copy[i].time_errors['uncertainty'])) print("Pick uncertainty: {}".format(picks_copy[i].time_errors['uncertainty']))
print("{1} S-Pick of station {0} will not be saved in outputfile".format( print("{1} S-Pick of station {0} will not be saved in outputfile".format(
picks_copy[i].waveform_id.station_code, picks_copy[i].waveform_id.station_code,
picks_copy[i].method_id)) picks_copy[i].method_id))
print("#")
del picks_copy[i] del picks_copy[i]
break break
for s in no_uncertainties_p:
print("P-Pick of station {0} does not have upper_uncertainty and cant be checked".format(s))
for s in no_uncertainties_s:
print("S-Pick of station {0} does not have upper_uncertainty and cant be checked".format(s))
if fnext == '.obs': if fnext == '.obs':
try: try:
@@ -383,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
@@ -426,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
@@ -457,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
@@ -466,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)
@@ -478,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
@@ -491,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:
@@ -519,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):
""" """
+28 -30
View File
@@ -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 ''
@@ -517,7 +516,7 @@ def writephases(arrivals, fformat, filename, parameter=None, eventinfo=None):
arrivals = chooseArrivals(arrivals) # MP MP what is chooseArrivals? It is not defined anywhere arrivals = chooseArrivals(arrivals) # MP MP what is chooseArrivals? It is not defined anywhere
for key in arrivals: for key in arrivals:
# P onsets # P onsets
if arrivals[key].has_key('P'): if 'P' in arrivals[key]:
try: try:
fm = arrivals[key]['P']['fm'] fm = arrivals[key]['P']['fm']
except KeyError as e: except KeyError as e:
@@ -551,7 +550,7 @@ def writephases(arrivals, fformat, filename, parameter=None, eventinfo=None):
ss_ms, ss_ms,
pweight)) pweight))
# S onsets # S onsets
if arrivals[key].has_key('S') and arrivals[key]['S']['mpp'] is not None: if 'S' in arrivals[key] and arrivals[key]['S']['mpp'] is not None:
fm = '?' fm = '?'
onset = arrivals[key]['S']['mpp'] onset = arrivals[key]['S']['mpp']
year = onset.year year = onset.year
@@ -670,7 +669,7 @@ def writephases(arrivals, fformat, filename, parameter=None, eventinfo=None):
arrivals = chooseArrivals(arrivals) # MP MP what is chooseArrivals? It is not defined anywhere arrivals = chooseArrivals(arrivals) # MP MP what is chooseArrivals? It is not defined anywhere
for key in arrivals: for key in arrivals:
# P onsets # P onsets
if arrivals[key].has_key('P') and arrivals[key]['P']['mpp'] is not None: if 'P' in arrivals[key] and arrivals[key]['P']['mpp'] is not None:
if arrivals[key]['P']['weight'] < 4: if arrivals[key]['P']['weight'] < 4:
Ponset = arrivals[key]['P']['mpp'] Ponset = arrivals[key]['P']['mpp']
pyear = Ponset.year pyear = Ponset.year
@@ -699,7 +698,7 @@ def writephases(arrivals, fformat, filename, parameter=None, eventinfo=None):
fid.write('%-5s P1 %4.0f %02d %02d %02d %02d %05.02f %5.3f -999. 0.00 -999. 0.00\n' fid.write('%-5s P1 %4.0f %02d %02d %02d %02d %05.02f %5.3f -999. 0.00 -999. 0.00\n'
% (key, pyear, pmonth, pday, phh, pmm, Pss, pstd)) % (key, pyear, pmonth, pday, phh, pmm, Pss, pstd))
# S onsets # S onsets
if arrivals[key].has_key('S') and arrivals[key]['S']['mpp'] is not None: if 'S' in arrivals[key] and arrivals[key]['S']['mpp'] is not None:
if arrivals[key]['S']['weight'] < 4: if arrivals[key]['S']['weight'] < 4:
Sonset = arrivals[key]['S']['mpp'] Sonset = arrivals[key]['S']['mpp']
syear = Sonset.year syear = Sonset.year
@@ -768,7 +767,7 @@ def writephases(arrivals, fformat, filename, parameter=None, eventinfo=None):
usedarrivals = chooseArrivals(arrivals) usedarrivals = chooseArrivals(arrivals)
for key in usedarrivals: for key in usedarrivals:
# P onsets # P onsets
if usedarrivals[key].has_key('P'): if 'P' in usedarrivals[key]:
if usedarrivals[key]['P']['weight'] < 4: if usedarrivals[key]['P']['weight'] < 4:
n += 1 n += 1
stat = key stat = key
@@ -782,7 +781,7 @@ def writephases(arrivals, fformat, filename, parameter=None, eventinfo=None):
else: else:
fid.write('%-4sP%d%6.2f\n' % (stat, Pweight, Prt)) fid.write('%-4sP%d%6.2f\n' % (stat, Pweight, Prt))
# S onsets # S onsets
if usedarrivals[key].has_key('S'): if 'S' in usedarrivals[key]:
if usedarrivals[key]['S']['weight'] < 4: if usedarrivals[key]['S']['weight'] < 4:
n += 1 n += 1
stat = key stat = key
@@ -828,13 +827,13 @@ def writephases(arrivals, fformat, filename, parameter=None, eventinfo=None):
# prefer manual picks # prefer manual picks
usedarrivals = chooseArrivals(arrivals) usedarrivals = chooseArrivals(arrivals)
for key in usedarrivals: for key in usedarrivals:
if usedarrivals[key].has_key('P'): if 'P' in usedarrivals[key]:
# P onsets # P onsets
if usedarrivals[key]['P']['weight'] < 4: if usedarrivals[key]['P']['weight'] < 4:
Ponset = usedarrivals[key]['P']['mpp'] Ponset = usedarrivals[key]['P']['mpp']
Prt = Ponset - stime # onset time relative to source time Prt = Ponset - stime # onset time relative to source time
fid.write('%s %6.3f 1 P\n' % (key, Prt)) fid.write('%s %6.3f 1 P\n' % (key, Prt))
if usedarrivals[key].has_key('S'): if 'S' in usedarrivals[key]:
# S onsets # S onsets
if usedarrivals[key]['S']['weight'] < 4: if usedarrivals[key]['S']['weight'] < 4:
Sonset = usedarrivals[key]['S']['mpp'] Sonset = usedarrivals[key]['S']['mpp']
@@ -879,7 +878,7 @@ def writephases(arrivals, fformat, filename, parameter=None, eventinfo=None):
# prefer manual picks # prefer manual picks
usedarrivals = chooseArrivals(arrivals) usedarrivals = chooseArrivals(arrivals)
for key in usedarrivals: for key in usedarrivals:
if usedarrivals[key].has_key('P'): if 'P' in usedarrivals[key]:
if usedarrivals[key]['P']['weight'] < 4 and usedarrivals[key]['P']['fm'] is not None: if usedarrivals[key]['P']['weight'] < 4 and usedarrivals[key]['P']['fm'] is not None:
stat = key stat = key
for i in range(len(picks)): for i in range(len(picks)):
@@ -961,7 +960,7 @@ def writephases(arrivals, fformat, filename, parameter=None, eventinfo=None):
arrivals = chooseArrivals(arrivals) # MP MP what is chooseArrivals? It is not defined anywhere arrivals = chooseArrivals(arrivals) # MP MP what is chooseArrivals? It is not defined anywhere
# write phase lines # write phase lines
for key in arrivals: for key in arrivals:
if arrivals[key].has_key('P'): if 'P' in arrivals[key]:
if arrivals[key]['P']['weight'] < 4 and arrivals[key]['P']['fm'] is not None: if arrivals[key]['P']['weight'] < 4 and arrivals[key]['P']['fm'] is not None:
stat = key stat = key
ccode = arrivals[key]['P']['channel'] ccode = arrivals[key]['P']['channel']
@@ -1014,7 +1013,6 @@ def chooseArrivals(arrivals):
:return: arrivals but with the manual picks prefered if possible :return: arrivals but with the manual picks prefered if possible
""" """
# If len of arrivals is greater than 2 it comes from autopicking so only autopicks are available # If len of arrivals is greater than 2 it comes from autopicking so only autopicks are available
print("=== CHOOSE ===")
if len(arrivals) > 2: if len(arrivals) > 2:
return arrivals return arrivals
if arrivals['auto'] and arrivals['manual']: if arrivals['auto'] and arrivals['manual']:
+2 -2
View File
@@ -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')
+3 -17
View File
@@ -20,7 +20,7 @@ from pylot.core.pick.charfuns import CharacteristicFunction
from pylot.core.pick.charfuns import HOScf, AICcf, ARZcf, ARHcf, AR3Ccf from pylot.core.pick.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)
+6 -1
View File
@@ -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
+6 -5
View File
@@ -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:
+17 -17
View File
@@ -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
+21 -9
View File
@@ -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,
+7 -2
View File
@@ -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():
+1 -1
View File
@@ -42,7 +42,7 @@ def main(project_file_path, manual=False, auto=True, file_format='png', f_ext=''
for item in input_list: 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()
+3 -2
View File
@@ -41,7 +41,8 @@ class Thread(QThread):
exctype, value = sys.exc_info()[:2] exctype, value = sys.exc_info()[:2]
self._executedErrorInfo = '{} {} {}'. \ self._executedErrorInfo = '{} {} {}'. \
format(exctype, value, traceback.format_exc()) format(exctype, value, traceback.format_exc())
sys.stdout = self.old_stdout if self.redirect_stdout:
sys.stdout = self.old_stdout
def showProgressbar(self): def showProgressbar(self):
if self.progressText: if self.progressText:
@@ -159,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()
+60 -25
View File
@@ -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)
+483 -143
View File
@@ -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
@@ -5663,9 +5916,23 @@ class ChooseWaveFormWindow(QWidget):
def submit(self): def submit(self):
matplotlib.pyplot.close(self.currentSpectro) matplotlib.pyplot.close(self.currentSpectro)
t = self.chooseBoxTraces.currentText() + " " + self.chooseBoxComponent.currentText() t = self.chooseBoxTraces.currentText() + " " + self.chooseBoxComponent.currentText()
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()
self.applyFFT()
def applyFFT(self, trace):
tra = self.traces[self.chooseBoxTraces.currentText()[3:]]['Z']
transformed = abs(np.fft.rfft(tra.data))
print ( transformed )
matplotlib.pyplot.plot ( transformed )
matplotlib.pyplot.show()
def applyFFTs(self, tra):
transformed = abs(np.fft.rfft(tra.data))
print ( transformed )
matplotlib.pyplot.plot ( transformed )
matplotlib.pyplot.show()
def submitN(self): def submitN(self):
matplotlib.pyplot.close(self.currentSpectro) matplotlib.pyplot.close(self.currentSpectro)
@@ -5681,13 +5948,6 @@ class ChooseWaveFormWindow(QWidget):
self.chooseBoxTraces.currentText()[3:]]['E'].spectrogram(show=False, title=t) self.chooseBoxTraces.currentText()[3:]]['E'].spectrogram(show=False, title=t)
self.currentSpectro.show() self.currentSpectro.show()
def submitZ(self):
matplotlib.pyplot.close(self.currentSpectro)
t = self.chooseBoxTraces.currentText() + " " + self.chooseBoxComponent.currentText()
self.currentSpectro = self.traces[
self.chooseBoxTraces.currentText()[3:]]['Z'].spectrogram(show=False, title=t)
self.currentSpectro.show()
# Creates a QComboBox and adds all traces provided # Creates a QComboBox and adds all traces provided
def createComboBoxTraces(self): def createComboBoxTraces(self):
if len(self.wFs) <= 0: if len(self.wFs) <= 0:
@@ -5714,7 +5974,87 @@ class ChooseWaveFormWindow(QWidget):
pass pass
class SpectrogramTab(QWidget):
def __init__(self, traces, wfdata, parent=None):
super(SpectrogramTab, self).__init__(parent)
self.setupUi()
self.traces = traces
self.wfdata = wfdata
def setupUi(self):
pass
def makeSpecFig(self, direction = 'Z', height = 0, width = 0, parent = None):
i = 0
grams = []
figure, axis = matplotlib.pyplot.subplots(len(self.traces), sharex=True)
start, end = full_range(self.wfdata)
if height != 0 and width != 0:
figure.figsize = (width, height)
figure.set_figwidth = width
figure.set_figheight = height
#figure.tight_layout()
for t in self.traces:
tra = self.traces[t][direction]
#print(start, end)
# Set Title
if i == 0:
if direction == 'Z':
figure.suptitle("section: vertical components")
elif direction == 'E':
figure.suptitle("section: east-west components")
elif direction == 'N':
figure.suptitle("section: north-south components")
axis[i].vlines(0, axis[i].get_ylim()[0], axis[i].get_ylim()[1],
colors='m', linestyles='dashed',
linewidth=2)
# Different axis settings for visual improvements
# axis[i].set_xlim(left=0, right=end - start)
# axis[i].spines['top'].set_visible(False)
# axis[i].spines['right'].set_visible(False)
# # axis[i].spines['left'].set_visible(False)
# axis[i].tick_params(axis='x', which='both', bottom=False, top=False, labelbottom=False)
# if not (len(self.traces) == i - 1):
# axis[i].spines['bottom'].set_visible(False)
# axis[i].set_yticks([])
# axis[i].set_ylabel(t, loc='center', rotation='horizontal')
#ax.axhline(n, color="0.5", lw=0.5)
grams.append(tra.spectrogram(show=False, axes=axis[i]))
i+=1
#figure.setXLims([0, end - start])
figure.set_tight_layout(True)
fC = FigureCanvas(figure)
return fC
#for t in self.traces:
# tra = self.traces[t]['Z']
# transformed = abs(np.fft.rfft(tra.data))
# axis[i].plot(transformed, label=t)
# # axis[i].tick_params(labelbottom=False)
# axis[i].spines['top'].set_visible(False)
# axis[i].spines['right'].set_visible(False)
# axis[i].spines['left'].set_visible(False)
# if not (len(self.traces) == i - 1):
# axis[i].spines['bottom'].set_visible(False)
# axis[i].set_yticks([])
# axis[i].set_ylabel(t, loc='center', rotation='horizontal')
# # axis[i].axis('off')
# i += 1
# # self.applyFFTs(t)
if __name__ == '__main__': if __name__ == '__main__':
import doctest import doctest
doctest.testmod() doctest.testmod()