Merge remote-tracking branch 'origin/develop' into feature/refactor
This commit is contained in:
commit
5258a7e9b4
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,3 +1,4 @@
|
|||||||
*.pyc
|
*.pyc
|
||||||
*~
|
*~
|
||||||
pylot/RELEASE-VERSION
|
pylot/RELEASE-VERSION
|
||||||
|
*.idea
|
||||||
|
321
PyLoT.py
321
PyLoT.py
@ -86,7 +86,7 @@ from pylot.core.util.widgets import FilterOptionsDialog, NewEventDlg, \
|
|||||||
PylotCanvas, WaveformWidgetPG, PropertiesDlg, HelpForm, createAction, PickDlg, \
|
PylotCanvas, WaveformWidgetPG, PropertiesDlg, HelpForm, createAction, PickDlg, \
|
||||||
getDataType, ComparisonWidget, TuneAutopicker, PylotParaBox, AutoPickDlg, CanvasWidget, AutoPickWidget, \
|
getDataType, ComparisonWidget, TuneAutopicker, PylotParaBox, AutoPickDlg, CanvasWidget, AutoPickWidget, \
|
||||||
CompareEventsWidget
|
CompareEventsWidget
|
||||||
from pylot.core.util.map_projection import map_projection
|
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
|
||||||
from pylot.core.util.version import get_git_version as _getVersionString
|
from pylot.core.util.version import get_git_version as _getVersionString
|
||||||
@ -138,6 +138,9 @@ class MainWindow(QMainWindow):
|
|||||||
self._eventChanged = [False, False]
|
self._eventChanged = [False, False]
|
||||||
self.apd_local = None
|
self.apd_local = None
|
||||||
self.apd_sge = None
|
self.apd_sge = None
|
||||||
|
self.stations_highlighted = []
|
||||||
|
self.obspy_dmt = False
|
||||||
|
self.nextStation = False
|
||||||
|
|
||||||
self.poS_id = None
|
self.poS_id = None
|
||||||
self.ae_id = None
|
self.ae_id = None
|
||||||
@ -615,6 +618,10 @@ class MainWindow(QMainWindow):
|
|||||||
|
|
||||||
# add scroll area used in case number of traces gets too high
|
# add scroll area used in case number of traces gets too high
|
||||||
self.wf_scroll_area = QtGui.QScrollArea(self)
|
self.wf_scroll_area = QtGui.QScrollArea(self)
|
||||||
|
self.wf_scroll_area.setVisible(False)
|
||||||
|
self.no_data_label = QLabel('No Data')
|
||||||
|
self.no_data_label.setStyleSheet('color: red')
|
||||||
|
self.no_data_label.setAlignment(Qt.AlignCenter)
|
||||||
|
|
||||||
# create central matplotlib figure canvas widget
|
# create central matplotlib figure canvas widget
|
||||||
self.init_wfWidget()
|
self.init_wfWidget()
|
||||||
@ -632,11 +639,17 @@ class MainWindow(QMainWindow):
|
|||||||
array_tab.setLayout(self.array_layout)
|
array_tab.setLayout(self.array_layout)
|
||||||
events_tab.setLayout(self.events_layout)
|
events_tab.setLayout(self.events_layout)
|
||||||
|
|
||||||
|
# tighten up layouts inside tabs
|
||||||
|
for layout in [self.wf_layout, self.array_layout, self.events_layout]:
|
||||||
|
layout.setSpacing(0)
|
||||||
|
layout.setContentsMargins(0, 0, 0, 0)
|
||||||
|
|
||||||
# add tabs to main tab widget
|
# add tabs to main tab widget
|
||||||
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.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()
|
||||||
@ -1217,6 +1230,8 @@ class MainWindow(QMainWindow):
|
|||||||
tv.verticalHeader().hide()
|
tv.verticalHeader().hide()
|
||||||
tv.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows)
|
tv.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows)
|
||||||
|
|
||||||
|
current_event = self.get_current_event()
|
||||||
|
|
||||||
eventBox.setView(tv)
|
eventBox.setView(tv)
|
||||||
eventBox.clear()
|
eventBox.clear()
|
||||||
model = eventBox.model()
|
model = eventBox.model()
|
||||||
@ -1249,6 +1264,17 @@ class MainWindow(QMainWindow):
|
|||||||
event_ref = event.isRefEvent()
|
event_ref = event.isRefEvent()
|
||||||
event_test = event.isTestEvent()
|
event_test = event.isTestEvent()
|
||||||
|
|
||||||
|
time = lat = lon = depth = mag = None
|
||||||
|
if len(event.origins) == 1:
|
||||||
|
origin = event.origins[0]
|
||||||
|
time = origin.time + 0 # add 0 because there was an exception for time = 0s
|
||||||
|
lat = origin.latitude
|
||||||
|
lon = origin.longitude
|
||||||
|
depth = origin.depth
|
||||||
|
if len(event.magnitudes) == 1:
|
||||||
|
magnitude = event.magnitudes[0]
|
||||||
|
mag = magnitude.mag
|
||||||
|
|
||||||
# text = '{path:{plen}} | manual: [{p:3d}] | auto: [{a:3d}]'
|
# text = '{path:{plen}} | manual: [{p:3d}] | auto: [{a:3d}]'
|
||||||
# text = text.format(path=event_path,
|
# text = text.format(path=event_path,
|
||||||
# plen=plmax,
|
# plen=plmax,
|
||||||
@ -1256,6 +1282,11 @@ class MainWindow(QMainWindow):
|
|||||||
# a=event_nautopicks)
|
# a=event_nautopicks)
|
||||||
|
|
||||||
item_path = QtGui.QStandardItem('{path:{plen}}'.format(path=event_path, plen=plmax))
|
item_path = QtGui.QStandardItem('{path:{plen}}'.format(path=event_path, plen=plmax))
|
||||||
|
item_time = QtGui.QStandardItem('{}'.format(time))
|
||||||
|
item_lat = QtGui.QStandardItem('{}'.format(lat))
|
||||||
|
item_lon = QtGui.QStandardItem('{}'.format(lon))
|
||||||
|
item_depth = QtGui.QStandardItem('{}'.format(depth))
|
||||||
|
item_mag = QtGui.QStandardItem('{}'.format(mag))
|
||||||
item_nmp = QtGui.QStandardItem(str(ma_count['manual']))
|
item_nmp = QtGui.QStandardItem(str(ma_count['manual']))
|
||||||
item_nmp.setIcon(self.manupicksicon_small)
|
item_nmp.setIcon(self.manupicksicon_small)
|
||||||
item_nap = QtGui.QStandardItem(str(ma_count['auto']))
|
item_nap = QtGui.QStandardItem(str(ma_count['auto']))
|
||||||
@ -1281,10 +1312,17 @@ class MainWindow(QMainWindow):
|
|||||||
# item.setFont(font)
|
# item.setFont(font)
|
||||||
# item2.setForeground(QtGui.QColor('black'))
|
# item2.setForeground(QtGui.QColor('black'))
|
||||||
# item2.setFont(font)
|
# item2.setFont(font)
|
||||||
itemlist = [item_path, item_nmp, item_nap, item_ref, item_test, item_notes]
|
itemlist = [item_path, item_time, item_lat, item_lon, item_depth,
|
||||||
if event_test and select_events == 'ref':
|
item_mag, item_nmp, item_nap, item_ref, item_test, item_notes]
|
||||||
|
for item in itemlist:
|
||||||
|
item.setTextAlignment(Qt.AlignCenter)
|
||||||
|
if event_test and select_events == 'ref' or self.isEmpty(event_path):
|
||||||
for item in itemlist:
|
for item in itemlist:
|
||||||
item.setEnabled(False)
|
item.setEnabled(False)
|
||||||
|
|
||||||
|
#item color
|
||||||
|
self.setItemColor(itemlist, id, event, current_event)
|
||||||
|
|
||||||
model.appendRow(itemlist)
|
model.appendRow(itemlist)
|
||||||
if not event.path == self.eventBox.itemText(id).strip():
|
if not event.path == self.eventBox.itemText(id).strip():
|
||||||
message = ('Path missmatch creating eventbox.\n'
|
message = ('Path missmatch creating eventbox.\n'
|
||||||
@ -1296,6 +1334,23 @@ class MainWindow(QMainWindow):
|
|||||||
eventBox.setCurrentIndex(index)
|
eventBox.setCurrentIndex(index)
|
||||||
self.refreshRefTestButtons()
|
self.refreshRefTestButtons()
|
||||||
|
|
||||||
|
def isEmpty(self, event_path):
|
||||||
|
wf_stat = {True: 'processed',
|
||||||
|
False: 'raw',
|
||||||
|
None: None}
|
||||||
|
|
||||||
|
# self.data.processed is only None for PyLoT datastructure, else True or False
|
||||||
|
wf_dir = wf_stat[self.data.processed]
|
||||||
|
if wf_dir is not None:
|
||||||
|
wf_path = os.path.join(event_path, wf_dir)
|
||||||
|
if wf_dir is 'processed' and not os.path.exists(wf_path):
|
||||||
|
wf_path = os.path.join(event_path, 'raw')
|
||||||
|
else:
|
||||||
|
wf_path = event_path
|
||||||
|
if not os.path.exists(wf_path):
|
||||||
|
return True
|
||||||
|
return not bool(os.listdir(wf_path))
|
||||||
|
|
||||||
def filename_from_action(self, action):
|
def filename_from_action(self, action):
|
||||||
if action.data() is None:
|
if action.data() is None:
|
||||||
filt = "Supported file formats" \
|
filt = "Supported file formats" \
|
||||||
@ -1373,7 +1428,10 @@ class MainWindow(QMainWindow):
|
|||||||
def exportAllEvents(self, outformats=['.xml']):
|
def exportAllEvents(self, outformats=['.xml']):
|
||||||
for event in self.project.eventlist:
|
for event in self.project.eventlist:
|
||||||
self.get_data().setEvtData(event)
|
self.get_data().setEvtData(event)
|
||||||
|
try:
|
||||||
self.saveData(event, event.path, outformats)
|
self.saveData(event, event.path, outformats)
|
||||||
|
except Exception as e:
|
||||||
|
print('WARNING! Could not save event {}. Reason: {}'.format(event.path, e))
|
||||||
|
|
||||||
def enableSaveEventAction(self):
|
def enableSaveEventAction(self):
|
||||||
self.saveEventAction.setEnabled(True)
|
self.saveEventAction.setEnabled(True)
|
||||||
@ -1617,10 +1675,10 @@ class MainWindow(QMainWindow):
|
|||||||
if self._eventChanged[1]:
|
if self._eventChanged[1]:
|
||||||
self.refresh_array_map()
|
self.refresh_array_map()
|
||||||
if not plotted and self._eventChanged[0]:
|
if not plotted and self._eventChanged[0]:
|
||||||
# newWF(False) = load data without plotting
|
# newWF(plot=False) = load data without plotting
|
||||||
self.newWF(plot=False)
|
self.newWF(plot=False)
|
||||||
|
|
||||||
def newWF(self, plot=True):
|
def newWF(self, event=None, plot=True):
|
||||||
'''
|
'''
|
||||||
Load new data and plot if necessary.
|
Load new data and plot if necessary.
|
||||||
'''
|
'''
|
||||||
@ -1634,6 +1692,7 @@ class MainWindow(QMainWindow):
|
|||||||
call modal plot thread method when finished.
|
call modal plot thread method when finished.
|
||||||
'''
|
'''
|
||||||
if load:
|
if load:
|
||||||
|
self.prepareLoadWaveformData()
|
||||||
self.wfd_thread = Thread(self, self.loadWaveformData,
|
self.wfd_thread = Thread(self, self.loadWaveformData,
|
||||||
progressText='Reading data input...',
|
progressText='Reading data input...',
|
||||||
pb_widget=self.mainProgressBarWidget)
|
pb_widget=self.mainProgressBarWidget)
|
||||||
@ -1646,6 +1705,16 @@ class MainWindow(QMainWindow):
|
|||||||
if plot and not load:
|
if plot and not load:
|
||||||
self.plotWaveformDataThread()
|
self.plotWaveformDataThread()
|
||||||
|
|
||||||
|
def prepareLoadWaveformData(self):
|
||||||
|
self.fnames = self.getWFFnames_from_eventbox()
|
||||||
|
self.fnames_syn = []
|
||||||
|
eventpath = self.get_current_event_path()
|
||||||
|
basepath = eventpath.split(os.path.basename(eventpath))[0]
|
||||||
|
self.obspy_dmt = check_obspydmt_structure(basepath)
|
||||||
|
self.dataPlot.activateObspyDMToptions(self.obspy_dmt)
|
||||||
|
if self.obspy_dmt:
|
||||||
|
self.prepareObspyDMT_data(eventpath)
|
||||||
|
|
||||||
def loadWaveformData(self):
|
def loadWaveformData(self):
|
||||||
'''
|
'''
|
||||||
Load waveform data corresponding to current selected event.
|
Load waveform data corresponding to current selected event.
|
||||||
@ -1657,14 +1726,48 @@ class MainWindow(QMainWindow):
|
|||||||
# ans = self.data.setWFData(self.getWFFnames())
|
# ans = self.data.setWFData(self.getWFFnames())
|
||||||
# else:
|
# else:
|
||||||
# ans = False
|
# ans = False
|
||||||
self.fnames = self.getWFFnames_from_eventbox()
|
|
||||||
eventpath = self.get_current_event_path()
|
|
||||||
basepath = eventpath.split(os.path.basename(eventpath))[0]
|
|
||||||
obspy_dmt = check_obspydmt_structure(basepath)
|
|
||||||
self.data.setWFData(self.fnames,
|
self.data.setWFData(self.fnames,
|
||||||
|
self.fnames_syn,
|
||||||
checkRotated=True,
|
checkRotated=True,
|
||||||
metadata=self.metadata,
|
metadata=self.metadata)
|
||||||
obspy_dmt=obspy_dmt)
|
|
||||||
|
def prepareObspyDMT_data(self, eventpath):
|
||||||
|
qcbox_processed = self.dataPlot.qcombo_processed
|
||||||
|
qcheckb_syn = self.dataPlot.syn_checkbox
|
||||||
|
qcbox_processed.setEnabled(False)
|
||||||
|
qcheckb_syn.setEnabled(False)
|
||||||
|
for fpath in os.listdir(eventpath):
|
||||||
|
fpath = fpath.split('/')[-1]
|
||||||
|
if 'syngine' in fpath:
|
||||||
|
eventpath_syn = os.path.join(eventpath, fpath)
|
||||||
|
qcheckb_syn.setEnabled(True)
|
||||||
|
if self.dataPlot.syn_checkbox.isChecked():
|
||||||
|
self.fnames_syn = [os.path.join(eventpath_syn, filename) for filename in os.listdir(eventpath_syn)]
|
||||||
|
if 'processed' in fpath:
|
||||||
|
qcbox_processed.setEnabled(True)
|
||||||
|
if qcbox_processed.isEnabled():
|
||||||
|
wftype = qcbox_processed.currentText()
|
||||||
|
else:
|
||||||
|
wftype = 'raw'
|
||||||
|
qcbox_processed.setCurrentIndex(qcbox_processed.findText(wftype))
|
||||||
|
eventpath_dmt = os.path.join(eventpath, wftype)
|
||||||
|
self.fnames = [os.path.join(eventpath_dmt, filename) for filename in os.listdir(eventpath_dmt)]
|
||||||
|
|
||||||
|
# def setWFstatus(self):
|
||||||
|
# '''
|
||||||
|
# show status of current data, can be either 'raw' or 'processed'
|
||||||
|
# :param status:
|
||||||
|
# :return:
|
||||||
|
# '''
|
||||||
|
# status = self.data.processed
|
||||||
|
# wf_stat_color = {True: 'green',
|
||||||
|
# False: 'black',
|
||||||
|
# None: None}
|
||||||
|
# wf_stat = {True: 'processed',
|
||||||
|
# False: 'raw',
|
||||||
|
# None: None}
|
||||||
|
# self.dataPlot.setQCboxItem(wf_stat[status])
|
||||||
|
|
||||||
def check_plot_quantity(self):
|
def check_plot_quantity(self):
|
||||||
"""
|
"""
|
||||||
@ -1757,7 +1860,10 @@ class MainWindow(QMainWindow):
|
|||||||
|
|
||||||
def finish_pg_plot(self):
|
def finish_pg_plot(self):
|
||||||
self.getPlotWidget().updateWidget()
|
self.getPlotWidget().updateWidget()
|
||||||
plots = self.wfp_thread.data
|
plots, gaps = self.wfp_thread.data
|
||||||
|
# do not show plot if no data are given
|
||||||
|
self.wf_scroll_area.setVisible(len(plots) > 0)
|
||||||
|
self.no_data_label.setVisible(not len(plots) > 0)
|
||||||
for times, data, times_syn, data_syn in plots:
|
for times, data, times_syn, data_syn in plots:
|
||||||
self.dataPlot.plotWidget.getPlotItem().plot(times, data,
|
self.dataPlot.plotWidget.getPlotItem().plot(times, data,
|
||||||
pen=self.dataPlot.pen_linecolor)
|
pen=self.dataPlot.pen_linecolor)
|
||||||
@ -1765,8 +1871,7 @@ class MainWindow(QMainWindow):
|
|||||||
self.dataPlot.plotWidget.getPlotItem().plot(times_syn, data_syn,
|
self.dataPlot.plotWidget.getPlotItem().plot(times_syn, data_syn,
|
||||||
pen=self.dataPlot.pen_linecolor_syn)
|
pen=self.dataPlot.pen_linecolor_syn)
|
||||||
self.dataPlot.reinitMoveProxy()
|
self.dataPlot.reinitMoveProxy()
|
||||||
self.dataPlot.plotWidget.showAxis('left')
|
self.highlight_stations()
|
||||||
self.dataPlot.plotWidget.showAxis('bottom')
|
|
||||||
|
|
||||||
def finishWaveformDataPlot(self):
|
def finishWaveformDataPlot(self):
|
||||||
self.comparable = self.checkEvents4comparison()
|
self.comparable = self.checkEvents4comparison()
|
||||||
@ -1842,12 +1947,10 @@ class MainWindow(QMainWindow):
|
|||||||
comparable[event.pylot_id] = self.checkEvent4comparison(event)
|
comparable[event.pylot_id] = self.checkEvent4comparison(event)
|
||||||
return comparable
|
return comparable
|
||||||
|
|
||||||
def clearWaveformDataPlot(self):
|
def clearWaveformDataPlot(self, refresh_plot=False):
|
||||||
self.disconnectWFplotEvents()
|
self.disconnectWFplotEvents()
|
||||||
if self.pg:
|
if self.pg:
|
||||||
self.dataPlot.plotWidget.getPlotItem().clear()
|
self.dataPlot.plotWidget.getPlotItem().clear()
|
||||||
self.dataPlot.plotWidget.hideAxis('bottom')
|
|
||||||
self.dataPlot.plotWidget.hideAxis('left')
|
|
||||||
else:
|
else:
|
||||||
for ax in self.dataPlot.axes:
|
for ax in self.dataPlot.axes:
|
||||||
ax.cla()
|
ax.cla()
|
||||||
@ -1863,6 +1966,9 @@ class MainWindow(QMainWindow):
|
|||||||
self.openEventAction.setEnabled(False)
|
self.openEventAction.setEnabled(False)
|
||||||
self.openEventsAutoAction.setEnabled(False)
|
self.openEventsAutoAction.setEnabled(False)
|
||||||
self.loadpilotevent.setEnabled(False)
|
self.loadpilotevent.setEnabled(False)
|
||||||
|
if not refresh_plot:
|
||||||
|
self.wf_scroll_area.setVisible(False)
|
||||||
|
self.no_data_label.setVisible(True)
|
||||||
self.disableSaveEventAction()
|
self.disableSaveEventAction()
|
||||||
self.draw()
|
self.draw()
|
||||||
|
|
||||||
@ -1871,7 +1977,7 @@ class MainWindow(QMainWindow):
|
|||||||
Open a modal thread to plot current waveform data.
|
Open a modal thread to plot current waveform data.
|
||||||
'''
|
'''
|
||||||
self.check_plot_quantity()
|
self.check_plot_quantity()
|
||||||
self.clearWaveformDataPlot()
|
self.clearWaveformDataPlot(refresh_plot=True)
|
||||||
self.wfp_thread = Thread(self, self.plotWaveformData,
|
self.wfp_thread = Thread(self, self.plotWaveformData,
|
||||||
arg=filter,
|
arg=filter,
|
||||||
progressText='Plotting waveform data...',
|
progressText='Plotting waveform data...',
|
||||||
@ -1904,9 +2010,10 @@ class MainWindow(QMainWindow):
|
|||||||
self.plot_method = 'fast'
|
self.plot_method = 'fast'
|
||||||
else:
|
else:
|
||||||
self.plot_method = 'normal'
|
self.plot_method = 'normal'
|
||||||
plots = 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)
|
nth_sample=int(nth_sample), method=self.plot_method)
|
||||||
return plots
|
plots, gaps = rval if rval else ([], [])
|
||||||
|
return plots, gaps
|
||||||
|
|
||||||
def adjustPlotHeight(self):
|
def adjustPlotHeight(self):
|
||||||
if self.pg:
|
if self.pg:
|
||||||
@ -2135,10 +2242,12 @@ class MainWindow(QMainWindow):
|
|||||||
|
|
||||||
def pickOnStation(self, gui_event):
|
def pickOnStation(self, gui_event):
|
||||||
if self.pg:
|
if self.pg:
|
||||||
if not gui_event.button() == 1:
|
button = gui_event.button()
|
||||||
|
if not button in [1, 4]:
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
if not gui_event.button == 1:
|
button = gui_event.button
|
||||||
|
if not button == 1:
|
||||||
return
|
return
|
||||||
|
|
||||||
if self.pg:
|
if self.pg:
|
||||||
@ -2149,27 +2258,59 @@ class MainWindow(QMainWindow):
|
|||||||
wfID = self.getWFID(ycoord)
|
wfID = self.getWFID(ycoord)
|
||||||
|
|
||||||
if wfID is None: return
|
if wfID is None: return
|
||||||
self.pickDialog(wfID)
|
|
||||||
|
|
||||||
def pickDialog(self, wfID, nextStation=False):
|
network = self.getNetworkName(wfID)
|
||||||
station = self.getStationName(wfID)
|
station = self.getStationName(wfID)
|
||||||
|
if button == 1:
|
||||||
|
self.pickDialog(wfID, network, station)
|
||||||
|
elif button == 4:
|
||||||
|
self.toggle_station_color(wfID, network, station)
|
||||||
|
|
||||||
|
def toggle_station_color(self, wfID, network, station):
|
||||||
|
black_pen = pg.mkPen((0, 0, 0))
|
||||||
|
red_pen = pg.mkPen((200, 50, 50))
|
||||||
|
line_item = self.dataPlot.plotWidget.getPlotItem().listDataItems()[wfID - 1]
|
||||||
|
current_pen = line_item.opts['pen']
|
||||||
|
nwst = '{}.{}'.format(network, station)
|
||||||
|
if current_pen == black_pen:
|
||||||
|
line_item.setPen(red_pen)
|
||||||
|
if not nwst in self.stations_highlighted:
|
||||||
|
self.stations_highlighted.append(nwst)
|
||||||
|
else:
|
||||||
|
line_item.setPen(black_pen)
|
||||||
|
if nwst in self.stations_highlighted:
|
||||||
|
self.stations_highlighted.pop(self.stations_highlighted.index(nwst))
|
||||||
|
|
||||||
|
def highlight_stations(self):
|
||||||
|
for wfID, value in self.getPlotWidget().getPlotDict().items():
|
||||||
|
station, channel, network = value
|
||||||
|
nwst = '{}.{}'.format(network, station)
|
||||||
|
if nwst in self.stations_highlighted:
|
||||||
|
self.toggle_station_color(wfID, network, station)
|
||||||
|
|
||||||
|
def pickDialog(self, wfID, network=None, station=None):
|
||||||
|
if not network:
|
||||||
network = self.getNetworkName(wfID)
|
network = self.getNetworkName(wfID)
|
||||||
if not station:
|
if not station:
|
||||||
|
station = self.getStationName(wfID)
|
||||||
|
if not station or not network:
|
||||||
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()
|
data = self.get_data().getOriginalWFData().copy()
|
||||||
event = self.get_current_event()
|
event = self.get_current_event()
|
||||||
|
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=data.select(station=station),
|
||||||
station=station, network=network,
|
station=station, network=network,
|
||||||
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,
|
||||||
filteroptions=self.filteroptions)
|
filteroptions=self.filteroptions, wftype=wftype)
|
||||||
if self.filterActionP.isChecked() or self.filterActionS.isChecked():
|
if self.filterActionP.isChecked() or self.filterActionS.isChecked():
|
||||||
pickDlg.currentPhase = self.getSeismicPhase()
|
pickDlg.currentPhase = self.getSeismicPhase()
|
||||||
pickDlg.filterWFData()
|
pickDlg.filterWFData()
|
||||||
pickDlg.nextStation.setChecked(nextStation)
|
pickDlg.nextStation.setChecked(self.nextStation)
|
||||||
|
pickDlg.nextStation.stateChanged.connect(self.toggle_next_station)
|
||||||
if pickDlg.exec_():
|
if pickDlg.exec_():
|
||||||
if pickDlg._dirty:
|
if pickDlg._dirty:
|
||||||
self.setDirty(True)
|
self.setDirty(True)
|
||||||
@ -2179,13 +2320,13 @@ class MainWindow(QMainWindow):
|
|||||||
self.enableSaveEventAction()
|
self.enableSaveEventAction()
|
||||||
if replot1 or replot2:
|
if replot1 or replot2:
|
||||||
self.plotWaveformDataThread()
|
self.plotWaveformDataThread()
|
||||||
self.drawPicks()
|
|
||||||
self.draw()
|
self.draw()
|
||||||
else:
|
else:
|
||||||
self.drawPicks(station)
|
self.drawPicks(station)
|
||||||
self.draw()
|
self.draw()
|
||||||
if pickDlg.nextStation.isChecked():
|
if self.nextStation:
|
||||||
self.pickDialog(wfID - 1, nextStation=pickDlg.nextStation.isChecked())
|
self.pickDialog(wfID - 1)
|
||||||
else:
|
else:
|
||||||
self.update_status('picks discarded ({0})'.format(station))
|
self.update_status('picks discarded ({0})'.format(station))
|
||||||
if not self.get_loc_flag() and self.check4Loc():
|
if not self.get_loc_flag() and self.check4Loc():
|
||||||
@ -2194,7 +2335,12 @@ class MainWindow(QMainWindow):
|
|||||||
elif self.get_loc_flag() and not self.check4Loc():
|
elif self.get_loc_flag() and not self.check4Loc():
|
||||||
self.set_loc_flag(False)
|
self.set_loc_flag(False)
|
||||||
|
|
||||||
|
def toggle_next_station(self, signal):
|
||||||
|
self.nextStation = bool(signal)
|
||||||
|
|
||||||
def addListItem(self, text):
|
def addListItem(self, text):
|
||||||
|
textlist = text.split('\n')
|
||||||
|
for text in textlist:
|
||||||
self.listWidget.addItem(text)
|
self.listWidget.addItem(text)
|
||||||
self.listWidget.scrollToBottom()
|
self.listWidget.scrollToBottom()
|
||||||
|
|
||||||
@ -2254,7 +2400,7 @@ class MainWindow(QMainWindow):
|
|||||||
|
|
||||||
def tune_autopicker(self):
|
def tune_autopicker(self):
|
||||||
'''
|
'''
|
||||||
Initiates TuneAutopicker widget use to interactively
|
Initiates TuneAutopicker widget used to interactively
|
||||||
tune parameters for autopicking algorithm.
|
tune parameters for autopicking algorithm.
|
||||||
'''
|
'''
|
||||||
# figures and canvas have to be iniated from the main GUI
|
# figures and canvas have to be iniated from the main GUI
|
||||||
@ -2263,7 +2409,7 @@ class MainWindow(QMainWindow):
|
|||||||
self.init_fig_dict()
|
self.init_fig_dict()
|
||||||
#if not self.tap:
|
#if not self.tap:
|
||||||
# init TuneAutopicker object
|
# init TuneAutopicker object
|
||||||
self.tap = TuneAutopicker(self)
|
self.tap = TuneAutopicker(self, self.obspy_dmt)
|
||||||
# first call of update to init tabs with empty canvas
|
# first call of update to init tabs with empty canvas
|
||||||
self.update_autopicker()
|
self.update_autopicker()
|
||||||
# connect update signal of TuneAutopicker with update function
|
# connect update signal of TuneAutopicker with update function
|
||||||
@ -2345,6 +2491,7 @@ class MainWindow(QMainWindow):
|
|||||||
# export current picks etc.
|
# export current picks etc.
|
||||||
self.exportAllEvents(['.xml'])
|
self.exportAllEvents(['.xml'])
|
||||||
|
|
||||||
|
wfpath = self.dataPlot.qcombo_processed.currentText() if self.obspy_dmt else ''
|
||||||
# define arguments for picker
|
# define arguments for picker
|
||||||
args = {'parameter': self._inputs,
|
args = {'parameter': self._inputs,
|
||||||
'station': 'all',
|
'station': 'all',
|
||||||
@ -2353,8 +2500,8 @@ class MainWindow(QMainWindow):
|
|||||||
'iplot': 0,
|
'iplot': 0,
|
||||||
'fig_dict': None,
|
'fig_dict': None,
|
||||||
'fig_dict_wadatijack': self.fig_dict_wadatijack,
|
'fig_dict_wadatijack': self.fig_dict_wadatijack,
|
||||||
'locflag': 0}
|
'savexml': False,
|
||||||
|
'obspyDMT_wfpath': wfpath}
|
||||||
# init pick thread
|
# init pick thread
|
||||||
self.mp_thread = QtCore.QThreadPool()
|
self.mp_thread = QtCore.QThreadPool()
|
||||||
self.mp_worker = Worker(autoPyLoT, args, redirect_stdout=True)
|
self.mp_worker = Worker(autoPyLoT, args, redirect_stdout=True)
|
||||||
@ -2363,6 +2510,7 @@ class MainWindow(QMainWindow):
|
|||||||
|
|
||||||
self.mp_worker.signals.message.connect(self.addListItem)
|
self.mp_worker.signals.message.connect(self.addListItem)
|
||||||
self.mp_worker.signals.result.connect(self.finalizeAutoPick)
|
self.mp_worker.signals.result.connect(self.finalizeAutoPick)
|
||||||
|
self.mp_worker.signals.finished.connect(self.enableAutoPickWidget)
|
||||||
|
|
||||||
self.mp_thread.start(self.mp_worker)
|
self.mp_thread.start(self.mp_worker)
|
||||||
|
|
||||||
@ -2377,7 +2525,6 @@ class MainWindow(QMainWindow):
|
|||||||
self.apd_sge.show()
|
self.apd_sge.show()
|
||||||
|
|
||||||
def finalizeAutoPick(self, result):
|
def finalizeAutoPick(self, result):
|
||||||
self.apw.enable(True)
|
|
||||||
if result:
|
if result:
|
||||||
self.init_canvas_dict_wadatijack()
|
self.init_canvas_dict_wadatijack()
|
||||||
for eventID in result.keys():
|
for eventID in result.keys():
|
||||||
@ -2393,6 +2540,9 @@ class MainWindow(QMainWindow):
|
|||||||
self.drawPicks(picktype='auto')
|
self.drawPicks(picktype='auto')
|
||||||
self.draw()
|
self.draw()
|
||||||
|
|
||||||
|
def enableAutoPickWidget(self, event=None):
|
||||||
|
self.apw.enable(True)
|
||||||
|
|
||||||
def get_event_from_id(self, eventID):
|
def get_event_from_id(self, eventID):
|
||||||
for event in self.project.eventlist:
|
for event in self.project.eventlist:
|
||||||
if event.pylot_id == eventID:
|
if event.pylot_id == eventID:
|
||||||
@ -2607,55 +2757,28 @@ class MainWindow(QMainWindow):
|
|||||||
"""
|
"""
|
||||||
if not self.okToContinue():
|
if not self.okToContinue():
|
||||||
return
|
return
|
||||||
settings = QSettings()
|
parameter = self._inputs
|
||||||
# get location tool hook
|
loctool = 'nll'
|
||||||
loctool = settings.value("loc/tool", "nll")
|
|
||||||
lt = locateTool[loctool]
|
lt = locateTool[loctool]
|
||||||
# get working directory
|
# get working directory
|
||||||
locroot = settings.value("{0}/rootPath".format(loctool), None)
|
locroot = parameter['nllocroot']
|
||||||
if locroot is None:
|
if locroot is None:
|
||||||
self.PyLoTprefs()
|
self.PyLoTprefs()
|
||||||
self.locate_event()
|
self.locate_event()
|
||||||
|
|
||||||
infile = settings.value("{0}/inputFile".format(loctool), None)
|
ctrfile = os.path.join(locroot, 'run', parameter['ctrfile'])
|
||||||
|
|
||||||
if not infile:
|
ttt = parameter['ttpatter']
|
||||||
caption = 'Select {0} input file'.format(loctool)
|
outfile = parameter['outpatter']
|
||||||
filt = "Supported file formats" \
|
|
||||||
" (*.in *.ini *.conf *.cfg)"
|
|
||||||
ans = QFileDialog().getOpenFileName(self, caption=caption,
|
|
||||||
filter=filt, dir=locroot)
|
|
||||||
if ans[0]:
|
|
||||||
infile = ans[0]
|
|
||||||
else:
|
|
||||||
QMessageBox.information(self,
|
|
||||||
self.tr('No infile selected'),
|
|
||||||
self.tr('Inputfile necessary for localization.'))
|
|
||||||
return
|
|
||||||
settings.setValue("{0}/inputFile".format(loctool), infile)
|
|
||||||
settings.sync()
|
|
||||||
if loctool == 'nll':
|
|
||||||
ttt = settings.value("{0}/travelTimeTables", None)
|
|
||||||
ok = False
|
|
||||||
if ttt is None:
|
|
||||||
while not ok:
|
|
||||||
text, ok = QInputDialog.getText(self, 'Pattern for travel time tables',
|
|
||||||
'Base name of travel time tables',
|
|
||||||
echo=QLineEdit.Normal,
|
|
||||||
text="ttime")
|
|
||||||
ttt = text
|
|
||||||
|
|
||||||
outfile = settings.value("{0}/outputFile".format(loctool),
|
|
||||||
os.path.split(os.tempnam())[-1])
|
|
||||||
eventname = self.get_current_event_name()
|
eventname = self.get_current_event_name()
|
||||||
obsdir = os.path.join(self._inputs['rootpath'], self._inputs['datapath'], self._inputs['database'], eventname)
|
obsdir = os.path.join(self._inputs['rootpath'], self._inputs['datapath'], self._inputs['database'], eventname)
|
||||||
self.saveData(event=self.get_current_event(), directory=obsdir, outformats='.obs')
|
self.saveData(event=self.get_current_event(), directory=obsdir, outformats='.obs')
|
||||||
filename = 'PyLoT_' + eventname
|
filename = 'PyLoT_' + eventname
|
||||||
locpath = os.path.join(locroot, 'loc', filename)
|
locpath = os.path.join(locroot, 'loc', filename)
|
||||||
phasefile = os.path.join(obsdir, filename + '.obs')
|
phasefile = os.path.join(obsdir, filename + '.obs')
|
||||||
lt.modify_inputs(infile, locroot, filename, phasefile, ttt)
|
lt.modify_inputs(ctrfile, locroot, filename, phasefile, ttt)
|
||||||
try:
|
try:
|
||||||
lt.locate(infile)
|
lt.locate(ctrfile)
|
||||||
except RuntimeError as e:
|
except RuntimeError as e:
|
||||||
print(e.message)
|
print(e.message)
|
||||||
#finally:
|
#finally:
|
||||||
@ -2721,10 +2844,7 @@ class MainWindow(QMainWindow):
|
|||||||
self.init_metadata()
|
self.init_metadata()
|
||||||
if not self.metadata:
|
if not self.metadata:
|
||||||
return
|
return
|
||||||
self.am_figure = Figure()
|
self.array_map = Array_map(self)
|
||||||
self.am_canvas = FigureCanvas(self.am_figure)
|
|
||||||
self.am_toolbar = NavigationToolbar(self.am_canvas, self)
|
|
||||||
self.array_map = map_projection(self)
|
|
||||||
# self.array_map_thread()
|
# self.array_map_thread()
|
||||||
self.array_layout.addWidget(self.array_map)
|
self.array_layout.addWidget(self.array_map)
|
||||||
self.tabs.setCurrentIndex(index)
|
self.tabs.setCurrentIndex(index)
|
||||||
@ -2763,7 +2883,8 @@ class MainWindow(QMainWindow):
|
|||||||
lon = event.origins[0].longitude
|
lon = event.origins[0].longitude
|
||||||
self.array_map.eventLoc = (lat, lon)
|
self.array_map.eventLoc = (lat, lon)
|
||||||
if self.get_current_event():
|
if self.get_current_event():
|
||||||
self.array_map.refresh_drawings(self.get_current_event().getPicks())
|
self.array_map.refresh_drawings(self.get_current_event().getPicks(),
|
||||||
|
self.get_current_event().getAutopicks(),)
|
||||||
self._eventChanged[1] = False
|
self._eventChanged[1] = False
|
||||||
|
|
||||||
def init_event_table(self, tabindex=2):
|
def init_event_table(self, tabindex=2):
|
||||||
@ -2817,14 +2938,6 @@ class MainWindow(QMainWindow):
|
|||||||
event.addNotes(notes)
|
event.addNotes(notes)
|
||||||
self.fill_eventbox()
|
self.fill_eventbox()
|
||||||
|
|
||||||
def set_background_color(items, color):
|
|
||||||
for item in items:
|
|
||||||
item.setBackground(color)
|
|
||||||
|
|
||||||
def set_foreground_color(items, color):
|
|
||||||
for item in items:
|
|
||||||
item.setForeground(color)
|
|
||||||
|
|
||||||
current_event = self.get_current_event()
|
current_event = self.get_current_event()
|
||||||
|
|
||||||
# generate delete icon
|
# generate delete icon
|
||||||
@ -2894,7 +3007,7 @@ class MainWindow(QMainWindow):
|
|||||||
if hasattr(event, 'origins'):
|
if hasattr(event, 'origins'):
|
||||||
if event.origins:
|
if event.origins:
|
||||||
origin = event.origins[0]
|
origin = event.origins[0]
|
||||||
item_time.setText(str(origin.time).split('.')[0])
|
item_time.setText(str(origin.time + 0).split('.')[0]) # +0 as workaround in case time=0s
|
||||||
item_lon.setText(str(origin.longitude))
|
item_lon.setText(str(origin.longitude))
|
||||||
item_lat.setText(str(origin.latitude))
|
item_lat.setText(str(origin.latitude))
|
||||||
item_depth.setText(str(origin.depth))
|
item_depth.setText(str(origin.depth))
|
||||||
@ -2928,11 +3041,7 @@ class MainWindow(QMainWindow):
|
|||||||
item_nmp, item_nap, item_ref, item_test, item_notes]
|
item_nmp, item_nap, item_ref, item_test, item_notes]
|
||||||
self.project._table.append(column)
|
self.project._table.append(column)
|
||||||
|
|
||||||
if index%2:
|
self.setItemColor(column, index, event, current_event)
|
||||||
set_background_color(column, QtGui.QColor(*(245, 245, 245, 255)))
|
|
||||||
|
|
||||||
if event == current_event:
|
|
||||||
set_foreground_color(column, QtGui.QColor(*(0, 143, 143, 255)))
|
|
||||||
|
|
||||||
# manipulate items
|
# manipulate items
|
||||||
item_ref.setBackground(self._ref_test_colors['ref'])
|
item_ref.setBackground(self._ref_test_colors['ref'])
|
||||||
@ -2954,6 +3063,24 @@ class MainWindow(QMainWindow):
|
|||||||
self.events_layout.addWidget(self.event_table)
|
self.events_layout.addWidget(self.event_table)
|
||||||
self.tabs.setCurrentIndex(tabindex)
|
self.tabs.setCurrentIndex(tabindex)
|
||||||
|
|
||||||
|
def setItemColor(self, item_list, index, event, current_event):
|
||||||
|
def set_background_color(items, color):
|
||||||
|
for item in items:
|
||||||
|
item.setBackground(color)
|
||||||
|
|
||||||
|
def set_foreground_color(items, color):
|
||||||
|
for item in items:
|
||||||
|
item.setForeground(color)
|
||||||
|
|
||||||
|
if index % 2:
|
||||||
|
set_background_color(item_list, QtGui.QColor(*(245, 245, 245, 255)))
|
||||||
|
|
||||||
|
if self.isEmpty(event.path):
|
||||||
|
set_foreground_color(item_list, QtGui.QColor(*(180, 180, 180, 255)))
|
||||||
|
|
||||||
|
if event == current_event:
|
||||||
|
set_background_color(item_list, QtGui.QColor(*(0, 143, 143, 255)))
|
||||||
|
|
||||||
def read_metadata_thread(self, fninv):
|
def read_metadata_thread(self, fninv):
|
||||||
self.rm_thread = Thread(self, read_metadata, arg=fninv, progressText='Reading metadata...',
|
self.rm_thread = Thread(self, read_metadata, arg=fninv, progressText='Reading metadata...',
|
||||||
pb_widget=self.mainProgressBarWidget)
|
pb_widget=self.mainProgressBarWidget)
|
||||||
@ -3003,19 +3130,17 @@ class MainWindow(QMainWindow):
|
|||||||
settings.setValue("inventoryFile", self.project.inv_path)
|
settings.setValue("inventoryFile", self.project.inv_path)
|
||||||
|
|
||||||
fninv = settings.value("inventoryFile", None)
|
fninv = settings.value("inventoryFile", None)
|
||||||
if fninv and ask_default:
|
if (fninv and ask_default) and not new:
|
||||||
ans = QMessageBox.question(self, self.tr("Use default metadata..."),
|
ans = QMessageBox.question(self, self.tr("Use default metadata..."),
|
||||||
self.tr(
|
self.tr(
|
||||||
"Do you want to use the default value for metadata?\n({})".format(fninv)),
|
"Do you want to use the default value for metadata?\n({})".format(fninv)),
|
||||||
QMessageBox.Yes | QMessageBox.No,
|
QMessageBox.Yes | QMessageBox.No,
|
||||||
QMessageBox.Yes)
|
QMessageBox.Yes)
|
||||||
if ans == QMessageBox.No:
|
if ans == QMessageBox.Yes:
|
||||||
if not set_inv(settings):
|
|
||||||
return None
|
|
||||||
elif ans == QMessageBox.Yes:
|
|
||||||
self.read_metadata_thread(fninv)
|
|
||||||
if fninv and not ask_default:
|
|
||||||
self.read_metadata_thread(fninv)
|
self.read_metadata_thread(fninv)
|
||||||
|
return
|
||||||
|
set_inv(settings)
|
||||||
|
|
||||||
|
|
||||||
def calc_magnitude(self, type='ML'):
|
def calc_magnitude(self, type='ML'):
|
||||||
self.init_metadata()
|
self.init_metadata()
|
||||||
@ -3197,9 +3322,11 @@ class MainWindow(QMainWindow):
|
|||||||
self.fill_eventbox()
|
self.fill_eventbox()
|
||||||
self.getPlotWidget().draw()
|
self.getPlotWidget().draw()
|
||||||
if self.plot_method == 'fast':
|
if self.plot_method == 'fast':
|
||||||
self.dataPlot.setPermText('MIN/MAX plot', color='red')
|
self.dataPlot.setPermText(1, ' MIN/MAX plot ', color='red')
|
||||||
else:
|
else:
|
||||||
self.dataPlot.setPermText()
|
self.dataPlot.setPermText(1)
|
||||||
|
self.dataPlot.setPermText(0, '| Number of traces: {} |'.format(len(self.getPlotWidget().getPlotDict())))
|
||||||
|
|
||||||
|
|
||||||
def _setDirty(self):
|
def _setDirty(self):
|
||||||
self.setDirty(True)
|
self.setDirty(True)
|
||||||
|
52
autoPyLoT.py
52
autoPyLoT.py
@ -34,7 +34,7 @@ __version__ = _getVersionString()
|
|||||||
|
|
||||||
|
|
||||||
def autoPyLoT(input_dict=None, parameter=None, inputfile=None, fnames=None, eventid=None, savepath=None,
|
def autoPyLoT(input_dict=None, parameter=None, inputfile=None, fnames=None, eventid=None, savepath=None,
|
||||||
savexml=True, station='all', iplot=0, ncores=0):
|
savexml=True, station='all', iplot=0, ncores=0, obspyDMT_wfpath=False):
|
||||||
"""
|
"""
|
||||||
Determine phase onsets automatically utilizing the automatic picking
|
Determine phase onsets automatically utilizing the automatic picking
|
||||||
algorithms by Kueperkoch et al. 2010/2012.
|
algorithms by Kueperkoch et al. 2010/2012.
|
||||||
@ -94,7 +94,6 @@ def autoPyLoT(input_dict=None, parameter=None, inputfile=None, fnames=None, even
|
|||||||
fig_dict = None
|
fig_dict = None
|
||||||
fig_dict_wadatijack = None
|
fig_dict_wadatijack = None
|
||||||
|
|
||||||
locflag = 1
|
|
||||||
if input_dict and isinstance(input_dict, dict):
|
if input_dict and isinstance(input_dict, dict):
|
||||||
if 'parameter' in input_dict:
|
if 'parameter' in input_dict:
|
||||||
parameter = input_dict['parameter']
|
parameter = input_dict['parameter']
|
||||||
@ -110,10 +109,10 @@ def autoPyLoT(input_dict=None, parameter=None, inputfile=None, fnames=None, even
|
|||||||
eventid = input_dict['eventid']
|
eventid = input_dict['eventid']
|
||||||
if 'iplot' in input_dict:
|
if 'iplot' in input_dict:
|
||||||
iplot = input_dict['iplot']
|
iplot = input_dict['iplot']
|
||||||
if 'locflag' in input_dict:
|
|
||||||
locflag = input_dict['locflag']
|
|
||||||
if 'savexml' in input_dict:
|
if 'savexml' in input_dict:
|
||||||
savexml = input_dict['savexml']
|
savexml = input_dict['savexml']
|
||||||
|
if 'obspyDMT_wfpath' in input_dict:
|
||||||
|
obspyDMT_wfpath = input_dict['obspyDMT_wfpath']
|
||||||
|
|
||||||
if not parameter:
|
if not parameter:
|
||||||
if inputfile:
|
if inputfile:
|
||||||
@ -151,7 +150,8 @@ def autoPyLoT(input_dict=None, parameter=None, inputfile=None, fnames=None, even
|
|||||||
datastructure.setExpandFields(exf)
|
datastructure.setExpandFields(exf)
|
||||||
|
|
||||||
# check if default location routine NLLoc is available
|
# check if default location routine NLLoc is available
|
||||||
if real_None(parameter['nllocbin']) and locflag:
|
if real_None(parameter['nllocbin']):
|
||||||
|
locflag = 1
|
||||||
# get NLLoc-root path
|
# get NLLoc-root path
|
||||||
nllocroot = parameter.get('nllocroot')
|
nllocroot = parameter.get('nllocroot')
|
||||||
# get path to NLLoc executable
|
# get path to NLLoc executable
|
||||||
@ -167,7 +167,7 @@ def autoPyLoT(input_dict=None, parameter=None, inputfile=None, fnames=None, even
|
|||||||
ttpat = parameter.get('ttpatter')
|
ttpat = parameter.get('ttpatter')
|
||||||
# pattern of NLLoc-output file
|
# pattern of NLLoc-output file
|
||||||
nllocoutpatter = parameter.get('outpatter')
|
nllocoutpatter = parameter.get('outpatter')
|
||||||
maxnumit = 3 # maximum number of iterations for re-picking
|
maxnumit = 2 # maximum number of iterations for re-picking
|
||||||
else:
|
else:
|
||||||
locflag = 0
|
locflag = 0
|
||||||
print(" !!! ")
|
print(" !!! ")
|
||||||
@ -175,6 +175,14 @@ def autoPyLoT(input_dict=None, parameter=None, inputfile=None, fnames=None, even
|
|||||||
print("!!No source parameter estimation possible!!")
|
print("!!No source parameter estimation possible!!")
|
||||||
print(" !!! ")
|
print(" !!! ")
|
||||||
|
|
||||||
|
wfpath_extension = ''
|
||||||
|
if obspyDMT_wfpath not in [None, False, 'False', '']:
|
||||||
|
wfpath_extension = obspyDMT_wfpath
|
||||||
|
print('Using obspyDMT structure. There will be no restitution, as pre-processed data are expected.')
|
||||||
|
if wfpath_extension != 'processed':
|
||||||
|
print('WARNING: Expecting wfpath_extension to be "processed" for'
|
||||||
|
' pre-processed data but received "{}" instead!!!'.format(wfpath_extension))
|
||||||
|
|
||||||
if not input_dict:
|
if not input_dict:
|
||||||
# started in production mode
|
# started in production mode
|
||||||
datapath = datastructure.expandDataPath()
|
datapath = datastructure.expandDataPath()
|
||||||
@ -219,6 +227,7 @@ def autoPyLoT(input_dict=None, parameter=None, inputfile=None, fnames=None, even
|
|||||||
glocflag = locflag
|
glocflag = locflag
|
||||||
for eventpath in events:
|
for eventpath in events:
|
||||||
evID = os.path.split(eventpath)[-1]
|
evID = os.path.split(eventpath)[-1]
|
||||||
|
event_datapath = os.path.join(eventpath, wfpath_extension)
|
||||||
fext = '.xml'
|
fext = '.xml'
|
||||||
filename = os.path.join(eventpath, 'PyLoT_' + evID + fext)
|
filename = os.path.join(eventpath, 'PyLoT_' + evID + fext)
|
||||||
try:
|
try:
|
||||||
@ -231,7 +240,7 @@ def autoPyLoT(input_dict=None, parameter=None, inputfile=None, fnames=None, even
|
|||||||
pylot_event = Event(eventpath) # event should be path to event directory
|
pylot_event = Event(eventpath) # event should be path to event directory
|
||||||
data.setEvtData(pylot_event)
|
data.setEvtData(pylot_event)
|
||||||
if fnames == 'None':
|
if fnames == 'None':
|
||||||
data.setWFData(glob.glob(os.path.join(datapath, eventpath, '*')))
|
data.setWFData(glob.glob(os.path.join(datapath, event_datapath, '*')))
|
||||||
# the following is necessary because within
|
# the following is necessary because within
|
||||||
# multiple event processing no event ID is provided
|
# multiple event processing no event ID is provided
|
||||||
# in autopylot.in
|
# in autopylot.in
|
||||||
@ -268,9 +277,15 @@ def autoPyLoT(input_dict=None, parameter=None, inputfile=None, fnames=None, even
|
|||||||
wfdat = check4doubled(wfdat)
|
wfdat = check4doubled(wfdat)
|
||||||
wfdat = trim_station_components(wfdat, trim_start=True, trim_end=False)
|
wfdat = trim_station_components(wfdat, trim_start=True, trim_end=False)
|
||||||
metadata = read_metadata(parameter.get('invdir'))
|
metadata = read_metadata(parameter.get('invdir'))
|
||||||
|
# TODO: (idea) read metadata from obspy_dmt database
|
||||||
|
# if not wfpath_extension:
|
||||||
|
# metadata = read_metadata(parameter.get('invdir'))
|
||||||
|
# else:
|
||||||
|
# metadata = None
|
||||||
|
corr_dat = None
|
||||||
|
if metadata:
|
||||||
# rotate stations to ZNE
|
# rotate stations to ZNE
|
||||||
wfdat = check4rotated(wfdat, metadata)
|
wfdat = check4rotated(wfdat, metadata)
|
||||||
corr_dat = None
|
|
||||||
if locflag:
|
if locflag:
|
||||||
print("Restitute data ...")
|
print("Restitute data ...")
|
||||||
corr_dat = restitute_data(wfdat.copy(), *metadata, ncores=ncores)
|
corr_dat = restitute_data(wfdat.copy(), *metadata, ncores=ncores)
|
||||||
@ -331,6 +346,7 @@ def autoPyLoT(input_dict=None, parameter=None, inputfile=None, fnames=None, even
|
|||||||
picks[stats]['P'].update(props)
|
picks[stats]['P'].update(props)
|
||||||
evt = moment_mag.updated_event()
|
evt = moment_mag.updated_event()
|
||||||
net_mw = moment_mag.net_magnitude()
|
net_mw = moment_mag.net_magnitude()
|
||||||
|
if net_mw is not None:
|
||||||
print("Network moment magnitude: %4.1f" % net_mw.mag)
|
print("Network moment magnitude: %4.1f" % net_mw.mag)
|
||||||
# calculate local (Richter) magntiude
|
# calculate local (Richter) magntiude
|
||||||
WAscaling = parameter.get('WAscaling')
|
WAscaling = parameter.get('WAscaling')
|
||||||
@ -348,6 +364,13 @@ def autoPyLoT(input_dict=None, parameter=None, inputfile=None, fnames=None, even
|
|||||||
evt = local_mag.updated_event(magscaling)
|
evt = local_mag.updated_event(magscaling)
|
||||||
net_ml = local_mag.net_magnitude(magscaling)
|
net_ml = local_mag.net_magnitude(magscaling)
|
||||||
print("Network local magnitude: %4.1f" % net_ml.mag)
|
print("Network local magnitude: %4.1f" % net_ml.mag)
|
||||||
|
if magscaling == None:
|
||||||
|
scaling = False
|
||||||
|
elif magscaling[0] != 0 and magscaling[1] != 0:
|
||||||
|
scaling = False
|
||||||
|
else:
|
||||||
|
scaling = True
|
||||||
|
if scaling:
|
||||||
print("Network local magnitude scaled with:")
|
print("Network local magnitude scaled with:")
|
||||||
print("%f * Ml + %f" % (magscaling[0], magscaling[1]))
|
print("%f * Ml + %f" % (magscaling[0], magscaling[1]))
|
||||||
else:
|
else:
|
||||||
@ -406,6 +429,7 @@ def autoPyLoT(input_dict=None, parameter=None, inputfile=None, fnames=None, even
|
|||||||
picks[stats]['P'].update(props)
|
picks[stats]['P'].update(props)
|
||||||
evt = moment_mag.updated_event()
|
evt = moment_mag.updated_event()
|
||||||
net_mw = moment_mag.net_magnitude()
|
net_mw = moment_mag.net_magnitude()
|
||||||
|
if net_mw is not None:
|
||||||
print("Network moment magnitude: %4.1f" % net_mw.mag)
|
print("Network moment magnitude: %4.1f" % net_mw.mag)
|
||||||
# calculate local (Richter) magntiude
|
# calculate local (Richter) magntiude
|
||||||
WAscaling = parameter.get('WAscaling')
|
WAscaling = parameter.get('WAscaling')
|
||||||
@ -424,6 +448,13 @@ def autoPyLoT(input_dict=None, parameter=None, inputfile=None, fnames=None, even
|
|||||||
evt = local_mag.updated_event(magscaling)
|
evt = local_mag.updated_event(magscaling)
|
||||||
net_ml = local_mag.net_magnitude(magscaling)
|
net_ml = local_mag.net_magnitude(magscaling)
|
||||||
print("Network local magnitude: %4.1f" % net_ml.mag)
|
print("Network local magnitude: %4.1f" % net_ml.mag)
|
||||||
|
if magscaling == None:
|
||||||
|
scaling = False
|
||||||
|
elif magscaling[0] != 0 and magscaling[1] != 0:
|
||||||
|
scaling = False
|
||||||
|
else:
|
||||||
|
scaling = True
|
||||||
|
if scaling:
|
||||||
print("Network local magnitude scaled with:")
|
print("Network local magnitude scaled with:")
|
||||||
print("%f * Ml + %f" % (magscaling[0], magscaling[1]))
|
print("%f * Ml + %f" % (magscaling[0], magscaling[1]))
|
||||||
else:
|
else:
|
||||||
@ -512,9 +543,12 @@ if __name__ == "__main__":
|
|||||||
parser.add_argument('-c', '-C', '--ncores', type=int,
|
parser.add_argument('-c', '-C', '--ncores', type=int,
|
||||||
action='store', default=0,
|
action='store', default=0,
|
||||||
help='''optional, number of CPU cores used for parallel processing (default: all available(=0))''')
|
help='''optional, number of CPU cores used for parallel processing (default: all available(=0))''')
|
||||||
|
parser.add_argument('-dmt', '-DMT', '--obspy_dmt_wfpath', type=str,
|
||||||
|
action='store', default=False,
|
||||||
|
help='''optional, wftype (raw, processed) used for obspyDMT database structure''')
|
||||||
|
|
||||||
cla = parser.parse_args()
|
cla = parser.parse_args()
|
||||||
|
|
||||||
picks = autoPyLoT(inputfile=str(cla.inputfile), fnames=str(cla.fnames),
|
picks = autoPyLoT(inputfile=str(cla.inputfile), fnames=str(cla.fnames),
|
||||||
eventid=str(cla.eventid), savepath=str(cla.spath),
|
eventid=str(cla.eventid), savepath=str(cla.spath),
|
||||||
ncores=cla.ncores, iplot=int(cla.iplot))
|
ncores=cla.ncores, iplot=int(cla.iplot), obspyDMT_wfpath=str(cla.obspy_dmt_wfpath))
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
#
|
#
|
||||||
# WARNING! All changes made in this file will be lost!
|
# WARNING! All changes made in this file will be lost!
|
||||||
|
|
||||||
from PyQt4 import QtCore
|
from PySide import QtCore
|
||||||
|
|
||||||
qt_resource_data = "\
|
qt_resource_data = "\
|
||||||
\x00\x00\x9e\x04\
|
\x00\x00\x9e\x04\
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
#
|
#
|
||||||
# WARNING! All changes made in this file will be lost!
|
# WARNING! All changes made in this file will be lost!
|
||||||
|
|
||||||
from PyQt4 import QtCore
|
from PySide import QtCore
|
||||||
|
|
||||||
qt_resource_data = "\
|
qt_resource_data = "\
|
||||||
\x00\x00\x9e\x04\
|
\x00\x00\x9e\x04\
|
||||||
|
@ -4,19 +4,19 @@
|
|||||||
%Parameters are optimized for %extent data sets!
|
%Parameters are optimized for %extent data sets!
|
||||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||||
#main settings#
|
#main settings#
|
||||||
#rootpath# %project path
|
/DATA/Insheim #rootpath# %project path
|
||||||
#datapath# %data path
|
EVENT_DATA/LOCAL #datapath# %data path
|
||||||
#database# %name of data base
|
2018.02_Insheim #database# %name of data base
|
||||||
#eventID# %event ID for single event processing (* for all events found in database)
|
e0006.038.18 #eventID# %event ID for single event processing (* for all events found in database)
|
||||||
#invdir# %full path to inventory or dataless-seed file
|
/DATA/Insheim/STAT_INFO #invdir# %full path to inventory or dataless-seed file
|
||||||
PILOT #datastructure# %choose data structure
|
PILOT #datastructure# %choose data structure
|
||||||
True #apverbose# %choose 'True' or 'False' for terminal output
|
True #apverbose# %choose 'True' or 'False' for terminal output
|
||||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||||
#NLLoc settings#
|
#NLLoc settings#
|
||||||
None #nllocbin# %path to NLLoc executable
|
/home/ludger/NLLOC #nllocbin# %path to NLLoc executable
|
||||||
None #nllocroot# %root of NLLoc-processing directory
|
/home/ludger/NLLOC/Insheim #nllocroot# %root of NLLoc-processing directory
|
||||||
None #phasefile# %name of autoPyLoT-output phase file for NLLoc
|
AUTOPHASES.obs #phasefile# %name of autoPyLoT-output phase file for NLLoc
|
||||||
None #ctrfile# %name of autoPyLoT-output control file for NLLoc
|
Insheim_min1d032016_auto.in #ctrfile# %name of autoPyLoT-output control file for NLLoc
|
||||||
ttime #ttpatter# %pattern of NLLoc ttimes from grid
|
ttime #ttpatter# %pattern of NLLoc ttimes from grid
|
||||||
AUTOLOC_nlloc #outpatter# %pattern of NLLoc-output file
|
AUTOLOC_nlloc #outpatter# %pattern of NLLoc-output file
|
||||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||||
@ -27,31 +27,31 @@ AUTOLOC_nlloc #outpatter# %pattern of
|
|||||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||||
#settings local magnitude#
|
#settings local magnitude#
|
||||||
1.11 0.0009 -2.0 #WAscaling# %Scaling relation (log(Ao)+Alog(r)+Br+C) of Wood-Anderson amplitude Ao [nm] If zeros are set, original Richter magnitude is calculated!
|
1.11 0.0009 -2.0 #WAscaling# %Scaling relation (log(Ao)+Alog(r)+Br+C) of Wood-Anderson amplitude Ao [nm] If zeros are set, original Richter magnitude is calculated!
|
||||||
1.0382 -0.447 #magscaling# %Scaling relation for derived local magnitude [a*Ml+b]. If zeros are set, no scaling of network magnitude is applied!
|
0.0 0.0 #magscaling# %Scaling relation for derived local magnitude [a*Ml+b]. If zeros are set, no scaling of network magnitude is applied!
|
||||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||||
#filter settings#
|
#filter settings#
|
||||||
1.0 1.0 #minfreq# %Lower filter frequency [P, S]
|
2.0 2.0 #minfreq# %Lower filter frequency [P, S]
|
||||||
10.0 10.0 #maxfreq# %Upper filter frequency [P, S]
|
30.0 15.0 #maxfreq# %Upper filter frequency [P, S]
|
||||||
2 2 #filter_order# %filter order [P, S]
|
3 3 #filter_order# %filter order [P, S]
|
||||||
bandpass bandpass #filter_type# %filter type (bandpass, bandstop, lowpass, highpass) [P, S]
|
bandpass bandpass #filter_type# %filter type (bandpass, bandstop, lowpass, highpass) [P, S]
|
||||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||||
#common settings picker#
|
#common settings picker#
|
||||||
local #extent# %extent of array ("local", "regional" or "global")
|
local #extent# %extent of array ("local", "regional" or "global")
|
||||||
15.0 #pstart# %start time [s] for calculating CF for P-picking
|
7.0 #pstart# %start time [s] for calculating CF for P-picking
|
||||||
60.0 #pstop# %end time [s] for calculating CF for P-picking
|
16.0 #pstop# %end time [s] for calculating CF for P-picking
|
||||||
-1.0 #sstart# %start time [s] relative to P-onset for calculating CF for S-picking
|
-0.5 #sstart# %start time [s] relative to P-onset for calculating CF for S-picking
|
||||||
10.0 #sstop# %end time [s] after P-onset for calculating CF for S-picking
|
10.0 #sstop# %end time [s] after P-onset for calculating CF for S-picking
|
||||||
True #use_taup# %use estimated traveltimes from TauPy for calculating windows for CF
|
False #use_taup# %use estimated traveltimes from TauPy for calculating windows for CF
|
||||||
iasp91 #taup_model# %define TauPy model for traveltime estimation
|
iasp91 #taup_model# %define TauPy model for traveltime estimation
|
||||||
2.0 10.0 #bpz1# %lower/upper corner freq. of first band pass filter Z-comp. [Hz]
|
2.0 20.0 #bpz1# %lower/upper corner freq. of first band pass filter Z-comp. [Hz]
|
||||||
2.0 12.0 #bpz2# %lower/upper corner freq. of second band pass filter Z-comp. [Hz]
|
2.0 30.0 #bpz2# %lower/upper corner freq. of second band pass filter Z-comp. [Hz]
|
||||||
2.0 8.0 #bph1# %lower/upper corner freq. of first band pass filter H-comp. [Hz]
|
2.0 10.0 #bph1# %lower/upper corner freq. of first band pass filter H-comp. [Hz]
|
||||||
2.0 10.0 #bph2# %lower/upper corner freq. of second band pass filter z-comp. [Hz]
|
2.0 15.0 #bph2# %lower/upper corner freq. of second band pass filter z-comp. [Hz]
|
||||||
#special settings for calculating CF#
|
#special settings for calculating CF#
|
||||||
%!!Edit the following only if you know what you are doing!!%
|
%!!Edit the following only if you know what you are doing!!%
|
||||||
#Z-component#
|
#Z-component#
|
||||||
HOS #algoP# %choose algorithm for P-onset determination (HOS, ARZ, or AR3)
|
HOS #algoP# %choose algorithm for P-onset determination (HOS, ARZ, or AR3)
|
||||||
7.0 #tlta# %for HOS-/AR-AIC-picker, length of LTA window [s]
|
4.0 #tlta# %for HOS-/AR-AIC-picker, length of LTA window [s]
|
||||||
4 #hosorder# %for HOS-picker, order of Higher Order Statistics
|
4 #hosorder# %for HOS-picker, order of Higher Order Statistics
|
||||||
2 #Parorder# %for AR-picker, order of AR process of Z-component
|
2 #Parorder# %for AR-picker, order of AR process of Z-component
|
||||||
1.2 #tdet1z# %for AR-picker, length of AR determination window [s] for Z-component, 1st pick
|
1.2 #tdet1z# %for AR-picker, length of AR determination window [s] for Z-component, 1st pick
|
||||||
@ -59,12 +59,12 @@ HOS #algoP# %choose algo
|
|||||||
0.6 #tdet2z# %for AR-picker, length of AR determination window [s] for Z-component, 2nd pick
|
0.6 #tdet2z# %for AR-picker, length of AR determination window [s] for Z-component, 2nd pick
|
||||||
0.2 #tpred2z# %for AR-picker, length of AR prediction window [s] for Z-component, 2nd pick
|
0.2 #tpred2z# %for AR-picker, length of AR prediction window [s] for Z-component, 2nd pick
|
||||||
0.001 #addnoise# %add noise to seismogram for stable AR prediction
|
0.001 #addnoise# %add noise to seismogram for stable AR prediction
|
||||||
3.0 0.1 0.5 1.0 #tsnrz# %for HOS/AR, window lengths for SNR-and slope estimation [tnoise, tsafetey, tsignal, tslope] [s]
|
3.0 0.0 1.0 0.5 #tsnrz# %for HOS/AR, window lengths for SNR-and slope estimation [tnoise, tsafetey, tsignal, tslope] [s]
|
||||||
3.0 #pickwinP# %for initial AIC pick, length of P-pick window [s]
|
3.0 #pickwinP# %for initial AIC pick, length of P-pick window [s]
|
||||||
6.0 #Precalcwin# %for HOS/AR, window length [s] for recalculation of CF (relative to 1st pick)
|
6.0 #Precalcwin# %for HOS/AR, window length [s] for recalculation of CF (relative to 1st pick)
|
||||||
0.2 #aictsmooth# %for HOS/AR, take average of samples for smoothing of AIC-function [s]
|
0.4 #aictsmooth# %for HOS/AR, take average of samples for smoothing of AIC-function [s]
|
||||||
0.1 #tsmoothP# %for HOS/AR, take average of samples for smoothing CF [s]
|
0.1 #tsmoothP# %for HOS/AR, take average of samples for smoothing CF [s]
|
||||||
0.001 #ausP# %for HOS/AR, artificial uplift of samples (aus) of CF (P)
|
0.4 #ausP# %for HOS/AR, artificial uplift of samples (aus) of CF (P)
|
||||||
1.3 #nfacP# %for HOS/AR, noise factor for noise level determination (P)
|
1.3 #nfacP# %for HOS/AR, noise factor for noise level determination (P)
|
||||||
#H-components#
|
#H-components#
|
||||||
ARH #algoS# %choose algorithm for S-onset determination (ARH or AR3)
|
ARH #algoS# %choose algorithm for S-onset determination (ARH or AR3)
|
||||||
@ -75,7 +75,7 @@ ARH #algoS# %choose algo
|
|||||||
4 #Sarorder# %for AR-picker, order of AR process of H-components
|
4 #Sarorder# %for AR-picker, order of AR process of H-components
|
||||||
5.0 #Srecalcwin# %for AR-picker, window length [s] for recalculation of CF (2nd pick) (H)
|
5.0 #Srecalcwin# %for AR-picker, window length [s] for recalculation of CF (2nd pick) (H)
|
||||||
4.0 #pickwinS# %for initial AIC pick, length of S-pick window [s]
|
4.0 #pickwinS# %for initial AIC pick, length of S-pick window [s]
|
||||||
2.0 0.3 1.5 1.0 #tsnrh# %for ARH/AR3, window lengths for SNR-and slope estimation [tnoise, tsafetey, tsignal, tslope] [s]
|
2.0 0.2 1.5 1.0 #tsnrh# %for ARH/AR3, window lengths for SNR-and slope estimation [tnoise, tsafetey, tsignal, tslope] [s]
|
||||||
1.0 #aictsmoothS# %for AIC-picker, take average of samples for smoothing of AIC-function [s]
|
1.0 #aictsmoothS# %for AIC-picker, take average of samples for smoothing of AIC-function [s]
|
||||||
0.7 #tsmoothS# %for AR-picker, take average of samples for smoothing CF [s] (S)
|
0.7 #tsmoothS# %for AR-picker, take average of samples for smoothing CF [s] (S)
|
||||||
0.9 #ausS# %for HOS/AR, artificial uplift of samples (aus) of CF (S)
|
0.9 #ausS# %for HOS/AR, artificial uplift of samples (aus) of CF (S)
|
||||||
@ -85,16 +85,16 @@ ARH #algoS# %choose algo
|
|||||||
2.0 #minFMSNR# %miniumum required SNR for first-motion determination
|
2.0 #minFMSNR# %miniumum required SNR for first-motion determination
|
||||||
0.2 #fmpickwin# %pick window around P onset for calculating zero crossings
|
0.2 #fmpickwin# %pick window around P onset for calculating zero crossings
|
||||||
#quality assessment#
|
#quality assessment#
|
||||||
0.02 0.04 0.08 0.16 #timeerrorsP# %discrete time errors [s] corresponding to picking weights [0 1 2 3] for P
|
0.04 0.08 0.16 0.32 #timeerrorsP# %discrete time errors [s] corresponding to picking weights [0 1 2 3] for P
|
||||||
0.04 0.08 0.16 0.32 #timeerrorsS# %discrete time errors [s] corresponding to picking weights [0 1 2 3] for S
|
0.05 0.10 0.20 0.40 #timeerrorsS# %discrete time errors [s] corresponding to picking weights [0 1 2 3] for S
|
||||||
0.8 #minAICPslope# %below this slope [counts/s] the initial P pick is rejected
|
0.8 #minAICPslope# %below this slope [counts/s] the initial P pick is rejected
|
||||||
1.1 #minAICPSNR# %below this SNR the initial P pick is rejected
|
1.1 #minAICPSNR# %below this SNR the initial P pick is rejected
|
||||||
1.0 #minAICSslope# %below this slope [counts/s] the initial S pick is rejected
|
1.0 #minAICSslope# %below this slope [counts/s] the initial S pick is rejected
|
||||||
1.5 #minAICSSNR# %below this SNR the initial S pick is rejected
|
1.5 #minAICSSNR# %below this SNR the initial S pick is rejected
|
||||||
1.0 #minsiglength# %length of signal part for which amplitudes must exceed noiselevel [s]
|
1.0 #minsiglength# %length of signal part for which amplitudes must exceed noiselevel [s]
|
||||||
1.0 #noisefactor# %noiselevel*noisefactor=threshold
|
1.1 #noisefactor# %noiselevel*noisefactor=threshold
|
||||||
10.0 #minpercent# %required percentage of amplitudes exceeding threshold
|
50.0 #minpercent# %required percentage of amplitudes exceeding threshold
|
||||||
1.5 #zfac# %P-amplitude must exceed at least zfac times RMS-S amplitude
|
1.1 #zfac# %P-amplitude must exceed at least zfac times RMS-S amplitude
|
||||||
6.0 #mdttolerance# %maximum allowed deviation of P picks from median [s]
|
5.0 #mdttolerance# %maximum allowed deviation of P picks from median [s]
|
||||||
1.0 #wdttolerance# %maximum allowed deviation from Wadati-diagram
|
1.0 #wdttolerance# %maximum allowed deviation from Wadati-diagram
|
||||||
5.0 #jackfactor# %pick is removed if the variance of the subgroup with the pick removed is larger than the mean variance of all subgroups times safety factor
|
2.0 #jackfactor# %pick is removed if the variance of the subgroup with the pick removed is larger than the mean variance of all subgroups times safety factor
|
||||||
|
@ -6,6 +6,7 @@ Revised/extended summer 2017.
|
|||||||
|
|
||||||
:author: Ludger Küperkoch / MAGS2 EP3 working group
|
:author: Ludger Küperkoch / MAGS2 EP3 working group
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import matplotlib.pyplot as plt
|
import matplotlib.pyplot as plt
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import obspy.core.event as ope
|
import obspy.core.event as ope
|
||||||
@ -121,7 +122,13 @@ class Magnitude(object):
|
|||||||
|
|
||||||
def net_magnitude(self, magscaling=None):
|
def net_magnitude(self, magscaling=None):
|
||||||
if self:
|
if self:
|
||||||
if magscaling is not None and str(magscaling) is not '[0.0, 0.0]':
|
if magscaling == None:
|
||||||
|
scaling = False
|
||||||
|
elif magscaling[0] != 0 and magscaling[1] != 0:
|
||||||
|
scaling = False
|
||||||
|
else:
|
||||||
|
scaling = True
|
||||||
|
if scaling:
|
||||||
# scaling necessary
|
# scaling necessary
|
||||||
print("Scaling network magnitude ...")
|
print("Scaling network magnitude ...")
|
||||||
mag = ope.Magnitude(
|
mag = ope.Magnitude(
|
||||||
@ -140,7 +147,6 @@ class Magnitude(object):
|
|||||||
station_count=len(self.magnitudes),
|
station_count=len(self.magnitudes),
|
||||||
azimuthal_gap=self.origin_id.get_referred_object().quality.azimuthal_gap)
|
azimuthal_gap=self.origin_id.get_referred_object().quality.azimuthal_gap)
|
||||||
return mag
|
return mag
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
class LocalMagnitude(Magnitude):
|
class LocalMagnitude(Magnitude):
|
||||||
@ -219,7 +225,7 @@ class LocalMagnitude(Magnitude):
|
|||||||
sqH = np.sqrt(power_sum)
|
sqH = np.sqrt(power_sum)
|
||||||
|
|
||||||
# get time array
|
# get time array
|
||||||
th = np.arange(0, len(sqH) * dt, dt)
|
th=np.arange(0, st[0].stats.npts/st[0].stats.sampling_rate, st[0].stats.delta)
|
||||||
# get maximum peak within pick window
|
# get maximum peak within pick window
|
||||||
iwin = getsignalwin(th, t0 - stime, self.calc_win)
|
iwin = getsignalwin(th, t0 - stime, self.calc_win)
|
||||||
ii = min([iwin[len(iwin) - 1], len(th)])
|
ii = min([iwin[len(iwin) - 1], len(th)])
|
||||||
|
@ -15,7 +15,7 @@ from pylot.core.util.event import Event
|
|||||||
from pylot.core.util.utils import fnConstructor, full_range, remove_underscores, check4gaps, check4doubled, \
|
from pylot.core.util.utils import fnConstructor, full_range, remove_underscores, check4gaps, check4doubled, \
|
||||||
check4rotated, trim_station_components
|
check4rotated, trim_station_components
|
||||||
import pylot.core.loc.velest as velest
|
import pylot.core.loc.velest as velest
|
||||||
|
from pylot.core.util.obspyDMT_interface import qml_from_obspyDMT
|
||||||
|
|
||||||
class Data(object):
|
class Data(object):
|
||||||
"""
|
"""
|
||||||
@ -43,7 +43,7 @@ class Data(object):
|
|||||||
elif isinstance(evtdata, dict):
|
elif isinstance(evtdata, dict):
|
||||||
evt = readPILOTEvent(**evtdata)
|
evt = readPILOTEvent(**evtdata)
|
||||||
evtdata = evt
|
evtdata = evt
|
||||||
elif isinstance(evtdata, str):
|
elif type(evtdata) in [str, unicode]:
|
||||||
try:
|
try:
|
||||||
cat = read_events(evtdata)
|
cat = read_events(evtdata)
|
||||||
if len(cat) is not 1:
|
if len(cat) is not 1:
|
||||||
@ -60,6 +60,8 @@ class Data(object):
|
|||||||
raise NotImplementedError('PILOT location information '
|
raise NotImplementedError('PILOT location information '
|
||||||
'read support not yet '
|
'read support not yet '
|
||||||
'implemeted.')
|
'implemeted.')
|
||||||
|
elif 'event.pkl' in evtdata:
|
||||||
|
evtdata = qml_from_obspyDMT(evtdata)
|
||||||
else:
|
else:
|
||||||
raise e
|
raise e
|
||||||
else:
|
else:
|
||||||
@ -72,6 +74,7 @@ class Data(object):
|
|||||||
self.wforiginal = None
|
self.wforiginal = None
|
||||||
self.cuttimes = None
|
self.cuttimes = None
|
||||||
self.dirty = False
|
self.dirty = False
|
||||||
|
self.processed = None
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return str(self.wfdata)
|
return str(self.wfdata)
|
||||||
@ -368,7 +371,7 @@ class Data(object):
|
|||||||
data.filter(**kwargs)
|
data.filter(**kwargs)
|
||||||
self.dirty = True
|
self.dirty = True
|
||||||
|
|
||||||
def setWFData(self, fnames, checkRotated=False, metadata=None, obspy_dmt=False):
|
def setWFData(self, fnames, fnames_syn=None, checkRotated=False, metadata=None):
|
||||||
"""
|
"""
|
||||||
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
|
||||||
@ -377,30 +380,31 @@ class Data(object):
|
|||||||
self.wfdata = Stream()
|
self.wfdata = Stream()
|
||||||
self.wforiginal = None
|
self.wforiginal = None
|
||||||
self.wfsyn = Stream()
|
self.wfsyn = Stream()
|
||||||
wffnames = None
|
# if obspy_dmt:
|
||||||
wffnames_syn = None
|
# wfdir = 'raw'
|
||||||
wfdir = 'processed' if 'processed' in [fname.split('/')[-1] for fname in fnames] else 'raw'
|
# self.processed = False
|
||||||
if obspy_dmt:
|
# for fname in fnames:
|
||||||
for fpath in fnames:
|
# if fname.endswith('processed'):
|
||||||
if fpath.endswith(wfdir):
|
# wfdir = 'processed'
|
||||||
wffnames = [os.path.join(fpath, fname) for fname in os.listdir(fpath)]
|
# self.processed = True
|
||||||
if 'syngine' in fpath.split('/')[-1]:
|
# break
|
||||||
wffnames_syn = [os.path.join(fpath, fname) for fname in os.listdir(fpath)]
|
# for fpath in fnames:
|
||||||
else:
|
# if fpath.endswith(wfdir):
|
||||||
wffnames = fnames
|
# wffnames = [os.path.join(fpath, fname) for fname in os.listdir(fpath)]
|
||||||
if wffnames is not None:
|
# if 'syngine' in fpath.split('/')[-1]:
|
||||||
self.appendWFData(wffnames)
|
# wffnames_syn = [os.path.join(fpath, fname) for fname in os.listdir(fpath)]
|
||||||
if wffnames_syn is not None:
|
# else:
|
||||||
self.appendWFData(wffnames_syn, synthetic=True)
|
# wffnames = fnames
|
||||||
|
if fnames is not None:
|
||||||
|
self.appendWFData(fnames)
|
||||||
|
if fnames_syn is not None:
|
||||||
|
self.appendWFData(fnames_syn, synthetic=True)
|
||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# various pre-processing steps:
|
# various pre-processing steps:
|
||||||
# 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 doubled channels
|
|
||||||
check4gaps(self.wfdata)
|
|
||||||
check4doubled(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)
|
||||||
@ -441,7 +445,7 @@ class Data(object):
|
|||||||
except SacIOError as se:
|
except SacIOError as se:
|
||||||
warnmsg += '{0}\n{1}\n'.format(fname, se)
|
warnmsg += '{0}\n{1}\n'.format(fname, se)
|
||||||
if warnmsg:
|
if warnmsg:
|
||||||
warnmsg = 'WARNING: unable to read\n' + warnmsg
|
warnmsg = 'WARNING in appendWFData: unable to read waveform data\n' + warnmsg
|
||||||
print(warnmsg)
|
print(warnmsg)
|
||||||
|
|
||||||
def getWFData(self):
|
def getWFData(self):
|
||||||
|
@ -100,9 +100,9 @@ def autopickevent(data, param, iplot=0, fig_dict=None, fig_dict_wadatijack=None,
|
|||||||
|
|
||||||
# quality control
|
# quality control
|
||||||
# median check and jackknife on P-onset times
|
# median check and jackknife on P-onset times
|
||||||
jk_checked_onsets = checkPonsets(all_onsets, mdttolerance, jackfactor, 1, fig_dict_wadatijack)
|
jk_checked_onsets = checkPonsets(all_onsets, mdttolerance, jackfactor, iplot, fig_dict_wadatijack)
|
||||||
# check S-P times (Wadati)
|
# check S-P times (Wadati)
|
||||||
wadationsets = wadaticheck(jk_checked_onsets, wdttolerance, 1, fig_dict_wadatijack)
|
wadationsets = wadaticheck(jk_checked_onsets, wdttolerance, iplot, fig_dict_wadatijack)
|
||||||
return wadationsets
|
return wadationsets
|
||||||
|
|
||||||
|
|
||||||
@ -1057,8 +1057,64 @@ def nautopickstation(wfstream, pickparam, verbose=False,
|
|||||||
# read your pylot.in for details!
|
# read your pylot.in for details!
|
||||||
plt_flag = 0
|
plt_flag = 0
|
||||||
|
|
||||||
# get picking parameter dictionaries
|
# special parameters for P picking
|
||||||
p_params, s_params, first_motion_params, signal_length_params = get_pickparams(pickparam)
|
algoP = pickparam.get('algoP')
|
||||||
|
pstart = pickparam.get('pstart')
|
||||||
|
pstop = pickparam.get('pstop')
|
||||||
|
thosmw = pickparam.get('tlta')
|
||||||
|
tsnrz = pickparam.get('tsnrz')
|
||||||
|
hosorder = pickparam.get('hosorder')
|
||||||
|
bpz1 = pickparam.get('bpz1')
|
||||||
|
bpz2 = pickparam.get('bpz2')
|
||||||
|
pickwinP = pickparam.get('pickwinP')
|
||||||
|
aictsmoothP = pickparam.get('aictsmooth')
|
||||||
|
tsmoothP = pickparam.get('tsmoothP')
|
||||||
|
ausP = pickparam.get('ausP')
|
||||||
|
nfacP = pickparam.get('nfacP')
|
||||||
|
tpred1z = pickparam.get('tpred1z')
|
||||||
|
tdet1z = pickparam.get('tdet1z')
|
||||||
|
tpred2z = pickparam.get('tpred2z')
|
||||||
|
tdet2z = pickparam.get('tdet2z')
|
||||||
|
Parorder = pickparam.get('Parorder')
|
||||||
|
addnoise = pickparam.get('addnoise')
|
||||||
|
Precalcwin = pickparam.get('Precalcwin')
|
||||||
|
minAICPslope = pickparam.get('minAICPslope')
|
||||||
|
minAICPSNR = pickparam.get('minAICPSNR')
|
||||||
|
timeerrorsP = pickparam.get('timeerrorsP')
|
||||||
|
# special parameters for S picking
|
||||||
|
algoS = pickparam.get('algoS')
|
||||||
|
sstart = pickparam.get('sstart')
|
||||||
|
sstop = pickparam.get('sstop')
|
||||||
|
use_taup = real_Bool(pickparam.get('use_taup'))
|
||||||
|
taup_model = pickparam.get('taup_model')
|
||||||
|
bph1 = pickparam.get('bph1')
|
||||||
|
bph2 = pickparam.get('bph2')
|
||||||
|
tsnrh = pickparam.get('tsnrh')
|
||||||
|
pickwinS = pickparam.get('pickwinS')
|
||||||
|
tpred1h = pickparam.get('tpred1h')
|
||||||
|
tdet1h = pickparam.get('tdet1h')
|
||||||
|
tpred2h = pickparam.get('tpred2h')
|
||||||
|
tdet2h = pickparam.get('tdet2h')
|
||||||
|
Sarorder = pickparam.get('Sarorder')
|
||||||
|
aictsmoothS = pickparam.get('aictsmoothS')
|
||||||
|
tsmoothS = pickparam.get('tsmoothS')
|
||||||
|
ausS = pickparam.get('ausS')
|
||||||
|
minAICSslope = pickparam.get('minAICSslope')
|
||||||
|
minAICSSNR = pickparam.get('minAICSSNR')
|
||||||
|
Srecalcwin = pickparam.get('Srecalcwin')
|
||||||
|
nfacS = pickparam.get('nfacS')
|
||||||
|
timeerrorsS = pickparam.get('timeerrorsS')
|
||||||
|
# parameters for first-motion determination
|
||||||
|
minFMSNR = pickparam.get('minFMSNR')
|
||||||
|
fmpickwin = pickparam.get('fmpickwin')
|
||||||
|
minfmweight = pickparam.get('minfmweight')
|
||||||
|
# parameters for checking signal length
|
||||||
|
minsiglength = pickparam.get('minsiglength')
|
||||||
|
minpercent = pickparam.get('minpercent')
|
||||||
|
nfacsl = pickparam.get('noisefactor')
|
||||||
|
# parameter to check for spuriously picked S onset
|
||||||
|
zfac = pickparam.get('zfac')
|
||||||
|
# path to inventory-, dataless- or resp-files
|
||||||
|
|
||||||
# initialize output
|
# initialize output
|
||||||
Pweight = 4 # weight for P onset
|
Pweight = 4 # weight for P onset
|
||||||
@ -1159,6 +1215,8 @@ def nautopickstation(wfstream, pickparam, verbose=False,
|
|||||||
if p_params['use_taup'] is True:
|
if p_params['use_taup'] is True:
|
||||||
Lc = np.inf
|
Lc = np.inf
|
||||||
print('autopickstation: use_taup flag active.')
|
print('autopickstation: use_taup flag active.')
|
||||||
|
if not metadata:
|
||||||
|
metadata = [None, None]
|
||||||
if not metadata[1]:
|
if not metadata[1]:
|
||||||
print('Warning: Could not use TauPy to estimate onsets as there are no metadata given.')
|
print('Warning: Could not use TauPy to estimate onsets as there are no metadata given.')
|
||||||
else:
|
else:
|
||||||
@ -1207,7 +1265,7 @@ def nautopickstation(wfstream, pickparam, verbose=False,
|
|||||||
return
|
return
|
||||||
|
|
||||||
Ldiff = Lwf - abs(Lc)
|
Ldiff = Lwf - abs(Lc)
|
||||||
if Ldiff < 0 or pstop <= pstart:
|
if Ldiff <= 0 or pstop <= pstart or pstop - pstart <= thosmw:
|
||||||
msg = 'autopickstation: Cutting times are too large for actual ' \
|
msg = 'autopickstation: Cutting times are too large for actual ' \
|
||||||
'waveform!\nUsing entire waveform instead!'
|
'waveform!\nUsing entire waveform instead!'
|
||||||
if verbose: print(msg)
|
if verbose: print(msg)
|
||||||
@ -1345,8 +1403,8 @@ def nautopickstation(wfstream, pickparam, verbose=False,
|
|||||||
elif p_params['algoP'] == 'ARZ':
|
elif p_params['algoP'] == 'ARZ':
|
||||||
# calculate ARZ-CF using subclass ARZcf of class
|
# calculate ARZ-CF using subclass ARZcf of class
|
||||||
# CharcteristicFunction
|
# CharcteristicFunction
|
||||||
cf2 = ARZcf(z_copy, cuttimes2, p_params['tpred1z'], p_params['Parorder'], p_params['tdet1z'],
|
cf2 = ARZcf(z_copy, cuttimes2, tpred2z, Parorder, tdet2z,
|
||||||
p_params['addnoise']) # instance of ARZcf
|
addnoise) # instance of ARZcf
|
||||||
##############################################################
|
##############################################################
|
||||||
# get refined onset time from CF2 using class Picker
|
# get refined onset time from CF2 using class Picker
|
||||||
assert isinstance(cf2, CharacteristicFunction), 'cf2 is not set ' \
|
assert isinstance(cf2, CharacteristicFunction), 'cf2 is not set ' \
|
||||||
@ -1401,7 +1459,8 @@ def nautopickstation(wfstream, pickparam, verbose=False,
|
|||||||
msg = "autopickstation: P-weight: {0}, " \
|
msg = "autopickstation: P-weight: {0}, " \
|
||||||
"SNR: {1}, SNR[dB]: {2}, Polarity: {3}".format(Pweight, SNRP, SNRPdB, FM)
|
"SNR: {1}, SNR[dB]: {2}, Polarity: {3}".format(Pweight, SNRP, SNRPdB, FM)
|
||||||
print(msg)
|
print(msg)
|
||||||
msg = 'autopickstation: Refined P-Pick: {} s | P-Error: {} s'.format(mpickP, Perror)
|
msg = 'autopickstation: Refind P-Pick: {} s | P-Error: {} s'.format(zdat[0].stats.starttime \
|
||||||
|
+ mpickP, Perror)
|
||||||
print(msg)
|
print(msg)
|
||||||
Sflag = 1
|
Sflag = 1
|
||||||
|
|
||||||
@ -1667,7 +1726,8 @@ def nautopickstation(wfstream, pickparam, verbose=False,
|
|||||||
lpickS = lpick[ipick]
|
lpickS = lpick[ipick]
|
||||||
Serror = pickerr[ipick]
|
Serror = pickerr[ipick]
|
||||||
|
|
||||||
msg = 'autopickstation: Refined S-Pick: {} s | S-Error: {} s'.format(mpickS, Serror)
|
msg = 'autopickstation: Refined S-Pick: {} s | S-Error: {} s'.format(hdat[0].stats.starttime \
|
||||||
|
+ mpickS, Serror)
|
||||||
print(msg)
|
print(msg)
|
||||||
|
|
||||||
# get SNR
|
# get SNR
|
||||||
|
@ -78,7 +78,7 @@ class CharacteristicFunction(object):
|
|||||||
return self.cut
|
return self.cut
|
||||||
|
|
||||||
def setCut(self, cut):
|
def setCut(self, cut):
|
||||||
self.cut = cut
|
self.cut = (int(cut[0]), int(cut[1]))
|
||||||
|
|
||||||
def getTime1(self):
|
def getTime1(self):
|
||||||
return self.t1
|
return self.t1
|
||||||
|
@ -233,12 +233,41 @@ class AICPicker(AutoPicker):
|
|||||||
|
|
||||||
# quality assessment using SNR and slope from CF
|
# quality assessment using SNR and slope from CF
|
||||||
if self.Pick is not None:
|
if self.Pick is not None:
|
||||||
self.Data[0].data = check_counts_ms(self.Data[0].data)
|
# get noise window
|
||||||
|
inoise = getnoisewin(self.Tcf, self.Pick, self.TSNR[0], self.TSNR[1])
|
||||||
|
# check, if these are counts or m/s, important for slope estimation!
|
||||||
|
# this is quick and dirty, better solution? #Todo wtf
|
||||||
|
if max(self.Data[0].data < 1e-3) and max(self.Data[0].data >= 1e-6):
|
||||||
|
self.Data[0].data = self.Data[0].data * 1000000.
|
||||||
|
elif max(self.Data[0].data < 1e-6):
|
||||||
|
self.Data[0].data = self.Data[0].data * 1e13
|
||||||
|
# get signal window
|
||||||
|
isignal = getsignalwin(self.Tcf, self.Pick, self.TSNR[2])
|
||||||
|
if len(isignal) == 0:
|
||||||
|
return
|
||||||
|
ii = min([isignal[len(isignal) - 1], len(self.Tcf)])
|
||||||
|
isignal = isignal[0:ii]
|
||||||
|
try:
|
||||||
|
self.Data[0].data[isignal]
|
||||||
|
except IndexError as e:
|
||||||
|
msg = "Time series out of bounds! {}".format(e)
|
||||||
|
print(msg)
|
||||||
|
return
|
||||||
# calculate SNR from CF
|
# calculate SNR from CF
|
||||||
# subtract beginning of Tcf to get sample index in self.Data, which
|
self.SNR = max(abs(self.Data[0].data[isignal])) / \
|
||||||
# is cut out off trace
|
abs(np.mean(self.Data[0].data[inoise]))
|
||||||
self.SNR = getSNR(self.Data, self.TSNR, self.Pick-self.Tcf[0])[0] # TODO Check wether this yields similar results
|
|
||||||
# calculate slope from CF after initial pick
|
# calculate slope from CF after initial pick
|
||||||
|
# get slope window
|
||||||
|
tslope = self.TSNR[3] # slope determination window
|
||||||
|
tsafety = self.TSNR[1] # safety gap, AIC is usually a little bit too late
|
||||||
|
if tsafety >= 0:
|
||||||
|
islope = np.where((self.Tcf <= min([self.Pick + tslope + tsafety, self.Tcf[-1]])) \
|
||||||
|
& (self.Tcf >= self.Pick)) # TODO: put this in a seperate function like getsignalwin
|
||||||
|
else:
|
||||||
|
islope = np.where((self.Tcf <= min([self.Pick + tslope, self.Tcf[-1]])) \
|
||||||
|
& (self.Tcf >= self.Pick + tsafety)) # TODO: put this in a seperate function like getsignalwin
|
||||||
|
# find maximum within slope determination window
|
||||||
|
# 'cause slope should be calculated up to first local minimum only!
|
||||||
try:
|
try:
|
||||||
self.slope, iislope, datafit = calcSlope(self.Data, self.aicsmooth, self.Tcf, self.Pick, self.TSNR)
|
self.slope, iislope, datafit = calcSlope(self.Data, self.aicsmooth, self.Tcf, self.Pick, self.TSNR)
|
||||||
except Exception:
|
except Exception:
|
||||||
@ -264,6 +293,56 @@ class AICPicker(AutoPicker):
|
|||||||
pass
|
pass
|
||||||
plt.close(fig)
|
plt.close(fig)
|
||||||
return
|
return
|
||||||
|
if len(dataslope) < 2:
|
||||||
|
print('No or not enough data in slope window found!')
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
imaxs, = argrelmax(dataslope)
|
||||||
|
imaxs.size
|
||||||
|
imax = imaxs[0]
|
||||||
|
except ValueError as e:
|
||||||
|
print(e, 'picker: argrelmax not working!')
|
||||||
|
imax = np.argmax(dataslope)
|
||||||
|
iislope = islope[0][0:imax + 1]
|
||||||
|
if len(iislope) < 2:
|
||||||
|
# calculate slope from initial onset to maximum of AIC function
|
||||||
|
print("AICPicker: Not enough data samples left for slope calculation!")
|
||||||
|
print("Calculating slope from initial onset to maximum of AIC function ...")
|
||||||
|
imax = np.argmax(aicsmooth[islope[0][0:-1]])
|
||||||
|
if imax == 0:
|
||||||
|
print("AICPicker: Maximum for slope determination right at the beginning of the window!")
|
||||||
|
print("Choose longer slope determination window!")
|
||||||
|
if self.iplot > 1:
|
||||||
|
if self.fig == None or self.fig == 'None':
|
||||||
|
fig = plt.figure()
|
||||||
|
plt_flag = 1
|
||||||
|
else:
|
||||||
|
fig = self.fig
|
||||||
|
ax = fig.add_subplot(111)
|
||||||
|
x = self.Data[0].data
|
||||||
|
ax.plot(self.Tcf, x / max(x), color=self._linecolor, linewidth=0.7, label='(HOS-/AR-) Data')
|
||||||
|
ax.plot(self.Tcf, aicsmooth / max(aicsmooth), 'r', label='Smoothed AIC-CF')
|
||||||
|
ax.legend(loc=1)
|
||||||
|
ax.set_xlabel('Time [s] since %s' % self.Data[0].stats.starttime)
|
||||||
|
ax.set_yticks([])
|
||||||
|
ax.set_title(self.Data[0].stats.station)
|
||||||
|
if plt_flag == 1:
|
||||||
|
fig.show()
|
||||||
|
try: input()
|
||||||
|
except SyntaxError: pass
|
||||||
|
plt.close(fig)
|
||||||
|
return
|
||||||
|
iislope = islope[0][0:imax+1]
|
||||||
|
dataslope = self.Data[0].data[iislope]
|
||||||
|
# calculate slope as polynomal fit of order 1
|
||||||
|
xslope = np.arange(0, len(dataslope), 1)
|
||||||
|
P = np.polyfit(xslope, dataslope, 1)
|
||||||
|
datafit = np.polyval(P, xslope)
|
||||||
|
if datafit[0] >= datafit[-1]:
|
||||||
|
print('AICPicker: Negative slope, bad onset skipped!')
|
||||||
|
else:
|
||||||
|
self.slope = 1 / (len(dataslope) * self.Data[0].stats.delta) * (datafit[-1] - datafit[0])
|
||||||
|
|
||||||
else:
|
else:
|
||||||
self.SNR = -1
|
self.SNR = -1
|
||||||
self.slope = -1
|
self.slope = -1
|
||||||
@ -305,19 +384,18 @@ class AICPicker(AutoPicker):
|
|||||||
label='Slope Window')
|
label='Slope Window')
|
||||||
ax2.plot(self.Tcf[iislope], datafit, 'g', linewidth=2, label='Slope')
|
ax2.plot(self.Tcf[iislope], datafit, 'g', linewidth=2, label='Slope')
|
||||||
|
|
||||||
|
if self.slope is not None:
|
||||||
ax1.set_title('Station %s, SNR=%7.2f, Slope= %12.2f counts/s' % (self.Data[0].stats.station,
|
ax1.set_title('Station %s, SNR=%7.2f, Slope= %12.2f counts/s' % (self.Data[0].stats.station,
|
||||||
self.SNR, self.slope))
|
self.SNR, self.slope))
|
||||||
|
else:
|
||||||
|
ax1.set_title('Station %s, SNR=%7.2f' % (self.Data[0].stats.station, self.SNR))
|
||||||
ax2.set_xlabel('Time [s] since %s' % self.Data[0].stats.starttime)
|
ax2.set_xlabel('Time [s] since %s' % self.Data[0].stats.starttime)
|
||||||
ax2.set_ylabel('Counts')
|
ax2.set_ylabel('Counts')
|
||||||
ax2.set_yticks([])
|
ax2.set_yticks([])
|
||||||
ax2.legend(loc=1)
|
ax2.legend(loc=1)
|
||||||
if plt_flag == 1:
|
|
||||||
fig.show()
|
|
||||||
try: input()
|
|
||||||
except SyntaxError: pass
|
|
||||||
plt.close(fig)
|
|
||||||
else:
|
else:
|
||||||
ax1.set_title(self.Data[0].stats.station)
|
ax1.set_title(self.Data[0].stats.station)
|
||||||
|
|
||||||
if plt_flag == 1:
|
if plt_flag == 1:
|
||||||
fig.show()
|
fig.show()
|
||||||
try: input()
|
try: input()
|
||||||
|
458
pylot/core/util/array_map.py
Normal file
458
pylot/core/util/array_map.py
Normal file
@ -0,0 +1,458 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
import numpy as np
|
||||||
|
import obspy
|
||||||
|
from PySide import QtGui
|
||||||
|
from mpl_toolkits.basemap import Basemap
|
||||||
|
from matplotlib.figure import Figure
|
||||||
|
from mpl_toolkits.axes_grid1.inset_locator import inset_axes
|
||||||
|
from pylot.core.util.widgets import PickDlg, PylotCanvas
|
||||||
|
from scipy.interpolate import griddata
|
||||||
|
|
||||||
|
plt.interactive(False)
|
||||||
|
|
||||||
|
|
||||||
|
class Array_map(QtGui.QWidget):
|
||||||
|
def __init__(self, parent, figure=None):
|
||||||
|
'''
|
||||||
|
Create a map of the array.
|
||||||
|
:param parent: PyLoT Mainwindow class
|
||||||
|
:param figure:
|
||||||
|
'''
|
||||||
|
QtGui.QWidget.__init__(self)
|
||||||
|
self._parent = parent
|
||||||
|
self.metadata_type = parent.metadata[0]
|
||||||
|
self.metadata = parent.metadata[1]
|
||||||
|
self.picks = None
|
||||||
|
self.picks_dict = None
|
||||||
|
self.autopicks_dict = None
|
||||||
|
self.eventLoc = None
|
||||||
|
self.figure = figure
|
||||||
|
self.init_graphics()
|
||||||
|
self.init_stations()
|
||||||
|
self.init_basemap(resolution='l')
|
||||||
|
self.init_map()
|
||||||
|
self._style = parent._style
|
||||||
|
# self.show()
|
||||||
|
|
||||||
|
def init_map(self):
|
||||||
|
self.init_lat_lon_dimensions()
|
||||||
|
self.init_lat_lon_grid()
|
||||||
|
self.init_x_y_dimensions()
|
||||||
|
self.connectSignals()
|
||||||
|
self.draw_everything()
|
||||||
|
self.canvas.setZoomBorders2content()
|
||||||
|
|
||||||
|
def onpick(self, event):
|
||||||
|
ind = event.ind
|
||||||
|
button = event.mouseevent.button
|
||||||
|
if ind == [] or not button == 1:
|
||||||
|
return
|
||||||
|
data = self._parent.get_data().getWFData()
|
||||||
|
for index in ind:
|
||||||
|
station = str(self._station_onpick_ids[index].split('.')[-1])
|
||||||
|
try:
|
||||||
|
data = data.select(station=station)
|
||||||
|
if not data:
|
||||||
|
self._warn('No data for station {}'.format(station))
|
||||||
|
return
|
||||||
|
pickDlg = PickDlg(self._parent, parameter=self._parent._inputs,
|
||||||
|
data=data,
|
||||||
|
station=station,
|
||||||
|
picks=self._parent.get_current_event().getPick(station),
|
||||||
|
autopicks=self._parent.get_current_event().getAutopick(station),
|
||||||
|
filteroptions=self._parent.filteroptions)
|
||||||
|
except Exception as e:
|
||||||
|
message = 'Could not generate Plot for station {st}.\n {er}'.format(st=station, er=e)
|
||||||
|
self._warn(message)
|
||||||
|
print(message, e)
|
||||||
|
return
|
||||||
|
pyl_mw = self._parent
|
||||||
|
try:
|
||||||
|
if pickDlg.exec_():
|
||||||
|
pyl_mw.setDirty(True)
|
||||||
|
pyl_mw.update_status('picks accepted ({0})'.format(station))
|
||||||
|
replot = pyl_mw.get_current_event().setPick(station, pickDlg.getPicks())
|
||||||
|
self._refresh_drawings()
|
||||||
|
if replot:
|
||||||
|
pyl_mw.plotWaveformData()
|
||||||
|
pyl_mw.drawPicks()
|
||||||
|
pyl_mw.draw()
|
||||||
|
else:
|
||||||
|
pyl_mw.drawPicks(station)
|
||||||
|
pyl_mw.draw()
|
||||||
|
else:
|
||||||
|
pyl_mw.update_status('picks discarded ({0})'.format(station))
|
||||||
|
except Exception as e:
|
||||||
|
message = 'Could not save picks for station {st}.\n{er}'.format(st=station, er=e)
|
||||||
|
self._warn(message)
|
||||||
|
print(message, e)
|
||||||
|
|
||||||
|
def connectSignals(self):
|
||||||
|
self.comboBox_phase.currentIndexChanged.connect(self._refresh_drawings)
|
||||||
|
self.comboBox_am.currentIndexChanged.connect(self._refresh_drawings)
|
||||||
|
self.canvas.mpl_connect('motion_notify_event', self.mouse_moved)
|
||||||
|
#self.zoom_id = self.basemap.ax.figure.canvas.mpl_connect('scroll_event', self.zoom)
|
||||||
|
|
||||||
|
def _from_dict(self, function, key):
|
||||||
|
return function(self.stations_dict.values(), key=lambda x: x[key])[key]
|
||||||
|
|
||||||
|
def get_min_from_stations(self, key):
|
||||||
|
return self._from_dict(min, key)
|
||||||
|
|
||||||
|
def get_max_from_stations(self, key):
|
||||||
|
return self._from_dict(max, key)
|
||||||
|
|
||||||
|
def get_min_from_picks(self):
|
||||||
|
return min(self.picks_rel.values())
|
||||||
|
|
||||||
|
def get_max_from_picks(self):
|
||||||
|
return max(self.picks_rel.values())
|
||||||
|
|
||||||
|
def mouse_moved(self, event):
|
||||||
|
if not event.inaxes == self.main_ax:
|
||||||
|
return
|
||||||
|
x = event.xdata
|
||||||
|
y = event.ydata
|
||||||
|
lat, lon = self.basemap(x, y, inverse=True)
|
||||||
|
self.status_label.setText('Latitude: {}, Longitude: {}'.format(lat, lon))
|
||||||
|
|
||||||
|
def current_picks_dict(self):
|
||||||
|
picktype = self.comboBox_am.currentText()
|
||||||
|
auto_manu = {'auto': self.autopicks_dict,
|
||||||
|
'manual': self.picks_dict}
|
||||||
|
return auto_manu[picktype]
|
||||||
|
|
||||||
|
def init_graphics(self):
|
||||||
|
if not self.figure:
|
||||||
|
self.figure = Figure()
|
||||||
|
|
||||||
|
self.status_label = QtGui.QLabel()
|
||||||
|
|
||||||
|
self.main_ax = self.figure.add_subplot(111)
|
||||||
|
self.canvas = PylotCanvas(self.figure, parent=self._parent, multicursor=True,
|
||||||
|
panZoomX=False, panZoomY=False)
|
||||||
|
|
||||||
|
self.main_box = QtGui.QVBoxLayout()
|
||||||
|
self.setLayout(self.main_box)
|
||||||
|
|
||||||
|
self.top_row = QtGui.QHBoxLayout()
|
||||||
|
self.main_box.addLayout(self.top_row, 1)
|
||||||
|
|
||||||
|
self.comboBox_phase = QtGui.QComboBox()
|
||||||
|
self.comboBox_phase.insertItem(0, 'P')
|
||||||
|
self.comboBox_phase.insertItem(1, 'S')
|
||||||
|
|
||||||
|
self.comboBox_am = QtGui.QComboBox()
|
||||||
|
self.comboBox_am.insertItem(0, 'auto')
|
||||||
|
self.comboBox_am.insertItem(1, 'manual')
|
||||||
|
|
||||||
|
self.top_row.addWidget(QtGui.QLabel('Select a phase: '))
|
||||||
|
self.top_row.addWidget(self.comboBox_phase)
|
||||||
|
self.top_row.setStretch(1, 1) # set stretch of item 1 to 1
|
||||||
|
self.top_row.addWidget(QtGui.QLabel('Pick type: '))
|
||||||
|
self.top_row.addWidget(self.comboBox_am)
|
||||||
|
self.top_row.setStretch(3, 1) # set stretch of item 1 to 1
|
||||||
|
|
||||||
|
self.main_box.addWidget(self.canvas, 1)
|
||||||
|
self.main_box.addWidget(self.status_label, 0)
|
||||||
|
|
||||||
|
|
||||||
|
def init_stations(self):
|
||||||
|
def stat_info_from_parser(parser):
|
||||||
|
stations_dict = {}
|
||||||
|
for station in parser.stations:
|
||||||
|
station_name = station[0].station_call_letters
|
||||||
|
network_name = station[0].network_code
|
||||||
|
if not station_name in stations_dict.keys():
|
||||||
|
st_id = network_name + '.' + station_name
|
||||||
|
stations_dict[st_id] = {'latitude': station[0].latitude,
|
||||||
|
'longitude': station[0].longitude}
|
||||||
|
return stations_dict
|
||||||
|
|
||||||
|
def stat_info_from_inventory(inventory):
|
||||||
|
stations_dict = {}
|
||||||
|
for network in inventory.networks:
|
||||||
|
for station in network.stations:
|
||||||
|
station_name = station.code
|
||||||
|
network_name = network_name.code
|
||||||
|
if not station_name in stations_dict.keys():
|
||||||
|
st_id = network_name + '.' + station_name
|
||||||
|
stations_dict[st_id] = {'latitude': station[0].latitude,
|
||||||
|
'longitude': station[0].longitude}
|
||||||
|
return stations_dict
|
||||||
|
|
||||||
|
read_stat = {'xml': stat_info_from_inventory,
|
||||||
|
'dless': stat_info_from_parser}
|
||||||
|
|
||||||
|
self.stations_dict = read_stat[self.metadata_type](self.metadata)
|
||||||
|
self.latmin = self.get_min_from_stations('latitude')
|
||||||
|
self.lonmin = self.get_min_from_stations('longitude')
|
||||||
|
self.latmax = self.get_max_from_stations('latitude')
|
||||||
|
self.lonmax = self.get_max_from_stations('longitude')
|
||||||
|
|
||||||
|
def init_picks(self):
|
||||||
|
def get_picks(station_dict):
|
||||||
|
picks = {}
|
||||||
|
phase = self.comboBox_phase.currentText()
|
||||||
|
for st_id in station_dict.keys():
|
||||||
|
try:
|
||||||
|
station_name = st_id.split('.')[-1]
|
||||||
|
pick = self.current_picks_dict()[station_name][phase]
|
||||||
|
if pick['picker'] == 'auto':
|
||||||
|
if pick['weight'] > 3:
|
||||||
|
continue
|
||||||
|
picks[st_id] = pick['mpp']
|
||||||
|
except KeyError:
|
||||||
|
continue
|
||||||
|
except Exception as e:
|
||||||
|
print('Cannot display pick for station {}. Reason: {}'.format(station_name, e))
|
||||||
|
return picks
|
||||||
|
|
||||||
|
def get_picks_rel(picks):
|
||||||
|
picks_rel = {}
|
||||||
|
picks_utc = []
|
||||||
|
for pick in picks.values():
|
||||||
|
if type(pick) is obspy.core.utcdatetime.UTCDateTime:
|
||||||
|
picks_utc.append(pick)
|
||||||
|
self._earliest_picktime = min(picks_utc)
|
||||||
|
for st_id, pick in picks.items():
|
||||||
|
if type(pick) is obspy.core.utcdatetime.UTCDateTime:
|
||||||
|
pick -= self._earliest_picktime
|
||||||
|
picks_rel[st_id] = pick
|
||||||
|
return picks_rel
|
||||||
|
|
||||||
|
self.picks = get_picks(self.stations_dict)
|
||||||
|
self.picks_rel = get_picks_rel(self.picks)
|
||||||
|
|
||||||
|
def init_lat_lon_dimensions(self):
|
||||||
|
# init minimum and maximum lon and lat dimensions
|
||||||
|
self.londim = self.lonmax - self.lonmin
|
||||||
|
self.latdim = self.latmax - self.latmin
|
||||||
|
|
||||||
|
def init_x_y_dimensions(self):
|
||||||
|
# transformation of lat/lon to ax coordinate system
|
||||||
|
for st_id, coords in self.stations_dict.items():
|
||||||
|
lat, lon = coords['latitude'], coords['longitude']
|
||||||
|
coords['x'], coords['y'] = self.basemap(lon, lat)
|
||||||
|
|
||||||
|
self.xdim = self.get_max_from_stations('x') - self.get_min_from_stations('x')
|
||||||
|
self.ydim = self.get_max_from_stations('y') - self.get_min_from_stations('y')
|
||||||
|
|
||||||
|
def init_basemap(self, resolution='l'):
|
||||||
|
# basemap = Basemap(projection=projection, resolution = resolution, ax=self.main_ax)
|
||||||
|
width = 5e6
|
||||||
|
height = 2e6
|
||||||
|
basemap = Basemap(projection='lcc', resolution=resolution, ax=self.main_ax,
|
||||||
|
width=width, height=height,
|
||||||
|
lat_0=(self.latmin + self.latmax) / 2.,
|
||||||
|
lon_0=(self.lonmin + self.lonmax) / 2.)
|
||||||
|
|
||||||
|
# basemap.fillcontinents(color=None, lake_color='aqua',zorder=1)
|
||||||
|
basemap.drawmapboundary(zorder=2) # fill_color='darkblue')
|
||||||
|
basemap.shadedrelief(zorder=3)
|
||||||
|
basemap.drawcountries(zorder=4)
|
||||||
|
basemap.drawstates(zorder=5)
|
||||||
|
basemap.drawcoastlines(zorder=6)
|
||||||
|
# labels = [left,right,top,bottom]
|
||||||
|
parallels = np.arange(-90, 90, 5.)
|
||||||
|
parallels_small = np.arange(-90, 90, 2.5)
|
||||||
|
basemap.drawparallels(parallels_small, linewidth=0.5, zorder=7)
|
||||||
|
basemap.drawparallels(parallels, zorder=7)
|
||||||
|
meridians = np.arange(-180, 180, 5.)
|
||||||
|
meridians_small = np.arange(-180, 180, 2.5)
|
||||||
|
basemap.drawmeridians(meridians_small, linewidth=0.5, zorder=7)
|
||||||
|
basemap.drawmeridians(meridians, zorder=7)
|
||||||
|
self.basemap = basemap
|
||||||
|
self.figure._tight = True
|
||||||
|
self.figure.tight_layout()
|
||||||
|
|
||||||
|
def init_lat_lon_grid(self, nstep=250):
|
||||||
|
# create a regular grid to display colormap
|
||||||
|
lataxis = np.linspace(self.latmin, self.latmax, nstep)
|
||||||
|
lonaxis = np.linspace(self.lonmin, self.lonmax, nstep)
|
||||||
|
self.longrid, self.latgrid = np.meshgrid(lonaxis, lataxis)
|
||||||
|
|
||||||
|
def init_picksgrid(self):
|
||||||
|
picks, lats, lons = self.get_picks_lat_lon()
|
||||||
|
try:
|
||||||
|
self.picksgrid_active = griddata((lats, lons), picks, (self.latgrid, self.longrid),
|
||||||
|
method='linear')
|
||||||
|
except Exception as e:
|
||||||
|
self._warn('Could not init picksgrid: {}'.format(e))
|
||||||
|
|
||||||
|
def get_st_lat_lon_for_plot(self):
|
||||||
|
stations = []
|
||||||
|
latitudes = []
|
||||||
|
longitudes = []
|
||||||
|
for st_id, coords in self.stations_dict.items():
|
||||||
|
stations.append(st_id)
|
||||||
|
latitudes.append(coords['latitude'])
|
||||||
|
longitudes.append(coords['longitude'])
|
||||||
|
return stations, latitudes, longitudes
|
||||||
|
|
||||||
|
def get_st_x_y_for_plot(self):
|
||||||
|
stations = []
|
||||||
|
xs = []
|
||||||
|
ys = []
|
||||||
|
for st_id, coords in self.stations_dict.items():
|
||||||
|
stations.append(st_id)
|
||||||
|
xs.append(coords['x'])
|
||||||
|
ys.append(coords['y'])
|
||||||
|
return stations, xs, ys
|
||||||
|
|
||||||
|
def get_picks_lat_lon(self):
|
||||||
|
picks = []
|
||||||
|
latitudes = []
|
||||||
|
longitudes = []
|
||||||
|
for st_id, pick in self.picks_rel.items():
|
||||||
|
picks.append(pick)
|
||||||
|
latitudes.append(self.stations_dict[st_id]['latitude'])
|
||||||
|
longitudes.append(self.stations_dict[st_id]['longitude'])
|
||||||
|
return picks, latitudes, longitudes
|
||||||
|
|
||||||
|
def draw_contour_filled(self, nlevel='50'):
|
||||||
|
levels = np.linspace(self.get_min_from_picks(), self.get_max_from_picks(), nlevel)
|
||||||
|
self.contourf = self.basemap.contourf(self.longrid, self.latgrid, self.picksgrid_active,
|
||||||
|
levels, latlon=True, zorder=9, alpha=0.5)
|
||||||
|
|
||||||
|
def scatter_all_stations(self):
|
||||||
|
stations, lats, lons = self.get_st_lat_lon_for_plot()
|
||||||
|
self.sc = self.basemap.scatter(lons, lats, s=50, facecolor='none', latlon=True,
|
||||||
|
zorder=10, picker=True, edgecolor='m', label='Not Picked')
|
||||||
|
self.cid = self.canvas.mpl_connect('pick_event', self.onpick)
|
||||||
|
self._station_onpick_ids = stations
|
||||||
|
if self.eventLoc:
|
||||||
|
lats, lons = self.eventLoc
|
||||||
|
self.sc_event = self.basemap.scatter(lons, lats, s=100, facecolor='red',
|
||||||
|
latlon=True, zorder=11, label='Event (might be outside map region)')
|
||||||
|
|
||||||
|
def scatter_picked_stations(self):
|
||||||
|
picks, lats, lons = self.get_picks_lat_lon()
|
||||||
|
# workaround because of an issue with latlon transformation of arrays with len <3
|
||||||
|
if len(lons) <= 2 and len(lats) <= 2:
|
||||||
|
self.sc_picked = self.basemap.scatter(lons[0], lats[0], s=50, facecolor='white',
|
||||||
|
c=picks[0], latlon=True, zorder=11, label='Picked')
|
||||||
|
if len(lons) == 2 and len(lats) == 2:
|
||||||
|
self.sc_picked = self.basemap.scatter(lons[1], lats[1], s=50, facecolor='white',
|
||||||
|
c=picks[1], latlon=True, zorder=11)
|
||||||
|
else:
|
||||||
|
self.sc_picked = self.basemap.scatter(lons, lats, s=50, facecolor='white',
|
||||||
|
c=picks, latlon=True, zorder=11, label='Picked')
|
||||||
|
|
||||||
|
def annotate_ax(self):
|
||||||
|
self.annotations = []
|
||||||
|
stations, xs, ys = self.get_st_x_y_for_plot()
|
||||||
|
for st, x, y in zip(stations, xs, ys):
|
||||||
|
self.annotations.append(self.main_ax.annotate(' %s' % st, xy=(x, y),
|
||||||
|
fontsize='x-small', color='white', zorder=12))
|
||||||
|
self.legend = self.main_ax.legend(loc=1)
|
||||||
|
self.legend.get_frame().set_facecolor((1, 1, 1, 0.75))
|
||||||
|
|
||||||
|
def add_cbar(self, label):
|
||||||
|
self.cbax_bg = inset_axes(self.main_ax, width="6%", height="75%", loc=5)
|
||||||
|
cbax = inset_axes(self.main_ax, width='2%', height='70%', loc=5)
|
||||||
|
cbar = self.main_ax.figure.colorbar(self.sc_picked, cax = cbax)
|
||||||
|
cbar.set_label(label)
|
||||||
|
cbax.yaxis.tick_left()
|
||||||
|
cbax.yaxis.set_label_position('left')
|
||||||
|
for spine in self.cbax_bg.spines.values():
|
||||||
|
spine.set_visible(False)
|
||||||
|
self.cbax_bg.yaxis.set_ticks([])
|
||||||
|
self.cbax_bg.xaxis.set_ticks([])
|
||||||
|
self.cbax_bg.patch.set_facecolor((1, 1, 1, 0.75))
|
||||||
|
return cbar
|
||||||
|
|
||||||
|
def refresh_drawings(self, picks=None, autopicks=None):
|
||||||
|
self.picks_dict = picks
|
||||||
|
self.autopicks_dict = autopicks
|
||||||
|
self._refresh_drawings()
|
||||||
|
|
||||||
|
def _refresh_drawings(self):
|
||||||
|
self.remove_drawings()
|
||||||
|
self.draw_everything()
|
||||||
|
|
||||||
|
def draw_everything(self):
|
||||||
|
if self.picks_dict or self.autopicks_dict:
|
||||||
|
self.init_picks()
|
||||||
|
if len(self.picks) >= 3:
|
||||||
|
self.init_picksgrid()
|
||||||
|
self.draw_contour_filled()
|
||||||
|
self.scatter_all_stations()
|
||||||
|
if self.picks_dict or self.autopicks_dict:
|
||||||
|
self.scatter_picked_stations()
|
||||||
|
self.cbar = self.add_cbar(label='Time relative to first onset ({}) [s]'.format(self._earliest_picktime))
|
||||||
|
self.comboBox_phase.setEnabled(True)
|
||||||
|
else:
|
||||||
|
self.comboBox_phase.setEnabled(False)
|
||||||
|
self.annotate_ax()
|
||||||
|
self.canvas.draw()
|
||||||
|
|
||||||
|
def remove_drawings(self):
|
||||||
|
if hasattr(self, 'cbar'):
|
||||||
|
self.cbar.remove()
|
||||||
|
self.cbax_bg.remove()
|
||||||
|
del (self.cbar, self.cbax_bg)
|
||||||
|
if hasattr(self, 'sc_picked'):
|
||||||
|
self.sc_picked.remove()
|
||||||
|
del (self.sc_picked)
|
||||||
|
if hasattr(self, 'sc_event'):
|
||||||
|
self.sc_event.remove()
|
||||||
|
del (self.sc_event)
|
||||||
|
if hasattr(self, 'contourf'):
|
||||||
|
self.remove_contourf()
|
||||||
|
del (self.contourf)
|
||||||
|
if hasattr(self, 'cid'):
|
||||||
|
self.canvas.mpl_disconnect(self.cid)
|
||||||
|
del (self.cid)
|
||||||
|
try:
|
||||||
|
self.sc.remove()
|
||||||
|
except Exception as e:
|
||||||
|
print('Warning: could not remove station scatter plot.\nReason: {}'.format(e))
|
||||||
|
try:
|
||||||
|
self.legend.remove()
|
||||||
|
except Exception as e:
|
||||||
|
print('Warning: could not remove legend. Reason: {}'.format(e))
|
||||||
|
self.canvas.draw()
|
||||||
|
|
||||||
|
def remove_contourf(self):
|
||||||
|
for item in self.contourf.collections:
|
||||||
|
item.remove()
|
||||||
|
|
||||||
|
def remove_annotations(self):
|
||||||
|
for annotation in self.annotations:
|
||||||
|
annotation.remove()
|
||||||
|
|
||||||
|
def zoom(self, event):
|
||||||
|
map = self.basemap
|
||||||
|
xlim = map.ax.get_xlim()
|
||||||
|
ylim = map.ax.get_ylim()
|
||||||
|
x, y = event.xdata, event.ydata
|
||||||
|
zoom = {'up': 1. / 2.,
|
||||||
|
'down': 2.}
|
||||||
|
|
||||||
|
if not event.xdata or not event.ydata:
|
||||||
|
return
|
||||||
|
|
||||||
|
if event.button in zoom:
|
||||||
|
factor = zoom[event.button]
|
||||||
|
xdiff = (xlim[1] - xlim[0]) * factor
|
||||||
|
xl = x - 0.5 * xdiff
|
||||||
|
xr = x + 0.5 * xdiff
|
||||||
|
ydiff = (ylim[1] - ylim[0]) * factor
|
||||||
|
yb = y - 0.5 * ydiff
|
||||||
|
yt = y + 0.5 * ydiff
|
||||||
|
|
||||||
|
if xl < map.xmin or yb < map.ymin or xr > map.xmax or yt > map.ymax:
|
||||||
|
xl, xr = map.xmin, map.xmax
|
||||||
|
yb, yt = map.ymin, map.ymax
|
||||||
|
map.ax.set_xlim(xl, xr)
|
||||||
|
map.ax.set_ylim(yb, yt)
|
||||||
|
map.ax.figure.canvas.draw()
|
||||||
|
|
||||||
|
def _warn(self, message):
|
||||||
|
self.qmb = QtGui.QMessageBox(QtGui.QMessageBox.Icon.Warning,
|
||||||
|
'Warning', message)
|
||||||
|
self.qmb.show()
|
@ -12,6 +12,186 @@ from pylot.core.util.utils import key_for_set_value, find_in_list, \
|
|||||||
remove_underscores, gen_Pool
|
remove_underscores, gen_Pool
|
||||||
|
|
||||||
|
|
||||||
|
class Metadata(object):
|
||||||
|
def __init__(self, inventory=None):
|
||||||
|
self.inventories = []
|
||||||
|
if os.path.isdir(inventory):
|
||||||
|
self.add_inventory(inventory)
|
||||||
|
if os.path.isfile(inventory):
|
||||||
|
self.add_inventory_file(inventory)
|
||||||
|
self.seed_ids = {}
|
||||||
|
self.inventory_files = {}
|
||||||
|
|
||||||
|
|
||||||
|
def add_inventory(self, path_to_inventory):
|
||||||
|
# add paths to list of inventories
|
||||||
|
assert (os.path.isdir(path_to_inventory)), '{} is no directory'.format(path_to_inventory)
|
||||||
|
if not path_to_inventory in self.inventories:
|
||||||
|
self.inventories.append(path_to_inventory)
|
||||||
|
|
||||||
|
|
||||||
|
def add_inventory_file(self, path_to_inventory_file):
|
||||||
|
assert (os.path.isfile(path_to_inventory_file)), '{} is no directory'.format(path_to_inventory_file)
|
||||||
|
self.add_inventory(os.path.split(path_to_inventory_file)[0])
|
||||||
|
if not path_to_inventory_file in self.inventory_files.keys():
|
||||||
|
self.read_single_file(path_to_inventory_file)
|
||||||
|
|
||||||
|
|
||||||
|
def remove_inventory(self, path_to_inventory):
|
||||||
|
if not path_to_inventory in self.inventories:
|
||||||
|
print('Path {} not in inventories list.'.format(path_to_inventory))
|
||||||
|
return
|
||||||
|
self.inventories.remove(path_to_inventory)
|
||||||
|
|
||||||
|
|
||||||
|
def get_metadata(self, seed_id):
|
||||||
|
# get metadata for a specific seed_id, if not already read, try to read from inventories
|
||||||
|
if not seed_id in self.seed_ids.keys():
|
||||||
|
self._read_inventory_data(seed_id)
|
||||||
|
# if seed id is not found read all inventories and try to find it there
|
||||||
|
if not seed_id in self.seed_ids.keys():
|
||||||
|
print('No data found for seed id {}. Trying to find it in all known inventories...'.format(seed_id))
|
||||||
|
self.read_all()
|
||||||
|
for inv_fname, metadata in self.inventory_files.items():
|
||||||
|
# use get_coordinates to check for seed_id
|
||||||
|
if metadata['data'].get_coordinates(seed_id):
|
||||||
|
self.seed_ids[seed_id] = inv_fname
|
||||||
|
return metadata
|
||||||
|
print('Could not find metadata for station {}'.format(seed_id))
|
||||||
|
return None
|
||||||
|
fname = self.seed_ids[seed_id]
|
||||||
|
return self.inventory_files[fname]
|
||||||
|
|
||||||
|
|
||||||
|
def read_all(self):
|
||||||
|
for inventory in self.inventories:
|
||||||
|
for inv_fname in os.listdir(inventory):
|
||||||
|
inv_fname = os.path.join(inventory, inv_fname)
|
||||||
|
if not self.read_single_file(inv_fname):
|
||||||
|
continue
|
||||||
|
|
||||||
|
|
||||||
|
def read_single_file(self, inv_fname):
|
||||||
|
if not inv_fname in self.inventory_files.keys():
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
if not self.inventory_files[inv_fname]:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
invtype, robj = self._read_metadata_file(inv_fname)
|
||||||
|
if robj == None:
|
||||||
|
return
|
||||||
|
except Exception as e:
|
||||||
|
print('Could not read file {}'.format(inv_fname))
|
||||||
|
return
|
||||||
|
self.inventory_files[inv_fname] = {'invtype': invtype,
|
||||||
|
'data': robj}
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def get_coordinates(self, seed_id):
|
||||||
|
metadata = self.get_metadata(seed_id)
|
||||||
|
return metadata['data'].get_coordinates(seed_id)
|
||||||
|
|
||||||
|
|
||||||
|
def get_paz(self, seed_id, time=None):
|
||||||
|
metadata = self.get_metadata(seed_id)
|
||||||
|
if metadata['invtype'] in ['dless', 'dseed']:
|
||||||
|
return metadata['data'].get_paz(seed_id)
|
||||||
|
elif metadata['invtype'] in ['resp', 'xml']:
|
||||||
|
if not time:
|
||||||
|
print('Time needed to extract metadata from station inventory.')
|
||||||
|
return None
|
||||||
|
resp = metadata['data'].get_response(seed_id, time)
|
||||||
|
return resp.get_paz(seed_id)
|
||||||
|
|
||||||
|
|
||||||
|
def _read_inventory_data(self, seed_id=None):
|
||||||
|
for inventory in self.inventories:
|
||||||
|
if self._read_metadata_iterator(path_to_inventory=inventory, station_seed_id=seed_id):
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
def _read_metadata_iterator(self, path_to_inventory, station_seed_id):
|
||||||
|
'''
|
||||||
|
search for metadata for a specific station iteratively
|
||||||
|
'''
|
||||||
|
station, network, location, channel = station_seed_id.split('.')
|
||||||
|
fnames = glob.glob(os.path.join(path_to_inventory, '*' + station_seed_id + '*'))
|
||||||
|
if not fnames:
|
||||||
|
# search for station name in filename
|
||||||
|
fnames = glob.glob(os.path.join(path_to_inventory, '*' + station + '*'))
|
||||||
|
if not fnames:
|
||||||
|
# search for network name in filename
|
||||||
|
fnames = glob.glob(os.path.join(path_to_inventory, '*' + network + '*'))
|
||||||
|
if not fnames:
|
||||||
|
print('Could not find filenames matching station name, network name or seed id')
|
||||||
|
return
|
||||||
|
for fname in fnames:
|
||||||
|
if fname in self.inventory_files.keys():
|
||||||
|
if self.inventory_files[fname]:
|
||||||
|
# file already read
|
||||||
|
continue
|
||||||
|
invtype, robj = self._read_metadata_file(os.path.join(path_to_inventory, fname))
|
||||||
|
try:
|
||||||
|
robj.get_coordinates(station_seed_id)
|
||||||
|
self.inventory_files[fname] = {'invtype': invtype,
|
||||||
|
'data': robj}
|
||||||
|
if station_seed_id in self.seed_ids.keys():
|
||||||
|
print('WARNING: Overwriting metadata for station {}'.format(station_seed_id))
|
||||||
|
self.seed_ids[station_seed_id] = fname
|
||||||
|
return True
|
||||||
|
except Exception as e:
|
||||||
|
continue
|
||||||
|
print('Could not find metadata for station_seed_id {} in path {}'.format(station_seed_id, path_to_inventory))
|
||||||
|
|
||||||
|
|
||||||
|
def _read_metadata_file(self, path_to_inventory_filename):
|
||||||
|
'''
|
||||||
|
function reading metadata files (either dataless seed, xml or resp)
|
||||||
|
:param path_to_inventory_filename:
|
||||||
|
:return: file type/ending, inventory object (Parser or Inventory)
|
||||||
|
'''
|
||||||
|
# functions used to read metadata for different file endings (or file types)
|
||||||
|
read_functions = {'dless': self._read_dless,
|
||||||
|
'dseed': self._read_dless,
|
||||||
|
'xml': self._read_inventory_file,
|
||||||
|
'resp': self._read_inventory_file}
|
||||||
|
file_ending = path_to_inventory_filename.split('.')[-1]
|
||||||
|
if file_ending in read_functions.keys():
|
||||||
|
robj, exc = read_functions[file_ending](path_to_inventory_filename)
|
||||||
|
if exc is not None:
|
||||||
|
raise exc
|
||||||
|
return file_ending, robj
|
||||||
|
# in case file endings did not match the above keys, try and error
|
||||||
|
for file_type in ['dless', 'xml']:
|
||||||
|
robj, exc = read_functions[file_type](path_to_inventory_filename)
|
||||||
|
if exc is None:
|
||||||
|
return file_type, robj
|
||||||
|
return None, None
|
||||||
|
|
||||||
|
|
||||||
|
def _read_dless(self, path_to_inventory):
|
||||||
|
exc = None
|
||||||
|
try:
|
||||||
|
parser = Parser(path_to_inventory)
|
||||||
|
except Exception as exc:
|
||||||
|
parser = None
|
||||||
|
return parser, exc
|
||||||
|
|
||||||
|
|
||||||
|
def _read_inventory_file(self, path_to_inventory):
|
||||||
|
exc = None
|
||||||
|
try:
|
||||||
|
inv = read_inventory(path_to_inventory)
|
||||||
|
except Exception as exc:
|
||||||
|
inv = None
|
||||||
|
return inv, exc
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def time_from_header(header):
|
def time_from_header(header):
|
||||||
"""
|
"""
|
||||||
Function takes in the second line from a .gse file and takes out the date and time from that line.
|
Function takes in the second line from a .gse file and takes out the date and time from that line.
|
||||||
@ -196,6 +376,33 @@ def read_metadata(path_to_inventory):
|
|||||||
return invtype, robj
|
return invtype, robj
|
||||||
|
|
||||||
|
|
||||||
|
# idea to optimize read_metadata
|
||||||
|
# def read_metadata_new(path_to_inventory):
|
||||||
|
# metadata_objects = []
|
||||||
|
# # read multiple files from directory
|
||||||
|
# if os.path.isdir(path_to_inventory):
|
||||||
|
# fnames = os.listdir(path_to_inventory)
|
||||||
|
# # read single file
|
||||||
|
# elif os.path.isfile(path_to_inventory):
|
||||||
|
# fnames = [path_to_inventory]
|
||||||
|
# else:
|
||||||
|
# print("Neither dataless-SEED file, inventory-xml file nor "
|
||||||
|
# "RESP-file found!")
|
||||||
|
# print("!!WRONG CALCULATION OF SOURCE PARAMETERS!!")
|
||||||
|
# fnames = []
|
||||||
|
#
|
||||||
|
# for fname in fnames:
|
||||||
|
# path_to_inventory_filename = os.path.join(path_to_inventory, fname)
|
||||||
|
# try:
|
||||||
|
# ftype, robj = read_metadata_file(path_to_inventory_filename)
|
||||||
|
# metadata_objects.append((ftype, robj))
|
||||||
|
# except Exception as e:
|
||||||
|
# print('Could not read metadata file {} '
|
||||||
|
# 'because of the following Exception: {}'.format(path_to_inventory_filename, e))
|
||||||
|
# return metadata_objects
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def restitute_trace(input_tuple):
|
def restitute_trace(input_tuple):
|
||||||
tr, invtype, inobj, unit, force = input_tuple
|
tr, invtype, inobj, unit, force = input_tuple
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@ from obspy import UTCDateTime
|
|||||||
from obspy.core.event import Event as ObsPyEvent
|
from obspy.core.event import Event as ObsPyEvent
|
||||||
from obspy.core.event import Origin, ResourceIdentifier
|
from obspy.core.event import Origin, ResourceIdentifier
|
||||||
from pylot.core.io.phases import picks_from_picksdict
|
from pylot.core.io.phases import picks_from_picksdict
|
||||||
|
from pylot.core.util.obspyDMT_interface import qml_from_obspyDMT
|
||||||
|
|
||||||
|
|
||||||
class Event(ObsPyEvent):
|
class Event(ObsPyEvent):
|
||||||
@ -33,6 +34,7 @@ class Event(ObsPyEvent):
|
|||||||
self._testEvent = False
|
self._testEvent = False
|
||||||
self._refEvent = False
|
self._refEvent = False
|
||||||
self.get_notes()
|
self.get_notes()
|
||||||
|
self.get_obspy_event_info()
|
||||||
|
|
||||||
def get_notes_path(self):
|
def get_notes_path(self):
|
||||||
"""
|
"""
|
||||||
@ -43,6 +45,18 @@ class Event(ObsPyEvent):
|
|||||||
notesfile = os.path.join(self.path, 'notes.txt')
|
notesfile = os.path.join(self.path, 'notes.txt')
|
||||||
return notesfile
|
return notesfile
|
||||||
|
|
||||||
|
def get_obspy_event_info(self):
|
||||||
|
infile_pickle = os.path.join(self.path, 'info/event.pkl')
|
||||||
|
if not os.path.isfile(infile_pickle):
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
event_dmt = qml_from_obspyDMT(infile_pickle)
|
||||||
|
except Exception as e:
|
||||||
|
print('Could not get obspy event info: {}'.format(e))
|
||||||
|
return
|
||||||
|
self.magnitudes = event_dmt.magnitudes
|
||||||
|
self.origins = event_dmt.origins
|
||||||
|
|
||||||
def get_notes(self):
|
def get_notes(self):
|
||||||
"""
|
"""
|
||||||
set self.note attribute to content of notes file
|
set self.note attribute to content of notes file
|
||||||
|
@ -1,377 +0,0 @@
|
|||||||
#!/usr/bin/env python
|
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
|
|
||||||
import matplotlib.pyplot as plt
|
|
||||||
import numpy as np
|
|
||||||
import obspy
|
|
||||||
from PySide import QtGui
|
|
||||||
from matplotlib.backends.backend_qt4agg import NavigationToolbar2QT as NavigationToolbar
|
|
||||||
from mpl_toolkits.basemap import Basemap
|
|
||||||
from pylot.core.util.widgets import PickDlg
|
|
||||||
from scipy.interpolate import griddata
|
|
||||||
|
|
||||||
plt.interactive(False)
|
|
||||||
|
|
||||||
|
|
||||||
class map_projection(QtGui.QWidget):
|
|
||||||
def __init__(self, parent, figure=None):
|
|
||||||
'''
|
|
||||||
|
|
||||||
:param: picked, can be False, auto, manual
|
|
||||||
:value: str
|
|
||||||
'''
|
|
||||||
QtGui.QWidget.__init__(self)
|
|
||||||
self._parent = parent
|
|
||||||
self.metadata = parent.metadata
|
|
||||||
self.parser = parent.metadata[1]
|
|
||||||
self.picks = None
|
|
||||||
self.picks_dict = None
|
|
||||||
self.eventLoc = None
|
|
||||||
self.figure = figure
|
|
||||||
self.init_graphics()
|
|
||||||
self.init_stations()
|
|
||||||
self.init_basemap(resolution='l')
|
|
||||||
self.init_map()
|
|
||||||
self._style = parent._style
|
|
||||||
# self.show()
|
|
||||||
|
|
||||||
def init_map(self):
|
|
||||||
self.init_lat_lon_dimensions()
|
|
||||||
self.init_lat_lon_grid()
|
|
||||||
self.init_x_y_dimensions()
|
|
||||||
self.connectSignals()
|
|
||||||
self.draw_everything()
|
|
||||||
|
|
||||||
def onpick(self, event):
|
|
||||||
ind = event.ind
|
|
||||||
button = event.mouseevent.button
|
|
||||||
if ind == [] or not button == 1:
|
|
||||||
return
|
|
||||||
data = self._parent.get_data().getWFData()
|
|
||||||
for index in ind:
|
|
||||||
station = str(self.station_names[index].split('.')[-1])
|
|
||||||
try:
|
|
||||||
pickDlg = PickDlg(self, parameter=self._parent._inputs,
|
|
||||||
data=data.select(station=station),
|
|
||||||
station=station,
|
|
||||||
picks=self._parent.get_current_event().getPick(station),
|
|
||||||
autopicks=self._parent.get_current_event().getAutopick(station))
|
|
||||||
except Exception as e:
|
|
||||||
message = 'Could not generate Plot for station {st}.\n {er}'.format(st=station, er=e)
|
|
||||||
self._warn(message)
|
|
||||||
print(message, e)
|
|
||||||
return
|
|
||||||
pyl_mw = self._parent
|
|
||||||
try:
|
|
||||||
if pickDlg.exec_():
|
|
||||||
pyl_mw.setDirty(True)
|
|
||||||
pyl_mw.update_status('picks accepted ({0})'.format(station))
|
|
||||||
replot = pyl_mw.get_current_event().setPick(station, pickDlg.getPicks())
|
|
||||||
self._refresh_drawings()
|
|
||||||
if replot:
|
|
||||||
pyl_mw.plotWaveformData()
|
|
||||||
pyl_mw.drawPicks()
|
|
||||||
pyl_mw.draw()
|
|
||||||
else:
|
|
||||||
pyl_mw.drawPicks(station)
|
|
||||||
pyl_mw.draw()
|
|
||||||
else:
|
|
||||||
pyl_mw.update_status('picks discarded ({0})'.format(station))
|
|
||||||
except Exception as e:
|
|
||||||
message = 'Could not save picks for station {st}.\n{er}'.format(st=station, er=e)
|
|
||||||
self._warn(message)
|
|
||||||
print(message, e)
|
|
||||||
|
|
||||||
def connectSignals(self):
|
|
||||||
self.comboBox_phase.currentIndexChanged.connect(self._refresh_drawings)
|
|
||||||
self.zoom_id = self.basemap.ax.figure.canvas.mpl_connect('scroll_event', self.zoom)
|
|
||||||
|
|
||||||
def init_graphics(self):
|
|
||||||
if not self.figure:
|
|
||||||
if not hasattr(self._parent, 'am_figure'):
|
|
||||||
self.figure = plt.figure()
|
|
||||||
self.toolbar = NavigationToolbar(self.figure.canvas, self)
|
|
||||||
else:
|
|
||||||
self.figure = self._parent.am_figure
|
|
||||||
self.toolbar = self._parent.am_toolbar
|
|
||||||
|
|
||||||
self.main_ax = self.figure.add_subplot(111)
|
|
||||||
self.canvas = self.figure.canvas
|
|
||||||
|
|
||||||
self.main_box = QtGui.QVBoxLayout()
|
|
||||||
self.setLayout(self.main_box)
|
|
||||||
|
|
||||||
self.top_row = QtGui.QHBoxLayout()
|
|
||||||
self.main_box.addLayout(self.top_row)
|
|
||||||
|
|
||||||
self.comboBox_phase = QtGui.QComboBox()
|
|
||||||
self.comboBox_phase.insertItem(0, 'P')
|
|
||||||
self.comboBox_phase.insertItem(1, 'S')
|
|
||||||
|
|
||||||
self.comboBox_am = QtGui.QComboBox()
|
|
||||||
self.comboBox_am.insertItem(0, 'auto')
|
|
||||||
self.comboBox_am.insertItem(1, 'manual')
|
|
||||||
|
|
||||||
self.top_row.addWidget(QtGui.QLabel('Select a phase: '))
|
|
||||||
self.top_row.addWidget(self.comboBox_phase)
|
|
||||||
self.top_row.setStretch(1, 1) # set stretch of item 1 to 1
|
|
||||||
|
|
||||||
self.main_box.addWidget(self.canvas)
|
|
||||||
self.main_box.addWidget(self.toolbar)
|
|
||||||
|
|
||||||
def init_stations(self):
|
|
||||||
def get_station_names_lat_lon(parser):
|
|
||||||
station_names = []
|
|
||||||
lat = []
|
|
||||||
lon = []
|
|
||||||
for station in parser.stations:
|
|
||||||
station_name = station[0].station_call_letters
|
|
||||||
network = station[0].network_code
|
|
||||||
if not station_name in station_names:
|
|
||||||
station_names.append(network + '.' + station_name)
|
|
||||||
lat.append(station[0].latitude)
|
|
||||||
lon.append(station[0].longitude)
|
|
||||||
return station_names, lat, lon
|
|
||||||
|
|
||||||
station_names, lat, lon = get_station_names_lat_lon(self.parser)
|
|
||||||
self.station_names = station_names
|
|
||||||
self.lat = lat
|
|
||||||
self.lon = lon
|
|
||||||
|
|
||||||
def init_picks(self):
|
|
||||||
phase = self.comboBox_phase.currentText()
|
|
||||||
|
|
||||||
def get_picks(station_names):
|
|
||||||
picks = []
|
|
||||||
for station in station_names:
|
|
||||||
try:
|
|
||||||
station = station.split('.')[-1]
|
|
||||||
picks.append(self.picks_dict[station][phase]['mpp'])
|
|
||||||
except:
|
|
||||||
picks.append(np.nan)
|
|
||||||
return picks
|
|
||||||
|
|
||||||
def get_picks_rel(picks):
|
|
||||||
picks_rel = []
|
|
||||||
picks_utc = []
|
|
||||||
for pick in picks:
|
|
||||||
if type(pick) is obspy.core.utcdatetime.UTCDateTime:
|
|
||||||
picks_utc.append(pick)
|
|
||||||
minp = min(picks_utc)
|
|
||||||
for pick in picks:
|
|
||||||
if type(pick) is obspy.core.utcdatetime.UTCDateTime:
|
|
||||||
pick -= minp
|
|
||||||
picks_rel.append(pick)
|
|
||||||
return picks_rel
|
|
||||||
|
|
||||||
self.picks = get_picks(self.station_names)
|
|
||||||
self.picks_rel = get_picks_rel(self.picks)
|
|
||||||
|
|
||||||
def init_picks_active(self):
|
|
||||||
def remove_nan_picks(picks):
|
|
||||||
picks_no_nan = []
|
|
||||||
for pick in picks:
|
|
||||||
if not np.isnan(pick):
|
|
||||||
picks_no_nan.append(pick)
|
|
||||||
return picks_no_nan
|
|
||||||
|
|
||||||
self.picks_no_nan = remove_nan_picks(self.picks_rel)
|
|
||||||
|
|
||||||
def init_stations_active(self):
|
|
||||||
def remove_nan_lat_lon(picks, lat, lon):
|
|
||||||
lat_no_nan = []
|
|
||||||
lon_no_nan = []
|
|
||||||
for index, pick in enumerate(picks):
|
|
||||||
if not np.isnan(pick):
|
|
||||||
lat_no_nan.append(lat[index])
|
|
||||||
lon_no_nan.append(lon[index])
|
|
||||||
return lat_no_nan, lon_no_nan
|
|
||||||
|
|
||||||
self.lat_no_nan, self.lon_no_nan = remove_nan_lat_lon(self.picks_rel, self.lat, self.lon)
|
|
||||||
|
|
||||||
def init_lat_lon_dimensions(self):
|
|
||||||
def get_lon_lat_dim(lon, lat):
|
|
||||||
londim = max(lon) - min(lon)
|
|
||||||
latdim = max(lat) - min(lat)
|
|
||||||
return londim, latdim
|
|
||||||
|
|
||||||
self.londim, self.latdim = get_lon_lat_dim(self.lon, self.lat)
|
|
||||||
|
|
||||||
def init_x_y_dimensions(self):
|
|
||||||
def get_x_y_dim(x, y):
|
|
||||||
xdim = max(x) - min(x)
|
|
||||||
ydim = max(y) - min(y)
|
|
||||||
return xdim, ydim
|
|
||||||
|
|
||||||
self.x, self.y = self.basemap(self.lon, self.lat)
|
|
||||||
self.xdim, self.ydim = get_x_y_dim(self.x, self.y)
|
|
||||||
|
|
||||||
def init_basemap(self, resolution='l'):
|
|
||||||
# basemap = Basemap(projection=projection, resolution = resolution, ax=self.main_ax)
|
|
||||||
basemap = Basemap(projection='lcc', resolution=resolution, ax=self.main_ax,
|
|
||||||
width=5e6, height=2e6,
|
|
||||||
lat_0=(min(self.lat) + max(self.lat)) / 2.,
|
|
||||||
lon_0=(min(self.lon) + max(self.lon)) / 2.)
|
|
||||||
|
|
||||||
# basemap.fillcontinents(color=None, lake_color='aqua',zorder=1)
|
|
||||||
basemap.drawmapboundary(zorder=2) # fill_color='darkblue')
|
|
||||||
basemap.shadedrelief(zorder=3)
|
|
||||||
basemap.drawcountries(zorder=4)
|
|
||||||
basemap.drawstates(zorder=5)
|
|
||||||
basemap.drawcoastlines(zorder=6)
|
|
||||||
self.basemap = basemap
|
|
||||||
self.figure.tight_layout()
|
|
||||||
|
|
||||||
def init_lat_lon_grid(self):
|
|
||||||
def get_lat_lon_axis(lat, lon):
|
|
||||||
steplat = (max(lat) - min(lat)) / 250
|
|
||||||
steplon = (max(lon) - min(lon)) / 250
|
|
||||||
|
|
||||||
lataxis = np.arange(min(lat), max(lat), steplat)
|
|
||||||
lonaxis = np.arange(min(lon), max(lon), steplon)
|
|
||||||
return lataxis, lonaxis
|
|
||||||
|
|
||||||
def get_lat_lon_grid(lataxis, lonaxis):
|
|
||||||
longrid, latgrid = np.meshgrid(lonaxis, lataxis)
|
|
||||||
return latgrid, longrid
|
|
||||||
|
|
||||||
self.lataxis, self.lonaxis = get_lat_lon_axis(self.lat, self.lon)
|
|
||||||
self.latgrid, self.longrid = get_lat_lon_grid(self.lataxis, self.lonaxis)
|
|
||||||
|
|
||||||
def init_picksgrid(self):
|
|
||||||
self.picksgrid_no_nan = griddata((self.lat_no_nan, self.lon_no_nan),
|
|
||||||
self.picks_no_nan, (self.latgrid, self.longrid),
|
|
||||||
method='linear') ##################
|
|
||||||
|
|
||||||
def draw_contour_filled(self, nlevel='50'):
|
|
||||||
levels = np.linspace(min(self.picks_no_nan), max(self.picks_no_nan), nlevel)
|
|
||||||
self.contourf = self.basemap.contourf(self.longrid, self.latgrid, self.picksgrid_no_nan,
|
|
||||||
levels, latlon=True, zorder=9, alpha=0.5)
|
|
||||||
|
|
||||||
def scatter_all_stations(self):
|
|
||||||
self.sc = self.basemap.scatter(self.lon, self.lat, s=50, facecolor='none', latlon=True,
|
|
||||||
zorder=10, picker=True, edgecolor='m', label='Not Picked')
|
|
||||||
self.cid = self.canvas.mpl_connect('pick_event', self.onpick)
|
|
||||||
if self.eventLoc:
|
|
||||||
lat, lon = self.eventLoc
|
|
||||||
self.sc_event = self.basemap.scatter(lon, lat, s=100, facecolor='red',
|
|
||||||
latlon=True, zorder=11, label='Event (might be outside map region)')
|
|
||||||
|
|
||||||
def scatter_picked_stations(self):
|
|
||||||
lon = self.lon_no_nan
|
|
||||||
lat = self.lat_no_nan
|
|
||||||
|
|
||||||
# workaround because of an issue with latlon transformation of arrays with len <3
|
|
||||||
if len(lon) <= 2 and len(lat) <= 2:
|
|
||||||
self.sc_picked = self.basemap.scatter(lon[0], lat[0], s=50, facecolor='white',
|
|
||||||
c=self.picks_no_nan[0], latlon=True, zorder=11, label='Picked')
|
|
||||||
if len(lon) == 2 and len(lat) == 2:
|
|
||||||
self.sc_picked = self.basemap.scatter(lon[1], lat[1], s=50, facecolor='white',
|
|
||||||
c=self.picks_no_nan[1], latlon=True, zorder=11)
|
|
||||||
else:
|
|
||||||
self.sc_picked = self.basemap.scatter(lon, lat, s=50, facecolor='white',
|
|
||||||
c=self.picks_no_nan, latlon=True, zorder=11, label='Picked')
|
|
||||||
|
|
||||||
def annotate_ax(self):
|
|
||||||
self.annotations = []
|
|
||||||
for index, name in enumerate(self.station_names):
|
|
||||||
self.annotations.append(self.main_ax.annotate(' %s' % name, xy=(self.x[index], self.y[index]),
|
|
||||||
fontsize='x-small', color='white', zorder=12))
|
|
||||||
self.legend = self.main_ax.legend(loc=1)
|
|
||||||
|
|
||||||
def add_cbar(self, label):
|
|
||||||
cbar = self.main_ax.figure.colorbar(self.sc_picked, fraction=0.025)
|
|
||||||
cbar.set_label(label)
|
|
||||||
return cbar
|
|
||||||
|
|
||||||
def refresh_drawings(self, picks=None):
|
|
||||||
self.picks_dict = picks
|
|
||||||
self._refresh_drawings()
|
|
||||||
|
|
||||||
def _refresh_drawings(self):
|
|
||||||
self.remove_drawings()
|
|
||||||
self.draw_everything()
|
|
||||||
|
|
||||||
def draw_everything(self):
|
|
||||||
if self.picks_dict:
|
|
||||||
self.init_picks()
|
|
||||||
self.init_picks_active()
|
|
||||||
self.init_stations_active()
|
|
||||||
if len(self.picks_no_nan) >= 3:
|
|
||||||
self.init_picksgrid()
|
|
||||||
self.draw_contour_filled()
|
|
||||||
self.scatter_all_stations()
|
|
||||||
if self.picks_dict:
|
|
||||||
self.scatter_picked_stations()
|
|
||||||
self.cbar = self.add_cbar(label='Time relative to first onset [s]')
|
|
||||||
self.comboBox_phase.setEnabled(True)
|
|
||||||
else:
|
|
||||||
self.comboBox_phase.setEnabled(False)
|
|
||||||
self.annotate_ax()
|
|
||||||
self.canvas.draw()
|
|
||||||
|
|
||||||
def remove_drawings(self):
|
|
||||||
if hasattr(self, 'sc_picked'):
|
|
||||||
self.sc_picked.remove()
|
|
||||||
del (self.sc_picked)
|
|
||||||
if hasattr(self, 'sc_event'):
|
|
||||||
self.sc_event.remove()
|
|
||||||
del (self.sc_event)
|
|
||||||
if hasattr(self, 'cbar'):
|
|
||||||
self.cbar.remove()
|
|
||||||
del (self.cbar)
|
|
||||||
if hasattr(self, 'contourf'):
|
|
||||||
self.remove_contourf()
|
|
||||||
del (self.contourf)
|
|
||||||
if hasattr(self, 'cid'):
|
|
||||||
self.canvas.mpl_disconnect(self.cid)
|
|
||||||
del (self.cid)
|
|
||||||
try:
|
|
||||||
self.sc.remove()
|
|
||||||
except Exception as e:
|
|
||||||
print('Warning: could not remove station scatter plot.\nReason: {}'.format(e))
|
|
||||||
try:
|
|
||||||
self.legend.remove()
|
|
||||||
except Exception as e:
|
|
||||||
print('Warning: could not remove legend. Reason: {}'.format(e))
|
|
||||||
self.canvas.draw()
|
|
||||||
|
|
||||||
def remove_contourf(self):
|
|
||||||
for item in self.contourf.collections:
|
|
||||||
item.remove()
|
|
||||||
|
|
||||||
def remove_annotations(self):
|
|
||||||
for annotation in self.annotations:
|
|
||||||
annotation.remove()
|
|
||||||
|
|
||||||
def zoom(self, event):
|
|
||||||
map = self.basemap
|
|
||||||
xlim = map.ax.get_xlim()
|
|
||||||
ylim = map.ax.get_ylim()
|
|
||||||
x, y = event.xdata, event.ydata
|
|
||||||
zoom = {'up': 1. / 2.,
|
|
||||||
'down': 2.}
|
|
||||||
|
|
||||||
if not event.xdata or not event.ydata:
|
|
||||||
return
|
|
||||||
|
|
||||||
if event.button in zoom:
|
|
||||||
factor = zoom[event.button]
|
|
||||||
xdiff = (xlim[1] - xlim[0]) * factor
|
|
||||||
xl = x - 0.5 * xdiff
|
|
||||||
xr = x + 0.5 * xdiff
|
|
||||||
ydiff = (ylim[1] - ylim[0]) * factor
|
|
||||||
yb = y - 0.5 * ydiff
|
|
||||||
yt = y + 0.5 * ydiff
|
|
||||||
|
|
||||||
if xl < map.xmin or yb < map.ymin or xr > map.xmax or yt > map.ymax:
|
|
||||||
xl, xr = map.xmin, map.xmax
|
|
||||||
yb, yt = map.ymin, map.ymax
|
|
||||||
map.ax.set_xlim(xl, xr)
|
|
||||||
map.ax.set_ylim(yb, yt)
|
|
||||||
map.ax.figure.canvas.draw()
|
|
||||||
|
|
||||||
def _warn(self, message):
|
|
||||||
self.qmb = QtGui.QMessageBox(QtGui.QMessageBox.Icon.Warning,
|
|
||||||
'Warning', message)
|
|
||||||
self.qmb.show()
|
|
@ -25,4 +25,20 @@ def check_obspydmt_eventfolder(folder):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
return False, e
|
return False, e
|
||||||
|
|
||||||
check_obspydmt_eventfolder('20110311_054623.a')
|
def qml_from_obspyDMT(path):
|
||||||
|
import pickle
|
||||||
|
from obspy.core.event import Event, Magnitude, Origin
|
||||||
|
|
||||||
|
if not os.path.exists(path):
|
||||||
|
return IOError('Could not find Event at {}'.format(path))
|
||||||
|
infile = open(path, 'rb')
|
||||||
|
event_dmt = pickle.load(infile)
|
||||||
|
ev = Event(resource_id=event_dmt['event_id'])
|
||||||
|
origin = Origin(resource_id=event_dmt['origin_id'], time=event_dmt['datetime'], longitude=event_dmt['longitude'],
|
||||||
|
latitude=event_dmt['latitude'], depth=event_dmt['depth'])
|
||||||
|
mag = Magnitude(mag=event_dmt['magnitude'], magnitude_type=event_dmt['magnitude_type'],
|
||||||
|
origin_id=event_dmt['origin_id'])
|
||||||
|
ev.magnitudes.append(mag)
|
||||||
|
ev.origins.append(origin)
|
||||||
|
return ev
|
||||||
|
|
||||||
|
@ -16,6 +16,9 @@ import time
|
|||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
|
import matplotlib
|
||||||
|
matplotlib.use('QT4Agg')
|
||||||
|
|
||||||
from matplotlib.figure import Figure
|
from matplotlib.figure import Figure
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@ -26,6 +29,7 @@ from matplotlib.backends.backend_qt4agg import NavigationToolbar2QT
|
|||||||
from matplotlib.widgets import MultiCursor
|
from matplotlib.widgets import MultiCursor
|
||||||
from matplotlib.tight_layout import get_renderer, get_subplotspec_list, get_tight_layout_figure
|
from matplotlib.tight_layout import get_renderer, get_subplotspec_list, get_tight_layout_figure
|
||||||
from scipy.signal import argrelmin, argrelmax
|
from scipy.signal import argrelmin, argrelmax
|
||||||
|
from obspy import read
|
||||||
|
|
||||||
from PySide import QtCore, QtGui
|
from PySide import QtCore, QtGui
|
||||||
from PySide.QtGui import QAction, QApplication, QCheckBox, QComboBox, \
|
from PySide.QtGui import QAction, QApplication, QCheckBox, QComboBox, \
|
||||||
@ -447,23 +451,25 @@ class WaveformWidgetPG(QtGui.QWidget):
|
|||||||
self.orig_parent = parent
|
self.orig_parent = parent
|
||||||
# attribute plotdict is a dictionary connecting position and a name
|
# attribute plotdict is a dictionary connecting position and a name
|
||||||
self.plotdict = dict()
|
self.plotdict = dict()
|
||||||
|
# init labels
|
||||||
|
self.xlabel = None
|
||||||
|
self.ylabel = None
|
||||||
|
self.title = None
|
||||||
# create plot
|
# create plot
|
||||||
self.main_layout = QtGui.QVBoxLayout()
|
self.main_layout = QtGui.QVBoxLayout()
|
||||||
self.label_layout = QtGui.QHBoxLayout()
|
self.label_layout = QtGui.QHBoxLayout()
|
||||||
self.status_label = QtGui.QLabel()
|
self.add_labels()
|
||||||
self.perm_label = QtGui.QLabel()
|
self.connect_signals()
|
||||||
self.setLayout(self.main_layout)
|
|
||||||
self.plotWidget = self.pg.PlotWidget(self.parent(), title=title)
|
self.plotWidget = self.pg.PlotWidget(self.parent(), title=title)
|
||||||
self.main_layout.addWidget(self.plotWidget)
|
self.main_layout.addWidget(self.plotWidget)
|
||||||
self.main_layout.addLayout(self.label_layout)
|
self.main_layout.addLayout(self.label_layout)
|
||||||
self.label_layout.addWidget(self.status_label)
|
self.init_labels()
|
||||||
self.label_layout.addWidget(self.perm_label)
|
self.activateObspyDMToptions(False)
|
||||||
self.plotWidget.showGrid(x=False, y=True, alpha=0.3)
|
self.plotWidget.showGrid(x=False, y=True, alpha=0.3)
|
||||||
self.plotWidget.hideAxis('bottom')
|
|
||||||
self.plotWidget.hideAxis('left')
|
|
||||||
self.wfstart, self.wfend = 0, 0
|
self.wfstart, self.wfend = 0, 0
|
||||||
self.pen_multicursor = self.pg.mkPen(self.parent()._style['multicursor']['rgba'])
|
self.pen_multicursor = self.pg.mkPen(self.parent()._style['multicursor']['rgba'])
|
||||||
self.pen_linecolor = self.pg.mkPen(self.parent()._style['linecolor']['rgba'])
|
self.pen_linecolor = self.pg.mkPen(self.parent()._style['linecolor']['rgba'])
|
||||||
|
self.pen_linecolor_highlight = self.pg.mkPen((255, 100, 100, 255))
|
||||||
self.pen_linecolor_syn = self.pg.mkPen((100, 0, 255, 255))
|
self.pen_linecolor_syn = self.pg.mkPen((100, 0, 255, 255))
|
||||||
self.reinitMoveProxy()
|
self.reinitMoveProxy()
|
||||||
self._proxy = self.pg.SignalProxy(self.plotWidget.scene().sigMouseMoved, rateLimit=60, slot=self.mouseMoved)
|
self._proxy = self.pg.SignalProxy(self.plotWidget.scene().sigMouseMoved, rateLimit=60, slot=self.mouseMoved)
|
||||||
@ -489,12 +495,59 @@ class WaveformWidgetPG(QtGui.QWidget):
|
|||||||
self.vLine.setPos(mousePoint.x())
|
self.vLine.setPos(mousePoint.x())
|
||||||
self.hLine.setPos(mousePoint.y())
|
self.hLine.setPos(mousePoint.y())
|
||||||
|
|
||||||
|
def connect_signals(self):
|
||||||
|
self.qcombo_processed.activated.connect(self.parent().newWF)
|
||||||
|
self.syn_checkbox.clicked.connect(self.parent().newWF)
|
||||||
|
|
||||||
|
def init_labels(self):
|
||||||
|
self.label_layout.addWidget(self.status_label)
|
||||||
|
for label in self.perm_labels:
|
||||||
|
self.label_layout.addWidget(label)
|
||||||
|
self.label_layout.addWidget(self.syn_checkbox)
|
||||||
|
self.label_layout.addWidget(self.qcombo_processed)
|
||||||
|
self.syn_checkbox.setLayoutDirection(Qt.RightToLeft)
|
||||||
|
self.label_layout.setStretch(0, 4)
|
||||||
|
self.label_layout.setStretch(1, 0)
|
||||||
|
self.label_layout.setStretch(2, 0)
|
||||||
|
self.label_layout.setStretch(3, 0)
|
||||||
|
self.label_layout.setStretch(4, 3)
|
||||||
|
self.label_layout.setStretch(5, 1)
|
||||||
|
|
||||||
|
def add_labels(self):
|
||||||
|
self.status_label = QtGui.QLabel()
|
||||||
|
self.perm_labels = []
|
||||||
|
for index in range(3):
|
||||||
|
label = QtGui.QLabel()
|
||||||
|
self.perm_labels.append(label)
|
||||||
|
self.qcombo_processed = QtGui.QComboBox()
|
||||||
|
self.syn_checkbox = QtGui.QCheckBox('synthetics')
|
||||||
|
self.addQCboxItem('processed', 'green')
|
||||||
|
self.addQCboxItem('raw', 'black')
|
||||||
|
#self.perm_qcbox_right.setAlignment(2)
|
||||||
|
self.setLayout(self.main_layout)
|
||||||
|
|
||||||
def getPlotDict(self):
|
def getPlotDict(self):
|
||||||
return self.plotdict
|
return self.plotdict
|
||||||
|
|
||||||
def setPermText(self, text=None, color='black'):
|
def activateObspyDMToptions(self, activate):
|
||||||
self.perm_label.setText(text)
|
self.syn_checkbox.setVisible(activate)
|
||||||
self.perm_label.setStyleSheet('color: {}'.format(color))
|
self.qcombo_processed.setVisible(activate)
|
||||||
|
|
||||||
|
def setPermText(self, number, text=None, color='black'):
|
||||||
|
if not 0 <= number < len(self.perm_labels):
|
||||||
|
raise ValueError('No label for number {}'.format(number))
|
||||||
|
self.perm_labels[number].setText(text)
|
||||||
|
self.perm_labels[number].setStyleSheet('color: {}'.format(color))
|
||||||
|
|
||||||
|
def addQCboxItem(self, text=None, color='black'):
|
||||||
|
item = QtGui.QStandardItem(text)
|
||||||
|
model = self.qcombo_processed.model()
|
||||||
|
model.appendRow(item)
|
||||||
|
item.setForeground(QtGui.QColor('{}'.format(color)))
|
||||||
|
|
||||||
|
def setQCboxItem(self, text):
|
||||||
|
index = self.qcombo_processed.findText(text)
|
||||||
|
self.qcombo_processed.setCurrentIndex(index)
|
||||||
|
|
||||||
def setPlotDict(self, key, value):
|
def setPlotDict(self, key, value):
|
||||||
self.plotdict[key] = value
|
self.plotdict[key] = value
|
||||||
@ -529,6 +582,14 @@ class WaveformWidgetPG(QtGui.QWidget):
|
|||||||
else:
|
else:
|
||||||
st_select = wfdata
|
st_select = wfdata
|
||||||
|
|
||||||
|
gaps = st_select.get_gaps()
|
||||||
|
if gaps:
|
||||||
|
merged = ['{}.{}.{}.{}'.format(*gap[:4]) for gap in gaps]
|
||||||
|
st_select.merge()
|
||||||
|
print('Merged the following stations because of gaps:')
|
||||||
|
for merged_station in merged:
|
||||||
|
print(merged_station)
|
||||||
|
|
||||||
# list containing tuples of network, station, channel (for sorting)
|
# list containing tuples of network, station, channel (for sorting)
|
||||||
nsc = []
|
nsc = []
|
||||||
for trace in st_select:
|
for trace in st_select:
|
||||||
@ -552,6 +613,8 @@ class WaveformWidgetPG(QtGui.QWidget):
|
|||||||
st_syn = wfsyn.select(network=network, station=station, channel=channel)
|
st_syn = wfsyn.select(network=network, station=station, channel=channel)
|
||||||
if st_syn:
|
if st_syn:
|
||||||
trace_syn = st_syn[0].copy()
|
trace_syn = st_syn[0].copy()
|
||||||
|
else:
|
||||||
|
trace_syn = Trace()
|
||||||
if mapping:
|
if mapping:
|
||||||
comp = channel[-1]
|
comp = channel[-1]
|
||||||
n = compclass.getPlotPosition(str(comp))
|
n = compclass.getPlotPosition(str(comp))
|
||||||
@ -569,8 +632,10 @@ class WaveformWidgetPG(QtGui.QWidget):
|
|||||||
|
|
||||||
if method == 'fast':
|
if method == 'fast':
|
||||||
trace.data, time_ax = self.minMax(trace, time_ax)
|
trace.data, time_ax = self.minMax(trace, time_ax)
|
||||||
|
if trace_syn:
|
||||||
|
trace_syn.data, time_ax_syn = self.minMax(trace_syn, time_ax_syn)
|
||||||
|
|
||||||
if time_ax not in [None, []]:
|
if len(time_ax) > 0:
|
||||||
if not scaleddata:
|
if not scaleddata:
|
||||||
trace.detrend('constant')
|
trace.detrend('constant')
|
||||||
trace.normalize(np.max(np.abs(trace.data)) * 2)
|
trace.normalize(np.max(np.abs(trace.data)) * 2)
|
||||||
@ -581,23 +646,23 @@ class WaveformWidgetPG(QtGui.QWidget):
|
|||||||
times = np.array([time for index, time in enumerate(time_ax) if not index % nth_sample])
|
times = np.array([time for index, time in enumerate(time_ax) if not index % nth_sample])
|
||||||
times_syn = np.array([time for index, time in enumerate(time_ax_syn) if not index % nth_sample] if st_syn else [])
|
times_syn = np.array([time for index, time in enumerate(time_ax_syn) if not index % nth_sample] if st_syn else [])
|
||||||
trace.data = np.array([datum + n for index, datum in enumerate(trace.data) if not index % nth_sample])
|
trace.data = np.array([datum + n for index, datum in enumerate(trace.data) if not index % nth_sample])
|
||||||
trace.data_syn = np.array([datum + n for index, datum in enumerate(trace.data_syn)
|
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.data_syn))
|
times_syn, trace_syn.data))
|
||||||
self.setPlotDict(n, (station, channel, network))
|
self.setPlotDict(n, (station, channel, network))
|
||||||
self.xlabel = 'seconds since {0}'.format(self.wfstart)
|
self.xlabel = 'seconds since {0}'.format(self.wfstart)
|
||||||
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
|
return plots, gaps
|
||||||
|
|
||||||
def minMax(self, trace, time_ax):
|
def minMax(self, trace, time_ax):
|
||||||
'''
|
'''
|
||||||
create min/max array for fast plotting (approach based on obspy __plot_min_max function)
|
create min/max array for fast plotting (approach based on obspy __plot_min_max function)
|
||||||
:returns data, time_ax
|
:returns data, time_ax
|
||||||
'''
|
'''
|
||||||
npixel = self.width()
|
npixel = self.orig_parent.width()
|
||||||
ndata = len(trace.data)
|
ndata = len(trace.data)
|
||||||
pts_per_pixel = ndata/npixel
|
pts_per_pixel = ndata/npixel
|
||||||
if pts_per_pixel < 2:
|
if pts_per_pixel < 2:
|
||||||
@ -1013,6 +1078,14 @@ class PylotCanvas(FigureCanvas):
|
|||||||
if mapping:
|
if mapping:
|
||||||
plot_positions = self.calcPlotPositions(st_select, compclass)
|
plot_positions = self.calcPlotPositions(st_select, compclass)
|
||||||
|
|
||||||
|
gaps = st_select.get_gaps()
|
||||||
|
if gaps:
|
||||||
|
merged = ['{}.{}.{}.{}'.format(*gap[:4]) for gap in gaps]
|
||||||
|
st_select.merge()
|
||||||
|
print('Merged the following stations because of gaps:')
|
||||||
|
for merged_station in merged:
|
||||||
|
print(merged_station)
|
||||||
|
|
||||||
# list containing tuples of network, station, channel and plot position (for sorting)
|
# list containing tuples of network, station, channel and plot position (for sorting)
|
||||||
nsc = []
|
nsc = []
|
||||||
for plot_pos, trace in enumerate(st_select):
|
for plot_pos, trace in enumerate(st_select):
|
||||||
@ -1245,9 +1318,10 @@ class PickDlg(QDialog):
|
|||||||
|
|
||||||
def __init__(self, parent=None, data=None, station=None, network=None, picks=None,
|
def __init__(self, parent=None, data=None, station=None, network=None, picks=None,
|
||||||
autopicks=None, rotate=False, parameter=None, embedded=False, metadata=None,
|
autopicks=None, rotate=False, parameter=None, embedded=False, metadata=None,
|
||||||
event=None, filteroptions=None, model='iasp91'):
|
event=None, filteroptions=None, model='iasp91', wftype=None):
|
||||||
super(PickDlg, self).__init__(parent, 1)
|
super(PickDlg, self).__init__(parent, 1)
|
||||||
self.orig_parent = parent
|
self.orig_parent = parent
|
||||||
|
self.setAttribute(Qt.WA_DeleteOnClose)
|
||||||
|
|
||||||
# initialize attributes
|
# initialize attributes
|
||||||
self.parameter = parameter
|
self.parameter = parameter
|
||||||
@ -1256,6 +1330,7 @@ class PickDlg(QDialog):
|
|||||||
self.network = network
|
self.network = network
|
||||||
self.rotate = rotate
|
self.rotate = rotate
|
||||||
self.metadata = metadata
|
self.metadata = metadata
|
||||||
|
self.wftype = wftype
|
||||||
self.pylot_event = event
|
self.pylot_event = event
|
||||||
self.components = 'ZNE'
|
self.components = 'ZNE'
|
||||||
self.currentPhase = None
|
self.currentPhase = None
|
||||||
@ -1305,7 +1380,7 @@ class PickDlg(QDialog):
|
|||||||
self.cur_ylim = None
|
self.cur_ylim = None
|
||||||
|
|
||||||
# set attribute holding data
|
# set attribute holding data
|
||||||
if data is None:
|
if data is None or not data:
|
||||||
try:
|
try:
|
||||||
data = parent.get_data().getWFData().copy()
|
data = parent.get_data().getWFData().copy()
|
||||||
self.data = data.select(station=station)
|
self.data = data.select(station=station)
|
||||||
@ -1328,7 +1403,7 @@ class PickDlg(QDialog):
|
|||||||
|
|
||||||
# fill compare and scale channels
|
# fill compare and scale channels
|
||||||
self.compareChannel.addItem('-', None)
|
self.compareChannel.addItem('-', None)
|
||||||
self.scaleChannel.addItem('normalized', None)
|
self.scaleChannel.addItem('individual', None)
|
||||||
|
|
||||||
for trace in self.getWFData():
|
for trace in self.getWFData():
|
||||||
channel = trace.stats.channel
|
channel = trace.stats.channel
|
||||||
@ -1345,8 +1420,12 @@ class PickDlg(QDialog):
|
|||||||
actionS.setChecked(self.getChannelSettingsS(channel))
|
actionS.setChecked(self.getChannelSettingsS(channel))
|
||||||
|
|
||||||
# plot data
|
# plot data
|
||||||
|
title = self.getStation()
|
||||||
|
if self.wftype is not None:
|
||||||
|
title += ' | ({})'.format(self.wftype)
|
||||||
|
|
||||||
self.multicompfig.plotWFData(wfdata=self.getWFData(),
|
self.multicompfig.plotWFData(wfdata=self.getWFData(),
|
||||||
title=self.getStation())
|
title=title)
|
||||||
|
|
||||||
self.multicompfig.setZoomBorders2content()
|
self.multicompfig.setZoomBorders2content()
|
||||||
|
|
||||||
@ -1380,7 +1459,6 @@ class PickDlg(QDialog):
|
|||||||
self.setWindowTitle('Pickwindow on station: {}'.format(self.getStation()))
|
self.setWindowTitle('Pickwindow on station: {}'.format(self.getStation()))
|
||||||
self.setWindowState(QtCore.Qt.WindowMaximized)
|
self.setWindowState(QtCore.Qt.WindowMaximized)
|
||||||
|
|
||||||
self.deleteLater()
|
|
||||||
|
|
||||||
def setupUi(self):
|
def setupUi(self):
|
||||||
menuBar = QtGui.QMenuBar(self)
|
menuBar = QtGui.QMenuBar(self)
|
||||||
@ -1526,7 +1604,7 @@ class PickDlg(QDialog):
|
|||||||
_dialtoolbar.addWidget(QtGui.QLabel('Compare to channel: '))
|
_dialtoolbar.addWidget(QtGui.QLabel('Compare to channel: '))
|
||||||
_dialtoolbar.addWidget(self.compareChannel)
|
_dialtoolbar.addWidget(self.compareChannel)
|
||||||
_dialtoolbar.addSeparator()
|
_dialtoolbar.addSeparator()
|
||||||
_dialtoolbar.addWidget(QtGui.QLabel('Scale by: '))
|
_dialtoolbar.addWidget(QtGui.QLabel('Scaling: '))
|
||||||
_dialtoolbar.addWidget(self.scaleChannel)
|
_dialtoolbar.addWidget(self.scaleChannel)
|
||||||
|
|
||||||
# layout the innermost widget
|
# layout the innermost widget
|
||||||
@ -1649,7 +1727,7 @@ class PickDlg(QDialog):
|
|||||||
self.arrivalsText.append(ax.text(time_rel, ylims[0], arrival.name, color='0.5'))
|
self.arrivalsText.append(ax.text(time_rel, ylims[0], arrival.name, color='0.5'))
|
||||||
|
|
||||||
def drawArrivalsText(self):
|
def drawArrivalsText(self):
|
||||||
return self.drawArrivals(True)
|
return self.drawArrivals(textOnly=True)
|
||||||
|
|
||||||
def refreshArrivalsText(self, event=None):
|
def refreshArrivalsText(self, event=None):
|
||||||
self.removeArrivalsText()
|
self.removeArrivalsText()
|
||||||
@ -2514,6 +2592,9 @@ class PickDlg(QDialog):
|
|||||||
filtops_str = transformFilteroptions2String(filtoptions)
|
filtops_str = transformFilteroptions2String(filtoptions)
|
||||||
title += ' | Filteroptions: {}'.format(filtops_str)
|
title += ' | Filteroptions: {}'.format(filtops_str)
|
||||||
|
|
||||||
|
if self.wftype is not None:
|
||||||
|
title += ' | ({})'.format(self.wftype)
|
||||||
|
|
||||||
plot_additional = bool(self.compareChannel.currentText())
|
plot_additional = bool(self.compareChannel.currentText())
|
||||||
additional_channel = self.compareChannel.currentText()
|
additional_channel = self.compareChannel.currentText()
|
||||||
scale_channel = self.scaleChannel.currentText()
|
scale_channel = self.scaleChannel.currentText()
|
||||||
@ -2601,6 +2682,7 @@ class PickDlg(QDialog):
|
|||||||
phase = 'S'
|
phase = 'S'
|
||||||
filter = True
|
filter = True
|
||||||
self.plotWFData(phase=phase, filter=filter)
|
self.plotWFData(phase=phase, filter=filter)
|
||||||
|
self.drawArrivals()
|
||||||
|
|
||||||
def resetZoom(self):
|
def resetZoom(self):
|
||||||
ax = self.multicompfig.axes[0]
|
ax = self.multicompfig.axes[0]
|
||||||
@ -2864,7 +2946,7 @@ class AutoPickWidget(MultiEventWidget):
|
|||||||
def reinitEvents2plot(self):
|
def reinitEvents2plot(self):
|
||||||
for eventID, eventDict in self.events2plot.items():
|
for eventID, eventDict in self.events2plot.items():
|
||||||
for widget_key, widget in eventDict.items():
|
for widget_key, widget in eventDict.items():
|
||||||
widget.setParent(None)
|
del(widget)
|
||||||
self.events2plot = {}
|
self.events2plot = {}
|
||||||
self.eventbox.clear()
|
self.eventbox.clear()
|
||||||
self.refresh_plot_tabs()
|
self.refresh_plot_tabs()
|
||||||
@ -2946,13 +3028,15 @@ class TuneAutopicker(QWidget):
|
|||||||
:type: PyLoT Mainwindow
|
:type: PyLoT Mainwindow
|
||||||
'''
|
'''
|
||||||
|
|
||||||
def __init__(self, parent):
|
def __init__(self, parent, obspy_dmt=False):
|
||||||
QtGui.QWidget.__init__(self, parent, 1)
|
QtGui.QWidget.__init__(self, parent, 1)
|
||||||
self._style = parent._style
|
self._style = parent._style
|
||||||
self.setWindowTitle('PyLoT - Tune Autopicker')
|
self.setWindowTitle('PyLoT - Tune Autopicker')
|
||||||
self.parameter = self.parent()._inputs
|
self.parameter = self.parent()._inputs
|
||||||
self.fig_dict = self.parent().fig_dict
|
self.fig_dict = self.parent().fig_dict
|
||||||
self.data = Data()
|
self.data = Data()
|
||||||
|
self.obspy_dmt = obspy_dmt
|
||||||
|
self.wftype = None
|
||||||
self.pdlg_widget = None
|
self.pdlg_widget = None
|
||||||
self.pylot_picks = None
|
self.pylot_picks = None
|
||||||
self.init_main_layouts()
|
self.init_main_layouts()
|
||||||
@ -3009,8 +3093,45 @@ class TuneAutopicker(QWidget):
|
|||||||
self.eventBox.activated.connect(self.fill_tabs)
|
self.eventBox.activated.connect(self.fill_tabs)
|
||||||
self.stationBox.activated.connect(self.fill_tabs)
|
self.stationBox.activated.connect(self.fill_tabs)
|
||||||
|
|
||||||
|
def catch_station_ids(self):
|
||||||
|
self.station_ids = {}
|
||||||
|
eventpath = self.get_current_event_fp()
|
||||||
|
self.wftype = 'processed' if self.obspy_dmt else ''
|
||||||
|
wf_path = os.path.join(eventpath, self.wftype)
|
||||||
|
if not os.path.exists(wf_path) and self.obspy_dmt:
|
||||||
|
self.wftype = 'raw'
|
||||||
|
wf_path = os.path.join(eventpath, self.wftype)
|
||||||
|
for filename in os.listdir(wf_path):
|
||||||
|
filename = os.path.join(eventpath, self.wftype, filename)
|
||||||
|
try:
|
||||||
|
st = read(filename, headonly=True)
|
||||||
|
except Exception as e:
|
||||||
|
print('Warning: Could not read file {} as a stream object: {}'.format(filename, e))
|
||||||
|
continue
|
||||||
|
for trace in st:
|
||||||
|
network = trace.stats.network
|
||||||
|
station = trace.stats.station
|
||||||
|
location = trace.stats.location
|
||||||
|
station_id = '{}.{}.{}'.format(network, station, location)
|
||||||
|
if not station_id in self.station_ids:
|
||||||
|
self.station_ids[station_id] = []
|
||||||
|
self.station_ids[station_id].append(filename)
|
||||||
|
|
||||||
def fill_stationbox(self):
|
def fill_stationbox(self):
|
||||||
fnames = self.parent().getWFFnames_from_eventbox(eventbox=self.eventBox)
|
self.stationBox.clear()
|
||||||
|
model = self.stationBox.model()
|
||||||
|
|
||||||
|
self.catch_station_ids()
|
||||||
|
st_ids_list = list(self.station_ids.keys())
|
||||||
|
st_ids_list.sort()
|
||||||
|
for station_id in st_ids_list:
|
||||||
|
item = QtGui.QStandardItem(station_id)
|
||||||
|
if station_id.split('.')[1] in self.get_current_event().pylot_picks:
|
||||||
|
item.setBackground(self.parent()._ref_test_colors['ref'])
|
||||||
|
model.appendRow(item)
|
||||||
|
|
||||||
|
def load_wf_data(self):
|
||||||
|
fnames = self.station_ids[self.get_current_station_id()]
|
||||||
self.data.setWFData(fnames)
|
self.data.setWFData(fnames)
|
||||||
wfdat = self.data.getWFData() # all available streams
|
wfdat = self.data.getWFData() # all available streams
|
||||||
# remove possible underscores in station names
|
# remove possible underscores in station names
|
||||||
@ -3022,21 +3143,6 @@ class TuneAutopicker(QWidget):
|
|||||||
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
|
||||||
trim_station_components(wfdat, trim_start=True, trim_end=False)
|
trim_station_components(wfdat, trim_start=True, trim_end=False)
|
||||||
self.stationBox.clear()
|
|
||||||
stations = []
|
|
||||||
for trace in self.data.getWFData():
|
|
||||||
station = trace.stats.station
|
|
||||||
network = trace.stats.network
|
|
||||||
ns_tup = (str(network), str(station))
|
|
||||||
if not ns_tup in stations:
|
|
||||||
stations.append(ns_tup)
|
|
||||||
stations.sort()
|
|
||||||
model = self.stationBox.model()
|
|
||||||
for network, station in stations:
|
|
||||||
item = QtGui.QStandardItem(network + '.' + station)
|
|
||||||
if station in self.get_current_event().pylot_picks:
|
|
||||||
item.setBackground(self.parent()._ref_test_colors['ref'])
|
|
||||||
model.appendRow(item)
|
|
||||||
|
|
||||||
def init_figure_tabs(self):
|
def init_figure_tabs(self):
|
||||||
self.figure_tabs = QtGui.QTabWidget()
|
self.figure_tabs = QtGui.QTabWidget()
|
||||||
@ -3093,10 +3199,14 @@ class TuneAutopicker(QWidget):
|
|||||||
def get_current_event_autopicks(self, station):
|
def get_current_event_autopicks(self, station):
|
||||||
event = self.get_current_event()
|
event = self.get_current_event()
|
||||||
if event.pylot_autopicks:
|
if event.pylot_autopicks:
|
||||||
|
if station in event.pylot_autopicks:
|
||||||
return event.pylot_autopicks[station]
|
return event.pylot_autopicks[station]
|
||||||
|
|
||||||
def get_current_station(self):
|
def get_current_station(self):
|
||||||
return str(self.stationBox.currentText()).split('.')[-1]
|
return str(self.stationBox.currentText()).split('.')[1]
|
||||||
|
|
||||||
|
def get_current_station_id(self):
|
||||||
|
return str(self.stationBox.currentText())
|
||||||
|
|
||||||
def gen_tab_widget(self, name, canvas):
|
def gen_tab_widget(self, name, canvas):
|
||||||
widget = QtGui.QWidget()
|
widget = QtGui.QWidget()
|
||||||
@ -3111,17 +3221,19 @@ class TuneAutopicker(QWidget):
|
|||||||
self.pdlg_widget.setParent(None)
|
self.pdlg_widget.setParent(None)
|
||||||
self.pdlg_widget = None
|
self.pdlg_widget = None
|
||||||
return
|
return
|
||||||
|
self.load_wf_data()
|
||||||
station = self.get_current_station()
|
station = self.get_current_station()
|
||||||
data = self.data.getWFData()
|
wfdata = self.data.getWFData()
|
||||||
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
|
||||||
self.pickDlg = PickDlg(self.parent(), data=data.select(station=station),
|
wftype = self.wftype if self.obspy_dmt else ''
|
||||||
|
self.pickDlg = PickDlg(self.parent(), data=wfdata.select(station=station).copy(),
|
||||||
station=station, parameter=self.parameter,
|
station=station, parameter=self.parameter,
|
||||||
picks=self.get_current_event_picks(station),
|
picks=self.get_current_event_picks(station),
|
||||||
autopicks=self.get_current_event_autopicks(station),
|
autopicks=self.get_current_event_autopicks(station),
|
||||||
metadata=metadata, event=event, filteroptions=filteroptions,
|
metadata=metadata, event=event, filteroptions=filteroptions,
|
||||||
embedded=True)
|
embedded=True, wftype=wftype)
|
||||||
self.pickDlg.update_picks.connect(self.picks_from_pickdlg)
|
self.pickDlg.update_picks.connect(self.picks_from_pickdlg)
|
||||||
self.pickDlg.update_picks.connect(self.fill_eventbox)
|
self.pickDlg.update_picks.connect(self.fill_eventbox)
|
||||||
self.pickDlg.update_picks.connect(self.fill_stationbox)
|
self.pickDlg.update_picks.connect(self.fill_stationbox)
|
||||||
@ -3301,14 +3413,17 @@ class TuneAutopicker(QWidget):
|
|||||||
if not station:
|
if not station:
|
||||||
self._warn('No station selected')
|
self._warn('No station selected')
|
||||||
return
|
return
|
||||||
|
wfpath = self.wftype if self.obspy_dmt else ''
|
||||||
args = {'parameter': self.parameter,
|
args = {'parameter': self.parameter,
|
||||||
'station': station,
|
'station': station,
|
||||||
'fnames': 'None',
|
'fnames': 'None',
|
||||||
'eventid': [self.get_current_event_fp()],
|
'eventid': [self.get_current_event_fp()],
|
||||||
'iplot': 2,
|
'iplot': 2,
|
||||||
'fig_dict': self.fig_dict,
|
'fig_dict': self.fig_dict,
|
||||||
'locflag': 0,
|
'savexml': False,
|
||||||
'savexml': False}
|
'obspyDMT_wfpath': wfpath}
|
||||||
|
event = self.get_current_event()
|
||||||
|
self.parent().saveData(event, event.path, '.xml')
|
||||||
for key in self.fig_dict.keys():
|
for key in self.fig_dict.keys():
|
||||||
if not key == 'plot_style':
|
if not key == 'plot_style':
|
||||||
self.fig_dict[key].clear()
|
self.fig_dict[key].clear()
|
||||||
@ -3364,8 +3479,7 @@ class TuneAutopicker(QWidget):
|
|||||||
if hasattr(self, 'pdlg_widget'):
|
if hasattr(self, 'pdlg_widget'):
|
||||||
if self.pdlg_widget:
|
if self.pdlg_widget:
|
||||||
self.pdlg_widget.setParent(None)
|
self.pdlg_widget.setParent(None)
|
||||||
# TODO: removing widget by parent deletion raises exception when activating stationbox:
|
del(self.pdlg_widget)
|
||||||
# RuntimeError: Internal C++ object (PylotCanvas) already deleted.
|
|
||||||
if hasattr(self, 'overview'):
|
if hasattr(self, 'overview'):
|
||||||
self.overview.setParent(None)
|
self.overview.setParent(None)
|
||||||
if hasattr(self, 'p_tabs'):
|
if hasattr(self, 'p_tabs'):
|
||||||
|
@ -179,9 +179,9 @@ background-color:transparent;
|
|||||||
|
|
||||||
QTabWidget::pane{
|
QTabWidget::pane{
|
||||||
background-color:rgba(0, 0, 0, 255);
|
background-color:rgba(0, 0, 0, 255);
|
||||||
border-style:solid;
|
margin: 0px, 0px, 0px, 0px;
|
||||||
border-color:rgba(245, 245, 245, 255);
|
padding: 0px;
|
||||||
border-width:1px;
|
border-width:0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
QTabWidget::tab{
|
QTabWidget::tab{
|
||||||
|
@ -178,9 +178,9 @@ background-color:transparent;
|
|||||||
|
|
||||||
QTabWidget::pane{
|
QTabWidget::pane{
|
||||||
background-color:rgba(70, 70, 80, 255);
|
background-color:rgba(70, 70, 80, 255);
|
||||||
border-style:solid;
|
margin: 0px, 0px, 0px, 0px;
|
||||||
border-color:rgba(70, 70, 80, 255);
|
padding: 0px;
|
||||||
border-width:1px;
|
border-width:0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
QTabWidget::tab{
|
QTabWidget::tab{
|
||||||
|
Loading…
Reference in New Issue
Block a user