diff --git a/.gitignore b/.gitignore
index c99398e7..d2384e94 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
*.pyc
*~
+.idea
pylot/RELEASE-VERSION
diff --git a/.png b/.png
deleted file mode 100644
index d9aa3ab8..00000000
Binary files a/.png and /dev/null differ
diff --git a/PyLoT.py b/PyLoT.py
index 7b4a64f9..c24a69c5 100755
--- a/PyLoT.py
+++ b/PyLoT.py
@@ -24,30 +24,27 @@ https://www.iconfinder.com/iconsets/flavour
"""
import argparse
-import matplotlib
+import json
import os
import platform
import shutil
import sys
-
import traceback
-
-import json
from datetime import datetime
-matplotlib.use('Qt4Agg')
-matplotlib.rcParams['backend.qt4'] = 'PySide'
-matplotlib.rcParams['savefig.dpi'] = 300
+import matplotlib
+matplotlib.use('Qt5Agg')
-from PySide import QtGui, QtCore
-from PySide.QtCore import QCoreApplication, QSettings, Signal, QFile, \
+from PySide2 import QtGui, QtCore, QtWidgets
+from PySide2.QtCore import QCoreApplication, QSettings, Signal, QFile, \
QFileInfo, Qt, QSize
-from PySide.QtGui import QMainWindow, QInputDialog, QIcon, QFileDialog, \
- QWidget, QHBoxLayout, QVBoxLayout, QStyle, QKeySequence, QLabel, QFrame, QAction, \
- QDialog, QApplication, QPixmap, QMessageBox, QSplashScreen, \
+from PySide2.QtGui import QIcon, QKeySequence, QPixmap, QStandardItem
+from PySide2.QtWidgets import QMainWindow, QInputDialog, QFileDialog, \
+ QWidget, QHBoxLayout, QVBoxLayout, QStyle, QLabel, QFrame, QAction, \
+ QDialog, QApplication, QMessageBox, QSplashScreen, \
QActionGroup, QListWidget, QListView, QAbstractItemView, \
- QTreeView, QComboBox, QTabWidget, QPushButton, QGridLayout
+ QTreeView, QComboBox, QTabWidget, QPushButton, QGridLayout, QTableWidgetItem, QTableWidget
import numpy as np
from obspy import UTCDateTime, Stream
from obspy.core.event import Magnitude, Origin
@@ -58,9 +55,9 @@ from pylot.core.util.obspyDMT_interface import check_obspydmt_structure
import pyqtgraph as pg
try:
- from matplotlib.backends.backend_qt4agg import FigureCanvas
+ from matplotlib.backends.backend_qt5agg import FigureCanvas
except ImportError:
- from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
+ from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
from pylot.core.analysis.magnitude import LocalMagnitude, MomentMagnitude
@@ -68,8 +65,8 @@ from pylot.core.io.data import Data
from pylot.core.io.inputs import FilterOptions, PylotParameter
from autoPyLoT import autoPyLoT
from pylot.core.pick.compare import Comparison
-from pylot.core.pick.utils import symmetrize_error, getQualityFromUncertainty, getPickQuality, get_quality_class
-from pylot.core.io.phases import picksdict_from_picks, picks_from_picksdict
+from pylot.core.pick.utils import getQualityFromUncertainty
+from pylot.core.io.phases import picksdict_from_picks
import pylot.core.loc.nll as nll
from pylot.core.util.errors import DatastructureError, \
OverwriteError
@@ -79,29 +76,28 @@ from pylot.core.util.utils import fnConstructor, getLogin, \
full_range, readFilterInformation, pick_color_plt, \
pick_linestyle_plt, identifyPhaseID, excludeQualityClasses, \
transform_colors_mpl, transform_colors_mpl_str, getAutoFilteroptions, check_all_obspy, \
- check_all_pylot, get_Bool, get_None, SetChannelComponents
+ check_all_pylot, get_Bool, get_None
from pylot.core.util.gui import make_pen
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, \
PylotCanvas, WaveformWidgetPG, PropertiesDlg, HelpForm, createAction, PickDlg, \
ComparisonWidget, TuneAutopicker, PylotParaBox, AutoPickDlg, CanvasWidget, AutoPickWidget, \
- CompareEventsWidget, ProgressBarWidget, AddMetadataWidget, SingleTextLineDialog
+ CompareEventsWidget, ProgressBarWidget, AddMetadataWidget, SingleTextLineDialog, LogWidget
from pylot.core.util.array_map import Array_map
from pylot.core.util.structure import DATASTRUCTURE
from pylot.core.util.thread import Thread, Worker
from pylot.core.util.version import get_git_version as _getVersionString
-from pylot.core.io.getEventListFromXML import geteventlistfromxml
-from pylot.core.io.getQualitiesfromxml import getQualitiesfromxml
+from pylot.core.io.getEventListFromXML import geteventlistfromxml
+from pylot.core.io.phases import getQualitiesfromxml
from pylot.styles import style_settings
if sys.version_info.major == 3:
import icons_rc_3 as icons_rc
-elif sys.version_info.major == 2:
- import icons_rc_2 as icons_rc
else:
- raise ImportError('Could not determine python version.')
+ raise ImportError(f'Python version {sys.version_info.major} of current interpreter not supported.'
+ f'\nPlease use Python 3+.')
# workaround to prevent PyCharm from deleting icons_rc import when optimizing imports
icons_rc = icons_rc
@@ -116,8 +112,21 @@ class MainWindow(QMainWindow):
def __init__(self, parent=None, infile=None, reset_qsettings=False):
super(MainWindow, self).__init__(parent)
- self.init_config_files(infile)
+ # check for default pylot.in-file
+ if not infile:
+ infile = os.path.join(os.path.expanduser('~'), '.pylot', 'pylot.in')
+ print('Using default input file {}'.format(infile))
+ if os.path.isfile(infile) == False:
+ infile = QFileDialog().getOpenFileName(caption='Choose PyLoT-input file')
+ if not os.path.exists(infile[0]):
+ QMessageBox.warning(self, "PyLoT Warning",
+ "No PyLoT-input file declared!")
+ sys.exit(0)
+ self.infile = infile[0]
+ else:
+ self.infile = infile
+ self._inputs = PylotParameter(infile)
self._props = None
self.gain = 1.
@@ -178,6 +187,7 @@ class MainWindow(QMainWindow):
self.table_headers = ['', 'Event', 'Time', 'Lat', 'Lon', 'Depth', 'Ml', 'Mw', '[N] MP', '[N] AP', 'Tuning Set',
'Test Set', 'Notes']
+ # TODO: refactor rootpath to datapath
while True:
try:
if settings.value("user/FullName", None) is None:
@@ -244,7 +254,7 @@ class MainWindow(QMainWindow):
self._inputs.export2File(infile)
self.infile = infile
- def setupUi(self):
+ def setupUi(self, use_logwidget=True):
try:
self.startTime = min(
[tr.stats.starttime for tr in self.data.wfdata])
@@ -423,9 +433,9 @@ class MainWindow(QMainWindow):
None, paraIcon,
"Modify Parameter")
self.deleteAutopicksAction = self.createAction(self, "Delete Autopicks",
- self.deleteAllAutopicks,
- None, deleteIcon,
- "Delete all automatic picks from Project.")
+ self.deleteAllAutopicks,
+ None, deleteIcon,
+ "Delete all automatic picks from Project.")
self.filterActionP = createAction(parent=self, text='Apply P Filter',
slot=self.filterP,
icon=self.filter_icon_p,
@@ -479,8 +489,9 @@ class MainWindow(QMainWindow):
self.qualities_action.setVisible(True)
self.eventlist_xml_action = self.createAction(parent=self, text='Create Eventlist from XML',
- slot=self.eventlistXml, shortcut='Alt+X',
- icon=eventlist_xml_icon, tip='Create an Eventlist from a XML File')
+ slot=self.eventlistXml, shortcut='Alt+X',
+ icon=eventlist_xml_icon,
+ tip='Create an Eventlist from a XML File')
self.eventlist_xml_action.setEnabled(False)
printAction = self.createAction(self, "&Print event ...",
@@ -492,11 +503,8 @@ class MainWindow(QMainWindow):
"""Show either the documentation
homepage (internet connection available),
or shipped documentation files.""")
-
- # phaseToolBar = self.addToolBar("PhaseTools")
- # phaseToolActions = (self.selectPAction, self.selectSAction)
- # phaseToolBar.setObjectName("PhaseTools")
- # self.addActions(phaseToolBar, phaseToolActions)
+ logAction = self.createAction(self, "&Show Log", self.showLogWidget,
+ tip="""Display Log""")
# create button group for component selection
@@ -551,7 +559,8 @@ 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, self.qualities_action, self.eventlist_xml_action)
+ pickActions = (
+ self.auto_tune, self.auto_pick, self.compare_action, self.qualities_action, self.eventlist_xml_action)
# pickToolBar = self.addToolBar("PickTools")
# pickToolActions = (selectStation, )
@@ -562,7 +571,7 @@ class MainWindow(QMainWindow):
shortcut='Alt+Ctrl+L',
icon=locate_icon,
tip='Locate the event using '
- 'the displayed manual arrivals.')
+ 'the displayed manual arrivals.')
self.locateEventAction.setEnabled(False)
locationToolActions = (self.locateEventAction,)
@@ -592,11 +601,10 @@ class MainWindow(QMainWindow):
self.autoPickMenu = self.pickMenu.addMenu(self.autopicksicon_small, 'Automatic picking')
self.autoPickMenu.setEnabled(False)
-
autoPickActions = (self.auto_pick, self.auto_pick_local, self.auto_pick_sge)
self.helpMenu = self.menuBar().addMenu('&Help')
- helpActions = (helpAction,)
+ helpActions = (helpAction, logAction)
fileToolActions = (self.newProjectAction,
self.openProjectAction, self.saveProjectAction,
@@ -674,7 +682,7 @@ class MainWindow(QMainWindow):
self._main_layout.addWidget(self.mainProgressBarWidget)
# add scroll area used in case number of traces gets too high
- self.wf_scroll_area = QtGui.QScrollArea(self)
+ self.wf_scroll_area = QtWidgets.QScrollArea(self)
self.wf_scroll_area.setVisible(False)
self.no_data_label = QLabel('No Data')
self.no_data_label.setStyleSheet('color: red')
@@ -684,14 +692,14 @@ class MainWindow(QMainWindow):
self.init_wfWidget()
# init main widgets for main tabs
- wf_tab = QtGui.QWidget(self)
- array_tab = QtGui.QWidget(self)
- events_tab = QtGui.QWidget(self)
+ wf_tab = QtWidgets.QWidget(self)
+ array_tab = QtWidgets.QWidget(self)
+ events_tab = QtWidgets.QWidget(self)
# init main widgets layouts
- self.wf_layout = QtGui.QVBoxLayout()
- self.array_layout = QtGui.QVBoxLayout()
- self.events_layout = QtGui.QVBoxLayout()
+ self.wf_layout = QtWidgets.QVBoxLayout()
+ self.array_layout = QtWidgets.QVBoxLayout()
+ self.events_layout = QtWidgets.QVBoxLayout()
wf_tab.setLayout(self.wf_layout)
array_tab.setLayout(self.array_layout)
events_tab.setLayout(self.events_layout)
@@ -723,8 +731,16 @@ class MainWindow(QMainWindow):
_widget.setLayout(self._main_layout)
_widget.showFullScreen()
- self.setCentralWidget(_widget)
+ if use_logwidget:
+ self.logwidget = LogWidget(parent=None)
+ self.logwidget.show()
+ self.stdout = self.logwidget.stdout
+ self.stderr = self.logwidget.stderr
+ sys.stdout = self.stdout
+ sys.stderr = self.stderr
+
+ self.setCentralWidget(_widget)
def init_wfWidget(self):
xlab = self.startTime.strftime('seconds since %Y/%m/%d %H:%M:%S (%Z)')
@@ -742,8 +758,8 @@ class MainWindow(QMainWindow):
'''
Initiate/create buttons for assigning events containing manual picks to reference or test set.
'''
- self.ref_event_button = QtGui.QPushButton('Tune')
- self.test_event_button = QtGui.QPushButton('Test')
+ self.ref_event_button = QtWidgets.QPushButton('Tune')
+ self.test_event_button = QtWidgets.QPushButton('Test')
self.ref_event_button.setMinimumWidth(100)
self.test_event_button.setMinimumWidth(100)
self.ref_event_button.setToolTip('Set manual picks of current ' +
@@ -759,6 +775,10 @@ class MainWindow(QMainWindow):
self.ref_event_button.setEnabled(False)
self.test_event_button.setEnabled(False)
+ def showLogWidget(self):
+ self.logwidget.show()
+ self.logwidget.activateWindow()
+
def keyPressEvent(self, event):
if event.key() == QtCore.Qt.Key.Key_Control:
self._ctrl = True
@@ -771,7 +791,6 @@ class MainWindow(QMainWindow):
if event.key() == QtCore.Qt.Key.Key_R:
self.reset_gain()
-
def keyReleaseEvent(self, event):
if event.key() == QtCore.Qt.Key.Key_Control:
self._ctrl = False
@@ -791,7 +810,7 @@ class MainWindow(QMainWindow):
def modify_gain(self, direction, factor):
assert (direction in ['+', '-']), 'unknown direction'
if self._ctrl:
- factor = factor**3
+ factor = factor ** 3
if direction == '+':
self.gain *= factor
elif direction == '-':
@@ -943,7 +962,6 @@ class MainWindow(QMainWindow):
self.recentProjectsMenu.addAction(action)
-
@property
def inputs(self):
return self._inputs
@@ -981,7 +999,7 @@ class MainWindow(QMainWindow):
if not sld.exec_():
return
fext = sld.lineEdit.text()
- #fext = '.xml'
+ # fext = '.xml'
for event in events:
path = event.path
eventname = path.split('/')[-1] # or event.pylot_id
@@ -1013,7 +1031,7 @@ class MainWindow(QMainWindow):
data_new = Data(self, evtdata=str(fname))
# MP MP commented because adding several picks might cause inconsistencies
data = data_new
- #data += data_new
+ # data += data_new
except ValueError:
qmb = QMessageBox(self, icon=QMessageBox.Question,
text='Warning: Missmatch in event identifiers {} and {}. Continue?'.format(
@@ -1162,13 +1180,14 @@ class MainWindow(QMainWindow):
eventlist_file = os.path.join(basepath, 'eventlist.txt')
if os.path.isfile(eventlist_file):
with open(eventlist_file, 'r') as infile:
- eventlist_subset = [os.path.join(basepath, filename.split('\n')[0]) for filename in infile.readlines()]
+ eventlist_subset = [os.path.join(basepath, filename.split('\n')[0]) for filename in
+ infile.readlines()]
msg = 'Found file "eventlist.txt" in database path. WILL ONLY USE SELECTED EVENTS out of {} events ' \
'contained in this subset'
print(msg.format(len(eventlist_subset)))
eventlist = [eventname for eventname in eventlist if eventname in eventlist_subset]
if check_obspydmt_structure(basepath):
- print('Recognized obspyDMT structure in selected files. Settings Datastructure to ObspyDMT')
+ print('Recognized obspyDMT structure in selected files. Setting Datastructure to ObspyDMT')
self.dataStructure = DATASTRUCTURE['obspyDMT']()
eventlist = check_all_obspy(eventlist)
else:
@@ -1192,13 +1211,14 @@ class MainWindow(QMainWindow):
if system_name in ["Linux", "Darwin"]:
dirs = {
'database': path.split('/')[-2],
- 'datapath': path.split('/')[-3],
+ 'datapath': os.path.split(path)[0], # path.split('/')[-3],
'rootpath': '/' + os.path.join(*path.split('/')[:-3])
}
elif system_name == "Windows":
rootpath = path.split('/')[:-3]
rootpath[0] += '/'
dirs = {
+ # TODO: Arrange path to meet Win standards
'database': path.split('/')[-2],
'datapath': path.split('/')[-3],
'rootpath': os.path.join(*rootpath)
@@ -1212,7 +1232,7 @@ class MainWindow(QMainWindow):
print('Warning: Could not automatically init folder structure. ({})'.format(e))
settings = QSettings()
- settings.setValue("data/dataRoot", dirs['rootpath'])
+ settings.setValue("data/dataRoot", dirs['datapath']) # d irs['rootpath'])
settings.sync()
if not self.project.eventlist:
@@ -1230,14 +1250,16 @@ class MainWindow(QMainWindow):
if not dirs_box.exec_():
return
self.project.rootpath = dirs['rootpath']
+ self.project.datapath = dirs['datapath']
else:
- if hasattr(self.project, 'rootpath'):
- if not self.project.rootpath == dirs['rootpath']:
+ if hasattr(self.project, 'datapath'):
+ if not self.project.datapath == dirs['datapath']:
QMessageBox.warning(self, "PyLoT Warning",
- 'Rootpath missmatch to current project!')
+ 'Datapath missmatch to current project!')
return
else:
self.project.rootpath = dirs['rootpath']
+ self.project.datapath = dirs['datapath']
self.project.add_eventlist(eventlist)
self.init_events()
@@ -1295,7 +1317,7 @@ class MainWindow(QMainWindow):
tabindex = self.tabs.currentIndex()
def user_modify_path(self, reason=''):
- dialog = QtGui.QInputDialog(parent=self)
+ dialog = QtWidgets.QInputDialog(parent=self)
new_path, executed = dialog.getText(self, 'Change Project rootpath',
'{}Rename project path {}:'.format(reason, self.project.rootpath))
return new_path, executed
@@ -1324,6 +1346,7 @@ class MainWindow(QMainWindow):
return True
def modify_project_path(self, new_rootpath):
+ # TODO: change root to datapath
self.project.rootpath = new_rootpath
for event in self.project.eventlist:
event.rootpath = new_rootpath
@@ -1348,13 +1371,13 @@ class MainWindow(QMainWindow):
if not eventBox:
eventBox = self.eventBox
index = eventBox.currentIndex()
- tv = QtGui.QTableView()
+ tv = QtWidgets.QTableView()
header = tv.horizontalHeader()
- header.setResizeMode(QtGui.QHeaderView.ResizeToContents)
+ header.setResizeMode(QtWidgets.QHeaderView.ResizeToContents)
header.setStretchLastSection(True)
header.hide()
tv.verticalHeader().hide()
- tv.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows)
+ tv.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
current_event = self.get_current_event()
@@ -1393,18 +1416,25 @@ class MainWindow(QMainWindow):
event_ref = event.isRefEvent()
event_test = event.isTestEvent()
- time = lat = lon = depth = localmag = momentmag = None
+ time = lat = lon = depth = localmag = None
if len(event.origins) == 1:
origin = event.origins[0]
time = origin.time + 0 # add 0 because there was an exception for time = 0s
lat = origin.latitude
lon = origin.longitude
depth = origin.depth
- if len(event.magnitudes) > 1:
+ if len(event.magnitudes): # if magnitude information exists, i.e., event.magnitudes has at least 1 entry
moment_magnitude = event.magnitudes[0]
- local_magnitude = event.magnitudes[1]
- localmag = '%4.1f' % local_magnitude.mag
- momentmag = '%4.1f'% moment_magnitude.mag
+ momentmag = '%4.1f' % moment_magnitude.mag
+ if len(event.magnitudes) > 1:
+ local_magnitude = event.magnitudes[1]
+ localmag = '%4.1f' % local_magnitude.mag
+ else:
+ localmag = ' '
+
+ else:
+ momentmag = ' '
+ localmag = ' '
# text = '{path:{plen}} | manual: [{p:3d}] | auto: [{a:3d}]'
# text = text.format(path=event_path,
@@ -1415,24 +1445,24 @@ class MainWindow(QMainWindow):
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_lat = QtGui.QStandardItem('{}'.format(lat))
- item_lon = QtGui.QStandardItem('{}'.format(lon))
- item_depth = QtGui.QStandardItem('{}'.format(depth))
- item_localmag = QtGui.QStandardItem('{}'.format(localmag))
- item_momentmag = QtGui.QStandardItem('{}'.format(momentmag))
- item_nmp = QtGui.QStandardItem('{}({})'.format(ma_count['manual'], ma_count_total['manual']))
+ item_path = QStandardItem(event_str)
+ item_time = QStandardItem('{}'.format(time))
+ item_lat = QStandardItem('{}'.format(lat))
+ item_lon = QStandardItem('{}'.format(lon))
+ item_depth = QStandardItem('{}'.format(depth))
+ item_localmag = QStandardItem('{}'.format(localmag))
+ item_momentmag = QStandardItem('{}'.format(momentmag))
+ item_nmp = QStandardItem('{}({})'.format(ma_count['manual'], ma_count_total['manual']))
item_nmp.setIcon(self.manupicksicon_small)
- item_nap = QtGui.QStandardItem('{}({})'.format(ma_count['auto'], ma_count_total['auto']))
+ item_nap = QStandardItem('{}({})'.format(ma_count['auto'], ma_count_total['auto']))
item_nap.setIcon(self.autopicksicon_small)
- item_ref = QtGui.QStandardItem() # str(event_ref))
- item_test = QtGui.QStandardItem() # str(event_test))
+ item_ref = QStandardItem() # str(event_ref))
+ item_test = QStandardItem() # str(event_test))
if event_ref:
item_ref.setBackground(self._ref_test_colors['ref'])
if event_test:
item_test.setBackground(self._ref_test_colors['test'])
- item_notes = QtGui.QStandardItem(event.notes)
+ item_notes = QStandardItem(event.notes)
openIcon = self.style().standardIcon(QStyle.SP_DirOpenIcon)
item_path.setIcon(openIcon)
@@ -1478,7 +1508,7 @@ class MainWindow(QMainWindow):
wf_dir = wf_stat[self.data.processed]
if wf_dir is not None:
wf_path = os.path.join(event_path, wf_dir)
- if wf_dir is 'processed' and not os.path.exists(wf_path):
+ if wf_dir == 'processed' and not os.path.exists(wf_path):
wf_path = os.path.join(event_path, 'raw')
else:
wf_path = event_path
@@ -1516,6 +1546,7 @@ class MainWindow(QMainWindow):
event = self.get_current_event()
if not type(outformats) == list:
outformats = [outformats]
+
def getSavePath(event, directory, outformats):
if not directory:
title = 'Save event data as {} to directory ...'.format(outformats)
@@ -1638,14 +1669,14 @@ class MainWindow(QMainWindow):
self.cmpw.show()
def pickQualities(self):
- path = self._inputs['rootpath'] + '/' + self._inputs['datapath'] + '/' + self._inputs['database']
- getQualitiesfromxml(path)
+ path = self.get_current_event_path()
+ getQualitiesfromxml(path, self._inputs.get('timeerrorsP'), self._inputs.get('timeerrorsS'), plotflag=1)
return
def eventlistXml(self):
- path = self._inputs['rootpath'] + '/' + self._inputs['datapath'] + '/' + self._inputs['database']
+ path = self._inputs['rootpath'] + '/' + self._inputs['datapath'] + '/' + self._inputs['database']
outpath = self.project.location[:self.project.location.rfind('/')]
- geteventlistfromxml(path, outpath)
+ geteventlistfromxml(path, outpath)
return
def compareMulti(self):
@@ -1885,6 +1916,10 @@ class MainWindow(QMainWindow):
settings = QSettings()
curr_event = self.get_current_event()
+ if not curr_event:
+ print('Could not find current event. Try reload?')
+ return
+
if len(curr_event.origins) > 0:
origin_time = curr_event.origins[0].time
tstart = settings.value('tstart') if get_None(settings.value('tstart')) else 0
@@ -1900,7 +1935,7 @@ class MainWindow(QMainWindow):
checkRotated=True,
metadata=self.metadata,
tstart=tstart,
- tstop=tstop,)
+ tstop=tstop, )
def prepareObspyDMT_data(self, eventpath):
qcbox_processed = self.dataPlot.qcombo_processed
@@ -2097,7 +2132,7 @@ class MainWindow(QMainWindow):
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, obspy_dmt_inv = True)
+ self.metadata.add_inventory(invpath, obspy_dmt_inv=True)
# check if directory is empty
if os.listdir(invpath):
self.init_map_button.setEnabled(True)
@@ -2500,7 +2535,7 @@ class MainWindow(QMainWindow):
if not seed_id:
seed_id = self.getTraceID(wfID)
try:
- network, station, location = seed_id.split('.')[:3]
+ network, station, location = seed_id.split('.')[:3]
except:
print("Warning! No network, station, and location info available!")
return
@@ -2628,7 +2663,7 @@ class MainWindow(QMainWindow):
self.init_fig_dict()
# if not self.tap:
# init TuneAutopicker object
- self.tap = TuneAutopicker(self, self.obspy_dmt)
+ self.tap = TuneAutopicker(self)
# first call of update to init tabs with empty canvas
self.update_autopicker()
# connect update signal of TuneAutopicker with update function
@@ -2670,9 +2705,9 @@ class MainWindow(QMainWindow):
# init event selection options for autopick
self.pickoptions = [('current event', self.get_current_event, None),
('tune events', self.get_ref_events, self._style['ref']['rgba']),
- ('test events', self.get_test_events, self._style['test']['rgba']),]
- #('all (picked) events', self.get_manu_picked_events, None),
- #('all events', self.get_all_events, None)]
+ ('test events', self.get_test_events, self._style['test']['rgba']), ]
+ # ('all (picked) events', self.get_manu_picked_events, None),
+ # ('all events', self.get_all_events, None)]
self.listWidget = QListWidget()
self.setDirty(True)
@@ -2870,7 +2905,7 @@ class MainWindow(QMainWindow):
def safetyCopy(self, event_path):
fpath = self.get_deleted_picks_fpath(event_path)
- fpath_new = fpath.split('.json')[0] + '_copy_{}.json'.format(datetime.now()).replace(' ', '_')
+ fpath_new = fpath.split('.json')[0] + '_copy_{}.json'.format(datetime.now()).replace(' ', '_')
shutil.move(fpath, fpath_new)
def load_deleted_picks(self, event_path):
@@ -2894,7 +2929,6 @@ class MainWindow(QMainWindow):
event.addPicks(picksdict['manual'])
event.addAutopicks(picksdict['auto'])
-
def drawPicks(self, station=None, picktype=None, stime=None):
# if picktype not specified, draw both
if not stime:
@@ -2947,7 +2981,7 @@ class MainWindow(QMainWindow):
phaseID = self.getPhaseID(phase)
# get quality classes
- if phaseID == 'P':
+ if phaseID == 'P':
quality = getQualityFromUncertainty(picks['spe'], self._inputs['timeerrorsP'])
elif phaseID == 'S':
quality = getQualityFromUncertainty(picks['spe'], self._inputs['timeerrorsS'])
@@ -3100,11 +3134,12 @@ class MainWindow(QMainWindow):
self.metadata_widget.setLayout(grid_layout)
self.array_layout.addWidget(self.metadata_widget)
- def init_array_map(self, index=1):
+ def init_array_map(self, checked=0, index=1):
'''
Try to init array map widget. If no metadata are given,
self.get_metadata will be called.
'''
+ if checked: pass # dummy argument for QAction trigger signal
self.tabs.setCurrentIndex(1)
# if there is no metadata (invetories is an empty list), just initialize the default empty tab
if not self.metadata.inventories:
@@ -3195,24 +3230,24 @@ class MainWindow(QMainWindow):
# changes attributes of the corresponding event
table = self.project._table
event = self.project.getEventFromPath(table[row][1].text().split('*')[0])
- if column == 9 or column == 10:
+ if column == 10 or column == 11:
# toggle checked states (exclusive)
- item_ref = table[row][9]
- item_test = table[row][10]
- if column == 9 and item_ref.checkState():
+ item_ref = table[row][10]
+ item_test = table[row][11]
+ if column == 10 and item_ref.checkState():
item_test.setCheckState(QtCore.Qt.Unchecked)
event.setRefEvent(True)
- elif column == 9 and not item_ref.checkState():
+ elif column == 10 and not item_ref.checkState():
event.setRefEvent(False)
- elif column == 10 and item_test.checkState():
+ elif column == 11 and item_test.checkState():
item_ref.setCheckState(QtCore.Qt.Unchecked)
event.setTestEvent(True)
- elif column == 10 and not item_test.checkState():
+ elif column == 11 and not item_test.checkState():
event.setTestEvent(False)
self.fill_eventbox()
- elif column == 11:
+ elif column == 12:
# update event notes
- notes = table[row][11].text()
+ notes = table[row][12].text()
event.addNotes(notes)
self.fill_eventbox()
@@ -3232,14 +3267,14 @@ class MainWindow(QMainWindow):
self.events_layout.removeWidget(self.event_table)
# init new qtable
- self.event_table = QtGui.QTableWidget(self)
- self.event_table.setColumnCount(12)
+ self.event_table = QTableWidget(self)
+ self.event_table.setColumnCount(len(self.table_headers))
self.event_table.setRowCount(len(eventlist))
self.event_table.setHorizontalHeaderLabels(self.table_headers)
# iterate through eventlist and generate items for table rows
self.project._table = []
- for index, event in enumerate(eventlist):
+ for index, event in enumerate(eventlist):
phaseErrors = {'P': self._inputs['timeerrorsP'],
'S': self._inputs['timeerrorsS']}
@@ -3247,8 +3282,6 @@ class MainWindow(QMainWindow):
'auto': event.pylot_autopicks}
ma_count = {'manual': 0,
'auto': 0}
- ma_count_total = {'manual': 0,
- 'auto': 0}
for ma in ma_props.keys():
if ma_props[ma]:
@@ -3258,25 +3291,24 @@ class MainWindow(QMainWindow):
continue
if pick.get('spe'):
ma_count[ma] += 1
- ma_count_total[ma] += 1
# init table items for current row
- item_delete = QtGui.QTableWidgetItem()
+ item_delete = QTableWidgetItem()
item_delete.setIcon(del_icon)
- item_path = QtGui.QTableWidgetItem()
- item_time = QtGui.QTableWidgetItem()
- item_lat = QtGui.QTableWidgetItem()
- item_lon = QtGui.QTableWidgetItem()
- item_depth = QtGui.QTableWidgetItem()
- item_momentmag = QtGui.QTableWidgetItem()
- item_localmag = QtGui.QTableWidgetItem()
- item_nmp = QtGui.QTableWidgetItem('{}({})'.format(ma_count['manual'], ma_count_total['manual']))
+ item_path = QTableWidgetItem()
+ item_time = QTableWidgetItem()
+ item_lat = QTableWidgetItem()
+ item_lon = QTableWidgetItem()
+ item_depth = QTableWidgetItem()
+ item_momentmag = QTableWidgetItem()
+ item_localmag = QTableWidgetItem()
+ item_nmp = QTableWidgetItem('{}'.format(ma_count['manual'])) # , ma_count_total['manual']))
item_nmp.setIcon(self.manupicksicon_small)
- item_nap = QtGui.QTableWidgetItem('{}({})'.format(ma_count['auto'], ma_count_total['auto']))
+ item_nap = QTableWidgetItem('{}'.format(ma_count['auto'])) # , ma_count_total['auto']))
item_nap.setIcon(self.autopicksicon_small)
- item_ref = QtGui.QTableWidgetItem()
- item_test = QtGui.QTableWidgetItem()
- item_notes = QtGui.QTableWidgetItem()
+ item_ref = QTableWidgetItem()
+ item_test = QTableWidgetItem()
+ item_notes = QTableWidgetItem()
event_str = event.path
if event.dirty:
@@ -3333,7 +3365,7 @@ class MainWindow(QMainWindow):
item_test.setCheckState(QtCore.Qt.Unchecked)
row = [item_delete, item_path, item_time, item_lat, item_lon, item_depth, item_localmag,
- item_momentmag, item_nmp, item_nap, item_ref, item_test, item_notes]
+ item_momentmag, item_nmp, item_nap, item_ref, item_test, item_notes]
self.project._table.append(row)
self.setItemColor(row, index, event, current_event)
@@ -3347,13 +3379,13 @@ class MainWindow(QMainWindow):
for r_index, row in enumerate(self.project._table):
for c_index, item in enumerate(row):
- if type(item) == QtGui.QTableWidgetItem:
+ if type(item) == QTableWidgetItem:
self.event_table.setItem(r_index, c_index, item)
- elif type(item) in [QtGui.QWidget, QtGui.QPushButton]:
+ elif type(item) in [QWidget, QPushButton]:
self.event_table.setCellWidget(r_index, c_index, item)
header = self.event_table.horizontalHeader()
- header.setResizeMode(QtGui.QHeaderView.ResizeToContents)
+ header.setResizeMode(QtWidgets.QHeaderView.ResizeToContents)
header.setStretchLastSection(True)
self.event_table.cellChanged[int, int].connect(cell_changed)
self.event_table.cellClicked[int, int].connect(cell_clicked)
@@ -3373,7 +3405,7 @@ class MainWindow(QMainWindow):
return
separator = sld.lineEdit.text()
- fd = QtGui.QFileDialog()
+ fd = QtWidgets.QFileDialog()
fname = fd.getSaveFileName(self, 'Browse for file.',
filter='Table (*.csv)')[0]
if not fname: return
@@ -3390,20 +3422,20 @@ class MainWindow(QMainWindow):
def exportProjectTable(self, filename, separator=';'):
with open(filename, 'w') as outfile:
- for header in self.table_headers[1:12]:
+ for header in self.table_headers[1:13]:
outfile.write('{}{}'.format(header, separator))
outfile.write('\n')
for row in self.project._table:
- row = row[1:12]
- event, time, lat, lon, depth, mag, nmp, nap, tune, test, notes = row
+ row = row[1:13]
+ event, time, lat, lon, depth, ml, mw, nmp, nap, tune, test, notes = row
row_str = ''
for index in range(len(row)):
- row_str += '{}'+'{}'.format(separator)
+ row_str += '{}' + '{}'.format(separator)
- row_str = row_str.format(event.text(), time.text(), lat.text(), lon.text(), depth.text(), mag.text(),
- nmp.text(), nap.text(), bool(tune.checkState()), bool(test.checkState()),
- notes.text())
+ row_str = row_str.format(event.text(), time.text(), lat.text(), lon.text(), depth.text(), ml.text(),
+ mw.text(), nmp.text(), nap.text(), bool(tune.checkState()),
+ bool(test.checkState()), notes.text())
outfile.write(row_str + '\n')
message = 'Wrote table to file: {}'.format(filename)
@@ -3428,7 +3460,6 @@ class MainWindow(QMainWindow):
if event == current_event:
set_background_color(item_list, QtGui.QColor(*(0, 143, 143, 255)))
-
def set_metadata(self):
self.project.inventories = self.metadata.inventories
if self.metadata.inventories:
@@ -3440,7 +3471,7 @@ class MainWindow(QMainWindow):
self.init_map_button.setEnabled(False)
self.initMapAction.setEnabled(False)
self.inventory_label.setText("No inventory set...")
- #self.setDirty(False)
+ # self.setDirty(False)
def add_metadata(self):
self.add_metadata_widget = AddMetadataWidget(self, metadata=self.metadata)
@@ -3643,7 +3674,7 @@ class MainWindow(QMainWindow):
autosaveXML = get_Bool(settings.value('autosaveXML', True))
if autosaveXML:
self.exportEvents()
- self.project.save(filename)
+ if not self.project.save(filename): return False
self.setDirty(False)
self.saveProjectAsAction.setEnabled(True)
self.update_status('Saved new project to {}'.format(filename), duration=5000)
@@ -3667,7 +3698,7 @@ class MainWindow(QMainWindow):
autosaveXML = get_Bool(settings.value('autosaveXML', True))
if autosaveXML:
self.exportEvents()
- self.project.save()
+ if not self.project.save(): return False
self.update_obspy_dmt()
if not self.project.dirty:
self.setDirty(False)
@@ -3688,7 +3719,7 @@ class MainWindow(QMainWindow):
else:
self.dataPlot.setPermText(1)
self.dataPlot.setPermText(0, '| Number of traces: {} | Gain: {}'.format(len(self.getPlotWidget().getPlotDict()),
- self.gain))
+ self.gain))
def _setDirty(self):
self.setDirty(True)
@@ -3702,15 +3733,17 @@ class MainWindow(QMainWindow):
def closeEvent(self, event):
if self.okToContinue():
+ self.logwidget.close()
event.accept()
else:
event.ignore()
# self.closing.emit()
# QMainWindow.closeEvent(self, event)
- def setParameter(self, show=True):
+ def setParameter(self, checked=0, show=True):
+ if checked: pass # dummy argument to receive trigger signal (checked) if called by QAction
if not self.paraBox:
- self.paraBox = PylotParaBox(self._inputs, parent=self, windowflag=1)
+ self.paraBox = PylotParaBox(self._inputs, parent=self, windowflag=Qt.Window)
self.paraBox.accepted.connect(self._setDirty)
self.paraBox.accepted.connect(self.filterOptionsFromParameter)
if show:
@@ -3733,7 +3766,6 @@ class MainWindow(QMainWindow):
self.plotWaveformDataThread()
self.refreshTabs()
-
def PyLoTprefs(self):
if not self._props:
self._props = PropertiesDlg(self, infile=self.infile,
@@ -3763,10 +3795,12 @@ class Project(object):
Pickable class containing information of a PyLoT project, like event lists and file locations.
'''
+ # TODO: remove rootpath
def __init__(self):
self.eventlist = []
self.location = None
self.rootpath = None
+ self.datapath = None
self.dirty = False
self.parameter = None
self._table = None
@@ -3855,7 +3889,7 @@ class Project(object):
if not event.datapath in datapaths:
datapaths.append(event.datapath)
for datapath in datapaths:
- datapath = os.path.join(self.rootpath, datapath)
+ # datapath = os.path.join(self.rootpath, datapath)
if os.path.isdir(datapath):
for filename in os.listdir(datapath):
filename = os.path.join(datapath, filename)
@@ -3893,34 +3927,38 @@ class Project(object):
Can be loaded by using project.load(filename).
'''
try:
- import cPickle
+ import pickle
except ImportError:
- import _pickle as cPickle
+ import _pickle as pickle
if filename:
self.location = filename
else:
filename = self.location
+ table = self._table # MP: see below
try:
outfile = open(filename, 'wb')
- cPickle.dump(self, outfile, -1)
+ self._table = [] # MP: Workaround as long as table cannot be saved as part of project
+ pickle.dump(self, outfile, protocol=pickle.HIGHEST_PROTOCOL)
self.setDirty(False)
+ self._table = table # MP: see above
+ return True
except Exception as e:
print('Could not pickle PyLoT project. Reason: {}'.format(e))
self.setDirty()
+ self._table = table # MP: see above
+ return False
@staticmethod
def load(filename):
'''
Load project from filename.
'''
- try:
- import cPickle
- except ImportError:
- import _pickle as cPickle
+ import pickle
infile = open(filename, 'rb')
- project = cPickle.load(infile)
+ project = pickle.load(infile)
+ infile.close()
project.location = filename
print('Loaded %s' % filename)
return project
diff --git a/README.md b/README.md
index b3444b3f..57fd990f 100644
--- a/README.md
+++ b/README.md
@@ -1,40 +1,58 @@
# PyLoT
-version: 0.2
+version: 0.3
The Python picking and Localisation Tool
-This python library contains a graphical user interfaces for picking
-seismic phases. This software needs [ObsPy][ObsPy]
-and the PySide Qt4 bindings for python to be installed first.
+This python library contains a graphical user interfaces for picking seismic phases. This software needs [ObsPy][ObsPy]
+and the PySide2 Qt5 bindings for python to be installed first.
-PILOT has originally been developed in Mathworks' MatLab. In order to
-distribute PILOT without facing portability problems, it has been decided
-to redevelop 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.
+PILOT has originally been developed in Mathworks' MatLab. In order to distribute PILOT without facing portability
+problems, it has been decided to redevelop 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 and AlpArray.
## Installation
-At the moment there is no automatic installation procedure available for PyLoT.
-Best way to install is to clone the repository and add the path to your Python path.
+At the moment there is no automatic installation procedure available for PyLoT. Best way to install is to clone the
+repository and add the path to your Python path.
+
+It is highly recommended to use Anaconda for a simple creation of a Python installation using either the *pylot.yml* or the *requirements.txt* file found in the PyLoT root directory. First make sure that the *conda-forge* channel is available in your Anaconda installation:
+
+ conda config --add channels conda-forge
+
+Afterwards run (from the PyLoT main directory where the files *requirements.txt* and *pylot.yml* are located)
+
+ conda create --name pylot_38 --file requirements.txt
+or
+
+ conda env create -f pylot.yml
+
+to create a new Anaconda environment called "pylot_38".
+
+Afterwards activate the environment by typing
+
+ conda activate pylot_38
#### Prerequisites:
In order to run PyLoT you need to install:
-- python 2 or 3
+- Python 3
+- obspy
+- pyside2
+- pyqtgraph
+- cartopy
+
+(the following are already dependencies of the above packages):
- scipy
- numpy
-- matplotlib
-- obspy
-- pyside
+- matplotlib <= 3.3.x
#### Some handwork:
-PyLoT needs a properties folder on your system to work. It should be situated in your home directory
+PyLoT needs a properties folder on your system to work. It should be situated in your home directory
(on Windows usually C:/Users/*username*):
mkdir ~/.pylot
@@ -53,7 +71,8 @@ In the next step you have to copy some files to this directory:
cp path-to-pylot/inputs/pylot_global.in ~/.pylot/pylot.in
-and some extra information on error estimates (just needed for reading old PILOT data) and the Richter magnitude scaling relation
+and some extra information on error estimates (just needed for reading old PILOT data) and the Richter magnitude scaling
+relation
cp path-to-pylot/inputs/PILOT_TimeErrors.in path-to-pylot/inputs/richter_scaling.data ~/.pylot/
@@ -61,7 +80,6 @@ You may need to do some modifications to these files. Especially folder names sh
PyLoT has been tested on Mac OSX (10.11), Debian Linux 8 and on Windows 10.
-
## Release notes
#### Features:
@@ -71,22 +89,19 @@ PyLoT has been tested on Mac OSX (10.11), Debian Linux 8 and on Windows 10.
- consistent automatic phase picking routines using Higher Order Statistics, AIC and Autoregression
- interactive tuning of auto-pick parameters
- uniform uncertainty estimation from waveform's properties for automatic and manual picks
-- pdf representation and comparison of picks taking the uncertainty intrinsically into account
+- pdf representation and comparison of picks taking the uncertainty intrinsically into account
- Richter and moment magnitude estimation
- location determination with external installation of [NonLinLoc](http://alomax.free.fr/nlloc/index.html)
#### Known issues:
-- Sometimes an error might occur when using Qt
-
We hope to solve these with the next release.
## Staff
Original author(s): L. Kueperkoch, S. Wehling-Benatelli, M. Bischoff (PILOT)
-Developer(s): S. Wehling-Benatelli, L. Kueperkoch, K. Olbert, M. Bischoff,
- C. Wollin, M. Rische, M. Paffrath
+Developer(s): S. Wehling-Benatelli, L. Kueperkoch, K. Olbert, M. Bischoff, C. Wollin, M. Rische, M. Paffrath
Others: A. Bruestle, T. Meier, W. Friederich
diff --git a/autoPyLoT.py b/autoPyLoT.py
index 703f068d..00da0797 100755
--- a/autoPyLoT.py
+++ b/autoPyLoT.py
@@ -8,6 +8,7 @@ import datetime
import glob
import os
import traceback
+
from obspy import read_events
from obspy.core.event import ResourceIdentifier
@@ -145,7 +146,7 @@ def autoPyLoT(input_dict=None, parameter=None, inputfile=None, fnames=None, even
exf = ['root', 'dpath', 'dbase']
- if parameter['eventID'] is not '*' and fnames == 'None':
+ if parameter['eventID'] != '*' and fnames == 'None':
dsfields['eventID'] = parameter['eventID']
exf.append('eventID')
@@ -189,12 +190,12 @@ def autoPyLoT(input_dict=None, parameter=None, inputfile=None, fnames=None, even
if not input_dict:
# started in production mode
datapath = datastructure.expandDataPath()
- if fnames == 'None' and parameter['eventID'] is '*':
+ if fnames == 'None' and parameter['eventID'] == '*':
# multiple event processing
# read each event in database
events = [event for event in glob.glob(os.path.join(datapath, '*')) if
(os.path.isdir(event) and not event.endswith('EVENTS-INFO'))]
- elif fnames == 'None' and parameter['eventID'] is not '*' and not type(parameter['eventID']) == list:
+ elif fnames == 'None' and parameter['eventID'] != '*' and not type(parameter['eventID']) == list:
# single event processing
events = glob.glob(os.path.join(datapath, parameter['eventID']))
elif fnames == 'None' and type(parameter['eventID']) == list:
@@ -277,7 +278,7 @@ def autoPyLoT(input_dict=None, parameter=None, inputfile=None, fnames=None, even
if not wfdat:
print('Could not find station {}. STOP!'.format(station))
return
- #wfdat = remove_underscores(wfdat)
+ # wfdat = remove_underscores(wfdat)
# trim components for each station to avoid problems with different trace starttimes for one station
wfdat = check4gapsAndRemove(wfdat)
wfdat = check4doubled(wfdat)
diff --git a/docs/gui.md b/docs/gui.md
index cb2bea41..4fc735c1 100644
--- a/docs/gui.md
+++ b/docs/gui.md
@@ -2,36 +2,36 @@
- [PyLoT Documentation](#pylot-documentation)
- [PyLoT GUI](#pylot-gui)
- - [First start](#first-start)
- - [Main Screen](#main-screen)
- - [Waveform Plot](#waveform-plot)
- - [Mouse view controls](#mouse-view-controls)
- - [Buttons](#buttons)
- - [Array Map](#array-map)
- - [Eventlist](#eventlist)
- - [Usage](#usage)
- - [Projects and Events](#projects-and-events)
- - [Event folder structure](#event-folder-structure)
- - [Loading event information from CSV file](#loading-event-information-from-csv-file)
- - [Adding events to project](#adding-events-to-project)
- - [Saving projects](#saving-projects)
- - [Adding metadata](#adding-metadata)
+ - [First start](#first-start)
+ - [Main Screen](#main-screen)
+ - [Waveform Plot](#waveform-plot)
+ - [Mouse view controls](#mouse-view-controls)
+ - [Buttons](#buttons)
+ - [Array Map](#array-map)
+ - [Eventlist](#eventlist)
+ - [Usage](#usage)
+ - [Projects and Events](#projects-and-events)
+ - [Event folder structure](#event-folder-structure)
+ - [Loading event information from CSV file](#loading-event-information-from-csv-file)
+ - [Adding events to project](#adding-events-to-project)
+ - [Saving projects](#saving-projects)
+ - [Adding metadata](#adding-metadata)
- [Picking](#picking)
- - [Manual Picking](#manual-picking)
- - [Picking window](#picking-window)
- - [Picking Window Settings](#picking-window-settings)
- - [Filtering](#filtering)
- - [Export and Import of manual picks](#export-and-import-of-manual-picks)
- - [Export](#export)
- - [Import](#import)
- - [Automatic Picking](#automatic-picking)
- - [Tuning](#tuning)
- - [Production run of the autopicker](#production-run-of-the-autopicker)
- - [Evaluation of automatic picks](#evaluation-of-automatic-picks)
- - [1. Jackknife check](#1-jackknife-check)
- - [2. Wadati check](#2-wadati-check)
- - [Comparison between automatic and manual picks](#comparison-between-automatic-and-manual-picks)
- - [Export and Import of automatic picks](#export-and-import-of-automatic-picks)
+ - [Manual Picking](#manual-picking)
+ - [Picking window](#picking-window)
+ - [Picking Window Settings](#picking-window-settings)
+ - [Filtering](#filtering)
+ - [Export and Import of manual picks](#export-and-import-of-manual-picks)
+ - [Export](#export)
+ - [Import](#import)
+ - [Automatic Picking](#automatic-picking)
+ - [Tuning](#tuning)
+ - [Production run of the autopicker](#production-run-of-the-autopicker)
+ - [Evaluation of automatic picks](#evaluation-of-automatic-picks)
+ - [1. Jackknife check](#1-jackknife-check)
+ - [2. Wadati check](#2-wadati-check)
+ - [Comparison between automatic and manual picks](#comparison-between-automatic-and-manual-picks)
+ - [Export and Import of automatic picks](#export-and-import-of-automatic-picks)
- [Location determination](#location-determination)
- [FAQ](#faq)
@@ -44,15 +44,17 @@ This section describes how to use PyLoT graphically to view waveforms and create
After opening PyLoT for the first time, the setup routine asks for the following information:
Questions:
+
1. Full Name
2. Authority: Enter authority/institution name
3. Format: Enter output format (*.xml, *.cnv, *.obs)
- [//]: <> (TODO: explain what these things mean, where they are used)
+[//]: <> (TODO: explain what these things mean, where they are used)
## Main Screen
-After entering the [information](#first-start), PyLoTs main window is shown. It defaults to a view of the [Waveform Plot](#waveform-plot), which starts empty.
+After entering the [information](#first-start), PyLoTs main window is shown. It defaults to a view of
+the [Waveform Plot](#waveform-plot), which starts empty.
@@ -61,24 +63,21 @@ Add trace data by [loading a project](#projects-and-events) or by [adding event
### Waveform Plot
The waveform plot shows a trace list of all stations of an event.
-Click on any trace to open the stations [picking window](#picking-window), where you can review automatic and manual picks.
+Click on any trace to open the stations [picking window](#picking-window), where you can review automatic and manual
+picks.
-Above the traces the currently displayed event can be selected.
-In the bottom bar information about the trace under the mouse cursor is shown. This information includes the station name (station), the absolute UTC time (T) of the point under the mouse cursor and the relative time since the first trace start in seconds (t) as well as a trace count.
+Above the traces the currently displayed event can be selected. In the bottom bar information about the trace under the
+mouse cursor is shown. This information includes the station name (station), the absolute UTC time (T) of the point
+under the mouse cursor and the relative time since the first trace start in seconds (t) as well as a trace count.
-#### Mouse view controls
+#### Mouse view controls
Hold left mouse button and drag to pan view.
-Hold right mouse button and
-Direction | Result
---- | ---
-Move the mouse up | Increase amplitude scale
-Move the mouse down | Decrease amplitude scale
-Move the mouse right | Increase time scale
-Move the mouse left | Decrease time scale
+Hold right mouse button and Direction | Result --- | --- Move the mouse up | Increase amplitude scale Move the mouse
+down | Decrease amplitude scale Move the mouse right | Increase time scale Move the mouse left | Decrease time scale
Press right mouse button and click "View All" from the context menu to reset the view.
@@ -86,81 +85,100 @@ Press right mouse button and click "View All" from the context menu to reset the
[//]: <> (Hack: We need these invisible spaces to add space to the first column, otherwise )
-Icon | Description
---- | ---
- | Create a new project, for more information about projects see [Projects and Events](#projects-and-events).
- | Load a project file from disk.
- | Save all current events into an associated project file on disk. If there is no project file currently associated, you will be asked to create a new one.
- | Save all current events into a new project file on disk. See [Saving projects](#saving-projects).
- | Add event data by selecting directories containing waveforms. For more information see [Event folder structure](#event-folder-structure).
- | Load picks/origins from disk into the currently displayed event. If a pick already exists for a station, the one from file will overwrite the existing one.
- | Load picks/origins for all events of the current project. PyLoT searches for files within the directory of the event and tries to load them for that event. For this function to work, the files containing picks/origins have to be named as described in [Event folder structure](#event-folder-structure). If a pick already exists for a station, the one from file will overwrite the existing one.
- | Save event information such as picks and origin to file. You will be asked to select a directory in which this information should be saved.
- | Load location information from disk,
- | Load event information from a previous, MatLab based PILOT version.
- | Display Z component of streams in waveform plot.
- | Display N component of streams in waveform plot.
- | Display E component of streams in waveform plot.
- | Open the [Tune Autopicker window](#tuning).
- | Opens a window that allows starting the autopicker for all events ([Production run of the AutoPicker](#production-run-of-the-autopicker)).
- | Compare automatic and manual picks, only available if automatic and manual picks for an event exist. See [Comparison between automatic and manual picks](#comparison-between-automatic-and-manual-picks).
- | Run a location routine (NonLinLoc) as configured in the settings on the picks. See [Location determination](#location-determination).
-
+| Icon | Description |
+|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| | Create a new project, for more information about projects see [Projects and Events](#projects-and-events). |
+| | Load a project file from disk. |
+| | Save all current events into an associated project file on disk. If there is no project file currently associated, you will be asked to create a new one. |
+| | Save all current events into a new project file on disk. See [Saving projects](#saving-projects). |
+| | Add event data by selecting directories containing waveforms. For more information see [Event folder structure](#event-folder-structure). |
+| | Load picks/origins from disk into the currently displayed event. If a pick already exists for a station, the one from file will overwrite the existing one. |
+| | Load picks/origins for all events of the current project. PyLoT searches for files within the directory of the event and tries to load them for that event. For this function to work, the files containing picks/origins have to be named as described in [Event folder structure](#event-folder-structure). If a pick already exists for a station, the one from file will overwrite the existing one. |
+| | Save event information such as picks and origin to file. You will be asked to select a directory in which this information should be saved. |
+| | Load location information from disk, |
+| | Load event information from a previous, MatLab based PILOT version. |
+| | Display Z component of streams in waveform plot. |
+| | Display N component of streams in waveform plot. |
+| | Display E component of streams in waveform plot. |
+| | Open the [Tune Autopicker window](#tuning). |
+| | Opens a window that allows starting the autopicker for all events ([Production run of the AutoPicker](#production-run-of-the-autopicker)). |
+| | Compare automatic and manual picks, only available if automatic and manual picks for an event exist. See [Comparison between automatic and manual picks](#comparison-between-automatic-and-manual-picks). |
+| | Run a location routine (NonLinLoc) as configured in the settings on the picks. See [Location determination](#location-determination). |
### Array Map
-The array map will display a color diagram to allow a visual check of the consistency of picks across multiple stations. This works by calculating the time difference of every onset to the earliest onset. Then isolines are drawn between stations with the same time difference and the areas between isolines are colored.
-The result should resemble a color gradient as the wavefront rolls over the network area. Stations where picks are earlier/later than their neighbours can be reviewed by clicking on them, which opens the [picking window](#picking-window).
+The array map will display a color diagram to allow a visual check of the consistency of picks across multiple stations.
+This works by calculating the time difference of every onset to the earliest onset. Then isolines are drawn between
+stations with the same time difference and the areas between isolines are colored.
+The result should resemble a color gradient as the wavefront rolls over the network area. Stations where picks are
+earlier/later than their neighbours can be reviewed by clicking on them, which opens
+the [picking window](#picking-window).
-Above the Array Map the picks that are used to create the map can be customized.
-The phase of picks that should be used can be selected, which allows checking the consistency of the P- and S-phase separately.
-Additionally the pick type can be set to manual, automatic or hybrid, meaning display only manual picks, automatic picks or only display automatic picks for stations where there are no manual ones.
+Above the Array Map the picks that are used to create the map can be customized. The phase of picks that should be used
+can be selected, which allows checking the consistency of the P- and S-phase separately. Additionally the pick type can
+be set to manual, automatic or hybrid, meaning display only manual picks, automatic picks or only display automatic
+picks for stations where there are no manual ones.
![Array Map](images/gui/arraymap-example.png "Array Map")
-*Array Map for an event at the Northern Mid Atlantic Ridge, between North Africa and Mexico (Lat. 22.58, Lon. -45.11). The wavefront moved from west to east over the network area (Alps and Balcan region), with the earliest onsets in blue in the west.*
+*Array Map for an event at the Northern Mid Atlantic Ridge, between North Africa and Mexico (Lat. 22.58, Lon. -45.11).
+The wavefront moved from west to east over the network area (Alps and Balcan region), with the earliest onsets in blue
+in the west.*
-To be able to display an array map PyLoT needs to load an inventory file, where the metadata of seismic stations is kept. For more information see [Metadata](#adding-metadata). Additionally automatic or manual picks need to exist for the current event.
+To be able to display an array map PyLoT needs to load an inventory file, where the metadata of seismic stations is
+kept. For more information see [Metadata](#adding-metadata). Additionally automatic or manual picks need to exist for
+the current event.
### Eventlist
-The eventlist displays event parameters. The displayed parameters are saved in the .xml file in the event folder. Events can be deleted from the project by pressing the red X in the leftmost column of the corresponding event.
+The eventlist displays event parameters. The displayed parameters are saved in the .xml file in the event folder. Events
+can be deleted from the project by pressing the red X in the leftmost column of the corresponding event.
-Column | Description
---- | ---
-Event | Full path to the events folder.
-Time | Time of event.
-Lat | Latitude in degrees of event location.
-Lon | Longitude in degrees of event location.
-Depth | Depth in km of event.
-Mag | Magnitude of event.
-[N] MP | Number of manual picks.
-[N] AP | Number of automatic picks.
-Tuning Set | Select whether this event is a Tuning event. See [Automatic Picking](#automatic-picking).
-Test Set | Select whether this event is a Test event. See [Automatic Picking](#automatic-picking).
-Notes | Free form text field for notes regarding this event. Text will be saved in the notes.txt file in the event folder.
+| Column | Description |
+|------------|--------------------------------------------------------------------------------------------------------------------|
+| Event | Full path to the events folder. |
+| Time | Time of event. |
+| Lat | Latitude in degrees of event location. |
+| Lon | Longitude in degrees of event location. |
+| Depth | Depth in km of event. |
+| Mag | Magnitude of event. |
+| [N] MP | Number of manual picks. |
+| [N] AP | Number of automatic picks. |
+| Tuning Set | Select whether this event is a Tuning event. See [Automatic Picking](#automatic-picking). |
+| Test Set | Select whether this event is a Test event. See [Automatic Picking](#automatic-picking). |
+| Notes | Free form text field for notes regarding this event. Text will be saved in the notes.txt file in the event folder. |
## Usage
### Projects and Events
-PyLoT uses projects to categorize different seismic data. A project consists of one or multiple events. Events contain seismic traces from one or multiple stations. An event also contains further information, e.g. origin time, source parameters and automatic as well as manual picks.
-Projects are used to group events which should be analysed together. A project could contain all events from a specific region within a timeframe of interest or all recorded events of a seismological experiment.
+PyLoT uses projects to categorize different seismic data. A project consists of one or multiple events. Events contain
+seismic traces from one or multiple stations. An event also contains further information, e.g. origin time, source
+parameters and automatic as well as manual picks. Projects are used to group events which should be analysed together. A
+project could contain all events from a specific region within a timeframe of interest or all recorded events of a
+seismological experiment.
### Event folder structure
PyLoT expects the following folder structure for seismic data:
+
* Every event should be in it's own folder with the following naming scheme for the folders:
- ``e[id].[doy].[yy]``, where ``[id]`` is a four-digit numerical id increasing from 0001, ``[doy]`` the three digit day of year and ``[yy]`` the last two digits of the year of the event. This structure has to be created by the user of PyLoT manually.
+ ``e[id].[doy].[yy]``, where ``[id]`` is a four-digit numerical id increasing from 0001, ``[doy]`` the three digit day
+ of year and ``[yy]`` the last two digits of the year of the event. This structure has to be created by the user of
+ PyLoT manually.
* These folders should contain the seismic data for their event as ``.mseed`` or other supported filetype
-* All automatic and manual picks should be in an ``.xml`` file in their event folder. PyLoT saves picks in this file. This file does not have to be added manually unless there are picks to be imported. The format used to save picks is QUAKEML.
-Picks are saved in a file with the same filename as the event folder with ``PyLoT_`` prepended.
-* The file ``notes.txt`` is used for saving analysts comments. Everything saved here will be displayed in the 'Notes' column of the eventlist.
+* All automatic and manual picks should be in an ``.xml`` file in their event folder. PyLoT saves picks in this file.
+ This file does not have to be added manually unless there are picks to be imported. The format used to save picks is
+ QUAKEML.
+ Picks are saved in a file with the same filename as the event folder with ``PyLoT_`` prepended.
+* The file ``notes.txt`` is used for saving analysts comments. Everything saved here will be displayed in the 'Notes'
+ column of the eventlist.
### Loading event information from CSV file
-Event information can be saved in a ``.csv`` file located in the rootpath. The file is made from one header line, which is followed by one or multiple data lines. Values are separated by comma, while a dot is used as a decimal separator.
+Event information can be saved in a ``.csv`` file located in the rootpath. The file is made from one header line, which
+is followed by one or multiple data lines. Values are separated by comma, while a dot is used as a decimal separator.
This information is then shown in the table in the [Eventlist tab](#Eventlist).
One example header and data line is shown below.
@@ -169,50 +187,63 @@ One example header and data line is shown below.
The meaning of the header entries is:
-Header | description
---- | ---
-event | Event id, has to be the same as the folder name in which waveform data for this event is kept.
-Data | Origin date of the event, format DD/MM/YY or DD/MM/YYYY.
-Time | Origin time of the event. Format HH:MM:SS.
-Lat, Long | Origin latitude and longitude in decimal degrees.
-Region | Flinn-Engdahl region name.
-Basis Lat, Basis Lon | Latitude and longitude of the basis of the station network in decimal degrees.
-Distance [km] | Distance from origin coordinates to basis coordinates in km.
-Distance [rad] | Distance from origin coordinates to basis coordinates in rad.
-
+| Header | description |
+|----------------------|------------------------------------------------------------------------------------------------|
+| event | Event id, has to be the same as the folder name in which waveform data for this event is kept. |
+| Data | Origin date of the event, format DD/MM/YY or DD/MM/YYYY. |
+| Time | Origin time of the event. Format HH:MM:SS. |
+| Lat, Long | Origin latitude and longitude in decimal degrees. |
+| Region | Flinn-Engdahl region name. |
+| Basis Lat, Basis Lon | Latitude and longitude of the basis of the station network in decimal degrees. |
+| Distance [km] | Distance from origin coordinates to basis coordinates in km. |
+| Distance [rad] | Distance from origin coordinates to basis coordinates in rad. |
### Adding events to project
-PyLoT GUI starts with an empty project. To add events, use the add event data button. Select one or multiple folders containing events.
+PyLoT GUI starts with an empty project. To add events, use the add event data button. Select one or multiple folders
+containing events.
[//]: <> (TODO: explain _Directories: Root path, Data path, Database path_)
### Saving projects
-Save the current project from the menu with File->Save project or File->Save project as.
-PyLoT uses ``.plp`` files to save project information. This file format is not interchangeable between different versions of Python interpreters.
-Saved projects contain the automatic and manual picks. Seismic trace data is not included into the ``.plp`` file, but read from its location used when saving the file.
+Save the current project from the menu with File->Save project or File->Save project as. PyLoT uses ``.plp`` files to
+save project information. This file format is not interchangeable between different versions of Python interpreters.
+Saved projects contain the automatic and manual picks. Seismic trace data is not included into the ``.plp`` file, but
+read from its location used when saving the file.
### Adding metadata
[//]: <> (TODO: Add picture of metadata "manager" when it is done)
-PyLoT can handle ``.dless``, ``.xml``, ``.resp`` and ``.dseed`` file formats for Metadata. Metadata files stored on disk can be added to a project by clicking *Edit*->*Manage Inventories*. This opens up a window where the folders which contain metadata files can be selected. PyLoT will then search these files for the station names when it needs the information.
+PyLoT can handle ``.dless``, ``.xml``, ``.resp`` and ``.dseed`` file formats for Metadata. Metadata files stored on disk
+can be added to a project by clicking *Edit*->*Manage Inventories*. This opens up a window where the folders which
+contain metadata files can be selected. PyLoT will then search these files for the station names when it needs the
+information.
# Picking
PyLoTs automatic and manual pick determination works as following:
-* Using certain parameters, a first initial/coarse pick is determined. The first manual pick is determined by visual review of the whole waveform and selection of the most likely onset by the analyst. The first automatic pick is determined by calculation of a characteristic function (CF) for the seismic trace. When a wave arrives, the CFs properties change, which is determined as the signals onset.
-* Afterwards, a refined set of parameters is applied to a small part of the waveform around the initial onset. For manual picks this means a closer view of the trace, for automatic picks this is done by a recalculated CF with different parameters.
+
+* Using certain parameters, a first initial/coarse pick is determined. The first manual pick is determined by visual
+ review of the whole waveform and selection of the most likely onset by the analyst. The first automatic pick is
+ determined by calculation of a characteristic function (CF) for the seismic trace. When a wave arrives, the CFs
+ properties change, which is determined as the signals onset.
+* Afterwards, a refined set of parameters is applied to a small part of the waveform around the initial onset. For
+ manual picks this means a closer view of the trace, for automatic picks this is done by a recalculated CF with
+ different parameters.
* This second picking phase results in the precise pick, which is treated as the onset time.
## Manual Picking
-To create manual picks, you will need to open or create a project that contains seismic trace data (see [Adding events to projects](#adding-events-to-project)). Click on a trace to open the [Picking window](#picking-window).
+To create manual picks, you will need to open or create a project that contains seismic trace data (
+see [Adding events to projects](#adding-events-to-project)). Click on a trace to open
+the [Picking window](#picking-window).
### Picking window
-Open the picking window of a station by leftclicking on any trace in the waveform plot. Here you can create manual picks for the selected station.
+Open the picking window of a station by leftclicking on any trace in the waveform plot. Here you can create manual picks
+for the selected station.
@@ -220,24 +251,24 @@ Open the picking window of a station by leftclicking on any trace in the wavefor
#### Picking Window Settings
-Icon | Shortcut | Menu Alternative | Description
----|---|---|---
- | p | Filter->Apply P Filter | Filter all channels according to the options specified in Filter parameter, P Filter section.
- | s | Filter->Apply S Filter | Filter all channels according to the options specified in Filter parameter, S Filter section.
- | Ctrl + a | Filter->Automatic Filtering | If enabled, automatically select the correct filter option (P, S) depending on the selected phase to be picked.
-![desc](images/gui/picking/phase_selection.png "Phase selection") | 1 (P) or 5 (S) | Picks->P or S | Select phase to pick. If Automatic Filtering is enabled, this will apply the appropriate filter depending on the phase.
-![Zoom into](../icons/zoom_in.png "Zoom into waveform") | - | - | Zoom into waveform.
-![Reset zoom](../icons/zoom_0.png "Reset zoom") | - | - | Reset zoom to default view.
-![Delete picks](../icons/delete.png "Delete picks") | - | - | Delete all manual picks on this station.
-![Rename a phase](../icons/sync.png "Rename a phase") | - | - | Click this button and then the picked phase to rename it.
-![Continue](images/gui/picking/continue.png "Continue with next station") | - | - | If checked, after accepting the manual picks for this station with 'OK', the picking window for the next station will be opened. This option is useful for fast manual picking of a complete event.
-Estimated onsets | - | - | Show the theoretical onsets for this station. Needs metadata and origin information.
-Compare to channel | - | - | Select a data channel to compare against. The selected channel will be displayed in the picking window behind every channel allowing the analyst to visually compare signal correlation between different channels.
-Scaling | - | - | Individual means every channel is scaled to its own maximum. If a channel is selected here, all channels will be scaled relatively to this channel.
+| Icon | Shortcut | Menu Alternative | Description |
+|----------------------------------------------------------------------------------|----------------|-----------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| | p | Filter->Apply P Filter | Filter all channels according to the options specified in Filter parameter, P Filter section. |
+| | s | Filter->Apply S Filter | Filter all channels according to the options specified in Filter parameter, S Filter section. |
+| | Ctrl + a | Filter->Automatic Filtering | If enabled, automatically select the correct filter option (P, S) depending on the selected phase to be picked. |
+| ![desc](images/gui/picking/phase_selection.png "Phase selection") | 1 (P) or 5 (S) | Picks->P or S | Select phase to pick. If Automatic Filtering is enabled, this will apply the appropriate filter depending on the phase. |
+| ![Zoom into](../icons/zoom_in.png "Zoom into waveform") | - | - | Zoom into waveform. |
+| ![Reset zoom](../icons/zoom_0.png "Reset zoom") | - | - | Reset zoom to default view. |
+| ![Delete picks](../icons/delete.png "Delete picks") | - | - | Delete all manual picks on this station. |
+| ![Rename a phase](../icons/sync.png "Rename a phase") | - | - | Click this button and then the picked phase to rename it. |
+| ![Continue](images/gui/picking/continue.png "Continue with next station") | - | - | If checked, after accepting the manual picks for this station with 'OK', the picking window for the next station will be opened. This option is useful for fast manual picking of a complete event. |
+| Estimated onsets | - | - | Show the theoretical onsets for this station. Needs metadata and origin information. |
+| Compare to channel | - | - | Select a data channel to compare against. The selected channel will be displayed in the picking window behind every channel allowing the analyst to visually compare signal correlation between different channels. |
+| Scaling | - | - | Individual means every channel is scaled to its own maximum. If a channel is selected here, all channels will be scaled relatively to this channel. |
-Menu Command | Shortcut | Description
----|---|---
-P Channels and S Channels | - | Select which channels should be treated as P or S channels during picking. When picking a phase, only the corresponding channels will be shown during the precise pick. Normally, the Z channel should be selected for the P phase and the N and E channel for the S phase.
+| Menu Command | Shortcut | Description |
+|---------------------------|----------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| P Channels and S Channels | - | Select which channels should be treated as P or S channels during picking. When picking a phase, only the corresponding channels will be shown during the precise pick. Normally, the Z channel should be selected for the P phase and the N and E channel for the S phase. |
### Filtering
@@ -245,105 +276,167 @@ Access the Filter options by pressing Ctrl+f on the Waveform plot or by the menu
- Here you are able to select filter type, order and frequencies for the P and S pick separately. These settings are used in the GUI for displaying the filtered waveform data and during manual picking. The values used by PyLoT for automatic picking are displayed next to the manual values. They can be changed in the [Tune Autopicker dialog](#tuning).
-A green value automatic value means the automatic and manual filter parameter is configured the same, red means they are configured differently.
-By toggling the "Overwrite filteroptions" checkmark you can set whether the manual precise/second pick uses the filter settings for the automatic picker (unchecked) or whether it uses the filter options in this dialog (checked).
-To guarantee consistent picking results between automatic and manual picking it is recommended to use the same filter settings for the determination of automatic and manual picks.
+Here you are able to select filter type, order and frequencies for the P and S pick separately. These settings are used
+in the GUI for displaying the filtered waveform data and during manual picking. The values used by PyLoT for automatic
+picking are displayed next to the manual values. They can be changed in the [Tune Autopicker dialog](#tuning).
+A green value automatic value means the automatic and manual filter parameter is configured the same, red means they are
+configured differently. By toggling the "Overwrite filteroptions" checkmark you can set whether the manual
+precise/second pick uses the filter settings for the automatic picker (unchecked) or whether it uses the filter options
+in this dialog (checked). To guarantee consistent picking results between automatic and manual picking it is recommended
+to use the same filter settings for the determination of automatic and manual picks.
### Export and Import of manual picks
#### Export
-After the creation of manual picks they can either be saved in the project file (see [Saving projects](#saving-projects)). Alternatively the picks can be exported by pressing the button above the waveform plot or in the menu File->Save event information (shortcut Ctrl+p). Select the event directory in which to save the file. The filename will be ``PyLoT_[event_folder_name].[filetype selected during first startup]``.
-You can rename and copy this file, but PyLoT will then no longer be able to automatically recognize the correct picks for an event and the file will have to be manually selected when loading.
+After the creation of manual picks they can either be saved in the project file (
+see [Saving projects](#saving-projects)). Alternatively the picks can be exported by pressing
+the
+button above the waveform plot or in the menu File->Save event information (shortcut Ctrl+p). Select the event directory
+in which to save the file. The filename will be ``PyLoT_[event_folder_name].[filetype selected during first startup]``
+.
+You can rename and copy this file, but PyLoT will then no longer be able to automatically recognize the correct picks
+for an event and the file will have to be manually selected when loading.
#### Import
-To import previously saved picks press the button and select the file to load. You will be asked to save the current state of your project if you have not done so before. You can continue without saving by pressing "Discard". This does not delete any information from your project, it just means that no project file is saved before the changes of importing picks are applied.
-PyLoT will automatically load files named after the scheme it uses when saving picks, described in the paragraph above. If it can't find any matching files, a file dialogue will open and you can select the file you wish to load.
+To import previously saved picks press
+the button and select the
+file to load. You will be asked to save the current state of your project if you have not done so before. You can
+continue without saving by pressing "Discard". This does not delete any information from your project, it just means
+that no project file is saved before the changes of importing picks are applied. PyLoT will automatically load files
+named after the scheme it uses when saving picks, described in the paragraph above. If it can't find any matching files,
+a file dialogue will open and you can select the file you wish to load.
-If you see a warning "Mismatch in event identifiers" and are asked whether to continue loading the picks, this means that PyLoT doesn't recognize the picks in the file as belonging to this specific event. They could have either been saved under a different installation of PyLoT but with the same waveform data, which means they are still compatible and you can continue loading them. Or they could be picks from a different event, in which case loading them is not recommended.
+If you see a warning "Mismatch in event identifiers" and are asked whether to continue loading the picks, this means
+that PyLoT doesn't recognize the picks in the file as belonging to this specific event. They could have either been
+saved under a different installation of PyLoT but with the same waveform data, which means they are still compatible and
+you can continue loading them. Or they could be picks from a different event, in which case loading them is not
+recommended.
## Automatic Picking
-
The general workflow for automatic picking is as following:
-- After setting up the project by loading waveforms and optionally metadata, the right parameters for the autopicker have to be determined
+
+- After setting up the project by loading waveforms and optionally metadata, the right parameters for the autopicker
+ have to be determined
- This [tuning](#tuning) is done for single stations with immediate graphical feedback of all picking results
- Afterwards the autopicker can be run for all or a subset of events from the project
-For automatic picking PyLoT discerns between tune and test events, which the user has to set as such. Tune events are used to calibrate the autopicking algorithm, test events are then used to test the calibration. The purpose of that is controlling whether the parameters found during tuning are able to reliably pick the "unknown" test events.
-If this behaviour is not desired and all events should be handled the same, dont mark any events. Since this is just a way to group events to compare the picking results, nothing else will change.
+For automatic picking PyLoT discerns between tune and test events, which the user has to set as such. Tune events are
+used to calibrate the autopicking algorithm, test events are then used to test the calibration. The purpose of that is
+controlling whether the parameters found during tuning are able to reliably pick the "unknown" test events.
+If this behaviour is not desired and all events should be handled the same, dont mark any events. Since this is just a
+way to group events to compare the picking results, nothing else will change.
### Tuning
-Tuning describes the process of adjusting the autopicker settings to the characteristics of your data set. To do this in PyLoT, use the button to open the Tune Autopicker.
+Tuning describes the process of adjusting the autopicker settings to the characteristics of your data set. To do this in
+PyLoT, use the button to
+open the Tune Autopicker.
-View of a station in the Tune Autopicker window.
+View of a station in the Tune Autopicker window.
+
1. Select the event to be displayed and processed.
-2. Select the station from the event.
-3. To pick the currently displayed trace, click the button.
-4. These tabs are used to select the current view. __Traces Plot__ contains a plot of the stations traces, where manual picks can be created/edited. __Overview__ contains graphical results of the automatic picking process. The __P and S tabs__ contain the automatic picking results of the P and S phase, while __log__ contains a useful text output of automatic picking.
-5. These buttons are used to load/save/reset settings for automatic picking. The parameters can be saved in PyLoT input files, which have the file ending *.in*. They are human readable text files, which can also be edited by hand. Saving the parameters allows you to load them again later, even on different machines.
-6. These menus control the behaviour of the creation of manual picks from the Tune Autopicker window. Picks allows to select the phase for which a manual pick should be created, Filter allows to filter waveforms and edit the filter parameters. P-Channels and S-Channels allow to select the channels that should be displayed when creating a manual P or S pick.
-7. This menu is the same as in the [Picking Window](#picking-window-settings), with the exception of the __Manual Onsets__ options. The __Manual Onsets__ buttons accepts or reject the manual picks created in the Tune Autopicker window, pressing accept adds them to the manual picks for the event, while reject removes them.
+2. Select the station from the event.
+3. To pick the currently displayed trace, click
+ the
+ button.
+4. These tabs are used to select the current view. __Traces Plot__ contains a plot of the stations traces, where manual
+ picks can be created/edited. __Overview__ contains graphical results of the automatic picking process. The __P and S
+ tabs__ contain the automatic picking results of the P and S phase, while __log__ contains a useful text output of
+ automatic picking.
+5. These buttons are used to load/save/reset settings for automatic picking. The parameters can be saved in PyLoT input
+ files, which have the file ending *.in*. They are human readable text files, which can also be edited by hand. Saving
+ the parameters allows you to load them again later, even on different machines.
+6. These menus control the behaviour of the creation of manual picks from the Tune Autopicker window. Picks allows to
+ select the phase for which a manual pick should be created, Filter allows to filter waveforms and edit the filter
+ parameters. P-Channels and S-Channels allow to select the channels that should be displayed when creating a manual P
+ or S pick.
+7. This menu is the same as in the [Picking Window](#picking-window-settings), with the exception of the __Manual
+ Onsets__ options. The __Manual Onsets__ buttons accepts or reject the manual picks created in the Tune Autopicker
+ window, pressing accept adds them to the manual picks for the event, while reject removes them.
8. The traces plot in the centre allows creating manual picks and viewing the waveforms.
-9. The parameters which influence the autopicking result are in the Main settings and Advanced settings tabs on the left side. For a description of all the parameters see the [tuning documentation](tuning.md).
+9. The parameters which influence the autopicking result are in the Main settings and Advanced settings tabs on the left
+ side. For a description of all the parameters see the [tuning documentation](tuning.md).
### Production run of the autopicker
-After the settings used during tuning give the desired results, the autopicker can be used on the complete dataset. To invoke the autopicker on the whole set of events, click the button.
+After the settings used during tuning give the desired results, the autopicker can be used on the complete dataset. To
+invoke the autopicker on the whole set of events, click
+the button.
### Evaluation of automatic picks
PyLoT has two internal consistency checks for automatic picks that were determined for an event:
+
1. Jackknife check
2. Wadati check
#### 1. Jackknife check
-The jackknife test in PyLoT checks the consistency of automatically determined P-picks by checking the statistical variance of the picks. The variance of all P-picks is calculated and compared to the variance of subsets, in which one pick is removed.
-The idea is, that picks that are close together in time should not influence the estimation of the variance much, while picks whose positions deviates from the norm influence the variance to a greater extent. If the estimated variance of a subset with a pick removed differs to much from the estimated variance of all picks, the pick that was removed from the subset will be marked as invalid.
-The factor by which picks are allowed to skew from the estimation of variance can be configured, it is called *jackfactor*, see [here](tuning.md#Pick-quality-control).
+The jackknife test in PyLoT checks the consistency of automatically determined P-picks by checking the statistical
+variance of the picks. The variance of all P-picks is calculated and compared to the variance of subsets, in which one
+pick is removed.
+The idea is, that picks that are close together in time should not influence the estimation of the variance much, while
+picks whose positions deviates from the norm influence the variance to a greater extent. If the estimated variance of a
+subset with a pick removed differs to much from the estimated variance of all picks, the pick that was removed from the
+subset will be marked as invalid.
+The factor by which picks are allowed to skew from the estimation of variance can be configured, it is called *
+jackfactor*, see [here](tuning.md#Pick-quality-control).
-Additionally, the deviation of picks from the median is checked. For that, the median of all P-picks that passed the Jackknife test is calculated. Picks whose onset times deviate from the mean onset time by more than the *mdttolerance* are marked as invalid.
+Additionally, the deviation of picks from the median is checked. For that, the median of all P-picks that passed the
+Jackknife test is calculated. Picks whose onset times deviate from the mean onset time by more than the *mdttolerance*
+are marked as invalid.
-*The result of both tests (Jackknife and Median) is shown in a diagram afterwards. The onset time is plotted against a running number of stations. Picks that failed either the Jackknife or the median test are colored red. The median is plotted as a green line.*
+*The result of both tests (Jackknife and Median) is shown in a diagram afterwards. The onset time is plotted against a
+running number of stations. Picks that failed either the Jackknife or the median test are colored red. The median is
+plotted as a green line.*
-The Jackknife and median check are suitable to check for picks that are outside of the expected time window, for example, when a wrong phase was picked. It won't recognize picks that are in close proximity to the right onset which are just slightly to late/early.
+The Jackknife and median check are suitable to check for picks that are outside of the expected time window, for
+example, when a wrong phase was picked. It won't recognize picks that are in close proximity to the right onset which
+are just slightly to late/early.
#### 2. Wadati check
-The Wadati check checks the consistency of S picks. For this the SP-time, the time difference between S and P onset is plotted against the P onset time. A line is fitted to the points, which minimizes the error. Then the deviation of single picks to this line is checked. If the deviation in seconds is above the *wdttolerance* parameter ([see here](tuning.md#Pick-quality-control)), the pick is marked as invalid.
+The Wadati check checks the consistency of S picks. For this the SP-time, the time difference between S and P onset is
+plotted against the P onset time. A line is fitted to the points, which minimizes the error. Then the deviation of
+single picks to this line is checked. If the deviation in seconds is above the *wdttolerance*
+parameter ([see here](tuning.md#Pick-quality-control)), the pick is marked as invalid.
-*The Wadati plot in PyLoT shows the SP onset time difference over the P onset time. A first line is fitted (black). All picks which deviate to much from this line are marked invalid (red). Then a second line is fitted which excludes the invalid picks. From this lines slope, the ratio of P and S wave velocity is determined.*
+*The Wadati plot in PyLoT shows the SP onset time difference over the P onset time. A first line is fitted (black). All
+picks which deviate to much from this line are marked invalid (red). Then a second line is fitted which excludes the
+invalid picks. From this lines slope, the ratio of P and S wave velocity is determined.*
### Comparison between automatic and manual picks
-Every pick in PyLoT consists of an earliest possible, latest possible and most likely onset time.
-The earliest and latest possible onset time characterize the uncertainty of a pick.
-This approach is described in Diel, Kissling and Bormann (2012) - Tutorial for consistent phase picking at local to regional distances.
-These times are represented as a Probability Density Function (PDF) for every pick.
-The PDF is implemented as two exponential distributions around the most likely onset as the expected value.
+Every pick in PyLoT consists of an earliest possible, latest possible and most likely onset time. The earliest and
+latest possible onset time characterize the uncertainty of a pick. This approach is described in Diel, Kissling and
+Bormann (2012) - Tutorial for consistent phase picking at local to regional distances. These times are represented as a
+Probability Density Function (PDF) for every pick. The PDF is implemented as two exponential distributions around the
+most likely onset as the expected value.
-To compare two single picks, their PDFs are cross correlated to create a new PDF.
-This corresponds to the subtraction of the automatic pick from the manual pick.
+To compare two single picks, their PDFs are cross correlated to create a new PDF. This corresponds to the subtraction of
+the automatic pick from the manual pick.
- *Comparison between an automatic and a manual pick for a station in PyLoT by comparing their PDFs.*
- *The upper plot shows the difference between the two single picks that are shown in the lower plot.*
- *The difference is implemented as a cross correlation between the two PDFs. and results in a new PDF, the comparison PDF.*
- *The expected value of the comparison PDF corresponds to the time distance between the automatic and manual picks most likely onset.*
- *The standard deviation corresponds to the combined uncertainty.*
+*Comparison between an automatic and a manual pick for a station in PyLoT by comparing their PDFs.*
+*The upper plot shows the difference between the two single picks that are shown in the lower plot.*
+*The difference is implemented as a cross correlation between the two PDFs. and results in a new PDF, the comparison
+PDF.*
+*The expected value of the comparison PDF corresponds to the time distance between the automatic and manual picks most
+likely onset.*
+*The standard deviation corresponds to the combined uncertainty.*
-To compare the automatic and manual picks between multiple stations of an event, the properties of all the comparison PDFs are shown in a histogram.
+To compare the automatic and manual picks between multiple stations of an event, the properties of all the comparison
+PDFs are shown in a histogram.
@@ -352,11 +445,13 @@ To compare the automatic and manual picks between multiple stations of an event,
*The bottom left plot shows the expected values of the comparison PDFs for P picks.*
*The top right plot shows the standard deviation of the comparison PDFs for S picks.*
*The bottom right plot shows the expected values of the comparison PDFs for S picks.*
-*The standard deviation plots show that most P picks have an uncertainty between 1 and 2 seconds, while S pick uncertainties have a much larger spread between 1 to 15 seconds.*
+*The standard deviation plots show that most P picks have an uncertainty between 1 and 2 seconds, while S pick
+uncertainties have a much larger spread between 1 to 15 seconds.*
*This means P picks have higher quality classes on average than S picks.*
-*The expected values are largely negative, meaning that the algorithm tends to pick earlier than the analyst with the applied settings (Manual - Automatic).*
-*The number of samples mentioned in the plots legends is the amount of stations that have an automatic and a manual P pick.*
-
+*The expected values are largely negative, meaning that the algorithm tends to pick earlier than the analyst with the
+applied settings (Manual - Automatic).*
+*The number of samples mentioned in the plots legends is the amount of stations that have an automatic and a manual P
+pick.*
### Export and Import of automatic picks
@@ -369,7 +464,11 @@ To be added.
# FAQ
Q: During manual picking the error "No channel to plot for phase ..." is displayed, and I am unable to create a pick.
-A: Select a channel that should be used for the corresponding phase in the Pickwindow. For further information read [Picking Window settings](#picking-window-settings).
+A: Select a channel that should be used for the corresponding phase in the Pickwindow. For further information
+read [Picking Window settings](#picking-window-settings).
Q: I see a warning "Mismatch in event identifiers" when loading picks from a file.
-A: This means that PyLoT doesn't recognize the picks in the file as belonging to this specific event. They could have been saved under a different installation of PyLoT but with the same waveform data, which means they are still compatible and you can continue loading them or they could be the picks of a different event, in which case loading them is not recommended.
+A: This means that PyLoT doesn't recognize the picks in the file as belonging to this specific event. They could have
+been saved under a different installation of PyLoT but with the same waveform data, which means they are still
+compatible and you can continue loading them or they could be the picks of a different event, in which case loading them
+is not recommended.
diff --git a/docs/tuning.md b/docs/tuning.md
index c872e272..f68d4cc6 100644
--- a/docs/tuning.md
+++ b/docs/tuning.md
@@ -6,95 +6,145 @@ A description of the parameters used for determining automatic picks.
Parameters applied to the traces before picking algorithm starts.
-Name | Description
---- | ---
-*P Start*, *P Stop* | Define time interval relative to trace start time for CF calculation on vertical trace. Value is relative to theoretical onset time if 'Use TauPy' option is enabled in main settings of 'Tune Autopicker' dialogue.
-*S Start*, *S Stop* | Define time interval relative to trace start time for CF calculation on horizontal traces. Value is relative to theoretical onset time if 'Use TauPy' option is enabled in main settings of 'Tune Autopicker' dialogue.
-*Bandpass Z1* | Filter settings for Butterworth bandpass applied to vertical trace for calculation of initial P pick.
-*Bandpass Z2* | Filter settings for Butterworth bandpass applied to vertical trace for calculation of precise P pick.
-*Bandpass H1* | Filter settings for Butterworth bandpass applied to horizontal traces for calculation of initial S pick.
-*Bandpass H2* | Filter settings for Butterworth bandpass applied to horizontal traces for calculation of precise S pick.
+| Name | Description |
+|---------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| *P Start*, *P
+Stop* | Define time interval relative to trace start time for CF calculation on vertical trace. Value is relative to theoretical onset time if 'Use TauPy' option is enabled in main settings of 'Tune Autopicker' dialogue. |
+| *S Start*, *S
+Stop* | Define time interval relative to trace start time for CF calculation on horizontal traces. Value is relative to theoretical onset time if 'Use TauPy' option is enabled in main settings of 'Tune Autopicker' dialogue. |
+| *Bandpass
+Z1* | Filter settings for Butterworth bandpass applied to vertical trace for calculation of initial P pick. |
+| *Bandpass
+Z2* | Filter settings for Butterworth bandpass applied to vertical trace for calculation of precise P pick. |
+| *Bandpass
+H1* | Filter settings for Butterworth bandpass applied to horizontal traces for calculation of initial S pick. |
+| *Bandpass
+H2* | Filter settings for Butterworth bandpass applied to horizontal traces for calculation of precise S pick. |
## Inital P pick
Parameters used for determination of initial P pick.
-Name | Description
---- | ---
-*tLTA* | Size of gliding LTA window in seconds used for calculation of HOS-CF.
-*pickwin P* | Size of time window in seconds in which the minimum of the AIC-CF in front of the maximum of the HOS-CF is determined.
-*AICtsmooth* | Average of samples in this time window will be used for smoothing of the AIC-CF.
-*checkwinP* | Time in front of the global maximum of the HOS-CF in which to search for a second local extrema.
-*minfactorP* | Used with *checkwinP*. If a second local maximum is found, it has to be at least as big as the first maximum * *minfactorP*.
-*tsignal* | Time window in seconds after the initial P pick used for determining signal amplitude.
-*tnoise* | Time window in seconds in front of initial P pick used for determining noise amplitude.
-*tsafetey* | Time in seconds between *tsignal* and *tnoise*.
-*tslope* | Time window in seconds after initial P pick in which the slope of the onset is calculated.
+| Name | Description |
+|--------------|------------------------------------------------------------------------------------------------------------------------------|
+| *
+tLTA* | Size of gliding LTA window in seconds used for calculation of HOS-CF. |
+| *pickwin
+P* | Size of time window in seconds in which the minimum of the AIC-CF in front of the maximum of the HOS-CF is determined. |
+| *
+AICtsmooth* | Average of samples in this time window will be used for smoothing of the AIC-CF. |
+| *
+checkwinP* | Time in front of the global maximum of the HOS-CF in which to search for a second local extrema. |
+| *minfactorP* | Used with *
+checkwinP*. If a second local maximum is found, it has to be at least as big as the first maximum * *minfactorP*. |
+| *
+tsignal* | Time window in seconds after the initial P pick used for determining signal amplitude. |
+| *
+tnoise* | Time window in seconds in front of initial P pick used for determining noise amplitude. |
+| *tsafetey* | Time in seconds between *tsignal* and *
+tnoise*. |
+| *
+tslope* | Time window in seconds after initial P pick in which the slope of the onset is calculated. |
## Inital S pick
Parameters used for determination of initial S pick
-Name | Description
---- | ---
-*tdet1h* | Length of time window in seconds in which AR params of the waveform are determined.
-*tpred1h* | Length of time window in seconds in which the waveform is predicted using the AR model.
-*AICtsmoothS* | Average of samples in this time window is used for smoothing the AIC-CF.
-*pickwinS* | Time window in which the minimum in the AIC-CF in front of the maximum in the ARH-CF is determined.
-*checkwinS* | Time in front of the global maximum of the ARH-CF in which to search for a second local extrema.
-*minfactorP* | Used with *checkwinS*. If a second local maximum is found, it has to be at least as big as the first maximum * *minfactorS*.
-*tsignal* | Time window in seconds after the initial P pick used for determining signal amplitude.
-*tnoise* | Time window in seconds in front of initial P pick used for determining noise amplitude.
-*tsafetey* | Time in seconds between *tsignal* and *tnoise*.
-*tslope* | Time window in seconds after initial P pick in which the slope of the onset is calculated.
+| Name | Description |
+|---------------|------------------------------------------------------------------------------------------------------------------------------|
+| *
+tdet1h* | Length of time window in seconds in which AR params of the waveform are determined. |
+| *
+tpred1h* | Length of time window in seconds in which the waveform is predicted using the AR model. |
+| *
+AICtsmoothS* | Average of samples in this time window is used for smoothing the AIC-CF. |
+| *
+pickwinS* | Time window in which the minimum in the AIC-CF in front of the maximum in the ARH-CF is determined. |
+| *
+checkwinS* | Time in front of the global maximum of the ARH-CF in which to search for a second local extrema. |
+| *minfactorP* | Used with *
+checkwinS*. If a second local maximum is found, it has to be at least as big as the first maximum * *minfactorS*. |
+| *
+tsignal* | Time window in seconds after the initial P pick used for determining signal amplitude. |
+| *
+tnoise* | Time window in seconds in front of initial P pick used for determining noise amplitude. |
+| *tsafetey* | Time in seconds between *tsignal* and *
+tnoise*. |
+| *
+tslope* | Time window in seconds after initial P pick in which the slope of the onset is calculated. |
## Precise P pick
Parameters used for determination of precise P pick.
-Name | Description
---- | ---
-*Precalcwin* | Time window in seconds for recalculation of the HOS-CF. The new CF will be two times the size of *Precalcwin*, since it will be calculated from the initial pick to +/- *Precalcwin*.
-*tsmoothP* | Average of samples in this time window will be used for smoothing the second HOS-CF.
-*ausP* | Controls artificial uplift of samples during precise picking. A common local minimum of the smoothed and unsmoothed HOS-CF is found when the previous sample is larger or equal to the current sample times (1+*ausP*).
+| Name | Description |
+|--------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| *Precalcwin* | Time window in seconds for recalculation of the HOS-CF. The new CF will be two times the size of *
+Precalcwin*, since it will be calculated from the initial pick to +/- *Precalcwin*. |
+| *
+tsmoothP* | Average of samples in this time window will be used for smoothing the second HOS-CF. |
+| *
+ausP* | Controls artificial uplift of samples during precise picking. A common local minimum of the smoothed and unsmoothed HOS-CF is found when the previous sample is larger or equal to the current sample times (1+*
+ausP*). |
## Precise S pick
Parameters used for determination of precise S pick.
-Name | Description
---- | ---
-*tdet2h* | Time window for determination of AR coefficients.
-*tpred2h* | Time window in which the waveform is predicted using the determined AR parameters.
-*Srecalcwin* | Time window for recalculation of ARH-CF. New CF will be calculated from initial pick +/- *Srecalcwin*.
-*tsmoothS* | Average of samples in this time window will be used for smoothing the second ARH-CF.
-*ausS* | Controls artificial uplift of samples during precise picking. A common local minimum of the smoothed and unsmoothed ARH-CF is found when the previous sample is larger or equal to the current sample times (1+*ausS*).
-*pickwinS* | Time window around initial pick in which to look for a precise pick.
-
+| Name | Description |
+|--------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| *
+tdet2h* | Time window for determination of AR coefficients. |
+| *
+tpred2h* | Time window in which the waveform is predicted using the determined AR parameters. |
+| *Srecalcwin* | Time window for recalculation of ARH-CF. New CF will be calculated from initial pick +/- *
+Srecalcwin*. |
+| *
+tsmoothS* | Average of samples in this time window will be used for smoothing the second ARH-CF. |
+| *
+ausS* | Controls artificial uplift of samples during precise picking. A common local minimum of the smoothed and unsmoothed ARH-CF is found when the previous sample is larger or equal to the current sample times (1+*
+ausS*). |
+| *
+pickwinS* | Time window around initial pick in which to look for a precise pick. |
## Pick quality control
Parameters used for checking quality and integrity of automatic picks.
-Name | Description
---- | ---
-*minAICPslope* | Initial P picks with a slope lower than this value will be discared.
-*minAICPSNR* | Initial P picks with a SNR below this value will be discarded.
-*minAICSslope* | Initial S picks with a slope lower than this value will be discarded.
-*minAICSSNR* | Initial S picks with a SNR below this value will be discarded.
-*minsiglength*, *noisefacor*. *minpercent* | Parameters for checking signal length. In the time window of size *minsiglength* after the initial P pick *minpercent* of samples have to be larger than the RMS value.
-*zfac* | To recognize misattributed S picks, the RMS amplitude of vertical and horizontal traces are compared. The RMS amplitude of the vertical traces has to be at least *zfac* higher than the RMS amplitude on the horizontal traces for the pick to be accepted as a valid P pick.
-*jackfactor* | A P pick is removed if the jackknife pseudo value of the variance of his subgroup is larger than the variance of all picks multiplied with the *jackfactor*.
-*mdttolerance* | Maximum allowed deviation of P onset times from the median. Value in seconds.
-*wdttolerance* | Maximum allowed deviation of S onset times from the line during the Wadati test. Value in seconds.
+| Name | Description |
+|--------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| *
+minAICPslope* | Initial P picks with a slope lower than this value will be discared. |
+| *
+minAICPSNR* | Initial P picks with a SNR below this value will be discarded. |
+| *
+minAICSslope* | Initial S picks with a slope lower than this value will be discarded. |
+| *
+minAICSSNR* | Initial S picks with a SNR below this value will be discarded. |
+| *minsiglength*, *noisefacor*. *minpercent* | Parameters for checking signal length. In the time window of size *
+minsiglength* after the initial P pick *
+minpercent* of samples have to be larger than the RMS value. |
+| *
+zfac* | To recognize misattributed S picks, the RMS amplitude of vertical and horizontal traces are compared. The RMS amplitude of the vertical traces has to be at least *
+zfac* higher than the RMS amplitude on the horizontal traces for the pick to be accepted as a valid P pick. |
+| *
+jackfactor* | A P pick is removed if the jackknife pseudo value of the variance of his subgroup is larger than the variance of all picks multiplied with the *
+jackfactor*. |
+| *
+mdttolerance* | Maximum allowed deviation of P onset times from the median. Value in seconds. |
+| *
+wdttolerance* | Maximum allowed deviation of S onset times from the line during the Wadati test. Value in seconds. |
## Pick quality determination
Parameters for discrete quality classes.
-Name | Description
---- | ---
-*timeerrorsP* | Width of the time windows in seconds between earliest and latest possible pick which represent the quality classes 0, 1, 2, 3 for P onsets.
-*timeerrorsS* | Width of the time windows in seconds between earliest and latest possible pick which represent the quality classes 0, 1, 2, 3 for S onsets.
-*nfacP*, *nfacS* | For determination of latest possible onset time. The time when the signal reaches an amplitude of *nfac* * mean value of the RMS amplitude in the time window *tnoise* corresponds to the latest possible onset time.
+| Name | Description |
+|------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| *
+timeerrorsP* | Width of the time windows in seconds between earliest and latest possible pick which represent the quality classes 0, 1, 2, 3 for P onsets. |
+| *
+timeerrorsS* | Width of the time windows in seconds between earliest and latest possible pick which represent the quality classes 0, 1, 2, 3 for S onsets. |
+| *nfacP*, *nfacS* | For determination of latest possible onset time. The time when the signal reaches an amplitude of *
+nfac* * mean value of the RMS amplitude in the time window *tnoise* corresponds to the latest possible onset time. |
diff --git a/icons_rc_2.py b/icons_rc_2.py
index a33e0635..4664cb5e 100644
--- a/icons_rc_2.py
+++ b/icons_rc_2.py
@@ -6,7 +6,7 @@
#
# WARNING! All changes made in this file will be lost!
-from PyQt4 import QtCore
+from PySide2 import QtCore
qt_resource_data = "\
\x00\x00\x9e\x04\
diff --git a/icons_rc_3.py b/icons_rc_3.py
index 963cf353..3a309944 100644
--- a/icons_rc_3.py
+++ b/icons_rc_3.py
@@ -7,9 +7,9 @@
#
# WARNING! All changes made in this file will be lost!
-from PySide import QtCore
+from PySide2 import QtCore
-qt_resource_data = "\
+qt_resource_data = b"\
\x00\x00\x9e\x04\
\x89\
\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\
@@ -109000,7 +109000,7 @@ qt_resource_data = "\
\x62\x6f\x64\x79\x3e\x0a\x3c\x2f\x68\x74\x6d\x6c\x3e\x0a\
"
-qt_resource_name = "\
+qt_resource_name = b"\
\x00\x04\
\x00\x06\xec\x30\
\x00\x68\
@@ -109235,7 +109235,7 @@ qt_resource_name = "\
\x00\x6e\x00\x64\x00\x65\x00\x78\x00\x2e\x00\x68\x00\x74\x00\x6d\x00\x6c\
"
-qt_resource_struct = "\
+qt_resource_struct = b"\
\x00\x00\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\x01\
\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x3a\
\x00\x00\x00\x0e\x00\x02\x00\x00\x00\x35\x00\x00\x00\x05\
diff --git a/pylot.yml b/pylot.yml
new file mode 100644
index 00000000..1b94c539
--- /dev/null
+++ b/pylot.yml
@@ -0,0 +1,14 @@
+name: pylot_38
+channels:
+ - conda-forge
+ - defaults
+dependencies:
+ - cartopy=0.20.2
+ - matplotlib-base=3.3.4
+ - numpy=1.22.3
+ - obspy=1.3.0
+ - pyqtgraph=0.12.4
+ - pyside2=5.13.2
+ - python=3.8.12
+ - qt=5.12.9
+ - scipy=1.8.0
\ No newline at end of file
diff --git a/pylot/core/analysis/magnitude.py b/pylot/core/analysis/magnitude.py
index 3b8119c4..b097cbd0 100644
--- a/pylot/core/analysis/magnitude.py
+++ b/pylot/core/analysis/magnitude.py
@@ -223,14 +223,14 @@ class LocalMagnitude(Magnitude):
in 'Z3']
# checking horizontal count and calculating power_sum accordingly
if len(power) == 1:
- print ('WARNING: Only one horizontal found for station {0}.'.format(st[0].stats.station))
- power_sum = power[0]
+ print('WARNING: Only one horizontal found for station {0}.'.format(st[0].stats.station))
+ power_sum = power[0]
elif len(power) == 2:
- power_sum = power[0] + power[1]
- else:
- raise ValueError('Wood-Anderson aomplitude defintion only valid for'
- ' up to two horizontals: {0} given'.format(len(power)))
-
+ power_sum = power[0] + power[1]
+ else:
+ raise ValueError('Wood-Anderson aomplitude defintion only valid for'
+ ' up to two horizontals: {0} given'.format(len(power)))
+
sqH = np.sqrt(power_sum)
# get time array
@@ -325,7 +325,7 @@ class LocalMagnitude(Magnitude):
if self.verbose:
print(
"Local Magnitude for station {0}: ML = {1:3.1f}".format(
- station, magnitude.mag))
+ station, magnitude.mag))
magnitude.origin_id = self.origin_id
magnitude.waveform_id = pick.waveform_id
magnitude.amplitude_id = amplitude.resource_id
@@ -404,7 +404,7 @@ class MomentMagnitude(Magnitude):
if not wf:
continue
try:
- scopy = wf.copy()
+ scopy = wf.copy()
except AssertionError:
print("WARNING: Something's wrong with the data,"
"station {},"
@@ -464,7 +464,7 @@ def calcMoMw(wfstream, w0, rho, vp, delta, verbosity=False):
# additional common parameters for calculating Mo
# average radiation pattern of P waves (Aki & Richards, 1980)
- rP = 2 / np.sqrt(15)
+ rP = 2 / np.sqrt(15)
freesurf = 2.0 # free surface correction, assuming vertical incidence
Mo = w0 * 4 * np.pi * rho * np.power(vp, 3) * delta / (rP * freesurf)
@@ -524,7 +524,7 @@ def calcsourcespec(wfstream, onset, vp, delta, azimuth, incidence,
iplot = 2
else:
iplot = 0
-
+
# get Q value
Q, A = qp
@@ -594,13 +594,13 @@ def calcsourcespec(wfstream, onset, vp, delta, azimuth, incidence,
# fft
fny = freq / 2
- #l = len(xdat) / freq
+ # l = len(xdat) / freq
# number of fft bins after Bath
- #n = freq * l
+ # n = freq * l
# find next power of 2 of data length
m = pow(2, np.ceil(np.log(len(xdat)) / np.log(2)))
N = min(int(np.power(m, 2)), 16384)
- #N = int(np.power(m, 2))
+ # N = int(np.power(m, 2))
y = dt * np.fft.fft(xdat, N)
Y = abs(y[: N / 2])
L = (N - 1) / freq
@@ -643,8 +643,8 @@ def calcsourcespec(wfstream, onset, vp, delta, azimuth, incidence,
w0 = np.median([w01, w02])
Fc = np.median([fc1, fc2])
if verbosity:
- print("calcsourcespec: Using w0-value = %e m/Hz and fc = %f Hz" % (
- w0, Fc))
+ print("calcsourcespec: Using w0-value = %e m/Hz and fc = %f Hz" % (
+ w0, Fc))
if iplot >= 1:
f1 = plt.figure()
tLdat = np.arange(0, len(Ldat) * dt, dt)
@@ -733,7 +733,7 @@ def fitSourceModel(f, S, fc0, iplot, verbosity=False):
iplot = 2
else:
iplot = 0
-
+
w0 = []
stdw0 = []
fc = []
diff --git a/pylot/core/io/data.py b/pylot/core/io/data.py
index 122459c6..f846b2d6 100644
--- a/pylot/core/io/data.py
+++ b/pylot/core/io/data.py
@@ -1,18 +1,18 @@
-#!/usr/bin/env pyth n
+#!/usr/bin/env python
# -*- coding: utf-8 -*-
import copy
import os
+
+from PySide2.QtWidgets import QMessageBox
from obspy import read_events
from obspy.core import read, Stream, UTCDateTime
from obspy.core.event import Event as ObsPyEvent
from obspy.io.sac import SacIOError
-from PySide.QtGui import QMessageBox
-
-import pylot.core.loc.velest as velest
import pylot.core.loc.focmec as focmec
import pylot.core.loc.hypodd as hypodd
+import pylot.core.loc.velest as velest
from pylot.core.io.phases import readPILOTEvent, picks_from_picksdict, \
picksdict_from_pilot, merge_picks, PylotParameter
from pylot.core.util.errors import FormatError, OverwriteError
@@ -21,13 +21,14 @@ from pylot.core.util.obspyDMT_interface import qml_from_obspyDMT
from pylot.core.util.utils import fnConstructor, full_range, check4rotated, \
check4gapsAndMerge, trim_station_components
+
class Data(object):
"""
Data container with attributes wfdata holding ~obspy.core.stream.
- :type parent: PySide.QtGui.QWidget object, optional
- :param parent: A PySide.QtGui.QWidget object utilized when
- called by a GUI to display a PySide.QtGui.QMessageBox instead of printing
+ :type parent: PySide2.QtWidgets.QWidget object, optional
+ :param parent: A PySide2.QtWidgets.QWidget object utilized when
+ called by a GUI to display a PySide2.QtWidgets.QMessageBox instead of printing
to standard out.
:type evtdata: ~obspy.core.event.Event object, optional
:param evtdata ~obspy.core.event.Event object containing all derived or
@@ -47,10 +48,10 @@ class Data(object):
elif isinstance(evtdata, dict):
evt = readPILOTEvent(**evtdata)
evtdata = evt
- elif type(evtdata) in [str, unicode]:
+ elif isinstance(evtdata, str):
try:
cat = read_events(evtdata)
- if len(cat) is not 1:
+ if len(cat) != 1:
raise ValueError('ambiguous event information for file: '
'{file}'.format(file=evtdata))
evtdata = cat[0]
@@ -99,7 +100,7 @@ class Data(object):
old_pick.phase_hint == new_pick.phase_hint,
old_pick.method_id == new_pick.method_id]
if all(comparison):
- del(old_pick)
+ del (old_pick)
old_picks.append(new_pick)
elif not other.isNew() and self.isNew():
new = other + self
@@ -111,7 +112,7 @@ class Data(object):
return self + other
else:
raise ValueError("both Data objects have differing "
- "unique Event identifiers")
+ "unique Event identifiers")
return self
def getPicksStr(self):
@@ -162,9 +163,8 @@ class Data(object):
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
+ Check information in supplied event and own event and replace with own
+ information if no other information are given 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
@@ -250,7 +250,7 @@ class Data(object):
for pick in self.get_evt_data().picks:
if picktype in str(pick.method_id.id):
picks.append(pick)
-
+
def exportEvent(self, fnout, fnext='.xml', fcheck='auto', upperErrors=None):
"""
Export event to file
@@ -260,7 +260,7 @@ class Data(object):
can be a str or a list of strings of ['manual', 'auto', 'origin', 'magnitude']
"""
from pylot.core.util.defaults import OUTPUTFORMATS
-
+
if not type(fcheck) == list:
fcheck = [fcheck]
@@ -293,7 +293,7 @@ class Data(object):
return
self.checkEvent(event, fcheck)
self.setEvtData(event)
-
+
self.get_evt_data().write(fnout + fnext, format=evtformat)
# try exporting event
@@ -318,7 +318,7 @@ class Data(object):
del picks_copy[k]
break
lendiff = len(picks) - len(picks_copy)
- if lendiff is not 0:
+ if lendiff != 0:
print("Manual as well as automatic picks available. Prefered the {} manual ones!".format(lendiff))
if upperErrors:
@@ -360,13 +360,13 @@ class Data(object):
nllocfile = open(fnout + fnext)
l = nllocfile.readlines()
# Adding A0/Generic Amplitude to .obs file
- #l2 = []
- #for li in l:
+ # l2 = []
+ # for li in l:
# for amp in evtdata_org.amplitudes:
# if amp.waveform_id.station_code == li[0:5].strip():
# li = li[0:64] + '{:0.2e}'.format(amp.generic_amplitude) + li[73:-1] + '\n'
# l2.append(li)
- #l = l2
+ # l = l2
nllocfile.close()
l.insert(0, header)
nllocfile = open(fnout + fnext, 'w')
@@ -503,7 +503,8 @@ class Data(object):
real_or_syn_data[synthetic] += read(fname, format='GSE2', starttime=self.tstart, endtime=self.tstop)
except Exception as e:
try:
- real_or_syn_data[synthetic] += read(fname, format='SEGY', starttime=self.tstart, endtime=self.tstop)
+ real_or_syn_data[synthetic] += read(fname, format='SEGY', starttime=self.tstart,
+ endtime=self.tstop)
except Exception as e:
warnmsg += '{0}\n{1}\n'.format(fname, e)
except SacIOError as se:
@@ -794,8 +795,8 @@ class PilotDataStructure(GenericDataStructure):
def __init__(self, **fields):
if not fields:
- fields = {'database': '2006.01',
- 'root': '/data/Egelados/EVENT_DATA/LOCAL'}
+ fields = {'database': '',
+ 'root': ''}
GenericDataStructure.__init__(self, **fields)
diff --git a/pylot/core/io/default_parameters.py b/pylot/core/io/default_parameters.py
index 62e59536..6e216939 100644
--- a/pylot/core/io/default_parameters.py
+++ b/pylot/core/io/default_parameters.py
@@ -515,9 +515,9 @@ defaults = {'rootpath': {'type': str,
'namestring': 'TauPy model'},
'taup_phases': {'type': str,
- 'tooltip': 'Specify possible phases for TauPy (comma separated). See Obspy TauPy documentation for possible values.',
- 'value': 'ttall',
- 'namestring': 'TauPy phases'},
+ 'tooltip': 'Specify possible phases for TauPy (comma separated). See Obspy TauPy documentation for possible values.',
+ 'value': 'ttall',
+ 'namestring': 'TauPy phases'},
}
settings_main = {
diff --git a/pylot/core/io/getEventListFromXML.py b/pylot/core/io/getEventListFromXML.py
index cf99a98e..d8dce4f8 100644
--- a/pylot/core/io/getEventListFromXML.py
+++ b/pylot/core/io/getEventListFromXML.py
@@ -8,14 +8,10 @@
Edited for use in PyLoT
JG, igem, 01/2022
"""
-import pdb
-import os
-import argparse
-import numpy as np
-import matplotlib.pyplot as plt
+import glob
+
from obspy.core.event import read_events
from pyproj import Proj
-import glob
"""
Creates an eventlist file summarizing all events found in a certain folder. Only called by pressing UI Button eventlis_xml_action
@@ -24,14 +20,15 @@ Creates an eventlist file summarizing all events found in a certain folder. Only
:param path: Path to root folder where single Event folder are to found
"""
+
def geteventlistfromxml(path, outpath):
p = Proj(proj='utm', zone=32, ellps='WGS84')
-
# open eventlist file and write header
evlist = outpath + '/eventlist'
evlistobj = open(evlist, 'w')
- evlistobj.write('EventID Date To Lat Lon EAST NORTH Dep Ml NoP NoS RMS errH errZ Gap \n')
+ evlistobj.write(
+ 'EventID Date To Lat Lon EAST NORTH Dep Ml NoP NoS RMS errH errZ Gap \n')
# data path
dp = path + "/e*/*.xml"
@@ -52,28 +49,31 @@ def geteventlistfromxml(path, outpath):
NoP = []
NoS = []
except IndexError:
- print ('Insufficient data found for event (not localised): ' + names.split('/')[-1].split('_')[-1][:-4] + ' Skipping event for eventlist.' )
+ print('Insufficient data found for event (not localised): ' + names.split('/')[-1].split('_')[-1][
+ :-4] + ' Skipping event for eventlist.')
continue
-
+
for i in range(len(cat.events[0].origins[0].arrivals)):
if cat.events[0].origins[0].arrivals[i].phase == 'P':
NoP.append(cat.events[0].origins[0].arrivals[i].phase)
elif cat.events[0].origins[0].arrivals[i].phase == 'S':
NoS.append(cat.events[0].origins[0].arrivals[i].phase)
- #NoP = cat.events[0].origins[0].quality.used_station_count
+ # NoP = cat.events[0].origins[0].quality.used_station_count
errH = cat.events[0].origins[0].origin_uncertainty.max_horizontal_uncertainty
errZ = cat.events[0].origins[0].depth_errors.uncertainty
Gap = cat.events[0].origins[0].quality.azimuthal_gap
- #evID = names.split('/')[6]
+ # evID = names.split('/')[6]
evID = names.split('/')[-1].split('_')[-1][:-4]
Date = str(st.year) + str('%02d' % st.month) + str('%02d' % st.day)
To = str('%02d' % st.hour) + str('%02d' % st.minute) + str('%02d' % st.second) + \
- '.' + str('%06d' % st.microsecond)
+ '.' + str('%06d' % st.microsecond)
# write into eventlist
- evlistobj.write('%s %s %s %9.6f %9.6f %13.6f %13.6f %8.6f %3.1f %d %d NaN %d %d %d\n' %(evID, \
- Date, To, Lat, Lon, EAST, NORTH, Dep, Ml, len(NoP), len(NoS), errH, errZ, Gap))
- print ('Adding Event ' + names.split('/')[-1].split('_')[-1][:-4] + ' to eventlist')
+ evlistobj.write('%s %s %s %9.6f %9.6f %13.6f %13.6f %8.6f %3.1f %d %d NaN %d %d %d\n' % (evID, \
+ Date, To, Lat, Lon,
+ EAST, NORTH, Dep, Ml,
+ len(NoP), len(NoS),
+ errH, errZ, Gap))
+ print('Adding Event ' + names.split('/')[-1].split('_')[-1][:-4] + ' to eventlist')
print('Eventlist created and saved in: ' + outpath)
evlistobj.close()
-
diff --git a/pylot/core/io/getQualitiesfromxml.py b/pylot/core/io/getQualitiesfromxml.py
deleted file mode 100644
index 0e73d0bb..00000000
--- a/pylot/core/io/getQualitiesfromxml.py
+++ /dev/null
@@ -1,139 +0,0 @@
-#!/usr/bin/python
-# -*- coding: utf-8 -*-
-
-"""
- 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
- rev.: Ludger Küperkoch, igem, 10/2020
- Edited for usage in PyLoT: Jeldrik Gaal, igem, 01/2022
-"""
-
-import argparse
-import numpy as np
-import matplotlib.pyplot as plt
-from obspy.core.event import read_events
-import glob
-
-def getQualitiesfromxml(path):
-
- # uncertainties
- ErrorsP = [0.02, 0.04, 0.08, 0.16]
- ErrorsS = [0.04, 0.08, 0.16, 0.32]
-
- Pw0 = []
- Pw1 = []
- Pw2 = []
- Pw3 = []
- Pw4 = []
- Sw0 = []
- Sw1 = []
- Sw2 = []
- Sw3 = []
- Sw4 = []
-
- # data path
- dp = path + '/e*/*.xml'
- # list of all available xml-files
- xmlnames = glob.glob(dp)
-
- # read all onset weights
- for names in xmlnames:
- print("Getting onset weights from {}".format(names))
- cat = read_events(names)
- arrivals = cat.events[0].picks
- for Pick in arrivals:
- if Pick.phase_hint[0] == 'P':
- if Pick.time_errors.uncertainty <= ErrorsP[0]:
- Pw0.append(Pick.time_errors.uncertainty)
- elif Pick.time_errors.uncertainty > ErrorsP[0] and \
- Pick.time_errors.uncertainty <= ErrorsP[1]:
- Pw1.append(Pick.time_errors.uncertainty)
- elif Pick.time_errors.uncertainty > ErrorsP[1] and \
- Pick.time_errors.uncertainty <= ErrorsP[2]:
- Pw2.append(Pick.time_errors.uncertainty)
- elif Pick.time_errors.uncertainty > ErrorsP[2] and \
- Pick.time_errors.uncertainty <= ErrorsP[3]:
- Pw3.append(Pick.time_errors.uncertainty)
- elif Pick.time_errors.uncertainty > ErrorsP[3]:
- Pw4.append(Pick.time_errors.uncertainty)
- else:
- pass
- elif Pick.phase_hint[0] == 'S':
- if Pick.time_errors.uncertainty <= ErrorsS[0]:
- Sw0.append(Pick.time_errors.uncertainty)
- elif Pick.time_errors.uncertainty > ErrorsS[0] and \
- Pick.time_errors.uncertainty <= ErrorsS[1]:
- Sw1.append(Pick.time_errors.uncertainty)
- elif Pick.time_errors.uncertainty > ErrorsS[1] and \
- Pick.time_errors.uncertainty <= ErrorsS[2]:
- Sw2.append(Pick.time_errors.uncertainty)
- elif Pick.time_errors.uncertainty > ErrorsS[2] and \
- Pick.time_errors.uncertainty <= ErrorsS[3]:
- Sw3.append(Pick.time_errors.uncertainty)
- elif Pick.time_errors.uncertainty > ErrorsS[3]:
- Sw4.append(Pick.time_errors.uncertainty)
- else:
- pass
- else:
- print("Phase hint not defined for picking!")
- pass
- # get percentage of weights
- numPweights = np.sum([len(Pw0), len(Pw1), len(Pw2), len(Pw3), len(Pw4)])
- numSweights = np.sum([len(Sw0), len(Sw1), len(Sw2), len(Sw3), len(Sw4)])
- try:
- P0perc = 100.0 / numPweights * len(Pw0)
- except:
- P0perc = 0
- try:
- P1perc = 100.0 / numPweights * len(Pw1)
- except:
- P1perc = 0
- try:
- P2perc = 100.0 / numPweights * len(Pw2)
- except:
- P2perc = 0
- try:
- P3perc = 100.0 / numPweights * len(Pw3)
- except:
- P3perc = 0
- try:
- P4perc = 100.0 / numPweights * len(Pw4)
- except:
- P4perc = 0
- try:
- S0perc = 100.0 / numSweights * len(Sw0)
- except:
- Soperc = 0
- try:
- S1perc = 100.0 / numSweights * len(Sw1)
- except:
- S1perc = 0
- try:
- S2perc = 100.0 / numSweights * len(Sw2)
- except:
- S2perc = 0
- try:
- S3perc = 100.0 / numSweights * len(Sw3)
- except:
- S3perc = 0
- try:
- S4perc = 100.0 / numSweights * len(Sw4)
- except:
- S4perc = 0
-
- weights = ('0', '1', '2', '3', '4')
- y_pos = np.arange(len(weights))
- width = 0.34
- p1, = plt.bar(0 - width, P0perc, width, color='black')
- p2, = plt.bar(0, S0perc, width, color='red')
- plt.bar(y_pos - width, [P0perc, P1perc, P2perc, P3perc, P4perc], width, color='black')
- plt.bar(y_pos, [S0perc, S1perc, S2perc, S3perc, S4perc], width, color='red')
- plt.ylabel('%')
- plt.xticks(y_pos, weights)
- plt.xlim([-0.5, 4.5])
- plt.xlabel('Qualities')
- plt.title('{0} P-Qualities, {1} S-Qualities'.format(numPweights, numSweights))
- plt.legend([p1, p2], ['P-Weights', 'S-Weights'])
- plt.show()
-
diff --git a/pylot/core/io/phases.py b/pylot/core/io/phases.py
index 84513101..d7dc5856 100644
--- a/pylot/core/io/phases.py
+++ b/pylot/core/io/phases.py
@@ -1,13 +1,13 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
-import pdb
import glob
+import os
+import warnings
+
import matplotlib.pyplot as plt
import numpy as np
import obspy.core.event as ope
-import os
import scipy.io as sio
-import warnings
from obspy.core import UTCDateTime
from obspy.core.event import read_events
from obspy.core.util import AttribDict
@@ -17,7 +17,7 @@ from pylot.core.io.location import create_event, \
create_magnitude
from pylot.core.pick.utils import select_for_phase, get_quality_class
from pylot.core.util.utils import getOwner, full_range, four_digits, transformFilterString4Export, \
- backtransformFilterString
+ backtransformFilterString, loopIdentifyPhase, identifyPhase
def add_amplitudes(event, amplitudes):
@@ -232,7 +232,7 @@ def picksdict_from_picks(evt):
for pick in evt.picks:
phase = {}
station = pick.waveform_id.station_code
- if pick.waveform_id.channel_code == None:
+ if pick.waveform_id.channel_code is None:
channel = ''
else:
channel = pick.waveform_id.channel_code
@@ -375,8 +375,7 @@ def picks_from_picksdict(picks, creation_info=None):
def reassess_pilot_db(root_dir, db_dir, out_dir=None, fn_param=None, verbosity=0):
- import glob
-
+ # TODO: change root to datapath
db_root = os.path.join(root_dir, db_dir)
evt_list = glob.glob1(db_root, 'e????.???.??')
@@ -391,9 +390,7 @@ def reassess_pilot_event(root_dir, db_dir, event_id, out_dir=None, fn_param=None
from pylot.core.io.inputs import PylotParameter
from pylot.core.pick.utils import earllatepicker
-
- if fn_param is None:
- fn_param = defaults.AUTOMATIC_DEFAULTS
+ # TODO: change root to datapath
default = PylotParameter(fn_param, verbosity)
@@ -483,7 +480,6 @@ def reassess_pilot_event(root_dir, db_dir, event_id, out_dir=None, fn_param=None
os.makedirs(out_dir)
fnout_prefix = os.path.join(out_dir, 'PyLoT_{0}.'.format(event_id))
evt.write(fnout_prefix + 'xml', format='QUAKEML')
- # evt.write(fnout_prefix + 'cnv', format='VELEST')
def writephases(arrivals, fformat, filename, parameter=None, eventinfo=None):
@@ -511,14 +507,14 @@ def writephases(arrivals, fformat, filename, parameter=None, eventinfo=None):
:param eventinfo: optional, needed for VELEST-cnv file
and FOCMEC- and HASH-input files
:type eventinfo: `obspy.core.event.Event` object
- """
+ """
if fformat == 'NLLoc':
print("Writing phases to %s for NLLoc" % filename)
fid = open("%s" % filename, 'w')
# write header
fid.write('# EQEVENT: %s Label: EQ%s Loc: X 0.00 Y 0.00 Z 10.00 OT 0.00 \n' %
(parameter.get('database'), parameter.get('eventID')))
- arrivals = chooseArrivals(arrivals)
+ arrivals = chooseArrivals(arrivals) # MP MP what is chooseArrivals? It is not defined anywhere
for key in arrivals:
# P onsets
if arrivals[key].has_key('P'):
@@ -572,20 +568,20 @@ def writephases(arrivals, fformat, filename, parameter=None, eventinfo=None):
sweight = 0 # do not use pick
except KeyError as e:
print(str(e) + '; no weight set during processing')
- Ao = arrivals[key]['S']['Ao'] # peak-to-peak amplitude
+ Ao = arrivals[key]['S']['Ao'] # peak-to-peak amplitude
if Ao == None:
Ao = 0.0
- #fid.write('%s ? ? ? S %s %d%02d%02d %02d%02d %7.4f GAU 0 0 0 0 %d \n' % (key,
+ # fid.write('%s ? ? ? S %s %d%02d%02d %02d%02d %7.4f GAU 0 0 0 0 %d \n' % (key,
fid.write('%s ? ? ? S %s %d%02d%02d %02d%02d %7.4f GAU 0 %9.2f 0 0 %d \n' % (key,
- fm,
- year,
- month,
- day,
- hh,
- mm,
- ss_ms,
- Ao,
- sweight))
+ fm,
+ year,
+ month,
+ day,
+ hh,
+ mm,
+ ss_ms,
+ Ao,
+ sweight))
fid.close()
elif fformat == 'HYPO71':
@@ -594,7 +590,7 @@ def writephases(arrivals, fformat, filename, parameter=None, eventinfo=None):
# write header
fid.write(' %s\n' %
parameter.get('eventID'))
- arrivals = chooseArrivals(arrivals)
+ arrivals = chooseArrivals(arrivals) # MP MP what is chooseArrivals? It is not defined anywhere
for key in arrivals:
if arrivals[key]['P']['weight'] < 4:
stat = key
@@ -671,7 +667,7 @@ def writephases(arrivals, fformat, filename, parameter=None, eventinfo=None):
fid = open("%s" % filename, 'w')
# write header
fid.write('%s, event %s \n' % (parameter.get('database'), parameter.get('eventID')))
- arrivals = chooseArrivals(arrivals)
+ arrivals = chooseArrivals(arrivals) # MP MP what is chooseArrivals? It is not defined anywhere
for key in arrivals:
# P onsets
if arrivals[key].has_key('P') and arrivals[key]['P']['mpp'] is not None:
@@ -769,7 +765,7 @@ def writephases(arrivals, fformat, filename, parameter=None, eventinfo=None):
arrivals = picksdict_from_picks(evt)
# check for automatic and manual picks
# prefer manual picks
- usedarrivals = chooseArrival(arrivals)
+ usedarrivals = chooseArrivals(arrivals)
for key in usedarrivals:
# P onsets
if usedarrivals[key].has_key('P'):
@@ -781,7 +777,7 @@ def writephases(arrivals, fformat, filename, parameter=None, eventinfo=None):
Ponset = usedarrivals[key]['P']['mpp']
Pweight = usedarrivals[key]['P']['weight']
Prt = Ponset - stime # onset time relative to source time
- if n % 6 is not 0:
+ if n % 6 != 0:
fid.write('%-4sP%d%6.2f' % (stat, Pweight, Prt))
else:
fid.write('%-4sP%d%6.2f\n' % (stat, Pweight, Prt))
@@ -795,7 +791,7 @@ def writephases(arrivals, fformat, filename, parameter=None, eventinfo=None):
Sonset = usedarrivals[key]['S']['mpp']
Sweight = usedarrivals[key]['S']['weight']
Srt = Ponset - stime # onset time relative to source time
- if n % 6 is not 0:
+ if n % 6 != 0:
fid.write('%-4sS%d%6.2f' % (stat, Sweight, Srt))
else:
fid.write('%-4sS%d%6.2f\n' % (stat, Sweight, Srt))
@@ -815,9 +811,9 @@ def writephases(arrivals, fformat, filename, parameter=None, eventinfo=None):
event = eventinfo['pylot_id']
hddID = event.split('.')[0][1:5]
except:
- print ("Error 1111111!")
- hddID = "00000"
- # write header
+ print("Error 1111111!")
+ hddID = "00000"
+ # write header
fid.write('# %d %d %d %d %d %5.2f %7.4f +%6.4f %7.4f %4.2f 0.1 0.5 %4.2f %s\n' % (
stime.year, stime.month, stime.day, stime.hour, stime.minute, stime.second,
eventsource['latitude'], eventsource['longitude'], eventsource['depth'] / 1000,
@@ -830,7 +826,7 @@ def writephases(arrivals, fformat, filename, parameter=None, eventinfo=None):
arrivals = picksdict_from_picks(evt)
# check for automatic and manual picks
# prefer manual picks
- usedarrivals = chooseArrival(arrivals)
+ usedarrivals = chooseArrivals(arrivals)
for key in usedarrivals:
if usedarrivals[key].has_key('P'):
# P onsets
@@ -881,7 +877,7 @@ def writephases(arrivals, fformat, filename, parameter=None, eventinfo=None):
arrivals = picksdict_from_picks(evt)
# check for automatic and manual picks
# prefer manual picks
- usedarrivals = chooseArrival(arrivals)
+ usedarrivals = chooseArrivals(arrivals)
for key in usedarrivals:
if usedarrivals[key].has_key('P'):
if usedarrivals[key]['P']['weight'] < 4 and usedarrivals[key]['P']['fm'] is not None:
@@ -962,7 +958,7 @@ def writephases(arrivals, fformat, filename, parameter=None, eventinfo=None):
erh, erz, eventinfo.magnitudes[0]['mag'],
hashID))
# Prefer Manual Picks over automatic ones if possible
- arrivals = chooseArrivals(arrivals)
+ arrivals = chooseArrivals(arrivals) # MP MP what is chooseArrivals? It is not defined anywhere
# write phase lines
for key in arrivals:
if arrivals[key].has_key('P'):
@@ -1009,7 +1005,8 @@ def writephases(arrivals, fformat, filename, parameter=None, eventinfo=None):
fid1.close()
fid2.close()
-def chooseArrival(arrivals):
+
+def chooseArrivals(arrivals):
"""
takes arrivals and returns the manual picks if manual and automatic ones are there
returns automatic picks if only automatic picks are there
@@ -1058,37 +1055,63 @@ def merge_picks(event, picks):
return event
-def getQualitiesfromxml(xmlnames, ErrorsP, ErrorsS, plotflag=1):
+def getQualitiesfromxml(path, errorsP, errorsS, plotflag=1, figure=None, verbosity=0):
"""
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 path: path containing xml files
+ :type path: str
+ :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 get_quality_class
- from pylot.core.util.utils import loopIdentifyPhase, identifyPhase
+ def calc_perc(uncertainties, ntotal):
+ ''' simple function that calculates percentage of number of uncertainties (list length)'''
+ if len(uncertainties) == 0:
+ return 0
+ else:
+ return 100. / ntotal * len(uncertainties)
+
+ def calc_weight_perc(psweights, weight_ids):
+ ''' calculate percentages of different weights (pick classes!?) of total number of uncertainties of a phase'''
+ # count total number of list items for this phase
+ numWeights = np.sum([len(weight) for weight in psweights.values()])
+
+ # iterate over all available weights to return a list with percentages for plotting
+ plot_list = []
+ for weight_id in weight_ids:
+ plot_list.append(calc_perc(psweights[weight_id], numWeights))
+
+ return plot_list, numWeights
+
+ # get all xmlfiles in path (maybe this should be changed to one xml file for this function, selectable via GUI?)
+ xmlnames = glob.glob(os.path.join(path, '*.xml'))
+ if len(xmlnames) == 0:
+ print(f'No files found in path {path}.')
+ return False
+
+ # first define possible phases here
+ phases = ['P', 'S']
+
+ # define possible weights (0-4)
+ weight_ids = list(range(5))
+
+ # put both error lists in a dictionary with P/S key so that amount of code can be halfed by simply using P/S as key
+ errors = dict(P=errorsP, S=errorsS)
+
+ # create dictionaries for each phase (P/S) with a dictionary of empty list for each weight defined in weights
+ # tuple above
+ weights = {}
+ for phase in phases:
+ weights[phase] = {weight_id: [] for weight_id in weight_ids}
- # read all onset weights
- Pw0 = []
- Pw1 = []
- Pw2 = []
- Pw3 = []
- Pw4 = []
- Sw0 = []
- Sw1 = []
- Sw2 = []
- Sw3 = []
- Sw4 = []
for names in xmlnames:
print("Getting onset weights from {}".format(names))
cat = read_events(names)
@@ -1096,117 +1119,60 @@ def getQualitiesfromxml(xmlnames, ErrorsP, ErrorsS, plotflag=1):
arrivals = cat.events[0].picks
arrivals_copy = cat_copy.events[0].picks
# Prefere manual picks if qualities are sufficient!
- for Pick in arrivals:
- if Pick.method_id.id.split('/')[1] == 'manual':
- mstation = Pick.waveform_id.station_code
+ for pick in arrivals:
+ if pick.method_id.id.split('/')[1] == 'manual':
+ mstation = pick.waveform_id.station_code
mstation_ext = mstation + '_'
for mpick in arrivals_copy:
- phase = identifyPhase(loopIdentifyPhase(Pick.phase_hint))
- if phase == 'P':
- if ((mpick.waveform_id.station_code == mstation) or
- (mpick.waveform_id.station_code == mstation_ext)) and \
- (mpick.method_id.split('/')[1] == 'auto') and \
- (mpick.time_errors['uncertainty'] <= ErrorsP[3]):
- del mpick
- break
- elif phase == 'S':
- if ((mpick.waveform_id.station_code == mstation) or
- (mpick.waveform_id.station_code == mstation_ext)) and \
- (mpick.method_id.split('/')[1] == 'auto') and \
- (mpick.time_errors['uncertainty'] <= ErrorsS[3]):
- del mpick
- break
+ phase = identifyPhase(loopIdentifyPhase(pick.phase_hint)) # MP MP catch if this fails?
+ if ((mpick.waveform_id.station_code == mstation) or
+ (mpick.waveform_id.station_code == mstation_ext)) and \
+ (mpick.method_id.id.split('/')[1] == 'auto') and \
+ (mpick.time_errors['uncertainty'] <= errors[phase][3]):
+ del mpick
+ break
lendiff = len(arrivals) - len(arrivals_copy)
- if lendiff is not 0:
+ if lendiff != 0:
print("Found manual as well as automatic picks, prefered the {} manual ones!".format(lendiff))
- for Pick in arrivals_copy:
- phase = identifyPhase(loopIdentifyPhase(Pick.phase_hint))
- if phase == 'P':
- Pqual = get_quality_class(Pick.time_errors.uncertainty, ErrorsP)
- if Pqual == 0:
- Pw0.append(Pick.time_errors.uncertainty)
- elif Pqual == 1:
- Pw1.append(Pick.time_errors.uncertainty)
- elif Pqual == 2:
- Pw2.append(Pick.time_errors.uncertainty)
- elif Pqual == 3:
- Pw3.append(Pick.time_errors.uncertainty)
- elif Pqual == 4:
- Pw4.append(Pick.time_errors.uncertainty)
- elif phase == 'S':
- Squal = get_quality_class(Pick.time_errors.uncertainty, ErrorsS)
- if Squal == 0:
- Sw0.append(Pick.time_errors.uncertainty)
- elif Squal == 1:
- Sw1.append(Pick.time_errors.uncertainty)
- elif Squal == 2:
- Sw2.append(Pick.time_errors.uncertainty)
- elif Squal == 3:
- Sw3.append(Pick.time_errors.uncertainty)
- elif Squal == 4:
- Sw4.append(Pick.time_errors.uncertainty)
- else:
+ for pick in arrivals_copy:
+ phase = identifyPhase(loopIdentifyPhase(pick.phase_hint))
+ uncertainty = pick.time_errors.uncertainty
+ if not uncertainty:
+ if verbosity > 0:
+ print('No uncertainty, pick {} invalid!'.format(pick.method_id.id))
+ continue
+ # check P/S phase
+ if phase not in phases:
print("Phase hint not defined for picking!")
- pass
+ continue
+
+ qual = get_quality_class(uncertainty, errors[phase])
+ weights[phase][qual].append(uncertainty)
if plotflag == 0:
- Punc = [Pw0, Pw1, Pw2, Pw3, Pw4]
- Sunc = [Sw0, Sw1, Sw2, Sw3, Sw4]
- return Punc, Sunc
+ p_unc = [weights['P'][weight_id] for weight_id in weight_ids]
+ s_unc = [weights['S'][weight_id] for weight_id in weight_ids]
+ return p_unc, s_unc
else:
+ if not figure:
+ fig = plt.figure()
+ ax = fig.add_subplot(111)
# get percentage of weights
- numPweights = np.sum([len(Pw0), len(Pw1), len(Pw2), len(Pw3), len(Pw4)])
- numSweights = np.sum([len(Sw0), len(Sw1), len(Sw2), len(Sw3), len(Sw4)])
- if len(Pw0) > 0:
- P0perc = 100 / numPweights * len(Pw0)
- else:
- P0perc = 0
- if len(Pw1) > 0:
- P1perc = 100 / numPweights * len(Pw1)
- else:
- P1perc = 0
- if len(Pw2) > 0:
- P2perc = 100 / numPweights * len(Pw2)
- else:
- P2perc = 0
- if len(Pw3) > 0:
- P3perc = 100 / numPweights * len(Pw3)
- else:
- P3perc = 0
- if len(Pw4) > 0:
- P4perc = 100 / numPweights * len(Pw4)
- else:
- P4perc = 0
- if len(Sw0) > 0:
- S0perc = 100 / numSweights * len(Sw0)
- else:
- S0perc = 0
- if len(Sw1) > 0:
- S1perc = 100 / numSweights * len(Sw1)
- else:
- S1perc = 0
- if len(Sw2) > 0:
- S2perc = 100 / numSweights * len(Sw2)
- else:
- S2perc = 0
- if len(Sw3) > 0:
- S3perc = 100 / numSweights * len(Sw3)
- else:
- S3perc = 0
- if len(Sw4) > 0:
- S4perc = 100 / numSweights * len(Sw4)
- else:
- S4perc = 0
+ listP, numPweights = calc_weight_perc(weights['P'], weight_ids)
+ listS, numSweights = calc_weight_perc(weights['S'], weight_ids)
- weights = ('0', '1', '2', '3', '4')
- y_pos = np.arange(len(weights))
+ y_pos = np.arange(len(weight_ids))
width = 0.34
- plt.bar(y_pos - width, [P0perc, P1perc, P2perc, P3perc, P4perc], width, color='black')
- plt.bar(y_pos, [S0perc, S1perc, S2perc, S3perc, S4perc], width, color='red')
- plt.ylabel('%')
- plt.xticks(y_pos, weights)
- plt.xlim([-0.5, 4.5])
- plt.xlabel('Qualities')
- plt.title('{0} P-Qualities, {1} S-Qualities'.format(numPweights, numSweights))
- plt.show()
+ ax.bar(y_pos - width, listP, width, color='black')
+ ax.bar(y_pos, listS, width, color='red')
+ ax.set_ylabel('%')
+ ax.set_xticks(y_pos, weight_ids)
+ ax.set_xlim([-0.5, 4.5])
+ ax.set_xlabel('Qualities')
+ ax.set_title('{0} P-Qualities, {1} S-Qualities'.format(numPweights, numSweights))
+
+ if not figure:
+ fig.show()
+
+ return listP, listS
diff --git a/pylot/core/loc/nll.py b/pylot/core/loc/nll.py
index 5ed9d101..a3dd45a3 100644
--- a/pylot/core/loc/nll.py
+++ b/pylot/core/loc/nll.py
@@ -4,11 +4,12 @@
import glob
import os
import subprocess
+
from obspy import read_events
from pylot.core.io.phases import writephases
-from pylot.core.util.utils import getPatternLine, runProgram
from pylot.core.util.gui import which
+from pylot.core.util.utils import getPatternLine, runProgram
from pylot.core.util.version import get_git_version as _getVersionString
__version__ = _getVersionString()
diff --git a/pylot/core/pick/autopick.py b/pylot/core/pick/autopick.py
index fbaf0099..91f104f4 100644
--- a/pylot/core/pick/autopick.py
+++ b/pylot/core/pick/autopick.py
@@ -9,21 +9,21 @@ function conglomerate utils.
:author: MAGS2 EP3 working group / Ludger Kueperkoch
"""
import copy
+import traceback
+
import matplotlib.pyplot as plt
import numpy as np
-import traceback
+from obspy import Trace
from obspy.taup import TauPyModel
from pylot.core.pick.charfuns import CharacteristicFunction
from pylot.core.pick.charfuns import HOScf, AICcf, ARZcf, ARHcf, AR3Ccf
from pylot.core.pick.picker import AICPicker, PragPicker
from pylot.core.pick.utils import checksignallength, checkZ4S, earllatepicker, \
- getSNR, fmpicker, checkPonsets, wadaticheck, get_pickparams, get_quality_class
-from pylot.core.util.utils import getPatternLine, gen_Pool,\
+ getSNR, fmpicker, checkPonsets, wadaticheck, get_quality_class
+from pylot.core.util.utils import getPatternLine, gen_Pool, \
get_Bool, identifyPhaseID, get_None, correct_iplot
-from obspy.taup import TauPyModel
-from obspy import Trace
def autopickevent(data, param, iplot=0, fig_dict=None, fig_dict_wadatijack=None, ncores=0, metadata=None, origin=None):
"""
@@ -182,25 +182,25 @@ class PickingResults(dict):
# TODO What are those?
self.w0 = None
self.fc = None
- self.Ao = None # Wood-Anderson peak-to-peak amplitude
+ self.Ao = None # Wood-Anderson peak-to-peak amplitude
# Station information
self.network = None
self.channel = None
# pick information
- self.picker = 'auto' # type of pick
+ self.picker = 'auto' # type of pick
self.marked = []
# pick results
- self.epp = None # earliest possible pick
- self.mpp = None # most likely onset
- self.lpp = None # latest possible pick
- self.fm = 'N' # first motion polarity, can be set to 'U' (Up) or 'D' (Down)
- self.snr = None # signal-to-noise ratio of onset
- self.snrdb = None # signal-to-noise ratio of onset [dB]
- self.spe = None # symmetrized picking error
- self.weight = 4 # weight of onset
+ self.epp = None # earliest possible pick
+ self.mpp = None # most likely onset
+ self.lpp = None # latest possible pick
+ self.fm = 'N' # first motion polarity, can be set to 'U' (Up) or 'D' (Down)
+ self.snr = None # signal-to-noise ratio of onset
+ self.snrdb = None # signal-to-noise ratio of onset [dB]
+ self.spe = None # symmetrized picking error
+ self.weight = 4 # weight of onset
# to correctly provide dot access to dictionary attributes, all attribute access of the class is forwarded to the
# dictionary
@@ -335,9 +335,10 @@ class AutopickStation(object):
"""
waveform_data = {}
for key in self.channelorder:
- waveform_data[key] = self.wfstream.select(component=key) # try ZNE first
+ waveform_data[key] = self.wfstream.select(component=key) # try ZNE first
if len(waveform_data[key]) == 0:
- waveform_data[key] = self.wfstream.select(component=str(self.channelorder[key])) # use 123 as second option
+ waveform_data[key] = self.wfstream.select(
+ component=str(self.channelorder[key])) # use 123 as second option
return waveform_data['Z'], waveform_data['N'], waveform_data['E']
def get_traces_from_streams(self):
@@ -474,13 +475,9 @@ class AutopickStation(object):
# TODO here the pickparams is modified, instead of a copy
self.pickparams["pstart"] = 0
- if self.pickparams["use_taup"] is False or not self.origin or not self.metadata:
+ if self.pickparams["use_taup"] is False:
# correct user mistake where a relative cuttime is selected (pstart < 0) but use of taupy is disabled/ has
# not the required parameters
- if not self.origin:
- print('Requested use_taup but no origin given. Exit taupy.')
- if not self.metadata:
- print('Requested use_taup but no metadata given. Exit taupy.')
exit_taupy()
return
@@ -528,7 +525,7 @@ class AutopickStation(object):
self.plot_pick_results()
self.finish_picking()
- return [{'P': self.p_results, 'S':self.s_results}, self.ztrace.stats.station]
+ return [{'P': self.p_results, 'S': self.s_results}, self.ztrace.stats.station]
def finish_picking(self):
@@ -577,7 +574,7 @@ class AutopickStation(object):
self.s_results.channel = self.etrace.stats.channel
self.s_results.network = self.etrace.stats.network
- self.s_results.fm = None # override default value 'N'
+ self.s_results.fm = None # override default value 'N'
def plot_pick_results(self):
if self.iplot > 0:
@@ -592,12 +589,14 @@ class AutopickStation(object):
plt_flag = 0
fig._tight = True
ax1 = fig.add_subplot(311)
- tdata = np.linspace(start=0, stop=self.ztrace.stats.endtime-self.ztrace.stats.starttime, num=self.ztrace.stats.npts)
+ tdata = np.linspace(start=0, stop=self.ztrace.stats.endtime - self.ztrace.stats.starttime,
+ num=self.ztrace.stats.npts)
# plot tapered trace filtered with bpz2 filter settings
- ax1.plot(tdata, self.tr_filt_z_bpz2.data/max(self.tr_filt_z_bpz2.data), color=linecolor, linewidth=0.7, label='Data')
+ ax1.plot(tdata, self.tr_filt_z_bpz2.data / max(self.tr_filt_z_bpz2.data), color=linecolor, linewidth=0.7,
+ label='Data')
if self.p_results.weight < 4:
# plot CF of initial onset (HOScf or ARZcf)
- ax1.plot(self.cf1.getTimeArray(), self.cf1.getCF()/max(self.cf1.getCF()), 'b', label='CF1')
+ ax1.plot(self.cf1.getTimeArray(), self.cf1.getCF() / max(self.cf1.getCF()), 'b', label='CF1')
if self.p_data.p_aic_plot_flag == 1:
aicpick = self.p_data.aicpick
refPpick = self.p_data.refPpick
@@ -635,23 +634,28 @@ class AutopickStation(object):
if self.horizontal_traces_exist() and self.s_data.Sflag == 1:
# plot E trace
ax2 = fig.add_subplot(3, 1, 2, sharex=ax1)
- th1data = np.linspace(0, self.etrace.stats.endtime-self.etrace.stats.starttime, self.etrace.stats.npts)
+ th1data = np.linspace(0, self.etrace.stats.endtime - self.etrace.stats.starttime,
+ self.etrace.stats.npts)
# plot filtered and tapered waveform
- ax2.plot(th1data, self.etrace.data / max(self.etrace.data), color=linecolor, linewidth=0.7, label='Data')
+ ax2.plot(th1data, self.etrace.data / max(self.etrace.data), color=linecolor, linewidth=0.7,
+ label='Data')
if self.p_results.weight < 4:
# plot initial CF (ARHcf or AR3Ccf)
- ax2.plot(self.arhcf1.getTimeArray(), self.arhcf1.getCF() / max(self.arhcf1.getCF()), 'b', label='CF1')
+ ax2.plot(self.arhcf1.getTimeArray(), self.arhcf1.getCF() / max(self.arhcf1.getCF()), 'b',
+ label='CF1')
if self.s_data.aicSflag == 1 and self.s_results.weight <= 4:
aicarhpick = self.aicarhpick
refSpick = self.refSpick
# plot second cf, used for determing precise onset (ARHcf or AR3Ccf)
- ax2.plot(self.arhcf2.getTimeArray(), self.arhcf2.getCF() / max(self.arhcf2.getCF()), 'm', label='CF2')
+ ax2.plot(self.arhcf2.getTimeArray(), self.arhcf2.getCF() / max(self.arhcf2.getCF()), 'm',
+ label='CF2')
# plot preliminary onset time, calculated from CF1
ax2.plot([aicarhpick.getpick(), aicarhpick.getpick()], [-1, 1], 'g', label='Initial S Onset')
ax2.plot([aicarhpick.getpick() - 0.5, aicarhpick.getpick() + 0.5], [1, 1], 'g')
ax2.plot([aicarhpick.getpick() - 0.5, aicarhpick.getpick() + 0.5], [-1, -1], 'g')
# plot precise onset time, calculated from CF2
- ax2.plot([refSpick.getpick(), refSpick.getpick()], [-1.3, 1.3], 'g', linewidth=2, label='Final S Pick')
+ ax2.plot([refSpick.getpick(), refSpick.getpick()], [-1.3, 1.3], 'g', linewidth=2,
+ label='Final S Pick')
ax2.plot([refSpick.getpick() - 0.5, refSpick.getpick() + 0.5], [1.3, 1.3], 'g', linewidth=2)
ax2.plot([refSpick.getpick() - 0.5, refSpick.getpick() + 0.5], [-1.3, -1.3], 'g', linewidth=2)
ax2.plot([self.s_results.lpp, self.s_results.lpp], [-1.1, 1.1], 'g--', label='lpp')
@@ -671,15 +675,19 @@ class AutopickStation(object):
# plot N trace
ax3 = fig.add_subplot(3, 1, 3, sharex=ax1)
- th2data= np.linspace(0, self.ntrace.stats.endtime-self.ntrace.stats.starttime, self.ntrace.stats.npts)
+ th2data = np.linspace(0, self.ntrace.stats.endtime - self.ntrace.stats.starttime,
+ self.ntrace.stats.npts)
# plot trace
- ax3.plot(th2data, self.ntrace.data / max(self.ntrace.data), color=linecolor, linewidth=0.7, label='Data')
+ ax3.plot(th2data, self.ntrace.data / max(self.ntrace.data), color=linecolor, linewidth=0.7,
+ label='Data')
if self.p_results.weight < 4:
- p22, = ax3.plot(self.arhcf1.getTimeArray(), self.arhcf1.getCF() / max(self.arhcf1.getCF()), 'b', label='CF1')
+ p22, = ax3.plot(self.arhcf1.getTimeArray(), self.arhcf1.getCF() / max(self.arhcf1.getCF()), 'b',
+ label='CF1')
if self.s_data.aicSflag == 1:
aicarhpick = self.aicarhpick
refSpick = self.refSpick
- ax3.plot(self.arhcf2.getTimeArray(), self.arhcf2.getCF() / max(self.arhcf2.getCF()), 'm', label='CF2')
+ ax3.plot(self.arhcf2.getTimeArray(), self.arhcf2.getCF() / max(self.arhcf2.getCF()), 'm',
+ label='CF2')
ax3.plot([aicarhpick.getpick(), aicarhpick.getpick()], [-1, 1], 'g', label='Initial S Onset')
ax3.plot([aicarhpick.getpick() - 0.5, aicarhpick.getpick() + 0.5], [1, 1], 'g')
ax3.plot([aicarhpick.getpick() - 0.5, aicarhpick.getpick() + 0.5], [-1, -1], 'g')
@@ -720,7 +728,8 @@ class AutopickStation(object):
if aicpick.getpick() is None:
msg = "Bad initial (AIC) P-pick, skipping this onset!\nAIC-SNR={0}, AIC-Slope={1}counts/s\n " \
"(min. AIC-SNR={2}, min. AIC-Slope={3}counts/s)"
- msg = msg.format(aicpick.getSNR(), aicpick.getSlope(), self.pickparams["minAICPSNR"], self.pickparams["minAICPslope"])
+ msg = msg.format(aicpick.getSNR(), aicpick.getSlope(), self.pickparams["minAICPSNR"],
+ self.pickparams["minAICPslope"])
self.vprint(msg)
return 0
# Quality check initial pick with minimum signal length
@@ -730,14 +739,16 @@ class AutopickStation(object):
if len(self.nstream) == 0 or len(self.estream) == 0:
msg = 'One or more horizontal component(s) missing!\n' \
'Signal length only checked on vertical component!\n' \
- 'Decreasing minsiglengh from {0} to {1}'\
- .format(minsiglength, minsiglength / 2)
+ 'Decreasing minsiglengh from {0} to {1}' \
+ .format(minsiglength, minsiglength / 2)
self.vprint(msg)
minsiglength = minsiglength / 2
else:
# filter, taper other traces as well since signal length is compared on all traces
- trH1_filt, _ = self.prepare_wfstream(self.estream, freqmin=self.pickparams["bph1"][0], freqmax=self.pickparams["bph1"][1])
- trH2_filt, _ = self.prepare_wfstream(self.nstream, freqmin=self.pickparams["bph1"][0], freqmax=self.pickparams["bph1"][1])
+ trH1_filt, _ = self.prepare_wfstream(self.estream, freqmin=self.pickparams["bph1"][0],
+ freqmax=self.pickparams["bph1"][1])
+ trH2_filt, _ = self.prepare_wfstream(self.nstream, freqmin=self.pickparams["bph1"][0],
+ freqmax=self.pickparams["bph1"][1])
zne += trH1_filt
zne += trH2_filt
minsiglength = minsiglength
@@ -823,15 +834,18 @@ class AutopickStation(object):
# get preliminary onset time from AIC-CF
self.set_current_figure('aicFig')
aicpick = AICPicker(aiccf, self.pickparams["tsnrz"], self.pickparams["pickwinP"], self.iplot,
- Tsmooth=self.pickparams["aictsmooth"], fig=self.current_figure, linecolor=self.current_linecolor)
+ Tsmooth=self.pickparams["aictsmooth"], fig=self.current_figure,
+ linecolor=self.current_linecolor)
# save aicpick for plotting later
self.p_data.aicpick = aicpick
# add pstart and pstop to aic plot
if self.current_figure:
# TODO remove plotting from picking, make own plot function
for ax in self.current_figure.axes:
- ax.vlines(self.pickparams["pstart"], ax.get_ylim()[0], ax.get_ylim()[1], color='c', linestyles='dashed', label='P start')
- ax.vlines(self.pickparams["pstop"], ax.get_ylim()[0], ax.get_ylim()[1], color='c', linestyles='dashed', label='P stop')
+ ax.vlines(self.pickparams["pstart"], ax.get_ylim()[0], ax.get_ylim()[1], color='c', linestyles='dashed',
+ label='P start')
+ ax.vlines(self.pickparams["pstop"], ax.get_ylim()[0], ax.get_ylim()[1], color='c', linestyles='dashed',
+ label='P stop')
ax.legend(loc=1)
Pflag = self._pick_p_quality_control(aicpick, z_copy, tr_filt)
@@ -845,7 +859,8 @@ class AutopickStation(object):
error_msg = 'AIC P onset slope to small: got {}, min {}'.format(slope, self.pickparams["minAICPslope"])
raise PickingFailedException(error_msg)
if aicpick.getSNR() < self.pickparams["minAICPSNR"]:
- error_msg = 'AIC P onset SNR to small: got {}, min {}'.format(aicpick.getSNR(), self.pickparams["minAICPSNR"])
+ error_msg = 'AIC P onset SNR to small: got {}, min {}'.format(aicpick.getSNR(),
+ self.pickparams["minAICPSNR"])
raise PickingFailedException(error_msg)
self.p_data.p_aic_plot_flag = 1
@@ -853,7 +868,8 @@ class AutopickStation(object):
'autopickstation: re-filtering vertical trace...'.format(aicpick.getSlope(), aicpick.getSNR())
self.vprint(msg)
# refilter waveform with larger bandpass
- tr_filt, z_copy = self.prepare_wfstream(self.zstream, freqmin=self.pickparams["bpz2"][0], freqmax=self.pickparams["bpz2"][1])
+ tr_filt, z_copy = self.prepare_wfstream(self.zstream, freqmin=self.pickparams["bpz2"][0],
+ freqmax=self.pickparams["bpz2"][1])
# save filtered trace in instance for later plotting
self.tr_filt_z_bpz2 = tr_filt
# determine new times around initial onset
@@ -865,25 +881,29 @@ class AutopickStation(object):
else:
self.cf2 = None
assert isinstance(self.cf2, CharacteristicFunction), 'cf2 is not set correctly: maybe the algorithm name () is ' \
- 'corrupted'.format(self.pickparams["algoP"])
+ 'corrupted'.format(self.pickparams["algoP"])
self.set_current_figure('refPpick')
# get refined onset time from CF2
- refPpick = PragPicker(self.cf2, self.pickparams["tsnrz"], self.pickparams["pickwinP"], self.iplot, self.pickparams["ausP"],
- self.pickparams["tsmoothP"], aicpick.getpick(), self.current_figure, self.current_linecolor)
+ refPpick = PragPicker(self.cf2, self.pickparams["tsnrz"], self.pickparams["pickwinP"], self.iplot,
+ self.pickparams["ausP"],
+ self.pickparams["tsmoothP"], aicpick.getpick(), self.current_figure,
+ self.current_linecolor)
# save PragPicker result for plotting
self.p_data.refPpick = refPpick
self.p_results.mpp = refPpick.getpick()
if self.p_results.mpp is None:
msg = 'Bad initial (AIC) P-pick, skipping this onset!\n AIC-SNR={}, AIC-Slope={}counts/s\n' \
'(min. AIC-SNR={}, min. AIC-Slope={}counts/s)'
- msg.format(aicpick.getSNR(), aicpick.getSlope(), self.pickparams["minAICPSNR"], self.pickparams["minAICPslope"])
+ msg.format(aicpick.getSNR(), aicpick.getSlope(), self.pickparams["minAICPSNR"],
+ self.pickparams["minAICPslope"])
self.vprint(msg)
self.s_data.Sflag = 0
raise PickingFailedException(msg)
# quality assessment, get earliest/latest pick and symmetrized uncertainty
- #todo quality assessment in own function
+ # todo quality assessment in own function
self.set_current_figure('el_Ppick')
- elpicker_results = earllatepicker(z_copy, self.pickparams["nfacP"], self.pickparams["tsnrz"], self.p_results.mpp,
+ elpicker_results = earllatepicker(z_copy, self.pickparams["nfacP"], self.pickparams["tsnrz"],
+ self.p_results.mpp,
self.iplot, fig=self.current_figure, linecolor=self.current_linecolor)
self.p_results.epp, self.p_results.lpp, self.p_results.spe = elpicker_results
snr_results = getSNR(z_copy, self.pickparams["tsnrz"], self.p_results.mpp)
@@ -891,7 +911,8 @@ class AutopickStation(object):
# weight P-onset using symmetric error
self.p_results.weight = get_quality_class(self.p_results.spe, self.pickparams["timeerrorsP"])
- if self.p_results.weight <= self.pickparams["minfmweight"] and self.p_results.snr >= self.pickparams["minFMSNR"]:
+ if self.p_results.weight <= self.pickparams["minfmweight"] and self.p_results.snr >= self.pickparams[
+ "minFMSNR"]:
# if SNR is high enough, try to determine first motion of onset
self.set_current_figure('fm_picker')
self.p_results.fm = fmpicker(self.zstream, z_copy, self.pickparams["fmpickwin"], self.p_results.mpp,
@@ -964,7 +985,7 @@ class AutopickStation(object):
trH1_filt, _ = self.prepare_wfstream(self.zstream, filter_freq_min, filter_freq_max)
trH2_filt, _ = self.prepare_wfstream(self.estream, filter_freq_min, filter_freq_max)
trH3_filt, _ = self.prepare_wfstream(self.nstream, filter_freq_min, filter_freq_max)
- h_copy =self. hdat.copy()
+ h_copy = self.hdat.copy()
h_copy[0].data = trH1_filt.data
h_copy[1].data = trH2_filt.data
h_copy[2].data = trH3_filt.data
@@ -1119,7 +1140,8 @@ class AutopickStation(object):
# get preliminary onset time from AIC cf
self.set_current_figure('aicARHfig')
aicarhpick = AICPicker(haiccf, self.pickparams["tsnrh"], self.pickparams["pickwinS"], self.iplot,
- Tsmooth=self.pickparams["aictsmoothS"], fig=self.current_figure, linecolor=self.current_linecolor)
+ Tsmooth=self.pickparams["aictsmoothS"], fig=self.current_figure,
+ linecolor=self.current_linecolor)
# save pick for later plotting
self.aicarhpick = aicarhpick
@@ -1130,8 +1152,10 @@ class AutopickStation(object):
# get refined onset time from CF2
self.set_current_figure('refSpick')
- refSpick = PragPicker(arhcf2, self.pickparams["tsnrh"], self.pickparams["pickwinS"], self.iplot, self.pickparams["ausS"],
- self.pickparams["tsmoothS"], aicarhpick.getpick(), self.current_figure, self.current_linecolor)
+ refSpick = PragPicker(arhcf2, self.pickparams["tsnrh"], self.pickparams["pickwinS"], self.iplot,
+ self.pickparams["ausS"],
+ self.pickparams["tsmoothS"], aicarhpick.getpick(), self.current_figure,
+ self.current_linecolor)
# save refSpick for later plotitng
self.refSpick = refSpick
self.s_results.mpp = refSpick.getpick()
@@ -1155,7 +1179,6 @@ class AutopickStation(object):
self.current_linecolor = plot_style['linecolor']['rgba_mpl']
-
def autopickstation(wfstream, pickparam, verbose=False, iplot=0, fig_dict=None, metadata=None, origin=None):
"""
Main function to calculate picks for the station.
@@ -1243,11 +1266,11 @@ def iteratepicker(wf, NLLocfile, picks, badpicks, pickparameter, fig_dict=None):
print(
"iteratepicker: The following picking parameters have been modified for iterative picking:")
print(
- "pstart: %fs => %fs" % (pstart_old, pickparameter.get('pstart')))
+ "pstart: %fs => %fs" % (pstart_old, pickparameter.get('pstart')))
print(
- "pstop: %fs => %fs" % (pstop_old, pickparameter.get('pstop')))
+ "pstop: %fs => %fs" % (pstop_old, pickparameter.get('pstop')))
print(
- "sstop: %fs => %fs" % (sstop_old, pickparameter.get('sstop')))
+ "sstop: %fs => %fs" % (sstop_old, pickparameter.get('sstop')))
print("pickwinP: %fs => %fs" % (
pickwinP_old, pickparameter.get('pickwinP')))
print("Precalcwin: %fs => %fs" % (
diff --git a/pylot/core/pick/charfuns.py b/pylot/core/pick/charfuns.py
index c589bdea..e231995e 100644
--- a/pylot/core/pick/charfuns.py
+++ b/pylot/core/pick/charfuns.py
@@ -18,8 +18,8 @@ autoregressive prediction: application ot local and regional distances, Geophys.
"""
import numpy as np
-from scipy import signal
from obspy.core import Stream
+from scipy import signal
class CharacteristicFunction(object):
@@ -150,7 +150,7 @@ class CharacteristicFunction(object):
if self.cut[0] == 0 and self.cut[1] == 0:
start = 0
stop = len(self.orig_data[0])
- elif self.cut[0] == 0 and self.cut[1] is not 0:
+ elif self.cut[0] == 0 and self.cut[1] != 0:
start = 0
stop = self.cut[1] / self.dt
else:
@@ -159,7 +159,7 @@ class CharacteristicFunction(object):
zz = self.orig_data.copy()
z1 = zz[0].copy()
zz[0].data = z1.data[int(start):int(stop)]
- if zz[0].stats.npts == 0: # cut times do not fit data length!
+ if zz[0].stats.npts == 0: # cut times do not fit data length!
zz[0].data = z1.data # take entire data
data = zz
return data
@@ -167,7 +167,7 @@ class CharacteristicFunction(object):
if self.cut[0] == 0 and self.cut[1] == 0:
start = 0
stop = min([len(self.orig_data[0]), len(self.orig_data[1])])
- elif self.cut[0] == 0 and self.cut[1] is not 0:
+ elif self.cut[0] == 0 and self.cut[1] != 0:
start = 0
stop = min([self.cut[1] / self.dt, len(self.orig_data[0]),
len(self.orig_data[1])])
@@ -187,7 +187,7 @@ class CharacteristicFunction(object):
start = 0
stop = min([self.cut[1] / self.dt, len(self.orig_data[0]),
len(self.orig_data[1]), len(self.orig_data[2])])
- elif self.cut[0] == 0 and self.cut[1] is not 0:
+ elif self.cut[0] == 0 and self.cut[1] != 0:
start = 0
stop = self.cut[1] / self.dt
else:
@@ -241,7 +241,7 @@ class AICcf(CharacteristicFunction):
ff = np.where(inf is True)
if len(ff) >= 1:
cf[ff] = 0
-
+
self.cf = cf - np.mean(cf)
self.xcf = x
@@ -305,7 +305,7 @@ class HOScf(CharacteristicFunction):
if ind.size:
first = ind[0]
LTA[:first] = LTA[first]
-
+
self.cf = LTA
self.xcf = x
@@ -313,7 +313,8 @@ class HOScf(CharacteristicFunction):
class ARZcf(CharacteristicFunction):
def __init__(self, data, cut, t1, t2, pickparams):
- super(ARZcf, self).__init__(data, cut, t1=t1, t2=t2, order=pickparams["Parorder"], fnoise=pickparams["addnoise"])
+ super(ARZcf, self).__init__(data, cut, t1=t1, t2=t2, order=pickparams["Parorder"],
+ fnoise=pickparams["addnoise"])
def calcCF(self, data):
"""
@@ -448,7 +449,8 @@ class ARZcf(CharacteristicFunction):
class ARHcf(CharacteristicFunction):
def __init__(self, data, cut, t1, t2, pickparams):
- super(ARHcf, self).__init__(data, cut, t1=t1, t2=t2, order=pickparams["Sarorder"], fnoise=pickparams["addnoise"])
+ super(ARHcf, self).__init__(data, cut, t1=t1, t2=t2, order=pickparams["Sarorder"],
+ fnoise=pickparams["addnoise"])
def calcCF(self, data):
"""
@@ -600,7 +602,8 @@ class ARHcf(CharacteristicFunction):
class AR3Ccf(CharacteristicFunction):
def __init__(self, data, cut, t1, t2, pickparams):
- super(AR3Ccf, self).__init__(data, cut, t1=t1, t2=t2, order=pickparams["Sarorder"], fnoise=pickparams["addnoise"])
+ super(AR3Ccf, self).__init__(data, cut, t1=t1, t2=t2, order=pickparams["Sarorder"],
+ fnoise=pickparams["addnoise"])
def calcCF(self, data):
"""
diff --git a/pylot/core/pick/compare.py b/pylot/core/pick/compare.py
index 37ffd60e..7091ae5d 100644
--- a/pylot/core/pick/compare.py
+++ b/pylot/core/pick/compare.py
@@ -2,10 +2,11 @@
# -*- coding: utf-8 -*-
import copy
-import matplotlib.pyplot as plt
-import numpy as np
import operator
import os
+
+import matplotlib.pyplot as plt
+import numpy as np
from obspy.core import AttribDict
from pylot.core.util.pdf import ProbabilityDensityFunction
@@ -117,7 +118,7 @@ class Comparison(object):
pdf_a = self.get('auto').generate_pdf_data(type)
pdf_b = self.get('manu').generate_pdf_data(type)
-
+
for station, phases in pdf_a.items():
if station in pdf_b.keys():
compare_pdf = dict()
@@ -401,6 +402,8 @@ class PDFstatistics(object):
Takes a path as argument.
"""
+ # TODO: change root to datapath
+
def __init__(self, directory):
"""Initiates some values needed when dealing with pdfs later"""
self._rootdir = directory
diff --git a/pylot/core/pick/picker.py b/pylot/core/pick/picker.py
index 77d9faad..95ae1ad9 100644
--- a/pylot/core/pick/picker.py
+++ b/pylot/core/pick/picker.py
@@ -19,9 +19,10 @@ calculated after Diehl & Kissling (2009).
:author: MAGS2 EP3 working group / Ludger Kueperkoch
"""
+import warnings
+
import matplotlib.pyplot as plt
import numpy as np
-import warnings
from scipy.signal import argrelmax, argrelmin
from pylot.core.pick.charfuns import CharacteristicFunction
@@ -476,7 +477,7 @@ class PragPicker(AutoPicker):
cfpick_r = 0
cfpick_l = 0
lpickwindow = int(round(self.PickWindow / self.dt))
- #for i in range(max(np.insert(ipick, 0, 2)), min([ipick1 + lpickwindow + 1, len(self.cf) - 1])):
+ # for i in range(max(np.insert(ipick, 0, 2)), min([ipick1 + lpickwindow + 1, len(self.cf) - 1])):
# # local minimum
# if self.cf[i + 1] > self.cf[i] <= self.cf[i - 1]:
# if cfsmooth[i - 1] * (1 + aus1) >= cfsmooth[i]:
diff --git a/pylot/core/pick/utils.py b/pylot/core/pick/utils.py
index 6591b5f6..467728f5 100644
--- a/pylot/core/pick/utils.py
+++ b/pylot/core/pick/utils.py
@@ -9,10 +9,11 @@
"""
import warnings
+
import matplotlib.pyplot as plt
import numpy as np
-from scipy.signal import argrelmax
from obspy.core import Stream, UTCDateTime
+from scipy.signal import argrelmax
from pylot.core.util.utils import get_Bool, get_None, SetChannelComponents
@@ -73,7 +74,7 @@ def earllatepicker(X, nfac, TSNR, Pick1, iplot=0, verbosity=1, fig=None, linecol
x = X[0].data
t = np.linspace(0, X[0].stats.endtime - X[0].stats.starttime,
- X[0].stats.npts)
+ X[0].stats.npts)
inoise = getnoisewin(t, Pick1, TSNR[0], TSNR[1])
# get signal window
isignal = getsignalwin(t, Pick1, TSNR[2])
@@ -218,7 +219,7 @@ def fmpicker(Xraw, Xfilt, pickwin, Pick, iplot=0, fig=None, linecolor='k'):
xraw = Xraw[0].data
xfilt = Xfilt[0].data
t = np.linspace(0, Xraw[0].stats.endtime - Xraw[0].stats.starttime,
- Xraw[0].stats.npts)
+ Xraw[0].stats.npts)
# get pick window
ipick = np.where((t <= min([Pick + pickwin, len(Xraw[0])])) & (t >= Pick))
if len(ipick[0]) <= 1:
@@ -536,9 +537,10 @@ def getslopewin(Tcf, Pick, tslope):
:rtype: `numpy.ndarray`
"""
# TODO: fill out docstring
- slope = np.where( (Tcf <= min(Pick + tslope, Tcf[-1])) & (Tcf >= Pick) )
+ slope = np.where((Tcf <= min(Pick + tslope, Tcf[-1])) & (Tcf >= Pick))
return slope[0]
+
def getResolutionWindow(snr, extent):
"""
Produce the half of the time resolution window width from given SNR value
@@ -848,7 +850,7 @@ def checksignallength(X, pick, minsiglength, pickparams, iplot=0, fig=None, line
print("Presumably picked noise peak, pick is rejected!")
print("(min. signal length required: %s s)" % minsiglength)
returnflag = 0
- else:
+ else:
# calculate minimum adjusted signal level
minsiglevel = np.mean(rms[inoise]) * nfac
# minimum adjusted number of samples over minimum signal level
@@ -1207,7 +1209,7 @@ def checkZ4S(X, pick, pickparams, iplot, fig=None, linecolor='k'):
rms = rms_dict[key]
trace = traces_dict[key]
t = np.linspace(diff_dict[key], trace.stats.endtime - trace.stats.starttime + diff_dict[key],
- trace.stats.npts)
+ trace.stats.npts)
if i == 0:
if get_None(fig) is None:
fig = plt.figure() # self.iplot) ### WHY? MP MP
@@ -1318,6 +1320,7 @@ def get_quality_class(uncertainty, weight_classes):
:return: quality of pick (0-4)
:rtype: int
"""
+ if not uncertainty: return len(weight_classes)
try:
# create generator expression containing all indices of values in weight classes that are >= than uncertainty.
# call next on it once to receive first value
@@ -1328,6 +1331,7 @@ def get_quality_class(uncertainty, weight_classes):
quality = len(weight_classes)
return quality
+
def set_NaNs_to(data, nan_value):
"""
Replace all NaNs in data with nan_value
@@ -1343,6 +1347,7 @@ def set_NaNs_to(data, nan_value):
data[nn] = nan_value
return data
+
def taper_cf(cf):
"""
Taper cf data to get rid off of side maximas
@@ -1354,6 +1359,7 @@ def taper_cf(cf):
tap = np.hanning(len(cf))
return tap * cf
+
def cf_positive(cf):
"""
Shifts cf so that all values are positive
@@ -1364,6 +1370,7 @@ def cf_positive(cf):
"""
return cf + max(abs(cf))
+
def smooth_cf(cf, t_smooth, delta):
"""
Smooth cf by taking samples over t_smooth length
@@ -1392,6 +1399,7 @@ def smooth_cf(cf, t_smooth, delta):
cf_smooth -= offset # remove offset from smoothed function
return cf_smooth
+
def check_counts_ms(data):
"""
check if data is in counts or m/s
@@ -1451,9 +1459,9 @@ def calcSlope(Data, datasmooth, Tcf, Pick, TSNR):
if imax == 0:
print("AICPicker: Maximum for slope determination right at the beginning of the window!")
print("Choose longer slope determination window!")
- raise IndexError
+ raise IndexError
iislope = islope[0][0:imax + 1] # cut index so it contains only the first maximum
- dataslope = Data[0].data[iislope] # slope will only be calculated to the first maximum
+ dataslope = Data[0].data[iislope] # slope will only be calculated to the first maximum
# calculate slope as polynomal fit of order 1
xslope = np.arange(0, len(dataslope))
P = np.polyfit(xslope, dataslope, 1)
@@ -1474,8 +1482,10 @@ def get_pickparams(pickparam):
:rtype: (dict, dict, dict, dict)
"""
# Define names of all parameters in different groups
- p_parameter_names = 'algoP pstart pstop use_taup taup_model tlta tsnrz hosorder bpz1 bpz2 pickwinP aictsmooth tsmoothP ausP nfacP tpred1z tdet1z Parorder addnoise Precalcwin minAICPslope minAICPSNR timeerrorsP checkwindowP minfactorP'.split(' ')
- s_parameter_names = 'algoS sstart sstop bph1 bph2 tsnrh pickwinS tpred1h tdet1h tpred2h tdet2h Sarorder aictsmoothS tsmoothS ausS minAICSslope minAICSSNR Srecalcwin nfacS timeerrorsS zfac checkwindowS minfactorS'.split(' ')
+ p_parameter_names = 'algoP pstart pstop use_taup taup_model tlta tsnrz hosorder bpz1 bpz2 pickwinP aictsmooth tsmoothP ausP nfacP tpred1z tdet1z Parorder addnoise Precalcwin minAICPslope minAICPSNR timeerrorsP checkwindowP minfactorP'.split(
+ ' ')
+ s_parameter_names = 'algoS sstart sstop bph1 bph2 tsnrh pickwinS tpred1h tdet1h tpred2h tdet2h Sarorder aictsmoothS tsmoothS ausS minAICSslope minAICSSNR Srecalcwin nfacS timeerrorsS zfac checkwindowS minfactorS'.split(
+ ' ')
first_motion_names = 'minFMSNR fmpickwin minfmweight'.split(' ')
signal_length_names = 'minsiglength minpercent noisefactor'.split(' ')
# Get list of values from pickparam by name
@@ -1493,6 +1503,7 @@ def get_pickparams(pickparam):
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
@@ -1516,6 +1527,7 @@ def getQualityFromUncertainty(uncertainty, Errors):
return quality
+
if __name__ == '__main__':
import doctest
diff --git a/pylot/core/util/array_map.py b/pylot/core/util/array_map.py
index b2f3b078..f6173053 100644
--- a/pylot/core/util/array_map.py
+++ b/pylot/core/util/array_map.py
@@ -1,38 +1,51 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
-import os
+import traceback
+
+import cartopy.crs as ccrs
+import cartopy.feature as cf
+import matplotlib
+import matplotlib.patheffects as PathEffects
import matplotlib.pyplot as plt
import numpy as np
import obspy
-import traceback
-from PySide import QtGui
-from matplotlib.figure import Figure
+from PySide2 import QtWidgets
+from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from mpl_toolkits.axes_grid1.inset_locator import inset_axes
-from mpl_toolkits.basemap import Basemap
from scipy.interpolate import griddata
-from pylot.core.util.widgets import PickDlg, PylotCanvas
from pylot.core.pick.utils import get_quality_class
+from pylot.core.util.widgets import PickDlg
-plt.interactive(False)
+matplotlib.use('Qt5Agg')
-class Array_map(QtGui.QWidget):
- def __init__(self, parent, metadata, parameter=None, figure=None, annotate=True, pointsize=25.,
+class MplCanvas(FigureCanvas):
+
+ def __init__(self, parent=None, extern_axes=None, width=5, height=4, dpi=100):
+ if extern_axes is None:
+ self.fig = plt.figure(figsize=(width, height), dpi=dpi)
+ self.axes = self.fig.add_subplot(111)
+ else:
+ self.fig = extern_axes.figure
+ self.axes = extern_axes
+
+ super(MplCanvas, self).__init__(self.fig)
+
+
+class Array_map(QtWidgets.QWidget):
+ def __init__(self, parent, metadata, parameter=None, axes=None, annotate=True, pointsize=25.,
linewidth=1.5, width=5e6, height=2e6):
- '''
- Create a map of the array.
- :param parent: object of PyLoT Mainwindow class
- :param parameter: object of PyLoT parameter class
- :param figure:
- '''
- QtGui.QWidget.__init__(self)
- assert (parameter != None or parent != None), 'either parent or parameter has to be set'
+ QtWidgets.QWidget.__init__(self, parent=parent)
+
+ assert (parameter is not None or parent is not None), 'either parent or parameter has to be set'
+ # set properties
self._parent = parent
self.metadata = metadata
self.pointsize = pointsize
self.linewidth = linewidth
+ self.extern_plot_axes = axes
self.width = width
self.height = height
self.annotate = annotate
@@ -43,34 +56,228 @@ class Array_map(QtGui.QWidget):
self.hybrids_dict = None
self.eventLoc = None
self.parameter = parameter if parameter else parent._inputs
- self.figure = figure
+
self.picks_rel = {}
self.marked_stations = []
self.highlighted_stations = []
+
+ # call functions to draw everything
self.init_graphics()
self.init_stations()
- self.init_basemap(resolution='l')
+ self.init_crtpyMap()
self.init_map()
- self._style = None if not hasattr(parent, '_style') else parent._style
- # self.show()
+ # set original map limits to fall back on when home button is pressed
+ self.org_xlim = self.canvas.axes.get_xlim()
+ self.org_ylim = self.canvas.axes.get_ylim()
+ # initial map without event
+ self.canvas.axes.set_xlim(self.org_xlim[0], self.org_xlim[1])
+ self.canvas.axes.set_ylim(self.org_ylim[0], self.org_ylim[1])
- def update_hybrids_dict(self):
- self.hybrids_dict = self.picks_dict.copy()
- for station, pick in self.autopicks_dict.items():
- if not station in self.hybrids_dict.keys():
- self.hybrids_dict[station] = pick
- return self.hybrids_dict
+ self._style = None if not hasattr(parent, '_style') else parent._style
+
+ self.show()
def init_map(self):
self.init_colormap()
self.connectSignals()
self.draw_everything()
- self.canvas.setZoomBorders2content()
+
+ def init_graphics(self):
+ """
+ Initializes all GUI components and figure elements to be populated by other functions
+ """
+ # initialize figure elements
+
+ if self.extern_plot_axes is None:
+ self.canvas = MplCanvas(self)
+ self.plotWidget = FigureCanvas(self.canvas.fig)
+ else:
+ self.canvas = MplCanvas(self, extern_axes=self.extern_plot_axes)
+ self.plotWidget = FigureCanvas(self.canvas.fig)
+
+ # initialize GUI elements
+ self.status_label = QtWidgets.QLabel()
+ self.map_reset_button = QtWidgets.QPushButton('Reset Map View')
+ self.save_map_button = QtWidgets.QPushButton('Save Map')
+ self.go2eq_button = QtWidgets.QPushButton('Go to Event Location')
+
+ self.main_box = QtWidgets.QVBoxLayout()
+ self.setLayout(self.main_box)
+
+ self.top_row = QtWidgets.QHBoxLayout()
+ self.main_box.addLayout(self.top_row, 1)
+
+ self.comboBox_phase = QtWidgets.QComboBox()
+ self.comboBox_phase.insertItem(0, 'P')
+ self.comboBox_phase.insertItem(1, 'S')
+
+ self.comboBox_am = QtWidgets.QComboBox()
+ self.comboBox_am.insertItem(0, 'hybrid (prefer manual)')
+ self.comboBox_am.insertItem(1, 'manual')
+ self.comboBox_am.insertItem(2, 'auto')
+
+ self.annotations_box = QtWidgets.QCheckBox('Annotate')
+ self.annotations_box.setChecked(True)
+ self.auto_refresh_box = QtWidgets.QCheckBox('Automatic refresh')
+ self.auto_refresh_box.setChecked(True)
+ self.refresh_button = QtWidgets.QPushButton('Refresh')
+ self.cmaps_box = QtWidgets.QComboBox()
+ self.cmaps_box.setMaxVisibleItems(20)
+ [self.cmaps_box.addItem(map_name) for map_name in sorted(plt.colormaps())]
+ # try to set to hsv as default
+ self.cmaps_box.setCurrentIndex(self.cmaps_box.findText('hsv'))
+
+ self.top_row.addWidget(QtWidgets.QLabel('Select a phase: '))
+ self.top_row.addWidget(self.comboBox_phase)
+ self.top_row.setStretch(1, 1) # set stretch of item 1 to 1
+ self.top_row.addWidget(QtWidgets.QLabel('Pick type: '))
+ self.top_row.addWidget(self.comboBox_am)
+ self.top_row.setStretch(3, 1) # set stretch of item 1 to 1
+ self.top_row.addWidget(self.cmaps_box)
+ self.top_row.addWidget(self.annotations_box)
+ self.top_row.addWidget(self.auto_refresh_box)
+ self.top_row.addWidget(self.refresh_button)
+
+ self.main_box.addWidget(self.plotWidget, 1)
+
+ self.bot_row = QtWidgets.QHBoxLayout()
+ self.main_box.addLayout(self.bot_row, 0.3)
+ self.bot_row.addWidget(QtWidgets.QLabel(''), 5)
+ self.bot_row.addWidget(self.map_reset_button, 2)
+ self.bot_row.addWidget(self.go2eq_button, 2)
+ self.bot_row.addWidget(self.save_map_button, 2)
+ self.bot_row.addWidget(self.status_label, 5)
def init_colormap(self):
self.init_lat_lon_dimensions()
self.init_lat_lon_grid()
- self.init_x_y_dimensions()
+
+ def init_crtpyMap(self):
+ self.canvas.axes.cla()
+ self.canvas.axes = plt.axes(projection=ccrs.PlateCarree())
+ self.canvas.axes.add_feature(cf.LAND)
+ self.canvas.axes.add_feature(cf.OCEAN)
+ self.canvas.axes.add_feature(cf.COASTLINE, linewidth=1, edgecolor='gray')
+ self.canvas.axes.add_feature(cf.BORDERS, alpha=0.7)
+ self.canvas.axes.add_feature(cf.LAKES, alpha=0.7)
+ self.canvas.axes.add_feature(cf.RIVERS, linewidth=1)
+
+ # parallels and meridians
+ self.add_merid_paral()
+
+ self.canvas.fig.tight_layout()
+
+ def add_merid_paral(self):
+ self.gridlines = self.canvas.axes.gridlines(draw_labels=False, alpha=0.6, color='gray',
+ linewidth=self.linewidth / 2, zorder=7)
+ # TODO: current cartopy version does not support label removal. Devs are working on it.
+ # Should be fixed in coming cartopy versions
+ # self.gridlines.xformatter = LONGITUDE_FORMATTER
+ # self.gridlines.yformatter = LATITUDE_FORMATTER
+
+ def remove_merid_paral(self):
+ if len(self.gridlines.xline_artists):
+ self.gridlines.xline_artists[0].remove()
+ self.gridlines.yline_artists[0].remove()
+
+ def org_map_view(self):
+ self.canvas.axes.set_xlim(self.org_xlim[0], self.org_xlim[1])
+ self.canvas.axes.set_ylim(self.org_ylim[0], self.org_ylim[1])
+ # parallels and meridians
+ self.remove_merid_paral()
+ self.add_merid_paral()
+
+ self.canvas.axes.figure.canvas.draw_idle()
+
+ def go2eq(self):
+ if self.eventLoc:
+ lats, lons = self.eventLoc
+ self.canvas.axes.set_xlim(lons - 10, lons + 10)
+ self.canvas.axes.set_ylim(lats - 5, lats + 5)
+ # parallels and meridians
+ self.remove_merid_paral()
+ self.add_merid_paral()
+
+ self.canvas.axes.figure.canvas.draw_idle()
+
+ else:
+ self.status_label.setText('No event information available')
+
+ def connectSignals(self):
+ self.comboBox_phase.currentIndexChanged.connect(self._refresh_drawings)
+ self.comboBox_am.currentIndexChanged.connect(self._refresh_drawings)
+ self.cmaps_box.currentIndexChanged.connect(self._refresh_drawings)
+ self.annotations_box.stateChanged.connect(self.switch_annotations)
+ self.refresh_button.clicked.connect(self._refresh_drawings)
+ self.map_reset_button.clicked.connect(self.org_map_view)
+ self.go2eq_button.clicked.connect(self.go2eq)
+ self.save_map_button.clicked.connect(self.saveFigure)
+
+ self.plotWidget.mpl_connect('motion_notify_event', self.mouse_moved)
+ self.plotWidget.mpl_connect('scroll_event', self.mouse_scroll)
+ self.plotWidget.mpl_connect('button_press_event', self.mouseLeftPress)
+ self.plotWidget.mpl_connect('button_release_event', self.mouseLeftRelease)
+
+ # set mouse events -----------------------------------------------------
+ def mouse_moved(self, event):
+ if not event.inaxes == self.canvas.axes:
+ return
+ lat = event.ydata
+ lon = event.xdata
+ self.status_label.setText('Latitude: {:3.5f}, Longitude: {:3.5f}'.format(lat, lon))
+
+ def mouse_scroll(self, event):
+ if not event.inaxes == self.canvas.axes:
+ return
+
+ zoom = {'up': 1. / 2., 'down': 2.}
+
+ if event.button in zoom:
+ xlim = self.canvas.axes.get_xlim()
+ ylim = self.canvas.axes.get_ylim()
+
+ x, y = event.xdata, event.ydata
+
+ factor = zoom[event.button]
+ xdiff = (xlim[1] - xlim[0]) * factor
+ xl = x - 0.5 * xdiff
+ xr = x + 0.5 * xdiff
+ ydiff = (ylim[1] - ylim[0]) * factor
+ yb = y - 0.5 * ydiff
+ yt = y + 0.5 * ydiff
+
+ self.canvas.axes.set_xlim(xl, xr)
+ self.canvas.axes.set_ylim(yb, yt)
+ # parallels and meridians
+ self.remove_merid_paral()
+ self.add_merid_paral()
+
+ self.canvas.axes.figure.canvas.draw_idle()
+
+ def mouseLeftPress(self, event):
+ if not event.inaxes == self.canvas.axes:
+ return
+ self.map_x = event.xdata
+ self.map_y = event.ydata
+ self.map_xlim = self.canvas.axes.get_xlim()
+ self.map_ylim = self.canvas.axes.get_ylim()
+
+ def mouseLeftRelease(self, event):
+ if not event.inaxes == self.canvas.axes:
+ return
+ new_x = event.xdata
+ new_y = event.ydata
+
+ dx = new_x - self.map_x
+ dy = new_y - self.map_y
+
+ self.canvas.axes.set_xlim((self.map_xlim[0] - dx, self.map_xlim[1] - dx))
+ self.canvas.axes.set_ylim(self.map_ylim[0] - dy, self.map_ylim[1] - dy)
+ # parallels and meridians
+ self.remove_merid_paral()
+ self.add_merid_paral()
+
+ self.canvas.axes.figure.canvas.draw_idle()
def onpick(self, event):
ind = event.ind
@@ -84,6 +291,14 @@ class Array_map(QtGui.QWidget):
elif button == 3:
self.pickInfo(ind)
+ # data handling -----------------------------------------------------
+ def update_hybrids_dict(self):
+ self.hybrids_dict = self.picks_dict.copy()
+ for station, pick in self.autopicks_dict.items():
+ if not station in self.hybrids_dict.keys():
+ self.hybrids_dict[station] = pick
+ return self.hybrids_dict
+
def deletePick(self, ind):
self.update_hybrids_dict()
for index in ind:
@@ -117,15 +332,6 @@ class Array_map(QtGui.QWidget):
except Exception as e:
print('Could not delete pick for station {}.{}: {}'.format(network, station, e))
- def highlight_station(self, network, station, color):
- stat_dict = self.stations_dict['{}.{}'.format(network, station)]
- lat = stat_dict['latitude']
- lon = stat_dict['longitude']
- self.highlighted_stations.append(self.basemap.scatter(lon, lat, s=self.pointsize, edgecolors=color,
- facecolors='none', latlon=True,
- zorder=12, label='deleted'))
- self.canvas.draw()
-
def pickInfo(self, ind):
self.update_hybrids_dict()
for index in ind:
@@ -139,57 +345,6 @@ class Array_map(QtGui.QWidget):
for key, info in picks.items():
print('{}: {}'.format(key, info))
- def openPickDlg(self, ind):
- data = self._parent.get_data().getWFData()
- for index in ind:
- network, station = self._station_onpick_ids[index].split('.')[:2]
- pyl_mw = self._parent
- try:
- data = data.select(station=station)
- if not data:
- self._warn('No data for station {}'.format(station))
- return
- pickDlg = PickDlg(self._parent, parameter=self.parameter,
- data=data, network=network, station=station,
- picks=self._parent.get_current_event().getPick(station),
- autopicks=self._parent.get_current_event().getAutopick(station),
- filteroptions=self._parent.filteroptions, metadata=self.metadata,
- event=pyl_mw.get_current_event())
- except Exception as e:
- message = 'Could not generate Plot for station {st}.\n {er}'.format(st=station, er=e)
- self._warn(message)
- print(message, e)
- print(traceback.format_exc())
- return
- try:
- if pickDlg.exec_():
- pyl_mw.setDirty(True)
- pyl_mw.update_status('picks accepted ({0})'.format(station))
- pyl_mw.addPicks(station, pickDlg.getPicks(picktype='manual'), type='manual')
- pyl_mw.addPicks(station, pickDlg.getPicks(picktype='auto'), type='auto')
- if self.auto_refresh_box.isChecked():
- self._refresh_drawings()
- else:
- self.highlight_station(network, station, color='yellow')
- pyl_mw.drawPicks(station)
- pyl_mw.draw()
- else:
- pyl_mw.update_status('picks discarded ({0})'.format(station))
- except Exception as e:
- message = 'Could not save picks for station {st}.\n{er}'.format(st=station, er=e)
- self._warn(message)
- print(message, e)
- print(traceback.format_exc())
-
- def connectSignals(self):
- self.comboBox_phase.currentIndexChanged.connect(self._refresh_drawings)
- self.comboBox_am.currentIndexChanged.connect(self._refresh_drawings)
- self.cmaps_box.currentIndexChanged.connect(self._refresh_drawings)
- self.annotations_box.stateChanged.connect(self.switch_annotations)
- self.refresh_button.clicked.connect(self._refresh_drawings)
- self.canvas.mpl_connect('motion_notify_event', self.mouse_moved)
- # self.zoom_id = self.basemap.ax.figure.canvas.mpl_connect('scroll_event', self.zoom)
-
def _from_dict(self, function, key):
return function(self.stations_dict.values(), key=lambda x: x[key])[key]
@@ -205,14 +360,6 @@ class Array_map(QtGui.QWidget):
def get_max_from_picks(self):
return max(self.picks_rel.values())
- def mouse_moved(self, event):
- if not event.inaxes == self.main_ax:
- return
- x = event.xdata
- y = event.ydata
- lat, lon = self.basemap(x, y, inverse=True)
- self.status_label.setText('Latitude: {}, Longitude: {}'.format(lat, lon))
-
def current_picks_dict(self):
picktype = self.comboBox_am.currentText().split(' ')[0]
auto_manu = {'auto': self.autopicks_dict,
@@ -220,57 +367,6 @@ class Array_map(QtGui.QWidget):
'hybrid': self.hybrids_dict}
return auto_manu[picktype]
- def init_graphics(self):
- if not self.figure:
- self.figure = Figure()
-
- self.status_label = QtGui.QLabel()
-
- self.main_ax = self.figure.add_subplot(111)
- #self.main_ax.set_facecolor('0.7')
- self.canvas = PylotCanvas(self.figure, parent=self._parent, multicursor=True,
- panZoomX=False, panZoomY=False)
-
- self.main_box = QtGui.QVBoxLayout()
- self.setLayout(self.main_box)
-
- self.top_row = QtGui.QHBoxLayout()
- self.main_box.addLayout(self.top_row, 1)
-
- self.comboBox_phase = QtGui.QComboBox()
- self.comboBox_phase.insertItem(0, 'P')
- self.comboBox_phase.insertItem(1, 'S')
-
- self.comboBox_am = QtGui.QComboBox()
- self.comboBox_am.insertItem(0, 'hybrid (prefer manual)')
- self.comboBox_am.insertItem(1, 'manual')
- self.comboBox_am.insertItem(2, 'auto')
-
- self.annotations_box = QtGui.QCheckBox('Annotate')
- self.annotations_box.setChecked(True)
- self.auto_refresh_box = QtGui.QCheckBox('Automatic refresh')
- self.auto_refresh_box.setChecked(True)
- self.refresh_button = QtGui.QPushButton('Refresh')
- self.cmaps_box = QtGui.QComboBox()
- self.cmaps_box.setMaxVisibleItems(20)
- [self.cmaps_box.addItem(map_name) for map_name in sorted(plt.colormaps())]
- # try to set to hsv as default
- self.cmaps_box.setCurrentIndex(self.cmaps_box.findText('hsv'))
-
- self.top_row.addWidget(QtGui.QLabel('Select a phase: '))
- self.top_row.addWidget(self.comboBox_phase)
- self.top_row.setStretch(1, 1) # set stretch of item 1 to 1
- self.top_row.addWidget(QtGui.QLabel('Pick type: '))
- self.top_row.addWidget(self.comboBox_am)
- self.top_row.setStretch(3, 1) # set stretch of item 1 to 1
- self.top_row.addWidget(self.cmaps_box)
- self.top_row.addWidget(self.annotations_box)
- self.top_row.addWidget(self.auto_refresh_box)
- self.top_row.addWidget(self.refresh_button)
-
- self.main_box.addWidget(self.canvas, 1)
- self.main_box.addWidget(self.status_label, 0)
-
def init_stations(self):
self.stations_dict = self.metadata.get_all_coordinates()
self.latmin = self.get_min_from_stations('latitude')
@@ -323,41 +419,6 @@ class Array_map(QtGui.QWidget):
self.londim = self.lonmax - self.lonmin
self.latdim = self.latmax - self.latmin
- def init_x_y_dimensions(self):
- # transformation of lat/lon to ax coordinate system
- for st_id, coords in self.stations_dict.items():
- lat, lon = coords['latitude'], coords['longitude']
- coords['x'], coords['y'] = self.basemap(lon, lat)
-
- self.xdim = self.get_max_from_stations('x') - self.get_min_from_stations('x')
- self.ydim = self.get_max_from_stations('y') - self.get_min_from_stations('y')
-
- def init_basemap(self, resolution='l'):
- # basemap = Basemap(projection=projection, resolution = resolution, ax=self.main_ax)
- basemap = Basemap(projection='lcc', resolution=resolution, ax=self.main_ax,
- width=self.width, height=self.height,
- lat_0=(self.latmin + self.latmax) / 2.,
- lon_0=(self.lonmin + self.lonmax) / 2.)
-
- # basemap.fillcontinents(color=None, lake_color='aqua',zorder=1)
- basemap.drawmapboundary(zorder=2) # fill_color='darkblue')
- basemap.shadedrelief(zorder=3)
- basemap.drawcountries(zorder=4)
- basemap.drawstates(zorder=5)
- basemap.drawcoastlines(zorder=6)
- # labels = [left,right,top,bottom]
- parallels = np.arange(-90, 90, 5.)
- parallels_small = np.arange(-90, 90, 2.5)
- basemap.drawparallels(parallels_small, linewidth=0.5, zorder=7)
- basemap.drawparallels(parallels, zorder=7)#, labels=[1, 1, 0, 0])
- meridians = np.arange(-180, 180, 5.)
- meridians_small = np.arange(-180, 180, 2.5)
- basemap.drawmeridians(meridians_small, linewidth=0.5, zorder=7)
- basemap.drawmeridians(meridians, zorder=7)#, labels=[0, 0, 1, 1])
- self.basemap = basemap
- self.figure._tight = True
- self.figure.tight_layout()
-
def init_lat_lon_grid(self, nstep=250):
# create a regular grid to display colormap
lataxis = np.linspace(self.latmin, self.latmax, nstep)
@@ -367,8 +428,7 @@ class Array_map(QtGui.QWidget):
def init_picksgrid(self):
picks, uncertainties, lats, lons = self.get_picks_lat_lon()
try:
- self.picksgrid_active = griddata((lats, lons), picks, (self.latgrid, self.longrid),
- method='linear')
+ self.picksgrid_active = griddata((lats, lons), picks, (self.latgrid, self.longrid), method='linear')
except Exception as e:
self._warn('Could not init picksgrid: {}'.format(e))
@@ -382,16 +442,6 @@ class Array_map(QtGui.QWidget):
longitudes.append(coords['longitude'])
return stations, latitudes, longitudes
- def get_st_x_y_for_plot(self):
- stations = []
- xs = []
- ys = []
- for st_id, coords in self.stations_dict.items():
- stations.append(st_id)
- xs.append(coords['x'])
- ys.append(coords['y'])
- return stations, xs, ys
-
def get_picks_lat_lon(self):
picks = []
uncertainties = []
@@ -404,65 +454,82 @@ class Array_map(QtGui.QWidget):
longitudes.append(self.stations_dict[st_id]['longitude'])
return picks, uncertainties, latitudes, longitudes
- def draw_contour_filled(self, nlevel='100'):
- # self.test_gradient()
+ # plotting -----------------------------------------------------
+ def highlight_station(self, network, station, color):
+ stat_dict = self.stations_dict['{}.{}'.format(network, station)]
+ lat = stat_dict['latitude']
+ lon = stat_dict['longitude']
+ self.highlighted_stations.append(self.canvas.axes.scatter(lon, lat, s=self.pointsize, edgecolors=color,
+ facecolors='none', zorder=12,
+ transform=ccrs.PlateCarree(), label='deleted'))
+ def openPickDlg(self, ind):
+ data = self._parent.get_data().getWFData()
+ for index in ind:
+ network, station = self._station_onpick_ids[index].split('.')[:2]
+ pyl_mw = self._parent
+ try:
+ data = data.select(station=station)
+ if not data:
+ self._warn('No data for station {}'.format(station))
+ return
+ pickDlg = PickDlg(self._parent, parameter=self.parameter,
+ data=data, network=network, station=station,
+ picks=self._parent.get_current_event().getPick(station),
+ autopicks=self._parent.get_current_event().getAutopick(station),
+ filteroptions=self._parent.filteroptions, metadata=self.metadata,
+ event=pyl_mw.get_current_event())
+ except Exception as e:
+ message = 'Could not generate Plot for station {st}.\n {er}'.format(st=station, er=e)
+ self._warn(message)
+ print(message, e)
+ print(traceback.format_exc())
+ return
+ try:
+ if pickDlg.exec_():
+ pyl_mw.setDirty(True)
+ pyl_mw.update_status('picks accepted ({0})'.format(station))
+ pyl_mw.addPicks(station, pickDlg.getPicks(picktype='manual'), type='manual')
+ pyl_mw.addPicks(station, pickDlg.getPicks(picktype='auto'), type='auto')
+ if self.auto_refresh_box.isChecked():
+ self._refresh_drawings()
+ else:
+ self.highlight_station(network, station, color='yellow')
+ pyl_mw.drawPicks(station)
+ pyl_mw.draw()
+ else:
+ pyl_mw.update_status('picks discarded ({0})'.format(station))
+ except Exception as e:
+ message = 'Could not save picks for station {st}.\n{er}'.format(st=station, er=e)
+ self._warn(message)
+ print(message, e)
+ print(traceback.format_exc())
+
+ def draw_contour_filled(self, nlevel=50):
levels = np.linspace(self.get_min_from_picks(), self.get_max_from_picks(), nlevel)
- self.contourf = self.basemap.contour(self.longrid, self.latgrid, self.picksgrid_active, levels,
- linewidths=self.linewidth, latlon=True, zorder=8, alpha=0.7,
- cmap=self.get_colormap())
+
+ self.contourf = self.canvas.axes.contourf(self.longrid, self.latgrid, self.picksgrid_active, levels,
+ linewidths=self.linewidth * 5, transform=ccrs.PlateCarree(),
+ alpha=0.4, zorder=8, cmap=self.get_colormap())
def get_colormap(self):
return plt.get_cmap(self.cmaps_box.currentText())
- def test_gradient(self):
- st_ids = self.picks_rel.keys()
- x, y = np.gradient(self.picksgrid_active)
- gradient_modulus = np.sqrt(x ** 2 + y ** 2)
- global_mean_gradient = np.nanmean(gradient_modulus)
- delta_gradient = []
- for st_id in st_ids:
- pick_item = self.picks_rel.pop(st_id)
- self.init_picksgrid()
- x, y = np.gradient(self.picksgrid_active)
- gradient_modulus = np.sqrt(x ** 2 + y ** 2)
- mean_gradient = np.nanmean(gradient_modulus)
- dgradient = global_mean_gradient - mean_gradient
- # print('station: {}, mean gradient: {}'.format(st_id, dgradient))
- delta_gradient.append(dgradient)
- self.picks_rel[st_id] = pick_item
- global_std_gradient = np.nanstd(delta_gradient)
- marked_stations = []
- for st_id, dg in zip(st_ids, delta_gradient):
- if abs(dg) > global_std_gradient:
- marked_stations.append(st_id)
- self.marked_stations = marked_stations
- self.init_picksgrid()
-
- # fig = plt.figure()
- # x = list(range(len(st_ids)))
- # gradients = zip(x, delta_gradient)
- # gradients.sort(key=lambda a: a[1])
- # plt.plot(gradients[0], gradients[1])
-
- # global_var_gradient = np.nanvar(delta_gradient)
- # plt.plot(x, delta_gradient)
- # plt.axhline(global_std_gradient, color='green')
- # plt.axhline(2 * global_std_gradient, color='blue')
- # plt.axhline(global_var_gradient, color='red')
- # plt.xticks(x, st_ids)
- # plt.show()
-
def scatter_all_stations(self):
stations, lats, lons = self.get_st_lat_lon_for_plot()
- self.sc = self.basemap.scatter(lons, lats, s=self.pointsize, facecolor='none', latlon=True, marker='.',
- zorder=10, picker=True, edgecolor='0.5', label='Not Picked')
- self.cid = self.canvas.mpl_connect('pick_event', self.onpick)
+
+ self.sc = self.canvas.axes.scatter(lons, lats, s=self.pointsize * 3, facecolor='none', marker='.',
+ zorder=10, picker=True, edgecolor='0.5', label='Not Picked',
+ transform=ccrs.PlateCarree())
+
+ self.cid = self.plotWidget.mpl_connect('pick_event', self.onpick)
self._station_onpick_ids = stations
if self.eventLoc:
lats, lons = self.eventLoc
- self.sc_event = self.basemap.scatter(lons, lats, s=2*self.pointsize, facecolor='red',
- latlon=True, zorder=11, label='Event (might be outside map region)')
+ self.sc_event = self.canvas.axes.scatter(lons, lats, s=5 * self.pointsize, facecolor='red', zorder=11,
+ label='Event (might be outside map region)', marker='*',
+ edgecolors='black',
+ transform=ccrs.PlateCarree())
def scatter_picked_stations(self):
picks, uncertainties, lats, lons = self.get_picks_lat_lon()
@@ -471,24 +538,16 @@ class Array_map(QtGui.QWidget):
phase = self.comboBox_phase.currentText()
timeerrors = self.parameter['timeerrors{}'.format(phase)]
- sizes = np.array([self.pointsize * ((5. - get_quality_class(uncertainty, timeerrors)))
+ sizes = np.array([self.pointsize * (5. - get_quality_class(uncertainty, timeerrors))
for uncertainty in uncertainties])
cmap = self.get_colormap()
- # workaround because of an issue with latlon transformation of arrays with len <3
- if len(lons) <= 2 and len(lats) <= 2:
- self.sc_picked = self.basemap.scatter(lons[0], lats[0], s=sizes, edgecolors='white', cmap=cmap,
- c=picks[0], latlon=True, zorder=11)
- if len(lons) == 2 and len(lats) == 2:
- self.sc_picked = self.basemap.scatter(lons[1], lats[1], s=sizes, edgecolors='white', cmap=cmap,
- c=picks[1], latlon=True, zorder=11)
- if len(lons) > 2 and len(lats) > 2:
- self.sc_picked = self.basemap.scatter(lons, lats, s=sizes, edgecolors='white', cmap=cmap,
- c=picks, latlon=True, zorder=11, label='Picked')
+ self.sc_picked = self.canvas.axes.scatter(lons, lats, s=sizes, edgecolors='white', cmap=cmap,
+ c=picks, zorder=11, label='Picked', transform=ccrs.PlateCarree())
def annotate_ax(self):
self.annotations = []
- stations, xs, ys = self.get_st_x_y_for_plot()
+ stations, ys, xs = self.get_st_lat_lon_for_plot()
# MP MP testing station highlighting if they have high impact on mean gradient of color map
# if self.picks_rel:
# self.test_gradient()
@@ -501,16 +560,21 @@ class Array_map(QtGui.QWidget):
color = 'lightgrey'
if st in self.marked_stations:
color = 'red'
- self.annotations.append(self.main_ax.annotate(' %s' % st, xy=(x, y),
- fontsize=self.pointsize/4., fontweight='semibold',
- color=color, zorder=14))
- self.legend = self.main_ax.legend(loc=1)
- self.legend.get_frame().set_facecolor((1, 1, 1, 0.75))
+ self.annotations.append(
+ self.canvas.axes.annotate(' %s' % st, xy=(x + 0.003, y + 0.003), fontsize=self.pointsize / 4.,
+ fontweight='semibold', color=color, alpha=0.8,
+ transform=ccrs.PlateCarree(), zorder=14,
+ path_effects=[PathEffects.withStroke(
+ linewidth=self.pointsize / 15., foreground='k')]))
+
+ self.legend = self.canvas.axes.legend(loc=1, framealpha=1)
+ self.legend.set_zorder(100)
+ self.legend.get_frame().set_facecolor((1, 1, 1, 0.95))
def add_cbar(self, label):
- self.cbax_bg = inset_axes(self.main_ax, width="6%", height="75%", loc=5)
- cbax = inset_axes(self.main_ax, width='2%', height='70%', loc=5)
- cbar = self.main_ax.figure.colorbar(self.sc_picked, cax=cbax)
+ self.cbax_bg = inset_axes(self.canvas.axes, width="6%", height="75%", loc=5)
+ cbax = inset_axes(self.canvas.axes, width='2%', height='70%', loc=5)
+ cbar = self.canvas.axes.figure.colorbar(self.sc_picked, cax=cbax)
cbar.set_label(label)
cbax.yaxis.tick_left()
cbax.yaxis.set_label_position('left')
@@ -521,6 +585,7 @@ class Array_map(QtGui.QWidget):
self.cbax_bg.patch.set_facecolor((1, 1, 1, 0.75))
return cbar
+ # handle drawings -----------------------------------------------------
def refresh_drawings(self, picks=None, autopicks=None):
self.picks_dict = picks
self.autopicks_dict = autopicks
@@ -560,7 +625,7 @@ class Array_map(QtGui.QWidget):
self.comboBox_phase.setEnabled(False)
if self.annotate:
self.annotate_ax()
- self.canvas.draw()
+ self.plotWidget.draw_idle()
def remove_drawings(self):
self.remove_annotations()
@@ -584,7 +649,7 @@ class Array_map(QtGui.QWidget):
self.remove_contourf()
del self.contourf
if hasattr(self, 'cid'):
- self.canvas.mpl_disconnect(self.cid)
+ self.plotWidget.mpl_disconnect(self.cid)
del self.cid
try:
self.sc.remove()
@@ -594,7 +659,7 @@ class Array_map(QtGui.QWidget):
self.legend.remove()
except Exception as e:
print('Warning: could not remove legend. Reason: {}'.format(e))
- self.canvas.draw()
+ self.plotWidget.draw_idle()
def remove_contourf(self):
for item in self.contourf.collections:
@@ -605,34 +670,16 @@ class Array_map(QtGui.QWidget):
annotation.remove()
self.annotations = []
- def zoom(self, event):
- map = self.basemap
- xlim = map.ax.get_xlim()
- ylim = map.ax.get_ylim()
- x, y = event.xdata, event.ydata
- zoom = {'up': 1. / 2.,
- 'down': 2.}
-
- if not event.xdata or not event.ydata:
- return
-
- if event.button in zoom:
- factor = zoom[event.button]
- xdiff = (xlim[1] - xlim[0]) * factor
- xl = x - 0.5 * xdiff
- xr = x + 0.5 * xdiff
- ydiff = (ylim[1] - ylim[0]) * factor
- yb = y - 0.5 * ydiff
- yt = y + 0.5 * ydiff
-
- if xl < map.xmin or yb < map.ymin or xr > map.xmax or yt > map.ymax:
- xl, xr = map.xmin, map.xmax
- yb, yt = map.ymin, map.ymax
- map.ax.set_xlim(xl, xr)
- map.ax.set_ylim(yb, yt)
- map.ax.figure.canvas.draw()
+ def saveFigure(self):
+ if self.canvas.fig:
+ fd = QtWidgets.QFileDialog()
+ fname, filter = fd.getSaveFileName(self.parent(), filter='Images (*.png *.svg *.jpg)')
+ if not fname:
+ return
+ if not any([fname.endswith(item) for item in ['.png', '.svg', '.jpg']]):
+ fname += '.png'
+ self.canvas.fig.savefig(fname)
def _warn(self, message):
- self.qmb = QtGui.QMessageBox(QtGui.QMessageBox.Icon.Warning,
- 'Warning', message)
+ self.qmb = QtWidgets.QMessageBox(QtWidgets.QMessageBox.Icon.Warning, 'Warning', message)
self.qmb.show()
diff --git a/pylot/core/util/connection.py b/pylot/core/util/connection.py
index 4a6e11f0..193d17b7 100644
--- a/pylot/core/util/connection.py
+++ b/pylot/core/util/connection.py
@@ -2,12 +2,13 @@
# -*- coding: utf-8 -*-
try:
+ # noinspection PyUnresolvedReferences
from urllib2 import urlopen
except:
from urllib.request import urlopen
-def checkurl(url='https://ariadne.geophysik.ruhr-uni-bochum.de/trac/PyLoT/'):
+def checkurl(url='https://git.geophysik.ruhr-uni-bochum.de/marcel/pylot/'):
"""
check if URL is available
:param url: url
diff --git a/pylot/core/util/dataprocessing.py b/pylot/core/util/dataprocessing.py
index 4ed96265..8ea9d8d8 100644
--- a/pylot/core/util/dataprocessing.py
+++ b/pylot/core/util/dataprocessing.py
@@ -2,9 +2,10 @@
# -*- coding: utf-8 -*-
import glob
-import numpy as np
import os
import sys
+
+import numpy as np
from obspy import UTCDateTime, read_inventory, read
from obspy.io.xseed import Parser
@@ -46,7 +47,7 @@ class Metadata(object):
def __repr__(self):
return self.__str__()
- def add_inventory(self, path_to_inventory, obspy_dmt_inv = False):
+ def add_inventory(self, path_to_inventory, obspy_dmt_inv=False):
"""
Add path to list of inventories.
:param path_to_inventory: Path to a folder
@@ -83,10 +84,10 @@ class Metadata(object):
print('Path {} not in inventories list.'.format(path_to_inventory))
return
self.inventories.remove(path_to_inventory)
- for filename in self.inventory_files.keys():
+ for filename in list(self.inventory_files.keys()):
if filename.startswith(path_to_inventory):
del (self.inventory_files[filename])
- for seed_id in self.seed_ids.keys():
+ for seed_id in list(self.seed_ids.keys()):
if self.seed_ids[seed_id].startswith(path_to_inventory):
del (self.seed_ids[seed_id])
# have to clean self.stations_dict as well
@@ -211,6 +212,7 @@ class Metadata(object):
self.stations_dict[st_id] = {'latitude': station[0].latitude,
'longitude': station[0].longitude,
'elevation': station[0].elevation}
+
read_stat = {'xml': stat_info_from_inventory,
'dless': stat_info_from_parser}
@@ -351,25 +353,25 @@ def check_time(datetime):
:type datetime: list
:return: returns True if Values are in supposed range, returns False otherwise
- >>> check_time([1999, 01, 01, 23, 59, 59, 999000])
+ >>> check_time([1999, 1, 1, 23, 59, 59, 999000])
True
- >>> check_time([1999, 01, 01, 23, 59, 60, 999000])
+ >>> check_time([1999, 1, 1, 23, 59, 60, 999000])
False
- >>> check_time([1999, 01, 01, 23, 59, 59, 1000000])
+ >>> check_time([1999, 1, 1, 23, 59, 59, 1000000])
False
- >>> check_time([1999, 01, 01, 23, 60, 59, 999000])
+ >>> check_time([1999, 1, 1, 23, 60, 59, 999000])
False
- >>> check_time([1999, 01, 01, 23, 60, 59, 999000])
+ >>> check_time([1999, 1, 1, 23, 60, 59, 999000])
False
- >>> check_time([1999, 01, 01, 24, 59, 59, 999000])
+ >>> check_time([1999, 1, 1, 24, 59, 59, 999000])
False
- >>> check_time([1999, 01, 31, 23, 59, 59, 999000])
+ >>> check_time([1999, 1, 31, 23, 59, 59, 999000])
True
- >>> check_time([1999, 02, 30, 23, 59, 59, 999000])
+ >>> check_time([1999, 2, 30, 23, 59, 59, 999000])
False
- >>> check_time([1999, 02, 29, 23, 59, 59, 999000])
+ >>> check_time([1999, 2, 29, 23, 59, 59, 999000])
False
- >>> check_time([2000, 02, 29, 23, 59, 59, 999000])
+ >>> check_time([2000, 2, 29, 23, 59, 59, 999000])
True
>>> check_time([2000, 13, 29, 23, 59, 59, 999000])
False
@@ -381,6 +383,7 @@ def check_time(datetime):
return False
+# TODO: change root to datapath
def get_file_list(root_dir):
"""
Function uses a directorie to get all the *.gse files from it.
@@ -444,7 +447,7 @@ def evt_head_check(root_dir, out_dir=None):
"""
if not out_dir:
print('WARNING files are going to be overwritten!')
- inp = str(raw_input('Continue? [y/N]'))
+ inp = str(input('Continue? [y/N]'))
if not inp == 'y':
sys.exit()
filelist = get_file_list(root_dir)
diff --git a/pylot/core/util/event.py b/pylot/core/util/event.py
index 69763801..ede1c9f9 100644
--- a/pylot/core/util/event.py
+++ b/pylot/core/util/event.py
@@ -2,6 +2,7 @@
# -*- coding: utf-8 -*-
import os
+
from obspy import UTCDateTime
from obspy.core.event import Event as ObsPyEvent
from obspy.core.event import Origin, ResourceIdentifier
@@ -21,12 +22,13 @@ class Event(ObsPyEvent):
:param path: path to event directory
:type path: str
"""
+ # TODO: remove rootpath and database
self.pylot_id = path.split('/')[-1]
# initialize super class
super(Event, self).__init__(resource_id=ResourceIdentifier('smi:local/' + self.pylot_id))
self.path = path
self.database = path.split('/')[-2]
- self.datapath = path.split('/')[-3]
+ self.datapath = os.path.split(path)[0] # path.split('/')[-3]
self.rootpath = '/' + os.path.join(*path.split('/')[:-3])
self.pylot_autopicks = {}
self.pylot_picks = {}
@@ -73,7 +75,7 @@ class Event(ObsPyEvent):
text = lines[0]
self.addNotes(text)
try:
- datetime = UTCDateTime(path.split('/')[-1])
+ datetime = UTCDateTime(self.path.split('/')[-1])
origin = Origin(resource_id=self.resource_id, time=datetime, latitude=0, longitude=0, depth=0)
self.origins.append(origin)
except:
@@ -296,13 +298,13 @@ class Event(ObsPyEvent):
:rtype: None
"""
try:
- import cPickle
+ import pickle
except ImportError:
- import _pickle as cPickle
+ import _pickle as pickle
try:
outfile = open(filename, 'wb')
- cPickle.dump(self, outfile, -1)
+ pickle.dump(self, outfile, -1)
self.dirty = False
except Exception as e:
print('Could not pickle PyLoT event. Reason: {}'.format(e))
@@ -317,11 +319,11 @@ class Event(ObsPyEvent):
:rtype: Event
"""
try:
- import cPickle
+ import pickle
except ImportError:
- import _pickle as cPickle
+ import _pickle as pickle
infile = open(filename, 'rb')
- event = cPickle.load(infile)
+ event = pickle.load(infile)
event.dirty = False
print('Loaded %s' % filename)
return event
diff --git a/pylot/core/util/generate_array_maps.py b/pylot/core/util/generate_array_maps.py
index c974df8d..ebb83c5f 100755
--- a/pylot/core/util/generate_array_maps.py
+++ b/pylot/core/util/generate_array_maps.py
@@ -3,24 +3,37 @@
# small script that creates array maps for each event within a previously generated PyLoT project
import os
+
+num_thread = "16"
+os.environ["OMP_NUM_THREADS"] = num_thread
+os.environ["OPENBLAS_NUM_THREADS"] = num_thread
+os.environ["MKL_NUM_THREADS"] = num_thread
+os.environ["VECLIB_MAXIMUM_THREADS"] = num_thread
+os.environ["NUMEXPR_NUM_THREADS"] = num_thread
+os.environ["NUMEXPR_MAX_THREADS"] = num_thread
+
import multiprocessing
+import sys
+import glob
+import matplotlib
+
+matplotlib.use('Qt5Agg')
+sys.path.append(os.path.join('/'.join(sys.argv[0].split('/')[:-1]), '../../..'))
from PyLoT import Project
from pylot.core.util.dataprocessing import Metadata
from pylot.core.util.array_map import Array_map
import matplotlib.pyplot as plt
+import argparse
+
def main(project_file_path, manual=False, auto=True, file_format='png', f_ext='', ncores=None):
project = Project.load(project_file_path)
nEvents = len(project.eventlist)
input_list = []
-
+ print('\n')
for index, event in enumerate(project.eventlist):
- # MP MP TESTING +++
- #if not eventdir.endswith('20170908_044946.a'):
- # continue
- # MP MP ----
kwargs = dict(project=project, event=event, nEvents=nEvents, index=index, manual=manual, auto=auto,
file_format=file_format, f_ext=f_ext)
input_list.append(kwargs)
@@ -30,14 +43,20 @@ def main(project_file_path, manual=False, auto=True, file_format='png', f_ext=''
array_map_worker(item)
else:
pool = multiprocessing.Pool(ncores)
- result = pool.map(array_map_worker, input_list)
+ pool.map(array_map_worker, input_list)
pool.close()
pool.join()
+
def array_map_worker(input_dict):
event = input_dict['event']
eventdir = event.path
print('Working on event: {} ({}/{})'.format(eventdir, input_dict['index'] + 1, input_dict['nEvents']))
+ xml_picks = glob.glob(os.path.join(eventdir, f'*{input_dict["f_ext"]}.xml'))
+ if not len(xml_picks):
+ print('Event {} does not have any picks associated with event file extension {}'.format(eventdir,
+ input_dict['f_ext']))
+ return
# check for picks
manualpicks = event.getPicks()
autopicks = event.getAutopicks()
@@ -51,11 +70,12 @@ def array_map_worker(input_dict):
continue
if not metadata:
metadata = Metadata(inventory=metadata_path, verbosity=0)
+
# create figure to plot on
- fig = plt.figure(figsize=(16,9))
+ fig, ax = plt.subplots(figsize=(15, 9))
# create array map object
- map = Array_map(None, metadata, parameter=input_dict['project'].parameter, figure=fig,
- width=2.13e6, height=1.2e6, pointsize=15., linewidth=1.0)
+ map = Array_map(None, metadata, parameter=input_dict['project'].parameter, axes=ax,
+ width=2.13e6, height=1.2e6, pointsize=25., linewidth=1.0)
# set combobox to auto/manual to plot correct pick type
map.comboBox_am.setCurrentIndex(map.comboBox_am.findText(pick_type))
# add picks to map and save file
@@ -65,11 +85,13 @@ def array_map_worker(input_dict):
fig.savefig(fpath_out, dpi=300.)
print('Wrote file: {}'.format(fpath_out))
-if __name__ == '__main__':
- dataroot = '/home/marcel'
- infiles=['alparray_all_events_0.03-0.1_mantle_correlated_v3.plp']
- for infile in infiles:
- main(os.path.join(dataroot, infile), f_ext='_correlated_0.1Hz', ncores=10)
- #main('E:\Shared\AlpArray\\test_aa.plp', f_ext='_correlated_0.5Hz', ncores=1)
- #main('/home/marcel/alparray_m6.5-6.9_mantle_correlated_v3.plp', f_ext='_correlated_0.5Hz')
+if __name__ == '__main__':
+ cl = argparse.ArgumentParser()
+ cl.add_argument('--dataroot', help='Directory containing the PyLoT .plp file', type=str)
+ cl.add_argument('--infiles', help='.plp files to use', nargs='+')
+ cl.add_argument('--ncores', hepl='Specify number of parallel processes', type=int, default=1)
+ args = cl.parse_args()
+
+ for infile in args.infiles:
+ main(os.path.join(args.dataroot, infile), f_ext='_correlated_0.03-0.1', ncores=args.ncores)
diff --git a/pylot/core/util/gui.py b/pylot/core/util/gui.py
index bc54cdf0..a1fc2e14 100644
--- a/pylot/core/util/gui.py
+++ b/pylot/core/util/gui.py
@@ -7,11 +7,10 @@ try:
except Exception as e:
print('Warning: Could not import module pyqtgraph.')
try:
- from PySide import QtCore
+ from PySide2 import QtCore
except Exception as e:
print('Warning: Could not import module QtCore.')
-
from pylot.core.util.utils import pick_color
@@ -49,11 +48,11 @@ def which(program, parameter):
:rtype: str
"""
try:
- from PySide.QtCore import QSettings
+ from PySide2.QtCore import QSettings
settings = QSettings()
for key in settings.allKeys():
if 'binPath' in key:
- os.environ['PATH'] += ':{0}'.format(settings.value(key))
+ os.environ['PATH'] += ':{0}'.format(settings.value(key))
nllocpath = ":" + parameter.get('nllocbin')
os.environ['PATH'] += nllocpath
except Exception as e:
@@ -73,7 +72,7 @@ def which(program, parameter):
return program
else:
for path in os.environ["PATH"].split(os.pathsep):
- exe_file = os.path.join(path, program)
+ exe_file = os.path.join(path, program)
for candidate in ext_candidates(exe_file):
if is_exe(candidate):
return candidate
@@ -101,4 +100,3 @@ def make_pen(picktype, phase, key, quality):
linestyle, width = pick_linestyle_pg(picktype, key)
pen = pg.mkPen(rgba, width=width, style=linestyle)
return pen
-
diff --git a/pylot/core/util/obspyDMT_interface.py b/pylot/core/util/obspyDMT_interface.py
index e8a1a021..4d6b8fc0 100644
--- a/pylot/core/util/obspyDMT_interface.py
+++ b/pylot/core/util/obspyDMT_interface.py
@@ -2,6 +2,7 @@
# -*- coding: utf-8 -*-
import os
+
from obspy import UTCDateTime
@@ -34,17 +35,25 @@ def qml_from_obspyDMT(path):
if not os.path.exists(path):
return IOError('Could not find Event at {}'.format(path))
- infile = open(path, 'rb')
- event_dmt = pickle.load(infile)#, fix_imports=True)
+
+ with open(path, 'rb') as infile:
+ event_dmt = pickle.load(infile) # , fix_imports=True)
+
event_dmt['origin_id'].id = str(event_dmt['origin_id'].id)
ev = Event(resource_id=event_dmt['event_id'])
- #small bugfix "unhashable type: 'newstr' "
+ # small bugfix "unhashable type: 'newstr' "
event_dmt['origin_id'].id = str(event_dmt['origin_id'].id)
- origin = Origin(resource_id=event_dmt['origin_id'], time=event_dmt['datetime'], longitude=event_dmt['longitude'],
- latitude=event_dmt['latitude'], depth=event_dmt['depth'])
- mag = Magnitude(mag=event_dmt['magnitude'], magnitude_type=event_dmt['magnitude_type'],
+
+ origin = Origin(resource_id=event_dmt['origin_id'],
+ time=event_dmt['datetime'],
+ longitude=event_dmt['longitude'],
+ latitude=event_dmt['latitude'],
+ depth=event_dmt['depth'])
+ mag = Magnitude(mag=event_dmt['magnitude'],
+ magnitude_type=event_dmt['magnitude_type'],
origin_id=event_dmt['origin_id'])
+
ev.magnitudes.append(mag)
ev.origins.append(origin)
return ev
diff --git a/pylot/core/util/pdf.py b/pylot/core/util/pdf.py
index 44b44bb4..19e117ca 100644
--- a/pylot/core/util/pdf.py
+++ b/pylot/core/util/pdf.py
@@ -1,8 +1,9 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
-import numpy as np
import warnings
+
+import numpy as np
from obspy import UTCDateTime
from pylot.core.util.utils import fit_curve, clims
diff --git a/pylot/core/util/thread.py b/pylot/core/util/thread.py
index d5ca3ef4..9aac2d3b 100644
--- a/pylot/core/util/thread.py
+++ b/pylot/core/util/thread.py
@@ -4,8 +4,8 @@ import os
import sys
import traceback
-from PySide.QtCore import QThread, Signal, Qt, Slot, QRunnable, QObject
-from PySide.QtGui import QDialog, QProgressBar, QLabel, QHBoxLayout
+from PySide2.QtCore import QThread, Signal, Qt, Slot, QRunnable, QObject
+from PySide2.QtWidgets import QDialog, QProgressBar, QLabel, QHBoxLayout, QPushButton
class Thread(QThread):
@@ -22,9 +22,11 @@ class Thread(QThread):
self.abortButton = abortButton
self.finished.connect(self.hideProgressbar)
self.showProgressbar()
+ self.old_stdout = None
def run(self):
if self.redirect_stdout:
+ self.old_stdout = sys.stdout
sys.stdout = self
try:
if self.arg is not None:
@@ -39,7 +41,7 @@ class Thread(QThread):
exctype, value = sys.exc_info()[:2]
self._executedErrorInfo = '{} {} {}'. \
format(exctype, value, traceback.format_exc())
- sys.stdout = sys.__stdout__
+ sys.stdout = self.old_stdout
def showProgressbar(self):
if self.progressText:
@@ -49,7 +51,23 @@ class Thread(QThread):
# self.pb_widget.setWindowFlags(Qt.SplashScreen)
# self.pb_widget.setModal(True)
- self.pb_widget.label.setText(self.progressText)
+ # generate widget if not given in init
+ if not self.pb_widget:
+ self.pb_widget = QDialog(self.parent())
+ self.pb_widget.setWindowFlags(Qt.SplashScreen)
+ self.pb_widget.setModal(True)
+
+ # add button
+ 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()
def hideProgressbar(self):
@@ -80,10 +98,12 @@ class Worker(QRunnable):
self.progressText = progressText
self.pb_widget = pb_widget
self.redirect_stdout = redirect_stdout
+ self.old_stdout = None
@Slot()
def run(self):
if self.redirect_stdout:
+ self.old_stdout = sys.stdout
sys.stdout = self
try:
@@ -96,7 +116,7 @@ class Worker(QRunnable):
self.signals.result.emit(result)
finally:
self.signals.finished.emit('Done')
- sys.stdout = sys.__stdout__
+ sys.stdout = self.old_stdout
def write(self, text):
self.signals.message.emit(text)
@@ -128,11 +148,13 @@ class MultiThread(QThread):
self.progressText = progressText
self.pb_widget = pb_widget
self.redirect_stdout = redirect_stdout
+ self.old_stdout = None
self.finished.connect(self.hideProgressbar)
self.showProgressbar()
def run(self):
if self.redirect_stdout:
+ self.old_stdout = sys.stdout
sys.stdout = self
try:
if not self.ncores:
@@ -148,7 +170,7 @@ class MultiThread(QThread):
exc_type, exc_obj, exc_tb = sys.exc_info()
fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1]
print('Exception: {}, file: {}, line: {}'.format(exc_type, fname, exc_tb.tb_lineno))
- sys.stdout = sys.__stdout__
+ sys.stdout = self.old_stdout
def showProgressbar(self):
if self.progressText:
diff --git a/pylot/core/util/utils.py b/pylot/core/util/utils.py
index 81f43b80..2567b079 100644
--- a/pylot/core/util/utils.py
+++ b/pylot/core/util/utils.py
@@ -2,12 +2,13 @@
# -*- coding: utf-8 -*-
import hashlib
-import numpy as np
import os
import platform
import re
import subprocess
import warnings
+
+import numpy as np
from obspy import UTCDateTime, read
from obspy.core import AttribDict
from obspy.signal.rotate import rotate2zne
@@ -227,7 +228,7 @@ def findComboBoxIndex(combo_box, val):
:type val: basestring
:return: index value of item with name val or 0
"""
- return combo_box.findText(val) if combo_box.findText(val) is not -1 else 0
+ return combo_box.findText(val) if combo_box.findText(val) != -1 else 0
def find_in_list(list, str):
@@ -950,10 +951,13 @@ def check4rotated(data, metadata=None, verbosity=1):
if any(rotation_required):
t_start = full_range(wfstream)
try:
- azimuts = [metadata.get_coordinates(tr_id, t_start)['azimuth'] for tr_id in trace_ids]
- dips = [metadata.get_coordinates(tr_id, t_start)['dip'] for tr_id in trace_ids]
+ azimuts = []
+ dips = []
+ for tr_id in trace_ids:
+ azimuts.append(metadata.get_coordinates(tr_id, t_start)['azimuth'])
+ dips.append(metadata.get_coordinates(tr_id, t_start)['dip'])
except (KeyError, TypeError) as e:
- print('Failed to rotate trace {}, no azimuth or dip available in metadata'.format(trace_id))
+ print('Failed to rotate trace {}, no azimuth or dip available in metadata'.format(tr_id))
return wfstream
if len(wfstream) < 3:
print('Failed to rotate Stream {}, not enough components available.'.format(wfstream))
@@ -964,7 +968,7 @@ def check4rotated(data, metadata=None, verbosity=1):
z, n, e = rotate2zne(wfstream[0], azimuts[0], dips[0],
wfstream[1], azimuts[1], dips[1],
wfstream[2], azimuts[2], dips[2])
- print('check4rotated: rotated trace {} to ZNE'.format(trace_id))
+ print('check4rotated: rotated trace {} to ZNE'.format(trace_ids))
# 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)
@@ -1013,7 +1017,7 @@ def scaleWFData(data, factor=None, components='all'):
:return: scaled waveform data
:rtype: `~obspy.core.stream.Stream` object
"""
- if components is not 'all':
+ if components != 'all':
for comp in components:
if factor is None:
max_val = np.max(np.abs(data.select(component=comp)[0].data))
diff --git a/pylot/core/util/version.py b/pylot/core/util/version.py
index c4006c12..8c328b2d 100644
--- a/pylot/core/util/version.py
+++ b/pylot/core/util/version.py
@@ -35,9 +35,9 @@ from __future__ import print_function
__all__ = "get_git_version"
+import inspect
# NO IMPORTS FROM PYLOT IN THIS FILE! (file gets used at installation time)
import os
-import inspect
from subprocess import Popen, PIPE
# NO IMPORTS FROM PYLOT IN THIS FILE! (file gets used at installation time)
diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py
index a2aff5ef..4b6c2281 100644
--- a/pylot/core/util/widgets.py
+++ b/pylot/core/util/widgets.py
@@ -8,35 +8,37 @@ Created on Wed Mar 19 11:27:35 2014
import copy
import datetime
import getpass
-import matplotlib
import multiprocessing
-import numpy as np
import os
import subprocess
import sys
import time
import traceback
-matplotlib.use('QT4Agg')
+import matplotlib
+import numpy as np
+
+matplotlib.use('QT5Agg')
from matplotlib.figure import Figure
try:
- from matplotlib.backends.backend_qt4agg import FigureCanvas
+ from matplotlib.backends.backend_qt5agg import FigureCanvas
except ImportError:
- from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
-from matplotlib.backends.backend_qt4agg import NavigationToolbar2QT
+ from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
+from matplotlib.backends.backend_qt5agg import NavigationToolbar2QT
from matplotlib.widgets import MultiCursor
from obspy import read
-from PySide import QtCore, QtGui
-from PySide.QtGui import QAction, QApplication, QCheckBox, QComboBox, \
+from PySide2 import QtCore, QtGui, QtWidgets
+from PySide2.QtGui import QIcon, QPixmap, QKeySequence
+from PySide2.QtWidgets import QAction, QApplication, QCheckBox, QComboBox, \
QDateTimeEdit, QDialog, QDialogButtonBox, QDoubleSpinBox, QGroupBox, \
- QGridLayout, QFormLayout, QIcon, QLabel, QLineEdit, QMessageBox, \
- QPixmap, QSpinBox, QTabWidget, QToolBar, QVBoxLayout, QHBoxLayout, QWidget, \
- QPushButton, QFileDialog, QInputDialog, QKeySequence
-from PySide.QtCore import QSettings, Qt, QUrl, Signal
-from PySide.QtWebKit import QWebView
+ QGridLayout, QLabel, QLineEdit, QMessageBox, \
+ QTabWidget, QToolBar, QVBoxLayout, QHBoxLayout, QWidget, \
+ QPushButton, QFileDialog, QInputDialog
+from PySide2.QtCore import QSettings, Qt, QUrl, Signal
+from PySide2.QtWebEngineWidgets import QWebEngineView as QWebView
from obspy import Stream, Trace, UTCDateTime
from obspy.core.util import AttribDict
from obspy.taup import TauPyModel
@@ -52,24 +54,88 @@ from pylot.core.util.utils import prepTimeAxis, full_range, demeanTrace, isSorte
pick_linestyle_plt, pick_color_plt, \
check4rotated, check4doubled, merge_stream, identifyPhase, \
loopIdentifyPhase, trim_station_components, transformFilteroptions2String, \
- identifyPhaseID, get_Bool, get_None, pick_color, getAutoFilteroptions, SetChannelComponents,\
+ identifyPhaseID, get_Bool, get_None, pick_color, getAutoFilteroptions, SetChannelComponents, \
station_id_remove_channel
from autoPyLoT import autoPyLoT
from pylot.core.util.thread import Thread
from pylot.core.util.dataprocessing import Metadata
-
if sys.version_info.major == 3:
import icons_rc_3 as icons_rc
-elif sys.version_info.major == 2:
- import icons_rc_2 as icons_rc
else:
- raise ImportError('Could not determine python version.')
+ raise ImportError(f'Python version {sys.version_info.major} of current interpreter not supported.'
+ f'\nPlease use Python 3+.')
# workaround to prevent PyCharm from deleting icons_rc import when optimizing imports
icons_rc = icons_rc
+class QSpinBox(QtWidgets.QSpinBox):
+ ''' Custom SpinBox, insensitive to Mousewheel (prevents accidental changes when scrolling through parameters) '''
+
+ def wheelEvent(self, event):
+ event.ignore()
+
+
+class QDoubleSpinBox(QtWidgets.QDoubleSpinBox):
+ ''' Custom DoubleSpinBox, insensitive to Mousewheel (prevents accidental changes when scrolling through parameters) '''
+
+ def wheelEvent(self, event):
+ event.ignore()
+
+
+class TextLogWidget(QtWidgets.QTextEdit):
+ highlight = Signal()
+
+ def __init__(self, parent, highlight_input=False):
+ super(TextLogWidget, self).__init__(parent)
+ self.highlight_input = highlight_input
+
+ def write(self, text):
+ self.append(text)
+ if self.highlight_input:
+ self.highlight.emit()
+
+
+class LogWidget(QtWidgets.QWidget):
+ def __init__(self, parent):
+ super(LogWidget, self).__init__(parent, Qt.Window)
+ self.current_active_error = False
+ self.qmb = None
+ self.setWindowTitle('PyLoT Log')
+ self.setMinimumWidth(800)
+ self.setMinimumHeight(600)
+
+ self.stdout = TextLogWidget(self)
+ self.stderr = TextLogWidget(self, highlight_input=True)
+ self.stderr.highlight.connect(self.active_error)
+
+ self.tabs = QTabWidget()
+ self.tabs.addTab(self.stdout, 'Log')
+ self.tabs.addTab(self.stderr, 'Errors')
+
+ self.layout = QtWidgets.QVBoxLayout()
+ self.textfield = QtWidgets.QTextEdit()
+ self.setLayout(self.layout)
+ self.layout.addWidget(self.tabs)
+
+ def active_error(self):
+ if self.current_active_error == False:
+ self.current_active_error = True
+ self.show()
+ self.activateWindow()
+ self.tabs.setCurrentWidget(self.stderr)
+ self.qmb = QtWidgets.QMessageBox(QtWidgets.QMessageBox.Icon.Warning,
+ 'Error', 'Error occurred. Please check log!')
+ self.qmb.buttonClicked.connect(self.reset_error)
+ self.qmb.show()
+
+ def reset_error(self):
+ # used to make sure that write errors is finished before raising new Message box etc.
+ self.current_active_error = False
+ self.stderr.append(60 * '#' + '\n\n')
+
+
def getDataType(parent):
type = QInputDialog().getItem(parent, "Select phases type", "Type:",
["manual", "automatic"])
@@ -106,7 +172,7 @@ def plot_pdf(_axes, x, y, annotation, bbox_props, xlabel=None, ylabel=None,
def createAction(parent, text, slot=None, shortcut=None, icon=None,
tip=None, checkable=False):
"""
- :rtype : ~PySide.QtGui.QAction
+ :rtype : ~PyQt5.QtWidgets.QAction
"""
action = QAction(text, parent)
if icon is not None:
@@ -122,11 +188,11 @@ def createAction(parent, text, slot=None, shortcut=None, icon=None,
return action
-class ProgressBarWidget(QtGui.QWidget):
+class ProgressBarWidget(QtWidgets.QWidget):
def __init__(self, parent=None):
super(ProgressBarWidget, self).__init__(parent)
- self.hlayout = QtGui.QHBoxLayout()
- self.pb = QtGui.QProgressBar()
+ self.hlayout = QtWidgets.QHBoxLayout()
+ self.pb = QtWidgets.QProgressBar()
self.pb.setRange(0, 0)
self.label = QLabel()
self.hlayout.addWidget(self.pb)
@@ -136,7 +202,7 @@ class ProgressBarWidget(QtGui.QWidget):
class AddMetadataWidget(QWidget):
- def __init__(self, parent=None, metadata=None, windowflag=1):
+ def __init__(self, parent=None, metadata=None, windowflag=Qt.Window):
super(AddMetadataWidget, self).__init__(parent, windowflag)
self.inventories_add = []
self.inventories_delete = []
@@ -163,8 +229,8 @@ class AddMetadataWidget(QWidget):
def center(self):
fm = self.frameGeometry()
- screen = QtGui.QApplication.desktop().screenNumber(QtGui.QApplication.desktop().cursor().pos())
- centerPoint = QtGui.QApplication.desktop().screenGeometry(screen).center()
+ screen = QtWidgets.QApplication.desktop().screenNumber(QtWidgets.QApplication.desktop().cursor().pos())
+ centerPoint = QtWidgets.QApplication.desktop().screenGeometry(screen).center()
fm.moveCenter(centerPoint)
self.move(fm.topLeft())
@@ -177,10 +243,10 @@ class AddMetadataWidget(QWidget):
def init_lineedit(self):
self.selection_layout = QVBoxLayout()
- self.lineedit_title = QtGui.QLabel("Choose metadata file to add:")
+ self.lineedit_title = QtWidgets.QLabel("Choose metadata file to add:")
self.selection_layout.insertWidget(0, self.lineedit_title)
self.lineedit_layout = QHBoxLayout()
- self.selection_box = QtGui.QLineEdit()
+ self.selection_box = QtWidgets.QLineEdit()
self.browse_button = self.init_button("Browse", explanation="Browse the file explorer")
self.lineedit_layout.addWidget(self.selection_box)
self.lineedit_layout.addWidget(self.browse_button)
@@ -221,12 +287,12 @@ class AddMetadataWidget(QWidget):
self.init_list_widget()
def init_list_title(self):
- self.list_title = QtGui.QLabel("Current metadata files:")
+ self.list_title = QtWidgets.QLabel("Current metadata files:")
self.list_layout.insertWidget(0, self.list_title)
def init_list_widget(self):
- self.list_view = QtGui.QListView()
- self.list_view.setSelectionMode(QtGui.QAbstractItemView.ExtendedSelection)
+ self.list_view = QtWidgets.QListView()
+ self.list_view.setSelectionMode(QtWidgets.QAbstractItemView.ExtendedSelection)
self.list_model = QtGui.QStandardItemModel(self.list_view)
self.list_view.setModel(self.list_model)
self.list_layout.insertWidget(1, self.list_view, 1)
@@ -301,7 +367,7 @@ class AddMetadataWidget(QWidget):
item = QtGui.QStandardItem(inventory_path)
item.setEditable(False)
self.inventories_add.append(inventory_path)
- self.list_model.appendRow(item) # adds path to visible list
+ self.list_model.appendRow(item) # adds path to visible list
self.selection_box.setText("")
def remove_item(self):
@@ -312,8 +378,8 @@ class AddMetadataWidget(QWidget):
"""
for index in reversed(sorted(self.list_view.selectionModel().selectedIndexes())):
item = self.list_model.itemData(index)
- inventory_path = item[0] # marked path
- self.list_model.removeRow(index.row()) # aus der Anzeige-Liste gelöscht
+ inventory_path = item[0] # marked path
+ self.list_model.removeRow(index.row()) # aus der Anzeige-Liste gelöscht
if inventory_path in self.inventories_add:
self.inventories_add.remove(inventory_path)
else:
@@ -337,7 +403,7 @@ class AddMetadataWidget(QWidget):
class ComparisonWidget(QWidget):
- def __init__(self, c, parent=None, windowflag=1):
+ def __init__(self, c, parent=None, windowflag=Qt.Window):
self._data = c
self._stats = c.stations
self._canvas = PlotWidget(self)
@@ -354,8 +420,8 @@ class ComparisonWidget(QWidget):
def center(self):
fm = self.frameGeometry()
- screen = QtGui.QApplication.desktop().screenNumber(QtGui.QApplication.desktop().cursor().pos())
- centerPoint = QtGui.QApplication.desktop().screenGeometry(screen).center()
+ screen = QtWidgets.QApplication.desktop().screenNumber(QtWidgets.QApplication.desktop().cursor().pos())
+ centerPoint = QtWidgets.QApplication.desktop().screenGeometry(screen).center()
fm.moveCenter(centerPoint)
self.move(fm.topLeft())
@@ -670,9 +736,9 @@ class PlotWidget(FigureCanvas):
return self._parent
-class WaveformWidgetPG(QtGui.QWidget):
+class WaveformWidgetPG(QtWidgets.QWidget):
def __init__(self, parent, title='Title'):
- QtGui.QWidget.__init__(self, parent=parent)
+ QtWidgets.QWidget.__init__(self, parent=parent)
self.pg = self.parent().pg
# added because adding widget to scrollArea will set scrollArea to parent
self.orig_parent = parent
@@ -683,8 +749,8 @@ class WaveformWidgetPG(QtGui.QWidget):
self.ylabel = None
self.title = None
# create plot
- self.main_layout = QtGui.QVBoxLayout()
- self.label_layout = QtGui.QHBoxLayout()
+ self.main_layout = QtWidgets.QVBoxLayout()
+ self.label_layout = QtWidgets.QHBoxLayout()
self.add_labels()
self.connect_signals()
self.plotWidget = self.pg.PlotWidget(self.parent(), title=title)
@@ -750,13 +816,13 @@ class WaveformWidgetPG(QtGui.QWidget):
self.label_layout.setStretch(5, 1)
def add_labels(self):
- self.status_label = QtGui.QLabel()
+ self.status_label = QtWidgets.QLabel()
self.perm_labels = []
for index in range(3):
- label = QtGui.QLabel()
+ label = QtWidgets.QLabel()
self.perm_labels.append(label)
- self.qcombo_processed = QtGui.QComboBox()
- self.syn_checkbox = QtGui.QCheckBox('synthetics')
+ self.qcombo_processed = QtWidgets.QComboBox()
+ self.syn_checkbox = QtWidgets.QCheckBox('synthetics')
self.addQCboxItem('processed', 'green')
self.addQCboxItem('raw', 'black')
# self.perm_qcbox_right.setAlignment(2)
@@ -875,7 +941,8 @@ class WaveformWidgetPG(QtGui.QWidget):
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 [])
- trace.data = np.array([datum * gain + n for index, datum in enumerate(trace.data) if not index % nth_sample])
+ trace.data = np.array(
+ [datum * gain + n for index, datum in enumerate(trace.data) if not index % nth_sample])
trace_syn.data = np.array([datum + n for index, datum in enumerate(trace_syn.data)
if not index % nth_sample] if st_syn else [])
plots.append((times, trace.data,
@@ -894,7 +961,7 @@ class WaveformWidgetPG(QtGui.QWidget):
'''
npixel = self.orig_parent.width()
ndata = len(trace.data)
- pts_per_pixel = ndata / npixel
+ pts_per_pixel = ndata // npixel
if pts_per_pixel < 2:
return trace.data, time_ax
remaining_samples = ndata % pts_per_pixel
@@ -1124,7 +1191,7 @@ class PylotCanvas(FigureCanvas):
def saveFigure(self):
if self.figure:
- fd = QtGui.QFileDialog()
+ fd = QtWidgets.QFileDialog()
fname, filter = fd.getSaveFileName(self.parent(), filter='Images (*.png *.svg *.jpg)')
if not fname:
return
@@ -1205,7 +1272,7 @@ class PylotCanvas(FigureCanvas):
parent.refreshPhaseText()
def keyPressHandler(self, gui_event):
- if gui_event.key == 'ctrl+s':
+ if gui_event.key == 'ctrl+p':
self.saveFigure()
def connectEvents(self):
@@ -1485,7 +1552,7 @@ class PylotCanvas(FigureCanvas):
self.draw()
-class SingleTextLineDialog(QtGui.QDialog):
+class SingleTextLineDialog(QtWidgets.QDialog):
def __init__(self, parent=None, label='Text: ', default_text='.xml'):
super(SingleTextLineDialog, self).__init__(parent)
self.default_text = default_text
@@ -1495,12 +1562,12 @@ class SingleTextLineDialog(QtGui.QDialog):
self.setupUi()
def setupUi(self):
- self.main_layout = QtGui.QVBoxLayout()
- self.sub_layout = QtGui.QHBoxLayout()
+ self.main_layout = QtWidgets.QVBoxLayout()
+ self.sub_layout = QtWidgets.QHBoxLayout()
#
self.setLayout(self.main_layout)
- self.textLabel = QtGui.QLabel(self.label)
- self.lineEdit = QtGui.QLineEdit(self.default_text)
+ self.textLabel = QtWidgets.QLabel(self.label)
+ self.lineEdit = QtWidgets.QLineEdit(self.default_text)
self.sub_layout.addWidget(self.textLabel)
self.sub_layout.addWidget(self.lineEdit)
@@ -1517,17 +1584,15 @@ class SingleTextLineDialog(QtGui.QDialog):
self._buttonbox.rejected.connect(self.reject)
-
-
-class PhaseDefaults(QtGui.QDialog):
+class PhaseDefaults(QtWidgets.QDialog):
def __init__(self, parent=None, nrow=10,
phase_defaults=['P', 'S'],
current_phases=[]):
super(PhaseDefaults, self).__init__(parent)
self.nrow = nrow
self.checktoggle = True
- self.main_layout = QtGui.QVBoxLayout()
- self.sub_layout = QtGui.QGridLayout()
+ self.main_layout = QtWidgets.QVBoxLayout()
+ self.sub_layout = QtWidgets.QGridLayout()
self.phase_names = phase_defaults
self.current_phases = current_phases
self.setButtons()
@@ -1537,7 +1602,7 @@ class PhaseDefaults(QtGui.QDialog):
self.selected_phases = []
def setButtons(self):
- self._check_all_button = QtGui.QPushButton('Check/Uncheck all')
+ self._check_all_button = QtWidgets.QPushButton('Check/Uncheck all')
self._buttonbox = QDialogButtonBox(QDialogButtonBox.Ok |
QDialogButtonBox.Cancel)
@@ -1573,9 +1638,8 @@ class PhaseDefaults(QtGui.QDialog):
checkbox.setChecked(bool(phase in self.current_phases))
row += 1
- @staticmethod
- def create_phase_box(phase_name):
- checkbox = QtGui.QCheckBox(phase_name)
+ def create_phase_box(self, phase_name):
+ checkbox = QtWidgets.QCheckBox(phase_name)
return checkbox
def update_selected_phases(self):
@@ -1586,7 +1650,7 @@ class PhaseDefaults(QtGui.QDialog):
def accept(self):
self.update_selected_phases()
- QtGui.QDialog.accept(self)
+ QtWidgets.QDialog.accept(self)
class PickDlg(QDialog):
@@ -1595,7 +1659,7 @@ class PickDlg(QDialog):
def __init__(self, parent=None, data=None, station=None, network=None, location=None, picks=None,
autopicks=None, rotate=False, parameter=None, embedded=False, metadata=None,
event=None, filteroptions=None, model=None, wftype=None):
- super(PickDlg, self).__init__(parent, 1)
+ super(PickDlg, self).__init__(parent, Qt.Window)
self.orig_parent = parent
self.setAttribute(Qt.WA_DeleteOnClose)
@@ -1640,14 +1704,14 @@ class PickDlg(QDialog):
else:
self.filteroptions = FILTERDEFAULTS
self.pick_block = False
- self.nextStation = QtGui.QCheckBox('Continue with next station ')
+ self.nextStation = QtWidgets.QCheckBox('Continue with next station ')
# comparison channel
- self.compareChannel = QtGui.QComboBox()
+ self.compareChannel = QtWidgets.QComboBox()
self.compareChannel.activated.connect(self.resetPlot)
# scale channel
- self.scaleChannel = QtGui.QComboBox()
+ self.scaleChannel = QtWidgets.QComboBox()
self.scaleChannel.activated.connect(self.resetPlot)
# initialize panning attributes
@@ -1738,10 +1802,10 @@ class PickDlg(QDialog):
self.setWindowState(QtCore.Qt.WindowMaximized)
def setupUi(self):
- menuBar = QtGui.QMenuBar(self)
+ menuBar = QtWidgets.QMenuBar(self)
if not self._embedded:
exitMenu = menuBar.addMenu('File')
- exitAction = QtGui.QAction('Close', self)
+ exitAction = QtWidgets.QAction('Close', self)
exitAction.triggered.connect(self.close)
exitMenu.addAction(exitAction)
@@ -1833,10 +1897,10 @@ class PickDlg(QDialog):
self.reject_button = QPushButton('&Reject')
self.disable_ar_buttons()
- self.statusbar = QtGui.QStatusBar(self)
+ self.statusbar = QtWidgets.QStatusBar(self)
# add hotkeys
- self._shortcut_space = QtGui.QShortcut(QtGui.QKeySequence(' '), self)
+ self._shortcut_space = QtWidgets.QShortcut(QtGui.QKeySequence(' '), self)
self._shortcut_space.activated.connect(self.accept_button.clicked)
# button shortcuts (1 for P-button, 2 for S-button)
# self.p_button.setShortcut(QKeySequence('1'))
@@ -1878,15 +1942,15 @@ class PickDlg(QDialog):
_dialtoolbar.addWidget(est_label)
_dialtoolbar.addWidget(self.plot_arrivals_button)
_dialtoolbar.addSeparator()
- _dialtoolbar.addWidget(QtGui.QLabel('Compare to channel: '))
+ _dialtoolbar.addWidget(QtWidgets.QLabel('Compare to channel: '))
_dialtoolbar.addWidget(self.compareChannel)
_dialtoolbar.addSeparator()
- _dialtoolbar.addWidget(QtGui.QLabel('Scaling: '))
+ _dialtoolbar.addWidget(QtWidgets.QLabel('Scaling: '))
_dialtoolbar.addWidget(self.scaleChannel)
# layout the innermost widget
_innerlayout = QVBoxLayout()
- _innerinnerlayout = QtGui.QHBoxLayout()
+ _innerinnerlayout = QtWidgets.QHBoxLayout()
_lowerlayout = QHBoxLayout()
_innerinnerlayout.addWidget(self.multicompfig)
_innerinnerlayout.addWidget(self.phaseplot)
@@ -1988,6 +2052,7 @@ class PickDlg(QDialog):
settings = QtCore.QSettings()
p_phases = settings.value('p_phases')
s_phases = settings.value('s_phases')
+ phases = ''
if not p_phases and not s_phases:
print('No phases for TauPy selected in Preferences.')
if p_phases and s_phases:
@@ -2235,7 +2300,6 @@ class PickDlg(QDialog):
self.draw()
else:
self.draw()
- # self.pick_block = self.togglePickBlocker()
self.disconnect_pick_delete()
def deactivatePicking(self):
@@ -2445,8 +2509,8 @@ class PickDlg(QDialog):
data = self.getPickPhases(data, phase)
data.normalize()
if not data:
- QtGui.QMessageBox.warning(self, 'No channel to plot',
- 'No channel to plot for phase: {}.'.format(phase))
+ QtWidgets.QMessageBox.warning(self, 'No channel to plot',
+ 'No channel to plot for phase: {}.'.format(phase))
self.leave_picking_mode()
return
@@ -2459,8 +2523,9 @@ class PickDlg(QDialog):
data.filter(**filteroptions)
# wfdata.filter(**filteroptions)# MP MP removed filtering of original data
except ValueError as e:
- self.qmb = QtGui.QMessageBox(QtGui.QMessageBox.Icon.Information,
- 'Denied', 'setIniPick{}: Could not filter waveform: {}'.format(phase, e))
+ self.qmb = QtWidgets.QMessageBox(QtWidgets.QMessageBox.Icon.Information,
+ 'Denied',
+ 'setIniPick{}: Could not filter waveform: {}'.format(phase, e))
self.qmb.show()
snr = []
@@ -2544,8 +2609,8 @@ class PickDlg(QDialog):
wfdata.detrend('linear')
wfdata.filter(**filteroptions)
except ValueError as e:
- self.qmb = QtGui.QMessageBox(QtGui.QMessageBox.Icon.Information,
- 'Denied', 'setPick: Could not filter waveform: {}'.format(e))
+ self.qmb = QtWidgets.QMessageBox(QtWidgets.QMessageBox.Icon.Information,
+ 'Denied', 'setPick: Could not filter waveform: {}'.format(e))
self.qmb.show()
# get earliest and latest possible pick and symmetric pick error
@@ -2585,9 +2650,8 @@ class PickDlg(QDialog):
minFMSNR = parameter.get('minFMSNR')
quality = get_quality_class(spe, parameter.get('timeerrorsP'))
if quality <= minFMweight and snr >= minFMSNR:
- FM = fmpicker(self.getWFData().select(channel=channel), wfdata, parameter.get('fmpickwin'),
- pick -stime_diff)
-
+ FM = fmpicker(self.getWFData().select(channel=channel), wfdata, parameter.get('fmpickwin'),
+ pick - stime_diff)
# save pick times for actual phase
phasepicks = dict(epp=epp, lpp=lpp, mpp=mpp, spe=spe, fm=FM,
@@ -2602,8 +2666,6 @@ class PickDlg(QDialog):
self.disconnectPressEvent()
self.enable_ar_buttons()
self.zoomAction.setEnabled(True)
- # self.pick_block = self.togglPickBlocker()
- # self.resetZoom()
self.leave_picking_mode()
@@ -2616,8 +2678,8 @@ class PickDlg(QDialog):
return True
def warn_unknown_phase(self, phase=None):
- QtGui.QMessageBox.warning(self, 'Unknown phase ID',
- 'Could not identify phase ID: {}.'.format(phase))
+ QtWidgets.QMessageBox.warning(self, 'Unknown phase ID',
+ 'Could not identify phase ID: {}.'.format(phase))
def disconnectPressEvent(self):
self.multicompfig.mpl_disconnect(self.cidpress)
@@ -2757,7 +2819,7 @@ class PickDlg(QDialog):
y = event.y
y = self.size().height() - y
pt = self.mapToGlobal(QtCore.QPoint(x, y))
- QtGui.QToolTip.showText(pt, message)
+ QtWidgets.QToolTip.showText(pt, message)
def onpick_info(self, event):
if not event.mouseevent.button == 1:
@@ -2791,14 +2853,14 @@ class PickDlg(QDialog):
allpicks = {'manual': self.picks,
'auto': self.autopicks}
picks = allpicks[picktype]
- dialog = QtGui.QInputDialog(parent=self)
+ dialog = QtWidgets.QInputDialog(parent=self)
new_phase, executed = dialog.getText(self, 'Rename phase', 'Rename phase {} to:'.format(phase))
if executed:
try:
self.renamePhaseInDict(picks, phase, new_phase)
except KeyError as e:
- QtGui.QMessageBox.warning(self, 'Could not rename phase',
- 'Could not rename phase {} to {}: {}'.format(phase, new_phase, e))
+ QtWidgets.QMessageBox.warning(self, 'Could not rename phase',
+ 'Could not rename phase {} to {}: {}'.format(phase, new_phase, e))
self.leave_rename_phase()
self.refreshPlot()
@@ -2905,11 +2967,6 @@ class PickDlg(QDialog):
if phase:
filtoptions = self.getFilterOptions(self.getPhaseID(phase), gui_filter=True).parseFilterOptions()
- # if self.filterActionP.isChecked() or self.filterActionS.isChecked():
- # if not phase:
- # filtoptions = FilterOptionsDialog.getFilterObject()
- # filtoptions = filtoptions.parseFilterOptions()
-
if filtoptions is not None:
data.detrend('linear')
data.taper(0.02, type='cosine')
@@ -3028,8 +3085,6 @@ class PickDlg(QDialog):
# set channel labels
self.multicompfig.setYTickLabels(pos, labels)
- # self.multicompfig.setXLims(ax, self.getXLims())
- # self.multicompfig.setYLims(ax, self.getYLims())
def zoom(self):
if self.zoomAction.isChecked() and self.pick_block:
@@ -3063,8 +3118,8 @@ class PickDlg(QDialog):
QDialog.reject(self)
else:
self.refreshPlot()
- self.qmb = QtGui.QMessageBox(QtGui.QMessageBox.Icon.Information,
- 'Denied', 'New picks rejected!')
+ self.qmb = QtWidgets.QMessageBox(QtWidgets.QMessageBox.Icon.Information,
+ 'Denied', 'New picks rejected!')
self.qmb.show()
def accept(self):
@@ -3072,8 +3127,8 @@ class PickDlg(QDialog):
if not self._embedded:
QDialog.accept(self)
else:
- self.qmb = QtGui.QMessageBox(QtGui.QMessageBox.Icon.Information,
- 'Accepted', 'New picks applied!')
+ self.qmb = QtWidgets.QMessageBox(QtWidgets.QMessageBox.Icon.Information,
+ 'Accepted', 'New picks applied!')
self.qmb.show()
@@ -3091,9 +3146,9 @@ class CanvasWidget(QWidget):
'''
def __init__(self, parent, canvas):
- QtGui.QWidget.__init__(self, parent) # , 1)
+ QtWidgets.QWidget.__init__(self, parent) # , 1)
canvas = canvas
- self.main_layout = QtGui.QVBoxLayout()
+ self.main_layout = QtWidgets.QVBoxLayout()
self.setLayout(self.main_layout)
self.main_layout.addWidget(canvas)
canvas.setZoomBorders2content()
@@ -3105,8 +3160,8 @@ class MultiEventWidget(QWidget):
'''
- def __init__(self, options=None, parent=None, windowflag=1):
- QtGui.QWidget.__init__(self, parent, windowflag)
+ def __init__(self, options=None, parent=None, windowflag=Qt.Window):
+ QtWidgets.QWidget.__init__(self, parent, windowflag)
self.options = options
self.setupUi()
@@ -3116,23 +3171,23 @@ class MultiEventWidget(QWidget):
def center(self):
fm = self.frameGeometry()
- screen = QtGui.QApplication.desktop().screenNumber(QtGui.QApplication.desktop().cursor().pos())
- centerPoint = QtGui.QApplication.desktop().screenGeometry(screen).center()
+ screen = QtWidgets.QApplication.desktop().screenNumber(QtWidgets.QApplication.desktop().cursor().pos())
+ centerPoint = QtWidgets.QApplication.desktop().screenGeometry(screen).center()
fm.moveCenter(centerPoint)
self.move(fm.topLeft())
def setupUi(self):
# init main layout
- self.main_layout = QtGui.QVBoxLayout()
+ self.main_layout = QtWidgets.QVBoxLayout()
self.setLayout(self.main_layout)
# init main splitter
- self.main_splitter = QtGui.QSplitter()
+ self.main_splitter = QtWidgets.QSplitter()
self.main_splitter.setChildrenCollapsible(False)
self.init_checkboxes()
- self.eventbox = QtGui.QComboBox()
- self.button_clear = QtGui.QPushButton('Clear')
+ self.eventbox = QtWidgets.QComboBox()
+ self.button_clear = QtWidgets.QPushButton('Clear')
self.main_layout.insertWidget(1, self.main_splitter)
@@ -3142,14 +3197,14 @@ class MultiEventWidget(QWidget):
self.main_splitter.setStretchFactor(1, 2)
def init_checkboxes(self):
- self.rb_layout = QtGui.QHBoxLayout()
+ self.rb_layout = QtWidgets.QHBoxLayout()
self.rb_dict = {}
- self.start_button = QtGui.QPushButton('Start')
+ self.start_button = QtWidgets.QPushButton('Start')
for index, (key, func, color) in enumerate(self.options):
- rb = QtGui.QRadioButton(key)
+ rb = QtWidgets.QRadioButton(key)
rb.toggled.connect(self.check_rb_selection)
if color:
color = 'rgba{}'.format(color)
@@ -3168,12 +3223,12 @@ class MultiEventWidget(QWidget):
self.rb_layout.insertWidget(index, rb)
self.rb_layout.setStretch(index, 0)
- self.pb = QtGui.QProgressBar()
+ self.pb = QtWidgets.QProgressBar()
self.pb.setRange(0, 0)
self.pb.setVisible(False)
# space holder for progressbar
- self._pb_space = QtGui.QWidget()
+ self._pb_space = QtWidgets.QWidget()
self.rb_layout.addWidget(self.start_button)
@@ -3223,7 +3278,7 @@ class AutoPickWidget(MultiEventWidget):
'''
def __init__(self, parent, options):
- MultiEventWidget.__init__(self, options, parent, 1)
+ MultiEventWidget.__init__(self, options, parent, Qt.Window)
self.events2plot = {}
self.connect_buttons()
self.init_plot_layout()
@@ -3238,21 +3293,21 @@ class AutoPickWidget(MultiEventWidget):
def init_plot_layout(self):
# init tab widget
- self.tab_plots = QtGui.QTabWidget()
- self.gb_plots = QtGui.QGroupBox('Plots')
+ self.tab_plots = QtWidgets.QTabWidget()
+ self.gb_plots = QtWidgets.QGroupBox('Plots')
self.gb_plots.setMinimumSize(100, 100)
self.main_splitter.insertWidget(1, self.gb_plots)
- self.plot_layout = QtGui.QVBoxLayout()
+ self.plot_layout = QtWidgets.QVBoxLayout()
self.plot_layout.insertWidget(1, self.tab_plots)
self.gb_plots.setLayout(self.plot_layout)
def init_log_layout(self):
- self.gb_log = QtGui.QGroupBox('Log')
+ self.gb_log = QtWidgets.QGroupBox('Log')
self.gb_log.setMinimumSize(100, 100)
self.main_splitter.insertWidget(0, self.gb_log)
def insert_log_widget(self, widget):
- vl = QtGui.QVBoxLayout()
+ vl = QtWidgets.QVBoxLayout()
vl.addWidget(widget)
self.gb_log.setLayout(vl)
@@ -3280,7 +3335,7 @@ class AutoPickWidget(MultiEventWidget):
def update_plots(self):
self.refresh_plot_tabs()
if len(self.events2plot) > 0:
- self.eventbox_layout = QtGui.QHBoxLayout()
+ self.eventbox_layout = QtWidgets.QHBoxLayout()
self.generate_combobox()
self.eventbox_layout.addWidget(self.eventbox)
self.eventbox_layout.addWidget(self.button_clear)
@@ -3294,7 +3349,7 @@ class AutoPickWidget(MultiEventWidget):
def reinitEvents2plot(self):
for eventID, eventDict in self.events2plot.items():
for widget_key, widget in eventDict.items():
- del widget
+ widget.setParent(None)
self.events2plot = {}
self.eventbox.clear()
self.refresh_plot_tabs()
@@ -3312,10 +3367,10 @@ class CompareEventsWidget(MultiEventWidget):
'''
def __init__(self, parent, options, eventdict, comparisons):
- MultiEventWidget.__init__(self, options, parent, 1)
+ MultiEventWidget.__init__(self, options, parent, Qt.Window)
self.eventdict = eventdict
self.comparisons = comparisons
- self.compare_widget = QtGui.QWidget()
+ self.compare_widget = QtWidgets.QWidget()
self.init_eventbox()
self.init_event_area()
self.fill_eventbox()
@@ -3326,8 +3381,8 @@ class CompareEventsWidget(MultiEventWidget):
def center(self):
fm = self.frameGeometry()
- screen = QtGui.QApplication.desktop().screenNumber(QtGui.QApplication.desktop().cursor().pos())
- centerPoint = QtGui.QApplication.desktop().screenGeometry(screen).center()
+ screen = QtWidgets.QApplication.desktop().screenNumber(QtWidgets.QApplication.desktop().cursor().pos())
+ centerPoint = QtWidgets.QApplication.desktop().screenGeometry(screen).center()
fm.moveCenter(centerPoint)
self.move(fm.topLeft())
@@ -3343,7 +3398,7 @@ class CompareEventsWidget(MultiEventWidget):
self.main_layout.insertWidget(1, self.event_area)
def init_eventbox(self):
- self.eventbox_layout = QtGui.QHBoxLayout()
+ self.eventbox_layout = QtWidgets.QHBoxLayout()
self.eventbox_layout.addWidget(self.eventbox)
self.eventbox.currentIndexChanged.connect(self.update_comparison)
@@ -3357,10 +3412,10 @@ class CompareEventsWidget(MultiEventWidget):
self.eventbox.clear()
self.fill_eventbox()
- def update_comparison(self, index=0):
+ def update_comparison(self):
self.compare_widget.setParent(None)
self.compare_widget = ComparisonWidget(
- self.comparisons[self.eventbox.currentText()], self, 0)
+ self.comparisons[self.eventbox.currentText()], self, Qt.Widget)
self.event_layout.insertWidget(1, self.compare_widget)
self.set_main_stretch()
@@ -3385,7 +3440,7 @@ class TuneAutopicker(QWidget):
'''
def __init__(self, parent, obspy_dmt=False):
- QtGui.QWidget.__init__(self, parent, 1)
+ QtWidgets.QWidget.__init__(self, parent, Qt.Window)
self._style = parent._style
self.setWindowTitle('PyLoT - Tune Autopicker')
self.parameter = self.parent()._inputs
@@ -3418,8 +3473,8 @@ class TuneAutopicker(QWidget):
def center(self):
fm = self.frameGeometry()
- screen = QtGui.QApplication.desktop().screenNumber(QtGui.QApplication.desktop().cursor().pos())
- centerPoint = QtGui.QApplication.desktop().screenGeometry(screen).center()
+ screen = QtWidgets.QApplication.desktop().screenNumber(QtWidgets.QApplication.desktop().cursor().pos())
+ centerPoint = QtWidgets.QApplication.desktop().screenGeometry(screen).center()
fm.moveCenter(centerPoint)
self.move(fm.topLeft())
@@ -3430,10 +3485,10 @@ class TuneAutopicker(QWidget):
self.fig_dict = fig_dict
def init_main_layouts(self):
- self.main_layout = QtGui.QVBoxLayout()
- self.tune_layout = QtGui.QHBoxLayout()
- self.trace_layout = QtGui.QHBoxLayout()
- self.parameter_layout = QtGui.QVBoxLayout()
+ self.main_layout = QtWidgets.QVBoxLayout()
+ self.tune_layout = QtWidgets.QHBoxLayout()
+ self.trace_layout = QtWidgets.QHBoxLayout()
+ self.parameter_layout = QtWidgets.QVBoxLayout()
self.main_layout.addLayout(self.trace_layout)
self.main_layout.addLayout(self.tune_layout)
@@ -3446,7 +3501,7 @@ class TuneAutopicker(QWidget):
self.trace_layout.addWidget(self.eventBox)
def init_stationlist(self):
- self.stationBox = QtGui.QComboBox()
+ self.stationBox = QtWidgets.QComboBox()
self.stationBox.setMaxVisibleItems(42)
self.trace_layout.addWidget(self.stationBox)
self.fill_stationbox()
@@ -3518,7 +3573,7 @@ class TuneAutopicker(QWidget):
trim_station_components(wfdat, trim_start=True, trim_end=False)
def init_figure_tabs(self):
- self.figure_tabs = QtGui.QTabWidget()
+ self.figure_tabs = QtWidgets.QTabWidget()
self.fill_figure_tabs()
def init_pbwidget(self):
@@ -3529,7 +3584,7 @@ class TuneAutopicker(QWidget):
self.stb_names = ['aicARHfig', 'refSpick', 'el_S1pick', 'el_S2pick']
def add_parameters(self):
- self.paraBox = PylotParaBox(self.parameter, parent=self, windowflag=0)
+ self.paraBox = PylotParaBox(self.parameter, parent=self, windowflag=Qt.Widget)
self.paraBox.set_tune_mode(True)
self.update_eventID()
self.parameter_layout.addWidget(self.paraBox)
@@ -3537,17 +3592,17 @@ class TuneAutopicker(QWidget):
self.tune_layout.insertLayout(1, self.parameter_layout)
def add_buttons(self):
- self.pick_button = QtGui.QPushButton('Pick Trace')
+ self.pick_button = QtWidgets.QPushButton('Pick Trace')
self.pick_button.setStyleSheet('QPushButton{border-color: rgba(110, 200, 0, 255)}')
self.pick_button.clicked.connect(self.call_picker)
- self.close_button = QtGui.QPushButton('Close')
+ self.close_button = QtWidgets.QPushButton('Close')
self.close_button.clicked.connect(self.hide)
self.trace_layout.addWidget(self.pick_button)
self.trace_layout.setStretch(0, 1)
self.parameter_layout.addWidget(self.close_button)
def add_log(self):
- self.listWidget = QtGui.QListWidget()
+ self.listWidget = QtWidgets.QListWidget()
self.figure_tabs.insertTab(4, self.listWidget, 'log')
def add_log_item(self, text):
@@ -3555,10 +3610,7 @@ class TuneAutopicker(QWidget):
self.listWidget.scrollToBottom()
def get_current_event(self):
- path = self.eventBox.currentText().split('*')[0]
- # the path sometimes contains the star that shows that the event has been modified
- # It would be cleaner to find out why/where the star is added to the eventbox and make this a visual only effect
- if path[-1] == "*" : path = path[:-1]
+ path = self.eventBox.currentText()
return self.parent().project.getEventFromPath(path)
def get_current_event_name(self):
@@ -3586,8 +3638,8 @@ class TuneAutopicker(QWidget):
@staticmethod
def gen_tab_widget(name, canvas):
- widget = QtGui.QWidget()
- v_layout = QtGui.QVBoxLayout()
+ widget = QtWidgets.QWidget()
+ v_layout = QtWidgets.QVBoxLayout()
v_layout.addWidget(canvas)
widget.setLayout(v_layout)
return widget
@@ -3630,8 +3682,8 @@ class TuneAutopicker(QWidget):
self.pickDlg.update_picks.connect(lambda: self.parent().setDirty(True))
self.pickDlg.update_picks.connect(self.parent().enableSaveEventAction)
self.pickDlg.update_picks.connect(self.plot_manual_picks_to_figs)
- self.pdlg_widget = QtGui.QWidget(self)
- hl = QtGui.QHBoxLayout()
+ self.pdlg_widget = QtWidgets.QWidget(self)
+ hl = QtWidgets.QHBoxLayout()
self.pdlg_widget.setLayout(hl)
hl.addWidget(self.pickDlg)
@@ -3708,9 +3760,9 @@ class TuneAutopicker(QWidget):
y_top = 0.9 * ax.get_ylim()[1]
y_bot = 0.9 * ax.get_ylim()[0]
- self._manual_pick_plots.append(ax.axvline(mpp, y_bot, y_top,
- color=color, linewidth=2,
- label='manual {} Onset (quality: {})'.format(phase, quality)))
+ self._manual_pick_plots.append(ax.vlines(mpp, y_bot, y_top,
+ color=color, linewidth=2,
+ label='manual {} Onset (quality: {})'.format(phase, quality)))
self._manual_pick_plots.append(ax.plot([mpp - 0.5, mpp + 0.5],
[y_bot, y_bot], linewidth=2,
color=color))
@@ -3761,8 +3813,8 @@ class TuneAutopicker(QWidget):
def fill_figure_tabs(self):
self.clear_all()
- self.p_tabs = QtGui.QTabWidget()
- self.s_tabs = QtGui.QTabWidget()
+ self.p_tabs = QtWidgets.QTabWidget()
+ self.s_tabs = QtWidgets.QTabWidget()
self.tune_layout.insertWidget(0, self.figure_tabs)
self.init_tab_names()
@@ -3814,7 +3866,7 @@ class TuneAutopicker(QWidget):
'savexml': False,
'obspyDMT_wfpath': wfpath}
event = self.get_current_event()
- #self.parent().saveData(event, event.path, '.xml') MP MP uncommented because overwriting pick files in tune mode
+ # self.parent().saveData(event, event.path, '.xml') MP MP uncommented because overwriting pick files in tune mode
for key in self.fig_dict.keys():
if not key == 'plot_style':
self.fig_dict[key].clear()
@@ -3889,17 +3941,17 @@ class TuneAutopicker(QWidget):
self.figure_tabs.setTabEnabled(3, bool)
def _warn(self, message, info=None):
- self.qmb = QtGui.QMessageBox(QtGui.QMessageBox.Icon.Warning,
- 'Warning', message)
+ self.qmb = QtWidgets.QMessageBox(QtWidgets.QMessageBox.Icon.Warning,
+ 'Warning', message)
self.qmb.setDetailedText(str(info))
self.qmb.show()
-class PylotParaBox(QtGui.QWidget):
+class PylotParaBox(QtWidgets.QWidget):
accepted = QtCore.Signal(str)
rejected = QtCore.Signal(str)
- def __init__(self, parameter, parent=None, windowflag=1):
+ def __init__(self, parameter, parent=None, windowflag=Qt.Window):
'''
Generate Widget containing parameters for PyLoT.
@@ -3907,10 +3959,10 @@ class PylotParaBox(QtGui.QWidget):
:type: PylotParameter (object)
'''
- QtGui.QWidget.__init__(self, parent, windowflag)
+ QtWidgets.QWidget.__init__(self, parent, windowflag)
self.parameter = parameter
- self.tabs = QtGui.QTabWidget()
- self.layout = QtGui.QVBoxLayout()
+ self.tabs = QtWidgets.QTabWidget()
+ self.layout = QtWidgets.QVBoxLayout()
self._init_save_buttons()
self._init_tabs()
self._init_dialog_buttons()
@@ -3932,21 +3984,21 @@ class PylotParaBox(QtGui.QWidget):
def center(self):
fm = self.frameGeometry()
- screen = QtGui.QApplication.desktop().screenNumber(QtGui.QApplication.desktop().cursor().pos())
- centerPoint = QtGui.QApplication.desktop().screenGeometry(screen).center()
+ screen = QtWidgets.QApplication.desktop().screenNumber(QtWidgets.QApplication.desktop().cursor().pos())
+ centerPoint = QtWidgets.QApplication.desktop().screenGeometry(screen).center()
fm.moveCenter(centerPoint)
self.move(fm.topLeft())
def _init_sublayouts(self):
- self._main_layout = QtGui.QVBoxLayout()
- self._advanced_layout = QtGui.QVBoxLayout()
+ self._main_layout = QtWidgets.QVBoxLayout()
+ self._advanced_layout = QtWidgets.QVBoxLayout()
self._create_advanced_cb()
def _init_save_buttons(self):
- self._buttons_layout = QtGui.QHBoxLayout()
- self.loadButton = QtGui.QPushButton('&Load settings')
- self.saveButton = QtGui.QPushButton('&Save settings')
- self.defaultsButton = QtGui.QPushButton('&Defaults')
+ self._buttons_layout = QtWidgets.QHBoxLayout()
+ self.loadButton = QtWidgets.QPushButton('&Load settings')
+ self.saveButton = QtWidgets.QPushButton('&Save settings')
+ self.defaultsButton = QtWidgets.QPushButton('&Defaults')
self._buttons_layout.addWidget(self.loadButton)
self._buttons_layout.addWidget(self.saveButton)
self._buttons_layout.addWidget(self.defaultsButton)
@@ -3959,10 +4011,10 @@ class PylotParaBox(QtGui.QWidget):
self.layout.addWidget(self.tabs)
def _init_dialog_buttons(self):
- self._dialog_buttons = QtGui.QHBoxLayout()
- self._okay = QtGui.QPushButton('Ok')
- self._close = QtGui.QPushButton('Close')
- self._apply = QtGui.QPushButton('Apply')
+ self._dialog_buttons = QtWidgets.QHBoxLayout()
+ self._okay = QtWidgets.QPushButton('Ok')
+ self._close = QtWidgets.QPushButton('Close')
+ self._apply = QtWidgets.QPushButton('Apply')
self._dialog_buttons.addWidget(self._okay)
self._dialog_buttons.addWidget(self._close)
self._dialog_buttons.addWidget(self._apply)
@@ -3973,7 +4025,7 @@ class PylotParaBox(QtGui.QWidget):
self.layout.addLayout(self._dialog_buttons)
def _create_advanced_cb(self):
- self._advanced_cb = QtGui.QCheckBox('Enable Advanced Settings')
+ self._advanced_cb = QtWidgets.QCheckBox('Enable Advanced Settings')
self._advanced_layout.insertWidget(0, self._advanced_cb)
self._advanced_cb.toggled.connect(self._toggle_advanced_settings)
@@ -4008,7 +4060,7 @@ class PylotParaBox(QtGui.QWidget):
self._close.show()
def init_boxes(self, parameter_names):
- grid = QtGui.QGridLayout()
+ grid = QtWidgets.QGridLayout()
for index1, name in enumerate(parameter_names):
default_item = self.parameter.get_defaults()[name]
@@ -4030,39 +4082,37 @@ class PylotParaBox(QtGui.QWidget):
self.boxes[name] = boxes
namestring = default_item['namestring'][0]
text = namestring + ' [?]'
- label = QtGui.QLabel(text)
+ label = QtWidgets.QLabel(text)
self.labels[name] = label
label.setToolTip(tooltip)
grid.addWidget(label, index1, 1)
grid.addWidget(box, index1, 2)
return grid
- @staticmethod
- def create_box(typ, tooltip):
+ def create_box(self, typ, tooltip):
if typ == str:
- box = QtGui.QLineEdit()
+ box = QtWidgets.QLineEdit()
elif typ == float:
- box = QtGui.QDoubleSpinBox()
+ box = QDoubleSpinBox()
box.setDecimals(4)
box.setRange(-10e4, 10e4)
elif typ == int:
- box = QtGui.QSpinBox()
+ box = QSpinBox()
elif typ == bool:
- box = QtGui.QCheckBox()
+ box = QtWidgets.QCheckBox()
else:
raise TypeError('Unrecognized type {}'.format(typ))
return box
- @staticmethod
- def create_multi_box(boxes, headline=None):
- box = QtGui.QWidget()
- gl = QtGui.QGridLayout()
+ def create_multi_box(self, boxes, headline=None):
+ box = QtWidgets.QWidget()
+ gl = QtWidgets.QGridLayout()
column = 0
if headline:
for index, item in enumerate(headline):
if not item:
continue
- gl.addWidget(QtGui.QLabel(item), index, 0, 2)
+ gl.addWidget(QtWidgets.QLabel(item), index, 0, Qt.Alignment(2))
column = 1
for index, b in enumerate(boxes):
gl.addWidget(b, index, column)
@@ -4070,8 +4120,8 @@ class PylotParaBox(QtGui.QWidget):
return box, column
def add_tab(self, layout, name):
- widget = QtGui.QWidget()
- scrollA = QtGui.QScrollArea()
+ widget = QtWidgets.QWidget()
+ scrollA = QtWidgets.QScrollArea()
scrollA.setWidgetResizable(True)
scrollA.setWidget(widget)
widget.setLayout(layout)
@@ -4122,8 +4172,8 @@ class PylotParaBox(QtGui.QWidget):
layout.insertWidget(position, groupbox)
def get_groupbox_exclusive(self, name):
- widget = QtGui.QWidget(self, 1)
- layout = QtGui.QVBoxLayout()
+ widget = QtWidgets.QWidget(self, Qt.WindowFlags(1))
+ layout = QtWidgets.QVBoxLayout()
widget.setLayout(layout)
layout.addWidget(self.groupboxes[name])
self._exclusive_widgets.append(widget)
@@ -4131,11 +4181,11 @@ class PylotParaBox(QtGui.QWidget):
def get_groupbox_dialog(self, name):
widget = self.get_groupbox_exclusive(name)
- dialog = QtGui.QDialog(self.parent())
- layout = QtGui.QVBoxLayout()
+ dialog = QtWidgets.QDialog(self.parent())
+ layout = QtWidgets.QVBoxLayout()
dialog.setLayout(layout)
- buttonbox = QtGui.QDialogButtonBox(QDialogButtonBox.Ok |
- QDialogButtonBox.Cancel)
+ buttonbox = QtWidgets.QDialogButtonBox(QDialogButtonBox.Ok |
+ QDialogButtonBox.Cancel)
buttonbox.accepted.connect(dialog.accept)
buttonbox.accepted.connect(self.refresh)
buttonbox.accepted.connect(self.params_from_gui)
@@ -4148,7 +4198,7 @@ class PylotParaBox(QtGui.QWidget):
return dialog
def add_to_layout(self, layout, name, items, position):
- groupbox = QtGui.QGroupBox(name)
+ groupbox = QtWidgets.QGroupBox(name)
groupbox._position = position
groupbox._parentLayout = layout
self.groupboxes[name] = groupbox
@@ -4238,13 +4288,13 @@ class PylotParaBox(QtGui.QWidget):
self.setValue(box, value)
def setValue(self, box, value):
- if type(box) == QtGui.QLineEdit:
+ if type(box) == QtWidgets.QLineEdit:
box.setText(str(value))
- elif type(box) == QtGui.QSpinBox or type(box) == QtGui.QDoubleSpinBox:
+ elif type(box) == QSpinBox or type(box) == QDoubleSpinBox:
if not value:
value = 0.
box.setValue(value)
- elif type(box) == QtGui.QCheckBox:
+ elif type(box) == QtWidgets.QCheckBox:
if value == 'True':
value = True
if value == 'False' or value is None:
@@ -4255,11 +4305,11 @@ class PylotParaBox(QtGui.QWidget):
self.setValue(b, value[index])
def getValue(self, box):
- if type(box) == QtGui.QLineEdit:
+ if type(box) == QtWidgets.QLineEdit:
value = str(box.text())
- elif type(box) == QtGui.QSpinBox or type(box) == QtGui.QDoubleSpinBox:
+ elif type(box) == QSpinBox or type(box) == QDoubleSpinBox:
value = box.value()
- elif type(box) == QtGui.QCheckBox:
+ elif type(box) == QtWidgets.QCheckBox:
value = box.isChecked()
elif type(box) == list:
value = []
@@ -4269,7 +4319,7 @@ class PylotParaBox(QtGui.QWidget):
return value
def openFile(self):
- fd = QtGui.QFileDialog()
+ fd = QtWidgets.QFileDialog()
fname = fd.getOpenFileName(self, 'Browse for settings file.',
filter='PyLoT input file (*.in)')
if fname[0]:
@@ -4281,7 +4331,7 @@ class PylotParaBox(QtGui.QWidget):
return
def saveFile(self):
- fd = QtGui.QFileDialog()
+ fd = QtWidgets.QFileDialog()
fname = fd.getSaveFileName(self, 'Browse for settings file.',
filter='PyLoT input file (*.in)')[0]
if fname:
@@ -4308,18 +4358,18 @@ class PylotParaBox(QtGui.QWidget):
if hasattr(self, '_exclusive_dialog'):
self._exclusive_dialog.close()
self._exclusive_widgets = []
- QtGui.QWidget.show(self)
+ QtWidgets.QWidget.show(self)
def close(self):
self.rejected.emit('reject')
- QtGui.QWidget.close(self)
+ QtWidgets.QWidget.close(self)
def accept(self):
self.accepted.emit('accept')
def _warn(self, message):
- self.qmb = QtGui.QMessageBox(QtGui.QMessageBox.Icon.Warning,
- 'Warning', message)
+ self.qmb = QtWidgets.QMessageBox(QtWidgets.QMessageBox.Icon.Warning,
+ 'Warning', message)
self.qmb.show()
@@ -4558,7 +4608,7 @@ class PropertiesDlg(QDialog):
self._current_values.append(values)
def reset_current(self):
- for values in self._current_values():
+ for values in self._current_values:
self.setValues(values)
@staticmethod
@@ -4604,7 +4654,7 @@ class InputsTab(PropTab):
self.fullNameEdit.setText(fulluser)
except TypeError as e:
self.fullNameEdit.setText(fulluser[0])
-
+ # TODO: check settings and substitute datapath for root
# information about data structure
dataroot = settings.value("data/dataRoot")
curstructure = settings.value("data/Structure")
@@ -4626,8 +4676,6 @@ class InputsTab(PropTab):
self.cuttimesLayout.addWidget(self.tstopBox, 10)
self.cuttimesLayout.addWidget(QLabel('[s]'), 0)
-
-
from pylot.core.util.structure import DATASTRUCTURE
self.structureSelect.addItems(list(DATASTRUCTURE.keys()))
@@ -4636,7 +4684,7 @@ class InputsTab(PropTab):
self.structureSelect.setCurrentIndex(dsind)
- layout = QFormLayout()
+ layout = QtWidgets.QFormLayout()
layout.addRow("Data root directory: ", self.dataDirEdit)
layout.addRow("Full name for user '{0}': ".format(pylot_user), self.fullNameEdit)
layout.addRow("Data structure: ", self.structureSelect)
@@ -4691,7 +4739,7 @@ class InputsTab(PropTab):
"data/Structure": self.structureSelect.setCurrentIndex(index),
"tstart": self.tstartBox.setValue(0),
"tstop": self.tstopBox.setValue(1e6),
- "autosaveXML": self.autosaveXML_checkbox.setChecked(True),}
+ "autosaveXML": self.autosaveXML_checkbox.setChecked(True), }
return values
@@ -4738,7 +4786,7 @@ class PhasesTab(PropTab):
self.PphasesEdit = QLineEdit()
self.SphasesEdit = QLineEdit()
- self.pickDefaultsButton = QtGui.QPushButton('Choose default phases...')
+ self.pickDefaultsButton = QtWidgets.QPushButton('Choose default phases...')
PphasesLabel = QLabel("P Phases to pick")
SphasesLabel = QLabel("S Phases to pick")
notes_label = QLabel(
@@ -4752,7 +4800,7 @@ class PhasesTab(PropTab):
self.PphasesEdit.setText("%s" % Pphases)
self.SphasesEdit.setText("%s" % Sphases)
- self.main_layout = QtGui.QHBoxLayout()
+ self.main_layout = QtWidgets.QHBoxLayout()
layout = QGridLayout()
layout.addWidget(PphasesLabel, 0, 0)
layout.addWidget(SphasesLabel, 1, 0)
@@ -4841,7 +4889,7 @@ class GraphicsTab(PropTab):
def add_style_settings(self):
styles = self.pylot_mainwindow._styles
active_stylename = self.pylot_mainwindow._stylename
- label = QtGui.QLabel('Application style (might require Application restart):')
+ label = QtWidgets.QLabel('Application style (might require Application restart):')
self.style_cb = QComboBox()
for stylename, style in styles.items():
self.style_cb.addItem(stylename, style)
@@ -4857,7 +4905,7 @@ class GraphicsTab(PropTab):
if not nth_sample:
nth_sample = 1
- self.spinbox_nth_sample = QtGui.QSpinBox()
+ self.spinbox_nth_sample = QSpinBox()
label = QLabel('nth sample')
label.setToolTip('Plot every nth sample (to speed up plotting)')
self.spinbox_nth_sample.setMinimum(1)
@@ -5140,10 +5188,10 @@ class FilterOptionsDialog(QDialog):
self.connectButtons()
def setupUi(self):
- self.main_layout = QtGui.QVBoxLayout()
- self.filter_layout = QtGui.QHBoxLayout()
- self.groupBoxes = {'P': QtGui.QGroupBox('P Filter'),
- 'S': QtGui.QGroupBox('S Filter')}
+ self.main_layout = QtWidgets.QVBoxLayout()
+ self.filter_layout = QtWidgets.QHBoxLayout()
+ self.groupBoxes = {'P': QtWidgets.QGroupBox('P Filter'),
+ 'S': QtWidgets.QGroupBox('S Filter')}
settings = QSettings()
overwriteFilter = get_Bool(settings.value('useGuiFilter'))
@@ -5158,7 +5206,7 @@ class FilterOptionsDialog(QDialog):
for phase in ['P', 'S']:
groupbox = self.groupBoxes[phase]
- box_layout = QtGui.QVBoxLayout()
+ box_layout = QtWidgets.QVBoxLayout()
groupbox.setLayout(box_layout)
self.filter_layout.addWidget(groupbox)
@@ -5298,8 +5346,8 @@ class FilterOptionsWidget(QWidget):
self.freqGroupBox = QGroupBox("Frequency range")
self.freqGroupLayout = QGridLayout()
- self.freqGroupLayout.addWidget(self.manuLabel, 0, 1, 80)
- self.freqGroupLayout.addWidget(self.autoLabel, 0, 2, 80)
+ self.freqGroupLayout.addWidget(self.manuLabel, 0, 1)
+ self.freqGroupLayout.addWidget(self.autoLabel, 0, 2)
self.freqGroupLayout.addWidget(self.freqminLabel, 1, 0)
self.freqGroupLayout.addWidget(self.freqminSpinBox, 1, 1)
self.freqGroupLayout.addWidget(self.autoMinFreq, 1, 2)
@@ -5463,7 +5511,7 @@ class LoadDataDlg(QDialog):
class HelpForm(QDialog):
def __init__(self, parent=None,
page=QUrl('https://ariadne.geophysik.ruhr-uni-bochum.de/trac/PyLoT/')):
- super(HelpForm, self).__init__(parent, 1)
+ super(HelpForm, self).__init__(parent)
self.setAttribute(Qt.WA_DeleteOnClose)
self.setAttribute(Qt.WA_GroupLeader)
diff --git a/requirements.txt b/requirements.txt
new file mode 100644
index 00000000..b5e3fe81
--- /dev/null
+++ b/requirements.txt
@@ -0,0 +1,12 @@
+# This file may be used to create an environment using:
+# $ conda create --name --file
+# platform: win-64
+cartopy=0.20.2
+matplotlib-base=3.3.4
+numpy=1.22.3
+obspy=1.3.0
+pyqtgraph=0.12.4
+pyside2=5.13.2
+python=3.8.12
+qt=5.12.9
+scipy=1.8.0
\ No newline at end of file
diff --git a/setup.py b/setup.py
deleted file mode 100644
index 09f8e357..00000000
--- a/setup.py
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-from distutils.core import setup
-
-setup(
- name='PyLoT',
- version='0.2',
- packages=['pylot', 'pylot.core', 'pylot.core.loc', 'pylot.core.pick',
- 'pylot.core.io', 'pylot.core.util', 'pylot.core.active',
- 'pylot.core.analysis', 'pylot.testing'],
- requires=['obspy', 'PySide', 'matplotlib', 'numpy', 'scipy', 'pyqtgraph'],
- url='dummy',
- license='LGPLv3',
- author='Sebastian Wehling-Benatelli',
- author_email='sebastian.wehling@rub.de',
- description='Comprehensive Python picking and Location Toolbox for seismological data.'
-)
diff --git a/tests/PyLoT_e0019.048.13.xml b/tests/PyLoT_e0019.048.13.xml
new file mode 100644
index 00000000..fd653f2a
--- /dev/null
+++ b/tests/PyLoT_e0019.048.13.xml
@@ -0,0 +1,1735 @@
+
+
+
+
+ smi:local/ef3aef4e-d2d8-4ad1-9de2-ae479d7ca69c
+
+ 2017 Non-Global, Insheim, autoPyLoT, min1d032016, Oct-Tree, incl. Stat.corr.
+
+
+ Marcel Paffrath RUB
+ 2019-06-18T16:42:26.000000Z
+ NLLoc:v6.00.0
+
+
+
+
+ 49.163
+ 0.00046421200739
+
+
+ 8.16073
+ 0.000753747936377
+
+
+ 3722.53
+ 68.5167132895
+ 68.0
+
+ from location
+
+ 68
+ 53
+ -1
+ 27
+ -1
+ 0.037278
+ 79.7007
+ 80.5059
+ -
+ 0.00827339904581
+ 0.0724656262939
+ 0.0426887281933
+
+
+ Note: Depth/Latitude/Longitude errors are calculated from covariance matrix as 1D marginal (Lon/Lat errors as great circle degrees) while OriginUncertainty min/max horizontal errors are calculated from 2D error ellipsoid and are therefore seemingly higher compared to 1D errors. Error estimates can be reconstructed from the following original NonLinLoc error statistics line:
+STATISTICS ExpectX 0.477716 Y 1.0484 Z 3.75905 CovXX 0.00702461 XY 0.00207683 XZ 1.28953e-05 YY 0.00266442 YZ 0.00160997 ZZ 0.00469454 EllAz1 160.459 Dip1 23.349 Len1 0.064808 Az2 310.589 Dip2 63.5354 Len2 0.135629 Len3 1.678650e-01
+
+
+ Marcel Paffrath RUB
+ 2019-10-23T15:13:14.000000Z
+ NLLoc:v6.00.0
+
+
+ uncertainty ellipse
+ 64.9393
+ 134.416
+ 68.1948
+ 68.0
+
+
+ smi:local/9eb9346c-a77f-46e2-8b0a-67bfcd505324
+ P
+ 46.8
+ 0.00827375877445
+
+ 156.7
+
+ 0.0325
+ 1.8785
+
+
+ smi:local/3509b878-4543-45eb-8a23-8457e5bec074
+ S
+ 46.8
+ 0.00827375877445
+
+ 152.0
+
+ 0.0476
+ 0.2859
+
+
+ smi:local/a41b22b3-45f2-4253-9a09-6301c3d8afa3
+ P
+ 64.6
+ 0.0140483028061
+
+ 144.1
+
+ 0.0158
+ 0.1918
+
+
+ smi:local/7b8fbe6b-a511-44bc-87d8-c82f75c9dac9
+ S
+ 64.6
+ 0.0140483028061
+
+ 136.8
+
+ -0.121
+ 0.5441
+
+
+ smi:local/4b01b7e6-ab09-48eb-b30a-6eff5421ade7
+ P
+ 45.5
+ 0.0140797790623
+
+ 144.2
+
+ 0.0222
+ 2.123
+
+
+ smi:local/42ab9322-b57b-4397-8c77-d0a67bd63d31
+ S
+ 45.5
+ 0.0140797790623
+
+ 136.9
+
+ 0.0001
+ 2.5296
+
+
+ smi:local/f70730f6-be92-4c8e-a8a4-135c11d3afd9
+ P
+ 347.7
+ 0.0161751984041
+
+ 139.6
+
+ 0.0314
+ 1.7408
+
+
+ smi:local/e7106a97-2b96-4f9e-925f-b05f9ff1e1f2
+ S
+ 347.7
+ 0.0161751984041
+
+ 130.8
+
+ 0.0069
+ 0.8411
+
+
+ smi:local/0ee1fad9-dc17-4143-b5f2-7c13e227c752
+ P
+ 96.9
+ 0.0270965599863
+
+ 118.6
+
+ 0.004
+ 1.5884
+
+
+ smi:local/beef17c4-ac24-48c3-8ed3-4dfd41164054
+ S
+ 96.9
+ 0.0270965599863
+
+ 108.7
+
+ -0.0798
+ 0.4144
+
+
+ smi:local/ec5af9ac-a554-4188-87f9-a11ca486f7b8
+ P
+ 194.8
+ 0.0280327537781
+
+ 117.0
+
+ 0.0022
+ 2.3198
+
+
+ smi:local/cd84d5a0-33fb-449b-b2f9-d5b0dc3d6f77
+ S
+ 194.8
+ 0.0280327537781
+
+ 107.4
+
+ -0.0249
+ 0.531
+
+
+ smi:local/768c794b-33ee-4b33-8b09-7f22e0b8f863
+ P
+ 33.3
+ 0.0296128618397
+
+ 114.8
+
+ -0.0077
+ 1.838
+
+
+ smi:local/2a9f5a43-a6ed-4c5d-be28-e121b5f1cd88
+ S
+ 33.3
+ 0.0296128618397
+
+ 105.5
+
+ -0.1241
+ 0.5469
+
+
+ smi:local/5cd96d25-efeb-448e-b833-c437e0e340e0
+ P
+ 339.2
+ 0.0303952716368
+
+ 113.9
+
+ -0.007
+ 2.0092
+
+
+ smi:local/2cabf2f4-3469-47dc-b5c9-9ac541a1d328
+ S
+ 339.2
+ 0.0303952716368
+
+ 104.6
+
+ -0.1354
+ 0.0555
+
+
+ smi:local/7201e0b3-ffe1-427a-9fb2-494633819fc3
+ P
+ 296.3
+ 0.0308224493997
+
+ 113.4
+
+ 0.021
+ 1.9757
+
+
+ smi:local/daf1eb4e-65ad-443b-8fc7-1f3f3920029e
+ S
+ 296.3
+ 0.0308224493997
+
+ 104.3
+
+ -0.0031
+ 2.4451
+
+
+ smi:local/e2ed4ec9-9afc-45b2-8a15-81c2805fc5bc
+ P
+ 42.0
+ 0.0314762562072
+
+ 112.5
+
+ -0.9908
+ 0.0
+
+
+ smi:local/1d05e3c5-7ffe-4d21-902d-427d1ff53c58
+ S
+ 42.0
+ 0.0314762562072
+
+ 103.7
+
+ -0.2474
+ 0.0
+
+
+ smi:local/aad56e84-3a47-4e2f-b5a1-912f5218e915
+ P
+ 275.3
+ 0.0338846394678
+
+ 110.1
+
+ 0.0506
+ 0.2825
+
+
+ smi:local/1176d79f-5802-4232-957a-53593a48ab59
+ S
+ 275.3
+ 0.0338846394678
+
+ 101.9
+
+ -0.0262
+ 0.5256
+
+
+ smi:local/6a86a988-d836-4c0e-83b7-38b476585f50
+ P
+ 141.4
+ 0.0345438422049
+
+ 109.2
+
+ -0.1982
+ 0.1577
+
+
+ smi:local/75c2ace4-0662-43eb-b915-b34fbb270aed
+ S
+ 141.4
+ 0.0345438422049
+
+ 101.4
+
+ -0.1225
+ 0.5822
+
+
+ smi:local/335d27d3-3a12-4567-a673-e2e2f7e39d81
+ P
+ 12.5
+ 0.0346238818279
+
+ 109.3
+
+ -28.5493
+ 0.0
+
+
+ smi:local/4bfbb944-a14c-4f4c-a98d-180132732efd
+ S
+ 12.5
+ 0.0346238818279
+
+ 101.4
+
+ -30.3321
+ 0.0
+
+
+ smi:local/d92ac253-8623-4e60-9f40-de70aa56f9ae
+ P
+ 55.0
+ 0.0348945776313
+
+ 109.1
+
+ -0.0002
+ 2.6446
+
+
+ smi:local/d8a61dea-b580-4232-9888-b3e22d430d7c
+ S
+ 55.0
+ 0.0348945776313
+
+ 101.2
+
+ -0.3054
+ 0.1585
+
+
+ smi:local/b0e035d7-26c8-4721-907f-5326f573fa6b
+ P
+ 151.9
+ 0.0426889979898
+
+ 103.3
+
+ -0.007
+ 1.9892
+
+
+ smi:local/1b51523f-7d82-4054-9175-6b3080d98d54
+ S
+ 151.9
+ 0.0426889979898
+
+ 97.9
+
+ -0.193
+ 0.1577
+
+
+ smi:local/e451ee86-0d52-431a-9afe-ab4cfd41ba82
+ P
+ 320.6
+ 0.044087443087
+
+ 102.5
+
+ -0.0003
+ 2.6367
+
+
+ smi:local/db4f0cc7-d4b5-44f3-9962-dc349ef333d6
+ S
+ 320.6
+ 0.044087443087
+
+ 97.4
+
+ -0.0711
+ 0.3056
+
+
+ smi:local/0fdf5ba1-7b3a-46b9-ab99-8695ee78a028
+ P
+ 134.0
+ 0.0454364254958
+
+ 101.8
+
+ 0.0332
+ 1.6518
+
+
+ smi:local/b5038aff-af5d-4826-baa8-7df243c844c6
+ S
+ 134.0
+ 0.0454364254958
+
+ 97.1
+
+ -0.1515
+ 0.2111
+
+
+ smi:local/ebbe37a8-5c48-4474-845a-40c87f0b422e
+ P
+ 22.4
+ 0.0464679473778
+
+ 101.3
+
+ 0.0224
+ 1.9944
+
+
+ smi:local/2771d1ac-f447-4937-84ec-41fe8377e0c3
+ S
+ 22.4
+ 0.0464679473778
+
+ 96.9
+
+ -0.0027
+ 2.4389
+
+
+ smi:local/46e17b3d-c7e0-4957-827a-4839452a4499
+ P
+ 100.2
+ 0.0485210986041
+
+ 100.3
+
+ -0.038
+ 0.3376
+
+
+ smi:local/7c7a61d9-899a-410d-b114-c527ce10d2d0
+ S
+ 100.2
+ 0.0485210986041
+
+ 96.4
+
+ -0.006
+ 2.0579
+
+
+ smi:local/9dd51cc4-1e3b-4073-bacb-52451c6be670
+ P
+ 137.0
+ 0.0486991642821
+
+ 100.3
+
+ 0.0209
+ 1.8984
+
+
+ smi:local/fcc088f8-6882-4c1e-8e93-478e57bbbc02
+ S
+ 137.0
+ 0.0486991642821
+
+ 96.5
+
+ -0.375
+ 0.0
+
+
+ smi:local/fde40d4d-dd41-4fd4-abfd-af2a9ead20e3
+ P
+ 152.2
+ 0.0543900714044
+
+ 98.2
+
+ 0.1885
+ 0.0
+
+
+ smi:local/6299e7b4-84e0-4074-9062-d5303ebae304
+ S
+ 152.2
+ 0.0543900714044
+
+ 95.5
+
+ -0.1481
+ 0.2264
+
+
+ smi:local/c21f0ffb-f205-4628-bfd5-20399915b6d2
+ P
+ 195.6
+ 0.0583344959679
+
+ 97.1
+
+ -0.4477
+ 0.0
+
+
+ smi:local/209a1bff-133c-43d8-91a7-8db058764903
+ S
+ 195.6
+ 0.0583344959679
+
+ 94.9
+
+ -0.025
+ 0.5136
+
+
+ smi:local/b4b55f1c-1adb-41c0-b8b9-d80fec768240
+ P
+ 171.9
+ 0.0600261199087
+
+ 96.7
+
+ -0.519
+ 0.0
+
+
+ smi:local/a6496421-ce03-4a09-ace0-6b7e0707e2e2
+ S
+ 171.9
+ 0.0600261199087
+
+ 94.7
+
+ -0.9593
+ 0.0
+
+
+ smi:local/726e568d-ca8c-4385-9af8-963cdb3fab68
+ P
+ 141.5
+ 0.0656127057246
+
+ 95.7
+
+ 0.0233
+ 1.7242
+
+
+ smi:local/15da4297-8ddf-4598-8c0b-453a2e4e90c0
+ S
+ 141.5
+ 0.0656127057246
+
+ 94.2
+
+ -0.3
+ 0.1585
+
+
+ smi:local/0a9faae3-dfff-4f43-939f-84cc1d6f6fec
+ P
+ 131.1
+ 0.0720320633477
+
+ 94.9
+
+ -0.0007
+ 2.6002
+
+
+ smi:local/99a719a3-83c5-4a48-8482-7e6ca8907158
+ S
+ 131.1
+ 0.0720320633477
+
+ 93.7
+
+ -4.0494
+ 0.0
+
+
+ smi:local/a2dd276c-dc83-4147-9826-f0c624ddc929
+ P
+ 107.1
+ 0.0724655363617
+
+ 94.8
+
+ -0.0814
+ 0.3501
+
+
+ smi:local/850a9d14-a2c4-42e6-a9b8-cd02e655c03c
+ S
+ 107.1
+ 0.0724655363617
+
+ 93.7
+
+ -0.0684
+ 0.2647
+
+
+ smi:local/4770a7f9-fc0d-4040-9459-9abc0393460d
+ P
+ 152.7
+ 0.0727110511601
+
+ 94.8
+
+ -35.393
+ 0.0
+
+
+ smi:local/b9116582-8324-446f-a793-0fbf64e4715f
+ S
+ 152.7
+ 0.0727110511601
+
+ 93.7
+
+ -37.5515
+ 0.0
+
+
+ smi:local/518dfe6f-d48b-46df-8997-eebe13962383
+ P
+ 78.9
+ 0.0765538523822
+
+ 94.5
+
+ -0.0405
+ 0.3269
+
+
+ smi:local/8cb5e43b-5dec-4a8f-a64d-09d00dd49862
+ S
+ 78.9
+ 0.0765538523822
+
+ 93.4
+
+ 0.2346
+ 0.0
+
+
+ smi:local/95c5230a-268f-4ec5-b317-e78fc7edafbe
+ P
+ 262.2
+ 0.077090747381
+
+ 94.4
+
+ -38.7361
+ 0.0
+
+
+ smi:local/98cfbee5-a483-4384-aa13-7be6a37c42bf
+ S
+ 262.2
+ 0.077090747381
+
+ 93.4
+
+ -41.2764
+ 0.0
+
+
+ smi:local/cd35bb76-ea0f-4001-83b2-fdf5a9ce2e37
+ P
+ 344.4
+ 0.0791178182807
+
+ 94.2
+
+ 0.0328
+ 1.5501
+
+
+ smi:local/d4da1423-4bb9-45bd-bceb-b7d15515f0aa
+ S
+ 344.4
+ 0.0791178182807
+
+ 93.3
+
+ 0.0303
+ 1.396
+
+
+ smi:local/9d7c03bd-c31a-405d-aeaf-a199ef608e1a
+ P
+ 105.7
+ 0.124408553676
+
+ 92.2
+
+ -30.6789
+ 0.0
+
+
+ smi:local/7a28a6df-94c6-4bc0-8c63-0326efec96c7
+ S
+ 105.7
+ 0.124408553676
+
+ 91.9
+
+ -33.8042
+ 0.0
+
+
+ smi:local/09130d2b-995b-4dcd-a136-623d0a42e356
+ P
+ 0.0
+ 0.0
+
+ 0.0
+
+ 0.0
+ 0.0
+
+
+ smi:local/8d70ecd2-7566-418b-93b1-ee97cb1ee755
+ P
+ 0.0
+ 0.0
+
+ 0.0
+
+ 0.0
+ 0.0
+
+
+ smi:local/ab6981b4-7891-452c-bfa8-31c4698479dd
+ P
+ 0.0
+ 0.0
+
+ 0.0
+
+ 0.0
+ 0.0
+
+
+ smi:local/a675bd0b-5f25-4576-8376-d7e2edd1a4b4
+ S
+ 0.0
+ 0.0
+
+ 0.0
+
+ 0.0
+ 0.0
+
+
+ smi:local/5b3f0af0-9687-4477-8109-0d52d8e256a7
+ S
+ 0.0
+ 0.0
+
+ 0.0
+
+ 0.0
+ 0.0
+
+
+ smi:local/66e1e068-f04b-4bc9-9b6b-3dec0ebaf86e
+ S
+ 0.0
+ 0.0
+
+ 0.0
+
+ 0.0
+ 0.0
+
+
+
+
+ 0.996637322062
+
+ Mw
+ smi:local/6b0e21af-f315-464f-a2d8-ad98be5fd4b5
+ 23
+ 79.7007
+
+
+
+ 3.58933832203
+
+ ML
+ smi:local/6b0e21af-f315-464f-a2d8-ad98be5fd4b5
+ 22
+ 79.7007
+
+
+ smi:local/ef3aef4e-d2d8-4ad1-9de2-ae479d7ca69c
+
+ -0.332990311889
+
+ Mw
+
+
+
+ smi:local/ef3aef4e-d2d8-4ad1-9de2-ae479d7ca69c
+
+ 0.18824563479
+
+ Mw
+
+
+
+ smi:local/ef3aef4e-d2d8-4ad1-9de2-ae479d7ca69c
+
+ 0.722100395992
+
+ Mw
+
+
+
+ smi:local/ef3aef4e-d2d8-4ad1-9de2-ae479d7ca69c
+
+ -0.179235300345
+
+ Mw
+
+
+
+ smi:local/ef3aef4e-d2d8-4ad1-9de2-ae479d7ca69c
+
+ -0.0430500185351
+
+ Mw
+
+
+
+ smi:local/ef3aef4e-d2d8-4ad1-9de2-ae479d7ca69c
+
+ 1.06860882517
+
+ Mw
+
+
+
+
+
+ smi:local/e0a56274-69da-4e08-a865-9c30df1444ba
+ smi:local/auto
+ P
+ positive
+
+
+
+
+ smi:local/7cf31005-aa47-4e25-8964-65097be85f63
+ smi:local/auto
+ S
+ positive
+
+
+
+
+ smi:local/f790b61e-599d-4648-872e-9a978e4ddb7d
+ smi:local/auto
+ P
+ positive
+
+
+
+
+ smi:local/574ac5e0-71fa-460d-932a-ea914ae7c001
+ smi:local/auto
+ S
+ positive
+
+
+
+
+ smi:local/fb611385-9908-42cd-a80d-692cdb71565b
+ smi:local/auto
+ P
+ positive
+
+
+
+
+ smi:local/64b7b43a-aff3-4500-9c70-72f019103e1c
+ smi:local/auto
+ S
+ positive
+
+
+
+
+ smi:local/bb758c4a-e033-4a20-92d0-96ac0a91e986
+ smi:local/auto
+ P
+ positive
+
+
+
+
+ smi:local/01d552b3-b487-4b2b-96e4-920eac0896bb
+ smi:local/auto
+ S
+ positive
+
+
+
+
+ smi:local/c7d4ee4c-d117-47df-88bc-52b97a28a059
+ smi:local/auto
+ P
+ positive
+
+
+
+
+ smi:local/8c8e2849-b009-4576-a947-810ba146d94b
+ smi:local/auto
+ S
+ positive
+
+
+
+
+ smi:local/fef6099f-cc83-4a1f-bcea-79db46dde3d3
+ smi:local/auto
+ P
+ positive
+
+
+
+
+ smi:local/f850cf4c-bfa2-4279-9996-0045cfecd92d
+ smi:local/auto
+ S
+ positive
+
+
+
+
+ smi:local/95d62390-f0a8-4978-b566-8d5c3e41dcb6
+ smi:local/auto
+ P
+ positive
+
+
+
+
+ smi:local/b77fe84e-d045-402e-9742-cbc1bb17a4a8
+ smi:local/auto
+ S
+ positive
+
+
+
+
+ smi:local/59914bbc-649a-4743-8a37-8db7153530ea
+ smi:local/auto
+ P
+ positive
+
+
+
+
+ smi:local/d872b42c-44d5-42a4-8c48-8fa2e0c2ab86
+ smi:local/auto
+ S
+ positive
+
+
+
+
+ smi:local/0f5a70ab-e6d1-45e5-8f4d-a50a597cfaac
+ smi:local/auto
+ P
+ positive
+
+
+
+
+ smi:local/9687129e-ecd1-4c4f-b03a-12d35a100a70
+ smi:local/auto
+ S
+ positive
+
+
+
+
+ smi:local/ef5ebb70-85c1-4338-b043-a4f1b44a6077
+ smi:local/auto
+ P
+ undecidable
+
+
+
+
+ smi:local/ac7aa4b6-272a-43d5-97c3-f840bb751971
+ smi:local/auto
+ S
+ undecidable
+
+
+
+
+ smi:local/56ac2ef7-8c24-4fc4-8131-21a8d3882968
+ smi:local/auto
+ P
+ undecidable
+
+
+
+
+ smi:local/32abb906-df42-48e0-a9cd-53e262766b2c
+ smi:local/auto
+ S
+ undecidable
+
+
+
+
+ smi:local/65a556dd-776e-466c-afdf-1f6eee5cc041
+ smi:local/auto
+ P
+ positive
+
+
+
+
+ smi:local/6ea57ab1-252b-40c7-8cd5-9e4da7e3a89b
+ smi:local/auto
+ S
+ positive
+
+
+
+
+ smi:local/3360cfe0-6a41-422d-ab5d-883ddf951bd7
+ smi:local/auto
+ P
+ positive
+
+
+
+
+ smi:local/ac755950-366d-4255-ac58-7577d90763a5
+ smi:local/auto
+ S
+ positive
+
+
+
+
+ smi:local/35aab2e9-8039-4b0d-a6e0-bb7f46c49ae5
+ smi:local/auto
+ P
+ positive
+
+
+
+
+ smi:local/106cf45e-6803-4cde-a019-ae3e85a57380
+ smi:local/auto
+ S
+ positive
+
+
+
+
+ smi:local/98d645ce-efbe-49ad-8efb-32ee7f4d4b04
+ smi:local/auto
+ P
+ positive
+
+
+
+
+ smi:local/99a30f03-cce8-4ca1-9d75-26dff71249be
+ smi:local/auto
+ S
+ positive
+
+
+
+
+ smi:local/b0ba1139-1603-4ab1-a76e-11b57ec1e1bf
+ smi:local/auto
+ P
+ positive
+
+
+
+
+ smi:local/fc89b2ed-19a0-4385-bec5-ed4c2df34f99
+ smi:local/auto
+ S
+ positive
+
+
+
+
+ smi:local/f9e7faba-c9db-4ef0-8fbe-78b585cc2450
+ smi:local/auto
+ P
+ positive
+
+
+
+
+ smi:local/bbafb091-ed59-4ca8-ae7e-51fd57c4532c
+ smi:local/auto
+ S
+ positive
+
+
+
+
+ smi:local/02479a1b-112e-4364-b1fc-d18957906999
+ smi:local/auto
+ P
+ positive
+
+
+
+
+ smi:local/c2cc46e1-de56-4a4f-a534-228f821204a3
+ smi:local/auto
+ S
+ positive
+
+
+
+
+ smi:local/765521f0-24de-4aa1-9106-7d85b2d93a9f
+ smi:local/auto
+ P
+ positive
+
+
+
+
+ smi:local/876cbbcb-d9b7-4c5b-b315-dd1a5b822573
+ smi:local/auto
+ S
+ positive
+
+
+
+
+ smi:local/b7b2db28-82cf-4ed1-b20d-407bee7d69ca
+ smi:local/auto
+ P
+ positive
+
+
+
+
+ smi:local/ffec4cec-ffaf-4f7d-a52e-695ce13db287
+ smi:local/auto
+ S
+ positive
+
+
+
+
+ smi:local/97eab993-631d-4d9e-81ad-b9b56540bef0
+ smi:local/auto
+ P
+ positive
+
+
+
+
+ smi:local/2f6eb30f-2c9d-47db-be4f-dd1b4875a6eb
+ smi:local/auto
+ S
+ positive
+
+
+
+
+ smi:local/e97718b6-a4b2-482b-9b79-3b2e218f8e73
+ smi:local/auto
+ P
+ positive
+
+
+
+
+ smi:local/3df734a8-1817-4fca-848d-095847245b58
+ smi:local/auto
+ S
+ positive
+
+
+
+
+ smi:local/123ff3ff-bdfb-4387-8afd-e432fc1bb66d
+ smi:local/auto
+ P
+ positive
+
+
+
+
+ smi:local/e02a231f-8bf9-4778-892a-ce451493ca25
+ smi:local/auto
+ S
+ positive
+
+
+
+
+ smi:local/8a00eabb-31f5-43f4-8d38-942492d3785e
+ smi:local/auto
+ P
+ positive
+
+
+
+
+ smi:local/c67578e7-0d57-4665-9c28-3a4b2772e621
+ smi:local/auto
+ S
+ positive
+
+
+
+
+ smi:local/76cd7615-cc63-4d09-8e60-335d13ab1519
+ smi:local/auto
+ P
+ positive
+
+
+
+
+ smi:local/f59da821-f218-414d-955e-3d056d65d0c3
+ smi:local/auto
+ S
+ positive
+
+
+
+
+ smi:local/8d2fcf23-d439-4e35-87a4-fbb300b7644e
+ smi:local/auto
+ P
+ positive
+
+
+
+
+ smi:local/c3773958-1d37-4800-ad22-79013a7838d7
+ smi:local/auto
+ S
+ positive
+
+
+
+
+ smi:local/2eb69827-257a-4a31-a474-f877660020b5
+ smi:local/auto
+ P
+ positive
+
+
+
+
+ smi:local/5272f1ac-0003-4865-ada9-2e976fc71f2c
+ smi:local/auto
+ S
+ positive
+
+
+
+
+ smi:local/05044fc0-34d6-4fe4-9ce5-190342197733
+ smi:local/auto
+ P
+ positive
+
+
+
+
+ smi:local/caea33fa-c8a6-47cc-83d9-456f093acdce
+ smi:local/auto
+ S
+ positive
+
+
+
+
+ smi:local/e1072847-5ffa-479a-8e9d-2a78e3dbd120
+ smi:local/auto
+ P
+ positive
+
+
+
+
+ smi:local/3ace5f6c-ced1-451e-bd3c-f49aa64bb070
+ smi:local/auto
+ S
+ positive
+
+
+
+
+ smi:local/d1f73b73-88af-4d14-8b0f-a8d179551d1c
+ smi:local/auto
+ P
+ positive
+
+
+
+
+ smi:local/80ddc356-a2bf-43e3-9e2f-bffe37bd861e
+ smi:local/auto
+ S
+ positive
+
+
+
+
+ smi:local/5e304b02-d30b-4010-ac17-925ffc0641ab
+ smi:local/auto
+ P
+ positive
+
+
+
+
+ smi:local/987c6631-367d-4db5-a789-7408c940c314
+ smi:local/auto
+ S
+ positive
+
+
+
+
+ smi:local/931b5820-b76a-4554-bdee-3dc79c94b4d0
+ smi:local/manual
+ P
+ undecidable
+
+
+
+
+ smi:local/944c8d37-eff4-4b84-acf9-2652064a2780
+ smi:local/manual
+ P
+ positive
+
+
+
+
+ smi:local/5858810c-a4eb-496f-a581-a881e2776bba
+ smi:local/manual
+ S
+ undecidable
+
+
+
+
+ smi:local/bandpass//freqmin/2.0//freqmax/30.0//corners/4//zerophase/False
+ smi:local/manual
+ P
+ negative
+
+
+
+
+ smi:local/bandpass//freqmin/2.0//freqmax/15.0//corners/4//zerophase/False
+ smi:local/manual
+ S
+ undecidable
+
+
+
+
diff --git a/tests/testPickingResults.py b/tests/testPickingResults.py
index 5d863f8d..ebb3000c 100644
--- a/tests/testPickingResults.py
+++ b/tests/testPickingResults.py
@@ -1,6 +1,8 @@
import unittest
+
from pylot.core.pick.autopick import PickingResults
+
class TestPickingResults(unittest.TestCase):
def setUp(self):
@@ -70,9 +72,9 @@ class TestPickingResults(unittest.TestCase):
curr_len = len(self.pr)
except Exception:
self.fail("test_dunder_attributes overwrote an instance internal dunder method")
- self.assertEqual(prev_len+1, curr_len) # +1 for the added __len__ key/value-pair
+ self.assertEqual(prev_len + 1, curr_len) # +1 for the added __len__ key/value-pair
self.pr.__len__ = 42
self.assertEqual(42, self.pr['__len__'])
- self.assertEqual(prev_len+1, curr_len, msg="__len__ was overwritten")
+ self.assertEqual(prev_len + 1, curr_len, msg="__len__ was overwritten")
diff --git a/tests/test_Metadata/test_Metadata.py b/tests/test_Metadata/test_Metadata.py
index 72927d2f..5aab85ec 100644
--- a/tests/test_Metadata/test_Metadata.py
+++ b/tests/test_Metadata/test_Metadata.py
@@ -1,5 +1,6 @@
import os
import unittest
+
from obspy import UTCDateTime
from obspy.io.xseed import Parser
from obspy.io.xseed.utils import SEEDParserException
@@ -27,7 +28,7 @@ class TestMetadata(unittest.TestCase):
result = {}
for channel in ('Z', 'N', 'E'):
with HidePrints():
- coords = self.m.get_coordinates(self.station_id+channel, time=self.time)
+ coords = self.m.get_coordinates(self.station_id + channel, time=self.time)
result[channel] = coords
self.assertDictEqual(result[channel], expected[channel])
@@ -42,7 +43,7 @@ class TestMetadata(unittest.TestCase):
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
self.assertDictEqual(result[channel], expected[channel])
@@ -145,7 +146,7 @@ class TestMetadata_read_single_file(unittest.TestCase):
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)
+ 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
@@ -172,7 +173,7 @@ class TestMetadata_read_single_file(unittest.TestCase):
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)
+ 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)
@@ -197,7 +198,8 @@ class TestMetadataMultipleTime(unittest.TestCase):
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 = 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)
@@ -299,7 +301,8 @@ Channels:
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 = 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)
diff --git a/tests/test_PickingParameters.py b/tests/test_PickingParameters.py
deleted file mode 100644
index 9ddca10a..00000000
--- a/tests/test_PickingParameters.py
+++ /dev/null
@@ -1,35 +0,0 @@
-import unittest
-from pylot.core.pick.autopick import PickingParameters
-
-class TestPickingParameters(unittest.TestCase):
-
- def setUp(self):
- self.simple_dict = {'a': 3, 'b': 14}
- self.nested_dict = {'a': self.simple_dict, 'b': self.simple_dict}
-
- def assertParameterEquality(self, dic, instance):
- """Test wether all parameters given in dic are found in instance"""
- for key, value in dic.items():
- self.assertEqual(value, getattr(instance, key))
-
- def test_add_params_from_dict_simple(self):
- pickparam = PickingParameters()
- pickparam.add_params_from_dict(self.simple_dict)
- self.assertParameterEquality(self.simple_dict, pickparam)
-
- def test_add_params_from_dict_nested(self):
- pickparam = PickingParameters()
- pickparam.add_params_from_dict(self.nested_dict)
- self.assertParameterEquality(self.nested_dict, pickparam)
-
- def test_init(self):
- pickparam = PickingParameters(self.simple_dict)
- self.assertParameterEquality(self.simple_dict, pickparam)
-
- def test_dot_access(self):
- pickparam = PickingParameters(self.simple_dict)
- self.assertEqual(pickparam.a, self.simple_dict['a'])
-
-
-if __name__ == '__main__':
- unittest.main()
diff --git a/tests/test_autopickstation/e0001.024.16/PyLoT_e0001.024.16.xml b/tests/test_autopickstation/e0001.024.16/PyLoT_e0001.024.16.xml
index 5231a039..f2176c20 100644
--- a/tests/test_autopickstation/e0001.024.16/PyLoT_e0001.024.16.xml
+++ b/tests/test_autopickstation/e0001.024.16/PyLoT_e0001.024.16.xml
@@ -1,21 +1,21 @@
-
-
-
-
-
- 59.66
-
-
- -153.45
-
-
- 128.0
-
-
-
-
+
+
+
+
+
+ 59.66
+
+
+ -153.45
+
+
+ 128.0
+
+
+
+
diff --git a/tests/test_autopickstation/test_autopickstation.py b/tests/test_autopickstation/test_autopickstation.py
index 08002377..218c1acc 100644
--- a/tests/test_autopickstation/test_autopickstation.py
+++ b/tests/test_autopickstation/test_autopickstation.py
@@ -1,12 +1,13 @@
-import unittest
-from unittest import skip
-import obspy
-from obspy import UTCDateTime
import os
import sys
-from pylot.core.pick.autopick import autopickstation
-from pylot.core.io.inputs import PylotParameter
+import unittest
+
+import obspy
+from obspy import UTCDateTime
+
from pylot.core.io.data import Data
+from pylot.core.io.inputs import PylotParameter
+from pylot.core.pick.autopick import autopickstation
from pylot.core.util.utils import trim_station_components
@@ -93,51 +94,100 @@ class TestAutopickStation(unittest.TestCase):
self.inputfile_taupy_disabled = os.path.join(os.path.dirname(__file__), 'autoPyLoT_global_taupy_false.in')
self.pickparam_taupy_enabled = PylotParameter(fnin=self.inputfile_taupy_enabled)
self.pickparam_taupy_disabled = PylotParameter(fnin=self.inputfile_taupy_disabled)
- self.xml_file = os.path.join(os.path.dirname(__file__),self.event_id, 'PyLoT_'+self.event_id+'.xml')
+ self.xml_file = os.path.join(os.path.dirname(__file__), self.event_id, 'PyLoT_' + self.event_id + '.xml')
self.data = Data(evtdata=self.xml_file)
# create origin for taupy testing
- self.origin = [obspy.core.event.origin.Origin(magnitude=7.1, latitude=59.66, longitude=-153.45, depth=128.0, time=UTCDateTime("2016-01-24T10:30:30.0"))]
+ self.origin = [obspy.core.event.origin.Origin(magnitude=7.1, latitude=59.66, longitude=-153.45, depth=128.0,
+ time=UTCDateTime("2016-01-24T10:30:30.0"))]
# mocking metadata since reading it takes a long time to read from file
self.metadata = MockMetadata()
# show complete diff when difference in results dictionaries are found
self.maxDiff = None
- #@skip("Works")
+ # @skip("Works")
def test_autopickstation_taupy_disabled_gra1(self):
- expected = {'P': {'picker': 'auto', 'snrdb': 15.405649120980094, 'weight': 0, 'Mo': None, 'marked': [], 'Mw': None, 'fc': None, 'snr': 34.718816470730317, 'mpp': UTCDateTime(2016, 1, 24, 10, 41, 31, 690000), 'w0': None, 'spe': 0.93333333333333235, 'network': u'GR', 'epp': UTCDateTime(2016, 1, 24, 10, 41, 28, 890000), 'lpp': UTCDateTime(2016, 1, 24, 10, 41, 32, 690000), 'fm': 'D', 'channel': u'LHZ'}, 'S': {'picker': 'auto', 'snrdb': 10.669661906545489, 'network': u'GR', 'weight': 0, 'Ao': None, 'lpp': UTCDateTime(2016, 1, 24, 10, 50, 30, 690000), 'snr': 11.667187857573905, 'epp': UTCDateTime(2016, 1, 24, 10, 50, 21, 690000), 'mpp': UTCDateTime(2016, 1, 24, 10, 50, 29, 690000), 'fm': None, 'spe': 2.6666666666666665, 'channel': u'LHE'}}
+ expected = {
+ 'P': {'picker': 'auto', 'snrdb': 15.405649120980094, 'weight': 0, 'Mo': None, 'marked': [], 'Mw': None,
+ 'fc': None, 'snr': 34.718816470730317, 'mpp': UTCDateTime(2016, 1, 24, 10, 41, 31, 690000),
+ 'w0': None, 'spe': 0.93333333333333235, 'network': u'GR',
+ 'epp': UTCDateTime(2016, 1, 24, 10, 41, 28, 890000),
+ 'lpp': UTCDateTime(2016, 1, 24, 10, 41, 32, 690000), 'fm': 'D', 'channel': u'LHZ'},
+ 'S': {'picker': 'auto', 'snrdb': 10.669661906545489, 'network': u'GR', 'weight': 0, 'Ao': None,
+ 'lpp': UTCDateTime(2016, 1, 24, 10, 50, 30, 690000), 'snr': 11.667187857573905,
+ 'epp': UTCDateTime(2016, 1, 24, 10, 50, 21, 690000),
+ 'mpp': UTCDateTime(2016, 1, 24, 10, 50, 29, 690000), 'fm': None, 'spe': 2.6666666666666665,
+ 'channel': u'LHE'}}
with HidePrints():
- result, station = autopickstation(wfstream=self.gra1, pickparam=self.pickparam_taupy_disabled, metadata=(None, None))
+ result, station = autopickstation(wfstream=self.gra1, pickparam=self.pickparam_taupy_disabled,
+ metadata=(None, None))
self.assertDictContainsSubset(expected=expected['P'], actual=result['P'])
self.assertDictContainsSubset(expected=expected['S'], actual=result['S'])
self.assertEqual('GRA1', station)
def test_autopickstation_taupy_enabled_gra1(self):
- expected = {'P': {'picker': 'auto', 'snrdb': 15.599905299126778, 'weight': 0, 'Mo': None, 'marked': [], 'Mw': None, 'fc': None, 'snr': 36.307013769185403, 'mpp': UTCDateTime(2016, 1, 24, 10, 41, 27, 690000), 'w0': None, 'spe': 0.93333333333333235, 'network': u'GR', 'epp': UTCDateTime(2016, 1, 24, 10, 41, 24, 890000), 'lpp': UTCDateTime(2016, 1, 24, 10, 41, 28, 690000), 'fm': 'U', 'channel': u'LHZ'}, 'S': {'picker': 'auto', 'snrdb': 10.669661906545489, 'network': u'GR', 'weight': 0, 'Ao': None, 'lpp': UTCDateTime(2016, 1, 24, 10, 50, 30, 690000), 'snr': 11.667187857573905, 'epp': UTCDateTime(2016, 1, 24, 10, 50, 21, 690000), 'mpp': UTCDateTime(2016, 1, 24, 10, 50, 29, 690000), 'fm': None, 'spe': 2.6666666666666665, 'channel': u'LHE'}}
+ expected = {
+ 'P': {'picker': 'auto', 'snrdb': 15.599905299126778, 'weight': 0, 'Mo': None, 'marked': [], 'Mw': None,
+ 'fc': None, 'snr': 36.307013769185403, 'mpp': UTCDateTime(2016, 1, 24, 10, 41, 27, 690000),
+ 'w0': None, 'spe': 0.93333333333333235, 'network': u'GR',
+ 'epp': UTCDateTime(2016, 1, 24, 10, 41, 24, 890000),
+ 'lpp': UTCDateTime(2016, 1, 24, 10, 41, 28, 690000), 'fm': 'U', 'channel': u'LHZ'},
+ 'S': {'picker': 'auto', 'snrdb': 10.669661906545489, 'network': u'GR', 'weight': 0, 'Ao': None,
+ 'lpp': UTCDateTime(2016, 1, 24, 10, 50, 30, 690000), 'snr': 11.667187857573905,
+ 'epp': UTCDateTime(2016, 1, 24, 10, 50, 21, 690000),
+ 'mpp': UTCDateTime(2016, 1, 24, 10, 50, 29, 690000), 'fm': None, 'spe': 2.6666666666666665,
+ 'channel': u'LHE'}}
with HidePrints():
- result, station = autopickstation(wfstream=self.gra1, pickparam=self.pickparam_taupy_enabled, metadata=self.metadata, origin=self.origin)
+ result, station = autopickstation(wfstream=self.gra1, pickparam=self.pickparam_taupy_enabled,
+ metadata=self.metadata, origin=self.origin)
self.assertDictContainsSubset(expected=expected['P'], actual=result['P'])
self.assertDictContainsSubset(expected=expected['S'], actual=result['S'])
self.assertEqual('GRA1', station)
def test_autopickstation_taupy_disabled_gra2(self):
- expected = {'P': {'picker': 'auto', 'snrdb': None, 'weight': 9, 'Mo': None, 'marked': 'shortsignallength', 'Mw': None, 'fc': None, 'snr': None, 'mpp': UTCDateTime(2016, 1, 24, 10, 36, 59, 150000), 'w0': None, 'spe': None, 'network': u'GR', 'epp': UTCDateTime(2016, 1, 24, 10, 36, 43, 150000), 'lpp': UTCDateTime(2016, 1, 24, 10, 37, 15, 150000), 'fm': 'N', 'channel': u'LHZ'}, 'S': {'picker': 'auto', 'snrdb': None, 'network': u'GR', 'weight': 4, 'Ao': None, 'lpp': UTCDateTime(2016, 1, 24, 10, 37, 15, 150000), 'snr': None, 'epp': UTCDateTime(2016, 1, 24, 10, 36, 43, 150000), 'mpp': UTCDateTime(2016, 1, 24, 10, 36, 59, 150000), 'fm': None, 'spe': None, 'channel': u'LHE'}}
+ expected = {
+ 'P': {'picker': 'auto', 'snrdb': None, 'weight': 9, 'Mo': None, 'marked': 'shortsignallength', 'Mw': None,
+ 'fc': None, 'snr': None, 'mpp': UTCDateTime(2016, 1, 24, 10, 36, 59, 150000), 'w0': None, 'spe': None,
+ 'network': u'GR', 'epp': UTCDateTime(2016, 1, 24, 10, 36, 43, 150000),
+ 'lpp': UTCDateTime(2016, 1, 24, 10, 37, 15, 150000), 'fm': 'N', 'channel': u'LHZ'},
+ 'S': {'picker': 'auto', 'snrdb': None, 'network': u'GR', 'weight': 4, 'Ao': None,
+ 'lpp': UTCDateTime(2016, 1, 24, 10, 37, 15, 150000), 'snr': None,
+ 'epp': UTCDateTime(2016, 1, 24, 10, 36, 43, 150000),
+ 'mpp': UTCDateTime(2016, 1, 24, 10, 36, 59, 150000), 'fm': None, 'spe': None, 'channel': u'LHE'}}
with HidePrints():
- result, station = autopickstation(wfstream=self.gra2, pickparam=self.pickparam_taupy_disabled, metadata=(None, None))
+ result, station = autopickstation(wfstream=self.gra2, pickparam=self.pickparam_taupy_disabled,
+ metadata=(None, None))
self.assertDictContainsSubset(expected=expected['P'], actual=result['P'])
self.assertDictContainsSubset(expected=expected['S'], actual=result['S'])
self.assertEqual('GRA2', station)
def test_autopickstation_taupy_enabled_gra2(self):
- expected = {'P': {'picker': 'auto', 'snrdb': 13.957959025719253, 'weight': 0, 'Mo': None, 'marked': [], 'Mw': None, 'fc': None, 'snr': 24.876879503607871, 'mpp': UTCDateTime(2016, 1, 24, 10, 41, 29, 150000), 'w0': None, 'spe': 1.0, 'network': u'GR', 'epp': UTCDateTime(2016, 1, 24, 10, 41, 26, 150000), 'lpp': UTCDateTime(2016, 1, 24, 10, 41, 30, 150000), 'fm': None, 'channel': u'LHZ'}, 'S': {'picker': 'auto', 'snrdb': 10.573236990555648, 'network': u'GR', 'weight': 1, 'Ao': None, 'lpp': UTCDateTime(2016, 1, 24, 10, 50, 34, 150000), 'snr': 11.410999834108294, 'epp': UTCDateTime(2016, 1, 24, 10, 50, 21, 150000), 'mpp': UTCDateTime(2016, 1, 24, 10, 50, 33, 150000), 'fm': None, 'spe': 4.666666666666667, 'channel': u'LHE'}}
+ expected = {
+ 'P': {'picker': 'auto', 'snrdb': 13.957959025719253, 'weight': 0, 'Mo': None, 'marked': [], 'Mw': None,
+ 'fc': None, 'snr': 24.876879503607871, 'mpp': UTCDateTime(2016, 1, 24, 10, 41, 29, 150000),
+ 'w0': None, 'spe': 1.0, 'network': u'GR', 'epp': UTCDateTime(2016, 1, 24, 10, 41, 26, 150000),
+ 'lpp': UTCDateTime(2016, 1, 24, 10, 41, 30, 150000), 'fm': None, 'channel': u'LHZ'},
+ 'S': {'picker': 'auto', 'snrdb': 10.573236990555648, 'network': u'GR', 'weight': 1, 'Ao': None,
+ 'lpp': UTCDateTime(2016, 1, 24, 10, 50, 34, 150000), 'snr': 11.410999834108294,
+ 'epp': UTCDateTime(2016, 1, 24, 10, 50, 21, 150000),
+ 'mpp': UTCDateTime(2016, 1, 24, 10, 50, 33, 150000), 'fm': None, 'spe': 4.666666666666667,
+ 'channel': u'LHE'}}
with HidePrints():
- result, station = autopickstation(wfstream=self.gra2, pickparam=self.pickparam_taupy_enabled, metadata=self.metadata, origin = self.origin)
+ result, station = autopickstation(wfstream=self.gra2, pickparam=self.pickparam_taupy_enabled,
+ metadata=self.metadata, origin=self.origin)
self.assertDictContainsSubset(expected=expected['P'], actual=result['P'])
self.assertDictContainsSubset(expected=expected['S'], actual=result['S'])
self.assertEqual('GRA2', station)
def test_autopickstation_taupy_disabled_ech(self):
- expected = {'P': {'picker': 'auto', 'snrdb': None, 'weight': 9, 'Mo': None, 'marked': 'SinsteadP', 'Mw': None, 'fc': None, 'snr': None, 'mpp': UTCDateTime(2016, 1, 24, 10, 26, 57), 'w0': None, 'spe': None, 'network': u'G', 'epp': UTCDateTime(2016, 1, 24, 10, 26, 41), 'lpp': UTCDateTime(2016, 1, 24, 10, 27, 13), 'fm': 'N', 'channel': u'LHZ'}, 'S': {'picker': 'auto', 'snrdb': None, 'network': u'G', 'weight': 4, 'Ao': None, 'lpp': UTCDateTime(2016, 1, 24, 10, 27, 13), 'snr': None, 'epp': UTCDateTime(2016, 1, 24, 10, 26, 41), 'mpp': UTCDateTime(2016, 1, 24, 10, 26, 57), 'fm': None, 'spe': None, 'channel': u'LHE'}}
+ expected = {'P': {'picker': 'auto', 'snrdb': None, 'weight': 9, 'Mo': None, 'marked': 'SinsteadP', 'Mw': None,
+ 'fc': None, 'snr': None, 'mpp': UTCDateTime(2016, 1, 24, 10, 26, 57), 'w0': None, 'spe': None,
+ 'network': u'G', 'epp': UTCDateTime(2016, 1, 24, 10, 26, 41),
+ 'lpp': UTCDateTime(2016, 1, 24, 10, 27, 13), 'fm': 'N', 'channel': u'LHZ'},
+ 'S': {'picker': 'auto', 'snrdb': None, 'network': u'G', 'weight': 4, 'Ao': None,
+ 'lpp': UTCDateTime(2016, 1, 24, 10, 27, 13), 'snr': None,
+ 'epp': UTCDateTime(2016, 1, 24, 10, 26, 41), 'mpp': UTCDateTime(2016, 1, 24, 10, 26, 57),
+ 'fm': None, 'spe': None, 'channel': u'LHE'}}
with HidePrints():
result, station = autopickstation(wfstream=self.ech, pickparam=self.pickparam_taupy_disabled)
self.assertDictContainsSubset(expected=expected['P'], actual=result['P'])
@@ -146,16 +196,32 @@ class TestAutopickStation(unittest.TestCase):
def test_autopickstation_taupy_enabled_ech(self):
# this station has a long time of before the first onset, so taupy will help during picking
- expected = {'P': {'picker': 'auto', 'snrdb': 9.9753586609166316, 'weight': 0, 'Mo': None, 'marked': [], 'Mw': None, 'fc': None, 'snr': 9.9434218804137107, 'mpp': UTCDateTime(2016, 1, 24, 10, 41, 34), 'w0': None, 'spe': 1.6666666666666667, 'network': u'G', 'epp': UTCDateTime(2016, 1, 24, 10, 41, 29), 'lpp': UTCDateTime(2016, 1, 24, 10, 41, 35), 'fm': None, 'channel': u'LHZ'}, 'S': {'picker': 'auto', 'snrdb': 12.698999454169567, 'network': u'G', 'weight': 0, 'Ao': None, 'lpp': UTCDateTime(2016, 1, 24, 10, 50, 44), 'snr': 18.616581906366577, 'epp': UTCDateTime(2016, 1, 24, 10, 50, 33), 'mpp': UTCDateTime(2016, 1, 24, 10, 50, 43), 'fm': None, 'spe': 3.3333333333333335, 'channel': u'LHE'}}
+ expected = {
+ 'P': {'picker': 'auto', 'snrdb': 9.9753586609166316, 'weight': 0, 'Mo': None, 'marked': [], 'Mw': None,
+ 'fc': None, 'snr': 9.9434218804137107, 'mpp': UTCDateTime(2016, 1, 24, 10, 41, 34), 'w0': None,
+ 'spe': 1.6666666666666667, 'network': u'G', 'epp': UTCDateTime(2016, 1, 24, 10, 41, 29),
+ 'lpp': UTCDateTime(2016, 1, 24, 10, 41, 35), 'fm': None, 'channel': u'LHZ'},
+ 'S': {'picker': 'auto', 'snrdb': 12.698999454169567, 'network': u'G', 'weight': 0, 'Ao': None,
+ 'lpp': UTCDateTime(2016, 1, 24, 10, 50, 44), 'snr': 18.616581906366577,
+ 'epp': UTCDateTime(2016, 1, 24, 10, 50, 33), 'mpp': UTCDateTime(2016, 1, 24, 10, 50, 43), 'fm': None,
+ 'spe': 3.3333333333333335, 'channel': u'LHE'}}
with HidePrints():
- result, station = autopickstation(wfstream=self.ech, pickparam=self.pickparam_taupy_enabled, metadata=self.metadata, origin=self.origin)
+ result, station = autopickstation(wfstream=self.ech, pickparam=self.pickparam_taupy_enabled,
+ metadata=self.metadata, origin=self.origin)
self.assertDictContainsSubset(expected=expected['P'], actual=result['P'])
self.assertDictContainsSubset(expected=expected['S'], actual=result['S'])
self.assertEqual('ECH', station)
def test_autopickstation_taupy_disabled_fiesa(self):
# this station has a long time of before the first onset, so taupy will help during picking
- expected = {'P': {'picker': 'auto', 'snrdb': None, 'weight': 9, 'Mo': None, 'marked': 'SinsteadP', 'Mw': None, 'fc': None, 'snr': None, 'mpp': UTCDateTime(2016, 1, 24, 10, 35, 58), 'w0': None, 'spe': None, 'network': u'CH', 'epp': UTCDateTime(2016, 1, 24, 10, 35, 42), 'lpp': UTCDateTime(2016, 1, 24, 10, 36, 14), 'fm': 'N', 'channel': u'LHZ'}, 'S': {'picker': 'auto', 'snrdb': None, 'network': u'CH', 'weight': 4, 'Ao': None, 'lpp': UTCDateTime(2016, 1, 24, 10, 36, 14), 'snr': None, 'epp': UTCDateTime(2016, 1, 24, 10, 35, 42), 'mpp': UTCDateTime(2016, 1, 24, 10, 35, 58), 'fm': None, 'spe': None, 'channel': u'LHE'}}
+ expected = {'P': {'picker': 'auto', 'snrdb': None, 'weight': 9, 'Mo': None, 'marked': 'SinsteadP', 'Mw': None,
+ 'fc': None, 'snr': None, 'mpp': UTCDateTime(2016, 1, 24, 10, 35, 58), 'w0': None, 'spe': None,
+ 'network': u'CH', 'epp': UTCDateTime(2016, 1, 24, 10, 35, 42),
+ 'lpp': UTCDateTime(2016, 1, 24, 10, 36, 14), 'fm': 'N', 'channel': u'LHZ'},
+ 'S': {'picker': 'auto', 'snrdb': None, 'network': u'CH', 'weight': 4, 'Ao': None,
+ 'lpp': UTCDateTime(2016, 1, 24, 10, 36, 14), 'snr': None,
+ 'epp': UTCDateTime(2016, 1, 24, 10, 35, 42), 'mpp': UTCDateTime(2016, 1, 24, 10, 35, 58),
+ 'fm': None, 'spe': None, 'channel': u'LHE'}}
with HidePrints():
result, station = autopickstation(wfstream=self.fiesa, pickparam=self.pickparam_taupy_disabled)
self.assertDictContainsSubset(expected=expected['P'], actual=result['P'])
@@ -164,9 +230,18 @@ class TestAutopickStation(unittest.TestCase):
def test_autopickstation_taupy_enabled_fiesa(self):
# this station has a long time of before the first onset, so taupy will help during picking
- expected = {'P': {'picker': 'auto', 'snrdb': 13.921049277904373, 'weight': 0, 'Mo': None, 'marked': [], 'Mw': None, 'fc': None, 'snr': 24.666352170589487, 'mpp': UTCDateTime(2016, 1, 24, 10, 41, 47), 'w0': None, 'spe': 1.2222222222222285, 'network': u'CH', 'epp': UTCDateTime(2016, 1, 24, 10, 41, 43, 333333), 'lpp': UTCDateTime(2016, 1, 24, 10, 41, 48), 'fm': None, 'channel': u'LHZ'}, 'S': {'picker': 'auto', 'snrdb': 10.893086316477728, 'network': u'CH', 'weight': 0, 'Ao': None, 'lpp': UTCDateTime(2016, 1, 24, 10, 51, 5), 'snr': 12.283118216397849, 'epp': UTCDateTime(2016, 1, 24, 10, 50, 59, 333333), 'mpp': UTCDateTime(2016, 1, 24, 10, 51, 2), 'fm': None, 'spe': 2.8888888888888764, 'channel': u'LHE'}}
+ expected = {
+ 'P': {'picker': 'auto', 'snrdb': 13.921049277904373, 'weight': 0, 'Mo': None, 'marked': [], 'Mw': None,
+ 'fc': None, 'snr': 24.666352170589487, 'mpp': UTCDateTime(2016, 1, 24, 10, 41, 47), 'w0': None,
+ 'spe': 1.2222222222222285, 'network': u'CH', 'epp': UTCDateTime(2016, 1, 24, 10, 41, 43, 333333),
+ 'lpp': UTCDateTime(2016, 1, 24, 10, 41, 48), 'fm': None, 'channel': u'LHZ'},
+ 'S': {'picker': 'auto', 'snrdb': 10.893086316477728, 'network': u'CH', 'weight': 0, 'Ao': None,
+ 'lpp': UTCDateTime(2016, 1, 24, 10, 51, 5), 'snr': 12.283118216397849,
+ 'epp': UTCDateTime(2016, 1, 24, 10, 50, 59, 333333), 'mpp': UTCDateTime(2016, 1, 24, 10, 51, 2),
+ 'fm': None, 'spe': 2.8888888888888764, 'channel': u'LHE'}}
with HidePrints():
- result, station = autopickstation(wfstream=self.fiesa, pickparam=self.pickparam_taupy_enabled, metadata=self.metadata, origin=self.origin)
+ result, station = autopickstation(wfstream=self.fiesa, pickparam=self.pickparam_taupy_enabled,
+ metadata=self.metadata, origin=self.origin)
self.assertDictContainsSubset(expected=expected['P'], actual=result['P'])
self.assertDictContainsSubset(expected=expected['S'], actual=result['S'])
self.assertEqual('FIESA', station)
@@ -176,7 +251,8 @@ class TestAutopickStation(unittest.TestCase):
wfstream = self.gra1.copy()
wfstream = wfstream.select(channel='*E') + wfstream.select(channel='*N')
with HidePrints():
- result, station = autopickstation(wfstream=wfstream, pickparam=self.pickparam_taupy_disabled, metadata=(None, None))
+ result, station = autopickstation(wfstream=wfstream, pickparam=self.pickparam_taupy_disabled,
+ metadata=(None, None))
self.assertIsNone(result)
self.assertEqual('GRA1', station)
@@ -184,17 +260,36 @@ class TestAutopickStation(unittest.TestCase):
"""Picking on a stream without horizontal traces should still pick the P phase on the vertical component"""
wfstream = self.gra1.copy()
wfstream = wfstream.select(channel='*Z')
- expected = {'P': {'picker': 'auto', 'snrdb': 15.405649120980094, 'network': u'GR', 'weight': 0, 'Ao': None, 'Mo': None, 'marked': [], 'lpp': UTCDateTime(2016, 1, 24, 10, 41, 32, 690000), 'Mw': None, 'fc': None, 'snr': 34.718816470730317, 'epp': UTCDateTime(2016, 1, 24, 10, 41, 28, 890000), 'mpp': UTCDateTime(2016, 1, 24, 10, 41, 31, 690000), 'w0': None, 'spe': 0.9333333333333323, 'fm': 'D', 'channel': u'LHZ'}, 'S': {'picker': 'auto', 'snrdb': None, 'network': None, 'weight': 4, 'Mo': None, 'Ao': None, 'lpp': None, 'Mw': None, 'fc': None, 'snr': None, 'marked': [], 'mpp': None, 'w0': None, 'spe': None, 'epp': None, 'fm': 'N', 'channel': None}}
+ expected = {
+ 'P': {'picker': 'auto', 'snrdb': 15.405649120980094, 'network': u'GR', 'weight': 0, 'Ao': None, 'Mo': None,
+ 'marked': [], 'lpp': UTCDateTime(2016, 1, 24, 10, 41, 32, 690000), 'Mw': None, 'fc': None,
+ 'snr': 34.718816470730317, 'epp': UTCDateTime(2016, 1, 24, 10, 41, 28, 890000),
+ 'mpp': UTCDateTime(2016, 1, 24, 10, 41, 31, 690000), 'w0': None, 'spe': 0.9333333333333323, 'fm': 'D',
+ 'channel': u'LHZ'},
+ 'S': {'picker': 'auto', 'snrdb': None, 'network': None, 'weight': 4, 'Mo': None, 'Ao': None, 'lpp': None,
+ 'Mw': None, 'fc': None, 'snr': None, 'marked': [], 'mpp': None, 'w0': None, 'spe': None, 'epp': None,
+ 'fm': 'N', 'channel': None}}
with HidePrints():
- result, station = autopickstation(wfstream=wfstream, pickparam=self.pickparam_taupy_disabled, metadata=(None, None))
+ result, station = autopickstation(wfstream=wfstream, pickparam=self.pickparam_taupy_disabled,
+ metadata=(None, None))
self.assertEqual(expected, result)
self.assertEqual('GRA1', station)
def test_autopickstation_a106_taupy_enabled(self):
"""This station has invalid values recorded on both N and E component, but a pick can still be found on Z"""
- expected = {'P': {'picker': 'auto', 'snrdb': 12.862128789922826, 'network': u'Z3', 'weight': 0, 'Ao': None, 'Mo': None, 'marked': [], 'lpp': UTCDateTime(2016, 1, 24, 10, 41, 34), 'Mw': None, 'fc': None, 'snr': 19.329155459132608, 'epp': UTCDateTime(2016, 1, 24, 10, 41, 30), 'mpp': UTCDateTime(2016, 1, 24, 10, 41, 33), 'w0': None, 'spe': 1.6666666666666667, 'fm': None, 'channel': u'LHZ'}, 'S': {'picker': 'auto', 'snrdb': None, 'network': u'Z3', 'weight': 4, 'Ao': None, 'Mo': None, 'marked': [], 'lpp': UTCDateTime(2016, 1, 24, 10, 28, 56), 'Mw': None, 'fc': None, 'snr': None, 'epp': UTCDateTime(2016, 1, 24, 10, 28, 24), 'mpp': UTCDateTime(2016, 1, 24, 10, 28, 40), 'w0': None, 'spe': None, 'fm': None, 'channel': u'LHE'}}
+ expected = {
+ 'P': {'picker': 'auto', 'snrdb': 12.862128789922826, 'network': u'Z3', 'weight': 0, 'Ao': None, 'Mo': None,
+ 'marked': [], 'lpp': UTCDateTime(2016, 1, 24, 10, 41, 34), 'Mw': None, 'fc': None,
+ 'snr': 19.329155459132608, 'epp': UTCDateTime(2016, 1, 24, 10, 41, 30),
+ 'mpp': UTCDateTime(2016, 1, 24, 10, 41, 33), 'w0': None, 'spe': 1.6666666666666667, 'fm': None,
+ 'channel': u'LHZ'},
+ 'S': {'picker': 'auto', 'snrdb': None, 'network': u'Z3', 'weight': 4, 'Ao': None, 'Mo': None, 'marked': [],
+ 'lpp': UTCDateTime(2016, 1, 24, 10, 28, 56), 'Mw': None, 'fc': None, 'snr': None,
+ 'epp': UTCDateTime(2016, 1, 24, 10, 28, 24), 'mpp': UTCDateTime(2016, 1, 24, 10, 28, 40), 'w0': None,
+ 'spe': None, 'fm': None, 'channel': u'LHE'}}
with HidePrints():
- result, station = autopickstation(wfstream=self.a106, pickparam=self.pickparam_taupy_enabled, metadata=self.metadata, origin=self.origin)
+ result, station = autopickstation(wfstream=self.a106, pickparam=self.pickparam_taupy_enabled,
+ metadata=self.metadata, origin=self.origin)
self.assertEqual(expected, result)
def test_autopickstation_station_missing_in_metadata(self):
@@ -202,10 +297,22 @@ class TestAutopickStation(unittest.TestCase):
relative to the theoretical onset to one relative to the traces starttime, eg never negative.
"""
self.pickparam_taupy_enabled.setParamKV('pstart', -100) # modify starttime to be relative to theoretical onset
- expected = {'P': {'picker': 'auto', 'snrdb': 14.464757855513506, 'network': u'Z3', 'weight': 0, 'Mo': None, 'Ao': None, 'lpp': UTCDateTime(2016, 1, 24, 10, 41, 39, 605000), 'Mw': None, 'fc': None, 'snr': 27.956048519707181, 'marked': [], 'mpp': UTCDateTime(2016, 1, 24, 10, 41, 38, 605000), 'w0': None, 'spe': 1.6666666666666667, 'epp': UTCDateTime(2016, 1, 24, 10, 41, 35, 605000), 'fm': None, 'channel': u'LHZ'}, 'S': {'picker': 'auto', 'snrdb': 10.112844176301248, 'network': u'Z3', 'weight': 1, 'Mo': None, 'Ao': None, 'lpp': UTCDateTime(2016, 1, 24, 10, 50, 51, 605000), 'Mw': None, 'fc': None, 'snr': 10.263238413785425, 'marked': [], 'mpp': UTCDateTime(2016, 1, 24, 10, 50, 48, 605000), 'w0': None, 'spe': 4.666666666666667, 'epp': UTCDateTime(2016, 1, 24, 10, 50, 40, 605000), 'fm': None, 'channel': u'LHE'}}
+ expected = {
+ 'P': {'picker': 'auto', 'snrdb': 14.464757855513506, 'network': u'Z3', 'weight': 0, 'Mo': None, 'Ao': None,
+ 'lpp': UTCDateTime(2016, 1, 24, 10, 41, 39, 605000), 'Mw': None, 'fc': None,
+ 'snr': 27.956048519707181, 'marked': [], 'mpp': UTCDateTime(2016, 1, 24, 10, 41, 38, 605000),
+ 'w0': None, 'spe': 1.6666666666666667, 'epp': UTCDateTime(2016, 1, 24, 10, 41, 35, 605000),
+ 'fm': None, 'channel': u'LHZ'},
+ 'S': {'picker': 'auto', 'snrdb': 10.112844176301248, 'network': u'Z3', 'weight': 1, 'Mo': None, 'Ao': None,
+ 'lpp': UTCDateTime(2016, 1, 24, 10, 50, 51, 605000), 'Mw': None, 'fc': None,
+ 'snr': 10.263238413785425, 'marked': [], 'mpp': UTCDateTime(2016, 1, 24, 10, 50, 48, 605000),
+ 'w0': None, 'spe': 4.666666666666667, 'epp': UTCDateTime(2016, 1, 24, 10, 50, 40, 605000), 'fm': None,
+ 'channel': u'LHE'}}
with HidePrints():
- result, station = autopickstation(wfstream = self.a005a, pickparam=self.pickparam_taupy_enabled, metadata=self.metadata, origin=self.origin)
+ result, station = autopickstation(wfstream=self.a005a, pickparam=self.pickparam_taupy_enabled,
+ metadata=self.metadata, origin=self.origin)
self.assertEqual(expected, result)
+
if __name__ == '__main__':
unittest.main()
diff --git a/tests/test_get_qualities_from_xml.py b/tests/test_get_qualities_from_xml.py
new file mode 100644
index 00000000..cf8125ea
--- /dev/null
+++ b/tests/test_get_qualities_from_xml.py
@@ -0,0 +1,33 @@
+import unittest
+
+from pylot.core.io.phases import getQualitiesfromxml
+
+
+class TestQualityFromXML(unittest.TestCase):
+ def setUp(self):
+ self.path = '.'
+ self.ErrorsP = [0.02, 0.04, 0.08, 0.16]
+ self.ErrorsS = [0.04, 0.08, 0.16, 0.32]
+ self.test0_result = [[0.0136956521739, 0.0126, 0.0101612903226, 0.00734848484849, 0.0135069444444,
+ 0.00649659863946, 0.0129513888889, 0.0122747747748, 0.0119252873563, 0.0103947368421,
+ 0.0092380952381, 0.00916666666667, 0.0104444444444, 0.0125333333333, 0.00904761904762,
+ 0.00885714285714, 0.00911616161616, 0.0164166666667, 0.0128787878788, 0.0122756410256,
+ 0.013653253667966917], [0.0239333333333, 0.0223791578953, 0.0217974304255],
+ [0.0504861111111, 0.0610833333333], [], [0.171029411765]], [
+ [0.0195, 0.0203623188406, 0.0212121212121, 0.0345833333333, 0.0196180555556,
+ 0.0202536231884, 0.0200347222222, 0.0189, 0.0210763888889, 0.018275862069,
+ 0.0213888888889, 0.0319791666667, 0.0205303030303, 0.0156388888889, 0.0192,
+ 0.0231349206349, 0.023625, 0.02875, 0.0195512820513, 0.0239393939394, 0.0234166666667,
+ 0.0174702380952, 0.0204151307995], [0.040314343081226646], [0.148555555556], [], []]
+ self.test1_result = [77.77777777777777, 11.11111111111111, 7.407407407407407, 0, 3.7037037037037037],\
+ [92.0, 4.0, 4.0, 0, 0]
+
+ def test_result_plotflag0(self):
+ self.assertEqual(getQualitiesfromxml(self.path, self.ErrorsP, self.ErrorsS, 0), self.test0_result)
+
+ def test_result_plotflag1(self):
+ self.assertEqual(getQualitiesfromxml(self.path, self.ErrorsP, self.ErrorsS, 1), self.test1_result)
+
+
+if __name__ == '__main__':
+ unittest.main()
\ No newline at end of file
diff --git a/tests/test_get_quality_class.py b/tests/test_get_quality_class.py
index ea784539..2bf54f27 100644
--- a/tests/test_get_quality_class.py
+++ b/tests/test_get_quality_class.py
@@ -1,4 +1,5 @@
import unittest
+
from pylot.core.pick.utils import get_quality_class
@@ -52,5 +53,6 @@ class TestQualityClassFromUncertainty(unittest.TestCase):
# Error exactly in class 3
self.assertEqual(3, get_quality_class(5.6, self.error_classes))
+
if __name__ == '__main__':
unittest.main()
diff --git a/tests/utils.py b/tests/utils.py
index 4c04b112..6a8ab1f0 100644
--- a/tests/utils.py
+++ b/tests/utils.py
@@ -33,6 +33,7 @@ class HidePrints:
def silencer(*args, **kwargs):
with HidePrints():
func(*args, **kwargs)
+
return silencer
def __init__(self, hide_prints=True):
@@ -49,4 +50,4 @@ class HidePrints:
def __exit__(self, exc_type, exc_val, exc_tb):
"""Reinstate old stdout"""
if self.hide:
- sys.stdout = self._original_stdout
\ No newline at end of file
+ sys.stdout = self._original_stdout