Merge branch 'develop' into feature/refactor

This commit is contained in:
Darius Arnold 2018-04-06 13:53:04 +02:00
commit 1edc745903
30 changed files with 122600 additions and 105397 deletions

3
.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
*.pyc
*~
pylot/RELEASE-VERSION

271
PyLoT.py
View File

@ -75,7 +75,7 @@ from pylot.core.util.dataprocessing import read_metadata, restitute_data
from pylot.core.util.utils import fnConstructor, getLogin, \
full_range, readFilterInformation, trim_station_components, check4gaps, make_pen, pick_color_plt, \
pick_linestyle_plt, remove_underscores, check4doubled, identifyPhaseID, excludeQualityClasses, has_spe, \
check4rotated, transform_colors_mpl, transform_colors_mpl_str
check4rotated, transform_colors_mpl, transform_colors_mpl_str, getAutoFilteroptions
from pylot.core.util.event import Event
from pylot.core.io.location import create_creation_info, create_event
from pylot.core.util.widgets import FilterOptionsDialog, NewEventDlg, \
@ -182,25 +182,22 @@ class MainWindow(QMainWindow):
settings.setValue("data/dataRoot", dirname)
if settings.value('compclass', None) is None:
settings.setValue('compclass', SetChannelComponents())
if settings.value('useGuiFilter') is None:
settings.setValue('useGuiFilter', False)
if settings.value('output/Format', None) is None:
outformat = QInputDialog.getText(self,
"Enter output format (*.xml, *.cnv, *.obs):",
"Format")
settings.setValue("output/Format", outformat)
if settings.value('autoFilter', None) is None:
settings.setValue('autoFilter', True)
settings.sync()
# setup UI
self.setupUi()
filter_info = readFilterInformation(self._inputs)
p_filter = filter_info['P']
s_filter = filter_info['S']
self.filteroptions = {'P': FilterOptions(p_filter['filtertype'],
p_filter['freq'],
p_filter['order']),
'S': FilterOptions(s_filter['filtertype'],
s_filter['freq'],
s_filter['order'])}
self.updateFilteroptions()
self.loc = False
def setupUi(self):
@ -270,8 +267,12 @@ class MainWindow(QMainWindow):
s_icon.addPixmap(QPixmap(':/icons/key_S.png'))
print_icon = QIcon()
print_icon.addPixmap(QPixmap(':/icons/printer.png'))
filter_icon = QIcon()
filter_icon.addPixmap(QPixmap(':/icons/filter.png'))
self.filter_icon = QIcon()
self.filter_icon.addPixmap(QPixmap(':/icons/filter.png'))
self.filter_icon_p = QIcon()
self.filter_icon_p.addPixmap(QPixmap(':/icons/filter_p.png'))
self.filter_icon_s = QIcon()
self.filter_icon_s.addPixmap(QPixmap(':/icons/filter_s.png'))
z_icon = QIcon()
z_icon.addPixmap(QPixmap(':/icons/key_Z.png'))
n_icon = QIcon()
@ -286,6 +287,8 @@ class MainWindow(QMainWindow):
locate_icon.addPixmap(QPixmap(':/icons/locate_button.png'))
compare_icon = QIcon()
compare_icon.addPixmap(QPixmap(':/icons/compare_button.png'))
qualities_icon = QIcon()
qualities_icon.addPixmap(QPixmap(':/icons/pick_qualities_button.png'))
self.newProjectAction = self.createAction(self, "&New project ...",
self.createNewProject,
QKeySequence.New, newIcon,
@ -366,15 +369,23 @@ class MainWindow(QMainWindow):
self.setParameter,
None, paraIcon,
"Modify Parameter")
self.filterAction = self.createAction(self, "&Filter ...",
self.filterWaveformData,
"Ctrl+F", filter_icon,
"""Toggle un-/filtered waveforms
to be displayed, according to the
desired seismic phase.""", True)
self.filterActionP = createAction(parent=self, text='Apply P Filter',
slot=self.filterP,
icon=self.filter_icon_p,
tip='Toggle filtered/original'
' waveforms',
checkable=True,
shortcut='P')
self.filterActionS = createAction(parent=self, text='Apply S Filter',
slot=self.filterS,
icon=self.filter_icon_s,
tip='Toggle filtered/original'
' waveforms',
checkable=True,
shortcut='S')
filterEditAction = self.createAction(self, "&Filter parameter ...",
self.adjustFilterOptions,
"Alt+F", filter_icon,
"Ctrl+F", self.filter_icon,
"""Adjust filter parameters.""")
self.inventoryAction = self.createAction(self, "Select &Inventory ...",
self.get_new_metadata,
@ -402,6 +413,10 @@ class MainWindow(QMainWindow):
"automatic pick "
"data.", False)
self.compare_action.setEnabled(False)
self.qualities_action = self.createAction(parent=self, text='Show pick qualitites...',
slot=self.pickQualities, shortcut='Alt+Q',
icon=qualities_icon, tip='Histogram of pick qualities')
self.qualities_action.setEnabled(False)
printAction = self.createAction(self, "&Print event ...",
self.show_event_information, QKeySequence.Print,
@ -472,7 +487,7 @@ class MainWindow(QMainWindow):
' the complete project on grid engine.')
self.auto_pick_sge.setEnabled(False)
pickActions = (self.auto_tune, self.auto_pick, self.compare_action)
pickActions = (self.auto_tune, self.auto_pick, self.compare_action, self.qualities_action)
# pickToolBar = self.addToolBar("PickTools")
# pickToolActions = (selectStation, )
@ -500,8 +515,8 @@ class MainWindow(QMainWindow):
self.updateFileMenu()
self.editMenu = self.menuBar().addMenu('&Edit')
editActions = (self.filterAction, filterEditAction, None,
self.selectPAction, self.selectSAction, None,
editActions = (self.filterActionP, self.filterActionS, filterEditAction, None,
#self.selectPAction, self.selectSAction, None,
self.inventoryAction, self.initMapAction, None,
prefsEventAction)
#printAction) #TODO: print event?
@ -561,20 +576,29 @@ class MainWindow(QMainWindow):
style = settings.value('style')
self.set_style(style)
# add event combo box and ref/test buttons
# add event combo box, forward, backward and ref/test buttons
self.eventBox = self.createEventBox()
self.eventBox.setMaxVisibleItems(30)
self.eventBox.setEnabled(False)
self.previous_button = QPushButton('<')
self.next_button = QPushButton('>')
self.init_ref_test_buttons()
self._event_layout = QHBoxLayout()
self._event_layout.addWidget(QLabel('Event: '))
self._event_layout.addWidget(self.eventBox)
self._event_layout.addWidget(self.previous_button)
self._event_layout.addWidget(self.next_button)
self._event_layout.addWidget(self.ref_event_button)
self._event_layout.addWidget(self.test_event_button)
self._event_layout.setStretch(1, 1) # set stretch of item 1 to 1
self._main_layout.addLayout(self._event_layout)
self.eventBox.activated.connect(self.refreshEvents)
self.previous_button.clicked.connect(self.previous_event)
self.next_button.clicked.connect(self.next_event)
self.previous_button.setEnabled(False)
self.next_button.setEnabled(False)
# add main tab widget
self.tabs = QTabWidget(self)
self._main_layout.addWidget(self.tabs)
@ -650,6 +674,8 @@ class MainWindow(QMainWindow):
'''
self.ref_event_button = QtGui.QPushButton('Tune')
self.test_event_button = QtGui.QPushButton('Test')
self.ref_event_button.setMinimumWidth(100)
self.test_event_button.setMinimumWidth(100)
self.ref_event_button.setToolTip('Set manual picks of current ' +
'event as reference picks for autopicker tuning.')
self.test_event_button.setToolTip('Set manual picks of current ' +
@ -763,6 +789,17 @@ class MainWindow(QMainWindow):
def metadata(self, value):
self._metadata = value
def updateFilteroptions(self):
filter_info = readFilterInformation(self._inputs)
p_filter = filter_info['P']
s_filter = filter_info['S']
self.filteroptions = {'P': FilterOptions(p_filter['filtertype'],
p_filter['freq'],
p_filter['order']),
'S': FilterOptions(s_filter['filtertype'],
s_filter['freq'],
s_filter['order'])}
def updateFileMenu(self):
self.fileMenu.clear()
@ -1004,7 +1041,8 @@ class MainWindow(QMainWindow):
and len(item.split('/')[-1].split('.')) == 3
and len(item.split('/')[-1]) == 12]
if not eventlist:
print('No events found! Expected structure for event folders: [evID.DOY.YR]')
print('No events found! Expected structure for event folders: [eEVID.DOY.YR],\n'
' e.g. eventID=1, doy=2, yr=2016: e0001.002.16')
return
else:
return
@ -1100,9 +1138,16 @@ class MainWindow(QMainWindow):
Initiate GUI widgets in case of changed or newly added events.
'''
nitems = self.eventBox.count()
if len(self.project.eventlist) == 0:
is_data = self.data_check()
if len(self.project.eventlist) == 0 or not is_data:
print('No events to init.')
self.clearWaveformDataPlot()
if not is_data:
new_path, modifypath = self.user_modify_path('Event folders not found! ')
if modifypath:
self.modify_project_path(new_path)
self.init_events(True)
self.setDirty(True)
return
self.eventBox.setEnabled(True)
self.fill_eventbox()
@ -1113,6 +1158,31 @@ class MainWindow(QMainWindow):
self.refreshEvents()
tabindex = self.tabs.currentIndex()
def user_modify_path(self, reason=''):
dialog = QtGui.QInputDialog(parent=self)
new_path, executed = dialog.getText(self, 'Change Project rootpath',
'{}Rename project path {}:'.format(reason, self.project.rootpath))
return new_path, executed
def data_check(self):
paths_exist = [os.path.exists(event.path) for event in self.project.eventlist]
if all(paths_exist):
return True
else:
info_str = ''
for event, path_exists in zip(self.project.eventlist, paths_exist):
info_str += '\n{} exists: {}'.format(event.path, path_exists)
print('Unable to find certain event paths:{}'.format(info_str))
return False
def modify_project_path(self, new_rootpath):
self.project.rootpath = new_rootpath
for event in self.project.eventlist:
event.rootpath = new_rootpath
event.path = os.path.join(event.rootpath, event.datapath, event.database, event.pylot_id)
event.path = event.path.replace('\\', '/')
event.path = event.path.replace('//', '/')
def fill_eventbox(self, event=None, eventBox=None, select_events='all'):
'''
(Re)fill the selected eventBox (type = QtGui.QComboBox).
@ -1353,6 +1423,9 @@ class MainWindow(QMainWindow):
self.cmpw.refresh_tooltips()
self.cmpw.show()
def pickQualities(self):
return
def compareMulti(self):
if not self.compareoptions:
return
@ -1401,7 +1474,8 @@ class MainWindow(QMainWindow):
return None
def getStime(self):
return self._stime
if self.get_data():
return full_range(self.get_data().getWFData())[0]
def addActions(self, target, actions):
for action in actions:
@ -1470,6 +1544,26 @@ class MainWindow(QMainWindow):
if self.tap:
self.tap.fill_eventbox()
def checkEventButtons(self):
if self.eventBox.currentIndex() == 0:
prev_state = False
else:
prev_state = True
if self.eventBox.currentIndex() == len(self.project.eventlist) - 1:
next_state = False
else:
next_state = True
self.previous_button.setEnabled(prev_state)
self.next_button.setEnabled(next_state)
def previous_event(self):
self.eventBox.setCurrentIndex(self.eventBox.currentIndex() - 1)
self.eventBox.activated.emit(-1)
def next_event(self):
self.eventBox.setCurrentIndex(self.eventBox.currentIndex() + 1)
self.eventBox.activated.emit(+1)
def refreshEvents(self):
'''
Refresh GUI when events get changed.
@ -1480,6 +1574,7 @@ class MainWindow(QMainWindow):
# array_map refresh is not necessary when changing event in waveform plot tab,
# but gets necessary when switching from one to another after changing an event.
self._eventChanged = [True, True]
self.checkEventButtons()
self.refreshTabs()
def refreshTabs(self):
@ -1554,18 +1649,38 @@ class MainWindow(QMainWindow):
# else:
# ans = False
self.fnames = self.getWFFnames_from_eventbox()
self.data.setWFData(self.fnames)
wfdat = self.data.getWFData() # all available streams
# remove possible underscores in station names
wfdat = remove_underscores(wfdat)
# check for gaps and doubled channels
check4gaps(wfdat)
check4doubled(wfdat)
# check for stations with rotated components
wfdat = check4rotated(wfdat, self.metadata, verbosity=0)
# trim station components to same start value
trim_station_components(wfdat, trim_start=True, trim_end=False)
self._stime = full_range(self.get_data().getWFData())[0]
self.data.setWFData(self.fnames,
checkRotated=True,
metadata=self.metadata)
def check_plot_quantity(self):
settings = QSettings()
nth_sample = settings.value("nth_sample") if settings.value("nth_sample") else 1
npts_max = 1e6
npts = self.get_npts_to_plot()
npts2plot = npts/nth_sample
if npts2plot < npts_max:
return
nth_sample_new = int(np.ceil(npts/npts_max))
message = "You are about to plot a huge dataset with {npts} datapoints. With a current setting of " \
"nth_sample = {nth_sample} a total of {npts2plot} points will be plotted which is more " \
"than the maximum setting of {npts_max}. " \
"PyLoT recommends to raise nth_sample from {nth_sample} to {nth_sample_new}. Continue?"
ans = QMessageBox.question(self, self.tr("Optimize plot performance..."),
self.tr(message.format(npts=npts,
nth_sample=nth_sample,
npts_max=npts_max,
nth_sample_new=nth_sample_new,
npts2plot=npts2plot)),
QMessageBox.Yes | QMessageBox.No,
QMessageBox.Yes)
if ans == QMessageBox.Yes:
settings.setValue("nth_sample", nth_sample_new)
settings.sync()
def get_npts_to_plot(self):
return sum(trace.stats.npts for trace in self.data.getWFData())
def connectWFplotEvents(self):
'''
@ -1661,8 +1776,10 @@ class MainWindow(QMainWindow):
if event.pylot_picks:
self.drawPicks(picktype='manual')
self.locateEvent.setEnabled(True)
self.qualities_action.setEnabled(True)
if event.pylot_autopicks:
self.drawPicks(picktype='auto')
self.qualities_action.setEnabled(True)
if True in self.comparable.values():
self.compare_action.setEnabled(True)
self.draw()
@ -1724,18 +1841,20 @@ class MainWindow(QMainWindow):
self.disableSaveEventAction()
self.draw()
def plotWaveformDataThread(self):
def plotWaveformDataThread(self, filter=True):
'''
Open a modal thread to plot current waveform data.
'''
self.check_plot_quantity()
self.clearWaveformDataPlot()
self.wfp_thread = Thread(self, self.plotWaveformData,
arg=filter,
progressText='Plotting waveform data...',
pb_widget=self.mainProgressBarWidget)
self.wfp_thread.finished.connect(self.finishWaveformDataPlot)
self.wfp_thread.start()
def plotWaveformData(self):
def plotWaveformData(self, filter=True):
'''
Plot waveform data to current plotWidget.
'''
@ -1747,6 +1866,10 @@ class MainWindow(QMainWindow):
comp = self.getComponent()
title = 'section: {0} components'.format(zne_text[comp])
wfst = self.get_data().getWFData()
if self.filterActionP.isChecked() and filter:
self.filterWaveformData(plot=False, phase='P')
elif self.filterActionS.isChecked() and filter:
self.filterWaveformData(plot=False, phase='S')
# wfst = self.get_data().getWFData().select(component=comp)
# wfst += self.get_data().getWFData().select(component=alter_comp)
plotWidget = self.getPlotWidget()
@ -1786,18 +1909,51 @@ class MainWindow(QMainWindow):
def pushFilterWF(self, param_args):
self.get_data().filterWFData(param_args)
def filterWaveformData(self):
def filterP(self):
self.filterActionS.setChecked(False)
if self.filterActionP.isChecked():
self.filterWaveformData(phase='P')
else:
self.resetWFData()
def filterS(self):
self.filterActionP.setChecked(False)
if self.filterActionS.isChecked():
self.filterWaveformData(phase='S')
else:
self.resetWFData()
def resetWFData(self):
self.get_data().resetWFData()
self.plotWaveformDataThread()
def filterWaveformData(self, plot=True, phase=None):
if not self.get_current_event():
return
if self.get_data():
if self.getFilterOptions() and self.filterAction.isChecked():
kwargs = self.getFilterOptions()[self.getSeismicPhase()].parseFilterOptions()
self.pushFilterWF(kwargs)
elif self.filterAction.isChecked():
if not phase:
if self.filterActionP.isChecked():
phase = 'P'
elif self.filterActionS.isChecked():
phase = 'S'
if self.getFilterOptions():
if (phase == 'P' and self.filterActionP.isChecked()) or (phase == 'S' and self.filterActionS.isChecked()):
kwargs = self.getFilterOptions()[phase].parseFilterOptions()
self.pushFilterWF(kwargs)
else:
self.get_data().resetWFData()
elif self.filterActionP.isChecked() or self.filterActionS.isChecked():
self.adjustFilterOptions()
else:
self.get_data().resetWFData()
self.plotWaveformDataThread()
self.drawPicks()
self.draw()
if plot:
self.plotWaveformDataThread(filter=False)
#self.drawPicks()
#self.draw()
def getAutoFilteroptions(self, phase):
return getAutoFilteroptions(phase, self._inputs)
def adjustFilterOptions(self):
fstring = "Filter Options"
@ -1806,10 +1962,11 @@ class MainWindow(QMainWindow):
if self.filterDlg.exec_():
filteroptions = self.filterDlg.getFilterOptions()
self.setFilterOptions(filteroptions)
if self.filterAction.isChecked():
if self.filterActionP.isChecked() or self.filterActionS.isChecked():
kwargs = self.getFilterOptions()[self.getSeismicPhase()].parseFilterOptions()
self.pushFilterWF(kwargs)
self.plotWaveformDataThread()
return True
def checkFilterOptions(self):
fstring = "Filter Options"
@ -1886,7 +2043,7 @@ class MainWindow(QMainWindow):
# '[{0}: {1} Hz]'.format(
# self.getFilterOptions().getFilterType(),
# self.getFilterOptions().getFreq()))
# if self.filterAction.isChecked():
# if self.filterActionP.isChecked() or self.filterActionS.isChecked():
# self.filterWaveformData()
def getSeismicPhase(self):
@ -1934,7 +2091,7 @@ class MainWindow(QMainWindow):
if self._shift:
factor = {'up': 1. / 2.,
'down': 2.}
xlims = self.dataPlot.getXLims()
xlims = self.dataPlot.getXLims(self.dataPlot.axes[0])
xdiff = xlims[1] - xlims[0]
xdiff *= factor[button]
xl = x - 0.5 * xdiff
@ -1943,7 +2100,7 @@ class MainWindow(QMainWindow):
xl = self._max_xlims[0]
if xr > self._max_xlims[1]:
xr = self._max_xlims[1]
self.dataPlot.setXLims((xl, xr))
self.dataPlot.setXLims(self.dataPlot.axes[0], (xl, xr))
self.dataPlot.draw()
def pickOnStation(self, gui_event):
@ -1970,7 +2127,7 @@ class MainWindow(QMainWindow):
if not station:
return
self.update_status('picking on station {0}'.format(station))
data = self.get_data().getWFData()
data = self.get_data().getOriginalWFData().copy()
event = self.get_current_event()
pickDlg = PickDlg(self, parameter=self._inputs,
data=data.select(station=station),
@ -1979,6 +2136,9 @@ class MainWindow(QMainWindow):
autopicks=self.getPicksOnStation(station, 'auto'),
metadata=self.metadata, event=event,
filteroptions=self.filteroptions)
if self.filterActionP.isChecked() or self.filterActionS.isChecked():
pickDlg.currentPhase = self.getSeismicPhase()
pickDlg.filterWFData()
pickDlg.nextStation.setChecked(nextStation)
if pickDlg.exec_():
if pickDlg._dirty:
@ -2315,7 +2475,7 @@ class MainWindow(QMainWindow):
if self.pg:
pw = self.getPlotWidget().plotWidget
else:
ax = self.getPlotWidget().axes
ax = self.getPlotWidget().axes[0]
ylims = np.array([-.5, +.5]) + plotID
stat_picks = self.getPicks(type=picktype)[station]
@ -2385,7 +2545,7 @@ class MainWindow(QMainWindow):
else:
if picktype == 'manual':
linestyle_mpp, width_mpp = pick_linestyle_plt(picktype, 'mpp')
color = pick_color_plt(picktype, phase, quality)
color = pick_color_plt(picktype, self.getPhaseID(phase), quality)
if picks['epp'] and picks['lpp']:
ax.fill_between([epp, lpp], ylims[0], ylims[1],
alpha=.25, color=color, label='EPP, LPP')
@ -2927,6 +3087,7 @@ class MainWindow(QMainWindow):
if hasattr(self.project, 'parameter'):
if self.project.parameter:
self._inputs = self.project.parameter
self.updateFilteroptions()
self.tabs.setCurrentIndex(0) # implemented to prevent double-loading of waveform data
self.init_events(new=True)
self.setDirty(False)

View File

@ -338,6 +338,7 @@ def autoPyLoT(input_dict=None, parameter=None, inputfile=None, fnames=None, even
local_mag = LocalMagnitude(corr_dat, evt,
parameter.get('sstop'),
WAscaling, True, iplot)
# update pick with local magnitude property values
for stats, amplitude in local_mag.amplitudes.items():
picks[stats]['S']['Ao'] = amplitude.generic_amplitude
print("Local station magnitudes scaled with:")
@ -412,6 +413,7 @@ def autoPyLoT(input_dict=None, parameter=None, inputfile=None, fnames=None, even
local_mag = LocalMagnitude(corr_dat, evt,
parameter.get('sstop'),
WAscaling, True, iplot)
# update pick with local magnitude property values
for stats, amplitude in local_mag.amplitudes.items():
if stats in picks:
picks[stats]['S']['Ao'] = amplitude.generic_amplitude
@ -496,7 +498,7 @@ if __name__ == "__main__":
help='''full path to the file containing the input
parameters for autoPyLoT''')
parser.add_argument('-p', '-P', '--iplot', type=int,
action='store',
action='store', default=0,
help='''optional, logical variable for plotting: 0=none, 1=partial, 2=all''')
parser.add_argument('-f', '-F', '--fnames', type=str,
action='store',

View File

@ -29,10 +29,12 @@
<file>icons/map.png</file>
<file>icons/openloc.png</file>
<file>icons/compare_button.png</file>
<file>icons/pick_qualities_button.png</file>
<file>icons/locate_button.png</file>
<file>icons/Matlab_PILOT_icon.png</file>
<file>icons/printer.png</file>
<file>icons/delete.png</file>
<file>icons/key_A.png</file>
<file>icons/key_E.png</file>
<file>icons/key_N.png</file>
<file>icons/key_P.png</file>
@ -45,6 +47,8 @@
<file>icons/key_W.png</file>
<file>icons/key_Z.png</file>
<file>icons/filter.png</file>
<file>icons/filter_p.png</file>
<file>icons/filter_s.png</file>
<file>icons/sync.png</file>
<file>icons/zoom_0.png</file>
<file>icons/zoom_in.png</file>

BIN
icons/filter_p.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

BIN
icons/filter_s.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 KiB

BIN
icons/key_A.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

File diff suppressed because it is too large Load Diff

214148
icons_rc_3.py

File diff suppressed because it is too large Load Diff

View File

@ -16,7 +16,6 @@ from pylot.core.util.utils import common_range, fit_curve
from scipy import integrate, signal
from scipy.optimize import curve_fit
def richter_magnitude_scaling(delta):
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,
@ -133,7 +132,7 @@ class Magnitude(object):
station_count=len(self.magnitudes),
azimuthal_gap=self.origin_id.get_referred_object().quality.azimuthal_gap)
else:
# no saling necessary
# no scaling necessary
mag = ope.Magnitude(
mag=np.median([M.mag for M in self.magnitudes.values()]),
magnitude_type=self.type,
@ -233,17 +232,33 @@ class LocalMagnitude(Magnitude):
# check for plot flag (for debugging only)
fig = None
if iplot > 1:
st.plot()
fig = plt.figure()
ax = fig.add_subplot(111)
ax = fig.add_subplot(211)
ax.plot(th, st[0].data, 'k')
ax.plot(th, sqH)
ax.plot(th[iwin], sqH[iwin], 'g')
ax.plot([t0, t0], [0, max(sqH)], 'r', linewidth=2)
ax.title(
'Station %s, RMS Horizontal Traces, WA-peak-to-peak=%4.1f mm' \
% (st[0].stats.station, wapp))
ax.plot([t0 - stime, t0 - stime], [0, max(sqH)], 'r', linewidth=2)
ax.set_title('Station %s, Channel %s, RMS Horizontal Trace, '
'WA-peak-to-peak=%6.3f mm' % (st[0].stats.station,
st[0].stats.channel,
wapp))
ax.set_xlabel('Time [s]')
ax.set_ylabel('Displacement [mm]')
ax = fig.add_subplot(212)
ax.plot(th, st[1].data, 'k')
ax.plot(th, sqH)
ax.plot(th[iwin], sqH[iwin], 'g')
ax.plot([t0 - stime, t0 - stime], [0, max(sqH)], 'r', linewidth=2)
ax.set_title('Channel %s, RMS Horizontal Trace, '
'WA-peak-to-peak=%6.3f mm' % (st[1].stats.channel,
wapp))
ax.set_xlabel('Time [s]')
ax.set_ylabel('Displacement [mm]')
fig.show()
try: input()
except SyntaxError: pass
plt.close(fig)
return wapp, fig
@ -251,6 +266,10 @@ class LocalMagnitude(Magnitude):
for a in self.arrivals:
if a.phase not in 'sS':
continue
# make sure calculating Ml only from reliable onsets
# NLLoc: time_weight = 0 => do not use onset!
if a.time_weight == 0:
continue
pick = a.pick_id.get_referred_object()
station = pick.waveform_id.station_code
wf = select_for_phase(self.stream.select(
@ -347,7 +366,11 @@ class MomentMagnitude(Magnitude):
def calc(self):
for a in self.arrivals:
if a.phase not in 'pP':
if a.phase not in 'pP':
continue
# make sure calculating Mo only from reliable onsets
# NLLoc: time_weight = 0 => do not use onset!
if a.time_weight == 0:
continue
pick = a.pick_id.get_referred_object()
station = pick.waveform_id.station_code

View File

@ -12,7 +12,8 @@ from pylot.core.io.phases import readPILOTEvent, picks_from_picksdict, \
picksdict_from_pilot, merge_picks
from pylot.core.util.errors import FormatError, OverwriteError
from pylot.core.util.event import Event
from pylot.core.util.utils import fnConstructor, full_range
from pylot.core.util.utils import fnConstructor, full_range, remove_underscores, check4gaps, check4doubled, \
check4rotated, trim_station_components
import pylot.core.loc.velest as velest
@ -99,6 +100,11 @@ class Data(object):
return self
def getPicksStr(self):
"""
Return picks in event data
:return: picks seperated by newlines
:rtype: str
"""
picks_str = ''
for pick in self.get_evt_data().picks:
picks_str += str(pick) + '\n'
@ -106,18 +112,11 @@ class Data(object):
def getParent(self):
"""
:return:
Get PySide.QtGui.QWidget parent object
"""
return self._parent
def isNew(self):
"""
:return:
"""
return self._new
def setNew(self):
@ -125,9 +124,9 @@ class Data(object):
def getCutTimes(self):
"""
:return:
Returns earliest start and latest end of all waveform data
:return: minimum start time and maximum end time as a tuple
:rtype: (UTCDateTime, UTCDateTime)
"""
if self.cuttimes is None:
self.updateCutTimes()
@ -135,22 +134,34 @@ class Data(object):
def updateCutTimes(self):
"""
Update cuttimes to contain earliest start and latest end time
of all waveform data
:rtype: None
"""
self.cuttimes = full_range(self.getWFData())
def getEventFileName(self):
"""
:return:
"""
ID = self.getID()
# handle forbidden filenames especially on windows systems
return fnConstructor(str(ID))
def checkEvent(self, event, fcheck, forceOverwrite=False):
"""
Check information in supplied event and own event and replace own
information with supplied information if own information not exiisting
or forced by forceOverwrite
:param event: Event that supplies information for comparison
:type event: pylot.core.util.event.Event
:param fcheck: check and delete existing information
can be a str or a list of strings of ['manual', 'auto', 'origin', 'magnitude']
:type fcheck: str, [str]
:param forceOverwrite: Set to true to force overwrite own information. If false,
supplied information from event is only used if there is no own information in that
category (given in fcheck: manual, auto, origin, magnitude)
:type forceOverwrite: bool
:return:
:rtype: None
"""
if 'origin' in fcheck:
self.replaceOrigin(event, forceOverwrite)
if 'magnitude' in fcheck:
@ -161,18 +172,47 @@ class Data(object):
self.replacePicks(event, 'manual')
def replaceOrigin(self, event, forceOverwrite=False):
"""
Replace own origin with the one supplied in event if own origin is not
existing or forced by forceOverwrite = True
:param event: Event that supplies information for comparison
:type event: pylot.core.util.event.Event
:param forceOverwrite: always replace own information with supplied one if true
:type forceOverwrite: bool
:return:
:rtype: None
"""
if self.get_evt_data().origins or forceOverwrite:
if event.origins:
print("Found origin, replace it by new origin.")
event.origins = self.get_evt_data().origins
def replaceMagnitude(self, event, forceOverwrite=False):
"""
Replace own magnitude with the one supplied in event if own magnitude is not
existing or forced by forceOverwrite = True
:param event: Event that supplies information for comparison
:type event: pylot.core.util.event.Event
:param forceOverwrite: always replace own information with supplied one if true
:type forceOverwrite: bool
:return:
:rtype: None
"""
if self.get_evt_data().magnitudes or forceOverwrite:
if event.magnitudes:
print("Found magnitude, replace it by new magnitude")
event.magnitudes = self.get_evt_data().magnitudes
def replacePicks(self, event, picktype):
"""
Replace own picks with the one in event
:param event: Event that supplies information for comparison
:type event: pylot.core.util.event.Event
:param picktype: 'auto' or 'manual' picks
:type picktype: str
:return:
:rtype: None
"""
checkflag = 0
picks = event.picks
# remove existing picks
@ -189,10 +229,10 @@ class Data(object):
picks.append(pick)
def exportEvent(self, fnout, fnext='.xml', fcheck='auto', upperErrors=None):
"""
Export event to file
:param fnout: basename of file
:param fnext: file extension
:param fnext: file extension, xml, cnv, obs
:param fcheck: check and delete existing information
can be a str or a list of strings of ['manual', 'auto', 'origin', 'magnitude']
"""
@ -304,17 +344,13 @@ class Data(object):
def getComp(self):
"""
:return:
Get component (ZNE)
"""
return self.comp
def getID(self):
"""
:return:
Get unique resource id
"""
try:
return self.evtdata.get('resource_id').id
@ -323,16 +359,20 @@ class Data(object):
def filterWFData(self, kwargs):
"""
:param kwargs:
Filter waveform data
:param kwargs: arguments to pass through to filter function
"""
self.getWFData().filter(**kwargs)
data = self.getWFData()
data.detrend('linear')
data.taper(0.02, type='cosine')
data.filter(**kwargs)
self.dirty = True
def setWFData(self, fnames):
def setWFData(self, fnames, checkRotated=False, metadata=None):
"""
:param fnames:
Clear current waveform data and set given waveform data
:param fnames: waveform data names to append
:type fnames: list
"""
self.wfdata = Stream()
self.wforiginal = None
@ -340,14 +380,31 @@ class Data(object):
self.appendWFData(fnames)
else:
return False
# various pre-processing steps:
# remove possible underscores in station names
self.wfdata = remove_underscores(self.wfdata)
# check for gaps and doubled channels
check4gaps(self.wfdata)
check4doubled(self.wfdata)
# check for stations with rotated components
if checkRotated and metadata is not None:
self.wfdata = check4rotated(self.wfdata, metadata, verbosity=0)
# trim station components to same start value
trim_station_components(self.wfdata, trim_start=True, trim_end=False)
# make a copy of original data
self.wforiginal = self.getWFData().copy()
self.dirty = False
return True
def appendWFData(self, fnames):
"""
:param fnames:
Read waveform data from fnames and append it to current wf data
:param fnames: waveform data to append
:type fnames: list
"""
assert isinstance(fnames, list), "input parameter 'fnames' is " \
"supposed to be of type 'list' " \
@ -372,54 +429,45 @@ class Data(object):
print(warnmsg)
def getWFData(self):
"""
:return:
"""
return self.wfdata
def getOriginalWFData(self):
"""
:return:
"""
return self.wforiginal
def resetWFData(self):
"""
Set waveform data to original waveform data
"""
self.wfdata = self.getOriginalWFData().copy()
if self.getOriginalWFData():
self.wfdata = self.getOriginalWFData().copy()
else:
self.wfdata = Stream()
self.dirty = False
def resetPicks(self):
"""
Clear all picks from event
"""
self.get_evt_data().picks = []
def get_evt_data(self):
"""
:return:
"""
return self.evtdata
def setEvtData(self, event):
self.evtdata = event
def applyEVTData(self, data, typ='pick', authority_id='rub'):
"""
:param data:
:param typ:
:param authority_id:
Either takes an `obspy.core.event.Event` object and applies all new
information on the event to the actual data if typ is 'event or
creates ObsPy pick objects and append it to the picks list from the
PyLoT dictionary contain all picks if type is pick
:param data: data to apply, either picks or complete event
:type data:
:param typ: which event data to apply, 'pick' or 'event'
:type typ: str
:param authority_id: (currently unused)
:type: str
:raise OverwriteError:
"""

View File

@ -1,5 +1,10 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import numpy as np
"""
Default parameters used for picking
"""
defaults = {'rootpath': {'type': str,
'tooltip': 'project path',
@ -74,11 +79,15 @@ defaults = {'rootpath': {'type': str,
'vp': {'type': float,
'tooltip': 'average P-wave velocity',
'value': 3530.,
'min': 0.,
'max': np.inf,
'namestring': 'P-velocity'},
'rho': {'type': float,
'tooltip': 'average rock density [kg/m^3]',
'value': 2500.,
'min': 0.,
'max': np.inf,
'namestring': 'Density'},
'Qp': {'type': (float, float),
@ -90,42 +99,58 @@ defaults = {'rootpath': {'type': str,
'tooltip': 'start time [s] for calculating CF for P-picking (if TauPy:'
' seconds relative to estimated onset)',
'value': 15.0,
'min': -np.inf,
'max': np.inf,
'namestring': 'P start'},
'pstop': {'type': float,
'tooltip': 'end time [s] for calculating CF for P-picking (if TauPy:'
' seconds relative to estimated onset)',
'value': 60.0,
'min': -np.inf,
'max': np.inf,
'namestring': 'P stop'},
'sstart': {'type': float,
'tooltip': 'start time [s] relative to P-onset for calculating CF for S-picking',
'value': -1.0,
'min': -np.inf,
'max': np.inf,
'namestring': 'S start'},
'sstop': {'type': float,
'tooltip': 'end time [s] after P-onset for calculating CF for S-picking',
'value': 10.0,
'min': -np.inf,
'max': np.inf,
'namestring': 'S stop'},
'bpz1': {'type': (float, float),
'tooltip': 'lower/upper corner freq. of first band pass filter Z-comp. [Hz]',
'value': (2, 20),
'min': (0., 0.),
'max': (np.inf, np.inf),
'namestring': ('Z-bandpass 1', 'Lower', 'Upper')},
'bpz2': {'type': (float, float),
'tooltip': 'lower/upper corner freq. of second band pass filter Z-comp. [Hz]',
'value': (2, 30),
'min': (0., 0.),
'max': (np.inf, np.inf),
'namestring': ('Z-bandpass 2', 'Lower', 'Upper')},
'bph1': {'type': (float, float),
'tooltip': 'lower/upper corner freq. of first band pass filter H-comp. [Hz]',
'value': (2, 15),
'min': (0., 0.),
'max': (np.inf, np.inf),
'namestring': ('H-bandpass 1', 'Lower', 'Upper')},
'bph2': {'type': (float, float),
'tooltip': 'lower/upper corner freq. of second band pass filter z-comp. [Hz]',
'value': (2, 20),
'min': (0., 0.),
'max': (np.inf, np.inf),
'namestring': ('H-bandpass 2', 'Lower', 'Upper')},
'algoP': {'type': str,
@ -136,76 +161,106 @@ defaults = {'rootpath': {'type': str,
'tlta': {'type': float,
'tooltip': 'for HOS-/AR-AIC-picker, length of LTA window [s]',
'value': 7.0,
'min': 0.,
'max': np.inf,
'namestring': 'LTA window'},
'hosorder': {'type': int,
'tooltip': 'for HOS-picker, order of Higher Order Statistics',
'value': 4,
'min': 0,
'max': np.inf,
'namestring': 'HOS order'},
'Parorder': {'type': int,
'tooltip': 'for AR-picker, order of AR process of Z-component',
'value': 2,
'min': 0,
'max': np.inf,
'namestring': 'AR order P'},
'tdet1z': {'type': float,
'tooltip': 'for AR-picker, length of AR determination window [s] for Z-component, 1st pick',
'value': 1.2,
'min': 0.,
'max': np.inf,
'namestring': 'AR det. window Z 1'},
'tpred1z': {'type': float,
'tooltip': 'for AR-picker, length of AR prediction window [s] for Z-component, 1st pick',
'value': 0.4,
'min': 0.,
'max': np.inf,
'namestring': 'AR pred. window Z 1'},
'tdet2z': {'type': float,
'tooltip': 'for AR-picker, length of AR determination window [s] for Z-component, 2nd pick',
'value': 0.6,
'min': 0.,
'max': np.inf,
'namestring': 'AR det. window Z 2'},
'tpred2z': {'type': float,
'tooltip': 'for AR-picker, length of AR prediction window [s] for Z-component, 2nd pick',
'value': 0.2,
'min': 0.,
'max': np.inf,
'namestring': 'AR pred. window Z 2'},
'addnoise': {'type': float,
'tooltip': 'add noise to seismogram for stable AR prediction',
'value': 0.001,
'min': 0.,
'max': np.inf,
'namestring': 'Add noise'},
'tsnrz': {'type': (float, float, float, float),
'tooltip': 'for HOS/AR, window lengths for SNR-and slope estimation [tnoise, tsafetey, tsignal, tslope] [s]',
'value': (3, 0.1, 0.5, 1.0),
'min': (0., 0., 0., 0.),
'max': (np.inf, np.inf, np.inf, np.inf),
'namestring': ('SNR windows P', 'Noise', 'Safety', 'Signal', 'Slope')},
'pickwinP': {'type': float,
'tooltip': 'for initial AIC pick, length of P-pick window [s]',
'value': 3.0,
'min': 0.,
'max': np.inf,
'namestring': 'AIC window P'},
'Precalcwin': {'type': float,
'tooltip': 'for HOS/AR, window length [s] for recalculation of CF (relative to 1st pick)',
'value': 6.0,
'min': 0.,
'max': np.inf,
'namestring': 'Recal. window P'},
'aictsmooth': {'type': float,
'tooltip': 'for HOS/AR, take average of samples for smoothing of AIC-function [s]',
'value': 0.2,
'min': 0.,
'max': np.inf,
'namestring': 'AIC smooth P'},
'tsmoothP': {'type': float,
'tooltip': 'for HOS/AR, take average of samples in this time window for smoothing CF [s]',
'value': 0.1,
'min': 0.,
'max': np.inf,
'namestring': 'CF smooth P'},
'ausP': {'type': float,
'tooltip': 'for HOS/AR, artificial uplift of samples (aus) of CF (P)',
'value': 0.001,
'min': 0.,
'max': np.inf,
'namestring': 'Artificial uplift P'},
'nfacP': {'type': float,
'tooltip': 'for HOS/AR, noise factor for noise level determination (P)',
'value': 1.3,
'min': 0.,
'max': np.inf,
'namestring': 'Noise factor P'},
'algoS': {'type': str,
@ -216,61 +271,85 @@ defaults = {'rootpath': {'type': str,
'tdet1h': {'type': float,
'tooltip': 'for HOS/AR, length of AR-determination window [s], H-components, 1st pick',
'value': 0.8,
'min': 0.,
'max': np.inf,
'namestring': 'AR det. window H 1'},
'tpred1h': {'type': float,
'tooltip': 'for HOS/AR, length of AR-prediction window [s], H-components, 1st pick',
'value': 0.4,
'min': 0.,
'max': np.inf,
'namestring': 'AR pred. window H 1'},
'tdet2h': {'type': float,
'tooltip': 'for HOS/AR, length of AR-determinaton window [s], H-components, 2nd pick',
'value': 0.6,
'min': 0.,
'max': np.inf,
'namestring': 'AR det. window H 2'},
'tpred2h': {'type': float,
'tooltip': 'for HOS/AR, length of AR-prediction window [s], H-components, 2nd pick',
'value': 0.3,
'min': 0.,
'max': np.inf,
'namestring': 'AR pred. window H 2'},
'Sarorder': {'type': int,
'tooltip': 'for AR-picker, order of AR process of H-components',
'value': 4,
'min': 0,
'max': np.inf,
'namestring': 'AR order S'},
'Srecalcwin': {'type': float,
'tooltip': 'for AR-picker, window length [s] for recalculation of CF (2nd pick) (H)',
'value': 5.0,
'min': 0.,
'max': np.inf,
'namestring': 'Recal. window S'},
'pickwinS': {'type': float,
'tooltip': 'for initial AIC pick, length of S-pick window [s]',
'value': 3.0,
'min': 0.,
'max': np.inf,
'namestring': 'AIC window S'},
'tsnrh': {'type': (float, float, float, float),
'tooltip': 'for ARH/AR3, window lengths for SNR-and slope estimation [tnoise, tsafetey, tsignal, tslope] [s]',
'value': (2, 0.2, 1.5, 0.5),
'min': (0., 0., 0., 0.),
'max': (np.inf, np.inf, np.inf, np.inf),
'namestring': ('SNR windows S', 'Noise', 'Safety', 'Signal', 'Slope')},
'aictsmoothS': {'type': float,
'tooltip': 'for AIC-picker, take average of samples in this time window for smoothing of AIC-function [s]',
'value': 0.5,
'min': 0.,
'max': np.inf,
'namestring': 'AIC smooth S'},
'tsmoothS': {'type': float,
'tooltip': 'for AR-picker, take average of samples for smoothing CF [s] (S)',
'value': 0.7,
'min': 0.,
'max': np.inf,
'namestring': 'CF smooth S'},
'ausS': {'type': float,
'tooltip': 'for HOS/AR, artificial uplift of samples (aus) of CF (S)',
'value': 0.9,
'min': 0.,
'max': np.inf,
'namestring': 'Artificial uplift S'},
'nfacS': {'type': float,
'tooltip': 'for AR-picker, noise factor for noise level determination (S)',
'value': 1.5,
'min': 0.,
'max': np.inf,
'namestring': 'Noise factor S'},
'minfmweight': {'type': int,
@ -281,103 +360,143 @@ defaults = {'rootpath': {'type': str,
'minFMSNR': {'type': float,
'tooltip': 'miniumum required SNR for first-motion determination',
'value': 2.,
'min': 0.,
'max': np.inf,
'namestring': 'Min SNR'},
'fmpickwin': {'type': float,
'tooltip': 'pick window [s] around P onset for calculating zero crossings',
'value': 0.2,
'min': 0.,
'max': np.inf,
'namestring': 'Zero crossings window'},
'timeerrorsP': {'type': (float, float, float, float),
'tooltip': 'discrete time errors [s] corresponding to picking weights [0 1 2 3] for P',
'value': (0.01, 0.02, 0.04, 0.08),
'min': (0., 0., 0., 0.),
'max': (np.inf, np.inf, np.inf, np.inf),
'namestring': ('Time errors P', '0', '1', '2', '3')},
'timeerrorsS': {'type': (float, float, float, float),
'tooltip': 'discrete time errors [s] corresponding to picking weights [0 1 2 3] for S',
'value': (0.04, 0.08, 0.16, 0.32),
'min': (0., 0., 0., 0.),
'max': (np.inf, np.inf, np.inf, np.inf),
'namestring': ('Time errors S', '0', '1', '2', '3')},
'minAICPslope': {'type': float,
'tooltip': 'below this slope [counts/s] the initial P pick is rejected',
'value': 0.8,
'min': 0.,
'max': np.inf,
'namestring': 'Min. slope P'},
'minAICPSNR': {'type': float,
'tooltip': 'below this SNR the initial P pick is rejected',
'value': 1.1,
'min': 0.,
'max': np.inf,
'namestring': 'Min. SNR P'},
'minAICSslope': {'type': float,
'tooltip': 'below this slope [counts/s] the initial S pick is rejected',
'value': 1.,
'min': 0.,
'max': np.inf,
'namestring': 'Min. slope S'},
'minAICSSNR': {'type': float,
'tooltip': 'below this SNR the initial S pick is rejected',
'value': 1.5,
'min': 0.,
'max': np.inf,
'namestring': 'Min. SNR S'},
'minsiglength': {'type': float,
'tooltip': 'length of signal part for which amplitudes must exceed noiselevel [s]',
'value': 1.,
'min': 0.,
'max': np.inf,
'namestring': 'Min. signal length'},
'noisefactor': {'type': float,
'tooltip': 'noiselevel*noisefactor=threshold',
'value': 1.0,
'min': 0.,
'max': np.inf,
'namestring': 'Noise factor'},
'minpercent': {'type': float,
'tooltip': 'required percentage of amplitudes exceeding threshold',
'value': 10.,
'min': 0.,
'max': np.inf,
'namestring': 'Min amplitude [%]'},
'zfac': {'type': float,
'tooltip': 'P-amplitude must exceed at least zfac times RMS-S amplitude',
'value': 1.5,
'min': 0.,
'max': np.inf,
'namestring': 'Z factor'},
'mdttolerance': {'type': float,
'tooltip': 'maximum allowed deviation of P picks from median [s]',
'value': 6.0,
'min': 0.,
'max': np.inf,
'namestring': 'Median tolerance'},
'wdttolerance': {'type': float,
'tooltip': 'maximum allowed deviation from Wadati-diagram',
'value': 1.0,
'min': 0.,
'max': np.inf,
'namestring': 'Wadati tolerance'},
'jackfactor': {'type': float,
'tooltip': 'pick is removed if the variance of the subgroup with the pick removed is larger than the mean variance of all subgroups times safety factor',
'value': 5.0,
'namestring': 'Jackknife safety factor'},
'tooltip': 'pick is removed if the variance of the subgroup with the pick removed is larger than the mean variance of all subgroups times safety factor',
'value': 5.0,
'min': 0.,
'max': np.inf,
'namestring': 'Jackknife safety factor'},
'WAscaling': {'type': (float, float, float),
'tooltip': 'Scaling relation (log(Ao)+Alog(r)+Br+C) of Wood-Anderson amplitude Ao [nm] \
If zeros are set, original Richter magnitude is calculated!',
'value': (0., 0., 0.),
'min': (0., 0., 0.),
'max': (np.inf, np.inf, np.inf),
'namestring': ('Wood-Anderson scaling', '', '', '')},
'magscaling': {'type': (float, float),
'tooltip': 'Scaling relation for derived local magnitude [a*Ml+b]. \
If zeros are set, no scaling of network magnitude is applied!',
'value': (0., 0.),
'min': (0., 0.),
'max': (np.inf, np.inf),
'namestring': ('Local mag. scaling', '', '')},
'minfreq': {'type': (float, float),
'tooltip': 'Lower filter frequency [P, S]',
'value': (1.0, 1.0),
'min': (0., 0.),
'max': (np.inf, np.inf),
'namestring': ('Lower freq.', 'P', 'S')},
'maxfreq': {'type': (float, float),
'tooltip': 'Upper filter frequency [P, S]',
'value': (10.0, 10.0),
'min': (0., 0.),
'max': (np.inf, np.inf),
'namestring': ('Upper freq.', 'P', 'S')},
'filter_order': {'type': (int, int),
'tooltip': 'filter order [P, S]',
'value': (2, 2),
'min': (0, 0),
'max': (np.inf, np.inf),
'namestring': ('Order', 'P', 'S')},
'filter_type': {'type': (str, str),

View File

@ -70,6 +70,7 @@ class PylotParameter(object):
# Set default values of parameter names
def __init_default_paras(self):
"""set default values of parameter names"""
parameters = default_parameters.defaults
self.__defaults = parameters
@ -92,6 +93,11 @@ class PylotParameter(object):
return None
def __setitem__(self, key, value):
try:
value = self.check_range(value, self.__defaults[key]['max'], self.__defaults[key]['min'])
except KeyError:
# no min/max values in defaults
pass
self.__parameter[key] = value
def __delitem__(self, key):
@ -104,15 +110,34 @@ class PylotParameter(object):
return len(self.__parameter.keys())
def iteritems(self):
"""
Iterate over parameters
:return: key, value tupel
:rtype:
"""
for key, value in self.__parameter.items():
yield key, value
def hasParam(self, parameter):
"""
Check if parameter is in keys
:param parameter: parameter to look for in keys
:type parameter:
:return:
:rtype: bool
"""
if parameter in self.__parameter.keys():
return True
return False
def get(self, *args):
"""
Get first available parameter in args
:param args:
:type args:
:return:
:rtype:
"""
try:
for param in args:
try:
@ -128,15 +153,35 @@ class PylotParameter(object):
raise ParameterError(e)
def get_defaults(self):
"""
get default parameters
:return:
:rtype: dict
"""
return self.__defaults
def get_main_para_names(self):
"""
Get main parameter names
:return: list of keys available in parameters
:rtype:
"""
return self._settings_main
def get_special_para_names(self):
"""
Get pick parameter names
:return: list of keys available in parameters
:rtype:
"""
return self._settings_special_pick
def get_all_para_names(self):
"""
Get all parameter names
:return:
:rtype: list
"""
all_names = []
all_names += self.get_main_para_names()['dirs']
all_names += self.get_main_para_names()['nlloc']
@ -150,7 +195,43 @@ class PylotParameter(object):
all_names += self.get_special_para_names()['quality']
return all_names
@staticmethod
def check_range(value, max_value, min_value):
"""
Check if value is within the min/max values defined in default_parameters. Works for tuple and scalar values.
:param value: Value to be checked against min/max range
:param max_value: Maximum allowed value, tuple or scalar
:param min_value: Minimum allowed value, tuple or scalar
:return: value tuple/scalar clamped to the valid range
>>> checkRange(-5, 10, 0)
0
>>> checkRange((-5., 100.), (10., 10.), (0., 0.))
(0.0, 10.0)
"""
try:
# Try handling tuples by comparing their elements
comparisons = [(a > b) for a, b in zip(value, max_value)]
if True in comparisons:
value = tuple(max_value[i] if comp else value[i] for i, comp in enumerate(comparisons))
comparisons = [(a < b) for a, b in zip(value, min_value)]
if True in comparisons:
value = tuple(min_value[i] if comp else value[i] for i, comp in enumerate(comparisons))
except TypeError:
value = max(min_value, min(max_value, value))
return value
def checkValue(self, param, value):
"""
Check type of value against expected type of param.
Print warning message if type check fails
:param param:
:type param:
:param value:
:type value:
:return:
:rtype:
"""
is_type = type(value)
expect_type = self.get_defaults()[param]['type']
if not is_type == expect_type and not is_type == tuple:
@ -159,9 +240,25 @@ class PylotParameter(object):
print(Warning(message))
def setParamKV(self, param, value):
"""
set parameter param to value
:param param:
:type param:
:param value:
:type value:
:return:
:rtype: None
"""
self.__setitem__(param, value)
def setParam(self, **kwargs):
"""
Set multiple parameters
:param kwargs:
:type kwargs:
:return:
:rtype: None
"""
for key in kwargs:
self.__setitem__(key, kwargs[key])
@ -170,11 +267,23 @@ class PylotParameter(object):
print('ParameterError:\n non-existent parameter %s' % errmsg)
def reset_defaults(self):
"""
Reset current parameters to default parameters
:return:
:rtype: None
"""
defaults = self.get_defaults()
for param in defaults:
self.setParamKV(param, defaults[param]['value'])
def from_file(self, fnin=None):
"""
read parameters from file and set values to read values
:param fnin: filename
:type fnin:
:return:
:rtype: None
"""
if not fnin:
if self.__filename is not None:
fnin = self.__filename
@ -221,6 +330,13 @@ class PylotParameter(object):
self.__parameter = self._parFileCont
def export2File(self, fnout):
"""
Export parameters to file
:param fnout: Filename of export file
:type fnout: str
:return:
:rtype:
"""
fid_out = open(fnout, 'w')
lines = []
# for key, value in self.iteritems():
@ -257,6 +373,19 @@ class PylotParameter(object):
'quality assessment', None)
def write_section(self, fid, names, title, separator):
"""
write a section of parameters to file
:param fid: File object to write to
:type fid:
:param names: which parameter names to write to file
:type names:
:param title: title of section
:type title: str
:param separator: section separator, written at start of section
:type separator: str
:return:
:rtype:
"""
if separator:
fid.write(separator)
fid.write('#{}#\n'.format(title))
@ -341,7 +470,9 @@ class FilterOptions(object):
def parseFilterOptions(self):
if self:
robject = {'type': self.getFilterType(), 'corners': self.getOrder()}
robject = {'type': self.getFilterType(),
'corners': self.getOrder(),
'zerophase': False}
if not self.getFilterType() in ['highpass', 'lowpass']:
robject['freqmin'] = self.getFreq()[0]
robject['freqmax'] = self.getFreq()[1]

View File

@ -54,7 +54,7 @@ def create_arrival(pickresID, cinfo, phase, azimuth=None, dist=None):
def create_creation_info(agency_id=None, creation_time=None, author=None):
'''
get creation info of obspy event
:param agency_id:
:param creation_time:
:param author:
@ -197,9 +197,9 @@ def create_pick(origintime, picknum, picktime, eventnum, cinfo, phase, station,
def create_resourceID(timetohash, restype, authority_id=None, hrstr=None):
'''
:param timetohash:
:type timetohash
create unique resource id
:param timetohash: event origin time to hash
:type timetohash: class: `~obspy.core.utcdatetime.UTCDateTime` object
:param restype: type of the resource, e.g. 'orig', 'earthquake' ...
:type restype: str
:param authority_id: name of the institution carrying out the processing

View File

@ -16,7 +16,8 @@ from pylot.core.io.inputs import PylotParameter
from pylot.core.io.location import create_event, \
create_magnitude
from pylot.core.pick.utils import select_for_phase
from pylot.core.util.utils import getOwner, full_range, four_digits
from pylot.core.util.utils import getOwner, full_range, four_digits, transformFilteroptions2String, \
transformFilterString4Export, backtransformFilterString
def add_amplitudes(event, amplitudes):
@ -118,6 +119,13 @@ def readPILOTEvent(phasfn=None, locfn=None, authority_id='RUB', **kwargs):
def picksdict_from_pilot(fn):
"""
Create pick dictionary from matlab file
:param fn: matlab file
:type fn:
:return: pick dictionary
:rtype: dict
"""
from pylot.core.util.defaults import TIMEERROR_DEFAULTS
picks = dict()
phases_pilot = sio.loadmat(fn)
@ -147,6 +155,13 @@ def picksdict_from_pilot(fn):
def stations_from_pilot(stat_array):
"""
Create stations list from pilot station array
:param stat_array:
:type stat_array:
:return:
:rtype: list
"""
stations = list()
cur_stat = None
for stat in stat_array:
@ -164,6 +179,13 @@ def stations_from_pilot(stat_array):
def convert_pilot_times(time_array):
"""
Convert pilot times to UTCDateTimes
:param time_array: pilot times
:type time_array:
:return:
:rtype:
"""
times = [int(time) for time in time_array]
microseconds = int((time_array[-1] - times[-1]) * 1e6)
times.append(microseconds)
@ -171,6 +193,13 @@ def convert_pilot_times(time_array):
def picksdict_from_obs(fn):
"""
create pick dictionary from obs file
:param fn: filename
:type fn:
:return:
:rtype:
"""
picks = dict()
station_name = str()
for line in open(fn, 'r'):
@ -207,6 +236,10 @@ def picksdict_from_picks(evt):
network = pick.waveform_id.network_code
mpp = pick.time
spe = pick.time_errors.uncertainty
if pick.filter_id:
filter_id = backtransformFilterString(str(pick.filter_id.id))
else:
filter_id = None
try:
picker = str(pick.method_id)
if picker.startswith('smi:local/'):
@ -222,10 +255,15 @@ def picksdict_from_picks(evt):
lpp = mpp + pick.time_errors.upper_uncertainty
epp = mpp - pick.time_errors.lower_uncertainty
except TypeError as e:
msg = e + ',\n falling back to symmetric uncertainties'
if not spe:
msg = 'No uncertainties found for pick: {}. Uncertainty set to 0'.format(pick)
lpp = mpp
epp = mpp
else:
msg = str(e) + ',\n falling back to symmetric uncertainties'
lpp = mpp + spe
epp = mpp - spe
warnings.warn(msg)
lpp = mpp + spe
epp = mpp - spe
phase['mpp'] = mpp
phase['epp'] = epp
phase['lpp'] = lpp
@ -233,6 +271,7 @@ def picksdict_from_picks(evt):
phase['channel'] = channel
phase['network'] = network
phase['picker'] = picker
phase['filter_id'] = filter_id if filter_id is not None else ''
onsets[pick.phase_hint] = phase.copy()
picksdict[picker][station] = onsets.copy()
@ -240,6 +279,16 @@ def picksdict_from_picks(evt):
def picks_from_picksdict(picks, creation_info=None):
"""
Create a list of picks out of a pick dictionary
:param picks: pick dictionary
:type picks: dict
:param creation_info: obspy creation information to apply to picks
:type creation_info:
:param creation_info: obspy creation information to apply to picks
:return: list of picks
:rtype: list
"""
picks_list = list()
for station, onsets in picks.items():
for label, phase in onsets.items():
@ -274,6 +323,13 @@ def picks_from_picksdict(picks, creation_info=None):
pick.waveform_id = ope.WaveformStreamID(station_code=station,
channel_code=ccode,
network_code=ncode)
try:
filter_id = phase['filteroptions']
filter_id = transformFilterString4Export(filter_id)
except KeyError as e:
warnings.warn(e.message, RuntimeWarning)
filter_id = ''
pick.filter_id = filter_id
try:
polarity = phase['fm']
if polarity == 'U' or '+':
@ -290,7 +346,6 @@ def picks_from_picksdict(picks, creation_info=None):
picks_list.append(pick)
return picks_list
def reassess_pilot_db(root_dir, db_dir, out_dir=None, fn_param=None, verbosity=0):
import glob
@ -410,25 +465,24 @@ def writephases(arrivals, fformat, filename, parameter=None, eventinfo=None):
HYPO71, NLLoc, VELEST, HYPOSAT, and hypoDD
:param: arrivals
:type: dictionary containing all phase information including
station ID, phase, first motion, weight (uncertainty),
....
:param arrivals:dictionary containing all phase information including
station ID, phase, first motion, weight (uncertainty), ...
:type arrivals: dict
:param: fformat
:type: string, chosen file format (location routine),
choose between NLLoc, HYPO71, HYPOSAT, VELEST,
HYPOINVERSE, and hypoDD
:param fformat: chosen file format (location routine),
choose between NLLoc, HYPO71, HYPOSAT, VELEST,
HYPOINVERSE, and hypoDD
:type fformat: str
:param: filename, full path and name of phase file
:type: string
:param filename: full path and name of phase file
:type filename: string
:param: parameter, all input information
:type: object
:param parameter: all input information
:type parameter: object
:param: eventinfo, optional, needed for VELEST-cnv file
:param eventinfo: optional, needed for VELEST-cnv file
and FOCMEC- and HASH-input files
:type: `obspy.core.event.Event` object
:type eventinfo: `obspy.core.event.Event` object
"""
if fformat == 'NLLoc':
@ -874,10 +928,20 @@ def merge_picks(event, picks):
def getQualitiesfromxml(xmlnames, ErrorsP, ErrorsS, plotflag=1):
"""
Script to get onset uncertainties from Quakeml.xml files created by PyLoT.
Script to get onset uncertainties from Quakeml.xml files created by PyLoT.
Uncertainties are tranformed into quality classes and visualized via histogram if desired.
Ludger Küperkoch, BESTEC GmbH, 07/2017
"""
:param xmlnames: list of xml obspy event files containing picks
:type xmlnames: list
:param ErrorsP: time errors of P waves for the four discrete quality classes
:type ErrorsP:
:param ErrorsS: time errors of S waves for the four discrete quality classes
:type ErrorsS:
:param plotflag:
:type plotflag:
:return:
:rtype:
"""
from pylot.core.pick.utils import getQualityFromUncertainty
from pylot.core.util.utils import loopIdentifyPhase, identifyPhase

View File

@ -18,11 +18,11 @@ def export(picks, fnout, parameter, eventinfo):
:param fnout: complete path to the exporting obs file
:type fnout: str
:param: parameter, all input information
:type: object
:param parameter: all input information
:type parameter: object
:param: eventinfo, source information needed for focmec format
:type: list object
:param eventinfo: source information needed for focmec format
:type eventinfo: list object
'''
# write phases to FOCMEC-phase file
writephases(picks, 'FOCMEC', fnout, parameter, eventinfo)

View File

@ -18,11 +18,11 @@ def export(picks, fnout, parameter, eventinfo):
:param fnout: complete path to the exporting obs file
:type fnout: str
:param: parameter, all input information
:type: object
:param parameter: all input information
:type parameter: object
:param: eventinfo, source information needed for HASH format
:type: list object
:param eventinfo: source information needed for HASH format
:type eventinfo: list object
'''
# write phases to HASH-phase file
writephases(picks, 'HASH', fnout, parameter, eventinfo)

View File

@ -18,8 +18,8 @@ def export(picks, fnout, parameter):
:param fnout: complete path to the exporting obs file
:type fnout: str
:param: parameter, all input information
:type: object
:param parameter: all input information
:type parameter: object
'''
# write phases to HYPO71-phase file
writephases(picks, 'HYPO71', fnout, parameter)

View File

@ -18,11 +18,11 @@ def export(picks, fnout, parameter, eventinfo):
:param fnout: complete path to the exporting obs file
:type fnout: str
:param: parameter, all input information
:type: object
:param parameter: all input information
:type parameter: object
:param: eventinfo, source information needed for hypoDD format
:type: list object
:param eventinfo: source information needed for hypoDD format
:type eventinfo: list object
'''
# write phases to hypoDD-phase file
writephases(picks, 'hypoDD', fnout, parameter, eventinfo)

View File

@ -18,8 +18,8 @@ def export(picks, fnout, parameter):
:param fnout: complete path to the exporting obs file
:type fnout: str
:param: parameter, all input information
:type: object
:param parameter: all input information
:type parameter: object
'''
# write phases to HYPOSAT-phase file
writephases(picks, 'HYPOSAT', fnout, parameter)

View File

@ -28,8 +28,8 @@ def export(picks, fnout, parameter):
:param fnout: complete path to the exporting obs file
:type fnout: str
:param: parameter, all input information
:type: object
:param parameter: all input information
:type parameter: object
'''
# write phases to NLLoc-phase file
writephases(picks, 'NLLoc', fnout, parameter)
@ -38,19 +38,19 @@ def export(picks, fnout, parameter):
def modify_inputs(ctrfn, root, nllocoutn, phasefn, tttn):
'''
:param ctrfn: name of NLLoc-control file
:type: str
:type ctrfn: str
:param root: root path to NLLoc working directory
:type: str
:type root: str
:param nllocoutn: name of NLLoc-location output file
:type: str
:type nllocoutn: str
:param phasefn: name of NLLoc-input phase file
:type: str
:type phasefn: str
:param tttn: pattern of precalculated NLLoc traveltime tables
:type: str
:type tttn: str
'''
# For locating the event the NLLoc-control file has to be modified!
# create comment line for NLLoc-control file NLLoc-output file
@ -75,9 +75,9 @@ def modify_inputs(ctrfn, root, nllocoutn, phasefn, tttn):
def locate(fnin, infile=None):
"""
takes an external program name
:param fnin:
:return:
takes an external program name and tries to run it
:param fnin: external program name
:return: None
"""
if infile is None:

View File

@ -18,11 +18,11 @@ def export(picks, fnout, eventinfo, parameter=None):
:param fnout: complete path to the exporting obs file
:type fnout: str
:param: eventinfo, source time needed for VELEST-cnv format
:type: list object
:param eventinfo: source time needed for VELEST-cnv format
:type eventinfo: list object
:param: parameter, all input information
:type: object
:param parameter: all input information
:type parameter: object
'''
# write phases to VELEST-phase file
writephases(picks, 'VELEST', fnout, parameter, eventinfo)

View File

@ -155,7 +155,7 @@ def autopickstation(wfstream, pickparam, verbose=False,
:type metadata: tuple (str, ~obspy.io.xseed.parser.Parser)
:param origin: list containing origin objects representing origins for all events
:type origin: list(~obspy.core.event.origin)
:return: :dictionary containing P pick, S pick and station name
:return: dictionary containing P pick, S pick and station name
:rtype: dict
"""

View File

@ -8,6 +8,13 @@ except:
def checkurl(url='https://ariadne.geophysik.ruhr-uni-bochum.de/trac/PyLoT/'):
"""
check if URL is available
:param url: url
:type url: str
:return: available: True/False
:rtype: bool
"""
try:
urlopen(url, timeout=1)
return True

View File

@ -15,6 +15,11 @@ class Event(ObsPyEvent):
'''
def __init__(self, path):
"""
Initialize event by event directory
:param path: path to event directory
:type path: str
"""
self.pylot_id = path.split('/')[-1]
# initialize super class
super(Event, self).__init__(resource_id=ResourceIdentifier('smi:local/' + self.pylot_id))
@ -30,10 +35,20 @@ class Event(ObsPyEvent):
self.get_notes()
def get_notes_path(self):
"""
Notes files is freely editable by the user and can contain notes regarding the event
:return: path to notes file
:rtype: str
"""
notesfile = os.path.join(self.path, 'notes.txt')
return notesfile
def get_notes(self):
"""
set self.note attribute to content of notes file
:return:
:rtype: None
"""
notesfile = self.get_notes_path()
if os.path.isfile(notesfile):
with open(notesfile) as infile:
@ -48,34 +63,81 @@ class Event(ObsPyEvent):
pass
def addNotes(self, notes):
"""
Set new notes string
:param notes: notes to save in Event object
:type notes: str
:return:
:rtype: None
"""
self.notes = str(notes)
def clearNotes(self):
"""
Clear event notes
:return:
:rtype: None
"""
self.notes = None
def isRefEvent(self):
"""
Return reference event flag
:return: True if event is refence event
:rtype: bool
"""
return self._refEvent
def isTestEvent(self):
"""
Return test event flag
:return: True if event is test event
:rtype: bool
"""
return self._testEvent
def setRefEvent(self, bool):
"""
Set reference event flag
:param bool: new reference event flag
:type bool: bool
:return:
:rtype: None
"""
self._refEvent = bool
if bool: self._testEvent = False
def setTestEvent(self, bool):
"""
Set test event flag
:param bool: new test event flag
:type bool: bool
:return:
:rtype: None
"""
self._testEvent = bool
if bool: self._refEvent = False
def clearObsPyPicks(self, picktype):
"""
Remove picks of a certain type from event
:param picktype: type of picks to remove, 'auto' or 'manual'
:type picktype: str
:return:
:rtype: None
"""
for index, pick in reversed(list(enumerate(self.picks))):
if picktype in str(pick.method_id):
self.picks.pop(index)
def addPicks(self, picks):
'''
"""
add pylot picks and overwrite existing ones
'''
:param picks: picks to add to event in pick dictionary
:type picks: dict
:return:
:rtype: None
"""
for station in picks:
self.pylot_picks[station] = picks[station]
# add ObsPy picks (clear old manual and copy all new manual from pylot)
@ -83,6 +145,13 @@ class Event(ObsPyEvent):
self.picks += picks_from_picksdict(self.pylot_picks)
def addAutopicks(self, autopicks):
"""
Add automatic picks to event
:param autopicks: automatic picks to add to event
:type autopicks dict:
:return:
:rtype: None
"""
for station in autopicks:
self.pylot_autopicks[station] = autopicks[station]
# add ObsPy picks (clear old auto and copy all new auto from pylot)
@ -90,6 +159,15 @@ class Event(ObsPyEvent):
self.picks += picks_from_picksdict(self.pylot_autopicks)
def setPick(self, station, pick):
"""
Set pick for a station
:param station: station name
:type station: str
:param pick:
:type pick: dict
:return:
:rtype:
"""
if pick:
self.pylot_picks[station] = pick
else:
@ -101,21 +179,46 @@ class Event(ObsPyEvent):
self.picks += picks_from_picksdict(self.pylot_picks)
def setPicks(self, picks):
'''
set pylot picks and delete and overwrite all existing
'''
"""
Set pylot picks and delete and overwrite all existing
:param picks: new picks
:type picks: dict
:return:
:rtype: None
"""
self.pylot_picks = picks
self.clearObsPyPicks('manual')
self.picks += picks_from_picksdict(self.pylot_picks)
def getPick(self, station):
"""
Get pick at station
:param station: station name
:type station: str
:return: pick dictionary of station
:rtype: dict
"""
if station in self.pylot_picks.keys():
return self.pylot_picks[station]
def getPicks(self):
"""
Return pylot picks
:return:
:rtype: dict
"""
return self.pylot_picks
def setAutopick(self, station, pick):
"""
Set pick at station
:param station: station name
:type station: str
:param pick:
:type pick: dict
:return:
:rtype: None
"""
if pick:
self.pylot_autopicks[station] = pick
else:
@ -127,25 +230,46 @@ class Event(ObsPyEvent):
self.picks += picks_from_picksdict(self.pylot_autopicks)
def setAutopicks(self, picks):
'''
set pylot picks and delete and overwrite all existing
'''
"""
Set pylot picks and delete and overwrite all existing
:param picks: new picks
:type picks: dict
:return:
:rtype: None
"""
self.pylot_autopicks = picks
self.clearObsPyPicks('auto')
self.picks += picks_from_picksdict(self.pylot_autopicks)
def getAutopick(self, station):
"""
Return autopick at station
:param station: station name
:type station: str
:return: pick dictionary
:rtype: dict
"""
if station in self.pylot_autopicks.keys():
return self.pylot_autopicks[station]
def getAutopicks(self):
"""
Get autopicks of event
:return: dict containing automatic picks
:rtype: dict
"""
return self.pylot_autopicks
def save(self, filename):
'''
Save PyLoT Event to a file.
"""
Save PyLoT Event to a file.
Can be loaded by using event.load(filename).
'''
Uses pickling to save event object to file
:param filename: filename to save project under
:type filename: str
:return:
:rtype: None
"""
try:
import cPickle
except ImportError:
@ -159,9 +283,13 @@ class Event(ObsPyEvent):
@staticmethod
def load(filename):
'''
Load project from filename.
'''
"""
Load project from filename
:param filename: to load event file
:type filename: str
:return: event loaded from file
:rtype: Event
"""
try:
import cPickle
except ImportError:

View File

@ -24,7 +24,7 @@ class Thread(QThread):
if self.redirect_stdout:
sys.stdout = self
try:
if self.arg:
if self.arg is not None:
self.data = self.func(self.arg)
else:
self.data = self.func()

View File

@ -13,7 +13,7 @@ from obspy.core import AttribDict
from obspy.signal.rotate import rotate2zne
from obspy.io.xseed.utils import SEEDParserException
from pylot.core.io.inputs import PylotParameter
from pylot.core.io.inputs import PylotParameter, FilterOptions
from pylot.styles import style_settings
from scipy.interpolate import splrep, splev
@ -31,6 +31,15 @@ def _pickle_method(m):
else:
return getattr, (m.im_self, m.im_func.func_name)
def getAutoFilteroptions(phase, parameter):
filtername = {'P': 'bpz2',
'S': 'bph2'}
if not phase in filtername.keys():
print('autoPickParameter: No filter options for phase {}.'.format(phase))
return
freqmin, freqmax = parameter.get(filtername[phase])
filteroptions = FilterOptions(type='bandpass', freq=[freqmin, freqmax], order=4) # order=4 default from obspy
return filteroptions
def readDefaultFilterInformation(fname):
"""
@ -327,9 +336,9 @@ def real_Bool(value):
:return: true boolean value
:rtype: bool
"""
if value == 'True':
if value in ['True', 'true']:
return True
elif value == 'False':
elif value in ['False', 'false']:
return False
else:
return value
@ -391,6 +400,38 @@ def full_range(stream):
return min_start, max_end
def transformFilteroptions2String(filtopts):
st = ''
if not filtopts:
return st
if 'type' in filtopts.keys():
st += '{}'.format(filtopts['type'])
if 'freq' in filtopts.keys():
st += ' | freq: {}'.format(filtopts['freq'])
elif 'freqmin' in filtopts.keys() and 'freqmax' in filtopts.keys():
st += ' | freqmin: {} | freqmax: {}'.format(filtopts['freqmin'], filtopts['freqmax'])
for key, value in filtopts.items():
if key in ['type', 'freq', 'freqmin', 'freqmax']:
continue
st += ' | {}: {}'.format(key, value)
return st
def transformFilterString4Export(st):
st = st.replace('|', '//')
st = st.replace(':', '/')
st = st.replace(' ', '')
return st
def backtransformFilterString(st):
st = st.split('smi:local/')
st = st[1] if len(st) > 1 else st[0]
st = st.replace('//', ' | ')
st = st.replace('/', ': ')
return st
def getHash(time):
"""
takes a time object and returns the corresponding SHA1 hash of the formatted date string
@ -945,7 +986,7 @@ def check4rotated(data, metadata=None, verbosity=1):
dip = blockette_.dip
azimut = blockette_.azimuth
break
if dip is None or azimut is None:
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
@ -1111,6 +1152,9 @@ def loopIdentifyPhase(phase):
"""
from pylot.core.util.defaults import ALTSUFFIX
if phase == None:
raise NameError('Can not identify phase that is None')
phase_copy = phase
while not identifyPhase(phase_copy):
identified = False
@ -1135,8 +1179,10 @@ def identifyPhase(phase):
:rtype: str or bool
"""
# common phase suffix for P and S
common_P = ['P', 'p']
common_P = ['P', 'p', 'R']
common_S = ['S', 's']
if phase is None:
return False
if phase[-1] in common_P:
return 'P'
if phase[-1] in common_S:

File diff suppressed because it is too large Load Diff

View File

@ -124,7 +124,6 @@ color:rgba(0, 0, 0, 255);
border-style: outset;
border-width: 1px;
border-color: rgba(100, 100, 120, 255);
min-width: 6em;
padding: 4px;
padding-left:5px;
padding-right:5px;

View File

@ -123,7 +123,6 @@ color:rgba(255, 255, 255, 255);
border-style: outset;
border-width: 2px;
border-color: rgba(50, 50, 60, 255);
min-width: 6em;
padding: 4px;
padding-left:5px;
padding-right:5px;