Merge remote-tracking branch 'origin/develop' into feature/refactor

This commit is contained in:
Darius Arnold 2018-08-03 11:55:13 +02:00
commit a45e817213
35 changed files with 1708 additions and 1056 deletions

398
PyLoT.py
View File

@ -32,6 +32,8 @@ import matplotlib
matplotlib.use('Qt4Agg') matplotlib.use('Qt4Agg')
matplotlib.rcParams['backend.qt4'] = 'PySide' matplotlib.rcParams['backend.qt4'] = 'PySide'
matplotlib.rcParams['savefig.dpi'] = 300
from PySide import QtGui, QtCore from PySide import QtGui, QtCore
from PySide.QtCore import QCoreApplication, QSettings, Signal, QFile, \ from PySide.QtCore import QCoreApplication, QSettings, Signal, QFile, \
@ -48,12 +50,7 @@ from obspy.core.util import AttribDict
from pylot.core.util.obspyDMT_interface import check_obspydmt_structure from pylot.core.util.obspyDMT_interface import check_obspydmt_structure
import pyqtgraph as pg
try:
import pyqtgraph as pg
except Exception as e:
print('PyLoT: Could not import pyqtgraph. {}'.format(e))
pg = None
try: try:
from matplotlib.backends.backend_qt4agg import FigureCanvas from matplotlib.backends.backend_qt4agg import FigureCanvas
@ -67,25 +64,25 @@ from pylot.core.io.data import Data
from pylot.core.io.inputs import FilterOptions, PylotParameter from pylot.core.io.inputs import FilterOptions, PylotParameter
from autoPyLoT import autoPyLoT from autoPyLoT import autoPyLoT
from pylot.core.pick.compare import Comparison from pylot.core.pick.compare import Comparison
from pylot.core.pick.utils import symmetrize_error, get_quality_class from pylot.core.pick.utils import symmetrize_error, getQualityFromUncertainty, getPickQuality, get_quality_class
from pylot.core.io.phases import picksdict_from_picks from pylot.core.io.phases import picksdict_from_picks
import pylot.core.loc.nll as nll import pylot.core.loc.nll as nll
from pylot.core.util.defaults import FILTERDEFAULTS, SetChannelComponents from pylot.core.util.defaults import FILTERDEFAULTS
from pylot.core.util.errors import DatastructureError, \ from pylot.core.util.errors import DatastructureError, \
OverwriteError OverwriteError
from pylot.core.util.connection import checkurl from pylot.core.util.connection import checkurl
from pylot.core.util.dataprocessing import read_metadata, restitute_data from pylot.core.util.dataprocessing import Metadata, restitute_data
from pylot.core.util.utils import fnConstructor, getLogin, \ from pylot.core.util.utils import fnConstructor, getLogin, \
full_range, readFilterInformation, trim_station_components, check4gaps, make_pen, pick_color_plt, \ full_range, readFilterInformation, trim_station_components, check4gaps, make_pen, pick_color_plt, \
pick_linestyle_plt, remove_underscores, check4doubled, identifyPhaseID, excludeQualityClasses, has_spe, \ pick_linestyle_plt, remove_underscores, check4doubled, identifyPhaseID, excludeQualityClasses, \
check4rotated, transform_colors_mpl, transform_colors_mpl_str, getAutoFilteroptions, check_all_obspy, \ check4rotated, transform_colors_mpl, transform_colors_mpl_str, getAutoFilteroptions, check_all_obspy, \
check_all_pylot, real_Bool check_all_pylot, real_Bool, SetChannelComponents
from pylot.core.util.event import Event from pylot.core.util.event import Event
from pylot.core.io.location import create_creation_info, create_event from pylot.core.io.location import create_creation_info, create_event
from pylot.core.util.widgets import FilterOptionsDialog, NewEventDlg, \ 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, ProgressBarWidget, AddMetadataWidget
from pylot.core.util.array_map import Array_map from pylot.core.util.array_map import Array_map
from pylot.core.util.structure import DATASTRUCTURE from pylot.core.util.structure import DATASTRUCTURE
from pylot.core.util.thread import Thread, Worker from pylot.core.util.thread import Thread, Worker
@ -121,7 +118,7 @@ class MainWindow(QMainWindow):
self.apw = None self.apw = None
self.paraBox = None self.paraBox = None
self.array_map = None self.array_map = None
self._metadata = None self._metadata = Metadata()
self._eventChanged = [False, False] self._eventChanged = [False, False]
self.apd_local = None self.apd_local = None
self.apd_sge = None self.apd_sge = None
@ -135,6 +132,9 @@ class MainWindow(QMainWindow):
self._ctrl = False # control key pressed self._ctrl = False # control key pressed
self._shift = False # shift key pressed self._shift = False # shift key pressed
self.drawnPicks = {'auto': {},
'manual': {}}
# default factor for dataplot e.g. enabling/disabling scrollarea # default factor for dataplot e.g. enabling/disabling scrollarea
self.height_factor = 12 self.height_factor = 12
self.plot_method = 'normal' self.plot_method = 'normal'
@ -155,6 +155,8 @@ class MainWindow(QMainWindow):
self.data._new = False self.data._new = False
self.autodata = Data(self) self.autodata = Data(self)
while True:
try:
if settings.value("user/FullName", None) is None: if settings.value("user/FullName", None) is None:
fulluser = QInputDialog.getText(self, "Enter Name:", "Full name") fulluser = QInputDialog.getText(self, "Enter Name:", "Full name")
settings.setValue("user/FullName", fulluser) settings.setValue("user/FullName", fulluser)
@ -164,6 +166,21 @@ class MainWindow(QMainWindow):
"Enter authority/institution name:", "Enter authority/institution name:",
"Authority") "Authority")
settings.setValue("agency_id", agency) settings.setValue("agency_id", agency)
break
except Exception as e:
qmb = QMessageBox(self, icon=QMessageBox.Question,
text='Could not init application settings: {}.'
'Do you want to reset application settings?'.format(e),
windowTitle='PyLoT - Init QSettings warning')
qmb.setStandardButtons(QMessageBox.Yes | QMessageBox.No)
qmb.setDefaultButton(QMessageBox.No)
ret = qmb.exec_()
if ret == qmb.Yes:
settings.clear()
else:
sys.exit()
print('Settings cleared!')
self.fnames = None self.fnames = None
self._stime = None self._stime = None
structure_setting = settings.value("data/Structure", "PILOT") structure_setting = settings.value("data/Structure", "PILOT")
@ -195,7 +212,6 @@ class MainWindow(QMainWindow):
self.loc = False self.loc = False
def init_config_files(self, infile): def init_config_files(self, infile):
pylot_config_dir = os.path.join(os.path.expanduser('~'), '.pylot') pylot_config_dir = os.path.join(os.path.expanduser('~'), '.pylot')
if not os.path.exists(pylot_config_dir): if not os.path.exists(pylot_config_dir):
@ -210,7 +226,6 @@ class MainWindow(QMainWindow):
self._inputs.export2File(infile) self._inputs.export2File(infile)
self.infile = infile self.infile = infile
def setupUi(self): def setupUi(self):
try: try:
self.startTime = min( self.startTime = min(
@ -339,7 +354,7 @@ class MainWindow(QMainWindow):
"Ctrl+A", "Ctrl+A",
openEventsIcon, openEventsIcon,
"Load event data automatically " "Load event data automatically "
"for for all events.") "for all events.")
self.openEventsAutoAction.setEnabled(False) self.openEventsAutoAction.setEnabled(False)
self.openEventsAutoAction.setData(None) self.openEventsAutoAction.setData(None)
@ -398,10 +413,10 @@ class MainWindow(QMainWindow):
self.adjustFilterOptions, self.adjustFilterOptions,
"Ctrl+F", self.filter_icon, "Ctrl+F", self.filter_icon,
"""Adjust filter parameters.""") """Adjust filter parameters.""")
self.inventoryAction = self.createAction(self, "Select &Inventory ...", self.inventoryAction = self.createAction(self, "Manage &Inventories ...",
self.get_new_metadata, self.add_metadata,
"Ctrl+I", self.inventoryIcon, "Ctrl+I", self.inventoryIcon,
"""Select metadata for current project""", """Manage metadata for current project""",
False) False)
self.initMapAction = self.createAction(self, "Init array map ...", self.initMapAction = self.createAction(self, "Init array map ...",
self.init_array_map, self.init_array_map,
@ -428,6 +443,8 @@ class MainWindow(QMainWindow):
slot=self.pickQualities, shortcut='Alt+Q', slot=self.pickQualities, shortcut='Alt+Q',
icon=qualities_icon, tip='Histogram of pick qualities') icon=qualities_icon, tip='Histogram of pick qualities')
self.qualities_action.setEnabled(False) self.qualities_action.setEnabled(False)
# MP MP not yet implemented, therefore hide:
self.qualities_action.setVisible(False)
printAction = self.createAction(self, "&Print event ...", printAction = self.createAction(self, "&Print event ...",
self.show_event_information, QKeySequence.Print, self.show_event_information, QKeySequence.Print,
@ -471,7 +488,6 @@ class MainWindow(QMainWindow):
checkable=True) checkable=True)
self.e_action.setEnabled(False) self.e_action.setEnabled(False)
componentActions = (self.z_action, self.n_action, self.e_action) componentActions = (self.z_action, self.n_action, self.e_action)
self.auto_tune = self.createAction(parent=self, text='autoTune', self.auto_tune = self.createAction(parent=self, text='autoTune',
@ -504,15 +520,15 @@ class MainWindow(QMainWindow):
# pickToolActions = (selectStation, ) # pickToolActions = (selectStation, )
# pickToolBar.setObjectName("PickTools") # pickToolBar.setObjectName("PickTools")
# self.addActions(pickToolBar, pickToolActions) # self.addActions(pickToolBar, pickToolActions)
self.locateEvent = self.createAction(parent=self, text='locate the event', self.locateEventAction = self.createAction(parent=self, text='locate the event',
slot=self.locate_event, slot=self.locate_event,
shortcut='Alt+Ctrl+L', shortcut='Alt+Ctrl+L',
icon=locate_icon, icon=locate_icon,
tip='Locate the event using ' tip='Locate the event using '
'the displayed manual arrivals.') 'the displayed manual arrivals.')
self.locateEvent.setEnabled(False) self.locateEventAction.setEnabled(False)
locationToolActions = (self.locateEvent,) locationToolActions = (self.locateEventAction,)
# add top menu # add top menu
self.fileMenu = self.menuBar().addMenu('&File') self.fileMenu = self.menuBar().addMenu('&File')
@ -527,10 +543,10 @@ class MainWindow(QMainWindow):
self.editMenu = self.menuBar().addMenu('&Edit') self.editMenu = self.menuBar().addMenu('&Edit')
editActions = (self.filterActionP, self.filterActionS, filterEditAction, None, editActions = (self.filterActionP, self.filterActionS, filterEditAction, None,
#self.selectPAction, self.selectSAction, None, # self.selectPAction, self.selectSAction, None,
self.inventoryAction, self.initMapAction, None, self.inventoryAction, self.initMapAction, None,
prefsEventAction) prefsEventAction)
#printAction) #TODO: print event? # printAction) #TODO: print event?
pickMenuActions = (self.parameterAction,) pickMenuActions = (self.parameterAction,)
self.pickMenu = self.menuBar().addMenu('&Picking') self.pickMenu = self.menuBar().addMenu('&Picking')
@ -546,13 +562,11 @@ class MainWindow(QMainWindow):
self.openProjectAction, self.saveProjectAction, self.openProjectAction, self.saveProjectAction,
self.saveProjectAsAction) self.saveProjectAsAction)
eventToolActions = (self.addEventDataAction, eventToolActions = (self.addEventDataAction,
self.openEventAction, self.openEventsAutoAction, self.openEventAction, self.openEventsAutoAction,
self.saveEventAction, self.loadlocationaction, self.saveEventAction, self.loadlocationaction,
self.loadpilotevent) self.loadpilotevent)
toolbars_keys = [ toolbars_keys = [
"FileTools", "FileTools",
"EventTools", "EventTools",
@ -616,7 +630,7 @@ class MainWindow(QMainWindow):
self.tabs.currentChanged.connect(self.refreshTabs) self.tabs.currentChanged.connect(self.refreshTabs)
# add progressbar # add progressbar
self.mainProgressBarWidget = QtGui.QWidget() self.mainProgressBarWidget = ProgressBarWidget(self)
self._main_layout.addWidget(self.mainProgressBarWidget) self._main_layout.addWidget(self.mainProgressBarWidget)
# add scroll area used in case number of traces gets too high # add scroll area used in case number of traces gets too high
@ -672,15 +686,9 @@ class MainWindow(QMainWindow):
self.setCentralWidget(_widget) self.setCentralWidget(_widget)
def init_wfWidget(self): def init_wfWidget(self):
settings = QSettings()
xlab = self.startTime.strftime('seconds since %Y/%m/%d %H:%M:%S (%Z)') xlab = self.startTime.strftime('seconds since %Y/%m/%d %H:%M:%S (%Z)')
plottitle = None # "Overview: {0} components ".format(self.getComponent()) plottitle = None # "Overview: {0} components ".format(self.getComponent())
self.disconnectWFplotEvents() self.disconnectWFplotEvents()
if str(settings.value('pyqtgraphic')) == 'false' or not pg:
self.pg = False
self.dataPlot = PylotCanvas(parent=self, connect_events=False, multicursor=True)
self.dataPlot.updateWidget(xlab, None, plottitle)
else:
self.pg = pg self.pg = pg
self.dataPlot = WaveformWidgetPG(parent=self, self.dataPlot = WaveformWidgetPG(parent=self,
title=plottitle) title=plottitle)
@ -860,7 +868,8 @@ class MainWindow(QMainWindow):
def inputs(self): def inputs(self):
return self._inputs return self._inputs
def getRoot(self): @staticmethod
def getRoot():
settings = QSettings() settings = QSettings()
return settings.value("data/dataRoot") return settings.value("data/dataRoot")
@ -891,7 +900,7 @@ class MainWindow(QMainWindow):
fext = '.xml' fext = '.xml'
for event in events: for event in events:
path = event.path path = event.path
eventname = path.split('/')[-1] #or event.pylot_id eventname = path.split('/')[-1] # or event.pylot_id
filename = os.path.join(path, 'PyLoT_' + eventname + fext) filename = os.path.join(path, 'PyLoT_' + eventname + fext)
if os.path.isfile(filename): if os.path.isfile(filename):
self.load_data(filename, draw=False, event=event, overwrite=True) self.load_data(filename, draw=False, event=event, overwrite=True)
@ -900,6 +909,7 @@ class MainWindow(QMainWindow):
return return
if self.get_current_event().pylot_picks: if self.get_current_event().pylot_picks:
self.refreshEvents() self.refreshEvents()
self.fill_eventbox()
self.setDirty(True) self.setDirty(True)
def load_data(self, fname=None, loc=False, draw=True, event=None, overwrite=False): def load_data(self, fname=None, loc=False, draw=True, event=None, overwrite=False):
@ -934,7 +944,9 @@ class MainWindow(QMainWindow):
return return
self.data = data self.data = data
print('Loading event info from file {}.'.format(fname)) message = 'Loading event info from file {}.'.format(fname)
print(message)
self.update_status(message)
if not loc: if not loc:
self.updatePicks(type='auto', event=event) self.updatePicks(type='auto', event=event)
self.updatePicks(type='manual', event=event) self.updatePicks(type='manual', event=event)
@ -961,7 +973,7 @@ class MainWindow(QMainWindow):
button.setAutoFillBackground(True) button.setAutoFillBackground(True)
elif type(color) == str: elif type(color) == str:
button.setStyleSheet('QPushButton{border-color: %s}' button.setStyleSheet('QPushButton{border-color: %s}'
'QPushButton:checked{background-color: rgba%s}'% (color, color)) 'QPushButton:checked{background-color: rgba%s}' % (color, color))
elif type(color) == tuple: elif type(color) == tuple:
button.setStyleSheet('QPushButton{border-color: rgba%s}' button.setStyleSheet('QPushButton{border-color: rgba%s}'
'QPushButton:checked{background-color: rgba%s}' % (str(color), str(color))) 'QPushButton:checked{background-color: rgba%s}' % (str(color), str(color)))
@ -1019,7 +1031,8 @@ class MainWindow(QMainWindow):
raise DatastructureError('not specified') raise DatastructureError('not specified')
return fnames return fnames
def getPhaseID(self, phase): @staticmethod
def getPhaseID(phase):
return identifyPhaseID(phase) return identifyPhaseID(phase)
def get_current_event(self, eventbox=None): def get_current_event(self, eventbox=None):
@ -1028,7 +1041,7 @@ class MainWindow(QMainWindow):
''' '''
if not eventbox: if not eventbox:
eventbox = self.eventBox eventbox = self.eventBox
path = eventbox.currentText() path = eventbox.currentText().split('*')[0]
return self.project.getEventFromPath(path) return self.project.getEventFromPath(path)
def get_current_event_path(self, eventbox=None): def get_current_event_path(self, eventbox=None):
@ -1065,7 +1078,7 @@ class MainWindow(QMainWindow):
self.dataStructure = DATASTRUCTURE['obspyDMT']() self.dataStructure = DATASTRUCTURE['obspyDMT']()
eventlist = check_all_obspy(eventlist) eventlist = check_all_obspy(eventlist)
else: else:
print('Settings Datastructure to PILOT') print('Setting Datastructure to PILOT')
self.dataStructure = DATASTRUCTURE['PILOT']() self.dataStructure = DATASTRUCTURE['PILOT']()
eventlist = check_all_pylot(eventlist) eventlist = check_all_pylot(eventlist)
if not eventlist: if not eventlist:
@ -1147,7 +1160,8 @@ class MainWindow(QMainWindow):
self.project.remove_event(event) self.project.remove_event(event)
self.init_events(True) self.init_events(True)
def createEventBox(self): @staticmethod
def createEventBox():
''' '''
Eventbox generator. Eventbox generator.
''' '''
@ -1257,6 +1271,8 @@ class MainWindow(QMainWindow):
'auto': event.pylot_autopicks} 'auto': event.pylot_autopicks}
ma_count = {'manual': 0, ma_count = {'manual': 0,
'auto': 0} 'auto': 0}
ma_count_total = {'manual': 0,
'auto': 0}
for ma in ma_props.keys(): for ma in ma_props.keys():
if ma_props[ma]: if ma_props[ma]:
@ -1264,8 +1280,9 @@ class MainWindow(QMainWindow):
for phasename, pick in picks.items(): for phasename, pick in picks.items():
if not type(pick) in [dict, AttribDict]: if not type(pick) in [dict, AttribDict]:
continue continue
if get_quality_class(has_spe(pick), phaseErrors[self.getPhaseID(phasename)]) < 4: if pick.get('spe'):
ma_count[ma] += 1 ma_count[ma] += 1
ma_count_total[ma] += 1
event_ref = event.isRefEvent() event_ref = event.isRefEvent()
event_test = event.isTestEvent() event_test = event.isTestEvent()
@ -1287,15 +1304,18 @@ class MainWindow(QMainWindow):
# p=event_npicks, # p=event_npicks,
# a=event_nautopicks) # a=event_nautopicks)
item_path = QtGui.QStandardItem('{path:{plen}}'.format(path=event_path, plen=plmax)) event_str = '{path:{plen}}'.format(path=event_path, plen=plmax)
if event.dirty:
event_str += '*'
item_path = QtGui.QStandardItem(event_str)
item_time = QtGui.QStandardItem('{}'.format(time)) item_time = QtGui.QStandardItem('{}'.format(time))
item_lat = QtGui.QStandardItem('{}'.format(lat)) item_lat = QtGui.QStandardItem('{}'.format(lat))
item_lon = QtGui.QStandardItem('{}'.format(lon)) item_lon = QtGui.QStandardItem('{}'.format(lon))
item_depth = QtGui.QStandardItem('{}'.format(depth)) item_depth = QtGui.QStandardItem('{}'.format(depth))
item_mag = QtGui.QStandardItem('{}'.format(mag)) item_mag = QtGui.QStandardItem('{}'.format(mag))
item_nmp = QtGui.QStandardItem(str(ma_count['manual'])) item_nmp = QtGui.QStandardItem('{}({})'.format(ma_count['manual'], ma_count_total['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('{}({})'.format(ma_count['auto'], ma_count_total['auto']))
item_nap.setIcon(self.autopicksicon_small) item_nap.setIcon(self.autopicksicon_small)
item_ref = QtGui.QStandardItem() # str(event_ref)) item_ref = QtGui.QStandardItem() # str(event_ref))
item_test = QtGui.QStandardItem() # str(event_test)) item_test = QtGui.QStandardItem() # str(event_test))
@ -1326,11 +1346,11 @@ class MainWindow(QMainWindow):
for item in itemlist: for item in itemlist:
item.setEnabled(False) item.setEnabled(False)
#item color # item color
self.setItemColor(itemlist, id, event, current_event) 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).split('*')[0].strip():
message = ('Path missmatch creating eventbox.\n' message = ('Path missmatch creating eventbox.\n'
'{} unequal {}.' '{} unequal {}.'
.format(event.path, self.eventBox.itemText(id))) .format(event.path, self.eventBox.itemText(id)))
@ -1429,15 +1449,24 @@ class MainWindow(QMainWindow):
self.update_status(msg) self.update_status(msg)
print(msg) print(msg)
event.dirty = False
self.fill_eventbox()
return True return True
def exportAllEvents(self, outformats=['.xml']): def exportEvents(self, outformats=['.xml'], events='all'):
for event in self.project.eventlist: if events == 'all':
events = self.project.eventlist
assert type(events) == list, 'Wrong input type: {}'.format(type(events))
for event in events:
if not event.dirty:
continue
self.get_data().setEvtData(event) self.get_data().setEvtData(event)
try: try:
self.saveData(event, event.path, outformats) self.saveData(event, event.path, outformats)
event.dirty = False
except Exception as e: except Exception as e:
print('WARNING! Could not save event {}. Reason: {}'.format(event.path, e)) print('WARNING! Could not save event {}. Reason: {}'.format(event.path, e))
self.fill_eventbox()
def enableSaveEventAction(self): def enableSaveEventAction(self):
self.saveEventAction.setEnabled(True) self.saveEventAction.setEnabled(True)
@ -1485,9 +1514,8 @@ class MainWindow(QMainWindow):
if len(eventdict) < 1: if len(eventdict) < 1:
return return
# init event selection options for autopick # init event selection options for autopick
self.compareoptions =[('tune events', self.get_ref_events, self._style['ref']['rgba']), self.compareoptions = [('tune events', self.get_ref_events, self._style['ref']['rgba']),
('test events', self.get_test_events, self._style['test']['rgba']), ('test events', self.get_test_events, self._style['test']['rgba']),
('all (picked) events', self.get_manu_picked_events, None)] ('all (picked) events', self.get_manu_picked_events, None)]
@ -1512,7 +1540,6 @@ class MainWindow(QMainWindow):
compare_widget = self.buildMultiCompareWidget(eventlist_overlap) compare_widget = self.buildMultiCompareWidget(eventlist_overlap)
compare_widget.show() compare_widget.show()
def buildMultiCompareWidget(self, eventlist): def buildMultiCompareWidget(self, eventlist):
global_comparison = Comparison(eventlist=eventlist) global_comparison = Comparison(eventlist=eventlist)
compare_widget = ComparisonWidget(global_comparison, self) compare_widget = ComparisonWidget(global_comparison, self)
@ -1784,7 +1811,7 @@ class MainWindow(QMainWindow):
nth_sample = int(settings.value("nth_sample")) if settings.value("nth_sample") else 1 nth_sample = int(settings.value("nth_sample")) if settings.value("nth_sample") else 1
npts_max = 1e7 npts_max = 1e7
npts = self.get_npts_to_plot() npts = self.get_npts_to_plot()
npts2plot = npts/nth_sample npts2plot = npts / nth_sample
if npts2plot < npts_max: if npts2plot < npts_max:
settings.setValue('large_dataset', False) settings.setValue('large_dataset', False)
else: else:
@ -1911,7 +1938,7 @@ class MainWindow(QMainWindow):
event = self.get_current_event() event = self.get_current_event()
if event.pylot_picks: if event.pylot_picks:
self.drawPicks(picktype='manual') self.drawPicks(picktype='manual')
self.locateEvent.setEnabled(True) self.locateEventAction.setEnabled(True)
self.qualities_action.setEnabled(True) self.qualities_action.setEnabled(True)
if event.pylot_autopicks: if event.pylot_autopicks:
self.drawPicks(picktype='auto') self.drawPicks(picktype='auto')
@ -1919,8 +1946,18 @@ class MainWindow(QMainWindow):
if True in self.comparable.values(): if True in self.comparable.values():
self.compare_action.setEnabled(True) self.compare_action.setEnabled(True)
self.draw() self.draw()
# TODO: Quick and dirty, improve this on later iteration
if self.obspy_dmt:
invpath = os.path.join(self.get_current_event_path(), 'resp')
if not invpath in self.metadata.inventories:
self.metadata.add_inventory(invpath)
# check if directory is empty
if os.listdir(invpath):
self.init_map_button.setEnabled(True)
self.initMapAction.setEnabled(True)
def checkEvent4comparison(self, event): @staticmethod
def checkEvent4comparison(event):
if event.pylot_picks and event.pylot_autopicks: if event.pylot_picks and event.pylot_autopicks:
for station in event.pylot_picks: for station in event.pylot_picks:
if station in event.pylot_autopicks: if station in event.pylot_autopicks:
@ -1972,6 +2009,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)
self.compare_action.setEnabled(False)
self.qualities_action.setEnabled(False)
self.locateEventAction.setEnabled(False)
if not refresh_plot: if not refresh_plot:
self.wf_scroll_area.setVisible(False) self.wf_scroll_area.setVisible(False)
self.no_data_label.setVisible(True) self.no_data_label.setVisible(True)
@ -2081,7 +2121,8 @@ class MainWindow(QMainWindow):
elif self.filterActionS.isChecked(): elif self.filterActionS.isChecked():
phase = 'S' phase = 'S'
if self.getFilterOptions(): if self.getFilterOptions():
if (phase == 'P' and self.filterActionP.isChecked()) or (phase == 'S' and self.filterActionS.isChecked()): if (phase == 'P' and self.filterActionP.isChecked()) or (
phase == 'S' and self.filterActionS.isChecked()):
kwargs = self.getFilterOptions()[phase].parseFilterOptions() kwargs = self.getFilterOptions()[phase].parseFilterOptions()
self.pushFilterWF(kwargs) self.pushFilterWF(kwargs)
else: else:
@ -2092,8 +2133,8 @@ class MainWindow(QMainWindow):
self.get_data().resetWFData() self.get_data().resetWFData()
if plot: if plot:
self.plotWaveformDataThread(filter=False) self.plotWaveformDataThread(filter=False)
#self.drawPicks() # self.drawPicks()
#self.draw() # self.draw()
def getAutoFilteroptions(self, phase): def getAutoFilteroptions(self, phase):
return getAutoFilteroptions(phase, self._inputs) return getAutoFilteroptions(phase, self._inputs)
@ -2321,14 +2362,12 @@ class MainWindow(QMainWindow):
if pickDlg._dirty: if pickDlg._dirty:
self.setDirty(True) self.setDirty(True)
self.update_status('picks accepted ({0})'.format(station)) self.update_status('picks accepted ({0})'.format(station))
replot1 = self.addPicks(station, pickDlg.getPicks(picktype='manual'), type='manual') self.addPicks(station, pickDlg.getPicks(picktype='manual'), type='manual')
replot2 = self.addPicks(station, pickDlg.getPicks(picktype='auto'), type='auto') self.addPicks(station, pickDlg.getPicks(picktype='auto'), type='auto')
self.enableSaveEventAction() self.enableSaveEventAction()
if replot1 or replot2: self.comparable = self.checkEvents4comparison()
self.plotWaveformDataThread() if True in self.comparable.values():
self.compare_action.setEnabled(True)
self.draw()
else:
self.drawPicks(station) self.drawPicks(station)
self.draw() self.draw()
if self.nextStation: if self.nextStation:
@ -2336,7 +2375,7 @@ class MainWindow(QMainWindow):
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():
self.locateEvent.setEnabled(True) self.locateEventAction.setEnabled(True)
self.set_loc_flag(True) self.set_loc_flag(True)
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)
@ -2413,7 +2452,7 @@ class MainWindow(QMainWindow):
# thread to prevent handling of QPixmap objects outside of # thread to prevent handling of QPixmap objects outside of
# the main thread # the main thread
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.obspy_dmt) 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
@ -2422,10 +2461,10 @@ class MainWindow(QMainWindow):
# creating and filling figure canvas # creating and filling figure canvas
self.tap.update.connect(self.update_autopicker) self.tap.update.connect(self.update_autopicker)
self.tap.figure_tabs.setCurrentIndex(0) self.tap.figure_tabs.setCurrentIndex(0)
#else: # else:
# self.update_autopicker() # self.update_autopicker()
# self.tap.fill_eventbox() # self.tap.fill_eventbox()
self.tap.show() self.tap.showMaximized()
def update_autopicker(self): def update_autopicker(self):
''' '''
@ -2437,11 +2476,13 @@ class MainWindow(QMainWindow):
canvas.setZoomBorders2content() canvas.setZoomBorders2content()
if self.tap.pylot_picks: if self.tap.pylot_picks:
station = self.tap.get_current_station() station = self.tap.get_current_station()
p_pick = self.tap.pylot_picks[station]['P'] p_pick = self.tap.pylot_picks[station].get('P')
s_pick = self.tap.pylot_picks[station]['S'] if p_pick:
self.tap.pickDlg.autopicks['P_tuning'] = p_pick self.tap.pickDlg.autopicks['P_tuning'] = p_pick
self.tap.pickDlg.autopicks['S_tuning'] = s_pick
self.tap.pickDlg.drawPicks(phase='P_tuning', picktype='auto', picks=p_pick) self.tap.pickDlg.drawPicks(phase='P_tuning', picktype='auto', picks=p_pick)
s_pick = self.tap.pylot_picks[station].get('S')
if s_pick:
self.tap.pickDlg.autopicks['S_tuning'] = s_pick
self.tap.pickDlg.drawPicks(phase='S_tuning', picktype='auto', picks=s_pick) self.tap.pickDlg.drawPicks(phase='S_tuning', picktype='auto', picks=s_pick)
def autoPick(self): def autoPick(self):
@ -2453,7 +2494,7 @@ class MainWindow(QMainWindow):
if not self.apw: if not self.apw:
# init event selection options for autopick # init event selection options for autopick
self.pickoptions =[('current event', self.get_current_event, None), self.pickoptions = [('current event', self.get_current_event, None),
('tune events', self.get_ref_events, self._style['ref']['rgba']), ('tune events', self.get_ref_events, self._style['ref']['rgba']),
('test events', self.get_test_events, self._style['test']['rgba']), ('test events', self.get_test_events, self._style['test']['rgba']),
('all (picked) events', self.get_manu_picked_events, None), ('all (picked) events', self.get_manu_picked_events, None),
@ -2495,7 +2536,7 @@ class MainWindow(QMainWindow):
self.apw.enable(False) self.apw.enable(False)
# export current picks etc. # export current picks etc.
self.exportAllEvents(['.xml']) self.exportEvents(['.xml'], events=events)
wfpath = self.dataPlot.qcombo_processed.currentText() if self.obspy_dmt else '' wfpath = self.dataPlot.qcombo_processed.currentText() if self.obspy_dmt else ''
# define arguments for picker # define arguments for picker
@ -2554,13 +2595,15 @@ class MainWindow(QMainWindow):
if event.pylot_id == eventID: if event.pylot_id == eventID:
return event return event
def get_event_paths(self, eventlist): @staticmethod
def get_event_paths(eventlist):
eventPaths = [] eventPaths = []
for event in eventlist: for event in eventlist:
eventPaths.append(event.path) eventPaths.append(event.path)
return eventPaths return eventPaths
def get_event_ids(self, eventlist): @staticmethod
def get_event_ids(eventlist):
eventIDs = [] eventIDs = []
for event in eventlist: for event in eventlist:
eventIDs.append(event.pylot_id) eventIDs.append(event.pylot_id)
@ -2633,7 +2676,7 @@ class MainWindow(QMainWindow):
picksdict = picksdict_from_picks(evt=self.get_data().get_evt_data()) picksdict = picksdict_from_picks(evt=self.get_data().get_evt_data())
if type == 'manual': if type == 'manual':
event.addPicks(picksdict['manual']) event.addPicks(picksdict['manual'])
#event.picks.update(picks) MP MP idea # event.picks.update(picks) MP MP idea
elif type == 'auto': elif type == 'auto':
event.addAutopicks(picksdict['auto']) event.addAutopicks(picksdict['auto'])
@ -2647,12 +2690,25 @@ class MainWindow(QMainWindow):
self.drawPicks(station, 'auto', stime) self.drawPicks(station, 'auto', stime)
return return
if not picktype in ['manual', 'auto']:
raise TypeError('Unknown picktype {0}'.format(picktype))
# if picks to draw not specified, draw all picks available # if picks to draw not specified, draw all picks available
if not station: if not station:
for station in self.getPicks(type=picktype): for station in self.getPicks(type=picktype):
self.drawPicks(station, picktype=picktype, stime=stime) self.drawPicks(station, picktype=picktype, stime=stime)
return return
if self.pg:
pw = self.getPlotWidget().plotWidget
else:
ax = self.getPlotWidget().axes[0]
if station in self.drawnPicks[picktype].keys():
for item in self.drawnPicks[picktype][station]:
pw.removeItem(item)
self.drawnPicks[picktype][station] = []
# check for station key in dictionary, else return # check for station key in dictionary, else return
if not station in self.getPicks(type=picktype): if not station in self.getPicks(type=picktype):
return return
@ -2661,13 +2717,10 @@ class MainWindow(QMainWindow):
plotID = self.getStationID(station) plotID = self.getStationID(station)
if plotID is None: if plotID is None:
return return
if self.pg:
pw = self.getPlotWidget().plotWidget
else:
ax = self.getPlotWidget().axes[0]
ylims = np.array([-.5, +.5]) + plotID ylims = np.array([-.5, +.5]) + plotID
stat_picks = self.getPicks(type=picktype)[station] stat_picks = self.getPicks(type=picktype)[station]
settings = QSettings()
for phase in stat_picks: for phase in stat_picks:
if phase == 'SPt': continue # wadati SP time if phase == 'SPt': continue # wadati SP time
@ -2675,13 +2728,16 @@ class MainWindow(QMainWindow):
if type(stat_picks[phase]) is not dict and type(stat_picks[phase]) is not AttribDict: if type(stat_picks[phase]) is not dict and type(stat_picks[phase]) is not AttribDict:
return return
phaseID = self.getPhaseID(phase)
# get quality classes # get quality classes
if self.getPhaseID(phase) == 'P': if phaseID == 'P':
quality = get_quality_class(picks['spe'], self._inputs['timeerrorsP']) quality = getQualityFromUncertainty(picks['spe'], self._inputs['timeerrorsP'])
phaseID = 'P' elif phaseID == 'S':
elif self.getPhaseID(phase) == 'S': quality = getQualityFromUncertainty(picks['spe'], self._inputs['timeerrorsS'])
quality = get_quality_class(picks['spe'], self._inputs['timeerrorsS'])
phaseID = 'S' # quality = getPickQuality(self.get_data().getWFData(),
# stat_picks, self._inputs, phaseID,
# compclass)
mpp = picks['mpp'] - stime mpp = picks['mpp'] - stime
if picks['epp'] and picks['lpp']: if picks['epp'] and picks['lpp']:
@ -2692,44 +2748,32 @@ class MainWindow(QMainWindow):
lpp = None lpp = None
spe = picks['spe'] spe = picks['spe']
if not spe and epp and lpp:
spe = symmetrize_error(mpp - epp, lpp - mpp)
if self.pg: if self.pg:
if picktype == 'manual': if spe:
if picks['epp'] and picks['lpp']: if picks['epp'] and picks['lpp']:
pen = make_pen(picktype, phaseID, 'epp', quality) pen = make_pen(picktype, phaseID, 'epp', quality)
pw.plot([epp, epp], ylims, self.drawnPicks[picktype][station].append(pw.plot([epp, epp], ylims,
alpha=.25, pen=pen, name='EPP') alpha=.25, pen=pen, name='EPP'))
pen = make_pen(picktype, phaseID, 'lpp', quality) pen = make_pen(picktype, phaseID, 'lpp', quality)
pw.plot([lpp, lpp], ylims, self.drawnPicks[picktype][station].append(pw.plot([lpp, lpp], ylims,
alpha=.25, pen=pen, name='LPP') alpha=.25, pen=pen, name='LPP'))
pen = make_pen(picktype, phaseID, 'spe', quality)
spe_l = pg.PlotDataItem([mpp - spe, mpp - spe], ylims, pen=pen,
name='{}-SPE'.format(phase))
spe_r = pg.PlotDataItem([mpp + spe, mpp + spe], ylims, pen=pen)
try:
color = pen.color()
color.setAlpha(100.)
brush = pen.brush()
brush.setColor(color)
fill = pg.FillBetweenItem(spe_l, spe_r, brush=brush)
fb = pw.addItem(fill)
self.drawnPicks[picktype][station].append(fill)
except:
print('Warning: drawPicks: Could not create fill for symmetric pick error.')
pen = make_pen(picktype, phaseID, 'mpp', quality) pen = make_pen(picktype, phaseID, 'mpp', quality)
if spe: self.drawnPicks[picktype][station].append(
# pen = make_pen(picktype, phaseID, 'spe', quality) pw.plot([mpp, mpp], ylims, pen=pen, name='{}-Pick'.format(phase)))
# spe_l = pg.PlotDataItem([mpp - spe, mpp - spe], ylims, pen=pen,
# name='{}-SPE'.format(phase))
# spe_r = pg.PlotDataItem([mpp + spe, mpp + spe], ylims, pen=pen)
# pw.addItem(spe_l)
# pw.addItem(spe_r)
# try:
# color = pen.color()
# color.setAlpha(100.)
# brush = pen.brush()
# brush.setColor(color)
# fill = pg.FillBetweenItem(spe_l, spe_r, brush=brush)
# fb = pw.addItem(fill)
# except:
# print('Warning: drawPicks: Could not create fill for symmetric pick error.')
pw.plot([mpp, mpp], ylims, pen=pen, name='{}-Pick'.format(phase))
else:
pw.plot([mpp, mpp], ylims, pen=pen, name='{}-Pick (NO PICKERROR)'.format(phase))
elif picktype == 'auto':
if quality < 4:
pen = make_pen(picktype, phaseID, 'mpp', quality)
pw.plot([mpp, mpp], ylims, pen=pen)
else:
raise TypeError('Unknown picktype {0}'.format(picktype))
else: else:
if picktype == 'manual': if picktype == 'manual':
linestyle_mpp, width_mpp = pick_linestyle_plt(picktype, 'mpp') linestyle_mpp, width_mpp = pick_linestyle_plt(picktype, 'mpp')
@ -2789,7 +2833,7 @@ class MainWindow(QMainWindow):
lt.locate(ctrfile) lt.locate(ctrfile)
except RuntimeError as e: except RuntimeError as e:
print(e.message) print(e.message)
#finally: # finally:
# os.remove(phasefile) # os.remove(phasefile)
self.get_data().applyEVTData(lt.read_location(locpath), typ='event') self.get_data().applyEVTData(lt.read_location(locpath), typ='event')
@ -2817,7 +2861,7 @@ class MainWindow(QMainWindow):
self.inventory_label = QLabel('No inventory set...') self.inventory_label = QLabel('No inventory set...')
# init inventory button # init inventory button
self.new_inv_button = QPushButton('Set inventory file') self.new_inv_button = QPushButton('Manage inventory files')
self.new_inv_button.setIcon(self.inventoryIcon) self.new_inv_button.setIcon(self.inventoryIcon)
self.new_inv_button.clicked.connect(self.inventoryAction.trigger) self.new_inv_button.clicked.connect(self.inventoryAction.trigger)
@ -2830,7 +2874,6 @@ class MainWindow(QMainWindow):
grid_layout.addWidget(self.new_inv_button, 2, 1) grid_layout.addWidget(self.new_inv_button, 2, 1)
grid_layout.addWidget(self.init_map_button, 3, 1) grid_layout.addWidget(self.init_map_button, 3, 1)
self.metadata = None
self.metadata_widget.setLayout(grid_layout) self.metadata_widget.setLayout(grid_layout)
self.array_layout.addWidget(self.metadata_widget) self.array_layout.addWidget(self.metadata_widget)
@ -2892,7 +2935,7 @@ class MainWindow(QMainWindow):
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.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):
@ -2984,6 +3027,8 @@ class MainWindow(QMainWindow):
'auto': event.pylot_autopicks} 'auto': event.pylot_autopicks}
ma_count = {'manual': 0, ma_count = {'manual': 0,
'auto': 0} 'auto': 0}
ma_count_total = {'manual': 0,
'auto': 0}
for ma in ma_props.keys(): for ma in ma_props.keys():
if ma_props[ma]: if ma_props[ma]:
@ -2991,8 +3036,9 @@ class MainWindow(QMainWindow):
for phasename, pick in picks.items(): for phasename, pick in picks.items():
if not type(pick) in [dict, AttribDict]: if not type(pick) in [dict, AttribDict]:
continue continue
if get_quality_class(has_spe(pick), phaseErrors[self.getPhaseID(phasename)]) < 4: if pick.get('spe'):
ma_count[ma] += 1 ma_count[ma] += 1
ma_count_total[ma] += 1
# init table items for current row # init table items for current row
item_delete = QtGui.QTableWidgetItem() item_delete = QtGui.QTableWidgetItem()
@ -3003,15 +3049,19 @@ class MainWindow(QMainWindow):
item_lon = QtGui.QTableWidgetItem() item_lon = QtGui.QTableWidgetItem()
item_depth = QtGui.QTableWidgetItem() item_depth = QtGui.QTableWidgetItem()
item_mag = QtGui.QTableWidgetItem() item_mag = QtGui.QTableWidgetItem()
item_nmp = QtGui.QTableWidgetItem(str(ma_count['manual'])) item_nmp = QtGui.QTableWidgetItem('{}({})'.format(ma_count['manual'], ma_count_total['manual']))
item_nmp.setIcon(self.manupicksicon_small) item_nmp.setIcon(self.manupicksicon_small)
item_nap = QtGui.QTableWidgetItem(str(ma_count['auto'])) item_nap = QtGui.QTableWidgetItem('{}({})'.format(ma_count['auto'], ma_count_total['auto']))
item_nap.setIcon(self.autopicksicon_small) item_nap.setIcon(self.autopicksicon_small)
item_ref = QtGui.QTableWidgetItem() item_ref = QtGui.QTableWidgetItem()
item_test = QtGui.QTableWidgetItem() item_test = QtGui.QTableWidgetItem()
item_notes = QtGui.QTableWidgetItem() item_notes = QtGui.QTableWidgetItem()
item_path.setText(event.path) event_str = event.path
if event.dirty:
event_str += '*'
item_path.setText(event_str)
if hasattr(event, 'origins'): if hasattr(event, 'origins'):
if event.origins: if event.origins:
origin = event.origins[0] origin = event.origins[0]
@ -3089,66 +3139,23 @@ class MainWindow(QMainWindow):
if event == current_event: if event == current_event:
set_background_color(item_list, QtGui.QColor(*(0, 143, 143, 255))) set_background_color(item_list, QtGui.QColor(*(0, 143, 143, 255)))
def read_metadata_thread(self, fninv):
self.rm_thread = Thread(self, read_metadata, arg=fninv, progressText='Reading metadata...',
pb_widget=self.mainProgressBarWidget)
self.rm_thread.finished.connect(self.set_metadata)
self.rm_thread.start()
def set_metadata(self): def set_metadata(self):
settings = QSettings() self.project.inventories = self.metadata.inventories
self.metadata = self.rm_thread.data if self.metadata.inventories:
if settings.value('saveMetadata'):
self.project.metadata = self.rm_thread.data
self.project.inv_path = settings.value("inventoryFile")
self.init_map_button.setEnabled(True) self.init_map_button.setEnabled(True)
self.initMapAction.setEnabled(True) self.initMapAction.setEnabled(True)
self.inventory_label.setText('Inventory set!') self.inventory_label.setText('Inventory set!')
self.new_inv_button.setText('Set another inventory file') self.setDirty(True)
def get_new_metadata(self): def add_metadata(self):
self.init_metadata(new=True) self.add_metadata_widget = AddMetadataWidget(self, metadata=self.metadata)
self.add_metadata_widget.close_button.clicked.connect(self.set_metadata)
def init_metadata(self, new=False, ask_default=True): def init_metadata(self, new=False, ask_default=True):
def set_inv(settings): if hasattr(self.project, 'inventories'):
fninv, _ = QFileDialog.getOpenFileName(self, self.tr( self.metadata = Metadata()
"Select inventory..."), self.tr("Select file")) for inventory in self.project.inventories:
if not fninv: self.metadata.add_inventory(inventory)
return False
ans = QMessageBox.question(self, self.tr("Make default..."),
self.tr(
"New inventory filename set.\n" + \
"Do you want to make it the default value?"),
QMessageBox.Yes | QMessageBox.No,
QMessageBox.No)
if ans == QMessageBox.Yes:
settings.setValue("inventoryFile", fninv)
settings.sync()
self.read_metadata_thread(fninv)
return True
settings = QSettings()
if hasattr(self.project, 'metadata') and not new:
self.metadata = self.project.metadata
return True
if self.metadata and not new:
return True
if hasattr(self.project, 'inv_path') and not new:
settings.setValue("inventoryFile", self.project.inv_path)
fninv = settings.value("inventoryFile", None)
if (fninv and ask_default) and not new:
ans = QMessageBox.question(self, self.tr("Use default metadata..."),
self.tr(
"Do you want to use the default value for metadata?\n({})".format(fninv)),
QMessageBox.Yes | QMessageBox.No,
QMessageBox.Yes)
if ans == QMessageBox.Yes:
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()
@ -3161,7 +3168,7 @@ class MainWindow(QMainWindow):
# raise ProcessingError('Restitution of waveform data failed!') # raise ProcessingError('Restitution of waveform data failed!')
if type == 'ML': if type == 'ML':
local_mag = LocalMagnitude(corr_wf, self.get_data().get_evt_data(), self.inputs.get('sstop'), local_mag = LocalMagnitude(corr_wf, self.get_data().get_evt_data(), self.inputs.get('sstop'),
verbosity=True) ## MP MP missing parameter wascaling in function call! self.inputs.get('WAScaling'), verbosity=True)
return local_mag.updated_event() return local_mag.updated_event()
elif type == 'Mw': elif type == 'Mw':
moment_mag = MomentMagnitude(corr_wf, self.get_data().get_evt_data(), self.inputs.get('vp'), moment_mag = MomentMagnitude(corr_wf, self.get_data().get_evt_data(), self.inputs.get('vp'),
@ -3268,20 +3275,17 @@ class MainWindow(QMainWindow):
if self.project.parameter: if self.project.parameter:
self._inputs = self.project.parameter self._inputs = self.project.parameter
self.updateFilteroptions() self.updateFilteroptions()
# added for backwards compatibility with older events not having a 'dirty' attribute
for event in self.project.eventlist:
if not hasattr(event, 'dirty'):
event.dirty = False
self.tabs.setCurrentIndex(0) # implemented to prevent double-loading of waveform data self.tabs.setCurrentIndex(0) # implemented to prevent double-loading of waveform data
self.init_events(new=True) self.init_events(new=True)
self.setDirty(False) self.setDirty(False)
if hasattr(self.project, 'metadata'): self.init_metadata()
if self.project.metadata:
self.init_metadata(ask_default=False)
#self.init_array_map(index=0)
return
if hasattr(self.project, 'inv_path'):
self.init_metadata(ask_default=False)
#self.init_array_map(index=0)
return
self.init_array_tab() self.init_array_tab()
self.set_metadata()
def saveProjectAs(self, exists=False): def saveProjectAs(self, exists=False):
''' '''
@ -3295,10 +3299,10 @@ class MainWindow(QMainWindow):
if not filename.split('.')[-1] == 'plp': if not filename.split('.')[-1] == 'plp':
filename = fnm[0] + '.plp' filename = fnm[0] + '.plp'
self.project.parameter = self._inputs self.project.parameter = self._inputs
self.exportEvents()
self.project.save(filename) self.project.save(filename)
self.setDirty(False) self.setDirty(False)
self.saveProjectAsAction.setEnabled(True) self.saveProjectAsAction.setEnabled(True)
self.exportAllEvents()
self.update_status('Saved new project to {}'.format(filename), duration=5000) self.update_status('Saved new project to {}'.format(filename), duration=5000)
return True return True
@ -3313,11 +3317,11 @@ class MainWindow(QMainWindow):
return False return False
else: else:
self.project.parameter = self._inputs self.project.parameter = self._inputs
self.exportEvents()
self.project.save() self.project.save()
self.exportAllEvents()
if not self.project.dirty: if not self.project.dirty:
self.update_status('Saved back project to file:\n{}'.format(self.project.location), duration=5000)
self.setDirty(False) self.setDirty(False)
self.update_status('Saved back project to file:\n{}'.format(self.project.location), duration=5000)
return True return True
else: else:
# if still dirty because saving failed # if still dirty because saving failed
@ -3335,7 +3339,6 @@ class MainWindow(QMainWindow):
self.dataPlot.setPermText(1) self.dataPlot.setPermText(1)
self.dataPlot.setPermText(0, '| Number of traces: {} |'.format(len(self.getPlotWidget().getPlotDict()))) self.dataPlot.setPermText(0, '| Number of traces: {} |'.format(len(self.getPlotWidget().getPlotDict())))
def _setDirty(self): def _setDirty(self):
self.setDirty(True) self.setDirty(True)
@ -3344,6 +3347,7 @@ class MainWindow(QMainWindow):
self.saveProjectAsAction.setEnabled(True) self.saveProjectAsAction.setEnabled(True)
self.project.setDirty(value) self.project.setDirty(value)
self.dirty = value self.dirty = value
self.fill_eventbox()
def closeEvent(self, event): def closeEvent(self, event):
if self.okToContinue(): if self.okToContinue():
@ -3579,7 +3583,7 @@ def create_window():
def main(args=None): def main(args=None):
project_filename = None project_filename = None
#args.project_filename = 'C:/Shared/AlpArray/alparray_data/project_alparray_test.plp' # args.project_filename = 'C:/Shared/AlpArray/alparray_data/project_alparray_test.plp'
pylot_infile = None pylot_infile = None
if args: if args:
if args.project_filename: if args.project_filename:

View File

@ -7,6 +7,7 @@ import argparse
import datetime import datetime
import glob import glob
import os import os
import traceback
import pylot.core.loc.focmec as focmec import pylot.core.loc.focmec as focmec
import pylot.core.loc.hash as hash import pylot.core.loc.hash as hash
@ -22,7 +23,7 @@ from pylot.core.analysis.magnitude import MomentMagnitude, LocalMagnitude
from pylot.core.io.data import Data from pylot.core.io.data import Data
from pylot.core.io.inputs import PylotParameter from pylot.core.io.inputs import PylotParameter
from pylot.core.pick.autopick import autopickevent, iteratepicker from pylot.core.pick.autopick import autopickevent, iteratepicker
from pylot.core.util.dataprocessing import restitute_data, read_metadata from pylot.core.util.dataprocessing import restitute_data, read_metadata, Metadata
from pylot.core.util.defaults import SEPARATOR from pylot.core.util.defaults import SEPARATOR
from pylot.core.util.event import Event from pylot.core.util.event import Event
from pylot.core.util.structure import DATASTRUCTURE from pylot.core.util.structure import DATASTRUCTURE
@ -38,6 +39,7 @@ def autoPyLoT(input_dict=None, parameter=None, inputfile=None, fnames=None, even
""" """
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.
:param obspyDMT_wfpath: if obspyDMT is used, name of data directory ("raw" or "processed")
:param input_dict: :param input_dict:
:type input_dict: :type input_dict:
:param parameter: PylotParameter object containing parameters used for automatic picking :param parameter: PylotParameter object containing parameters used for automatic picking
@ -53,7 +55,7 @@ def autoPyLoT(input_dict=None, parameter=None, inputfile=None, fnames=None, even
:type savepath: str :type savepath: str
:param savexml: export results in XML file if True :param savexml: export results in XML file if True
:type savexml: bool :type savexml: bool
:param station: list of station names or 'all' to pick all stations :param station: choose specific station name or 'all' to pick all stations
:type station: str :type station: str
:param iplot: logical variable for plotting: 0=none, 1=partial, 2=all :param iplot: logical variable for plotting: 0=none, 1=partial, 2=all
:type iplot: int :type iplot: int
@ -117,7 +119,7 @@ def autoPyLoT(input_dict=None, parameter=None, inputfile=None, fnames=None, even
if not parameter: if not parameter:
if inputfile: if inputfile:
parameter = PylotParameter(inputfile) parameter = PylotParameter(inputfile)
#iplot = parameter['iplot'] # iplot = parameter['iplot']
else: else:
infile = os.path.join(os.path.expanduser('~'), '.pylot', 'pylot.in') infile = os.path.join(os.path.expanduser('~'), '.pylot', 'pylot.in')
print('Using default input file {}'.format(infile)) print('Using default input file {}'.format(infile))
@ -149,8 +151,8 @@ def autoPyLoT(input_dict=None, parameter=None, inputfile=None, fnames=None, even
datastructure.modifyFields(**dsfields) datastructure.modifyFields(**dsfields)
datastructure.setExpandFields(exf) datastructure.setExpandFields(exf)
# check if default location routine NLLoc is available # check if default location routine NLLoc is available and all stations are used
if real_None(parameter['nllocbin']): if real_None(parameter['nllocbin']) and station == 'all':
locflag = 1 locflag = 1
# get NLLoc-root path # get NLLoc-root path
nllocroot = parameter.get('nllocroot') nllocroot = parameter.get('nllocroot')
@ -200,8 +202,7 @@ def autoPyLoT(input_dict=None, parameter=None, inputfile=None, fnames=None, even
events.append(os.path.join(datapath, eventID)) events.append(os.path.join(datapath, eventID))
else: else:
# autoPyLoT was initialized from GUI # autoPyLoT was initialized from GUI
events = [] events = [eventid]
events.append(eventid)
evID = os.path.split(eventid)[-1] evID = os.path.split(eventid)[-1]
locflag = 2 locflag = 2
else: else:
@ -271,24 +272,26 @@ def autoPyLoT(input_dict=None, parameter=None, inputfile=None, fnames=None, even
if not wfdat: if not wfdat:
print('Could not find station {}. STOP!'.format(station)) print('Could not find station {}. STOP!'.format(station))
return return
wfdat = remove_underscores(wfdat) #wfdat = remove_underscores(wfdat)
# trim components for each station to avoid problems with different trace starttimes for one station # trim components for each station to avoid problems with different trace starttimes for one station
wfdat = check4gaps(wfdat) wfdat = check4gaps(wfdat)
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')) if not wfpath_extension:
# TODO: (idea) read metadata from obspy_dmt database metadata = Metadata(parameter.get('invdir'))
# if not wfpath_extension: else:
# metadata = read_metadata(parameter.get('invdir')) metadata = Metadata(os.path.join(eventpath, 'resp'))
# else:
# metadata = None
corr_dat = None corr_dat = None
if metadata: if metadata:
# rotate stations to ZNE # rotate stations to ZNE
try:
wfdat = check4rotated(wfdat, metadata) wfdat = check4rotated(wfdat, metadata)
except Exception as e:
print('Could not rotate station {} to ZNE:\n{}'.format(wfdat[0].stats.station,
traceback.format_exc()))
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)
if not corr_dat and locflag: if not corr_dat and locflag:
locflag = 2 locflag = 2
print('Working on event %s. Stations: %s' % (eventpath, station)) print('Working on event %s. Stations: %s' % (eventpath, station))
@ -363,8 +366,9 @@ def autoPyLoT(input_dict=None, parameter=None, inputfile=None, fnames=None, even
WAscaling[2])) WAscaling[2]))
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)
if net_ml:
print("Network local magnitude: %4.1f" % net_ml.mag) print("Network local magnitude: %4.1f" % net_ml.mag)
if magscaling == None: if magscaling is None:
scaling = False scaling = False
elif magscaling[0] != 0 and magscaling[1] != 0: elif magscaling[0] != 0 and magscaling[1] != 0:
scaling = False scaling = False
@ -447,8 +451,9 @@ def autoPyLoT(input_dict=None, parameter=None, inputfile=None, fnames=None, even
WAscaling[2])) WAscaling[2]))
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)
if net_ml:
print("Network local magnitude: %4.1f" % net_ml.mag) print("Network local magnitude: %4.1f" % net_ml.mag)
if magscaling == None: if magscaling is None:
scaling = False scaling = False
elif magscaling[0] != 0 and magscaling[1] != 0: elif magscaling[0] != 0 and magscaling[1] != 0:
scaling = False scaling = False

View File

@ -17,6 +17,7 @@ from pylot.core.util.utils import common_range, fit_curve
from scipy import integrate, signal from scipy import integrate, signal
from scipy.optimize import curve_fit from scipy.optimize import curve_fit
def richter_magnitude_scaling(delta): def richter_magnitude_scaling(delta):
distance = np.array([0, 10, 20, 25, 30, 35, 40, 45, 50, 60, 70, 75, 85, 90, 100, 110, distance = np.array([0, 10, 20, 25, 30, 35, 40, 45, 50, 60, 70, 75, 85, 90, 100, 110,
120, 130, 140, 150, 160, 170, 180, 190, 200, 210, 230, 240, 250, 120, 130, 140, 150, 160, 170, 180, 190, 200, 210, 230, 240, 250,
@ -122,7 +123,7 @@ class Magnitude(object):
def net_magnitude(self, magscaling=None): def net_magnitude(self, magscaling=None):
if self: if self:
if magscaling == None: if magscaling is None:
scaling = False scaling = False
elif magscaling[0] != 0 and magscaling[1] != 0: elif magscaling[0] != 0 and magscaling[1] != 0:
scaling = False scaling = False
@ -225,7 +226,7 @@ class LocalMagnitude(Magnitude):
sqH = np.sqrt(power_sum) sqH = np.sqrt(power_sum)
# get time array # get time array
th=np.arange(0, st[0].stats.npts/st[0].stats.sampling_rate, st[0].stats.delta) 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)])
@ -261,11 +262,12 @@ class LocalMagnitude(Magnitude):
ax.set_xlabel('Time [s]') ax.set_xlabel('Time [s]')
ax.set_ylabel('Displacement [mm]') ax.set_ylabel('Displacement [mm]')
fig.show() fig.show()
try: input() try:
except SyntaxError: pass input()
except SyntaxError:
pass
plt.close(fig) plt.close(fig)
return wapp, fig return wapp, fig
def calc(self): def calc(self):
@ -510,6 +512,9 @@ def calcsourcespec(wfstream, onset, vp, delta, azimuth, incidence,
zdat = select_for_phase(wfstream, "P") zdat = select_for_phase(wfstream, "P")
if len(zdat) == 0:
raise IOError('No vertical component found in stream:\n{}'.format(wfstream))
dt = zdat[0].stats.delta dt = zdat[0].stats.delta
freq = zdat[0].stats.sampling_rate freq = zdat[0].stats.sampling_rate

View File

@ -17,6 +17,7 @@ from pylot.core.util.utils import fnConstructor, full_range, remove_underscores,
import pylot.core.loc.velest as velest import pylot.core.loc.velest as velest
from pylot.core.util.obspyDMT_interface import qml_from_obspyDMT from pylot.core.util.obspyDMT_interface import qml_from_obspyDMT
class Data(object): class Data(object):
""" """
Data container with attributes wfdata holding ~obspy.core.stream. Data container with attributes wfdata holding ~obspy.core.stream.
@ -299,7 +300,7 @@ class Data(object):
for i in range(len(picks_copy)): for i in range(len(picks_copy)):
if picks_copy[i].phase_hint[0] == 'P': if picks_copy[i].phase_hint[0] == 'P':
if (picks_copy[i].time_errors['upper_uncertainty'] >= upperErrors[0]) or \ if (picks_copy[i].time_errors['upper_uncertainty'] >= upperErrors[0]) or \
(picks_copy[i].time_errors['uncertainty'] == None): (picks_copy[i].time_errors['uncertainty'] is None):
print("Uncertainty exceeds or equal adjusted upper time error!") print("Uncertainty exceeds or equal adjusted upper time error!")
print("Adjusted uncertainty: {}".format(upperErrors[0])) print("Adjusted uncertainty: {}".format(upperErrors[0]))
print("Pick uncertainty: {}".format(picks_copy[i].time_errors['uncertainty'])) print("Pick uncertainty: {}".format(picks_copy[i].time_errors['uncertainty']))
@ -311,7 +312,7 @@ class Data(object):
break break
if picks_copy[i].phase_hint[0] == 'S': if picks_copy[i].phase_hint[0] == 'S':
if (picks_copy[i].time_errors['upper_uncertainty'] >= upperErrors[1]) or \ if (picks_copy[i].time_errors['upper_uncertainty'] >= upperErrors[1]) or \
(picks_copy[i].time_errors['uncertainty'] == None): (picks_copy[i].time_errors['uncertainty'] is None):
print("Uncertainty exceeds or equal adjusted upper time error!") print("Uncertainty exceeds or equal adjusted upper time error!")
print("Adjusted uncertainty: {}".format(upperErrors[1])) print("Adjusted uncertainty: {}".format(upperErrors[1]))
print("Pick uncertainty: {}".format(picks_copy[i].time_errors['uncertainty'])) print("Pick uncertainty: {}".format(picks_copy[i].time_errors['uncertainty']))
@ -404,7 +405,7 @@ class Data(object):
# 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 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)
@ -416,7 +417,6 @@ class Data(object):
self.dirty = False self.dirty = False
return True return True
def appendWFData(self, fnames, synthetic=False): def appendWFData(self, fnames, synthetic=False):
""" """
Read waveform data from fnames and append it to current wf data Read waveform data from fnames and append it to current wf data
@ -506,8 +506,10 @@ class Data(object):
# check for automatic picks # check for automatic picks
print("Writing phases to ObsPy-quakeml file") print("Writing phases to ObsPy-quakeml file")
for key in picks: for key in picks:
if not picks[key].get('P'):
continue
if picks[key]['P']['picker'] == 'auto': if picks[key]['P']['picker'] == 'auto':
print("Existing picks will be overwritten!") print("Existing auto-picks will be overwritten in pick-dictionary!")
picks = picks_from_picksdict(picks) picks = picks_from_picksdict(picks)
break break
else: else:

View File

@ -346,6 +346,7 @@ def picks_from_picksdict(picks, creation_info=None):
picks_list.append(pick) picks_list.append(pick)
return picks_list return picks_list
def reassess_pilot_db(root_dir, db_dir, out_dir=None, fn_param=None, verbosity=0): def reassess_pilot_db(root_dir, db_dir, out_dir=None, fn_param=None, verbosity=0):
import glob import glob
@ -499,7 +500,7 @@ def writephases(arrivals, fformat, filename, parameter=None, eventinfo=None):
except KeyError as e: except KeyError as e:
print(e) print(e)
fm = None fm = None
if fm == None: if fm is None:
fm = '?' fm = '?'
onset = arrivals[key]['P']['mpp'] onset = arrivals[key]['P']['mpp']
year = onset.year year = onset.year
@ -916,9 +917,9 @@ def merge_picks(event, picks):
network = pick.waveform_id.network_code network = pick.waveform_id.network_code
method = pick.method_id method = pick.method_id
for p in event.picks: for p in event.picks:
if p.waveform_id.station_code == station\ if p.waveform_id.station_code == station \
and p.waveform_id.network_code == network\ and p.waveform_id.network_code == network \
and p.phase_hint == phase\ and p.phase_hint == phase \
and (str(p.method_id) in str(method) and (str(p.method_id) in str(method)
or str(method) in str(p.method_id)): or str(method) in str(p.method_id)):
p.time, p.time_errors, p.waveform_id.network_code, p.method_id = time, err, network, method p.time, p.time_errors, p.waveform_id.network_code, p.method_id = time, err, network, method
@ -965,7 +966,7 @@ def getQualitiesfromxml(xmlnames, ErrorsP, ErrorsS, plotflag=1):
arrivals_copy = cat_copy.events[0].picks arrivals_copy = cat_copy.events[0].picks
# Prefere manual picks if qualities are sufficient! # Prefere manual picks if qualities are sufficient!
for Pick in arrivals: for Pick in arrivals:
if (Pick.method_id.id).split('/')[1] == 'manual': if Pick.method_id.id.split('/')[1] == 'manual':
mstation = Pick.waveform_id.station_code mstation = Pick.waveform_id.station_code
mstation_ext = mstation + '_' mstation_ext = mstation + '_'
for mpick in arrivals_copy: for mpick in arrivals_copy:
@ -973,14 +974,14 @@ def getQualitiesfromxml(xmlnames, ErrorsP, ErrorsS, plotflag=1):
if phase == 'P': if phase == 'P':
if ((mpick.waveform_id.station_code == mstation) or if ((mpick.waveform_id.station_code == mstation) or
(mpick.waveform_id.station_code == mstation_ext)) and \ (mpick.waveform_id.station_code == mstation_ext)) and \
((mpick.method_id).split('/')[1] == 'auto') and \ (mpick.method_id.split('/')[1] == 'auto') and \
(mpick.time_errors['uncertainty'] <= ErrorsP[3]): (mpick.time_errors['uncertainty'] <= ErrorsP[3]):
del mpick del mpick
break break
elif phase == 'S': elif phase == 'S':
if ((mpick.waveform_id.station_code == mstation) or if ((mpick.waveform_id.station_code == mstation) or
(mpick.waveform_id.station_code == mstation_ext)) and \ (mpick.waveform_id.station_code == mstation_ext)) and \
((mpick.method_id).split('/')[1] == 'auto') and \ (mpick.method_id.split('/')[1] == 'auto') and \
(mpick.time_errors['uncertainty'] <= ErrorsS[3]): (mpick.time_errors['uncertainty'] <= ErrorsS[3]):
del mpick del mpick
break break

View File

@ -76,6 +76,7 @@ def modify_inputs(ctrfn, root, nllocoutn, phasefn, tttn):
def locate(fnin, parameter=None): def locate(fnin, parameter=None):
""" """
takes an external program name and tries to run it takes an external program name and tries to run it
:param parameter: PyLoT Parameter object
:param fnin: external program name :param fnin: external program name
:return: None :return: None
""" """

View File

@ -8,6 +8,7 @@ function conglomerate utils.
:author: MAGS2 EP3 working group / Ludger Kueperkoch :author: MAGS2 EP3 working group / Ludger Kueperkoch
""" """
import traceback
import matplotlib.pyplot as plt import matplotlib.pyplot as plt
import numpy as np import numpy as np
@ -71,42 +72,34 @@ def autopickevent(data, param, iplot=0, fig_dict=None, fig_dict_wadatijack=None,
for station in stations: for station in stations:
topick = data.select(station=station) topick = data.select(station=station)
input_tuples.append((topick, param, apverbose, iplot, fig_dict, metadata, origin))
if iplot is None or iplot == 'None' or iplot == 0:
input_tuples.append((topick, param, apverbose, metadata, origin))
if iplot > 0:
all_onsets[station] = autopickstation(topick, param, verbose=apverbose,
iplot=iplot, fig_dict=fig_dict,
metadata=metadata, origin=origin)
if iplot > 0: if iplot > 0:
print('iPlot Flag active: NO MULTIPROCESSING possible.') print('iPlot Flag active: NO MULTIPROCESSING possible.')
return all_onsets ncores = 1
# rename str for ncores in case ncores == 0 (use all cores) # rename ncores for string representation in case ncores == 0 (use all cores)
ncores_str = ncores if ncores != 0 else 'all available' ncores_str = ncores if ncores != 0 else 'all available'
print('Autopickstation: Distribute autopicking for {} ' print('Autopickstation: Distribute autopicking for {} '
'stations on {} cores.'.format(len(input_tuples), ncores_str)) 'stations on {} cores.'.format(len(input_tuples), ncores_str))
pool = gen_Pool(ncores) if ncores == 1:
results = pool.map(call_autopickstation, input_tuples) results = serial_picking(input_tuples)
pool.close() else:
results = parallel_picking(input_tuples, ncores)
for result, wfstream in results: for result, station in results:
if type(result) == dict: if type(result) == dict:
station = result['station']
result.pop('station')
all_onsets[station] = result all_onsets[station] = result
else: else:
if result == None: if result is None:
result = 'Picker exited unexpectedly.' result = 'Picker exited unexpectedly.'
if len(wfstream) > 0:
station = wfstream[0].stats.station
else:
station = None
print('Could not pick a station: {}\nReason: {}'.format(station, result)) print('Could not pick a station: {}\nReason: {}'.format(station, result))
# no Wadati/JK for single station (also valid for tuning mode)
if len(stations) == 1:
return all_onsets
# 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, iplot, fig_dict_wadatijack) jk_checked_onsets = checkPonsets(all_onsets, mdttolerance, jackfactor, iplot, fig_dict_wadatijack)
@ -115,6 +108,20 @@ def autopickevent(data, param, iplot=0, fig_dict=None, fig_dict_wadatijack=None,
return wadationsets return wadationsets
def serial_picking(input_tuples):
result = []
for input_tuple in input_tuples:
result.append(call_autopickstation(input_tuple))
return result
def parallel_picking(input_tuples, ncores):
pool = gen_Pool(ncores)
result = pool.imap_unordered(call_autopickstation, input_tuples)
pool.close()
return result
def call_autopickstation(input_tuple): def call_autopickstation(input_tuple):
""" """
helper function used for multiprocessing helper function used for multiprocessing
@ -123,12 +130,16 @@ def call_autopickstation(input_tuple):
:return: dictionary containing P pick, S pick and station name :return: dictionary containing P pick, S pick and station name
:rtype: dict :rtype: dict
""" """
wfstream, pickparam, verbose, metadata, origin = input_tuple wfstream, pickparam, verbose, iplot, fig_dict, metadata, origin = input_tuple
if fig_dict:
print('Running in interactive mode')
# multiprocessing not possible with interactive plotting # multiprocessing not possible with interactive plotting
try: try:
return autopickstation(wfstream, pickparam, verbose, iplot=0, metadata=metadata, origin=origin), wfstream return autopickstation(wfstream, pickparam, verbose, fig_dict=fig_dict, iplot=iplot, metadata=metadata,
origin=origin)
except Exception as e: except Exception as e:
return e, wfstream tbe = traceback.format_exc()
return tbe, wfstream[0].stats.station
def get_source_coords(parser, station_id): def get_source_coords(parser, station_id):
@ -1312,9 +1323,12 @@ def nautopickstation(wfstream, pickparam, verbose=False,
# split components # split components
zdat, ndat, edat = get_components_from_waveformstream(wfstream) zdat, ndat, edat = get_components_from_waveformstream(wfstream)
picks = {}
station = wfstream[0].stats.station
if not zdat: if not zdat:
print('No z-component found for station {}. STOP'.format(wfstream[0].stats.station)) print('No z-component found for station {}. STOP'.format(station))
return return picks, station
if p_params['algoP'] == 'HOS' or p_params['algoP'] == 'ARZ' and zdat is not None: if p_params['algoP'] == 'HOS' or p_params['algoP'] == 'ARZ' and zdat is not None:
msg = '##################################################\nautopickstation:' \ msg = '##################################################\nautopickstation:' \
@ -1331,13 +1345,10 @@ def nautopickstation(wfstream, pickparam, verbose=False,
Lc = np.inf Lc = np.inf
print('autopickstation: use_taup flag active.') print('autopickstation: use_taup flag active.')
if not metadata: if not metadata:
metadata = [None, None]
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:
station_id = wfstream[0].get_id() station_id = wfstream[0].get_id()
parser = metadata[1] station_coords = metadata.get_coordinates(station_id, time=wfstream[0].stats.starttime)
station_coords = get_source_coords(parser, station_id)
if station_coords and origin: if station_coords and origin:
source_origin = origin[0] source_origin = origin[0]
model = TauPyModel(p_params['taup_model']) model = TauPyModel(p_params['taup_model'])
@ -1377,7 +1388,7 @@ def nautopickstation(wfstream, pickparam, verbose=False,
Lwf = zdat[0].stats.endtime - zdat[0].stats.starttime Lwf = zdat[0].stats.endtime - zdat[0].stats.starttime
if not Lwf > 0: if not Lwf > 0:
print('autopickstation: empty trace! Return!') print('autopickstation: empty trace! Return!')
return return picks, station
Ldiff = Lwf - abs(Lc) Ldiff = Lwf - abs(Lc)
if Ldiff <= 0 or pstop <= pstart or pstop - pstart <= thosmw: if Ldiff <= 0 or pstop <= pstart or pstop - pstart <= thosmw:
@ -1574,7 +1585,7 @@ 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: Refind P-Pick: {} s | P-Error: {} s'.format(zdat[0].stats.starttime \ msg = 'autopickstation: Refined P-Pick: {} s | P-Error: {} s'.format(zdat[0].stats.starttime \
+ mpickP, Perror) + mpickP, Perror)
print(msg) print(msg)
Sflag = 1 Sflag = 1
@ -1617,8 +1628,8 @@ def nautopickstation(wfstream, pickparam, verbose=False,
round(max([mpickP + s_params['sstart'], 0])), # MP MP relative time axis round(max([mpickP + s_params['sstart'], 0])), # MP MP relative time axis
round(min([ round(min([
mpickP + s_params['sstop'], mpickP + s_params['sstop'],
edat[0].stats.endtime-edat[0].stats.starttime, edat[0].stats.endtime - edat[0].stats.starttime,
ndat[0].stats.endtime-ndat[0].stats.starttime ndat[0].stats.endtime - ndat[0].stats.starttime
])) ]))
] ]
@ -1956,15 +1967,14 @@ def nautopickstation(wfstream, pickparam, verbose=False,
ax1.set_ylim([-1.5, 1.5]) ax1.set_ylim([-1.5, 1.5])
ax1.set_ylabel('Normalized Counts') ax1.set_ylabel('Normalized Counts')
# fig.suptitle(tr_filt.stats.starttime) # fig.suptitle(tr_filt.stats.starttime)
try: # only continue if one horizontal stream exists
len(edat[0]) if (ndat or edat) and Sflag == 1:
except: # mirror components in case one does not exist
if not edat:
edat = ndat edat = ndat
try: if not ndat:
len(ndat[0])
except:
ndat = edat ndat = edat
if len(edat[0]) > 1 and len(ndat[0]) > 1 and Sflag == 1: if len(edat[0]) > 1 and len(ndat[0]) > 1:
# plot horizontal traces # plot horizontal traces
ax2 = fig.add_subplot(3, 1, 2, sharex=ax1) ax2 = fig.add_subplot(3, 1, 2, sharex=ax1)
th1data = np.arange(0, th1data = np.arange(0,
@ -2082,12 +2092,22 @@ def nautopickstation(wfstream, pickparam, verbose=False,
epickP = zdat[0].stats.starttime - p_params['timeerrorsP'][3] epickP = zdat[0].stats.starttime - p_params['timeerrorsP'][3]
mpickP = zdat[0].stats.starttime mpickP = zdat[0].stats.starttime
# create dictionary
# for P phase
ccode = zdat[0].stats.channel
ncode = zdat[0].stats.network
ppick = dict(channel=ccode, network=ncode, lpp=lpickP, epp=epickP, mpp=mpickP, spe=Perror, snr=SNRP,
snrdb=SNRPdB, weight=Pweight, fm=FM, w0=None, fc=None, Mo=None,
Mw=None, picker=picker, marked=Pmarker)
if edat: if edat:
hdat = edat[0] hdat = edat[0]
elif ndat: elif ndat:
hdat = ndat[0] hdat = ndat[0]
else: else:
return # no horizontal components given
picks = dict(P=ppick)
return picks, station
if lpickS is not None and lpickS == mpickS: if lpickS is not None and lpickS == mpickS:
lpickS += hdat.stats.delta lpickS += hdat.stats.delta
@ -2104,21 +2124,14 @@ def nautopickstation(wfstream, pickparam, verbose=False,
epickS = hdat.stats.starttime - s_params['timeerrorsS'][3] epickS = hdat.stats.starttime - s_params['timeerrorsS'][3]
mpickS = hdat.stats.starttime mpickS = hdat.stats.starttime
# create dictionary
# for P phase
ccode = zdat[0].stats.channel
ncode = zdat[0].stats.network
ppick = dict(channel=ccode, network=ncode, lpp=lpickP, epp=epickP, mpp=mpickP, spe=Perror, snr=SNRP,
snrdb=SNRPdB, weight=Pweight, fm=FM, w0=None, fc=None, Mo=None,
Mw=None, picker=picker, marked=Pmarker)
# add S phase # add S phase
ccode = hdat.stats.channel ccode = hdat.stats.channel
ncode = hdat.stats.network ncode = hdat.stats.network
spick = dict(channel=ccode, network=ncode, lpp=lpickS, epp=epickS, mpp=mpickS, spe=Serror, snr=SNRS, spick = dict(channel=ccode, network=ncode, lpp=lpickS, epp=epickS, mpp=mpickS, spe=Serror, snr=SNRS,
snrdb=SNRSdB, weight=Sweight, fm=None, picker=picker, Ao=Ao) snrdb=SNRSdB, weight=Sweight, fm=None, picker=picker, Ao=Ao)
# merge picks into returning dictionary # merge picks into returning dictionary
picks = dict(P=ppick, S=spick, station=zdat[0].stats.station) picks = dict(P=ppick, S=spick)
return picks return picks, station
def iteratepicker(wf, NLLocfile, picks, badpicks, pickparameter, fig_dict=None): def iteratepicker(wf, NLLocfile, picks, badpicks, pickparameter, fig_dict=None):
@ -2203,7 +2216,7 @@ def iteratepicker(wf, NLLocfile, picks, badpicks, pickparameter, fig_dict=None):
print("zfac: %f => %f" % (zfac_old, pickparameter.get('zfac'))) print("zfac: %f => %f" % (zfac_old, pickparameter.get('zfac')))
# repick station # repick station
newpicks = autopickstation(wf2pick, pickparameter, fig_dict=fig_dict) newpicks, _ = autopickstation(wf2pick, pickparameter, fig_dict=fig_dict)
# replace old dictionary with new one # replace old dictionary with new one
picks[badpicks[i][0]] = newpicks picks[badpicks[i][0]] = newpicks

View File

@ -506,7 +506,8 @@ class PDFstatistics(object):
return rlist return rlist
def writeThetaToFile(self, array, out_dir): @staticmethod
def writeThetaToFile(array, out_dir):
""" """
Method to write array like data to file. Useful since acquiring can take Method to write array like data to file. Useful since acquiring can take
serious amount of time when dealing with large databases. serious amount of time when dealing with large databases.

View File

@ -191,16 +191,31 @@ class AICPicker(AutoPicker):
# remove offset in AIC function # remove offset in AIC function
offset = abs(min(aic) - min(aicsmooth)) offset = abs(min(aic) - min(aicsmooth))
aicsmooth = aicsmooth - offset aicsmooth = aicsmooth - offset
cf = self.Data[0].data
# get maximum of HOS/AR-CF as startimg point for searching # get maximum of HOS/AR-CF as startimg point for searching
# minimum in AIC function # minimum in AIC function
icfmax = np.argmax(self.Data[0].data) icfmax = np.argmax(cf)
# MP MP testing threshold
thresh_hit = False
thresh_factor = 0.7
thresh = thresh_factor * cf[icfmax]
for index, sample in enumerate(cf):
if sample >= thresh:
thresh_hit = True
# go on searching for the following maximum
if index > 0 and thresh_hit:
if sample <= cf[index - 1]:
icfmax = index - 1
break
# MP MP ---
# find minimum in AIC-CF front of maximum of HOS/AR-CF # find minimum in AIC-CF front of maximum of HOS/AR-CF
lpickwindow = int(round(self.PickWindow / self.dt)) lpickwindow = int(round(self.PickWindow / self.dt))
tsafety = self.TSNR[1] # safety gap, AIC is usually a little bit too late tsafety = self.TSNR[1] # safety gap, AIC is usually a little bit too late
left_corner_ind = max([icfmax - lpickwindow, 2]) left_corner_ind = max([icfmax - lpickwindow, 2])
right_corner_ind = icfmax + int(tsafety / self.dt) right_corner_ind = icfmax + int(tsafety / self.dt)
aic_snip = aicsmooth[left_corner_ind : right_corner_ind] aic_snip = aicsmooth[left_corner_ind: right_corner_ind]
minima = argrelmin(aic_snip)[0] # 0th entry of tuples for axes minima = argrelmin(aic_snip)[0] # 0th entry of tuples for axes
if len(minima) > 0: if len(minima) > 0:
pickindex = minima[-1] + left_corner_ind pickindex = minima[-1] + left_corner_ind
@ -233,14 +248,14 @@ class AICPicker(AutoPicker):
ii = min([isignal[len(isignal) - 1], len(self.Tcf)]) ii = min([isignal[len(isignal) - 1], len(self.Tcf)])
isignal = isignal[0:ii] isignal = isignal[0:ii]
try: try:
self.Data[0].data[isignal] cf[isignal]
except IndexError as e: except IndexError as e:
msg = "Time series out of bounds! {}".format(e) msg = "Time series out of bounds! {}".format(e)
print(msg) print(msg)
return return
# calculate SNR from CF # calculate SNR from CF
self.SNR = max(abs(self.Data[0].data[isignal])) / \ self.SNR = max(abs(cf[isignal])) / \
abs(np.mean(self.Data[0].data[inoise])) abs(np.mean(cf[inoise]))
# calculate slope from CF after initial pick # calculate slope from CF after initial pick
# get slope window # get slope window
tslope = self.TSNR[3] # slope determination window tslope = self.TSNR[3] # slope determination window
@ -249,11 +264,12 @@ class AICPicker(AutoPicker):
& (self.Tcf >= self.Pick)) # TODO: put this in a seperate function like getsignalwin & (self.Tcf >= self.Pick)) # TODO: put this in a seperate function like getsignalwin
else: else:
islope = np.where((self.Tcf <= min([self.Pick + tslope, self.Tcf[-1]])) \ 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 & (
self.Tcf >= self.Pick + tsafety)) # TODO: put this in a seperate function like getsignalwin
# find maximum within slope determination window # find maximum within slope determination window
# 'cause slope should be calculated up to first local minimum only! # 'cause slope should be calculated up to first local minimum only!
try: try:
dataslope = self.Data[0].data[islope[0][0:-1]] dataslope = cf[islope[0][0:-1]]
except IndexError: except IndexError:
print("Slope Calculation: empty array islope, check signal window") print("Slope Calculation: empty array islope, check signal window")
return return
@ -263,7 +279,7 @@ class AICPicker(AutoPicker):
try: try:
imaxs, = argrelmax(dataslope) imaxs, = argrelmax(dataslope)
imax = imaxs[0] imax = imaxs[0]
except ValueError as e: except (ValueError, IndexError) as e:
print(e, 'picker: argrelmax not working!') print(e, 'picker: argrelmax not working!')
imax = np.argmax(dataslope) imax = np.argmax(dataslope)
iislope = islope[0][0:imax + 1] iislope = islope[0][0:imax + 1]
@ -276,14 +292,14 @@ class AICPicker(AutoPicker):
print("AICPicker: Maximum for slope determination right at the beginning of the window!") print("AICPicker: Maximum for slope determination right at the beginning of the window!")
print("Choose longer slope determination window!") print("Choose longer slope determination window!")
if self.iplot > 1: if self.iplot > 1:
if self.fig == None or self.fig == 'None': if self.fig is None or self.fig == 'None':
fig = plt.figure() fig = plt.figure()
plt_flag = iplot plt_flag = iplot
else: else:
fig = self.fig fig = self.fig
ax = fig.add_subplot(111) ax = fig.add_subplot(111)
x = self.Data[0].data cf = cf
ax.plot(self.Tcf, x / max(x), color=self._linecolor, linewidth=0.7, label='(HOS-/AR-) Data') ax.plot(self.Tcf, cf / max(cf), color=self._linecolor, linewidth=0.7, label='(HOS-/AR-) Data')
ax.plot(self.Tcf, aicsmooth / max(aicsmooth), 'r', label='Smoothed AIC-CF') ax.plot(self.Tcf, aicsmooth / max(aicsmooth), 'r', label='Smoothed AIC-CF')
ax.legend(loc=1) ax.legend(loc=1)
ax.set_xlabel('Time [s] since %s' % self.Data[0].stats.starttime) ax.set_xlabel('Time [s] since %s' % self.Data[0].stats.starttime)
@ -291,12 +307,23 @@ class AICPicker(AutoPicker):
ax.set_title(self.Data[0].stats.station) ax.set_title(self.Data[0].stats.station)
if plt_flag in [1, 2]: if plt_flag in [1, 2]:
fig.show() fig.show()
try: input() try:
except SyntaxError: pass input()
except SyntaxError:
pass
plt.close(fig) plt.close(fig)
return return
iislope = islope[0][0:imax+1] iislope = islope[0][0:imax + 1]
dataslope = self.Data[0].data[iislope] # MP MP change slope calculation
# get all maxima of aicsmooth
iaicmaxima = argrelmax(aicsmooth)[0]
# get first index of maximum after pickindex (indices saved in iaicmaxima)
aicmax = iaicmaxima[np.where(iaicmaxima > pickindex)[0]]
if len(aicmax) > 0:
iaicmax = aicmax[0]
else:
iaicmax = -1
dataslope = aicsmooth[pickindex: iaicmax]
# calculate slope as polynomal fit of order 1 # calculate slope as polynomal fit of order 1
xslope = np.arange(0, len(dataslope), 1) xslope = np.arange(0, len(dataslope), 1)
P = np.polyfit(xslope, dataslope, 1) P = np.polyfit(xslope, dataslope, 1)
@ -306,24 +333,23 @@ class AICPicker(AutoPicker):
else: else:
self.slope = 1 / (len(dataslope) * self.Data[0].stats.delta) * (datafit[-1] - datafit[0]) self.slope = 1 / (len(dataslope) * self.Data[0].stats.delta) * (datafit[-1] - datafit[0])
# normalize slope to maximum of cf to make it unit independent # normalize slope to maximum of cf to make it unit independent
self.slope /= self.Data[0].data[icfmax] self.slope /= aicsmooth[iaicmax]
else: else:
self.SNR = None self.SNR = None
self.slope = None self.slope = None
if iplot > 1: if iplot > 1:
if self.fig == None or self.fig == 'None': if self.fig is None or self.fig == 'None':
fig = plt.figure() # self.iplot) fig = plt.figure() # self.iplot)
plt_flag = iplot plt_flag = iplot
else: else:
fig = self.fig fig = self.fig
fig._tight = True fig._tight = True
ax1 = fig.add_subplot(211) ax1 = fig.add_subplot(211)
x = self.Data[0].data if len(self.Tcf) > len(cf): # why? LK
if len(self.Tcf) > len(self.Data[0].data): # why? LK self.Tcf = self.Tcf[0:len(self.Tcf) - 1]
self.Tcf = self.Tcf[0:len(self.Tcf)-1] ax1.plot(self.Tcf, cf / max(cf), color=self._linecolor, linewidth=0.7, label='(HOS-/AR-) Data')
ax1.plot(self.Tcf, x / max(x), color=self._linecolor, linewidth=0.7, label='(HOS-/AR-) Data')
ax1.plot(self.Tcf, aicsmooth / max(aicsmooth), 'r', label='Smoothed AIC-CF') ax1.plot(self.Tcf, aicsmooth / max(aicsmooth), 'r', label='Smoothed AIC-CF')
if self.Pick is not None: if self.Pick is not None:
ax1.plot([self.Pick, self.Pick], [-0.1, 0.5], 'b', linewidth=2, label='AIC-Pick') ax1.plot([self.Pick, self.Pick], [-0.1, 0.5], 'b', linewidth=2, label='AIC-Pick')
@ -333,7 +359,7 @@ class AICPicker(AutoPicker):
if self.Pick is not None: if self.Pick is not None:
ax2 = fig.add_subplot(2, 1, 2, sharex=ax1) ax2 = fig.add_subplot(2, 1, 2, sharex=ax1)
ax2.plot(self.Tcf, x, color=self._linecolor, linewidth=0.7, label='Data') ax2.plot(self.Tcf, aicsmooth, color='r', linewidth=0.7, label='Data')
ax1.axvspan(self.Tcf[inoise[0]], self.Tcf[inoise[-1]], color='y', alpha=0.2, lw=0, label='Noise Window') ax1.axvspan(self.Tcf[inoise[0]], self.Tcf[inoise[-1]], color='y', alpha=0.2, lw=0, label='Noise Window')
ax1.axvspan(self.Tcf[isignal[0]], self.Tcf[isignal[-1]], color='b', alpha=0.2, lw=0, ax1.axvspan(self.Tcf[isignal[0]], self.Tcf[isignal[-1]], color='b', alpha=0.2, lw=0,
label='Signal Window') label='Signal Window')
@ -345,7 +371,8 @@ class AICPicker(AutoPicker):
label='Signal Window') label='Signal Window')
ax2.axvspan(self.Tcf[iislope[0]], self.Tcf[iislope[-1]], color='g', alpha=0.2, lw=0, ax2.axvspan(self.Tcf[iislope[0]], self.Tcf[iislope[-1]], color='g', alpha=0.2, lw=0,
label='Slope Window') label='Slope Window')
ax2.plot(self.Tcf[iislope], datafit, 'g', linewidth=2, label='Slope') ax2.plot(self.Tcf[pickindex: iaicmax], datafit, 'g', linewidth=2,
label='Slope') # MP MP changed temporarily!
if self.slope is not None: 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,
@ -361,15 +388,17 @@ class AICPicker(AutoPicker):
if plt_flag in [1, 2]: if plt_flag in [1, 2]:
fig.show() fig.show()
try: input() try:
except SyntaxError: pass input()
except SyntaxError:
pass
plt.close(fig) plt.close(fig)
if plt_flag == 3: if plt_flag == 3:
stats = self.Data[0].stats stats = self.Data[0].stats
netstlc = '{}.{}.{}'.format(stats.network, stats.station, stats.location) netstlc = '{}.{}.{}'.format(stats.network, stats.station, stats.location)
fig.savefig('aicfig_{}_{}.png'.format(netstlc, stats.channel)) fig.savefig('aicfig_{}_{}.png'.format(netstlc, stats.channel))
if self.Pick == None: if self.Pick is None:
print('AICPicker: Could not find minimum, picking window too short?') print('AICPicker: Could not find minimum, picking window too short?')
return return
@ -427,11 +456,11 @@ class PragPicker(AutoPicker):
# prominent trend: decrease aus # prominent trend: decrease aus
# flat: use given aus # flat: use given aus
cfdiff = np.diff(cfipick) cfdiff = np.diff(cfipick)
if len(cfdiff)<20: if len(cfdiff) < 20:
print('PragPicker: Very few samples for CF. Check LTA window dimensions!') print('PragPicker: Very few samples for CF. Check LTA window dimensions!')
i0diff = np.where(cfdiff > 0) i0diff = np.where(cfdiff > 0)
cfdiff = cfdiff[i0diff] cfdiff = cfdiff[i0diff]
if len(cfdiff)<1: if len(cfdiff) < 1:
print('PragPicker: Negative slope for CF. Check LTA window dimensions! STOP') print('PragPicker: Negative slope for CF. Check LTA window dimensions! STOP')
self.Pick = None self.Pick = None
return return
@ -455,7 +484,7 @@ class PragPicker(AutoPicker):
break break
# now we look to the left # now we look to the left
if len(self.cf) > ipick1 +1: if len(self.cf) > ipick1 + 1:
for i in range(ipick1, max([ipick1 - lpickwindow + 1, 2]), -1): for i in range(ipick1, max([ipick1 - lpickwindow + 1, 2]), -1):
if self.cf[i + 1] > self.cf[i] and self.cf[i - 1] >= self.cf[i]: if self.cf[i + 1] > self.cf[i] and self.cf[i - 1] >= self.cf[i]:
if cfsmooth[i - 1] * (1 + aus1) >= cfsmooth[i]: if cfsmooth[i - 1] * (1 + aus1) >= cfsmooth[i]:
@ -466,7 +495,7 @@ class PragPicker(AutoPicker):
cfpick_l = self.cf[i] cfpick_l = self.cf[i]
break break
else: else:
msg ='PragPicker: Initial onset too close to start of CF! \ msg = 'PragPicker: Initial onset too close to start of CF! \
Stop finalizing pick to the left.' Stop finalizing pick to the left.'
print(msg) print(msg)
@ -486,7 +515,7 @@ class PragPicker(AutoPicker):
pickflag = 0 pickflag = 0
if iplot > 1: if iplot > 1:
if self.fig == None or self.fig == 'None': if self.fig is None or self.fig == 'None':
fig = plt.figure() # self.getiplot()) fig = plt.figure() # self.getiplot())
plt_flag = 1 plt_flag = 1
else: else:
@ -496,15 +525,18 @@ class PragPicker(AutoPicker):
ax.plot(Tcfpick, cfipick, color=self._linecolor, linewidth=0.7, label='CF') ax.plot(Tcfpick, cfipick, color=self._linecolor, linewidth=0.7, label='CF')
ax.plot(Tcfpick, cfsmoothipick, 'r', label='Smoothed CF') ax.plot(Tcfpick, cfsmoothipick, 'r', label='Smoothed CF')
if pickflag > 0: if pickflag > 0:
ax.plot([self.Pick, self.Pick], [min(cfipick), max(cfipick)], self._pickcolor_p, linewidth=2, label='Pick') ax.plot([self.Pick, self.Pick], [min(cfipick), max(cfipick)], self._pickcolor_p, linewidth=2,
label='Pick')
ax.set_xlabel('Time [s] since %s' % self.Data[0].stats.starttime) ax.set_xlabel('Time [s] since %s' % self.Data[0].stats.starttime)
ax.set_yticks([]) ax.set_yticks([])
ax.set_title(self.Data[0].stats.station) ax.set_title(self.Data[0].stats.station)
ax.legend(loc=1) ax.legend(loc=1)
if plt_flag == 1: if plt_flag == 1:
fig.show() fig.show()
try: input() try:
except SyntaxError: pass input()
except SyntaxError:
pass
plt.close(fig) plt.close(fig)
return return

View File

@ -14,8 +14,7 @@ import matplotlib.pyplot as plt
import numpy as np import numpy as np
from scipy.signal import argrelmax from scipy.signal import argrelmax
from obspy.core import Stream, UTCDateTime from obspy.core import Stream, UTCDateTime
from pylot.core.util.utils import real_Bool, real_None from pylot.core.util.utils import real_Bool, real_None, SetChannelComponents
def earllatepicker(X, nfac, TSNR, Pick1, iplot=0, verbosity=1, fig=None, linecolor='k'): def earllatepicker(X, nfac, TSNR, Pick1, iplot=0, verbosity=1, fig=None, linecolor='k'):
@ -144,13 +143,16 @@ def earllatepicker(X, nfac, TSNR, Pick1, iplot=0, verbosity=1, fig=None, linecol
ax.plot(t, x, color=linecolor, linewidth=0.7, label='Data') ax.plot(t, x, color=linecolor, linewidth=0.7, label='Data')
ax.axvspan(t[inoise[0]], t[inoise[-1]], color='y', alpha=0.2, lw=0, label='Noise Window') ax.axvspan(t[inoise[0]], t[inoise[-1]], color='y', alpha=0.2, lw=0, label='Noise Window')
ax.axvspan(t[isignal[0]], t[isignal[-1]], color='b', alpha=0.2, lw=0, label='Signal Window') ax.axvspan(t[isignal[0]], t[isignal[-1]], color='b', alpha=0.2, lw=0, label='Signal Window')
ax.plot([t[0], t[int(len(t)) - 1]], [nlevel, nlevel], color=linecolor, linewidth=0.7, linestyle='dashed', label='Noise Level') ax.plot([t[0], t[int(len(t)) - 1]], [nlevel, nlevel], color=linecolor, linewidth=0.7, linestyle='dashed',
label='Noise Level')
ax.plot(t[pis[zc]], np.zeros(len(zc)), '*g', ax.plot(t[pis[zc]], np.zeros(len(zc)), '*g',
markersize=14, label='Zero Crossings') markersize=14, label='Zero Crossings')
ax.plot([t[0], t[int(len(t)) - 1]], [-nlevel, -nlevel], color=linecolor, linewidth=0.7, linestyle='dashed') ax.plot([t[0], t[int(len(t)) - 1]], [-nlevel, -nlevel], color=linecolor, linewidth=0.7, linestyle='dashed')
ax.plot([Pick1, Pick1], [max(x), -max(x)], 'b', linewidth=2, label='mpp') ax.plot([Pick1, Pick1], [max(x), -max(x)], 'b', linewidth=2, label='mpp')
ax.plot([LPick, LPick], [max(x) / 2, -max(x) / 2], color=linecolor, linewidth=0.7, linestyle='dashed', label='lpp') ax.plot([LPick, LPick], [max(x) / 2, -max(x) / 2], color=linecolor, linewidth=0.7, linestyle='dashed',
ax.plot([EPick, EPick], [max(x) / 2, -max(x) / 2], color=linecolor, linewidth=0.7, linestyle='dashed', label='epp') label='lpp')
ax.plot([EPick, EPick], [max(x) / 2, -max(x) / 2], color=linecolor, linewidth=0.7, linestyle='dashed',
label='epp')
ax.plot([Pick1 + PickError, Pick1 + PickError], ax.plot([Pick1 + PickError, Pick1 + PickError],
[max(x) / 2, -max(x) / 2], 'r--', label='spe') [max(x) / 2, -max(x) / 2], 'r--', label='spe')
ax.plot([Pick1 - PickError, Pick1 - PickError], ax.plot([Pick1 - PickError, Pick1 - PickError],
@ -163,8 +165,10 @@ def earllatepicker(X, nfac, TSNR, Pick1, iplot=0, verbosity=1, fig=None, linecol
ax.legend(loc=1) ax.legend(loc=1)
if plt_flag == 1: if plt_flag == 1:
fig.show() fig.show()
try: input() try:
except SyntaxError: pass input()
except SyntaxError:
pass
plt.close(fig) plt.close(fig)
return EPick, LPick, PickError return EPick, LPick, PickError
@ -227,8 +231,7 @@ def fmpicker(Xraw, Xfilt, pickwin, Pick, iplot=0, fig=None, linecolor='k'):
# get zero crossings after most likely pick # get zero crossings after most likely pick
# initial onset is assumed to be the first zero crossing # initial onset is assumed to be the first zero crossing
# first from unfiltered trace # first from unfiltered trace
zc1 = [] zc1 = [Pick]
zc1.append(Pick)
index1 = [] index1 = []
i = 0 i = 0
for j in range(ipick[0][1], ipick[0][len(t[ipick]) - 1]): for j in range(ipick[0][1], ipick[0][len(t[ipick]) - 1]):
@ -273,8 +276,7 @@ def fmpicker(Xraw, Xfilt, pickwin, Pick, iplot=0, fig=None, linecolor='k'):
# now using filterd trace # now using filterd trace
# next zero crossings after most likely pick # next zero crossings after most likely pick
zc2 = [] zc2 = [Pick]
zc2.append(Pick)
index2 = [] index2 = []
i = 0 i = 0
for j in range(ipick[0][1], ipick[0][len(t[ipick]) - 1]): for j in range(ipick[0][1], ipick[0][len(t[ipick]) - 1]):
@ -362,8 +364,10 @@ def fmpicker(Xraw, Xfilt, pickwin, Pick, iplot=0, fig=None, linecolor='k'):
ax2.set_yticks([]) ax2.set_yticks([])
if plt_flag == 1: if plt_flag == 1:
fig.show() fig.show()
try: input() try:
except SyntaxError: pass input()
except SyntaxError:
pass
plt.close(fig) plt.close(fig)
return FM return FM
@ -573,8 +577,6 @@ def select_for_phase(st, phase):
:rtype: `~obspy.core.stream.Stream` :rtype: `~obspy.core.stream.Stream`
""" """
from pylot.core.util.defaults import SetChannelComponents
sel_st = Stream() sel_st = Stream()
compclass = SetChannelComponents() compclass = SetChannelComponents()
if phase.upper() == 'P': if phase.upper() == 'P':
@ -623,14 +625,18 @@ def wadaticheck(pickdic, dttolerance, iplot=0, fig_dict=None):
ibad = 0 ibad = 0
for key in list(pickdic.keys()): for key in list(pickdic.keys()):
if pickdic[key]['P']['weight'] < 4 and pickdic[key]['S']['weight'] < 4: ppick = pickdic[key].get('P')
spick = pickdic[key].get('S')
if not ppick or not spick:
continue
if ppick['weight'] < 4 and spick['weight'] < 4:
# calculate S-P time # calculate S-P time
spt = pickdic[key]['S']['mpp'] - pickdic[key]['P']['mpp'] spt = spick['mpp'] - ppick['mpp']
# add S-P time to dictionary # add S-P time to dictionary
pickdic[key]['SPt'] = spt pickdic[key]['SPt'] = spt
# add P onsets and corresponding S-P times to list # add P onsets and corresponding S-P times to list
UTCPpick = UTCDateTime(pickdic[key]['P']['mpp']) UTCPpick = UTCDateTime(ppick['mpp'])
UTCSpick = UTCDateTime(pickdic[key]['S']['mpp']) UTCSpick = UTCDateTime(spick['mpp'])
Ppicks.append(UTCPpick.timestamp) Ppicks.append(UTCPpick.timestamp)
Spicks.append(UTCSpick.timestamp) Spicks.append(UTCSpick.timestamp)
SPtimes.append(spt) SPtimes.append(spt)
@ -660,11 +666,11 @@ def wadaticheck(pickdic, dttolerance, iplot=0, fig_dict=None):
# check, if deviation is larger than adjusted # check, if deviation is larger than adjusted
if wddiff > dttolerance: if wddiff > dttolerance:
# remove pick from dictionary # remove pick from dictionary
pickdic.pop(key) # # mark onset and downgrade S-weight to 9, also set SPE to None (disregarded in GUI)
# # mark onset and downgrade S-weight to 9
# # (not used anymore) # # (not used anymore)
# marker = 'badWadatiCheck' marker = 'badWadatiCheck'
# pickdic[key]['S']['weight'] = 9 pickdic[key]['S']['weight'] = 9
pickdic[key]['S']['spe'] = None
badstations.append(key) badstations.append(key)
ibad += 1 ibad += 1
else: else:
@ -677,7 +683,6 @@ def wadaticheck(pickdic, dttolerance, iplot=0, fig_dict=None):
checkedSPtimes.append(checkedSPtime) checkedSPtimes.append(checkedSPtime)
pickdic[key]['S']['marked'] = marker pickdic[key]['S']['marked'] = marker
#pickdic[key]['S']['marked'] = marker
print("wadaticheck: the following stations failed the check:") print("wadaticheck: the following stations failed the check:")
print(badstations) print(badstations)
@ -719,8 +724,8 @@ def wadaticheck(pickdic, dttolerance, iplot=0, fig_dict=None):
ax.plot(Ppicks, SPtimes, 'ro', label='Skipped S-Picks') ax.plot(Ppicks, SPtimes, 'ro', label='Skipped S-Picks')
if wfitflag == 0: if wfitflag == 0:
ax.plot(Ppicks, wdfit, color=linecolor, linewidth=0.7, label='Wadati 1') ax.plot(Ppicks, wdfit, color=linecolor, linewidth=0.7, label='Wadati 1')
ax.plot(Ppicks, wdfit+dttolerance, color='0.9', linewidth=0.5, label='Wadati 1 Tolerance') ax.plot(Ppicks, wdfit + dttolerance, color='0.9', linewidth=0.5, label='Wadati 1 Tolerance')
ax.plot(Ppicks, wdfit-dttolerance, color='0.9', linewidth=0.5) ax.plot(Ppicks, wdfit - dttolerance, color='0.9', linewidth=0.5)
ax.plot(checkedPpicks, wdfit2, 'g', label='Wadati 2') ax.plot(checkedPpicks, wdfit2, 'g', label='Wadati 2')
ax.plot(checkedPpicks, checkedSPtimes, color=linecolor, ax.plot(checkedPpicks, checkedSPtimes, color=linecolor,
linewidth=0, marker='o', label='Reliable S-Picks') linewidth=0, marker='o', label='Reliable S-Picks')
@ -851,8 +856,10 @@ def checksignallength(X, pick, TSNR, minsiglength, nfac, minpercent, iplot=0, fi
ax.set_yticks([]) ax.set_yticks([])
if plt_flag == 1: if plt_flag == 1:
fig.show() fig.show()
try: input() try:
except SyntaxError: pass input()
except SyntaxError:
pass
plt.close(fig) plt.close(fig)
return returnflag return returnflag
@ -886,9 +893,12 @@ def checkPonsets(pickdic, dttolerance, jackfactor=5, iplot=0, fig_dict=None):
Ppicks = [] Ppicks = []
stations = [] stations = []
for station in pickdic: for station in pickdic:
if pickdic[station]['P']['weight'] < 4: pick = pickdic[station].get('P')
if not pick:
continue
if pick['weight'] < 4:
# add P onsets to list # add P onsets to list
UTCPpick = UTCDateTime(pickdic[station]['P']['mpp']) UTCPpick = UTCDateTime(pick['mpp'])
Ppicks.append(UTCPpick.timestamp) Ppicks.append(UTCPpick.timestamp)
stations.append(station) stations.append(station)
@ -1087,7 +1097,6 @@ def checkZ4S(X, pick, zfac, checkwin, iplot, fig=None, linecolor='k'):
else: else:
iplot = 0 iplot = 0
assert isinstance(X, Stream), "%s is not a stream object" % str(X) assert isinstance(X, Stream), "%s is not a stream object" % str(X)
print("Check for spuriously picked S onset instead of P onset ...") print("Check for spuriously picked S onset instead of P onset ...")
@ -1188,12 +1197,81 @@ def checkZ4S(X, pick, zfac, checkwin, iplot, fig=None, linecolor='k'):
ax.set_xlabel('Time [s] since %s' % zdat[0].stats.starttime) ax.set_xlabel('Time [s] since %s' % zdat[0].stats.starttime)
if plt_flag == 1: if plt_flag == 1:
fig.show() fig.show()
try: input() try:
except SyntaxError: pass input()
except SyntaxError:
pass
plt.close(fig) plt.close(fig)
return returnflag return returnflag
def getPickQuality(wfdata, picks, inputs, phase, compclass=None):
quality = 4
components4phases = {'P': ['Z'],
'S': ['N', 'E']}
timeErrors4phases = {'P': 'timeerrorsP',
'S': 'timeerrorsS'}
tsnr4phases = {'P': 'tsnrz',
'S': 'tsnrh'}
if not phase in components4phases.keys():
raise IOError('getPickQuality: Could not understand phase: {}'.format(phase))
if not compclass:
print('Warning: No settings for channel components found. Using default')
compclass = SetChannelComponents()
picks = picks[phase]
mpp = picks.get('mpp')
uncertainty = picks.get('spe')
if not mpp:
print('getPickQuality: No pick found!')
return quality
if not uncertainty:
print('getPickQuality: No pick uncertainty (spe) found!')
return quality
tsnr = inputs[tsnr4phases[phase]]
timeErrors = inputs[timeErrors4phases[phase]]
snrdb_final = 0
for component in components4phases[phase]:
alter_comp = compclass.getCompPosition(component)
st_select = wfdata.select(component=component)
st_select += wfdata.select(component=alter_comp)
if st_select:
trace = st_select[0]
_, snrdb, _ = getSNR(st_select, tsnr,
mpp - trace.stats.starttime)
if snrdb > snrdb_final:
snrdb_final = snrdb
quality = getQualityFromUncertainty(uncertainty, timeErrors)
quality += getQualityFromSNR(snrdb_final)
return quality
def getQualityFromSNR(snrdb):
quality_modifier = 4
if not snrdb:
print('getQualityFromSNR: No snrdb!')
return quality_modifier
# MP MP ++++ experimental,
# raise pick quality by x classes if snrdb is lower than corresponding key
quality4snrdb = {3: 4,
5: 3,
7: 2,
9: 1,
11: 0}
# MP MP ---
# iterate over all thresholds and check whether snrdb is larger, if so, set new quality_modifier
for snrdb_threshold in sorted(list(quality4snrdb.keys())):
if snrdb > snrdb_threshold:
quality_modifier = quality4snrdb[snrdb_threshold]
return quality_modifier
def get_quality_class(uncertainty, weight_classes): def get_quality_class(uncertainty, weight_classes):
""" """
Script to transform uncertainty into quality classes 0-4 regarding adjusted time errors Script to transform uncertainty into quality classes 0-4 regarding adjusted time errors
@ -1378,6 +1456,26 @@ def get_pickparams(pickparam):
return p_params, s_params, first_motion_params, signal_length_params return p_params, s_params, first_motion_params, signal_length_params
def getQualityFromUncertainty(uncertainty, Errors):
# set initial quality to 4 (worst) and change only if one condition is hit
quality = 4
if real_None(uncertainty) is None:
return quality
if uncertainty <= Errors[0]:
quality = 0
elif (uncertainty > Errors[0]) and \
(uncertainty <= Errors[1]):
quality = 1
elif (uncertainty > Errors[1]) and \
(uncertainty <= Errors[2]):
quality = 2
elif (uncertainty > Errors[2]) and \
(uncertainty <= Errors[3]):
quality = 3
elif uncertainty > Errors[3]:
quality = 4
if __name__ == '__main__': if __name__ == '__main__':
import doctest import doctest

View File

@ -23,8 +23,7 @@ class Array_map(QtGui.QWidget):
''' '''
QtGui.QWidget.__init__(self) QtGui.QWidget.__init__(self)
self._parent = parent self._parent = parent
self.metadata_type = parent.metadata[0] self.metadata = parent.metadata
self.metadata = parent.metadata[1]
self.picks = None self.picks = None
self.picks_dict = None self.picks_dict = None
self.autopicks_dict = None self.autopicks_dict = None
@ -74,13 +73,9 @@ class Array_map(QtGui.QWidget):
if pickDlg.exec_(): if pickDlg.exec_():
pyl_mw.setDirty(True) pyl_mw.setDirty(True)
pyl_mw.update_status('picks accepted ({0})'.format(station)) pyl_mw.update_status('picks accepted ({0})'.format(station))
replot = pyl_mw.get_current_event().setPick(station, pickDlg.getPicks()) pyl_mw.addPicks(station, pickDlg.getPicks(picktype='manual'), type='manual')
pyl_mw.addPicks(station, pickDlg.getPicks(picktype='auto'), type='auto')
self._refresh_drawings() self._refresh_drawings()
if replot:
pyl_mw.plotWaveformData()
pyl_mw.drawPicks()
pyl_mw.draw()
else:
pyl_mw.drawPicks(station) pyl_mw.drawPicks(station)
pyl_mw.draw() pyl_mw.draw()
else: else:
@ -94,7 +89,7 @@ class Array_map(QtGui.QWidget):
self.comboBox_phase.currentIndexChanged.connect(self._refresh_drawings) self.comboBox_phase.currentIndexChanged.connect(self._refresh_drawings)
self.comboBox_am.currentIndexChanged.connect(self._refresh_drawings) self.comboBox_am.currentIndexChanged.connect(self._refresh_drawings)
self.canvas.mpl_connect('motion_notify_event', self.mouse_moved) self.canvas.mpl_connect('motion_notify_event', self.mouse_moved)
#self.zoom_id = self.basemap.ax.figure.canvas.mpl_connect('scroll_event', self.zoom) # self.zoom_id = self.basemap.ax.figure.canvas.mpl_connect('scroll_event', self.zoom)
def _from_dict(self, function, key): def _from_dict(self, function, key):
return function(self.stations_dict.values(), key=lambda x: x[key])[key] return function(self.stations_dict.values(), key=lambda x: x[key])[key]
@ -159,35 +154,8 @@ class Array_map(QtGui.QWidget):
self.main_box.addWidget(self.canvas, 1) self.main_box.addWidget(self.canvas, 1)
self.main_box.addWidget(self.status_label, 0) self.main_box.addWidget(self.status_label, 0)
def init_stations(self): def init_stations(self):
def stat_info_from_parser(parser): self.stations_dict = self.metadata.get_all_coordinates()
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.latmin = self.get_min_from_stations('latitude')
self.lonmin = self.get_min_from_stations('longitude') self.lonmin = self.get_min_from_stations('longitude')
self.latmax = self.get_max_from_stations('latitude') self.latmax = self.get_max_from_stations('latitude')
@ -196,13 +164,15 @@ class Array_map(QtGui.QWidget):
def init_picks(self): def init_picks(self):
def get_picks(station_dict): def get_picks(station_dict):
picks = {} picks = {}
# selected phase
phase = self.comboBox_phase.currentText() phase = self.comboBox_phase.currentText()
for st_id in station_dict.keys(): for st_id in station_dict.keys():
try: try:
station_name = st_id.split('.')[-1] station_name = st_id.split('.')[-1]
# current_picks_dict: auto or manual
pick = self.current_picks_dict()[station_name][phase] pick = self.current_picks_dict()[station_name][phase]
if pick['picker'] == 'auto': if pick['picker'] == 'auto':
if pick['weight'] > 3: if not pick['spe']:
continue continue
picks[st_id] = pick['mpp'] picks[st_id] = pick['mpp']
except KeyError: except KeyError:
@ -217,6 +187,7 @@ class Array_map(QtGui.QWidget):
for pick in picks.values(): for pick in picks.values():
if type(pick) is obspy.core.utcdatetime.UTCDateTime: if type(pick) is obspy.core.utcdatetime.UTCDateTime:
picks_utc.append(pick) picks_utc.append(pick)
if picks_utc:
self._earliest_picktime = min(picks_utc) self._earliest_picktime = min(picks_utc)
for st_id, pick in picks.items(): for st_id, pick in picks.items():
if type(pick) is obspy.core.utcdatetime.UTCDateTime: if type(pick) is obspy.core.utcdatetime.UTCDateTime:
@ -331,6 +302,8 @@ class Array_map(QtGui.QWidget):
def scatter_picked_stations(self): def scatter_picked_stations(self):
picks, lats, lons = self.get_picks_lat_lon() picks, lats, lons = self.get_picks_lat_lon()
if len(lons) < 1 and len(lats) < 1:
return
# workaround because of an issue with latlon transformation of arrays with len <3 # workaround because of an issue with latlon transformation of arrays with len <3
if len(lons) <= 2 and len(lats) <= 2: if len(lons) <= 2 and len(lats) <= 2:
self.sc_picked = self.basemap.scatter(lons[0], lats[0], s=50, facecolor='white', self.sc_picked = self.basemap.scatter(lons[0], lats[0], s=50, facecolor='white',
@ -354,7 +327,7 @@ class Array_map(QtGui.QWidget):
def add_cbar(self, label): def add_cbar(self, label):
self.cbax_bg = inset_axes(self.main_ax, width="6%", height="75%", loc=5) 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) cbax = inset_axes(self.main_ax, width='2%', height='70%', loc=5)
cbar = self.main_ax.figure.colorbar(self.sc_picked, cax = cbax) cbar = self.main_ax.figure.colorbar(self.sc_picked, cax=cbax)
cbar.set_label(label) cbar.set_label(label)
cbax.yaxis.tick_left() cbax.yaxis.tick_left()
cbax.yaxis.set_label_position('left') cbax.yaxis.set_label_position('left')
@ -375,13 +348,16 @@ class Array_map(QtGui.QWidget):
self.draw_everything() self.draw_everything()
def draw_everything(self): def draw_everything(self):
if self.picks_dict or self.autopicks_dict: picktype = self.comboBox_am.currentText()
if (self.picks_dict and picktype == 'manual') \
or (self.autopicks_dict and picktype == 'auto'):
self.init_picks() self.init_picks()
if len(self.picks) >= 3: if len(self.picks) >= 3:
self.init_picksgrid() self.init_picksgrid()
self.draw_contour_filled() self.draw_contour_filled()
self.scatter_all_stations() self.scatter_all_stations()
if self.picks_dict or self.autopicks_dict: if (self.picks_dict and picktype == 'manual') \
or (self.autopicks_dict and picktype == 'auto'):
self.scatter_picked_stations() self.scatter_picked_stations()
self.cbar = self.add_cbar(label='Time relative to first onset ({}) [s]'.format(self._earliest_picktime)) self.cbar = self.add_cbar(label='Time relative to first onset ({}) [s]'.format(self._earliest_picktime))
self.comboBox_phase.setEnabled(True) self.comboBox_phase.setEnabled(True)
@ -397,16 +373,16 @@ class Array_map(QtGui.QWidget):
del (self.cbar, self.cbax_bg) del (self.cbar, self.cbax_bg)
if hasattr(self, 'sc_picked'): if hasattr(self, 'sc_picked'):
self.sc_picked.remove() self.sc_picked.remove()
del (self.sc_picked) del self.sc_picked
if hasattr(self, 'sc_event'): if hasattr(self, 'sc_event'):
self.sc_event.remove() self.sc_event.remove()
del (self.sc_event) del self.sc_event
if hasattr(self, 'contourf'): if hasattr(self, 'contourf'):
self.remove_contourf() self.remove_contourf()
del (self.contourf) del self.contourf
if hasattr(self, 'cid'): if hasattr(self, 'cid'):
self.canvas.mpl_disconnect(self.cid) self.canvas.mpl_disconnect(self.cid)
del (self.cid) del self.cid
try: try:
self.sc.remove() self.sc.remove()
except Exception as e: except Exception as e:

View File

@ -13,86 +13,95 @@ from pylot.core.util.utils import key_for_set_value, find_in_list, \
class Metadata(object): class Metadata(object):
def __init__(self, inventory=None): def __init__(self, inventory=None):
self.inventories = [] self.inventories = []
# saves read metadata objects (Parser/inventory) for a filename # saves read metadata objects (Parser/inventory) for a filename
self.inventory_files = {} self.inventory_files = {}
# saves filenames holding metadata for a seed_id # saves filenames holding metadata for a seed_id
# seed id as key, path to file as value
self.seed_ids = {} self.seed_ids = {}
self.stations_dict = {}
if inventory: if inventory:
if os.path.isdir(inventory): if os.path.isdir(inventory):
self.add_inventory(inventory) self.add_inventory(inventory)
if os.path.isfile(inventory): if os.path.isfile(inventory):
self.add_inventory_file(inventory) self.add_inventory_file(inventory)
def __str__(self): def __str__(self):
repr = 'PyLoT Metadata object including the following inventories:\n\n' repr = 'PyLoT Metadata object including the following inventories:\n\n'
ntotal = len(self.inventories) ntotal = len(self.inventories)
for index, inventory in enumerate(self.inventories): for index, inventory in enumerate(self.inventories):
if index < 2 or (ntotal - index) < 3: if index < 2 or (ntotal - index) < 3:
repr += '{}\n'.format(inventory) repr += '{}\n'.format(inventory)
if ntotal > 4 and int(ntotal/2) == index: if ntotal > 4 and int(ntotal / 2) == index:
repr += '...\n' repr += '...\n'
if ntotal > 4: if ntotal > 4:
repr += '\nTotal of {} inventories. Use Metadata.inventories to see all.'.format(ntotal) repr += '\nTotal of {} inventories. Use Metadata.inventories to see all.'.format(ntotal)
return repr return repr
def __repr__(self): def __repr__(self):
return self.__str__() return self.__str__()
def add_inventory(self, path_to_inventory): def add_inventory(self, path_to_inventory):
''' """
add paths to list of inventories Add path to list of inventories.
:param path_to_inventory: Path to a folder
:param path_to_inventory: :type path_to_inventory: str
:return: :return: None
''' """
assert (os.path.isdir(path_to_inventory)), '{} is no directory'.format(path_to_inventory) assert (os.path.isdir(path_to_inventory)), '{} is no directory'.format(path_to_inventory)
if not path_to_inventory in self.inventories: if not path_to_inventory in self.inventories:
self.inventories.append(path_to_inventory) self.inventories.append(path_to_inventory)
def add_inventory_file(self, path_to_inventory_file): def add_inventory_file(self, path_to_inventory_file):
''' """
add a single file to inventory files Add the folder in which the file exists to the list of inventories.
:param path_to_inventory_file: full path including filename
:param path_to_inventory_file: :type path_to_inventory_file: str
:return: :return: None
"""
'''
assert (os.path.isfile(path_to_inventory_file)), '{} is no file'.format(path_to_inventory_file) assert (os.path.isfile(path_to_inventory_file)), '{} is no file'.format(path_to_inventory_file)
self.add_inventory(os.path.split(path_to_inventory_file)[0]) self.add_inventory(os.path.split(path_to_inventory_file)[0])
if not path_to_inventory_file in self.inventory_files.keys(): if not path_to_inventory_file in self.inventory_files.keys():
self.read_single_file(path_to_inventory_file) self.read_single_file(path_to_inventory_file)
def remove_all_inventories(self): def remove_all_inventories(self):
self.__init__() self.__init__()
def remove_inventory(self, path_to_inventory): def remove_inventory(self, path_to_inventory):
''' """
remove a path from inventories list Remove a path from inventories list. If path is not in inventories list, do nothing.
:param path_to_inventory: Path to a folder
:param path_to_inventory: """
:return:
'''
if not path_to_inventory in self.inventories: if not path_to_inventory in self.inventories:
print('Path {} not in inventories list.'.format(path_to_inventory)) print('Path {} not in inventories list.'.format(path_to_inventory))
return return
self.inventories.remove(path_to_inventory) self.inventories.remove(path_to_inventory)
for filename in self.inventory_files.keys(): for filename in self.inventory_files.keys():
if filename.startswith(path_to_inventory): if filename.startswith(path_to_inventory):
del(self.inventory_files[filename]) del (self.inventory_files[filename])
for seed_id in self.seed_ids.keys(): for seed_id in self.seed_ids.keys():
if self.seed_ids[seed_id].startswith(path_to_inventory): if self.seed_ids[seed_id].startswith(path_to_inventory):
del(self.seed_ids[seed_id]) del (self.seed_ids[seed_id])
def get_metadata(self, seed_id, time=None):
def get_metadata(self, seed_id): """
Get metadata for seed id at time. When time is not specified, metadata for current time is fetched.
:param seed_id: Seed id such as BW.WETR..HHZ (Network.Station.Location.Channel)
:type seed_id: str
:param time: Time for which the metadata should be returned
:type time: UTCDateTime
:return: Dictionary with keys data and invtype.
data is a obspy.io.xseed.parser.Parser or an obspy.core.inventory.inventory.Inventory depending on the metadata
file.
invtype is a string denoting of which type the value of the data key is. It can take the values 'dless',
'dseed', 'xml', 'resp', according to the filetype of the metadata.
:rtype: dict
"""
# try most recent data if no time is specified
if not time:
time = UTCDateTime()
# get metadata for a specific seed_id, if not already read, try to read from inventories # 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(): if not seed_id in self.seed_ids.keys():
self._read_inventory_data(seed_id) self._read_inventory_data(seed_id)
@ -100,12 +109,13 @@ class Metadata(object):
if not seed_id in self.seed_ids.keys(): 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)) print('No data found for seed id {}. Trying to find it in all known inventories...'.format(seed_id))
self.read_all() self.read_all()
for inv_fname, metadata in self.inventory_files.items(): for inv_fname, metadata_dict in self.inventory_files.items():
# use get_coordinates to check for seed_id # use get_coordinates to check for seed_id
try: try:
metadata['data'].get_coordinates(seed_id) metadata_dict['data'].get_coordinates(seed_id, time)
self.seed_ids[seed_id] = inv_fname self.seed_ids[seed_id] = inv_fname
return metadata print('Found metadata for station {}!'.format(seed_id))
return metadata_dict
except Exception as e: except Exception as e:
continue continue
print('Could not find metadata for station {}'.format(seed_id)) print('Could not find metadata for station {}'.format(seed_id))
@ -113,30 +123,30 @@ class Metadata(object):
fname = self.seed_ids[seed_id] fname = self.seed_ids[seed_id]
return self.inventory_files[fname] return self.inventory_files[fname]
def read_all(self): def read_all(self):
''' """
read all metadata files found in all inventories Read all metadata files found in all inventories
:return: """
'''
for inventory in self.inventories: for inventory in self.inventories:
for inv_fname in os.listdir(inventory): for inv_fname in os.listdir(inventory):
inv_fname = os.path.join(inventory, inv_fname) inv_fname = os.path.join(inventory, inv_fname)
if not self.read_single_file(inv_fname): if not self.read_single_file(inv_fname):
continue continue
def read_single_file(self, inv_fname): def read_single_file(self, inv_fname):
if not inv_fname in self.inventory_files.keys(): """
pass Try to read a single file as Parser/Inventory and add its dictionary to inventory files if reading sudceeded.
else: :param inv_fname: path/filename of inventory file
if not self.inventory_files[inv_fname]: :type inv_fname: str
pass :rtype: None
else: """
# return if it was read already
if self.inventory_files.get(inv_fname, None):
return return
try: try:
invtype, robj = self._read_metadata_file(inv_fname) invtype, robj = self._read_metadata_file(inv_fname)
if robj == None: if robj is None:
return return
except Exception as e: except Exception as e:
print('Could not read file {}'.format(inv_fname)) print('Could not read file {}'.format(inv_fname))
@ -145,15 +155,64 @@ class Metadata(object):
'data': robj} 'data': robj}
return True return True
def get_coordinates(self, seed_id, time=None):
def get_coordinates(self, seed_id): """
metadata = self.get_metadata(seed_id) Get coordinates of given seed id.
:param seed_id: Seed id such as BW.WETR..HHZ (Network.Station.Location.Channel)
:type seed_id: str
:param time: Used when a station has data available at multiple time intervals
:type time: UTCDateTime
:return: dict containing position information of the station
:rtype: dict
"""
# try most recent data if no time is specified
if not time:
time = UTCDateTime()
metadata = self.get_metadata(seed_id, time)
if not metadata: if not metadata:
return return
return metadata['data'].get_coordinates(seed_id) return metadata['data'].get_coordinates(seed_id, time)
def get_all_coordinates(self):
def stat_info_from_parser(parser):
for station in parser.stations:
station_name = station[0].station_call_letters
network_name = station[0].network_code
if not station_name in self.stations_dict.keys():
st_id = network_name + '.' + station_name
self.stations_dict[st_id] = {'latitude': station[0].latitude,
'longitude': station[0].longitude}
def stat_info_from_inventory(inventory):
for network in inventory.networks:
for station in network.stations:
station_name = station.code
network_name = network_name.code
if not station_name in self.stations_dict.keys():
st_id = network_name + '.' + station_name
self.stations_dict[st_id] = {'latitude': station[0].latitude,
'longitude': station[0].longitude}
read_stat = {'xml': stat_info_from_inventory,
'dless': stat_info_from_parser}
self.read_all()
for item in self.inventory_files.values():
inventory = item['data']
invtype = item['invtype']
read_stat[invtype](inventory)
return self.stations_dict
def get_paz(self, seed_id, time): def get_paz(self, seed_id, time):
"""
:param seed_id: Seed id such as BW.WETR..HHZ (Network.Station.Location.Channel)
:type seed_id: str
:param time: Used when a station has data available at multiple time intervals
:type time: UTCDateTime
:rtype: dict
"""
metadata = self.get_metadata(seed_id) metadata = self.get_metadata(seed_id)
if not metadata: if not metadata:
return return
@ -163,18 +222,17 @@ class Metadata(object):
resp = metadata['data'].get_response(seed_id, time) resp = metadata['data'].get_response(seed_id, time)
return resp.get_paz(seed_id) return resp.get_paz(seed_id)
def _read_inventory_data(self, seed_id=None): def _read_inventory_data(self, seed_id=None):
for inventory in self.inventories: for inventory in self.inventories:
if self._read_metadata_iterator(path_to_inventory=inventory, station_seed_id=seed_id): if self._read_metadata_iterator(path_to_inventory=inventory, station_seed_id=seed_id):
return return
def _read_metadata_iterator(self, path_to_inventory, station_seed_id): def _read_metadata_iterator(self, path_to_inventory, station_seed_id):
''' """
search for metadata for a specific station iteratively Search for metadata for a specific station iteratively.
''' """
station, network, location, channel = station_seed_id.split('.') station, network, location, channel = station_seed_id.split('.')
# seach for station seed id in filenames in invetory
fnames = glob.glob(os.path.join(path_to_inventory, '*' + station_seed_id + '*')) fnames = glob.glob(os.path.join(path_to_inventory, '*' + station_seed_id + '*'))
if not fnames: if not fnames:
# search for station name in filename # search for station name in filename
@ -203,13 +261,13 @@ class Metadata(object):
continue continue
print('Could not find metadata for station_seed_id {} in path {}'.format(station_seed_id, path_to_inventory)) 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): def _read_metadata_file(self, path_to_inventory_filename):
''' """
function reading metadata files (either dataless seed, xml or resp) function reading metadata files (either dataless seed, xml or resp)
:param path_to_inventory_filename: :param path_to_inventory_filename:
:return: file type/ending, inventory object (Parser or Inventory) :return: file type/ending, inventory object (Parser or Inventory)
''' :rtype: (str, obspy.io.xseed.Parser or obspy.core.inventory.inventory.Inventory)
"""
# functions used to read metadata for different file endings (or file types) # functions used to read metadata for different file endings (or file types)
read_functions = {'dless': self._read_dless, read_functions = {'dless': self._read_dless,
'dseed': self._read_dless, 'dseed': self._read_dless,
@ -228,8 +286,8 @@ class Metadata(object):
return file_type, robj return file_type, robj
return None, None return None, None
@staticmethod
def _read_dless(self, path_to_inventory): def _read_dless(path_to_inventory):
exc = None exc = None
try: try:
parser = Parser(path_to_inventory) parser = Parser(path_to_inventory)
@ -237,8 +295,8 @@ class Metadata(object):
parser = None parser = None
return parser, exc return parser, exc
@staticmethod
def _read_inventory_file(self, path_to_inventory): def _read_inventory_file(path_to_inventory):
exc = None exc = None
try: try:
inv = read_inventory(path_to_inventory) inv = read_inventory(path_to_inventory)
@ -247,7 +305,6 @@ class Metadata(object):
return inv, exc 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.
@ -458,15 +515,22 @@ def read_metadata(path_to_inventory):
# return metadata_objects # return metadata_objects
def restitute_trace(input_tuple): def restitute_trace(input_tuple):
def no_metadata(tr, seed_id):
print('no metadata file found '
'for trace {0}'.format(seed_id))
return tr, True
tr, metadata, unit, force = input_tuple tr, metadata, unit, force = input_tuple
remove_trace = False remove_trace = False
seed_id = tr.get_id() seed_id = tr.get_id()
mdata = metadata.get_metadata(seed_id) mdata = metadata.get_metadata(seed_id, time=tr.stats.starttime)
if not mdata:
return no_metadata(tr, seed_id)
invtype = mdata['invtype'] invtype = mdata['invtype']
inobj = mdata['data'] inobj = mdata['data']
@ -481,8 +545,7 @@ def restitute_trace(input_tuple):
if invtype == 'resp': if invtype == 'resp':
fresp = find_in_list(inobj, seed_id) fresp = find_in_list(inobj, seed_id)
if not fresp: if not fresp:
raise IOError('no response file found ' return no_metadata(tr, seed_id)
'for trace {0}'.format(seed_id))
fname = fresp fname = fresp
seedresp = dict(filename=fname, seedresp = dict(filename=fname,
date=stime, date=stime,
@ -504,9 +567,8 @@ def restitute_trace(input_tuple):
else: else:
finv = invlist[0] finv = invlist[0]
inventory = read_inventory(finv, format='STATIONXML') inventory = read_inventory(finv, format='STATIONXML')
elif invtype == None: elif invtype is None:
print("No restitution possible, as there are no station-meta data available!") return no_metadata(tr, seed_id)
return tr, True
else: else:
remove_trace = True remove_trace = True
# apply restitution to data # apply restitution to data
@ -542,9 +604,6 @@ def restitute_data(data, metadata, unit='VEL', force=False, ncores=0):
takes a data stream and a path_to_inventory and returns the corrected takes a data stream and a path_to_inventory and returns the corrected
waveform data stream waveform data stream
:param data: seismic data stream :param data: seismic data stream
:param invtype: type of found metadata
:param inobj: either list of metadata files or `obspy.io.xseed.Parser`
object
:param unit: unit to correct for (default: 'VEL') :param unit: unit to correct for (default: 'VEL')
:param force: force restitution for already corrected traces (default: :param force: force restitution for already corrected traces (default:
False) False)
@ -553,7 +612,7 @@ def restitute_data(data, metadata, unit='VEL', force=False, ncores=0):
restflag = list() restflag = list()
data = remove_underscores(data) #data = remove_underscores(data)
# loop over traces # loop over traces
input_tuples = [] input_tuples = []
@ -562,7 +621,7 @@ def restitute_data(data, metadata, unit='VEL', force=False, ncores=0):
data.remove(tr) data.remove(tr)
pool = gen_Pool(ncores) pool = gen_Pool(ncores)
result = pool.map(restitute_trace, input_tuples) result = pool.imap_unordered(restitute_trace, input_tuples)
pool.close() pool.close()
for tr, remove_trace in result: for tr, remove_trace in result:
@ -612,7 +671,7 @@ def get_prefilt(trace, tlow=(0.5, 0.9), thi=(5., 2.), verbosity=0):
fny = trace.stats.sampling_rate / 2 fny = trace.stats.sampling_rate / 2
fc21 = fny - (fny * thi[0] / 100.) fc21 = fny - (fny * thi[0] / 100.)
fc22 = fny - (fny * thi[1] / 100.) fc22 = fny - (fny * thi[1] / 100.)
return (tlow[0], tlow[1], fc21, fc22) return tlow[0], tlow[1], fc21, fc22
if __name__ == "__main__": if __name__ == "__main__":

View File

@ -16,7 +16,6 @@ from pylot.core.loc import hyposat
from pylot.core.loc import nll from pylot.core.loc import nll
from pylot.core.loc import velest from pylot.core.loc import velest
# determine system dependent path separator # determine system dependent path separator
system_name = platform.system() system_name = platform.system()
if system_name in ["Linux", "Darwin"]: if system_name in ["Linux", "Darwin"]:
@ -42,54 +41,3 @@ OUTPUTFORMATS = {'.xml': 'QUAKEML',
LOCTOOLS = dict(nll=nll, hyposat=hyposat, velest=velest, hypo71=hypo71, hypodd=hypodd) LOCTOOLS = dict(nll=nll, hyposat=hyposat, velest=velest, hypo71=hypo71, hypodd=hypodd)
class SetChannelComponents(object):
def __init__(self):
self.setDefaultCompPosition()
def setDefaultCompPosition(self):
# default component order
self.compPosition_Map = dict(Z=2, N=1, E=0)
self.compName_Map = {'3': 'Z',
'1': 'N',
'2': 'E'}
def _getCurrentPosition(self, component):
for key, value in self.compName_Map.items():
if value == component:
return key, value
errMsg = 'getCurrentPosition: Could not find former position of component {}.'.format(component)
raise ValueError(errMsg)
def _switch(self, component, component_alter):
# Without switching, multiple definitions of the same alter_comp are possible
old_alter_comp, _ = self._getCurrentPosition(component)
old_comp = self.compName_Map[component_alter]
if not old_alter_comp == component_alter and not old_comp == component:
self.compName_Map[old_alter_comp] = old_comp
print('switch: Automatically switched component {} to {}'.format(old_alter_comp, old_comp))
def setCompPosition(self, component_alter, component, switch=True):
component_alter = str(component_alter)
if not component_alter in self.compName_Map.keys():
errMsg = 'setCompPosition: Unrecognized alternative component {}. Expecting one of {}.'
raise ValueError(errMsg.format(component_alter, self.compName_Map.keys()))
if not component in self.compPosition_Map.keys():
errMsg = 'setCompPosition: Unrecognized target component {}. Expecting one of {}.'
raise ValueError(errMsg.format(component, self.compPosition_Map.keys()))
print('setCompPosition: set component {} to {}'.format(component_alter, component))
if switch:
self._switch(component, component_alter)
self.compName_Map[component_alter] = component
def getCompPosition(self, component):
return self._getCurrentPosition(component)[0]
def getPlotPosition(self, component):
component = str(component)
if component in self.compPosition_Map.keys():
return self.compPosition_Map[component]
elif component in self.compName_Map.keys():
return self.compPosition_Map[self.compName_Map[component]]
else:
errMsg = 'getCompPosition: Unrecognized component {}. Expecting one of {} or {}.'
raise ValueError(errMsg.format(component, self.compPosition_Map.keys(), self.compName_Map.keys()))

View File

@ -35,6 +35,7 @@ class Event(ObsPyEvent):
self._refEvent = False self._refEvent = False
self.get_notes() self.get_notes()
self.get_obspy_event_info() self.get_obspy_event_info()
self.dirty = False
def get_notes_path(self): def get_notes_path(self):
""" """
@ -143,6 +144,7 @@ class Event(ObsPyEvent):
for index, pick in reversed(list(enumerate(self.picks))): for index, pick in reversed(list(enumerate(self.picks))):
if picktype in str(pick.method_id): if picktype in str(pick.method_id):
self.picks.pop(index) self.picks.pop(index)
self.dirty = True
def addPicks(self, picks): def addPicks(self, picks):
""" """
@ -157,12 +159,12 @@ class Event(ObsPyEvent):
# add ObsPy picks (clear old manual and copy all new manual from pylot) # add ObsPy picks (clear old manual and copy all new manual from pylot)
self.clearObsPyPicks('manual') self.clearObsPyPicks('manual')
self.picks += picks_from_picksdict(self.pylot_picks) self.picks += picks_from_picksdict(self.pylot_picks)
self.dirty = True
def addAutopicks(self, autopicks): def addAutopicks(self, autopicks):
""" """
Add automatic picks to event Add automatic picks to event
:param autopicks: automatic picks to add to event :param autopicks: automatic picks to add to event
:type autopicks dict:
:return: :return:
:rtype: None :rtype: None
""" """
@ -171,6 +173,7 @@ class Event(ObsPyEvent):
# add ObsPy picks (clear old auto and copy all new auto from pylot) # add ObsPy picks (clear old auto and copy all new auto from pylot)
self.clearObsPyPicks('auto') self.clearObsPyPicks('auto')
self.picks += picks_from_picksdict(self.pylot_autopicks) self.picks += picks_from_picksdict(self.pylot_autopicks)
self.dirty = True
def setPick(self, station, pick): def setPick(self, station, pick):
""" """
@ -186,11 +189,13 @@ class Event(ObsPyEvent):
self.pylot_picks[station] = pick self.pylot_picks[station] = pick
else: else:
try: try:
if station in self.pylot_picks:
self.pylot_picks.pop(station) self.pylot_picks.pop(station)
except Exception as e: except Exception as e:
print('Could not remove pick {} from station {}: {}'.format(pick, station, e)) print('Could not remove pick {} from station {}: {}'.format(pick, station, e))
self.clearObsPyPicks('manual') self.clearObsPyPicks('manual')
self.picks += picks_from_picksdict(self.pylot_picks) self.picks += picks_from_picksdict(self.pylot_picks)
self.dirty = True
def setPicks(self, picks): def setPicks(self, picks):
""" """
@ -203,6 +208,7 @@ class Event(ObsPyEvent):
self.pylot_picks = picks self.pylot_picks = picks
self.clearObsPyPicks('manual') self.clearObsPyPicks('manual')
self.picks += picks_from_picksdict(self.pylot_picks) self.picks += picks_from_picksdict(self.pylot_picks)
self.dirty = True
def getPick(self, station): def getPick(self, station):
""" """
@ -237,11 +243,13 @@ class Event(ObsPyEvent):
self.pylot_autopicks[station] = pick self.pylot_autopicks[station] = pick
else: else:
try: try:
if station in self.pylot_autopicks:
self.pylot_autopicks.pop(station) self.pylot_autopicks.pop(station)
except Exception as e: except Exception as e:
print('Could not remove pick {} from station {}: {}'.format(pick, station, e)) print('Could not remove pick {} from station {}: {}'.format(pick, station, e))
self.clearObsPyPicks('auto') self.clearObsPyPicks('auto')
self.picks += picks_from_picksdict(self.pylot_autopicks) self.picks += picks_from_picksdict(self.pylot_autopicks)
self.dirty = True
def setAutopicks(self, picks): def setAutopicks(self, picks):
""" """
@ -254,6 +262,7 @@ class Event(ObsPyEvent):
self.pylot_autopicks = picks self.pylot_autopicks = picks
self.clearObsPyPicks('auto') self.clearObsPyPicks('auto')
self.picks += picks_from_picksdict(self.pylot_autopicks) self.picks += picks_from_picksdict(self.pylot_autopicks)
self.dirty = True
def getAutopick(self, station): def getAutopick(self, station):
""" """
@ -292,6 +301,7 @@ class Event(ObsPyEvent):
try: try:
outfile = open(filename, 'wb') outfile = open(filename, 'wb')
cPickle.dump(self, outfile, -1) cPickle.dump(self, outfile, -1)
self.dirty = False
except Exception as e: except Exception as e:
print('Could not pickle PyLoT event. Reason: {}'.format(e)) print('Could not pickle PyLoT event. Reason: {}'.format(e))
@ -310,5 +320,6 @@ class Event(ObsPyEvent):
import _pickle as cPickle import _pickle as cPickle
infile = open(filename, 'rb') infile = open(filename, 'rb')
event = cPickle.load(infile) event = cPickle.load(infile)
event.dirty = False
print('Loaded %s' % filename) print('Loaded %s' % filename)
return event return event

View File

@ -4,6 +4,7 @@
import os import os
from obspy import UTCDateTime from obspy import UTCDateTime
def check_obspydmt_structure(path): def check_obspydmt_structure(path):
''' '''
Check path for obspyDMT event structure. Check path for obspyDMT event structure.
@ -16,6 +17,7 @@ def check_obspydmt_structure(path):
return True return True
return False return False
def check_obspydmt_eventfolder(folder): def check_obspydmt_eventfolder(folder):
try: try:
time = folder.split('.')[0] time = folder.split('.')[0]
@ -25,6 +27,7 @@ def check_obspydmt_eventfolder(folder):
except Exception as e: except Exception as e:
return False, e return False, e
def qml_from_obspyDMT(path): def qml_from_obspyDMT(path):
import pickle import pickle
from obspy.core.event import Event, Magnitude, Origin from obspy.core.event import Event, Magnitude, Origin
@ -41,4 +44,3 @@ def qml_from_obspyDMT(path):
ev.magnitudes.append(mag) ev.magnitudes.append(mag)
ev.origins.append(origin) ev.origins.append(origin)
return ev return ev

View File

@ -33,31 +33,21 @@ class Thread(QThread):
self._executed = False self._executed = False
self._executedError = e self._executedError = e
traceback.print_exc() traceback.print_exc()
exctype, value = sys.exc_info ()[:2] exctype, value = sys.exc_info()[:2]
self._executedErrorInfo = '{} {} {}'.\ self._executedErrorInfo = '{} {} {}'. \
format(exctype, value, traceback.format_exc()) format(exctype, value, traceback.format_exc())
sys.stdout = sys.__stdout__ sys.stdout = sys.__stdout__
def showProgressbar(self): def showProgressbar(self):
if self.progressText: if self.progressText:
# generate widget if not given in init # # generate widget if not given in init
if not self.pb_widget: # if not self.pb_widget:
self.pb_widget = QDialog(self.parent()) # self.pb_widget = ProgressBarWidget(self.parent())
self.pb_widget.setWindowFlags(Qt.SplashScreen) # self.pb_widget.setWindowFlags(Qt.SplashScreen)
self.pb_widget.setModal(True) # self.pb_widget.setModal(True)
# add button self.pb_widget.label.setText(self.progressText)
delete_button = QPushButton('X')
delete_button.clicked.connect(self.exit)
hl = QHBoxLayout()
pb = QProgressBar()
pb.setRange(0, 0)
hl.addWidget(pb)
hl.addWidget(QLabel(self.progressText))
if self.abortButton:
hl.addWidget(delete_button)
self.pb_widget.setLayout(hl)
self.pb_widget.show() self.pb_widget.show()
def hideProgressbar(self): def hideProgressbar(self):
@ -75,6 +65,7 @@ class Worker(QRunnable):
''' '''
Worker class to be run by MultiThread(QThread). Worker class to be run by MultiThread(QThread).
''' '''
def __init__(self, fun, args, def __init__(self, fun, args,
progressText=None, progressText=None,
pb_widget=None, pb_widget=None,
@ -82,7 +73,7 @@ class Worker(QRunnable):
super(Worker, self).__init__() super(Worker, self).__init__()
self.fun = fun self.fun = fun
self.args = args self.args = args
#self.kwargs = kwargs # self.kwargs = kwargs
self.signals = WorkerSignals() self.signals = WorkerSignals()
self.progressText = progressText self.progressText = progressText
self.pb_widget = pb_widget self.pb_widget = pb_widget
@ -96,9 +87,9 @@ class Worker(QRunnable):
try: try:
result = self.fun(self.args) result = self.fun(self.args)
except: except:
exctype, value = sys.exc_info ()[:2] exctype, value = sys.exc_info()[:2]
print(exctype, value, traceback.format_exc()) print(exctype, value, traceback.format_exc())
self.signals.error.emit ((exctype, value, traceback.format_exc ())) self.signals.error.emit((exctype, value, traceback.format_exc()))
else: else:
self.signals.result.emit(result) self.signals.result.emit(result)
finally: finally:
@ -146,7 +137,7 @@ class MultiThread(QThread):
self.ncores = multiprocessing.cpu_count() self.ncores = multiprocessing.cpu_count()
pool = multiprocessing.Pool(self.ncores) pool = multiprocessing.Pool(self.ncores)
self.data = pool.map_async(self.func, self.args, callback=self.emitDone) self.data = pool.map_async(self.func, self.args, callback=self.emitDone)
#self.data = pool.apply_async(self.func, self.shotlist, callback=self.emitDone) #emit each time returned # self.data = pool.apply_async(self.func, self.shotlist, callback=self.emitDone) #emit each time returned
pool.close() pool.close()
self._executed = True self._executed = True
except Exception as e: except Exception as e:

View File

@ -22,11 +22,7 @@ from pylot.styles import style_settings
from scipy.interpolate import splrep, splev from scipy.interpolate import splrep, splev
from PySide import QtCore, QtGui from PySide import QtCore, QtGui
try: import pyqtgraph as pg
import pyqtgraph as pg
except Exception as e:
print('PyLoT: Could not import pyqtgraph. {}'.format(e))
pg = None
def _pickle_method(m): def _pickle_method(m):
if m.im_self is None: if m.im_self is None:
@ -34,6 +30,7 @@ def _pickle_method(m):
else: else:
return getattr, (m.im_self, m.im_func.func_name) return getattr, (m.im_self, m.im_func.func_name)
def getAutoFilteroptions(phase, parameter): def getAutoFilteroptions(phase, parameter):
filtername = {'P': 'bpz2', filtername = {'P': 'bpz2',
'S': 'bph2'} 'S': 'bph2'}
@ -44,6 +41,7 @@ def getAutoFilteroptions(phase, parameter):
filteroptions = FilterOptions(type='bandpass', freq=[freqmin, freqmax], order=4) # order=4 default from obspy filteroptions = FilterOptions(type='bandpass', freq=[freqmin, freqmax], order=4) # order=4 default from obspy
return filteroptions return filteroptions
def readDefaultFilterInformation(fname): def readDefaultFilterInformation(fname):
""" """
Read default filter information from pylot.in file Read default filter information from pylot.in file
@ -118,8 +116,12 @@ def gen_Pool(ncores=0):
""" """
import multiprocessing import multiprocessing
if ncores == 0: ncores_max = multiprocessing.cpu_count()
ncores = multiprocessing.cpu_count()
if ncores == 0 or ncores > ncores_max:
ncores = ncores_max
if ncores > ncores_max:
print('Reduced number of requested CPU slots to available number: {}'.format(ncores))
print('gen_Pool: Generated multiprocessing Pool with {} cores\n'.format(ncores)) print('gen_Pool: Generated multiprocessing Pool with {} cores\n'.format(ncores))
@ -397,6 +399,10 @@ def full_range(stream):
:return: minimum start time and maximum end time :return: minimum start time and maximum end time
:rtype: (`~maximum start time and minimum end time`, maximum start time and minimum end time) :rtype: (`~maximum start time and minimum end time`, maximum start time and minimum end time)
""" """
if not stream:
print('full_range: Empty Stream!')
return None, None
min_start = min([trace.stats.starttime for trace in stream]) min_start = min([trace.stats.starttime for trace in stream])
max_end = max([trace.stats.endtime for trace in stream]) max_end = max([trace.stats.endtime for trace in stream])
@ -537,7 +543,7 @@ def isSorted(iterable):
False False
""" """
assert isIterable(iterable), 'object is not iterable; object: {' \ assert isIterable(iterable), 'object is not iterable; object: {' \
'0}'.format(iterable) '}'.format(iterable)
if type(iterable) is str: if type(iterable) is str:
iterable = [s for s in iterable] iterable = [s for s in iterable]
return sorted(iterable) == iterable return sorted(iterable) == iterable
@ -675,7 +681,7 @@ def pick_color(picktype, phase, quality=0):
bpc = base_phase_colors(picktype, phase) # returns dict like {'modifier': 'g', 'rgba': (0, 0, 255, 255)} bpc = base_phase_colors(picktype, phase) # returns dict like {'modifier': 'g', 'rgba': (0, 0, 255, 255)}
rgba = bpc['rgba'] rgba = bpc['rgba']
modifier = bpc['modifier'] modifier = bpc['modifier']
intensity = 255.*quality/min_quality intensity = 255. * quality / min_quality
rgba = modify_rgba(rgba, modifier, intensity) rgba = modify_rgba(rgba, modifier, intensity)
return rgba return rgba
@ -787,6 +793,7 @@ def base_phase_colors(picktype, phase):
phasecolors = style_settings.phasecolors phasecolors = style_settings.phasecolors
return phasecolors[picktype][phase] return phasecolors[picktype][phase]
def transform_colors_mpl_str(colors, no_alpha=False): def transform_colors_mpl_str(colors, no_alpha=False):
""" """
Transforms rgba color values to a matplotlib string of color values with a range of [0, 1] Transforms rgba color values to a matplotlib string of color values with a range of [0, 1]
@ -805,6 +812,7 @@ def transform_colors_mpl_str(colors, no_alpha=False):
colors_mpl = '({}, {}, {}, {})'.format(*colors_mpl) colors_mpl = '({}, {}, {}, {})'.format(*colors_mpl)
return colors_mpl return colors_mpl
def transform_colors_mpl(colors): def transform_colors_mpl(colors):
""" """
Transform rgba colors from [0, 255] to [0, 1] Transform rgba colors from [0, 255] to [0, 1]
@ -817,6 +825,7 @@ def transform_colors_mpl(colors):
colors_mpl = tuple([color / 255. for color in colors]) colors_mpl = tuple([color / 255. for color in colors])
return colors_mpl return colors_mpl
def remove_underscores(data): def remove_underscores(data):
""" """
takes a `obspy.core.stream.Stream` object and removes all underscores takes a `obspy.core.stream.Stream` object and removes all underscores
@ -826,9 +835,9 @@ def remove_underscores(data):
:return: data stream :return: data stream
:rtype: `~obspy.core.stream.Stream` :rtype: `~obspy.core.stream.Stream`
""" """
for tr in data: #for tr in data:
# remove underscores # # remove underscores
tr.stats.station = tr.stats.station.strip('_') # tr.stats.station = tr.stats.station.strip('_')
return data return data
@ -926,7 +935,10 @@ def get_stations(data):
def check4rotated(data, metadata=None, verbosity=1): def check4rotated(data, metadata=None, verbosity=1):
""" """
Check all traces in data. If a trace is not in ZNE rotation (last symbol of channel code is numeric) and the trace
is in the metadata with azimuth and dip, rotate it to classical ZNE orientation.
Rotating the traces requires them to be of the same length, so, all traces will be trimmed to a common length as a
side effect.
:param data: stream object containing seismic traces :param data: stream object containing seismic traces
:type data: `~obspy.core.stream.Stream` :type data: `~obspy.core.stream.Stream`
:param metadata: tuple containing metadata type string and metadata parser object :param metadata: tuple containing metadata type string and metadata parser object
@ -943,86 +955,44 @@ def check4rotated(data, metadata=None, verbosity=1):
Azimut and dip are fetched from metadata. To be rotated, traces of a station have to be cut to the same length. Azimut and dip are fetched from metadata. To be rotated, traces of a station have to be cut to the same length.
Returns unrotated traces of no metadata is provided Returns unrotated traces of no metadata is provided
:param wfstream: stream containing seismic traces :param wfstream: stream containing seismic traces of a station
:type wfstream: `~obspy.core.stream.Stream` :type wfstream: `~obspy.core.stream.Stream`
:param metadata: tuple containing metadata type string and metadata parser object :param metadata: tuple containing metadata type string and metadata parser object
:type metadata: (str, `~obspy.io.xseed.parser.Parser`) :type metadata: (str, `~obspy.io.xseed.parser.Parser`)
:return: stream object with traditionally oriented traces (ZNE) :return: stream object with traditionally oriented traces (ZNE)
:rtype: `~obspy.core.stream.Stream` :rtype: `~obspy.core.stream.Stream`
""" """
try:
# indexing fails if metadata is None
metadata[0]
except TypeError:
if verbosity:
msg = 'Warning: could not rotate traces since no metadata was given\nset Inventory file!'
print(msg)
return wfstream
if metadata[0] is None:
# sometimes metadata is (None, (None,))
if verbosity:
msg = 'Warning: could not rotate traces since no metadata was given\nCheck inventory directory!'
print(msg)
return wfstream
else:
parser = metadata[1]
def get_dip_azimut(parser, trace_id):
"""
Gets azimuth and dip by trace id out of the metadata parser
:param parser: metadata parser object
:type parser: `~obspy.io.xseed.parser.Parser`
:param trace_id: eg. 'BW.RJOB..EHZ',
:type trace_id: str
:return: tuple containing dip and azimuth of the trace corresponding to trace_id
:rtype: (float, float)
"""
dip = None
azimut = None
try:
blockettes = parser._select(trace_id)
except SEEDParserException as e:
print(e)
raise ValueError
for blockette_ in blockettes:
if blockette_.id != 52:
continue
dip = blockette_.dip
azimut = blockette_.azimuth
break
if (dip is None or azimut is None) or (dip == 0 and azimut == 0):
error_msg = 'Dip and azimuth not available for trace_id {}'.format(trace_id)
raise ValueError(error_msg)
return dip, azimut
# check if any traces in this station need to be rotated
trace_ids = [trace.id for trace in wfstream] trace_ids = [trace.id for trace in wfstream]
for trace_id in trace_ids: orientations = [trace_id[-1] for trace_id in trace_ids]
orientation = trace_id[-1] # last letter if trace id is orientation code, ZNE or 123 rotation_required = [orientation.isnumeric() for orientation in orientations]
if orientation.isnumeric(): if any(rotation_required):
# misaligned channels have a number as orientation t_start = full_range(wfstream)
azimuts = []
dips = []
for trace_id in trace_ids:
try: try:
dip, azimut = get_dip_azimut(parser, trace_id) azimuts = [metadata.get_coordinates(tr_id, t_start)['azimuth'] for tr_id in trace_ids]
except ValueError as e: dips = [metadata.get_coordinates(tr_id, t_start)['dip'] for tr_id in trace_ids]
print(e) except (KeyError, TypeError) as e:
print('Failed to rotate station {}, no azimuth or dip available in metadata'.format(trace_id)) print('Failed to rotate trace {}, no azimuth or dip available in metadata'.format(trace_id))
return wfstream return wfstream
azimuts.append(azimut) if len(wfstream) < 3:
dips.append(dip) print('Failed to rotate Stream {}, not enough components available.'.format(wfstream))
# to rotate all traces must have same length return wfstream
# to rotate all traces must have same length, so trim them
wfstream = trim_station_components(wfstream, trim_start=True, trim_end=True) wfstream = trim_station_components(wfstream, trim_start=True, trim_end=True)
z, n, e = rotate2zne(wfstream[0], azimuts[0], dips[0], z, n, e = rotate2zne(wfstream[0], azimuts[0], dips[0],
wfstream[1], azimuts[1], dips[1], wfstream[1], azimuts[1], dips[1],
wfstream[2], azimuts[2], dips[2]) wfstream[2], azimuts[2], dips[2])
print('check4rotated: rotated station {} to ZNE'.format(trace_id)) print('check4rotated: rotated trace {} to ZNE'.format(trace_id))
z_index = dips.index(min(dips)) # get z-trace index (dip is measured from 0 to -90) # replace old data with rotated data, change the channel code to ZNE
z_index = dips.index(min(
dips)) # get z-trace index, z has minimum dip of -90 (dip is measured from 0 to -90, with -90 being vertical)
wfstream[z_index].data = z wfstream[z_index].data = z
wfstream[z_index].stats.channel = wfstream[z_index].stats.channel[0:-1] + 'Z' wfstream[z_index].stats.channel = wfstream[z_index].stats.channel[0:-1] + 'Z'
del trace_ids[z_index] del trace_ids[z_index]
for trace_id in trace_ids: for trace_id in trace_ids:
dip, az = get_dip_azimut(parser, trace_id) coordinates = metadata.get_coordinates(trace_id, t_start)
dip, az = coordinates['dip'], coordinates['azimuth']
trace = wfstream.select(id=trace_id)[0] trace = wfstream.select(id=trace_id)[0]
if az > 315 or az <= 45 or az > 135 and az <= 225: if az > 315 or az <= 45 or az > 135 and az <= 225:
trace.data = n trace.data = n
@ -1030,13 +1000,14 @@ def check4rotated(data, metadata=None, verbosity=1):
elif az > 45 and az <= 135 or az > 225 and az <= 315: elif az > 45 and az <= 135 or az > 225 and az <= 315:
trace.data = e trace.data = e
trace.stats.channel = trace.stats.channel[0:-1] + 'E' trace.stats.channel = trace.stats.channel[0:-1] + 'E'
break
else:
continue
return wfstream return wfstream
if metadata is None:
if verbosity:
msg = 'Warning: could not rotate traces since no metadata was given\nset Inventory file!'
print(msg)
return data
stations = get_stations(data) stations = get_stations(data)
for station in stations: # loop through all stations and rotate data if neccessary for station in stations: # loop through all stations and rotate data if neccessary
wf_station = data.select(station=station) wf_station = data.select(station=station)
rotate_components(wf_station, metadata) rotate_components(wf_station, metadata)
@ -1149,7 +1120,7 @@ def loopIdentifyPhase(phase):
""" """
from pylot.core.util.defaults import ALTSUFFIX from pylot.core.util.defaults import ALTSUFFIX
if phase == None: if phase is None:
raise NameError('Can not identify phase that is None') raise NameError('Can not identify phase that is None')
phase_copy = phase phase_copy = phase
@ -1199,20 +1170,6 @@ def identifyPhaseID(phase):
return identifyPhase(loopIdentifyPhase(phase)) return identifyPhase(loopIdentifyPhase(phase))
def has_spe(pick):
"""
Check for 'spe' key (symmetric picking error) in dict and return its value if found, else return None
:param pick: pick dictionary
:type pick: dict
:return: value of 'spe' key
:rtype: float or None
"""
if not 'spe' in pick.keys():
return None
else:
return pick['spe']
def check_all_obspy(eventlist): def check_all_obspy(eventlist):
ev_type = 'obspydmt' ev_type = 'obspydmt'
return check_event_folders(eventlist, ev_type) return check_event_folders(eventlist, ev_type)
@ -1276,3 +1233,56 @@ if __name__ == "__main__":
import doctest import doctest
doctest.testmod() doctest.testmod()
class SetChannelComponents(object):
def __init__(self):
self.setDefaultCompPosition()
def setDefaultCompPosition(self):
# default component order
self.compPosition_Map = dict(Z=2, N=1, E=0)
self.compName_Map = {'3': 'Z',
'1': 'N',
'2': 'E'}
def _getCurrentPosition(self, component):
for key, value in self.compName_Map.items():
if value == component:
return key, value
errMsg = 'getCurrentPosition: Could not find former position of component {}.'.format(component)
raise ValueError(errMsg)
def _switch(self, component, component_alter):
# Without switching, multiple definitions of the same alter_comp are possible
old_alter_comp, _ = self._getCurrentPosition(component)
old_comp = self.compName_Map[component_alter]
if not old_alter_comp == component_alter and not old_comp == component:
self.compName_Map[old_alter_comp] = old_comp
print('switch: Automatically switched component {} to {}'.format(old_alter_comp, old_comp))
def setCompPosition(self, component_alter, component, switch=True):
component_alter = str(component_alter)
if not component_alter in self.compName_Map.keys():
errMsg = 'setCompPosition: Unrecognized alternative component {}. Expecting one of {}.'
raise ValueError(errMsg.format(component_alter, self.compName_Map.keys()))
if not component in self.compPosition_Map.keys():
errMsg = 'setCompPosition: Unrecognized target component {}. Expecting one of {}.'
raise ValueError(errMsg.format(component, self.compPosition_Map.keys()))
print('setCompPosition: set component {} to {}'.format(component_alter, component))
if switch:
self._switch(component, component_alter)
self.compName_Map[component_alter] = component
def getCompPosition(self, component):
return self._getCurrentPosition(component)[0]
def getPlotPosition(self, component):
component = str(component)
if component in self.compPosition_Map.keys():
return self.compPosition_Map[component]
elif component in self.compName_Map.keys():
return self.compPosition_Map[self.compName_Map[component]]
else:
errMsg = 'getCompPosition: Unrecognized component {}. Expecting one of {} or {}.'
raise ValueError(errMsg.format(component, self.compPosition_Map.keys(), self.compName_Map.keys()))

View File

@ -17,6 +17,7 @@ import time
import numpy as np import numpy as np
import matplotlib import matplotlib
matplotlib.use('QT4Agg') matplotlib.use('QT4Agg')
from matplotlib.figure import Figure from matplotlib.figure import Figure
@ -48,15 +49,15 @@ from pylot.core.io.inputs import FilterOptions, PylotParameter
from pylot.core.pick.utils import getSNR, earllatepicker, getnoisewin, \ from pylot.core.pick.utils import getSNR, earllatepicker, getnoisewin, \
getResolutionWindow, get_quality_class getResolutionWindow, get_quality_class
from pylot.core.pick.compare import Comparison from pylot.core.pick.compare import Comparison
from pylot.core.util.defaults import OUTPUTFORMATS, FILTERDEFAULTS, \ from pylot.core.util.defaults import OUTPUTFORMATS, FILTERDEFAULTS
SetChannelComponents
from pylot.core.util.utils import prepTimeAxis, full_range, scaleWFData, \ from pylot.core.util.utils import prepTimeAxis, full_range, scaleWFData, \
demeanTrace, isSorted, findComboBoxIndex, clims, pick_linestyle_plt, pick_color_plt, \ demeanTrace, isSorted, findComboBoxIndex, clims, pick_linestyle_plt, pick_color_plt, \
check4rotated, check4doubled, check4gaps, remove_underscores, find_horizontals, identifyPhase, \ check4rotated, check4doubled, check4gaps, remove_underscores, find_horizontals, identifyPhase, \
loopIdentifyPhase, trim_station_components, transformFilteroptions2String, \ loopIdentifyPhase, trim_station_components, transformFilteroptions2String, \
identifyPhaseID, real_Bool, pick_color, getAutoFilteroptions identifyPhaseID, real_Bool, pick_color, getAutoFilteroptions, SetChannelComponents
from autoPyLoT import autoPyLoT from autoPyLoT import autoPyLoT
from pylot.core.util.thread import Thread from pylot.core.util.thread import Thread
from pylot.core.util.dataprocessing import Metadata
if sys.version_info.major == 3: if sys.version_info.major == 3:
import icons_rc_3 as icons_rc import icons_rc_3 as icons_rc
@ -118,6 +119,139 @@ def createAction(parent, text, slot=None, shortcut=None, icon=None,
return action return action
class ProgressBarWidget(QtGui.QWidget):
def __init__(self, parent=None):
super(ProgressBarWidget, self).__init__(parent)
self.hlayout = QtGui.QHBoxLayout()
self.pb = QtGui.QProgressBar()
self.pb.setRange(0, 0)
self.label = QLabel()
self.hlayout.addWidget(self.pb)
self.hlayout.addWidget(self.label)
self.setLayout(self.hlayout)
self.hide()
class AddMetadataWidget(QWidget):
def __init__(self, parent=None, metadata=None, windowflag=1):
super(AddMetadataWidget, self).__init__(parent, windowflag)
self.inventories = {}
self.main_layout = QVBoxLayout()
self.setLayout(self.main_layout)
self.setupUI()
self.connect_signals()
self.resize(600, 800)
self.metadata = metadata if metadata else Metadata()
self.from_metadata()
self.center()
self.show()
#self.__test__()
def __test__(self):
self.add_item(r'/rscratch/minos14/marcel/git/pylot/tests')
self.add_item(r'/rscratch/minos14/marcel/git/pylot/inputs')
self.add_item(r'/rscratch/minos14/marcel/git/pylot/pylot')
self.add_item(r'/rscratch/minos14/marcel/git/pylot/pylot_not_existing')
def center(self):
fm = self.frameGeometry()
screen = QtGui.QApplication.desktop().screenNumber(QtGui.QApplication.desktop().cursor().pos())
centerPoint = QtGui.QApplication.desktop().screenGeometry(screen).center()
fm.moveCenter(centerPoint)
self.move(fm.topLeft())
def setupUI(self):
self.init_selection_layout()
self.init_add_remove_layout()
self.init_list_widget()
self.init_close()
def init_selection_layout(self):
self.selection_layout = QHBoxLayout()
self.selection_box = QtGui.QLineEdit()
self.selection_button = QtGui.QPushButton('...')
self.selection_layout.addWidget(self.selection_box, 1)
self.selection_layout.addWidget(self.selection_button, 0)
self.main_layout.insertLayout(0, self.selection_layout, 0)
def init_add_remove_layout(self):
self.add_remove_layout = QHBoxLayout()
self.add_button = QtGui.QPushButton('+')
self.add_button.setShortcut(QtGui.QKeySequence(QtCore.Qt.Key_Return))
self.remove_button = QtGui.QPushButton('-')
self.remove_button.setShortcut(QtGui.QKeySequence(QtCore.Qt.Key_Delete))
self.add_remove_layout.addWidget(self.add_button, 1)
self.add_remove_layout.addWidget(self.remove_button, 1)
self.main_layout.insertLayout(1, self.add_remove_layout, 0)
def init_list_widget(self):
self.list_view = QtGui.QListView()
self.list_view.setSelectionMode(QtGui.QAbstractItemView.ExtendedSelection)
self.list_model = QtGui.QStandardItemModel(self.list_view)
self.list_view.setModel(self.list_model)
self.main_layout.insertWidget(2, self.list_view, 1)
def init_close(self):
self.close_button = QPushButton('Update')
self.close_button.setShortcut(QtGui.QKeySequence(QtCore.Qt.Key_Escape))
self.main_layout.addWidget(self.close_button)
def from_metadata(self):
for inventory_path in self.metadata.inventories:
self.add_item(inventory_path, from_metadata=True)
def refresh_list(self):
self.clear_list()
for inventory_path in self.inventories.keys():
self.add_item(inventory_path)
def connect_signals(self):
self.selection_button.clicked.connect(self.open_directory)
self.add_button.clicked.connect(self.add_item_from_box)
self.remove_button.clicked.connect(self.remove_item)
self.close_button.clicked.connect(self.hide)
def open_directory(self):
fninv = QFileDialog.getExistingDirectory(self, self.tr(
"Select inventory..."), self.tr("Select folder"))
if not fninv:
return
self.selection_box.setText(fninv)
def add_item_from_box(self):
inventory_path = self.selection_box.text()
self.add_item(inventory_path)
self.selection_box.setText('')
def add_item(self, inventory_path, from_metadata=False):
if not inventory_path:
return
if inventory_path in self.inventories.keys():
QMessageBox.warning(self, 'Info', 'Path already in list!')
return
if not os.path.isdir(inventory_path):
QMessageBox.warning(self, 'Warning', 'Path is no directory!')
return
item = QtGui.QStandardItem(inventory_path)
item.setEditable(False)
self.inventories[inventory_path] = item
self.list_model.appendRow(item)
if not from_metadata:
self.metadata.add_inventory(inventory_path)
def remove_item(self):
for index in reversed(self.list_view.selectionModel().selectedIndexes()):
item = self.list_model.itemData(index)
inventory_path = item[0]
del (self.inventories[inventory_path])
self.metadata.remove_inventory(inventory_path)
self.list_model.removeRow(index.row())
class ComparisonWidget(QWidget): class ComparisonWidget(QWidget):
def __init__(self, c, parent=None, windowflag=1): def __init__(self, c, parent=None, windowflag=1):
self._data = c self._data = c
@ -132,6 +266,14 @@ class ComparisonWidget(QWidget):
self.setupUI() self.setupUI()
self.resize(1280, 720) self.resize(1280, 720)
self.plotcomparison() self.plotcomparison()
self.center()
def center(self):
fm = self.frameGeometry()
screen = QtGui.QApplication.desktop().screenNumber(QtGui.QApplication.desktop().cursor().pos())
centerPoint = QtGui.QApplication.desktop().screenGeometry(screen).center()
fm.moveCenter(centerPoint)
self.move(fm.topLeft())
def setupUI(self): def setupUI(self):
@ -242,7 +384,8 @@ class ComparisonWidget(QWidget):
def clf(self): def clf(self):
self.canvas.figure.clf() self.canvas.figure.clf()
def hasvalue(self, sender): @staticmethod
def hasvalue(sender):
text = sender.currentText() text = sender.currentText()
index = sender.findText(text.upper()) index = sender.findText(text.upper())
return index return index
@ -380,8 +523,8 @@ class ComparisonWidget(QWidget):
ax = axes_dict[phase]['exp'] ax = axes_dict[phase]['exp']
xlims = ax.get_xlim() xlims = ax.get_xlim()
ylims = ax.get_ylim() ylims = ax.get_ylim()
#ax.fill_between([xlims[0], 0], ylims[0], ylims[1], color=(0.9, 1.0, 0.9, 0.5), label='earlier than manual') # ax.fill_between([xlims[0], 0], ylims[0], ylims[1], color=(0.9, 1.0, 0.9, 0.5), label='earlier than manual')
#ax.fill_between([0, xlims[1]], ylims[0], ylims[1], color=(1.0, 0.9, 0.9, 0.5), label='later than manual') # ax.fill_between([0, xlims[1]], ylims[0], ylims[1], color=(1.0, 0.9, 0.9, 0.5), label='later than manual')
legend = ax.legend() legend = ax.legend()
legend.draggable() legend.draggable()
@ -473,7 +616,7 @@ class WaveformWidgetPG(QtGui.QWidget):
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)
#self.plotWidget.getPlotItem().setDownsampling(auto=True) # self.plotWidget.getPlotItem().setDownsampling(auto=True)
def reinitMoveProxy(self): def reinitMoveProxy(self):
self.vLine = self.pg.InfiniteLine(angle=90, movable=False, pen=self.pen_multicursor) self.vLine = self.pg.InfiniteLine(angle=90, movable=False, pen=self.pen_multicursor)
@ -503,8 +646,17 @@ class WaveformWidgetPG(QtGui.QWidget):
self.label_layout.addWidget(self.status_label) self.label_layout.addWidget(self.status_label)
for label in self.perm_labels: for label in self.perm_labels:
self.label_layout.addWidget(label) self.label_layout.addWidget(label)
self.label_layout.addWidget(self.syn_checkbox) mid_widget = QWidget()
self.label_layout.addWidget(self.qcombo_processed) right_widget = QWidget()
# use widgets as placeholder, so that child widgets keep position when others are hidden
mid_layout = QHBoxLayout()
right_layout = QHBoxLayout()
mid_layout.addWidget(self.syn_checkbox)
right_layout.addWidget(self.qcombo_processed)
mid_widget.setLayout(mid_layout)
right_widget.setLayout(right_layout)
self.label_layout.addWidget(mid_widget)
self.label_layout.addWidget(right_widget)
self.syn_checkbox.setLayoutDirection(Qt.RightToLeft) self.syn_checkbox.setLayoutDirection(Qt.RightToLeft)
self.label_layout.setStretch(0, 4) self.label_layout.setStretch(0, 4)
self.label_layout.setStretch(1, 0) self.label_layout.setStretch(1, 0)
@ -523,7 +675,7 @@ class WaveformWidgetPG(QtGui.QWidget):
self.syn_checkbox = QtGui.QCheckBox('synthetics') self.syn_checkbox = QtGui.QCheckBox('synthetics')
self.addQCboxItem('processed', 'green') self.addQCboxItem('processed', 'green')
self.addQCboxItem('raw', 'black') self.addQCboxItem('raw', 'black')
#self.perm_qcbox_right.setAlignment(2) # self.perm_qcbox_right.setAlignment(2)
self.setLayout(self.main_layout) self.setLayout(self.main_layout)
def getPlotDict(self): def getPlotDict(self):
@ -607,7 +759,7 @@ class WaveformWidgetPG(QtGui.QWidget):
print('Warning: Could not set zoom limits') print('Warning: Could not set zoom limits')
for n, (network, station, channel) in enumerate(nsc): for n, (network, station, channel) in enumerate(nsc):
n+=1 n += 1
st = st_select.select(network=network, station=station, channel=channel) st = st_select.select(network=network, station=station, channel=channel)
trace = st[0].copy() trace = st[0].copy()
st_syn = wfsyn.select(network=network, station=station, channel=channel) st_syn = wfsyn.select(network=network, station=station, channel=channel)
@ -644,7 +796,8 @@ class WaveformWidgetPG(QtGui.QWidget):
trace_syn.normalize(np.max(np.abs(trace_syn.data)) * 2) trace_syn.normalize(np.max(np.abs(trace_syn.data)) * 2)
# TODO: change this to numpy operations instead of lists? # TODO: change this to numpy operations instead of lists?
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_syn.data = np.array([datum + n for index, datum in enumerate(trace_syn.data) trace_syn.data = np.array([datum + n for index, datum in enumerate(trace_syn.data)
if not index % nth_sample] if st_syn else []) if not index % nth_sample] if st_syn else [])
@ -664,11 +817,11 @@ class WaveformWidgetPG(QtGui.QWidget):
''' '''
npixel = self.orig_parent.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:
return trace.data, time_ax return trace.data, time_ax
remaining_samples = ndata%pts_per_pixel remaining_samples = ndata % pts_per_pixel
npixel = ndata//pts_per_pixel npixel = ndata // pts_per_pixel
if remaining_samples: if remaining_samples:
data = trace.data[:-remaining_samples] data = trace.data[:-remaining_samples]
else: else:
@ -848,7 +1001,7 @@ class PylotCanvas(FigureCanvas):
break break
if not ax_check: return if not ax_check: return
#self.updateCurrentLimits() #maybe put this down to else: # self.updateCurrentLimits() #maybe put this down to else:
# calculate delta (relative values in axis) # calculate delta (relative values in axis)
old_x, old_y = self.press_rel old_x, old_y = self.press_rel
@ -873,6 +1026,7 @@ class PylotCanvas(FigureCanvas):
new_ylim = self.calcPanZoom(self.ypress, y_bot, y_top, factor, (ydiff > 0)) new_ylim = self.calcPanZoom(self.ypress, y_bot, y_top, factor, (ydiff > 0))
self.setYLims(ax, new_ylim) self.setYLims(ax, new_ylim)
self.refreshPickDlgText()
self.draw() self.draw()
def set_frame_color(self, color='k'): def set_frame_color(self, color='k'):
@ -895,18 +1049,19 @@ class PylotCanvas(FigureCanvas):
fname += '.png' fname += '.png'
self.figure.savefig(fname) self.figure.savefig(fname)
def calcPanZoom(self, origin, lower_b, upper_b, factor, positive): @staticmethod
def calcPanZoom(origin, lower_b, upper_b, factor, positive):
d_lower = abs(origin - lower_b) d_lower = abs(origin - lower_b)
d_upper = abs(origin - upper_b) d_upper = abs(origin - upper_b)
if positive: if positive:
d_lower *= 1 - 1/factor d_lower *= 1 - 1 / factor
d_upper *= 1 - 1/factor d_upper *= 1 - 1 / factor
lower_b += d_lower lower_b += d_lower
upper_b -= d_upper upper_b -= d_upper
else: else:
d_lower /= 1 + 1/factor d_lower /= 1 + 1 / factor
d_upper /= 1 + 1/factor d_upper /= 1 + 1 / factor
lower_b -= d_lower lower_b -= d_lower
upper_b += d_upper upper_b += d_upper
@ -1029,7 +1184,8 @@ class PylotCanvas(FigureCanvas):
def clearPlotDict(self): def clearPlotDict(self):
self.plotdict = dict() self.plotdict = dict()
def calcPlotPositions(self, wfdata, compclass): @staticmethod
def calcPlotPositions(wfdata, compclass):
possible_plot_pos = list(range(len(wfdata))) possible_plot_pos = list(range(len(wfdata)))
plot_positions = {} plot_positions = {}
for trace in wfdata: for trace in wfdata:
@ -1130,9 +1286,9 @@ class PylotCanvas(FigureCanvas):
if noiselevel is not None: if noiselevel is not None:
for level in [-noiselevel[channel], noiselevel[channel]]: for level in [-noiselevel[channel], noiselevel[channel]]:
ax.plot([time_ax[0], time_ax[-1]], ax.plot([time_ax[0], time_ax[-1]],
[n+level, n+level], [n + level, n + level],
color = linecolor, color=linecolor,
linestyle = 'dashed') linestyle='dashed')
self.setPlotDict(n, (station, channel, network)) self.setPlotDict(n, (station, channel, network))
if plot_additional and additional_channel: if plot_additional and additional_channel:
compare_stream = wfdata.select(channel=additional_channel) compare_stream = wfdata.select(channel=additional_channel)
@ -1173,16 +1329,20 @@ class PylotCanvas(FigureCanvas):
self.setYLims(ax, zoomy) self.setYLims(ax, zoomy)
self.draw() self.draw()
def getXLims(self, ax): @staticmethod
def getXLims(ax):
return ax.get_xlim() return ax.get_xlim()
def getYLims(self, ax): @staticmethod
def getYLims(ax):
return ax.get_ylim() return ax.get_ylim()
def setXLims(self, ax, lims): @staticmethod
def setXLims(ax, lims):
ax.set_xlim(lims) ax.set_xlim(lims)
def setYLims(self, ax, lims): @staticmethod
def setYLims(ax, lims):
ax.set_ylim(lims) ax.set_ylim(lims)
def setYTickLabels(self, pos, labels): def setYTickLabels(self, pos, labels):
@ -1298,7 +1458,8 @@ class PhaseDefaults(QtGui.QDialog):
checkbox.setChecked(bool(phase in self.current_phases)) checkbox.setChecked(bool(phase in self.current_phases))
row += 1 row += 1
def create_phase_box(self, phase_name): @staticmethod
def create_phase_box(phase_name):
checkbox = QtGui.QCheckBox(phase_name) checkbox = QtGui.QCheckBox(phase_name)
return checkbox return checkbox
@ -1433,7 +1594,6 @@ class PickDlg(QDialog):
self.multicompfig.draw() self.multicompfig.draw()
self.multicompfig.setFocus() self.multicompfig.setFocus()
# set plot labels # set plot labels
self.setPlotLabels() self.setPlotLabels()
@ -1459,7 +1619,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)
def setupUi(self): def setupUi(self):
menuBar = QtGui.QMenuBar(self) menuBar = QtGui.QMenuBar(self)
if not self._embedded: if not self._embedded:
@ -1683,9 +1842,10 @@ class PickDlg(QDialog):
func = {True: self.model.get_ray_paths_geo, func = {True: self.model.get_ray_paths_geo,
False: self.model.get_travel_times_geo} False: self.model.get_travel_times_geo}
phases = self.prepare_phases() phases = self.prepare_phases()
station_id = self.data.traces[0].get_id() trace = self.data.traces[0]
parser = self.metadata[1] station_id = trace.get_id()
station_coords = parser.get_coordinates(station_id) starttime = trace.stats.starttime
station_coords = self.metadata.get_coordinates(station_id, starttime)
origins = self.pylot_event.origins origins = self.pylot_event.origins
if origins: if origins:
source_origin = origins[0] source_origin = origins[0]
@ -1699,7 +1859,8 @@ class PickDlg(QDialog):
phases) phases)
self.arrivals = arrivals self.arrivals = arrivals
def prepare_phases(self): @staticmethod
def prepare_phases():
settings = QtCore.QSettings() settings = QtCore.QSettings()
p_phases = settings.value('p_phases') p_phases = settings.value('p_phases')
s_phases = settings.value('s_phases') s_phases = settings.value('s_phases')
@ -1805,7 +1966,6 @@ class PickDlg(QDialog):
filterMenu.addAction(self.autoFilterAction) filterMenu.addAction(self.autoFilterAction)
filterMenu.addAction(filterOptionsAction) filterMenu.addAction(filterOptionsAction)
def filterOptions(self): def filterOptions(self):
if self.orig_parent.adjustFilterOptions(): if self.orig_parent.adjustFilterOptions():
phase = None phase = None
@ -1875,7 +2035,8 @@ class PickDlg(QDialog):
self.currentPhase = str(self.s_button.text()) self.currentPhase = str(self.s_button.text())
self.activatePicking() self.activatePicking()
def getPhaseID(self, phase): @staticmethod
def getPhaseID(phase):
return identifyPhaseID(phase) return identifyPhaseID(phase)
def set_button_border_color(self, button, color=None): def set_button_border_color(self, button, color=None):
@ -1936,14 +2097,14 @@ class PickDlg(QDialog):
self.zoomAction.trigger() self.zoomAction.trigger()
self.multicompfig.disconnectEvents() self.multicompfig.disconnectEvents()
self.cidpress = self.multicompfig.connectPressEvent(self.setIniPick) self.cidpress = self.multicompfig.connectPressEvent(self.setIniPick)
if not self.filterActionP.isChecked() and not self.filterActionS.isChecked():
if self.autoFilterAction.isChecked(): if self.autoFilterAction.isChecked():
for filteraction in [self.filterActionP, self.filterActionS]:
filteraction.setChecked(False)
self.filterWFData() self.filterWFData()
else:
self.draw() self.draw()
else: else:
self.draw() self.draw()
#self.pick_block = self.togglePickBlocker() # self.pick_block = self.togglePickBlocker()
self.disconnect_pick_delete() self.disconnect_pick_delete()
def deactivatePicking(self): def deactivatePicking(self):
@ -2087,10 +2248,11 @@ class PickDlg(QDialog):
st += data.select(channel=action.text()) st += data.select(channel=action.text())
return st return st
def calcNoiseScaleFactor(self, noiselevel, zoomfactor=5., norm=1): @staticmethod
def calcNoiseScaleFactor(noiselevel, zoomfactor=5., norm=1):
# calculate factor to upscale a trace normed to 'norm' in a way that all values # calculate factor to upscale a trace normed to 'norm' in a way that all values
# zoomfactor*noiselevel are found within -0.5*norm and 0.5*norm # zoomfactor*noiselevel are found within -0.5*norm and 0.5*norm
scaleFactor = (norm/2.) / (zoomfactor * noiselevel) scaleFactor = (norm / 2.) / (zoomfactor * noiselevel)
return scaleFactor return scaleFactor
def setIniPick(self, gui_event): def setIniPick(self, gui_event):
@ -2164,7 +2326,7 @@ class PickDlg(QDialog):
try: try:
data.detrend('linear') data.detrend('linear')
data.filter(**filteroptions) data.filter(**filteroptions)
#wfdata.filter(**filteroptions)# MP MP removed filtering of original data # wfdata.filter(**filteroptions)# MP MP removed filtering of original data
except ValueError as e: except ValueError as e:
self.qmb = QtGui.QMessageBox(QtGui.QMessageBox.Icon.Information, self.qmb = QtGui.QMessageBox(QtGui.QMessageBox.Icon.Information,
'Denied', 'setIniPick{}: Could not filter waveform: {}'.format(phase, e)) 'Denied', 'setIniPick{}: Could not filter waveform: {}'.format(phase, e))
@ -2200,7 +2362,7 @@ class PickDlg(QDialog):
x_res = getResolutionWindow(np.mean(snr), parameter.get('extent')) x_res = getResolutionWindow(np.mean(snr), parameter.get('extent'))
xlims = [ini_pick - x_res, ini_pick + x_res] xlims = [ini_pick - x_res, ini_pick + x_res]
ylims = list(np.array([-.5, .5]) + [0, len(data)-1]) ylims = list(np.array([-.5, .5]) + [0, len(data) - 1])
title = self.getStation() + ' picking mode' title = self.getStation() + ' picking mode'
if filterphase: if filterphase:
@ -2287,8 +2449,8 @@ class PickDlg(QDialog):
self.disconnectPressEvent() self.disconnectPressEvent()
self.enable_ar_buttons() self.enable_ar_buttons()
self.zoomAction.setEnabled(True) self.zoomAction.setEnabled(True)
#self.pick_block = self.togglPickBlocker() # self.pick_block = self.togglPickBlocker()
#self.resetZoom() # self.resetZoom()
self.leave_picking_mode() self.leave_picking_mode()
def savePick(self, phase, phasepicks): def savePick(self, phase, phasepicks):
@ -2349,43 +2511,38 @@ class PickDlg(QDialog):
lpp = picks['lpp'] - self.getStartTime() lpp = picks['lpp'] - self.getStartTime()
spe = picks['spe'] spe = picks['spe']
if not picktype in ['auto', 'manual']:
raise TypeError('Unknown picktype {0}'.format(picktype))
if picktype == 'manual': if picktype == 'manual':
baseorder = 5
elif picktype == 'auto':
baseorder = 0
color = pick_color_plt(picktype, phaseID, quality) color = pick_color_plt(picktype, phaseID, quality)
if not textOnly: if not textOnly:
linestyle_mpp, width_mpp = pick_linestyle_plt(picktype, 'mpp') linestyle_mpp, width_mpp = pick_linestyle_plt(picktype, 'mpp')
vl = ax.axvline(mpp, ylims[0], ylims[1], color=color, linestyle=linestyle_mpp, linewidth=width_mpp, vl = ax.axvline(mpp, ylims[0], ylims[1], color=color, linestyle=linestyle_mpp, linewidth=width_mpp,
label='{}-Pick (quality: {})'.format(phase, quality), picker=5) label='{}-{}-Pick (quality: {})'.format(phase, picktype, quality), picker=5,
self.phaseLines[phase] = vl zorder=baseorder+9)
phaseLineKey = '{}-{}'.format(phase, picktype)
self.phaseLines[phaseLineKey] = vl
if spe: if spe:
ax.fill_between([mpp-spe, mpp+spe], ylims[0], ylims[1], ax.fill_between([mpp - spe, mpp + spe], ylims[0], ylims[1],
alpha=.25, color=color, label='{}-SPE'.format(phase)) alpha=.25, color=color, label='{}-{}-SPE'.format(phase, picktype), zorder=baseorder+1)
if picks['epp']: if picks['epp']:
linestyle_epp, width_epp = pick_linestyle_plt(picktype, 'epp') linestyle_epp, width_epp = pick_linestyle_plt(picktype, 'epp')
ax.axvline(epp, ylims[0], ylims[1], color=color, linestyle=linestyle_epp, ax.axvline(epp, ylims[0], ylims[1], color=color, linestyle=linestyle_epp,
linewidth=width_epp, label='{}-EPP'.format(phase)) linewidth=width_epp, label='{}-{}-EPP'.format(phase, picktype), zorder=baseorder+2)
if picks['lpp']: if picks['lpp']:
linestyle_lpp, width_lpp = pick_linestyle_plt(picktype, 'lpp') linestyle_lpp, width_lpp = pick_linestyle_plt(picktype, 'lpp')
ax.axvline(lpp, ylims[0], ylims[1], color=color, linestyle=linestyle_lpp, ax.axvline(lpp, ylims[0], ylims[1], color=color, linestyle=linestyle_lpp,
linewidth=width_lpp, label='{}-LPP'.format(phase)) linewidth=width_lpp, label='{}-{}-LPP'.format(phase, picktype), zorder=baseorder+2)
# else: if picktype == 'auto':
# ax.plot([mpp, mpp], ylims, color=color, linestyle=linestyle_mpp, linewidth=width_mpp, ax.plot(mpp, ylims[1], color=color, marker='v', zorder=baseorder+3)
# label='{}-Pick (NO PICKERROR)'.format(phase), picker=5) ax.plot(mpp, ylims[0], color=color, marker='^', zorder=baseorder+3)
# append phase text (if textOnly: draw with current ylims) # append phase text (if textOnly: draw with current ylims)
self.phaseText.append(ax.text(mpp, ylims[1], phase, color=color)) self.phaseText.append(ax.text(mpp, ylims[1], phase, color=color, zorder=baseorder+10))
elif picktype == 'auto':
color = pick_color_plt(picktype, phaseID, quality)
linestyle_mpp, width_mpp = pick_linestyle_plt(picktype, 'mpp')
if not textOnly:
ax.plot(mpp, ylims[1], color=color, marker='v')
ax.plot(mpp, ylims[0], color=color, marker='^')
vl = ax.axvline(mpp, ylims[0], ylims[1], color=color, linestyle=linestyle_mpp, linewidth=width_mpp,
picker=5, label='{}-Autopick (quality: {})'.format(phase, quality))
self.phaseLines[phase] = vl
# append phase text (if textOnly: draw with current ylims)
self.phaseText.append(ax.text(mpp, ylims[1], phase, color=color))
else:
raise TypeError('Unknown picktype {0}'.format(picktype))
ax.legend(loc=1) ax.legend(loc=1)
def connect_mouse_motion(self): def connect_mouse_motion(self):
@ -2403,7 +2560,7 @@ class PickDlg(QDialog):
def on_motion(self, event): def on_motion(self, event):
x = event.xdata x = event.xdata
if x is not None: if x is not None:
time_code = 'T = {}, t = {} [s]'.format(self.stime+x, x) time_code = 'T = {}, t = {} [s]'.format(self.stime + x, x)
user_help = ' - Left-Click to Drag | Right-Click to Pan-Zoom |' \ user_help = ' - Left-Click to Drag | Right-Click to Pan-Zoom |' \
' Mousewheel to Zoom | Middle-Click to Delete Pick' ' Mousewheel to Zoom | Middle-Click to Delete Pick'
self.statusbar.showMessage(time_code + user_help) self.statusbar.showMessage(time_code + user_help)
@ -2421,7 +2578,7 @@ class PickDlg(QDialog):
if not x: if not x:
return return
allpicks, pick_rel, phase, picktype = self.identify_selected_picks(x) allpicks, pick_rel, phase, picktype = self.identify_selected_picks(x)
if pick_rel == None: if pick_rel is None:
return return
pick = allpicks[picktype][phase] pick = allpicks[picktype][phase]
message = '{} {}-pick'.format(picktype, phase) message = '{} {}-pick'.format(picktype, phase)
@ -2442,7 +2599,7 @@ class PickDlg(QDialog):
return return
x = event.mouseevent.xdata x = event.mouseevent.xdata
allpicks, pick_rel, phase, picktype = self.identify_selected_picks(x) allpicks, pick_rel, phase, picktype = self.identify_selected_picks(x)
if pick_rel == None: if pick_rel is None:
return return
pick = allpicks[picktype][phase] pick = allpicks[picktype][phase]
message = '{} {}-pick'.format(picktype, phase) message = '{} {}-pick'.format(picktype, phase)
@ -2499,16 +2656,17 @@ class PickDlg(QDialog):
if not self.picks and not self.autopicks: if not self.picks and not self.autopicks:
return return
allpicks, pick_rel, phase, picktype = self.identify_selected_picks(x) allpicks, pick_rel, phase, picktype = self.identify_selected_picks(x)
if pick_rel == None: if pick_rel is None:
return return
# delete the value from corresponding dictionary # delete the value from corresponding dictionary
allpicks[picktype].pop(phase) allpicks[picktype].pop(phase)
# delete line from vlines dictionary # delete line from vlines dictionary
if phase in self.phaseLines.keys(): if phase in self.phaseLines.keys():
del(self.phaseLines[phase]) del (self.phaseLines[phase])
# information output # information output
msg = 'Deleted {} pick for phase {}, at timestamp {} (relative time: {} s)' msg = 'Deleted {} pick for phase {}, station {} at timestamp {} (relative time: {} s)'
print(msg.format(picktype, phase, self.getStartTime()+pick_rel, pick_rel)) print(msg.format(picktype, phase, '{}.{}'.format(self.network, self.station),
self.getStartTime() + pick_rel, pick_rel))
self.setDirty(True) self.setDirty(True)
def identify_selected_picks(self, x): def identify_selected_picks(self, x):
@ -2534,8 +2692,6 @@ class PickDlg(QDialog):
pick_rel, phase, picktype = X[index] pick_rel, phase, picktype = X[index]
return allpicks, pick_rel, phase, picktype return allpicks, pick_rel, phase, picktype
def drawPhaseText(self): def drawPhaseText(self):
self.drawPicks(picktype='manual', textOnly=True) self.drawPicks(picktype='manual', textOnly=True)
self.drawPicks(picktype='auto', textOnly=True) self.drawPicks(picktype='auto', textOnly=True)
@ -2572,7 +2728,7 @@ class PickDlg(QDialog):
return return
self.cur_xlim = self.multicompfig.axes[0].get_xlim() self.cur_xlim = self.multicompfig.axes[0].get_xlim()
self.cur_ylim = self.multicompfig.axes[0].get_ylim() self.cur_ylim = self.multicompfig.axes[0].get_ylim()
#self.multicompfig.updateCurrentLimits() # self.multicompfig.updateCurrentLimits()
data = self.getWFData().copy() data = self.getWFData().copy()
title = self.getStation() title = self.getStation()
if filter: if filter:
@ -2626,15 +2782,18 @@ class PickDlg(QDialog):
settings = QSettings() settings = QSettings()
settings.setValue('autoFilter', self.autoFilterAction.isChecked()) settings.setValue('autoFilter', self.autoFilterAction.isChecked())
def updateChannelSettingsP(self, action): @staticmethod
def updateChannelSettingsP(action):
settings = QSettings() settings = QSettings()
settings.setValue('p_channel_{}'.format(action.text()), action.isChecked()) settings.setValue('p_channel_{}'.format(action.text()), action.isChecked())
def updateChannelSettingsS(self, action): @staticmethod
def updateChannelSettingsS(action):
settings = QSettings() settings = QSettings()
settings.setValue('s_channel_{}'.format(action.text()), action.isChecked()) settings.setValue('s_channel_{}'.format(action.text()), action.isChecked())
def getChannelSettingsP(self, channel): @staticmethod
def getChannelSettingsP(channel):
settings = QSettings() settings = QSettings()
rval = real_Bool(settings.value('p_channel_{}'.format(channel))) rval = real_Bool(settings.value('p_channel_{}'.format(channel)))
compclass = settings.value('compclass') compclass = settings.value('compclass')
@ -2648,7 +2807,8 @@ class PickDlg(QDialog):
rval = False rval = False
return rval return rval
def getChannelSettingsS(self, channel): @staticmethod
def getChannelSettingsS(channel):
settings = QSettings() settings = QSettings()
rval = real_Bool(settings.value('s_channel_{}'.format(channel))) rval = real_Bool(settings.value('s_channel_{}'.format(channel)))
compclass = settings.value('compclass') compclass = settings.value('compclass')
@ -2662,7 +2822,6 @@ class PickDlg(QDialog):
rval = False rval = False
return rval return rval
def resetPlot(self): def resetPlot(self):
self.resetZoom() self.resetZoom()
self.refreshPlot() self.refreshPlot()
@ -2701,8 +2860,8 @@ class PickDlg(QDialog):
# set channel labels # set channel labels
self.multicompfig.setYTickLabels(pos, labels) self.multicompfig.setYTickLabels(pos, labels)
#self.multicompfig.setXLims(ax, self.getXLims()) # self.multicompfig.setXLims(ax, self.getXLims())
#self.multicompfig.setYLims(ax, self.getYLims()) # self.multicompfig.setYLims(ax, self.getYLims())
def zoom(self): def zoom(self):
if self.zoomAction.isChecked() and self.pick_block: if self.zoomAction.isChecked() and self.pick_block:
@ -2764,7 +2923,7 @@ class CanvasWidget(QWidget):
''' '''
def __init__(self, parent, canvas): def __init__(self, parent, canvas):
QtGui.QWidget.__init__(self, parent)#, 1) QtGui.QWidget.__init__(self, parent) # , 1)
canvas = canvas canvas = canvas
self.main_layout = QtGui.QVBoxLayout() self.main_layout = QtGui.QVBoxLayout()
self.setLayout(self.main_layout) self.setLayout(self.main_layout)
@ -2777,6 +2936,7 @@ class MultiEventWidget(QWidget):
''' '''
''' '''
def __init__(self, options=None, parent=None, windowflag=1): def __init__(self, options=None, parent=None, windowflag=1):
QtGui.QWidget.__init__(self, parent, windowflag) QtGui.QWidget.__init__(self, parent, windowflag)
@ -2784,6 +2944,14 @@ class MultiEventWidget(QWidget):
self.setupUi() self.setupUi()
# set initial size # set initial size
self.resize(1280, 720) self.resize(1280, 720)
self.center()
def center(self):
fm = self.frameGeometry()
screen = QtGui.QApplication.desktop().screenNumber(QtGui.QApplication.desktop().cursor().pos())
centerPoint = QtGui.QApplication.desktop().screenGeometry(screen).center()
fm.moveCenter(centerPoint)
self.move(fm.topLeft())
def setupUi(self): def setupUi(self):
# init main layout # init main layout
@ -2836,7 +3004,7 @@ class MultiEventWidget(QWidget):
self.pb.setRange(0, 0) self.pb.setRange(0, 0)
self.pb.setVisible(False) self.pb.setVisible(False)
#space holder for progressbar # space holder for progressbar
self._pb_space = QtGui.QWidget() self._pb_space = QtGui.QWidget()
self.rb_layout.addWidget(self.start_button) self.rb_layout.addWidget(self.start_button)
@ -2854,7 +3022,7 @@ class MultiEventWidget(QWidget):
eventlist = func() eventlist = func()
if not type(eventlist) == list: if not type(eventlist) == list:
eventlist = [eventlist] eventlist = [eventlist]
tooltip='' tooltip = ''
for index, event in enumerate(eventlist): for index, event in enumerate(eventlist):
if not event: if not event:
continue continue
@ -2870,13 +3038,13 @@ class MultiEventWidget(QWidget):
for rb in self.rb_dict.values(): for rb in self.rb_dict.values():
if rb.isChecked(): if rb.isChecked():
check_events = (rb.toolTip() == 'No events for this selection') check_events = (rb.toolTip() == 'No events for this selection')
self.start_button.setEnabled(not(check_events)) self.start_button.setEnabled(not check_events)
def enable(self, bool): def enable(self, bool):
for rb in self.rb_dict.values(): for rb in self.rb_dict.values():
rb.setEnabled(bool) rb.setEnabled(bool)
self.start_button.setEnabled(bool) self.start_button.setEnabled(bool)
self.pb.setVisible(not(bool)) self.pb.setVisible(not bool)
self._pb_space.setVisible(bool) self._pb_space.setVisible(bool)
self.eventbox.setEnabled(bool) self.eventbox.setEnabled(bool)
self.button_clear.setEnabled(bool) self.button_clear.setEnabled(bool)
@ -2958,7 +3126,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():
del(widget) del widget
self.events2plot = {} self.events2plot = {}
self.eventbox.clear() self.eventbox.clear()
self.refresh_plot_tabs() self.refresh_plot_tabs()
@ -2986,6 +3154,14 @@ class CompareEventsWidget(MultiEventWidget):
self.connect_buttons() self.connect_buttons()
self.setWindowTitle('Compare events') self.setWindowTitle('Compare events')
self.set_main_stretch() self.set_main_stretch()
self.center()
def center(self):
fm = self.frameGeometry()
screen = QtGui.QApplication.desktop().screenNumber(QtGui.QApplication.desktop().cursor().pos())
centerPoint = QtGui.QApplication.desktop().screenGeometry(screen).center()
fm.moveCenter(centerPoint)
self.move(fm.topLeft())
def connect_buttons(self): def connect_buttons(self):
self.start_button.clicked.connect(self.run) self.start_button.clicked.connect(self.run)
@ -3062,6 +3238,7 @@ class TuneAutopicker(QWidget):
self.add_log() self.add_log()
self.set_stretch() self.set_stretch()
self.resize(1280, 720) self.resize(1280, 720)
self.center()
self._manual_pick_plots = [] self._manual_pick_plots = []
if hasattr(self.parent(), 'metadata'): if hasattr(self.parent(), 'metadata'):
self.metadata = self.parent().metadata self.metadata = self.parent().metadata
@ -3070,6 +3247,13 @@ class TuneAutopicker(QWidget):
# self.setWindowModality(QtCore.Qt.WindowModality.ApplicationModal) # self.setWindowModality(QtCore.Qt.WindowModality.ApplicationModal)
# self.setWindowFlags(self.windowFlags() | QtCore.Qt.WindowStaysOnTopHint) # self.setWindowFlags(self.windowFlags() | QtCore.Qt.WindowStaysOnTopHint)
def center(self):
fm = self.frameGeometry()
screen = QtGui.QApplication.desktop().screenNumber(QtGui.QApplication.desktop().cursor().pos())
centerPoint = QtGui.QApplication.desktop().screenGeometry(screen).center()
fm.moveCenter(centerPoint)
self.move(fm.topLeft())
def set_fig_dict(self, fig_dict): def set_fig_dict(self, fig_dict):
for key, value in fig_dict.items(): for key, value in fig_dict.items():
if key is not 'mainFig': if key is not 'mainFig':
@ -3147,7 +3331,7 @@ class TuneAutopicker(QWidget):
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
wfdat = remove_underscores(wfdat) #wfdat = remove_underscores(wfdat)
# rotate misaligned stations to ZNE # rotate misaligned stations to ZNE
# check for gaps and doubled channels # check for gaps and doubled channels
check4gaps(wfdat) check4gaps(wfdat)
@ -3161,7 +3345,7 @@ class TuneAutopicker(QWidget):
self.fill_figure_tabs() self.fill_figure_tabs()
def init_pbwidget(self): def init_pbwidget(self):
self.pb_widget = QtGui.QWidget() self.pb_widget = ProgressBarWidget()
def init_tab_names(self): def init_tab_names(self):
self.ptb_names = ['aicFig', 'slength', 'checkZ4s', 'refPpick', 'el_Ppick', 'fm_picker'] self.ptb_names = ['aicFig', 'slength', 'checkZ4s', 'refPpick', 'el_Ppick', 'fm_picker']
@ -3201,7 +3385,7 @@ class TuneAutopicker(QWidget):
return self.eventBox.currentText().split('/')[-1] return self.eventBox.currentText().split('/')[-1]
def get_current_event_fp(self): def get_current_event_fp(self):
return self.eventBox.currentText() return self.eventBox.currentText().split('*')[0]
def get_current_event_picks(self, station): def get_current_event_picks(self, station):
event = self.get_current_event() event = self.get_current_event()
@ -3220,7 +3404,8 @@ class TuneAutopicker(QWidget):
def get_current_station_id(self): def get_current_station_id(self):
return str(self.stationBox.currentText()) return str(self.stationBox.currentText())
def gen_tab_widget(self, name, canvas): @staticmethod
def gen_tab_widget(name, canvas):
widget = QtGui.QWidget() widget = QtGui.QWidget()
v_layout = QtGui.QVBoxLayout() v_layout = QtGui.QVBoxLayout()
v_layout.addWidget(canvas) v_layout.addWidget(canvas)
@ -3398,7 +3583,7 @@ class TuneAutopicker(QWidget):
if index == -1: if index == -1:
index += 1 index += 1
nevents = self.eventBox.model().rowCount() nevents = self.eventBox.model().rowCount()
path = self.eventBox.itemText(index) path = self.eventBox.itemText(index).split('*')[0]
if project.getEventFromPath(path).isTestEvent(): if project.getEventFromPath(path).isTestEvent():
for index in range(nevents): for index in range(nevents):
path = self.eventBox.itemText(index) path = self.eventBox.itemText(index)
@ -3491,7 +3676,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)
del(self.pdlg_widget) del self.pdlg_widget
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'):
@ -3546,10 +3731,18 @@ class PylotParaBox(QtGui.QWidget):
self.params_to_gui() self.params_to_gui()
self._toggle_advanced_settings() self._toggle_advanced_settings()
self.resize(720, 860) self.resize(720, 860)
self.center()
self.setWindowModality(QtCore.Qt.WindowModality.ApplicationModal) self.setWindowModality(QtCore.Qt.WindowModality.ApplicationModal)
self.accepted.connect(self.params_from_gui) self.accepted.connect(self.params_from_gui)
self.rejected.connect(self.params_to_gui) self.rejected.connect(self.params_to_gui)
def center(self):
fm = self.frameGeometry()
screen = QtGui.QApplication.desktop().screenNumber(QtGui.QApplication.desktop().cursor().pos())
centerPoint = QtGui.QApplication.desktop().screenGeometry(screen).center()
fm.moveCenter(centerPoint)
self.move(fm.topLeft())
def _init_sublayouts(self): def _init_sublayouts(self):
self._main_layout = QtGui.QVBoxLayout() self._main_layout = QtGui.QVBoxLayout()
self._advanced_layout = QtGui.QVBoxLayout() self._advanced_layout = QtGui.QVBoxLayout()
@ -3650,7 +3843,8 @@ class PylotParaBox(QtGui.QWidget):
grid.addWidget(box, index1, 2) grid.addWidget(box, index1, 2)
return grid return grid
def create_box(self, typ, tooltip): @staticmethod
def create_box(typ, tooltip):
if typ == str: if typ == str:
box = QtGui.QLineEdit() box = QtGui.QLineEdit()
elif typ == float: elif typ == float:
@ -3665,7 +3859,8 @@ class PylotParaBox(QtGui.QWidget):
raise TypeError('Unrecognized type {}'.format(typ)) raise TypeError('Unrecognized type {}'.format(typ))
return box return box
def create_multi_box(self, boxes, headline=None): @staticmethod
def create_multi_box(boxes, headline=None):
box = QtGui.QWidget() box = QtGui.QWidget()
gl = QtGui.QGridLayout() gl = QtGui.QGridLayout()
column = 0 column = 0
@ -3993,7 +4188,7 @@ class AutoPickDlg(QDialog):
self.gb.setLayout(self.jobLayout) self.gb.setLayout(self.jobLayout)
def exportParameter(self): def exportParameter(self):
self.parent().exportAllEvents() self.parent().exportEvents()
pylot_params = self.parent()._inputs pylot_params = self.parent()._inputs
self.addEvents2pp(pylot_params) self.addEvents2pp(pylot_params)
pylot_params.export2File(self.pp_export) pylot_params.export2File(self.pp_export)
@ -4366,7 +4561,8 @@ class PhasesTab(PropTab):
self.PphasesEdit.setText(p_phases) self.PphasesEdit.setText(p_phases)
self.SphasesEdit.setText(s_phases) self.SphasesEdit.setText(s_phases)
def sortPhases(self, phases): @staticmethod
def sortPhases(phases):
sorted_phases = {'P': [], sorted_phases = {'P': [],
'S': []} 'S': []}
for phase in phases: for phase in phases:
@ -4393,7 +4589,7 @@ class GraphicsTab(PropTab):
super(GraphicsTab, self).__init__(parent) super(GraphicsTab, self).__init__(parent)
self.pylot_mainwindow = parent._pylot_mainwindow self.pylot_mainwindow = parent._pylot_mainwindow
self.init_layout() self.init_layout()
self.add_pg_cb() #self.add_pg_cb()
self.add_nth_sample() self.add_nth_sample()
self.add_style_settings() self.add_style_settings()
self.setLayout(self.main_layout) self.setLayout(self.main_layout)
@ -4429,36 +4625,16 @@ class GraphicsTab(PropTab):
self.main_layout.addWidget(label, 1, 0) self.main_layout.addWidget(label, 1, 0)
self.main_layout.addWidget(self.spinbox_nth_sample, 1, 1) self.main_layout.addWidget(self.spinbox_nth_sample, 1, 1)
def add_pg_cb(self):
try:
import pyqtgraph as pg
pg = True
except:
pg = False
text = {True: 'Use pyqtgraphic library for plotting',
False: 'Cannot use library: pyqtgraphic not found on system'}
label = QLabel('PyQt graphic')
label.setToolTip(text[bool(pg)])
label.setEnabled(bool(pg))
self.checkbox_pg = QtGui.QCheckBox()
self.checkbox_pg.setEnabled(bool(pg))
self.checkbox_pg.setChecked(bool(pg))
self.main_layout.addWidget(label, 0, 0)
self.main_layout.addWidget(self.checkbox_pg, 0, 1)
def set_current_style(self): def set_current_style(self):
selected_style = self.style_cb.currentText() selected_style = self.style_cb.currentText()
self.pylot_mainwindow.set_style(selected_style) self.pylot_mainwindow.set_style(selected_style)
def getValues(self): def getValues(self):
values = {'nth_sample': self.spinbox_nth_sample.value(), values = {'nth_sample': self.spinbox_nth_sample.value()}
'pyqtgraphic': self.checkbox_pg.isChecked()}
return values return values
def resetValues(self, infile=None): def resetValues(self, infile=None):
values = {'nth_sample': self.spinbox_nth_sample.setValue(1), values = {'nth_sample': self.spinbox_nth_sample.setValue(1)}
'pyqtgraphic': self.checkbox_pg.setChecked(True)}
return values return values
@ -4607,7 +4783,8 @@ class LocalisationTab(PropTab):
self.rootlabel.setText("{0} root directory".format(curtool)) self.rootlabel.setText("{0} root directory".format(curtool))
self.binlabel.setText("{0} bin directory".format(curtool)) self.binlabel.setText("{0} bin directory".format(curtool))
def selectDirectory(self, edit): @staticmethod
def selectDirectory(edit):
selected_directory = QFileDialog.getExistingDirectory() selected_directory = QFileDialog.getExistingDirectory()
# check if string is empty # check if string is empty
if selected_directory: if selected_directory:
@ -4705,7 +4882,8 @@ class FilterOptionsDialog(QDialog):
'S': FilterOptions()} 'S': FilterOptions()}
self.setWindowTitle(titleString) self.setWindowTitle(titleString)
self.filterOptionWidgets = {'P': FilterOptionsWidget(self.filterOptions['P'], self.parent().getAutoFilteroptions('P')), self.filterOptionWidgets = {
'P': FilterOptionsWidget(self.filterOptions['P'], self.parent().getAutoFilteroptions('P')),
'S': FilterOptionsWidget(self.filterOptions['S'], self.parent().getAutoFilteroptions('S'))} 'S': FilterOptionsWidget(self.filterOptions['S'], self.parent().getAutoFilteroptions('S'))}
self.setupUi() self.setupUi()
self.updateUi() self.updateUi()
@ -4773,6 +4951,9 @@ class FilterOptionsDialog(QDialog):
def checkMinMax(self): def checkMinMax(self):
returnvals = [] returnvals = []
for foWidget in self.filterOptionWidgets.values(): for foWidget in self.filterOptionWidgets.values():
if foWidget.filterOptions._filtertype in ['highpass', 'lowpass']:
returnvals.append(True)
continue
returnvals.append(foWidget.checkMin()) returnvals.append(foWidget.checkMin())
returnvals.append(foWidget.checkMax()) returnvals.append(foWidget.checkMax())
if all(returnvals): if all(returnvals):
@ -4831,7 +5012,7 @@ class FilterOptionsWidget(QWidget):
# self.getFilterOptions().getFreq()[1]) # self.getFilterOptions().getFreq()[1])
# else: # else:
self.typeOptions = [None, "bandpass", "bandstop", "lowpass", "highpass"] self.typeOptions = ["bandpass", "bandstop", "lowpass", "highpass"]
self.resetButton = QPushButton('Reset') self.resetButton = QPushButton('Reset')
self.resetButton.setToolTip('Reset filter settings to settings for automatic picking.') self.resetButton.setToolTip('Reset filter settings to settings for automatic picking.')
@ -4900,6 +5081,8 @@ class FilterOptionsWidget(QWidget):
self.freqmaxSpinBox.valueChanged.connect(self.checkAutoManu) self.freqmaxSpinBox.valueChanged.connect(self.checkAutoManu)
self.checkAutoManu() self.checkAutoManu()
self.setWindowModality(QtCore.Qt.WindowModality.ApplicationModal)
def checkAutoManu(self): def checkAutoManu(self):
self.updateMFfromWidget() self.updateMFfromWidget()
@ -5054,7 +5237,7 @@ class HelpForm(QDialog):
toolBar.addWidget(self.pageLabel) toolBar.addWidget(self.pageLabel)
self.webBrowser = QWebView() self.webBrowser = QWebView()
self.webBrowser.load(page) self.webBrowser.load(page)
#self.webBrowser.load('C:/Shared/code/git/pylot/pylot/core/util/map_test.html') # self.webBrowser.load('C:/Shared/code/git/pylot/pylot/core/util/map_test.html')
layout = QVBoxLayout() layout = QVBoxLayout()
layout.addWidget(toolBar) layout.addWidget(toolBar)

View File

@ -5,18 +5,18 @@
# the base color # the base color
phasecolors = { phasecolors = {
'manual': { 'manual': {
'P':{ 'P': {
'rgba': (0, 0, 255, 255), 'rgba': (0, 0, 255, 255),
'modifier': 'g'}, 'modifier': 'g'},
'S':{ 'S': {
'rgba': (255, 0, 0, 255), 'rgba': (255, 0, 0, 255),
'modifier': 'b'} 'modifier': 'b'}
}, },
'auto':{ 'auto': {
'P':{ 'P': {
'rgba': (140, 0, 255, 255), 'rgba': (140, 0, 255, 255),
'modifier': 'g'}, 'modifier': 'g'},
'S':{ 'S': {
'rgba': (255, 140, 0, 255), 'rgba': (255, 140, 0, 255),
'modifier': 'b'} 'modifier': 'b'}
} }
@ -24,8 +24,8 @@ phasecolors = {
# Set plot colors and stylesheet for each style # Set plot colors and stylesheet for each style
stylecolors = { stylecolors = {
'default':{ 'default': {
'linecolor':{ 'linecolor': {
'rgba': (0, 0, 0, 255)}, 'rgba': (0, 0, 0, 255)},
'background': { 'background': {
'rgba': (255, 255, 255, 255)}, 'rgba': (255, 255, 255, 255)},
@ -67,4 +67,3 @@ stylecolors = {
'filename': 'bright.qss'} 'filename': 'bright.qss'}
} }
} }

View File

@ -8,7 +8,7 @@ setup(
packages=['pylot', 'pylot.core', 'pylot.core.loc', 'pylot.core.pick', packages=['pylot', 'pylot.core', 'pylot.core.loc', 'pylot.core.pick',
'pylot.core.io', 'pylot.core.util', 'pylot.core.active', 'pylot.core.io', 'pylot.core.util', 'pylot.core.active',
'pylot.core.analysis', 'pylot.testing'], 'pylot.core.analysis', 'pylot.testing'],
requires=['obspy', 'PySide', 'matplotlib', 'numpy'], requires=['obspy', 'PySide', 'matplotlib', 'numpy', 'scipy', 'pyqtgraph'],
url='dummy', url='dummy',
license='LGPLv3', license='LGPLv3',
author='Sebastian Wehling-Benatelli', author='Sebastian Wehling-Benatelli',

27
tests/__init__.py Normal file
View File

@ -0,0 +1,27 @@
# -*- coding: utf-8 -*-
# --------------------------------------------------------
# Purpose: Convience imports for PyLoT
#
'''
================================================
PyLoT - the Python picking and Localization Tool
================================================
This python library contains a graphical user interfaces for picking
seismic phases. This software needs ObsPy (http://github.com/obspy/obspy/wiki)
and the Qt4 libraries to be installed first.
PILOT has been developed in Mathworks' MatLab. In order to distribute
PILOT without facing portability problems, it has been decided to re-
develop the software package in Python. The great work of the ObsPy
group allows easy handling of a bunch of seismic data and PyLoT will
benefit a lot compared to the former MatLab version.
The development of PyLoT is part of the joint research project MAGS2.
:copyright:
The PyLoT Development Team
:license:
GNU Lesser General Public License, Version 3
(http://www.gnu.org/copyleft/lesser.html)
'''

View File

@ -1,32 +1,59 @@
import unittest import unittest
import os import os
from obspy import UTCDateTime
from obspy.io.xseed.utils import SEEDParserException
from obspy.io.xseed import Parser
from pylot.core.util.dataprocessing import Metadata from pylot.core.util.dataprocessing import Metadata
from tests.utils import HidePrints
class TestMetadata(unittest.TestCase): class TestMetadata(unittest.TestCase):
def setUp(self): def setUp(self):
self.station_id = 'BW.WETR..HH' self.station_id = 'BW.WETR..HH'
metadata_folder = 'metadata1' self.time = UTCDateTime('2012-08-01')
metadata_folder = os.path.join('test_data', 'dless_multiple_files', 'metadata1')
self.m = Metadata(metadata_folder) self.m = Metadata(metadata_folder)
def test_get_coordinates_sucess(self): def test_get_coordinates_sucess(self):
expected = {'Z': {u'elevation': 607.0, u'longitude': 12.87571, u'local_depth': 0.0, u'azimuth': 0.0, u'latitude': 49.14502, u'dip': -90.0}, expected = {'Z': {u'elevation': 607.0, u'longitude': 12.87571, u'local_depth': 0.0, u'azimuth': 0.0,
'E': {u'azimuth': 90.0, u'dip': 0.0, u'elevation': 607.0, u'latitude': 49.14502, u'local_depth': 0.0, u'longitude': 12.87571}, u'latitude': 49.14502, u'dip': -90.0},
'N': {u'azimuth': 0.0, u'dip': 0.0, u'elevation': 607.0, u'latitude': 49.14502, u'local_depth': 0.0, u'longitude': 12.87571} 'E': {u'azimuth': 90.0, u'dip': 0.0, u'elevation': 607.0, u'latitude': 49.14502,
u'local_depth': 0.0, u'longitude': 12.87571},
'N': {u'azimuth': 0.0, u'dip': 0.0, u'elevation': 607.0, u'latitude': 49.14502, u'local_depth': 0.0,
u'longitude': 12.87571}
} }
result = {} result = {}
for channel in ('Z', 'N', 'E'): for channel in ('Z', 'N', 'E'):
with HidePrints():
coords = self.m.get_coordinates(self.station_id+channel, time=self.time)
result[channel] = coords
self.assertDictEqual(result[channel], expected[channel])
def test_get_coordinates_sucess_no_time(self):
expected = {'Z': {u'elevation': 607.0, u'longitude': 12.87571, u'local_depth': 0.0, u'azimuth': 0.0,
u'latitude': 49.14502, u'dip': -90.0},
'E': {u'azimuth': 90.0, u'dip': 0.0, u'elevation': 607.0, u'latitude': 49.14502,
u'local_depth': 0.0, u'longitude': 12.87571},
'N': {u'azimuth': 0.0, u'dip': 0.0, u'elevation': 607.0, u'latitude': 49.14502, u'local_depth': 0.0,
u'longitude': 12.87571}
}
result = {}
for channel in ('Z', 'N', 'E'):
with HidePrints():
coords = self.m.get_coordinates(self.station_id+channel) coords = self.m.get_coordinates(self.station_id+channel)
result[channel] = coords result[channel] = coords
self.assertDictEqual(result[channel], expected[channel]) self.assertDictEqual(result[channel], expected[channel])
class TestMetadataAdding(unittest.TestCase): class TestMetadataAdding(unittest.TestCase):
"""Tests if adding files and directories to a metadata object works.""" """Tests if adding files and directories to a metadata object works."""
def setUp(self): def setUp(self):
self.station_id = 'BW.WETR..HH' self.station_id = 'BW.WETR..HH'
self.metadata_folders = ('metadata1', 'metadata2') self.metadata_folders = (os.path.join('test_data', 'dless_multiple_files', 'metadata1'),
os.path.join('test_data', 'dless_multiple_files', 'metadata2'))
self.m = Metadata() self.m = Metadata()
def test_add_inventory_folder(self): def test_add_inventory_folder(self):
@ -43,8 +70,10 @@ class TestMetadataAdding(unittest.TestCase):
fpath = os.path.join(self.metadata_folders[0], 'DATALESS.BW.WETR..HHZ') fpath = os.path.join(self.metadata_folders[0], 'DATALESS.BW.WETR..HHZ')
self.m.add_inventory_file(fpath) self.m.add_inventory_file(fpath)
# adding an inventory file should append its folder to the list of inventories and the file to the # adding an inventory file should append its folder to the list of inventories and the file to the
self.assertEqual(['metadata1/DATALESS.BW.WETR..HHZ'], self.m.inventory_files.keys()) # does the filename exist in inventory files? self.assertEqual([os.path.join(self.metadata_folders[0], 'DATALESS.BW.WETR..HHZ')],
self.assertEqual(['data', 'invtype'], self.m.inventory_files['metadata1/DATALESS.BW.WETR..HHZ'].keys()) # is the required information attacht to the filename? self.m.inventory_files.keys()) # does the filename exist in inventory files?
self.assertEqual(['data', 'invtype'], self.m.inventory_files[os.path.join(self.metadata_folders[0],
'DATALESS.BW.WETR..HHZ')].keys()) # is the required information attacht to the filename?
self.assertDictEqual({}, self.m.seed_ids) self.assertDictEqual({}, self.m.seed_ids)
self.assertEqual([self.metadata_folders[0]], self.m.inventories) self.assertEqual([self.metadata_folders[0]], self.m.inventories)
@ -66,7 +95,8 @@ class TestMetadataRemoval(unittest.TestCase):
def setUp(self): def setUp(self):
self.station_id = 'BW.WETR..HH' self.station_id = 'BW.WETR..HH'
self.metadata_folders = ('metadata1', 'metadata2') self.metadata_folders = (os.path.join('test_data', 'dless_multiple_files', 'metadata1'),
os.path.join('test_data', 'dless_multiple_files', 'metadata2'))
self.m = Metadata() self.m = Metadata()
def test_remove_all_inventories(self): def test_remove_all_inventories(self):
@ -93,6 +123,7 @@ class TestMetadataRemoval(unittest.TestCase):
exist in the instance.""" exist in the instance."""
# add multiple inventories # add multiple inventories
self.m.add_inventory(self.metadata_folders[0]) self.m.add_inventory(self.metadata_folders[0])
with HidePrints():
self.m.remove_inventory('metadata_not_existing') self.m.remove_inventory('metadata_not_existing')
self.assertIn(self.metadata_folders[0], self.m.inventories) self.assertIn(self.metadata_folders[0], self.m.inventories)
@ -102,3 +133,202 @@ class TestMetadataRemoval(unittest.TestCase):
self.assertDictEqual({}, metadata.seed_ids) self.assertDictEqual({}, metadata.seed_ids)
self.assertEqual([], metadata.inventories) self.assertEqual([], metadata.inventories)
class TestMetadata_read_single_file(unittest.TestCase):
def setUp(self):
self.station_id = 'BW.WETR..HHZ'
self.metadata_folders = (os.path.join('test_data', 'dless_multiple_files', 'metadata1'),
os.path.join('test_data', 'dless_multiple_files', 'metadata2'))
self.metadata_paths = []
self.m = Metadata()
def test_read_single_file(self):
"""Test if reading a single file works"""
fname = os.path.join(self.metadata_folders[0], 'DATALESS.'+self.station_id)
with HidePrints():
res = self.m.read_single_file(fname)
# method should return true if file is successfully read
self.assertTrue(res)
# list of inventories (folders) should be empty
self.assertEqual([], self.m.inventories)
# list of inventory files should contain the added file
self.assertIn(fname, self.m.inventory_files.keys())
self.assertEqual({}, self.m.seed_ids)
def test_read_single_file_invalid_path(self):
"""Test if reading from a non existing file fails. The filename should not be
added to the metadata object"""
fname = os.path.join("this", "path", "doesnt", "exist")
with HidePrints():
res = self.m.read_single_file(fname)
# method should return None if file reading fails
self.assertIsNone(res)
# list of inventories (folders) should be empty
self.assertEqual([], self.m.inventories)
# list of inventory files should not contain the added file
self.assertNotIn(fname, self.m.inventory_files.keys())
self.assertEqual({}, self.m.seed_ids)
def test_read_single_file_multiple_times(self):
"""Test if reading a file twice doesnt add it twice to the metadata object"""
fname = os.path.join(self.metadata_folders[0], 'DATALESS.'+self.station_id)
with HidePrints():
res1 = self.m.read_single_file(fname)
res2 = self.m.read_single_file(fname)
self.assertTrue(res1)
self.assertIsNone(res2)
self.assertItemsEqual([fname], self.m.inventory_files.keys())
class TestMetadataMultipleTime(unittest.TestCase):
"""Test if stations with multiple metadata entries in a single file are handled correctly.
The user must specify the time where he wants to get metadata.
The station ROTT changed has metadata available at multiple times
LE.ROTT..HNE | 200.00 Hz | Titan 4g-EDR-209, Very Low gain, 200 sps | 2015-01-08 - 2015-03-19 | Lat: 49.1, Lng: 8.1
LE.ROTT..HNE | 200.00 Hz | Titan 4g-EDR-209, Very Low gain, 200 sps | 2015-03-19 - | Lat: 49.1, Lng: 8.1
LE.ROTT..HNN | 200.00 Hz | Titan 4g-EDR-209, Very Low gain, 200 sps | 2015-01-08 - 2015-03-19 | Lat: 49.1, Lng: 8.1
LE.ROTT..HNN | 200.00 Hz | Titan 4g-EDR-209, Very Low gain, 200 sps | 2015-03-19 - | Lat: 49.1, Lng: 8.1
LE.ROTT..HNZ | 200.00 Hz | Titan 4g-EDR-209, Very Low gain, 200 sps | 2015-01-08 - 2015-03-19 | Lat: 49.1, Lng: 8.1
LE.ROTT..HNZ | 200.00 Hz | Titan 4g-EDR-209, Very Low gain, 200 sps | 2015-03-19 - | Lat: 49.1, Lng: 8.1
"""
def setUp(self):
self.seed_id = 'LE.ROTT..HN'
path = os.path.dirname(__file__) # gets path to currently running script
metadata = os.path.join('test_data', 'dless_multiple_times', 'MAGS2_LE_ROTT.dless') # specific subfolder of test data
metadata_path = os.path.join(path, metadata)
self.m = Metadata(metadata_path)
self.p = Parser(metadata_path)
def test_get_metadata_works_without_datetime(self):
"""Test if get_metadata works if multiple metadata entries are available but no time is
specified."""
for channel in ('Z', 'N', 'E'):
with HidePrints():
md = self.m.get_metadata(self.seed_id + channel)
self.assertDictEqual(md['data'].get_inventory(), self.p.get_inventory())
def test_get_metadata_works_with_first_datetime(self):
"""Test if get_metadata works if multiple metadata entries are available and the older time is specified."""
t = UTCDateTime('2015-02-08')
for channel in ('Z', 'N', 'E'):
with HidePrints():
md = self.m.get_metadata(self.seed_id + channel, t)
self.assertDictEqual(md['data'].get_inventory(), self.p.get_inventory())
def test_get_metadata_fails_when_time_before_starttime(self):
"""Tests if get_metadata returns None when given a data that is before the start date
of the metadata"""
with HidePrints():
md = self.m.get_metadata(self.seed_id, UTCDateTime('1960-07-20'))
self.assertIs(md, None)
def test_get_metadata_invalid_seed_id(self):
"""Tes if get metadata returns none when asked for a seed id that does not exist"""
with HidePrints():
res = self.m.get_metadata("this.doesnt..exist")
self.assertIsNone(res)
class TestMetadataMultipleEntries(unittest.TestCase):
"""
The station KB.TMO07 has changed instruments multiple times.
Networks:
KB (KB network)
Stations:
KB.TMO07 (Karlsruhe GPI)
Channels:
KB.TMO07.00.BHE | 50.00 Hz | Streckeisen KABBA-STS-2 | 2004-12-06 - 2005-04-18 | Lat: 49.0, Lng: 8.4
KB.TMO07.00.BHE | 50.00 Hz | Streckeisen KABBA-STS-2 | 2005-04-18 - 2006-07-18 | Lat: 49.0, Lng: 8.4
KB.TMO07.00.BHE | 50.00 Hz | Lennartz KABBA-LE-3D/5 | 2006-10-10 - 2006-11-14 | Lat: 49.0, Lng: 8.4
KB.TMO07.00.BHE | 50.00 Hz | Lennartz KABBA-LE-3D/5 | 2006-11-24 - 2007-01-12 | Lat: 49.0, Lng: 8.4
KB.TMO07.00.BHE | 50.00 Hz | Lennartz KABBA-LE-3D/5 | 2007-01-18 - 2007-03-15 | Lat: 49.0, Lng: 8.4
KB.TMO07.00.BHE | 50.00 Hz | Lennartz KABBA-LE-3D/5 | 2007-10-25 - 2007-11-21 | Lat: 49.0, Lng: 8.4
KB.TMO07.00.BHE | 50.00 Hz | Lennartz KABBA-LE-3D/5 | 2007-11-21 - 2008-01-17 | Lat: 49.0, Lng: 8.4
KB.TMO07.00.BHN | 50.00 Hz | Streckeisen KABBA-STS-2 | 2004-12-06 - 2005-04-18 | Lat: 49.0, Lng: 8.4
KB.TMO07.00.BHN | 50.00 Hz | Streckeisen KABBA-STS-2 | 2005-04-18 - 2006-07-18 | Lat: 49.0, Lng: 8.4
KB.TMO07.00.BHN | 50.00 Hz | Lennartz KABBA-LE-3D/5 | 2006-10-10 - 2006-11-14 | Lat: 49.0, Lng: 8.4
KB.TMO07.00.BHN | 50.00 Hz | Lennartz KABBA-LE-3D/5 | 2006-11-24 - 2007-01-12 | Lat: 49.0, Lng: 8.4
KB.TMO07.00.BHN | 50.00 Hz | Lennartz KABBA-LE-3D/5 | 2007-01-18 - 2007-03-15 | Lat: 49.0, Lng: 8.4
KB.TMO07.00.BHN | 50.00 Hz | Lennartz KABBA-LE-3D/5 | 2007-10-25 - 2007-11-21 | Lat: 49.0, Lng: 8.4
KB.TMO07.00.BHN | 50.00 Hz | Lennartz KABBA-LE-3D/5 | 2007-11-21 - 2008-01-17 | Lat: 49.0, Lng: 8.4
KB.TMO07.00.BHZ | 50.00 Hz | Streckeisen KABBA-STS-2 | 2004-12-06 - 2005-04-18 | Lat: 49.0, Lng: 8.4
KB.TMO07.00.BHZ | 50.00 Hz | Streckeisen KABBA-STS-2 | 2005-04-18 - 2006-07-18 | Lat: 49.0, Lng: 8.4
KB.TMO07.00.BHZ | 50.00 Hz | Lennartz KABBA-LE-3D/5 | 2006-10-10 - 2006-11-14 | Lat: 49.0, Lng: 8.4
KB.TMO07.00.BHZ | 50.00 Hz | Lennartz KABBA-LE-3D/5 | 2006-11-24 - 2007-01-12 | Lat: 49.0, Lng: 8.4
KB.TMO07.00.BHZ | 50.00 Hz | Lennartz KABBA-LE-3D/5 | 2007-01-18 - 2007-03-15 | Lat: 49.0, Lng: 8.4
KB.TMO07.00.BHZ | 50.00 Hz | Lennartz KABBA-LE-3D/5 | 2007-10-25 - 2007-11-21 | Lat: 49.0, Lng: 8.4
KB.TMO07.00.BHZ | 50.00 Hz | Lennartz KABBA-LE-3D/5 | 2007-11-21 - 2008-01-17 | Lat: 49.0, Lng: 8.4
KB.TMO07.00.HHE | 100.00 Hz | Lennartz KABBA-LE-3D/5 | 2007-01-12 - 2007-01-18 | Lat: 49.0, Lng: 8.4
KB.TMO07.00.HHE | 100.00 Hz | Lennartz KABBA-LE-3D/5 | 2007-10-10 - 2007-10-25 | Lat: 49.0, Lng: 8.4
KB.TMO07.00.HHE | 100.00 Hz | Streckeisen KABBA-STS-2 | 2008-07-11 - 2008-12-05 | Lat: 49.0, Lng: 8.4
KB.TMO07.00.HHE | 100.00 Hz | Streckeisen KABBA-STS-2 | 2009-05-12 - 2010-02-15 | Lat: 49.0, Lng: 8.4
KB.TMO07.00.HHE | 100.00 Hz | Streckeisen KABBA-STS-2 | 2010-02-15 - 2010-04-07 | Lat: 49.0, Lng: 8.4
KB.TMO07.00.HHE | 100.00 Hz | Lennartz KABBA-LE-3D/1 | 2010-04-07 - 2010-08-03 | Lat: 49.0, Lng: 8.4
KB.TMO07.00.HHE | 200.00 Hz | Streckeisen KABBA-STS-2 | 2010-08-05 - 2010-12-20 | Lat: 49.0, Lng: 8.4
KB.TMO07.00.HHE | 100.00 Hz | Streckeisen KABBA-STS-2 | 2010-12-20 - 2010-12-22 | Lat: 49.0, Lng: 8.4
KB.TMO07.00.HHE | 200.00 Hz | Streckeisen KABBA-STS-2 | 2010-12-22 - 2011-04-02 | Lat: 49.0, Lng: 8.4
KB.TMO07.00.HHE | 200.00 Hz | Streckeisen KABBA-STS-2 | 2011-04-15 - 2012-05-07 | Lat: 49.0, Lng: 8.4
KB.TMO07.00.HHE | 200.00 Hz | Streckeisen KABBA-STS-2 | 2012-05-07 - | Lat: 49.0, Lng: 8.4
KB.TMO07.00.HHN | 100.00 Hz | Lennartz KABBA-LE-3D/5 | 2007-01-12 - 2007-01-18 | Lat: 49.0, Lng: 8.4
KB.TMO07.00.HHN | 100.00 Hz | Lennartz KABBA-LE-3D/5 | 2007-10-10 - 2007-10-25 | Lat: 49.0, Lng: 8.4
KB.TMO07.00.HHN | 100.00 Hz | Streckeisen KABBA-STS-2 | 2008-07-11 - 2008-12-05 | Lat: 49.0, Lng: 8.4
KB.TMO07.00.HHN | 100.00 Hz | Streckeisen KABBA-STS-2 | 2009-05-12 - 2010-02-15 | Lat: 49.0, Lng: 8.4
KB.TMO07.00.HHN | 100.00 Hz | Streckeisen KABBA-STS-2 | 2010-02-15 - 2010-04-07 | Lat: 49.0, Lng: 8.4
KB.TMO07.00.HHN | 100.00 Hz | Lennartz KABBA-LE-3D/1 | 2010-04-07 - 2010-08-03 | Lat: 49.0, Lng: 8.4
KB.TMO07.00.HHN | 200.00 Hz | Streckeisen KABBA-STS-2 | 2010-08-05 - 2010-12-20 | Lat: 49.0, Lng: 8.4
KB.TMO07.00.HHN | 100.00 Hz | Streckeisen KABBA-STS-2 | 2010-12-20 - 2010-12-22 | Lat: 49.0, Lng: 8.4
KB.TMO07.00.HHN | 200.00 Hz | Streckeisen KABBA-STS-2 | 2010-12-22 - 2011-04-02 | Lat: 49.0, Lng: 8.4
KB.TMO07.00.HHN | 200.00 Hz | Streckeisen KABBA-STS-2 | 2011-04-15 - 2012-05-07 | Lat: 49.0, Lng: 8.4
KB.TMO07.00.HHN | 200.00 Hz | Streckeisen KABBA-STS-2 | 2012-05-07 - | Lat: 49.0, Lng: 8.4
KB.TMO07.00.HHZ | 100.00 Hz | Lennartz KABBA-LE-3D/5 | 2007-01-12 - 2007-01-18 | Lat: 49.0, Lng: 8.4
KB.TMO07.00.HHZ | 100.00 Hz | Lennartz KABBA-LE-3D/5 | 2007-10-10 - 2007-10-25 | Lat: 49.0, Lng: 8.4
KB.TMO07.00.HHZ | 100.00 Hz | Streckeisen KABBA-STS-2 | 2008-07-11 - 2008-12-05 | Lat: 49.0, Lng: 8.4
KB.TMO07.00.HHZ | 100.00 Hz | Streckeisen KABBA-STS-2 | 2009-05-12 - 2010-02-15 | Lat: 49.0, Lng: 8.4
KB.TMO07.00.HHZ | 100.00 Hz | Streckeisen KABBA-STS-2 | 2010-02-15 - 2010-04-07 | Lat: 49.0, Lng: 8.4
KB.TMO07.00.HHZ | 100.00 Hz | Lennartz KABBA-LE-3D/1 | 2010-04-07 - 2010-08-03 | Lat: 49.0, Lng: 8.4
KB.TMO07.00.HHZ | 200.00 Hz | Streckeisen KABBA-STS-2 | 2010-08-05 - 2010-12-20 | Lat: 49.0, Lng: 8.4
KB.TMO07.00.HHZ | 100.00 Hz | Streckeisen KABBA-STS-2 | 2010-12-20 - 2010-12-22 | Lat: 49.0, Lng: 8.4
KB.TMO07.00.HHZ | 200.00 Hz | Streckeisen KABBA-STS-2 | 2010-12-22 - 2011-04-02 | Lat: 49.0, Lng: 8.4
KB.TMO07.00.HHZ | 200.00 Hz | Streckeisen KABBA-STS-2 | 2011-04-15 - 2012-05-07 | Lat: 49.0, Lng: 8.4
KB.TMO07.00.HHZ | 200.00 Hz | Streckeisen KABBA-STS-2 | 2012-05-07 - | Lat: 49.0, Lng: 8.4
"""
def setUp(self):
self.seed_id = 'KB.TMO07.00.HHZ'
path = os.path.dirname(__file__) # gets path to currently running script
metadata = os.path.join('test_data', 'dless_multiple_instruments', 'MAGS2_KB_TMO07.dless') # specific subfolder of test data
metadata_path = os.path.join(path, metadata)
self.m = Metadata(metadata_path)
self.p = Parser(metadata_path)
def test_get_paz_current_time(self):
"""Test if getting the paz from the metadata object with the current time works"""
t = UTCDateTime()
with HidePrints():
pazm = self.m.get_paz(self.seed_id, t)
pazp = self.p.get_paz(self.seed_id, t)
self.assertEqual(pazm, pazp)
def test_get_paz_past(self):
"""Test if getting paz from metadata object with a time in the past works"""
t = UTCDateTime('2007-01-13')
with HidePrints():
pazm = self.m.get_paz(self.seed_id, t)
pazp = self.p.get_paz(self.seed_id, t)
self.assertEqual(pazm, pazp)
def test_get_paz_time_not_exisiting(self):
"""Test if getting paz from metadata at a time where there is no metadata
available fails correctly"""
with self.assertRaises(SEEDParserException):
with HidePrints():
self.m.get_paz(self.seed_id, UTCDateTime('1990-1-1'))
def test_get_paz_seed_id_not_existing(self):
"""Test if getting paz from a non existing seed id returns None as expected."""
with HidePrints():
res = self.m.get_paz('This.doesnt..exist', UTCDateTime)
self.assertIsNone(res)

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

52
tests/utils.py Normal file
View File

@ -0,0 +1,52 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""Utilities/helpers for testing"""
import sys
import os
class HidePrints:
"""
Context manager that hides all standard output within its body.
The optional hide_prints argument can be used to quickly enable printing during debugging of tests.
Use (This will result in all console output of noisy_function to be suppressed):
from tests.utils import HidePrints
with HidePrints():
noise_function()
"""
@staticmethod
def hide(func, *args, **kwargs):
"""Decorator that hides all prints of the decorated function.
Use:
from tests.utils import HidePrints
@HidePrints.hide
def noise()
print("NOISE")
"""
def silencer(*args, **kwargs):
with HidePrints():
func(*args, **kwargs)
return silencer
def __init__(self, hide_prints=True):
"""Create object with hide_prints=False to disable print hiding"""
self.hide = hide_prints
def __enter__(self):
"""Redirect stdout to /dev/null, save old stdout"""
if self.hide:
self._original_stdout = sys.stdout
devnull = open(os.devnull, "w")
sys.stdout = devnull
def __exit__(self, exc_type, exc_val, exc_tb):
"""Reinstate old stdout"""
if self.hide:
sys.stdout = self._original_stdout