7 Commits

11 changed files with 335 additions and 283 deletions

View File

@@ -76,7 +76,7 @@ from pylot.core.util.utils import fnConstructor, getLogin, \
full_range, readFilterInformation, pick_color_plt, \ full_range, readFilterInformation, pick_color_plt, \
pick_linestyle_plt, identifyPhaseID, excludeQualityClasses, \ pick_linestyle_plt, identifyPhaseID, excludeQualityClasses, \
transform_colors_mpl, transform_colors_mpl_str, getAutoFilteroptions, check_all_obspy, \ transform_colors_mpl, transform_colors_mpl_str, getAutoFilteroptions, check_all_obspy, \
check_all_pylot, get_bool, get_None check_all_pylot, get_bool, get_None, get_pylot_eventfile_with_extension
from pylot.core.util.gui import make_pen from pylot.core.util.gui import make_pen
from pylot.core.util.event import Event from pylot.core.util.event import Event
from pylot.core.io.location import create_creation_info, create_event from pylot.core.io.location import create_creation_info, create_event
@@ -84,7 +84,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 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
@@ -181,7 +181,6 @@ 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
@@ -1007,16 +1006,15 @@ 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 = sld.lineEdit.text()
# fext = '.xml' # fext = '.xml'
for event in events: for event in events:
path = event.path filename = get_pylot_eventfile_with_extension(event, fext)
eventname = path.split('/')[-1] # or event.pylot_id if filename:
filename = os.path.join(path, 'PyLoT_' + eventname + fext)
if os.path.isfile(filename):
self.load_data(filename, draw=False, event=event, overwrite=True) self.load_data(filename, draw=False, event=event, overwrite=True)
refresh = True refresh = True
if not refresh: if not refresh:
@@ -1131,19 +1129,16 @@ class MainWindow(QMainWindow):
else: else:
return return
def getWFFnames_from_eventbox(self, eventbox: str = None, subpath: str = None) -> list: def getWFFnames_from_eventbox(self, eventbox=None):
''' '''
Return waveform filenames from event in eventbox. 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)
if subpath: basepath = eventpath.split(os.path.basename(eventpath))[0]
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')
@@ -1891,6 +1886,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()
@@ -1925,7 +1921,6 @@ class MainWindow(QMainWindow):
self.spectro_layout.addWidget(newSpectroWidget) self.spectro_layout.addWidget(newSpectroWidget)
self.spectroWidget = newSpectroWidget self.spectroWidget = newSpectroWidget
def newWF(self, event=None, plot=True): def newWF(self, event=None, plot=True):
''' '''
Load new data and plot if necessary. Load new data and plot if necessary.
@@ -1955,20 +1950,13 @@ class MainWindow(QMainWindow):
def prepareLoadWaveformData(self): def prepareLoadWaveformData(self):
self.fnames = self.getWFFnames_from_eventbox() self.fnames = self.getWFFnames_from_eventbox()
self.fnames_comp = [] self.fnames_syn = []
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):
''' '''
@@ -1983,6 +1971,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?')
@@ -1999,7 +1989,7 @@ class MainWindow(QMainWindow):
tstop = None tstop = None
self.data.setWFData(self.fnames, self.data.setWFData(self.fnames,
self.fnames_comp, self.fnames_syn,
checkRotated=True, checkRotated=True,
metadata=self.metadata, metadata=self.metadata,
tstart=tstart, tstart=tstart,
@@ -2007,7 +1997,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.comp_checkbox qcheckb_syn = self.dataPlot.syn_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):
@@ -2015,8 +2005,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.comp_checkbox.isChecked(): if self.dataPlot.syn_checkbox.isChecked():
self.fnames_comp = [os.path.join(eventpath_syn, filename) for filename in os.listdir(eventpath_syn)] self.fnames_syn = [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():
@@ -2138,7 +2128,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)
@@ -2297,7 +2287,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().getAltWFdata() wfsyn = self.get_data().getSynWFData()
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:
@@ -2312,8 +2302,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:
@@ -2609,21 +2599,18 @@ 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))
wfdata = self.get_data().getOriginalWFData().copy() data = 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=wfdata.select(station=station), data=data.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()
@@ -3647,7 +3634,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:
@@ -3821,7 +3808,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()

View File

@@ -9,4 +9,4 @@
conda activate pylot_38 conda activate pylot_38
python ./autoPyLoT.py -i /home/marcel/.pylot/pylot_janis_noisy.in -c $NSLOTS python ./autoPyLoT.py -i /home/marcel/.pylot/pylot_adriaarray.in -c 20 -dmt processed

View File

@@ -19,7 +19,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 +64,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:
@@ -451,25 +451,27 @@ class Data(object):
data.filter(**kwargs) data.filter(**kwargs)
self.dirty = True self.dirty = True
def setWFData(self, fnames, fnames_alt=None, checkRotated=False, metadata=None, tstart=0, tstop=0): def setWFData(self, fnames, fnames_syn=None, checkRotated=False, metadata=None, tstart=0, tstop=0):
""" """
Clear current waveform data and set given waveform data 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.wf_alt = Stream() self.wfsyn = 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 = [fname for fname in fnames if not os.path.isdir(fname)] fnames_syn = check_fname_exists(fnames_syn)
fnames_alt = [fname for fname in fnames_alt if not os.path.isdir(fname)]
# if obspy_dmt: # if obspy_dmt:
# wfdir = 'raw' # wfdir = 'raw'
# self.processed = False # self.processed = False
@@ -487,8 +489,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_alt is not None: if fnames_syn is not None:
self.appendWFData(fnames_alt, alternative=True) self.appendWFData(fnames_syn, synthetic=True)
else: else:
return False return False
@@ -496,7 +498,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)
@@ -508,7 +512,7 @@ class Data(object):
self.dirty = False self.dirty = False
return True return True
def appendWFData(self, fnames, alternative=False): def appendWFData(self, fnames, synthetic=False):
""" """
Read waveform data from fnames and append it to current wf data Read waveform data from fnames and append it to current wf data
:param fnames: waveform data to append :param fnames: waveform data to append
@@ -521,20 +525,20 @@ class Data(object):
if self.dirty: if self.dirty:
self.resetWFData() self.resetWFData()
orig_or_alternative_data = {True: self.wf_alt, real_or_syn_data = {True: self.wfsyn,
False: self.wfdata} False: self.wfdata}
warnmsg = '' warnmsg = ''
for fname in set(fnames): for fname in set(fnames):
try: try:
orig_or_alternative_data[alternative] += read(fname, starttime=self.tstart, endtime=self.tstop) real_or_syn_data[synthetic] += read(fname, starttime=self.tstart, endtime=self.tstop)
except TypeError: except TypeError:
try: try:
orig_or_alternative_data[alternative] += read(fname, format='GSE2', starttime=self.tstart, endtime=self.tstop) real_or_syn_data[synthetic] += read(fname, format='GSE2', starttime=self.tstart, endtime=self.tstop)
except Exception as e: except Exception as e:
try: try:
orig_or_alternative_data[alternative] += read(fname, format='SEGY', starttime=self.tstart, real_or_syn_data[synthetic] += 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:
@@ -549,8 +553,8 @@ class Data(object):
def getOriginalWFData(self): def getOriginalWFData(self):
return self.wforiginal return self.wforiginal
def getAltWFdata(self): def getSynWFData(self):
return self.wf_alt return self.wfsyn
def resetWFData(self): def resetWFData(self):
""" """

View File

@@ -660,7 +660,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,

View File

@@ -1332,22 +1332,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

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
@@ -279,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)
@@ -384,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
@@ -463,19 +474,17 @@ class Array_map(QtWidgets.QWidget):
transform=ccrs.PlateCarree(), label='deleted')) transform=ccrs.PlateCarree(), label='deleted'))
def openPickDlg(self, ind): def openPickDlg(self, ind):
wfdata = self._parent.get_data().getWFData() data = 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:
wfdata = wfdata.select(station=station) data = data.select(station=station)
wfdata_comp = wfdata_comp.select(station=station) if not data:
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=wfdata.copy(), data_compare=wfdata_comp.copy(), network=network, station=station, data=data, network=network, station=station,
picks=self._parent.get_current_event().getPick(station), 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,

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):

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()

View File

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

View File

@@ -2,6 +2,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import hashlib import hashlib
import logging
import os import os
import platform import platform
import re import re
@@ -119,7 +120,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
@@ -818,19 +819,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
@@ -851,12 +839,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()
@@ -867,7 +855,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):
@@ -897,6 +885,33 @@ 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 = eventpath.split('/')[-1] # or event.pylot_id
filename = os.path.join(eventpath, 'PyLoT_' + eventname + fext)
if os.path.isfile(filename):
return filename
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

View File

@@ -36,7 +36,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 +51,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 prepTimeAxis, full_range, demeanTrace, isSorted, findComboBoxIndex, clims, \ from pylot.core.util.utils import prepTimeAxis, 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
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 +793,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.comp_checkbox.clicked.connect(self.parent().newWF) self.syn_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 +804,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.comp_checkbox) mid_layout.addWidget(self.syn_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.comp_checkbox.setLayoutDirection(Qt.RightToLeft) self.syn_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 +825,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.comp_checkbox = QtWidgets.QCheckBox('Load comparison data') self.syn_checkbox = QtWidgets.QCheckBox('synthetics')
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,11 +834,9 @@ class WaveformWidgetPG(QtWidgets.QWidget):
def getPlotDict(self): def getPlotDict(self):
return self.plotdict return self.plotdict
def activateObspyDMToptions(self, activate: bool) -> None: def activateObspyDMToptions(self, activate):
self.qcombo_processed.setEnabled(activate) self.syn_checkbox.setVisible(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):
@@ -864,9 +862,24 @@ class WaveformWidgetPG(QtWidgets.QWidget):
def plotWFData(self, wfdata, wfsyn=None, title=None, scaleddata=False, mapping=True, def plotWFData(self, wfdata, wfsyn=None, title=None, scaleddata=False, mapping=True,
component='*', nth_sample=1, verbosity=0, method='normal', gain=1., shift_syn=0.2): component='*', nth_sample=1, verbosity=0, method='normal', gain=1., shift_syn=0.2):
def station_sort(nslc):
"""Try to sort after station integer in case of a line array (e.g. active seismics)"""
try:
rval = sorted(nslc, key=lambda x: int(x.split('.')[1]))
return rval
except ValueError as e:
# this is the standard case for seismological stations
pass
except Exception as e:
print(f'Sorting by station integer failed with unknown exception: {e}')
# fallback to default sorting
return sorted(nslc)
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 +897,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 +959,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 + shift_syn for index, datum in enumerate(trace_syn.data) trace_syn.data = np.array([datum + n for index, datum in enumerate(trace_syn.data)
if not index % nth_sample] if st_syn else []) 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 +968,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 +990,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 +998,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 +1148,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_idle() ax.figure.canvas.draw()
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_idle() self.figure.canvas.draw()
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,15 +1371,11 @@ class PylotCanvas(FigureCanvas):
plot_positions[channel] = plot_pos plot_positions[channel] = plot_pos
return plot_positions return plot_positions
def plotWFData(self, wfdata, wfdata_compare=None, title=None, zoomx=None, zoomy=None, def plotWFData(self, wfdata, title=None, zoomx=None, zoomy=None,
noiselevel=None, scaleddata=False, mapping=True, 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()
@@ -1377,33 +1386,21 @@ 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])
plot_streams['wfdata']['data'] = wfdata.select(component=component) st_select = wfdata.select(component=component)
plot_streams['wfdata']['data'] += wfdata.select(component=alter_comp) st_select += 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:
plot_streams['wfdata']['data'] = wfdata st_select = 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_main, compclass) plot_positions = self.calcPlotPositions(st_select, 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_main): for plot_pos, trace in enumerate(st_select):
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
@@ -1411,48 +1408,44 @@ 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('.')
for wf_name, wf_dict in plot_streams.items(): st = st_select.select(id=seed_id)
st_select = wf_dict.get('data') trace = st[0].copy()
if not st_select: if mapping:
continue n = plot_positions[trace.stats.channel]
st = st_select.select(id=seed_id) if n > nmax:
trace = st[0].copy() nmax = n
if mapping: if verbosity:
n = plot_positions[trace.stats.channel] msg = 'plotting %s channel of station %s' % (channel, station)
if n > nmax: print(msg)
nmax = n stime = trace.stats.starttime - wfstart
if verbosity: time_ax = prepTimeAxis(stime, trace)
msg = 'plotting %s channel of station %s' % (channel, station) if time_ax is not None:
print(msg) if scaleToChannel:
stime = trace.stats.starttime - wfstart st_scale = wfdata.select(channel=scaleToChannel)
time_ax = prepTimeAxis(stime, trace) if st_scale:
if time_ax is not None: tr = st_scale[0]
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(trace.data)) * 2) trace.normalize(np.max(np.abs(tr.data)) * 2)
scaleddata = True
if not scaleddata:
trace.detrend('constant')
trace.normalize(np.max(np.abs(trace.data)) * 2)
offset = wf_dict.get('offset') times = [time for index, time in enumerate(time_ax) if not index % nth_sample]
data = [datum + n for index, datum in enumerate(trace.data) if not index % nth_sample]
times = [time for index, time in enumerate(time_ax) if not index % nth_sample] ax.axhline(n, color="0.5", lw=0.5)
data = [datum + n + offset for index, datum in enumerate(trace.data) if not index % nth_sample] ax.plot(times, data, color=linecolor, linewidth=0.7)
ax.axhline(n, color="0.5", lw=0.5) if noiselevel is not None:
ax.plot(times, data, color=wf_dict.get('linecolor'), **wf_dict.get('plot_kwargs')) for level in [-noiselevel[channel], noiselevel[channel]]:
if noiselevel is not None: ax.plot([time_ax[0], time_ax[-1]],
for level in [-noiselevel[channel], noiselevel[channel]]: [n + level, n + level],
ax.plot([time_ax[0], time_ax[-1]], color=linecolor,
[n + level, n + level], linestyle='dashed')
color=wf_dict.get('linecolor'), self.setPlotDict(n, seed_id)
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:
@@ -1575,6 +1568,96 @@ 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.default_text = default_text
self.label = label
self.setButtons()
self.setupUi()
self.connectSignals()
self.showPaths()
self.refresh_timer = QTimer(self)
self.refresh_timer.timeout.connect(self.showPaths)
self.refresh_timer.start(10000)
self.resize(800, 450)
def setupUi(self):
self.main_layout = QtWidgets.QVBoxLayout()
self.header_layout = QtWidgets.QHBoxLayout()
#
self.setLayout(self.main_layout)
# widgets inside the dialog
self.textLabel = QtWidgets.QLabel(self.label)
self.lineEdit = QtWidgets.QLineEdit(self.default_text)
# optional search button, currently disabled. List refreshed when text changes
self.searchButton = QtWidgets.QPushButton('Search')
self.searchButton.setVisible(False)
self.tableWidget = QtWidgets.QTableWidget()
tableWidget = self.tableWidget
tableWidget.setColumnCount(2)
tableWidget.setRowCount(len(self.events))
tableWidget.setHorizontalHeaderLabels(('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.lineEdit)
self.header_layout.addWidget(self.searchButton)
self.main_layout.addLayout(self.header_layout)
self.main_layout.addWidget(self.tableWidget)
self.main_layout.addWidget(self.statusText)
self.main_layout.addWidget(self._buttonbox)
def showPaths(self):
self.filepaths = []
fext = self.lineEdit.text()
self.tableWidget.clearContents()
for index, event in enumerate(self.events):
filename = get_pylot_eventfile_with_extension(event, fext)
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'{filename}')
ts_item = QtWidgets.QTableWidgetItem(f'{datetime.datetime.fromtimestamp(ts)}')
self.tableWidget.setItem(index, 0, fname_item)
self.tableWidget.setItem(index, 1, ts_item)
# TODO: Idea -> only refresh if table contents changed. Use selection to load only a subset of files
if len(self.filepaths) > 0:
status_text = f'Found {len(self.filepaths)} eventfiles. Do you want to load them?'
else:
status_text = 'Did not find any files for specified file mask.'
self.statusText.setText(status_text)
def setButtons(self):
self._buttonbox = QDialogButtonBox(QDialogButtonBox.Ok |
QDialogButtonBox.Cancel)
def connectSignals(self):
self._buttonbox.accepted.connect(self.accept)
self._buttonbox.rejected.connect(self.reject)
self.lineEdit.textChanged.connect(self.showPaths)
self.searchButton.clicked.connect(self.showPaths)
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)
@@ -1679,8 +1762,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, data_compare=None, station=None, network=None, location=None, picks=None, def __init__(self, parent=None, data=None, station=None, network=None, location=None, picks=None,
autopicks=None, rotate=False, parameter=None, embedded=False, metadata=None, show_comp_data=False, autopicks=None, rotate=False, parameter=None, embedded=False, metadata=None,
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
@@ -1689,7 +1772,6 @@ 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
@@ -1728,32 +1810,11 @@ class PickDlg(QDialog):
else: else:
self.filteroptions = FILTERDEFAULTS self.filteroptions = FILTERDEFAULTS
self.pick_block = False self.pick_block = False
# set attribute holding data
if data is None or not data:
try:
data = parent.get_data().getWFData().copy()
self.data = data.select(station=station)
except AttributeError as e:
errmsg = 'You either have to put in a data or an appropriate ' \
'parent (PyLoT MainWindow) object: {0}'.format(e)
raise Exception(errmsg)
else:
self.data = data
self.data_compare = data_compare
self.nextStation = QtWidgets.QCheckBox('Continue with next station ') self.nextStation = QtWidgets.QCheckBox('Continue with next station ')
# comparison channel # comparison channel
self.referenceChannel = QtWidgets.QComboBox() self.compareChannel = QtWidgets.QComboBox()
self.referenceChannel.activated.connect(self.resetPlot) self.compareChannel.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 # scale channel
self.scaleChannel = QtWidgets.QComboBox() self.scaleChannel = QtWidgets.QComboBox()
@@ -1766,6 +1827,18 @@ class PickDlg(QDialog):
self.cur_xlim = None self.cur_xlim = None
self.cur_ylim = None self.cur_ylim = None
# set attribute holding data
if data is None or not data:
try:
data = parent.get_data().getWFData().copy()
self.data = data.select(station=station)
except AttributeError as e:
errmsg = 'You either have to put in a data or an appropriate ' \
'parent (PyLoT MainWindow) object: {0}'.format(e)
raise Exception(errmsg)
else:
self.data = data
self.stime, self.etime = full_range(self.getWFData()) self.stime, self.etime = full_range(self.getWFData())
# initialize plotting widget # initialize plotting widget
@@ -1777,12 +1850,12 @@ class PickDlg(QDialog):
self.setupUi() self.setupUi()
# fill compare and scale channels # fill compare and scale channels
self.referenceChannel.addItem('-', None) self.compareChannel.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.referenceChannel.addItem(channel, trace) self.compareChannel.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
@@ -1799,7 +1872,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(), wfdata_compare=self.getWFDataComp(), self.multicompfig.plotWFData(wfdata=self.getWFData(),
title=title) title=title)
self.multicompfig.setZoomBorders2content() self.multicompfig.setZoomBorders2content()
@@ -1975,11 +2048,8 @@ 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('Plot reference channel: ')) _dialtoolbar.addWidget(QtWidgets.QLabel('Compare to channel: '))
_dialtoolbar.addWidget(self.referenceChannel) _dialtoolbar.addWidget(self.compareChannel)
_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)
@@ -2314,7 +2384,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.referenceChannel.setEnabled(False) self.compareChannel.setEnabled(False)
self.scaleChannel.setEnabled(False) self.scaleChannel.setEnabled(False)
phase = self.currentPhase phase = self.currentPhase
phaseID = self.getPhaseID(phase) phaseID = self.getPhaseID(phase)
@@ -2346,7 +2416,7 @@ class PickDlg(QDialog):
self.disconnectPressEvent() self.disconnectPressEvent()
self.multicompfig.connectEvents() self.multicompfig.connectEvents()
self.renamePhaseAction.setEnabled(True) self.renamePhaseAction.setEnabled(True)
self.referenceChannel.setEnabled(True) self.compareChannel.setEnabled(True)
self.scaleChannel.setEnabled(True) self.scaleChannel.setEnabled(True)
self.connect_pick_delete() self.connect_pick_delete()
self.draw() self.draw()
@@ -2419,12 +2489,6 @@ 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()
@@ -2546,16 +2610,11 @@ class PickDlg(QDialog):
stime = self.getStartTime() stime = self.getStartTime()
# copy wfdata for plotting # copy data for plotting
wfdata = self.getWFData().copy() data = self.getWFData().copy()
wfdata_comp = self.getWFDataComp().copy() data = self.getPickPhases(data, phase)
wfdata = self.getPickPhases(wfdata, phase) data.normalize()
wfdata_comp = self.getPickPhases(wfdata_comp, phase) if not data:
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: {}. ' 'No channel to plot for phase: {}. '
'Make sure to select the correct channels for P and S ' 'Make sure to select the correct channels for P and S '
@@ -2563,16 +2622,14 @@ class PickDlg(QDialog):
self.leave_picking_mode() self.leave_picking_mode()
return return
# filter wfdata and trace on which is picked prior to determination of SNR # filter data and trace on which is picked prior to determination of SNR
filterphase = self.currentFilterPhase() filterphase = self.currentFilterPhase()
if filterphase: if filterphase:
filteroptions = self.getFilterOptions(filterphase).parseFilterOptions() filteroptions = self.getFilterOptions(filterphase).parseFilterOptions()
try: try:
for wfd in [wfdata, wfdata_comp]: data.detrend('linear')
if wfd: data.filter(**filteroptions)
wfd.detrend('linear') # wfdata.filter(**filteroptions)# MP MP removed filtering of original data
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',
@@ -2582,8 +2639,8 @@ class PickDlg(QDialog):
snr = [] snr = []
noiselevels = {} noiselevels = {}
# determine SNR and noiselevel # determine SNR and noiselevel
for trace in wfdata.traces: for trace in data.traces:
st = wfdata.select(channel=trace.stats.channel) st = data.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])
@@ -2594,25 +2651,23 @@ class PickDlg(QDialog):
noiselevel = nfac noiselevel = nfac
noiselevels[trace.stats.channel] = noiselevel noiselevels[trace.stats.channel] = noiselevel
# prepare plotting of wfdata # prepare plotting of data
for wfd in [wfdata, wfdata_comp]: for trace in data:
if wfd: t = prepTimeAxis(trace.stats.starttime - stime, trace)
for trace in wfd: inoise = getnoisewin(t, ini_pick, noise_win, gap_win)
t = prepTimeAxis(trace.stats.starttime - stime, trace) trace = demeanTrace(trace, inoise)
inoise = getnoisewin(t, ini_pick, noise_win, gap_win) # upscale trace data in a way that each trace is vertically zoomed to noiselevel*factor
trace = demeanTrace(trace, inoise) channel = trace.stats.channel
# upscale trace wfdata in a way that each trace is vertically zoomed to noiselevel*factor noiselevel = noiselevels[channel]
channel = trace.stats.channel noiseScaleFactor = self.calcNoiseScaleFactor(noiselevel, zoomfactor=5.)
noiselevel = noiselevels[channel] trace.data *= noiseScaleFactor
noiseScaleFactor = self.calcNoiseScaleFactor(noiselevel, zoomfactor=5.) noiselevels[channel] *= noiseScaleFactor
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(wfdata) - 1]) ylims = list(np.array([-.5, .5]) + [0, len(data) - 1])
title = self.getStation() + ' picking mode' title = self.getStation() + ' picking mode'
title += ' | SNR: {}'.format(mean_snr) title += ' | SNR: {}'.format(mean_snr)
@@ -2620,10 +2675,9 @@ 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.referenceChannel.currentText()) plot_additional = bool(self.compareChannel.currentText())
additional_channel = self.referenceChannel.currentText() additional_channel = self.compareChannel.currentText()
self.multicompfig.plotWFData(wfdata=wfdata, self.multicompfig.plotWFData(wfdata=data,
wfdata_compare=wfdata_comp,
title=title, title=title,
zoomx=xlims, zoomx=xlims,
zoomy=ylims, zoomy=ylims,
@@ -3014,8 +3068,7 @@ 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()
wfdata = self.getWFData().copy() data = self.getWFData().copy()
wfdata_comp = self.getWFDataComp().copy()
title = self.getStation() title = self.getStation()
if filter: if filter:
filtoptions = None filtoptions = None
@@ -3023,22 +3076,19 @@ 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:
for wfd in [wfdata, wfdata_comp]: data.detrend('linear')
if wfd: data.taper(0.02, type='cosine')
wfd.detrend('linear') data.filter(**filtoptions)
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.referenceChannel.currentText()) plot_additional = bool(self.compareChannel.currentText())
additional_channel = self.referenceChannel.currentText() additional_channel = self.compareChannel.currentText()
scale_channel = self.scaleChannel.currentText() scale_channel = self.scaleChannel.currentText()
self.multicompfig.plotWFData(wfdata=wfdata, wfdata_compare=wfdata_comp, self.multicompfig.plotWFData(wfdata=data, title=title,
title=title,
zoomx=self.getXLims(), zoomx=self.getXLims(),
zoomy=self.getYLims(), zoomy=self.getYLims(),
plot_additional=plot_additional, plot_additional=plot_additional,
@@ -3111,9 +3161,6 @@ 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)
@@ -3624,8 +3671,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
@@ -3724,13 +3772,11 @@ 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),
@@ -3782,6 +3828,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),
@@ -3814,7 +3861,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_idle() canvas.draw()
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