Compare commits
67
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5d90904838 | ||
|
|
7a13288c85 | ||
|
|
3f97097bf6 | ||
|
|
29107ee40c | ||
|
|
fa310461d0 | ||
|
|
42a7d12292 | ||
|
|
2e49813292 | ||
|
|
5d6f4619cc | ||
|
|
db11e125c0 | ||
|
|
b59232d77b | ||
|
|
176e93d833 | ||
|
|
759e7bb848 | ||
|
|
61c3f40063 | ||
|
|
213819c702 | ||
|
|
67f34cc871 | ||
|
|
f4f48a930f | ||
|
|
b41e2b2de6 | ||
|
|
a068bb8457 | ||
|
|
452f2a2e18 | ||
|
|
c3a2ef5022 | ||
|
|
8e7bd87711 | ||
|
|
d5817adc46 | ||
|
|
14f01ec46d | ||
|
|
1b074d14ff | ||
|
|
ce71c549ca | ||
|
|
c4220b389e | ||
|
|
0f29d0e20d | ||
|
|
e1e0913e3a | ||
|
|
cdcd226c87 | ||
|
|
5f53cc5365 | ||
|
|
6cce05b035 | ||
|
|
7326f061e5 | ||
|
|
1a18401fe3 | ||
|
|
ec930dbc12 | ||
|
|
b991f771af | ||
|
|
2c3b1876ab | ||
|
|
0acd23d4d0 | ||
|
|
f349c8bc7e | ||
|
|
6688ef845d | ||
|
|
5b18e9ab71 | ||
|
|
c79e886d77 | ||
|
|
76f2d5d972 | ||
|
|
2d08fd029d | ||
|
|
8f22d438d3 | ||
|
|
93b7de3baa | ||
|
|
05642e775b | ||
|
|
47205ca493 | ||
|
|
5c7f0b56eb | ||
|
|
c574031931 | ||
|
|
e1a0fde619 | ||
|
|
48d196df11 | ||
|
|
6cc9cb4a96 | ||
|
|
5eab686445 | ||
|
|
b12e7937ac | ||
|
|
78f2dbcab2 | ||
|
|
31ca0d7a85 | ||
|
|
8b95c7a0fe | ||
|
|
c7f9ad4c6f | ||
|
|
b3fdbc811e | ||
|
|
9fce4998d3 | ||
|
|
c468bfbe84 | ||
|
|
4861d33e9a | ||
|
|
f5f4635c3d | ||
|
|
b12d92eebb | ||
|
|
e9da81376e | ||
|
|
e68fc849f0 | ||
|
|
efb117177c |
@@ -0,0 +1,39 @@
|
|||||||
|
Darius Arnold <Darius.Arnold@ruhr-uni-bochum.de> <Darius_A@web.de>
|
||||||
|
Darius Arnold <Darius.Arnold@ruhr-uni-bochum.de> <darius.arnold@rub.de>
|
||||||
|
Darius Arnold <Darius.Arnold@ruhr-uni-bochum.de> <darius.arnold@ruhr-uni-bochum.de>
|
||||||
|
Darius Arnold <Darius.Arnold@ruhr-uni-bochum.de> <mail@dariusarnold.de>
|
||||||
|
|
||||||
|
Dennis Wlecklik <dennisw@minos02.geophysik.ruhr-uni-bochum.de>
|
||||||
|
|
||||||
|
Jeldrik Gaal <jeldrikgaal@gmail.com>
|
||||||
|
|
||||||
|
Kaan Coekerim <kaan.coekerim@ruhr-uni-bochum.de>
|
||||||
|
Kaan Coekerim <kaan.coekerim@ruhr-uni-bochum.de> <kaan.coekerim@rub.de>
|
||||||
|
|
||||||
|
Ludger Kueperkoch <kueperkoch@igem-energie.de> <kueperkoch@bestec-for-nature.com>
|
||||||
|
Ludger Kueperkoch <kueperkoch@igem-energie.de> <ludger@quake2.(none)>
|
||||||
|
Ludger Kueperkoch <kueperkoch@igem-energie.de> <ludger@sauron.bestec-for-nature>
|
||||||
|
|
||||||
|
Marc S. Boxberg <marc.boxberg@rub.de>
|
||||||
|
|
||||||
|
Marcel Paffrath <marcel.paffrath@ruhr-uni-bochum.de> <marcel.paffrath@rub.de>
|
||||||
|
Marcel Paffrath <marcel.paffrath@ruhr-uni-bochum.de> <marcel@minos01.geophysik.ruhr-uni-bochum.de>
|
||||||
|
Marcel Paffrath <marcel.paffrath@ruhr-uni-bochum.de> <marcel@minos02.geophysik.ruhr-uni-bochum.de>
|
||||||
|
Marcel Paffrath <marcel.paffrath@ruhr-uni-bochum.de> <marcel@minos25.geophysik.ruhr-uni-bochum.de>
|
||||||
|
Marcel Paffrath <marcel.paffrath@ruhr-uni-bochum.de> <marcel@email.com>
|
||||||
|
|
||||||
|
Sally Zimmermann <sally.zimmermann@ruhr-uni-bochum.de>
|
||||||
|
|
||||||
|
Sebastian Wehling-Benatelli <sebastian.wehling-benatelli@cgi.com> <sebastianw@minos01.geophysik.ruhr-uni-bochum.de>
|
||||||
|
Sebastian Wehling-Benatelli <sebastian.wehling-benatelli@cgi.com> <sebastianw@minos02.geophysik.ruhr-uni-bochum.de>
|
||||||
|
Sebastian Wehling-Benatelli <sebastian.wehling-benatelli@cgi.com> <sebastianw@minos22.geophysik.ruhr-uni-bochum.de>
|
||||||
|
Sebastian Wehling-Benatelli <sebastian.wehling-benatelli@cgi.com> <sebastian.wehling-benatelli@scisys.de>
|
||||||
|
Sebastian Wehling-Benatelli <sebastian.wehling-benatelli@cgi.com> <sebastian.wehling@rub.de>
|
||||||
|
Sebastian Wehling-Benatelli <sebastian.wehling-benatelli@cgi.com> <sebastian.wehling@rub.de>
|
||||||
|
Sebastian Wehling-Benatelli <sebastian.wehling-benatelli@cgi.com> <DarkBeQst@users.noreply.github.com>
|
||||||
|
|
||||||
|
Thomas Moeller <thomas.moeller@rub.de>
|
||||||
|
|
||||||
|
Ann-Christin Koch <ann-christin.koch@ruhr-uni-bochum.de> <Ann-Christin.Koch@ruhr-uni-bochum.de>
|
||||||
|
|
||||||
|
Sebastian Priebe <sebastian.priebe@rub.de>
|
||||||
@@ -25,6 +25,7 @@ https://www.iconfinder.com/iconsets/flavour
|
|||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
import json
|
import json
|
||||||
|
import logging
|
||||||
import os
|
import os
|
||||||
import platform
|
import platform
|
||||||
import shutil
|
import shutil
|
||||||
@@ -60,7 +61,7 @@ except ImportError:
|
|||||||
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
|
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
|
||||||
from matplotlib.figure import Figure
|
from matplotlib.figure import Figure
|
||||||
|
|
||||||
from pylot.core.analysis.magnitude import LocalMagnitude, MomentMagnitude, calcsourcespec
|
from pylot.core.analysis.magnitude import LocalMagnitude, MomentMagnitude
|
||||||
from pylot.core.io.data import Data
|
from pylot.core.io.data import Data
|
||||||
from pylot.core.io.inputs import FilterOptions, PylotParameter
|
from pylot.core.io.inputs import FilterOptions, PylotParameter
|
||||||
from autoPyLoT import autoPyLoT
|
from autoPyLoT import autoPyLoT
|
||||||
@@ -72,19 +73,19 @@ from pylot.core.util.errors import DatastructureError, \
|
|||||||
OverwriteError
|
OverwriteError
|
||||||
from pylot.core.util.connection import checkurl
|
from pylot.core.util.connection import checkurl
|
||||||
from pylot.core.util.dataprocessing import Metadata, restitute_data
|
from pylot.core.util.dataprocessing import Metadata, restitute_data
|
||||||
from pylot.core.util.utils import fnConstructor, getLogin, \
|
from pylot.core.util.utils import fnConstructor, get_login, \
|
||||||
full_range, readFilterInformation, pick_color_plt, \
|
full_range, readFilterInformation, pick_color_plt, \
|
||||||
pick_linestyle_plt, identifyPhaseID, excludeQualityClasses, \
|
pick_linestyle_plt, identifyPhaseID, excludeQualityClasses, \
|
||||||
transform_colors_mpl, transform_colors_mpl_str, getAutoFilteroptions, check_all_obspy, \
|
transform_colors_mpl, transform_colors_mpl_str, getAutoFilteroptions, check_all_obspy, \
|
||||||
check_all_pylot, get_bool, get_None
|
check_all_pylot, get_bool, get_none
|
||||||
from pylot.core.util.gui import make_pen
|
from pylot.core.util.gui import make_pen
|
||||||
from pylot.core.util.event import Event
|
from pylot.core.util.event import Event
|
||||||
from pylot.core.io.location import create_creation_info, create_event
|
from pylot.core.io.location import create_creation_info, create_event
|
||||||
from pylot.core.util.widgets import FilterOptionsDialog, NewEventDlg, \
|
from pylot.core.util.widgets import FilterOptionsDialog, NewEventDlg, \
|
||||||
PylotCanvas, WaveformWidgetPG, PropertiesDlg, HelpForm, createAction, PickDlg, \
|
PylotCanvas, WaveformWidgetPG, PropertiesDlg, HelpForm, createAction, PickDlg, \
|
||||||
ComparisonWidget, TuneAutopicker, PylotParaBox, AutoPickDlg, CanvasWidget, AutoPickWidget, \
|
ComparisonWidget, TuneAutopicker, PylotParameterWidget, AutoPickDlg, CanvasWidget, AutoPickWidget, \
|
||||||
CompareEventsWidget, ProgressBarWidget, AddMetadataWidget, SingleTextLineDialog, LogWidget, PickQualitiesFromXml, \
|
CompareEventsWidget, ProgressBarWidget, AddMetadataWidget, SingleTextLineDialog, LogWidget, PickQualitiesFromXml, \
|
||||||
SourceSpecWindow, ChooseWaveFormWindow, SpectrogramTab
|
SpectrogramTab, SearchFileByExtensionDialog
|
||||||
from pylot.core.util.array_map import Array_map
|
from pylot.core.util.array_map import Array_map
|
||||||
from pylot.core.util.structure import DATASTRUCTURE
|
from pylot.core.util.structure import DATASTRUCTURE
|
||||||
from pylot.core.util.thread import Thread, Worker
|
from pylot.core.util.thread import Thread, Worker
|
||||||
@@ -113,11 +114,7 @@ class MainWindow(QMainWindow):
|
|||||||
def __init__(self, parent=None, infile=None, reset_qsettings=False):
|
def __init__(self, parent=None, infile=None, reset_qsettings=False):
|
||||||
super(MainWindow, self).__init__(parent)
|
super(MainWindow, self).__init__(parent)
|
||||||
|
|
||||||
# check for default pylot.in-file
|
if infile and os.path.isfile(infile) is False:
|
||||||
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) is False:
|
|
||||||
infile = QFileDialog().getOpenFileName(caption='Choose PyLoT-input file')[0]
|
infile = QFileDialog().getOpenFileName(caption='Choose PyLoT-input file')[0]
|
||||||
|
|
||||||
if not os.path.exists(infile):
|
if not os.path.exists(infile):
|
||||||
@@ -139,7 +136,7 @@ class MainWindow(QMainWindow):
|
|||||||
self.project.parameter = self._inputs
|
self.project.parameter = self._inputs
|
||||||
self.tap = None
|
self.tap = None
|
||||||
self.apw = None
|
self.apw = None
|
||||||
self.paraBox = None
|
self.parameterWidget = None
|
||||||
self.array_map = None
|
self.array_map = None
|
||||||
self._metadata = Metadata(verbosity=0)
|
self._metadata = Metadata(verbosity=0)
|
||||||
self._eventChanged = [False, False]
|
self._eventChanged = [False, False]
|
||||||
@@ -191,13 +188,12 @@ class MainWindow(QMainWindow):
|
|||||||
self.table_headers = ['', 'Event', 'Time', 'Lat', 'Lon', 'Depth', 'Ml', 'Mw', '[N] MP', '[N] AP', 'Tuning Set',
|
self.table_headers = ['', 'Event', 'Time', 'Lat', 'Lon', 'Depth', 'Ml', 'Mw', '[N] MP', '[N] AP', 'Tuning Set',
|
||||||
'Test Set', 'Notes']
|
'Test Set', 'Notes']
|
||||||
|
|
||||||
# TODO: refactor rootpath to datapath
|
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
if settings.value("user/FullName", None) is None:
|
if settings.value("user/FullName", None) is None:
|
||||||
fulluser = QInputDialog.getText(self, "Enter Name:", "Full name")
|
fulluser = QInputDialog.getText(self, "Enter Name:", "Full name")
|
||||||
settings.setValue("user/FullName", fulluser)
|
settings.setValue("user/FullName", fulluser)
|
||||||
settings.setValue("user/Login", getLogin())
|
settings.setValue("user/Login", get_login())
|
||||||
if settings.value("agency_id", None) is None:
|
if settings.value("agency_id", None) is None:
|
||||||
agency = QInputDialog.getText(self,
|
agency = QInputDialog.getText(self,
|
||||||
"Enter authority/institution name:",
|
"Enter authority/institution name:",
|
||||||
@@ -254,7 +250,7 @@ class MainWindow(QMainWindow):
|
|||||||
self._inputs.reset_defaults()
|
self._inputs.reset_defaults()
|
||||||
# check for default pylot.in-file
|
# check for default pylot.in-file
|
||||||
infile = os.path.join(pylot_config_dir, '.pylot.in')
|
infile = os.path.join(pylot_config_dir, '.pylot.in')
|
||||||
print('Using default input file {}'.format(infile))
|
logging.warning('Using default input file {}'.format(infile))
|
||||||
self._inputs.export2File(infile)
|
self._inputs.export2File(infile)
|
||||||
self.infile = infile
|
self.infile = infile
|
||||||
|
|
||||||
@@ -689,10 +685,9 @@ class MainWindow(QMainWindow):
|
|||||||
# add scroll area used in case number of traces gets too high
|
# add scroll area used in case number of traces gets too high
|
||||||
self.wf_scroll_area = QtWidgets.QScrollArea(self)
|
self.wf_scroll_area = QtWidgets.QScrollArea(self)
|
||||||
self.wf_scroll_area.setVisible(False)
|
self.wf_scroll_area.setVisible(False)
|
||||||
self.no_data_label = QLabel('No Data')
|
self.no_data_label = QLabel('No Data. If data were already loaded, try to select the event again in the eventbox.')
|
||||||
self.no_data_label.setStyleSheet('color: red')
|
self.no_data_label.setStyleSheet('color: red')
|
||||||
self.no_data_label.setAlignment(Qt.AlignCenter)
|
self.no_data_label.setAlignment(Qt.AlignCenter)
|
||||||
|
|
||||||
# create central matplotlib figure canvas widget
|
# create central matplotlib figure canvas widget
|
||||||
self.init_wfWidget()
|
self.init_wfWidget()
|
||||||
|
|
||||||
@@ -1007,17 +1002,16 @@ class MainWindow(QMainWindow):
|
|||||||
return
|
return
|
||||||
refresh = False
|
refresh = False
|
||||||
events = self.project.eventlist
|
events = self.project.eventlist
|
||||||
sld = SingleTextLineDialog(label='Specify file extension: ', default_text='.xml')
|
sld = SearchFileByExtensionDialog(label='Specify file extension: ', default_text='.xml',
|
||||||
|
events=events)
|
||||||
if not sld.exec_():
|
if not sld.exec_():
|
||||||
return
|
return
|
||||||
fext = sld.lineEdit.text()
|
|
||||||
# fext = '.xml'
|
filenames = sld.getChecked()
|
||||||
for event in events:
|
for event in events:
|
||||||
path = event.path
|
for filename in filenames:
|
||||||
eventname = path.split('/')[-1] # or event.pylot_id
|
if os.path.isfile(filename) and event.pylot_id in filename:
|
||||||
filename = os.path.join(path, 'PyLoT_' + eventname + fext)
|
self.load_data(filename, draw=False, event=event, ask_user=True, merge_strategy=sld.merge_strategy)
|
||||||
if os.path.isfile(filename):
|
|
||||||
self.load_data(filename, draw=False, event=event, overwrite=True)
|
|
||||||
refresh = True
|
refresh = True
|
||||||
if not refresh:
|
if not refresh:
|
||||||
return
|
return
|
||||||
@@ -1026,8 +1020,8 @@ class MainWindow(QMainWindow):
|
|||||||
self.fill_eventbox()
|
self.fill_eventbox()
|
||||||
self.setDirty(True)
|
self.setDirty(True)
|
||||||
|
|
||||||
def load_data(self, fname=None, loc=False, draw=True, event=None, overwrite=False):
|
def load_data(self, fname=None, loc=False, draw=True, event=None, ask_user=False, merge_strategy='Overwrite'):
|
||||||
if not overwrite:
|
if not ask_user:
|
||||||
if not self.okToContinue():
|
if not self.okToContinue():
|
||||||
return
|
return
|
||||||
if fname is None:
|
if fname is None:
|
||||||
@@ -1036,14 +1030,32 @@ class MainWindow(QMainWindow):
|
|||||||
fname = self.filename_from_action(action)
|
fname = self.filename_from_action(action)
|
||||||
if not fname:
|
if not fname:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
qmb = QMessageBox(self, icon=QMessageBox.Question,
|
||||||
|
text='Do you want to overwrite this data?',)
|
||||||
|
overwrite_button = qmb.addButton('Overwrite', QMessageBox.YesRole)
|
||||||
|
merge_button = qmb.addButton('Merge', QMessageBox.NoRole)
|
||||||
|
|
||||||
|
qmb.exec_()
|
||||||
|
|
||||||
|
if qmb.clickedButton() == overwrite_button:
|
||||||
|
merge_strategy = 'Overwrite'
|
||||||
|
elif qmb.clickedButton() == merge_button:
|
||||||
|
merge_strategy = 'Merge'
|
||||||
|
else:
|
||||||
|
return
|
||||||
|
|
||||||
if not event:
|
if not event:
|
||||||
event = self.get_current_event()
|
event = self.get_current_event()
|
||||||
data = Data(self, event)
|
data = Data(self, event)
|
||||||
try:
|
try:
|
||||||
data_new = Data(self, evtdata=str(fname))
|
data_new = Data(self, evtdata=str(fname))
|
||||||
# MP MP commented because adding several picks might cause inconsistencies
|
if merge_strategy == 'Overwrite':
|
||||||
data = data_new
|
data = data_new
|
||||||
# data += data_new
|
elif merge_strategy == 'Merge':
|
||||||
|
data += data_new
|
||||||
|
else:
|
||||||
|
raise NotImplementedError(f'Unknown merge strategy: {merge_strategy}')
|
||||||
except ValueError:
|
except ValueError:
|
||||||
qmb = QMessageBox(self, icon=QMessageBox.Question,
|
qmb = QMessageBox(self, icon=QMessageBox.Question,
|
||||||
text='Warning: Missmatch in event identifiers {} and {}. Continue?'.format(
|
text='Warning: Missmatch in event identifiers {} and {}. Continue?'.format(
|
||||||
@@ -1197,7 +1209,7 @@ class MainWindow(QMainWindow):
|
|||||||
with open(eventlist_file, 'r') as infile:
|
with open(eventlist_file, 'r') as infile:
|
||||||
eventlist_subset = [os.path.join(basepath, filename.split('\n')[0]) for filename in
|
eventlist_subset = [os.path.join(basepath, filename.split('\n')[0]) for filename in
|
||||||
infile.readlines()]
|
infile.readlines()]
|
||||||
msg = 'Found file "eventlist.txt" in database path. WILL ONLY USE SELECTED EVENTS out of {} events ' \
|
msg = 'Found file "eventlist.txt" in datapath. WILL ONLY USE SELECTED EVENTS out of {} events ' \
|
||||||
'contained in this subset'
|
'contained in this subset'
|
||||||
print(msg.format(len(eventlist_subset)))
|
print(msg.format(len(eventlist_subset)))
|
||||||
eventlist = [eventname for eventname in eventlist if eventname in eventlist_subset]
|
eventlist = [eventname for eventname in eventlist if eventname in eventlist_subset]
|
||||||
@@ -1222,49 +1234,34 @@ class MainWindow(QMainWindow):
|
|||||||
# get path from first event in list and split them
|
# get path from first event in list and split them
|
||||||
path = eventlist[0]
|
path = eventlist[0]
|
||||||
try:
|
try:
|
||||||
system_name = platform.system()
|
datapath = os.path.split(path)[0]
|
||||||
if system_name in ["Linux", "Darwin"]:
|
|
||||||
dirs = {
|
dirs = {
|
||||||
'database': path.split('/')[-2],
|
'datapath': datapath,
|
||||||
'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)
|
|
||||||
}
|
}
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
dirs = {
|
dirs = {
|
||||||
'database': '',
|
|
||||||
'datapath': '',
|
'datapath': '',
|
||||||
'rootpath': ''
|
|
||||||
}
|
}
|
||||||
print('Warning: Could not automatically init folder structure. ({})'.format(e))
|
print('Warning: Could not automatically init folder structure. ({})'.format(e))
|
||||||
|
|
||||||
settings = QSettings()
|
settings = QSettings()
|
||||||
settings.setValue("data/dataRoot", dirs['datapath']) # d irs['rootpath'])
|
settings.setValue("data/dataRoot", dirs['datapath'])
|
||||||
settings.sync()
|
settings.sync()
|
||||||
|
|
||||||
if not self.project.eventlist:
|
if not self.project.eventlist:
|
||||||
# init parameter object
|
# init parameter object
|
||||||
self.setParameter(show=False)
|
self.setParameter(show=False)
|
||||||
# hide all parameter (show all needed parameter later)
|
# hide all parameter (show all needed parameter later)
|
||||||
self.paraBox.hide_parameter()
|
self.parameterWidget.hide_parameter()
|
||||||
for directory in dirs.keys():
|
for directory in dirs.keys():
|
||||||
# set parameter
|
# set parameter
|
||||||
box = self.paraBox.boxes[directory]
|
box = self.parameterWidget.boxes[directory]
|
||||||
self.paraBox.setValue(box, dirs[directory])
|
self.parameterWidget.setValue(box, dirs[directory])
|
||||||
# show needed parameter in box
|
# show needed parameter in box
|
||||||
self.paraBox.show_parameter(directory)
|
self.parameterWidget.show_parameter(directory)
|
||||||
dirs_box = self.paraBox.get_groupbox_dialog('Directories')
|
dirs_box = self.parameterWidget.get_groupbox_dialog('Directories')
|
||||||
if not dirs_box.exec_():
|
if not dirs_box.exec_():
|
||||||
return
|
return
|
||||||
self.project.rootpath = dirs['rootpath']
|
|
||||||
self.project.datapath = dirs['datapath']
|
self.project.datapath = dirs['datapath']
|
||||||
else:
|
else:
|
||||||
if hasattr(self.project, 'datapath'):
|
if hasattr(self.project, 'datapath'):
|
||||||
@@ -1273,7 +1270,6 @@ class MainWindow(QMainWindow):
|
|||||||
'Datapath missmatch to current project!')
|
'Datapath missmatch to current project!')
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
self.project.rootpath = dirs['rootpath']
|
|
||||||
self.project.datapath = dirs['datapath']
|
self.project.datapath = dirs['datapath']
|
||||||
|
|
||||||
self.project.add_eventlist(eventlist)
|
self.project.add_eventlist(eventlist)
|
||||||
@@ -1361,11 +1357,10 @@ class MainWindow(QMainWindow):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
def modify_project_path(self, new_rootpath):
|
def modify_project_path(self, new_rootpath):
|
||||||
# TODO: change root to datapath
|
self.project.datapath = new_rootpath
|
||||||
self.project.rootpath = new_rootpath
|
|
||||||
for event in self.project.eventlist:
|
for event in self.project.eventlist:
|
||||||
event.rootpath = new_rootpath
|
event.datapath = new_rootpath
|
||||||
event.path = os.path.join(event.rootpath, event.datapath, event.database, event.pylot_id)
|
event.path = os.path.join(event.datapath, event.pylot_id)
|
||||||
event.path = event.path.replace('\\', '/')
|
event.path = event.path.replace('\\', '/')
|
||||||
event.path = event.path.replace('//', '/')
|
event.path = event.path.replace('//', '/')
|
||||||
|
|
||||||
@@ -1408,25 +1403,28 @@ class MainWindow(QMainWindow):
|
|||||||
|
|
||||||
for id, event in enumerate(self.project.eventlist):
|
for id, event in enumerate(self.project.eventlist):
|
||||||
event_path = event.path
|
event_path = event.path
|
||||||
phaseErrors = {'P': self._inputs['timeerrorsP'],
|
#phaseErrors = {'P': self._inputs['timeerrorsP'],
|
||||||
'S': self._inputs['timeerrorsS']}
|
# 'S': self._inputs['timeerrorsS']}
|
||||||
|
|
||||||
ma_props = {'manual': event.pylot_picks,
|
man_au_picks = {'manual': event.pylot_picks,
|
||||||
'auto': event.pylot_autopicks}
|
'auto': event.pylot_autopicks}
|
||||||
ma_count = {'manual': 0,
|
npicks = {'manual': {'P': 0, 'S': 0},
|
||||||
'auto': 0}
|
'auto': {'P': 0, 'S': 0}}
|
||||||
ma_count_total = {'manual': 0,
|
npicks_total = {'manual': {'P': 0, 'S': 0},
|
||||||
'auto': 0}
|
'auto': {'P': 0, 'S': 0}}
|
||||||
|
|
||||||
for ma in ma_props.keys():
|
for ma in man_au_picks.keys():
|
||||||
if ma_props[ma]:
|
if man_au_picks[ma]:
|
||||||
for picks in ma_props[ma].values():
|
for picks in man_au_picks[ma].values():
|
||||||
for phasename, pick in picks.items():
|
for phasename, pick in picks.items():
|
||||||
if not type(pick) in [dict, AttribDict]:
|
if not type(pick) in [dict, AttribDict]:
|
||||||
continue
|
continue
|
||||||
|
phase_ID = identifyPhaseID(phasename)
|
||||||
|
if not phase_ID in npicks[ma].keys():
|
||||||
|
continue
|
||||||
if pick.get('spe'):
|
if pick.get('spe'):
|
||||||
ma_count[ma] += 1
|
npicks[ma][phase_ID] += 1
|
||||||
ma_count_total[ma] += 1
|
npicks_total[ma][phase_ID] += 1
|
||||||
|
|
||||||
event_ref = event.isRefEvent()
|
event_ref = event.isRefEvent()
|
||||||
event_test = event.isTestEvent()
|
event_test = event.isTestEvent()
|
||||||
@@ -1461,16 +1459,23 @@ class MainWindow(QMainWindow):
|
|||||||
if event.dirty:
|
if event.dirty:
|
||||||
event_str += '*'
|
event_str += '*'
|
||||||
item_path = QStandardItem(event_str)
|
item_path = QStandardItem(event_str)
|
||||||
item_time = QStandardItem('{}'.format(time))
|
item_time = QStandardItem('{}'.format(time.strftime("%Y-%m-%d %H:%M:%S") if time else ''))
|
||||||
item_lat = QStandardItem('{}'.format(lat))
|
item_lat = QStandardItem('{}'.format(lat))
|
||||||
item_lon = QStandardItem('{}'.format(lon))
|
item_lon = QStandardItem('{}'.format(lon))
|
||||||
item_depth = QStandardItem('{}'.format(depth))
|
item_depth = QStandardItem('{}'.format(depth))
|
||||||
item_localmag = QStandardItem('{}'.format(localmag))
|
item_localmag = QStandardItem('{}'.format(localmag))
|
||||||
item_momentmag = QStandardItem('{}'.format(momentmag))
|
item_momentmag = QStandardItem('{}'.format(momentmag))
|
||||||
item_nmp = QStandardItem('{}({})'.format(ma_count['manual'], ma_count_total['manual']))
|
|
||||||
|
item_nmp = QStandardItem()
|
||||||
|
item_nap = QStandardItem()
|
||||||
item_nmp.setIcon(self.manupicksicon_small)
|
item_nmp.setIcon(self.manupicksicon_small)
|
||||||
item_nap = QStandardItem('{}({})'.format(ma_count['auto'], ma_count_total['auto']))
|
|
||||||
item_nap.setIcon(self.autopicksicon_small)
|
item_nap.setIcon(self.autopicksicon_small)
|
||||||
|
|
||||||
|
for picktype, item_np in [('manual', item_nmp), ('auto', item_nap)]:
|
||||||
|
npicks_str = f"{npicks[picktype]['P']}|{npicks[picktype]['S']}"
|
||||||
|
#npicks_str += f"({npicks_total[picktype]['P']}/{npicks_total[picktype]['S']})"
|
||||||
|
item_np.setText(npicks_str)
|
||||||
|
|
||||||
item_ref = QStandardItem() # str(event_ref))
|
item_ref = QStandardItem() # str(event_ref))
|
||||||
item_test = QStandardItem() # str(event_test))
|
item_test = QStandardItem() # str(event_test))
|
||||||
if event_ref:
|
if event_ref:
|
||||||
@@ -1549,7 +1554,7 @@ class MainWindow(QMainWindow):
|
|||||||
self.set_fname(self.get_data().getEventFileName(), type)
|
self.set_fname(self.get_data().getEventFileName(), type)
|
||||||
return self.get_fnames(type)
|
return self.get_fnames(type)
|
||||||
|
|
||||||
def saveData(self, event=None, directory=None, outformats=['.xml', '.cnv', '.obs', '_focmec.in', '.pha']):
|
def saveData(self, event=None, directory=None, outformats=None):
|
||||||
'''
|
'''
|
||||||
Save event data to directory with specified output formats.
|
Save event data to directory with specified output formats.
|
||||||
:param event: PyLoT Event, if not set current event will be used
|
:param event: PyLoT Event, if not set current event will be used
|
||||||
@@ -1557,6 +1562,8 @@ class MainWindow(QMainWindow):
|
|||||||
:param outformats: str/list of output formats
|
:param outformats: str/list of output formats
|
||||||
:return:
|
:return:
|
||||||
'''
|
'''
|
||||||
|
if outformats is None:
|
||||||
|
outformats = ['.xml', '.cnv', '.obs', '_focmec.in', '.pha']
|
||||||
if not event:
|
if not event:
|
||||||
event = self.get_current_event()
|
event = self.get_current_event()
|
||||||
if not type(outformats) == list:
|
if not type(outformats) == list:
|
||||||
@@ -1692,7 +1699,7 @@ class MainWindow(QMainWindow):
|
|||||||
|
|
||||||
# WIP JG
|
# WIP JG
|
||||||
def eventlistXml(self):
|
def eventlistXml(self):
|
||||||
path = self._inputs['rootpath'] + '/' + self._inputs['datapath'] + '/' + self._inputs['database']
|
path = self._inputs['datapath']
|
||||||
outpath = self.project.location[:self.project.location.rfind('/')]
|
outpath = self.project.location[:self.project.location.rfind('/')]
|
||||||
geteventlistfromxml(path, outpath)
|
geteventlistfromxml(path, outpath)
|
||||||
return
|
return
|
||||||
@@ -1891,6 +1898,7 @@ class MainWindow(QMainWindow):
|
|||||||
# which will read in data input twice. Therefore current tab is changed to 0
|
# which will read in data input twice. Therefore current tab is changed to 0
|
||||||
# in loadProject before calling this function.
|
# in loadProject before calling this function.
|
||||||
self.fill_eventbox()
|
self.fill_eventbox()
|
||||||
|
#print(f'{self.get_current_event()=}')
|
||||||
plotted = False
|
plotted = False
|
||||||
if self.tabs.currentIndex() == 2:
|
if self.tabs.currentIndex() == 2:
|
||||||
self.init_event_table()
|
self.init_event_table()
|
||||||
@@ -1925,7 +1933,6 @@ class MainWindow(QMainWindow):
|
|||||||
self.spectro_layout.addWidget(newSpectroWidget)
|
self.spectro_layout.addWidget(newSpectroWidget)
|
||||||
self.spectroWidget = newSpectroWidget
|
self.spectroWidget = newSpectroWidget
|
||||||
|
|
||||||
|
|
||||||
def newWF(self, event=None, plot=True):
|
def newWF(self, event=None, plot=True):
|
||||||
'''
|
'''
|
||||||
Load new data and plot if necessary.
|
Load new data and plot if necessary.
|
||||||
@@ -1983,6 +1990,8 @@ class MainWindow(QMainWindow):
|
|||||||
# ans = False
|
# ans = False
|
||||||
|
|
||||||
settings = QSettings()
|
settings = QSettings()
|
||||||
|
# process application events to wait for event items to appear in event box
|
||||||
|
QApplication.processEvents()
|
||||||
curr_event = self.get_current_event()
|
curr_event = self.get_current_event()
|
||||||
if not curr_event:
|
if not curr_event:
|
||||||
print('Could not find current event. Try reload?')
|
print('Could not find current event. Try reload?')
|
||||||
@@ -1990,8 +1999,8 @@ class MainWindow(QMainWindow):
|
|||||||
|
|
||||||
if len(curr_event.origins) > 0:
|
if len(curr_event.origins) > 0:
|
||||||
origin_time = curr_event.origins[0].time
|
origin_time = curr_event.origins[0].time
|
||||||
tstart = settings.value('tstart') if get_None(settings.value('tstart')) else 0
|
tstart = settings.value('tstart') if get_none(settings.value('tstart')) else 0
|
||||||
tstop = settings.value('tstop') if get_None(settings.value('tstop')) else 0
|
tstop = settings.value('tstop') if get_none(settings.value('tstop')) else 0
|
||||||
tstart = origin_time + float(tstart)
|
tstart = origin_time + float(tstart)
|
||||||
tstop = origin_time + float(tstop)
|
tstop = origin_time + float(tstop)
|
||||||
else:
|
else:
|
||||||
@@ -2138,7 +2147,7 @@ class MainWindow(QMainWindow):
|
|||||||
|
|
||||||
def finish_pg_plot(self):
|
def finish_pg_plot(self):
|
||||||
self.getPlotWidget().updateWidget()
|
self.getPlotWidget().updateWidget()
|
||||||
plots, gaps = self.wfp_thread.data
|
plots = self.wfp_thread.data
|
||||||
# do not show plot if no data are given
|
# do not show plot if no data are given
|
||||||
self.wf_scroll_area.setVisible(len(plots) > 0)
|
self.wf_scroll_area.setVisible(len(plots) > 0)
|
||||||
self.no_data_label.setVisible(not len(plots) > 0)
|
self.no_data_label.setVisible(not len(plots) > 0)
|
||||||
@@ -2186,6 +2195,7 @@ class MainWindow(QMainWindow):
|
|||||||
if event.pylot_autopicks:
|
if event.pylot_autopicks:
|
||||||
self.drawPicks(picktype='auto')
|
self.drawPicks(picktype='auto')
|
||||||
if event.pylot_picks or event.pylot_autopicks:
|
if event.pylot_picks or event.pylot_autopicks:
|
||||||
|
if not self._inputs.get('extent') == 'global':
|
||||||
self.locateEventAction.setEnabled(True)
|
self.locateEventAction.setEnabled(True)
|
||||||
self.qualities_action.setEnabled(True)
|
self.qualities_action.setEnabled(True)
|
||||||
self.eventlist_xml_action.setEnabled(True)
|
self.eventlist_xml_action.setEnabled(True)
|
||||||
@@ -2306,14 +2316,14 @@ class MainWindow(QMainWindow):
|
|||||||
# wfst += self.get_data().getWFData().select(component=alter_comp)
|
# wfst += self.get_data().getWFData().select(component=alter_comp)
|
||||||
plotWidget = self.getPlotWidget()
|
plotWidget = self.getPlotWidget()
|
||||||
self.adjustPlotHeight()
|
self.adjustPlotHeight()
|
||||||
if get_bool(settings.value('large_dataset')):
|
if get_bool(settings.value('large_dataset')) == True:
|
||||||
self.plot_method = 'fast'
|
self.plot_method = 'fast'
|
||||||
else:
|
else:
|
||||||
self.plot_method = 'normal'
|
self.plot_method = 'normal'
|
||||||
rval = plotWidget.plotWFData(wfdata=wfst, wfsyn=wfsyn, title=title, mapping=False, component=comp,
|
rval = plotWidget.plotWFData(wfdata=wfst, wfsyn=wfsyn, title=title, mapping=False, component=comp,
|
||||||
nth_sample=int(nth_sample), method=self.plot_method, gain=self.gain)
|
nth_sample=int(nth_sample), method=self.plot_method, gain=self.gain)
|
||||||
plots, gaps = rval if rval else ([], [])
|
plots = rval if rval else []
|
||||||
return plots, gaps
|
return plots
|
||||||
|
|
||||||
def adjustPlotHeight(self):
|
def adjustPlotHeight(self):
|
||||||
if self.pg:
|
if self.pg:
|
||||||
@@ -2416,7 +2426,7 @@ class MainWindow(QMainWindow):
|
|||||||
filterS = filteroptions['S']
|
filterS = filteroptions['S']
|
||||||
minP, maxP = filterP.getFreq()
|
minP, maxP = filterP.getFreq()
|
||||||
minS, maxS = filterS.getFreq()
|
minS, maxS = filterS.getFreq()
|
||||||
self.paraBox.params_to_gui()
|
self.parameterWidget.params_to_gui()
|
||||||
|
|
||||||
def getFilterOptions(self):
|
def getFilterOptions(self):
|
||||||
return self.filteroptions
|
return self.filteroptions
|
||||||
@@ -2621,7 +2631,6 @@ class MainWindow(QMainWindow):
|
|||||||
picks=self.getPicksOnStation(station, 'manual'),
|
picks=self.getPicksOnStation(station, 'manual'),
|
||||||
autopicks=self.getPicksOnStation(station, 'auto'),
|
autopicks=self.getPicksOnStation(station, 'auto'),
|
||||||
metadata=self.metadata, event=event,
|
metadata=self.metadata, event=event,
|
||||||
model=self.inputs.get('taup_model'),
|
|
||||||
filteroptions=self.filteroptions, wftype=wftype,
|
filteroptions=self.filteroptions, wftype=wftype,
|
||||||
show_comp_data=self.dataPlot.comp_checkbox.isChecked())
|
show_comp_data=self.dataPlot.comp_checkbox.isChecked())
|
||||||
if self.filterActionP.isChecked():
|
if self.filterActionP.isChecked():
|
||||||
@@ -3002,10 +3011,16 @@ class MainWindow(QMainWindow):
|
|||||||
event = self.get_current_event()
|
event = self.get_current_event()
|
||||||
event.pylot_picks = {}
|
event.pylot_picks = {}
|
||||||
event.pylot_autopicks = {}
|
event.pylot_autopicks = {}
|
||||||
picksdict = picksdict_from_picks(evt=self.get_data().get_evt_data())
|
picksdict = picksdict_from_picks(evt=self.get_data().get_evt_data(), parameter=self.getParameter())
|
||||||
event.addPicks(picksdict['manual'])
|
event.addPicks(picksdict['manual'])
|
||||||
event.addAutopicks(picksdict['auto'])
|
event.addAutopicks(picksdict['auto'])
|
||||||
|
|
||||||
|
def getParameter(self):
|
||||||
|
if hasattr(self.project, 'parameter') and isinstance(self.project.parameter, PylotParameter):
|
||||||
|
return self.project.parameter
|
||||||
|
else:
|
||||||
|
return self._inputs
|
||||||
|
|
||||||
def drawPicks(self, station=None, picktype=None, stime=None):
|
def drawPicks(self, station=None, picktype=None, stime=None):
|
||||||
# if picktype not specified, draw both
|
# if picktype not specified, draw both
|
||||||
if not stime:
|
if not stime:
|
||||||
@@ -3159,8 +3174,8 @@ class MainWindow(QMainWindow):
|
|||||||
ttt = parameter['ttpatter']
|
ttt = parameter['ttpatter']
|
||||||
outfile = parameter['outpatter']
|
outfile = parameter['outpatter']
|
||||||
eventname = self.get_current_event_name()
|
eventname = self.get_current_event_name()
|
||||||
obsdir = os.path.join(self._inputs['rootpath'], self._inputs['datapath'], self._inputs['database'], eventname)
|
obsdir = os.path.join(self._inputs['datapath'], eventname)
|
||||||
self.saveData(event=self.get_current_event(), directory=obsdir, outformats='.obs')
|
self.saveData(event=self.get_current_event(), directory=obsdir, outformats=['.obs'])
|
||||||
filename = 'PyLoT_' + eventname
|
filename = 'PyLoT_' + eventname
|
||||||
locpath = os.path.join(locroot, 'loc', filename)
|
locpath = os.path.join(locroot, 'loc', filename)
|
||||||
phasefile = os.path.join(obsdir, filename + '.obs')
|
phasefile = os.path.join(obsdir, filename + '.obs')
|
||||||
@@ -3647,7 +3662,7 @@ class MainWindow(QMainWindow):
|
|||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def update_status(self, message, duration=5000):
|
def update_status(self, message, duration=10000):
|
||||||
self.statusBar().showMessage(message, duration)
|
self.statusBar().showMessage(message, duration)
|
||||||
if self.get_data() is not None:
|
if self.get_data() is not None:
|
||||||
if not self.get_current_event() or not self.project.location:
|
if not self.get_current_event() or not self.project.location:
|
||||||
@@ -3700,10 +3715,13 @@ class MainWindow(QMainWindow):
|
|||||||
if not self.okToContinue():
|
if not self.okToContinue():
|
||||||
return
|
return
|
||||||
if not fnm:
|
if not fnm:
|
||||||
dlg = QFileDialog(parent=self)
|
settings = QSettings()
|
||||||
|
dir = settings.value('current_project_path')
|
||||||
|
dlg = QFileDialog(parent=self, directory=dir)
|
||||||
fnm = dlg.getOpenFileName(self, 'Open project file...', filter='Pylot project (*.plp)')[0]
|
fnm = dlg.getOpenFileName(self, 'Open project file...', filter='Pylot project (*.plp)')[0]
|
||||||
if not fnm:
|
if not fnm:
|
||||||
return
|
return
|
||||||
|
settings.setValue('current_project_path', os.path.split(fnm)[0])
|
||||||
if not os.path.exists(fnm):
|
if not os.path.exists(fnm):
|
||||||
QMessageBox.warning(self, 'Could not open file',
|
QMessageBox.warning(self, 'Could not open file',
|
||||||
'Could not open project file {}. File does not exist.'.format(fnm))
|
'Could not open project file {}. File does not exist.'.format(fnm))
|
||||||
@@ -3714,6 +3732,7 @@ class MainWindow(QMainWindow):
|
|||||||
if self.project.parameter:
|
if self.project.parameter:
|
||||||
# do this step to update default parameter on older PyLoT projects
|
# do this step to update default parameter on older PyLoT projects
|
||||||
self.project.parameter.reinit_default_parameters()
|
self.project.parameter.reinit_default_parameters()
|
||||||
|
PylotParameter.check_deprecated_parameters(self.project.parameter)
|
||||||
|
|
||||||
self._inputs = self.project.parameter
|
self._inputs = self.project.parameter
|
||||||
self.updateFilteroptions()
|
self.updateFilteroptions()
|
||||||
@@ -3821,6 +3840,7 @@ class MainWindow(QMainWindow):
|
|||||||
|
|
||||||
def closeEvent(self, event):
|
def closeEvent(self, event):
|
||||||
if self.okToContinue():
|
if self.okToContinue():
|
||||||
|
if hasattr(self, 'logwidget'):
|
||||||
self.logwidget.close()
|
self.logwidget.close()
|
||||||
event.accept()
|
event.accept()
|
||||||
else:
|
else:
|
||||||
@@ -3830,13 +3850,13 @@ class MainWindow(QMainWindow):
|
|||||||
|
|
||||||
def setParameter(self, checked=0, show=True):
|
def setParameter(self, checked=0, show=True):
|
||||||
if checked: pass # dummy argument to receive trigger signal (checked) if called by QAction
|
if checked: pass # dummy argument to receive trigger signal (checked) if called by QAction
|
||||||
if not self.paraBox:
|
if not self.parameterWidget:
|
||||||
self.paraBox = PylotParaBox(self._inputs, parent=self, windowflag=Qt.Window)
|
self.parameterWidget = PylotParameterWidget(self._inputs, parent=self, windowflag=Qt.Window)
|
||||||
self.paraBox.accepted.connect(self._setDirty)
|
self.parameterWidget.accepted.connect(self._setDirty)
|
||||||
self.paraBox.accepted.connect(self.filterOptionsFromParameter)
|
self.parameterWidget.accepted.connect(self.filterOptionsFromParameter)
|
||||||
if show:
|
if show:
|
||||||
self.paraBox.params_to_gui()
|
self.parameterWidget.params_to_gui()
|
||||||
self.paraBox.show()
|
self.parameterWidget.show()
|
||||||
|
|
||||||
def deleteAllAutopicks(self):
|
def deleteAllAutopicks(self):
|
||||||
qmb = QMessageBox(self, icon=QMessageBox.Question,
|
qmb = QMessageBox(self, icon=QMessageBox.Question,
|
||||||
@@ -3883,16 +3903,18 @@ class Project(object):
|
|||||||
Pickable class containing information of a PyLoT project, like event lists and file locations.
|
Pickable class containing information of a PyLoT project, like event lists and file locations.
|
||||||
'''
|
'''
|
||||||
|
|
||||||
# TODO: remove rootpath
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.eventlist = []
|
self.eventlist = []
|
||||||
self.location = None
|
self.location = None
|
||||||
self.rootpath = None
|
|
||||||
self.datapath = None
|
self.datapath = None
|
||||||
self.dirty = False
|
self.dirty = False
|
||||||
self.parameter = None
|
self.parameter = None
|
||||||
self._table = None
|
self._table = None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def rootpath(self):
|
||||||
|
return self.datapath
|
||||||
|
|
||||||
def add_eventlist(self, eventlist):
|
def add_eventlist(self, eventlist):
|
||||||
'''
|
'''
|
||||||
Add events from an eventlist containing paths to event directories.
|
Add events from an eventlist containing paths to event directories.
|
||||||
@@ -3902,8 +3924,6 @@ class Project(object):
|
|||||||
return
|
return
|
||||||
for item in eventlist:
|
for item in eventlist:
|
||||||
event = Event(item)
|
event = Event(item)
|
||||||
event.rootpath = self.parameter['rootpath']
|
|
||||||
event.database = self.parameter['database']
|
|
||||||
event.datapath = self.parameter['datapath']
|
event.datapath = self.parameter['datapath']
|
||||||
if not event.path in self.getPaths():
|
if not event.path in self.getPaths():
|
||||||
self.eventlist.append(event)
|
self.eventlist.append(event)
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ PILOT has originally been developed in Mathworks' MatLab. In order to distribute
|
|||||||
problems, it has been decided to redevelop the software package in Python. The great work of the ObsPy group allows easy
|
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.
|
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.
|
The development of PyLoT is part of the joint research project MAGS2, AlpArray and AdriaArray.
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
@@ -27,28 +27,30 @@ Afterwards run (from the PyLoT main directory where the files *requirements.txt*
|
|||||||
conda env create -f pylot.yml
|
conda env create -f pylot.yml
|
||||||
or
|
or
|
||||||
|
|
||||||
conda create --name pylot_38 --file requirements.txt
|
conda create -c conda-forge --name pylot_311 python=3.11 --file requirements.txt
|
||||||
|
|
||||||
to create a new Anaconda environment called "pylot_38".
|
to create a new Anaconda environment called *pylot_311*.
|
||||||
|
|
||||||
Afterwards activate the environment by typing
|
Afterwards activate the environment by typing
|
||||||
|
|
||||||
conda activate pylot_38
|
conda activate pylot_311
|
||||||
|
|
||||||
#### Prerequisites:
|
#### Prerequisites:
|
||||||
|
|
||||||
In order to run PyLoT you need to install:
|
In order to run PyLoT you need to install:
|
||||||
|
|
||||||
- Python 3
|
- Python 3
|
||||||
- obspy
|
|
||||||
- pyside2
|
|
||||||
- pyqtgraph
|
|
||||||
- cartopy
|
- cartopy
|
||||||
|
- joblib
|
||||||
|
- obspy
|
||||||
|
- pyaml
|
||||||
|
- pyqtgraph
|
||||||
|
- pyside2
|
||||||
|
|
||||||
(the following are already dependencies of the above packages):
|
(the following are already dependencies of the above packages):
|
||||||
- scipy
|
- scipy
|
||||||
- numpy
|
- numpy
|
||||||
- matplotlib <= 3.3.x
|
- matplotlib
|
||||||
|
|
||||||
#### Some handwork:
|
#### Some handwork:
|
||||||
|
|
||||||
@@ -108,4 +110,4 @@ Others: A. Bruestle, T. Meier, W. Friederich
|
|||||||
|
|
||||||
[ObsPy]: http://github.com/obspy/obspy/wiki
|
[ObsPy]: http://github.com/obspy/obspy/wiki
|
||||||
|
|
||||||
April 2022
|
August 2024
|
||||||
|
|||||||
+10
-18
@@ -28,7 +28,7 @@ from pylot.core.util.dataprocessing import restitute_data, Metadata
|
|||||||
from pylot.core.util.defaults import SEPARATOR
|
from pylot.core.util.defaults import SEPARATOR
|
||||||
from pylot.core.util.event import Event
|
from pylot.core.util.event import Event
|
||||||
from pylot.core.util.structure import DATASTRUCTURE
|
from pylot.core.util.structure import DATASTRUCTURE
|
||||||
from pylot.core.util.utils import get_None, trim_station_components, check4gapsAndRemove, check4doubled, \
|
from pylot.core.util.utils import get_none, trim_station_components, check4gapsAndRemove, check4doubled, \
|
||||||
check4rotated
|
check4rotated
|
||||||
from pylot.core.util.version import get_git_version as _getVersionString
|
from pylot.core.util.version import get_git_version as _getVersionString
|
||||||
|
|
||||||
@@ -91,9 +91,9 @@ def autoPyLoT(input_dict=None, parameter=None, inputfile=None, fnames=None, even
|
|||||||
sp=sp_info)
|
sp=sp_info)
|
||||||
print(splash)
|
print(splash)
|
||||||
|
|
||||||
parameter = get_None(parameter)
|
parameter = get_none(parameter)
|
||||||
inputfile = get_None(inputfile)
|
inputfile = get_none(inputfile)
|
||||||
eventid = get_None(eventid)
|
eventid = get_none(eventid)
|
||||||
|
|
||||||
fig_dict = None
|
fig_dict = None
|
||||||
fig_dict_wadatijack = None
|
fig_dict_wadatijack = None
|
||||||
@@ -119,13 +119,9 @@ def autoPyLoT(input_dict=None, parameter=None, inputfile=None, fnames=None, even
|
|||||||
obspyDMT_wfpath = input_dict['obspyDMT_wfpath']
|
obspyDMT_wfpath = input_dict['obspyDMT_wfpath']
|
||||||
|
|
||||||
if not parameter:
|
if not parameter:
|
||||||
if inputfile:
|
if not inputfile:
|
||||||
|
print('Using default input parameter')
|
||||||
parameter = PylotParameter(inputfile)
|
parameter = PylotParameter(inputfile)
|
||||||
# iplot = parameter['iplot']
|
|
||||||
else:
|
|
||||||
infile = os.path.join(os.path.expanduser('~'), '.pylot', 'pylot.in')
|
|
||||||
print('Using default input file {}'.format(infile))
|
|
||||||
parameter = PylotParameter(infile)
|
|
||||||
else:
|
else:
|
||||||
if not type(parameter) == PylotParameter:
|
if not type(parameter) == PylotParameter:
|
||||||
print('Wrong input type for parameter: {}'.format(type(parameter)))
|
print('Wrong input type for parameter: {}'.format(type(parameter)))
|
||||||
@@ -140,11 +136,9 @@ def autoPyLoT(input_dict=None, parameter=None, inputfile=None, fnames=None, even
|
|||||||
if parameter.hasParam('datastructure'):
|
if parameter.hasParam('datastructure'):
|
||||||
# getting information on data structure
|
# getting information on data structure
|
||||||
datastructure = DATASTRUCTURE[parameter.get('datastructure')]()
|
datastructure = DATASTRUCTURE[parameter.get('datastructure')]()
|
||||||
dsfields = {'root': parameter.get('rootpath'),
|
dsfields = {'dpath': parameter.get('datapath'),}
|
||||||
'dpath': parameter.get('datapath'),
|
|
||||||
'dbase': parameter.get('database')}
|
|
||||||
|
|
||||||
exf = ['root', 'dpath', 'dbase']
|
exf = ['dpath']
|
||||||
|
|
||||||
if parameter['eventID'] != '*' and fnames == 'None':
|
if parameter['eventID'] != '*' and fnames == 'None':
|
||||||
dsfields['eventID'] = parameter['eventID']
|
dsfields['eventID'] = parameter['eventID']
|
||||||
@@ -154,7 +148,7 @@ def autoPyLoT(input_dict=None, parameter=None, inputfile=None, fnames=None, even
|
|||||||
datastructure.setExpandFields(exf)
|
datastructure.setExpandFields(exf)
|
||||||
|
|
||||||
# check if default location routine NLLoc is available and all stations are used
|
# check if default location routine NLLoc is available and all stations are used
|
||||||
if get_None(parameter['nllocbin']) and station == 'all':
|
if get_none(parameter['nllocbin']) and station == 'all':
|
||||||
locflag = 1
|
locflag = 1
|
||||||
# get NLLoc-root path
|
# get NLLoc-root path
|
||||||
nllocroot = parameter.get('nllocroot')
|
nllocroot = parameter.get('nllocroot')
|
||||||
@@ -210,12 +204,10 @@ def autoPyLoT(input_dict=None, parameter=None, inputfile=None, fnames=None, even
|
|||||||
locflag = 2
|
locflag = 2
|
||||||
else:
|
else:
|
||||||
# started in tune or interactive mode
|
# started in tune or interactive mode
|
||||||
datapath = os.path.join(parameter['rootpath'],
|
datapath = parameter['datapath']
|
||||||
parameter['datapath'])
|
|
||||||
events = []
|
events = []
|
||||||
for eventID in eventid:
|
for eventID in eventid:
|
||||||
events.append(os.path.join(datapath,
|
events.append(os.path.join(datapath,
|
||||||
parameter['database'],
|
|
||||||
eventID))
|
eventID))
|
||||||
|
|
||||||
if not events:
|
if not events:
|
||||||
|
|||||||
+4
-4
@@ -3,10 +3,10 @@
|
|||||||
#$ -l low
|
#$ -l low
|
||||||
#$ -cwd
|
#$ -cwd
|
||||||
#$ -pe smp 40
|
#$ -pe smp 40
|
||||||
#$ -l mem=2G
|
##$ -l mem=3G
|
||||||
#$ -l h_vmem=2G
|
#$ -l h_vmem=6G
|
||||||
#$ -l os=*stretch
|
#$ -l os=*stretch
|
||||||
|
|
||||||
conda activate pylot_38
|
conda activate pylot_311
|
||||||
|
|
||||||
python ./autoPyLoT.py -i /home/marcel/.pylot/pylot_janis_noisy.in -c $NSLOTS
|
python ./autoPyLoT.py -i /home/marcel/.pylot/pylot_adriaarray.in -c 20 -dmt processed
|
||||||
|
|||||||
@@ -203,8 +203,6 @@ The meaning of the header entries is:
|
|||||||
PyLoT GUI starts with an empty project. To add events, use the add event data button. Select one or multiple folders
|
PyLoT GUI starts with an empty project. To add events, use the add event data button. Select one or multiple folders
|
||||||
containing events.
|
containing events.
|
||||||
|
|
||||||
[//]: <> (TODO: explain _Directories: Root path, Data path, Database path_)
|
|
||||||
|
|
||||||
### Saving projects
|
### Saving projects
|
||||||
|
|
||||||
Save the current project from the menu with File->Save project or File->Save project as. PyLoT uses ``.plp`` files to
|
Save the current project from the menu with File->Save project or File->Save project as. PyLoT uses ``.plp`` files to
|
||||||
|
|||||||
@@ -4,10 +4,8 @@
|
|||||||
%Parameters are optimized for %extent data sets!
|
%Parameters are optimized for %extent data sets!
|
||||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||||
#main settings#
|
#main settings#
|
||||||
#rootpath# %project path
|
|
||||||
#datapath# %data path
|
#datapath# %data path
|
||||||
#database# %name of data base
|
#eventID# %event ID for single event processing (* for all events found in datapath)
|
||||||
#eventID# %event ID for single event processing (* for all events found in database)
|
|
||||||
#invdir# %full path to inventory or dataless-seed file
|
#invdir# %full path to inventory or dataless-seed file
|
||||||
PILOT #datastructure# %choose data structure
|
PILOT #datastructure# %choose data structure
|
||||||
True #apverbose# %choose 'True' or 'False' for terminal output
|
True #apverbose# %choose 'True' or 'False' for terminal output
|
||||||
|
|||||||
@@ -4,10 +4,8 @@
|
|||||||
%Parameters are optimized for %extent data sets!
|
%Parameters are optimized for %extent data sets!
|
||||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||||
#main settings#
|
#main settings#
|
||||||
/DATA/Insheim #rootpath# %project path
|
/DATA/Insheim/EVENT_DATA/LOCAL/2018.02_Insheim #datapath# %data path
|
||||||
EVENT_DATA/LOCAL #datapath# %data path
|
e0006.038.18 #eventID# %event ID for single event processing (* for all events found in datapath)
|
||||||
2018.02_Insheim #database# %name of data base
|
|
||||||
e0006.038.18 #eventID# %event ID for single event processing (* for all events found in database)
|
|
||||||
/DATA/Insheim/STAT_INFO #invdir# %full path to inventory or dataless-seed file
|
/DATA/Insheim/STAT_INFO #invdir# %full path to inventory or dataless-seed file
|
||||||
PILOT #datastructure# %choose data structure
|
PILOT #datastructure# %choose data structure
|
||||||
True #apverbose# %choose 'True' or 'False' for terminal output
|
True #apverbose# %choose 'True' or 'False' for terminal output
|
||||||
|
|||||||
@@ -4,10 +4,8 @@
|
|||||||
%Parameters are optimized for %extent data sets!
|
%Parameters are optimized for %extent data sets!
|
||||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||||
#main settings#
|
#main settings#
|
||||||
#rootpath# %project path
|
|
||||||
#datapath# %data path
|
#datapath# %data path
|
||||||
#database# %name of data base
|
#eventID# %event ID for single event processing (* for all events found in datapath)
|
||||||
#eventID# %event ID for single event processing (* for all events found in database)
|
|
||||||
#invdir# %full path to inventory or dataless-seed file
|
#invdir# %full path to inventory or dataless-seed file
|
||||||
PILOT #datastructure# %choose data structure
|
PILOT #datastructure# %choose data structure
|
||||||
True #apverbose# %choose 'True' or 'False' for terminal output
|
True #apverbose# %choose 'True' or 'False' for terminal output
|
||||||
|
|||||||
@@ -1,14 +1,12 @@
|
|||||||
name: pylot_38
|
name: pylot_311
|
||||||
channels:
|
channels:
|
||||||
- conda-forge
|
- conda-forge
|
||||||
- defaults
|
- defaults
|
||||||
dependencies:
|
dependencies:
|
||||||
- cartopy=0.20.2
|
- cartopy=0.23.0=py311hcf9f919_1
|
||||||
- matplotlib-base=3.3.4
|
- joblib=1.4.2=pyhd8ed1ab_0
|
||||||
- numpy=1.22.3
|
- obspy=1.4.1=py311he736701_3
|
||||||
- obspy=1.3.0
|
- pyaml=24.7.0=pyhd8ed1ab_0
|
||||||
- pyqtgraph=0.12.4
|
- pyqtgraph=0.13.7=pyhd8ed1ab_0
|
||||||
- pyside2>=5.13.2
|
- pyside2=5.15.8=py311h3d699ce_4
|
||||||
- python=3.8.12
|
- pytest=8.3.2=pyhd8ed1ab_0
|
||||||
- qt>=5.12.9
|
|
||||||
- scipy=1.8.0
|
|
||||||
+1
-1
@@ -9,7 +9,7 @@ PyLoT - the Python picking and Localization Tool
|
|||||||
|
|
||||||
This python library contains a graphical user interfaces for picking
|
This python library contains a graphical user interfaces for picking
|
||||||
seismic phases. This software needs ObsPy (http://github.com/obspy/obspy/wiki)
|
seismic phases. This software needs ObsPy (http://github.com/obspy/obspy/wiki)
|
||||||
and the Qt4 libraries to be installed first.
|
and the Qt libraries to be installed first.
|
||||||
|
|
||||||
PILOT has been developed in Mathworks' MatLab. In order to distribute
|
PILOT has been developed in Mathworks' MatLab. In order to distribute
|
||||||
PILOT without facing portability problems, it has been decided to re-
|
PILOT without facing portability problems, it has been decided to re-
|
||||||
|
|||||||
+17
-11
@@ -2,6 +2,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
import copy
|
import copy
|
||||||
|
import logging
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from PySide2.QtWidgets import QMessageBox
|
from PySide2.QtWidgets import QMessageBox
|
||||||
@@ -19,7 +20,7 @@ from pylot.core.util.errors import FormatError, OverwriteError
|
|||||||
from pylot.core.util.event import Event
|
from pylot.core.util.event import Event
|
||||||
from pylot.core.util.obspyDMT_interface import qml_from_obspyDMT
|
from pylot.core.util.obspyDMT_interface import qml_from_obspyDMT
|
||||||
from pylot.core.util.utils import fnConstructor, full_range, check4rotated, \
|
from pylot.core.util.utils import fnConstructor, full_range, check4rotated, \
|
||||||
check4gapsAndMerge, trim_station_components
|
check_for_gaps_and_merge, trim_station_components, check_for_nan
|
||||||
|
|
||||||
|
|
||||||
class Data(object):
|
class Data(object):
|
||||||
@@ -64,7 +65,7 @@ class Data(object):
|
|||||||
elif 'LOC' in evtdata:
|
elif 'LOC' in evtdata:
|
||||||
raise NotImplementedError('PILOT location information '
|
raise NotImplementedError('PILOT location information '
|
||||||
'read support not yet '
|
'read support not yet '
|
||||||
'implemeted.')
|
'implemented.')
|
||||||
elif 'event.pkl' in evtdata:
|
elif 'event.pkl' in evtdata:
|
||||||
evtdata = qml_from_obspyDMT(evtdata)
|
evtdata = qml_from_obspyDMT(evtdata)
|
||||||
else:
|
else:
|
||||||
@@ -408,18 +409,16 @@ class Data(object):
|
|||||||
not implemented: {1}'''.format(evtformat, e))
|
not implemented: {1}'''.format(evtformat, e))
|
||||||
if fnext == '_focmec.in':
|
if fnext == '_focmec.in':
|
||||||
try:
|
try:
|
||||||
infile = os.path.join(os.path.expanduser('~'), '.pylot', 'pylot.in')
|
parameter = PylotParameter()
|
||||||
print('Using default input file {}'.format(infile))
|
logging.warning('Using default input parameter')
|
||||||
parameter = PylotParameter(infile)
|
|
||||||
focmec.export(picks_copy, fnout + fnext, parameter, eventinfo=self.get_evt_data())
|
focmec.export(picks_copy, fnout + fnext, parameter, eventinfo=self.get_evt_data())
|
||||||
except KeyError as e:
|
except KeyError as e:
|
||||||
raise KeyError('''{0} export format
|
raise KeyError('''{0} export format
|
||||||
not implemented: {1}'''.format(evtformat, e))
|
not implemented: {1}'''.format(evtformat, e))
|
||||||
if fnext == '.pha':
|
if fnext == '.pha':
|
||||||
try:
|
try:
|
||||||
infile = os.path.join(os.path.expanduser('~'), '.pylot', 'pylot.in')
|
parameter = PylotParameter()
|
||||||
print('Using default input file {}'.format(infile))
|
logging.warning('Using default input parameter')
|
||||||
parameter = PylotParameter(infile)
|
|
||||||
hypodd.export(picks_copy, fnout + fnext, parameter, eventinfo=self.get_evt_data())
|
hypodd.export(picks_copy, fnout + fnext, parameter, eventinfo=self.get_evt_data())
|
||||||
except KeyError as e:
|
except KeyError as e:
|
||||||
raise KeyError('''{0} export format
|
raise KeyError('''{0} export format
|
||||||
@@ -458,6 +457,11 @@ class Data(object):
|
|||||||
:param fnames_alt: alternative data to show (e.g. synthetic/processed)
|
:param fnames_alt: alternative data to show (e.g. synthetic/processed)
|
||||||
:type fnames: list
|
:type fnames: list
|
||||||
"""
|
"""
|
||||||
|
def check_fname_exists(filenames: list) -> list:
|
||||||
|
if filenames:
|
||||||
|
filenames = [fn for fn in filenames if os.path.isfile(fn)]
|
||||||
|
return filenames
|
||||||
|
|
||||||
self.wfdata = Stream()
|
self.wfdata = Stream()
|
||||||
self.wforiginal = None
|
self.wforiginal = None
|
||||||
self.wf_alt = Stream()
|
self.wf_alt = Stream()
|
||||||
@@ -467,8 +471,8 @@ class Data(object):
|
|||||||
self.tstop = tstop
|
self.tstop = tstop
|
||||||
|
|
||||||
# remove directories
|
# remove directories
|
||||||
fnames = [fname for fname in fnames if not os.path.isdir(fname)]
|
fnames = check_fname_exists(fnames)
|
||||||
fnames_alt = [fname for fname in fnames_alt if not os.path.isdir(fname)]
|
fnames_alt = check_fname_exists(fnames_alt)
|
||||||
|
|
||||||
# if obspy_dmt:
|
# if obspy_dmt:
|
||||||
# wfdir = 'raw'
|
# wfdir = 'raw'
|
||||||
@@ -496,7 +500,9 @@ class Data(object):
|
|||||||
# remove possible underscores in station names
|
# remove possible underscores in station names
|
||||||
# self.wfdata = remove_underscores(self.wfdata)
|
# self.wfdata = remove_underscores(self.wfdata)
|
||||||
# check for gaps and merge
|
# check for gaps and merge
|
||||||
self.wfdata = check4gapsAndMerge(self.wfdata)
|
self.wfdata, _ = check_for_gaps_and_merge(self.wfdata)
|
||||||
|
# check for nans
|
||||||
|
check_for_nan(self.wfdata)
|
||||||
# check for stations with rotated components
|
# check for stations with rotated components
|
||||||
if checkRotated and metadata is not None:
|
if checkRotated and metadata is not None:
|
||||||
self.wfdata = check4rotated(self.wfdata, metadata, verbosity=0)
|
self.wfdata = check4rotated(self.wfdata, metadata, verbosity=0)
|
||||||
|
|||||||
@@ -6,24 +6,14 @@ import numpy as np
|
|||||||
Default parameters used for picking
|
Default parameters used for picking
|
||||||
"""
|
"""
|
||||||
|
|
||||||
defaults = {'rootpath': {'type': str,
|
defaults = {'datapath': {'type': str,
|
||||||
'tooltip': 'project path',
|
'tooltip': 'path to eventfolders',
|
||||||
'value': '',
|
|
||||||
'namestring': 'Root path'},
|
|
||||||
|
|
||||||
'datapath': {'type': str,
|
|
||||||
'tooltip': 'data path',
|
|
||||||
'value': '',
|
'value': '',
|
||||||
'namestring': 'Data path'},
|
'namestring': 'Data path'},
|
||||||
|
|
||||||
'database': {'type': str,
|
|
||||||
'tooltip': 'name of data base',
|
|
||||||
'value': '',
|
|
||||||
'namestring': 'Database path'},
|
|
||||||
|
|
||||||
'eventID': {'type': str,
|
'eventID': {'type': str,
|
||||||
'tooltip': 'event ID for single event processing (* for all events found in database)',
|
'tooltip': 'event ID for single event processing (* for all events found in datapath)',
|
||||||
'value': '',
|
'value': '*',
|
||||||
'namestring': 'Event ID'},
|
'namestring': 'Event ID'},
|
||||||
|
|
||||||
'extent': {'type': str,
|
'extent': {'type': str,
|
||||||
@@ -511,7 +501,7 @@ defaults = {'rootpath': {'type': str,
|
|||||||
|
|
||||||
'taup_model': {'type': str,
|
'taup_model': {'type': str,
|
||||||
'tooltip': 'Define TauPy model for traveltime estimation. Possible values: 1066a, 1066b, ak135, ak135f, herrin, iasp91, jb, prem, pwdk, sp6',
|
'tooltip': 'Define TauPy model for traveltime estimation. Possible values: 1066a, 1066b, ak135, ak135f, herrin, iasp91, jb, prem, pwdk, sp6',
|
||||||
'value': None,
|
'value': 'iasp91',
|
||||||
'namestring': 'TauPy model'},
|
'namestring': 'TauPy model'},
|
||||||
|
|
||||||
'taup_phases': {'type': str,
|
'taup_phases': {'type': str,
|
||||||
@@ -522,9 +512,7 @@ defaults = {'rootpath': {'type': str,
|
|||||||
|
|
||||||
settings_main = {
|
settings_main = {
|
||||||
'dirs': [
|
'dirs': [
|
||||||
'rootpath',
|
|
||||||
'datapath',
|
'datapath',
|
||||||
'database',
|
|
||||||
'eventID',
|
'eventID',
|
||||||
'invdir',
|
'invdir',
|
||||||
'datastructure',
|
'datastructure',
|
||||||
|
|||||||
+27
-3
@@ -1,5 +1,7 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
|
||||||
from pylot.core.io import default_parameters
|
from pylot.core.io import default_parameters
|
||||||
from pylot.core.util.errors import ParameterError
|
from pylot.core.util.errors import ParameterError
|
||||||
@@ -88,10 +90,10 @@ class PylotParameter(object):
|
|||||||
return bool(self.__parameter)
|
return bool(self.__parameter)
|
||||||
|
|
||||||
def __getitem__(self, key):
|
def __getitem__(self, key):
|
||||||
try:
|
if key in self.__parameter:
|
||||||
return self.__parameter[key]
|
return self.__parameter[key]
|
||||||
except:
|
else:
|
||||||
return None
|
logging.warning(f'{key} not found in PylotParameter')
|
||||||
|
|
||||||
def __setitem__(self, key, value):
|
def __setitem__(self, key, value):
|
||||||
try:
|
try:
|
||||||
@@ -418,6 +420,28 @@ class PylotParameter(object):
|
|||||||
line = value + name + ttip
|
line = value + name + ttip
|
||||||
fid.write(line)
|
fid.write(line)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def check_deprecated_parameters(parameters):
|
||||||
|
if parameters.hasParam('database') and parameters.hasParam('rootpath'):
|
||||||
|
parameters['datapath'] = os.path.join(parameters['rootpath'], parameters['datapath'],
|
||||||
|
parameters['database'])
|
||||||
|
logging.warning(
|
||||||
|
f'Parameters database and rootpath are deprecated. '
|
||||||
|
f'Tried to merge them to now path: {parameters["datapath"]}.'
|
||||||
|
)
|
||||||
|
|
||||||
|
remove_keys = []
|
||||||
|
for key in parameters:
|
||||||
|
if not key in default_parameters.defaults.keys():
|
||||||
|
remove_keys.append(key)
|
||||||
|
logging.warning(f'Removing deprecated parameter: {key}')
|
||||||
|
|
||||||
|
for key in remove_keys:
|
||||||
|
del parameters[key]
|
||||||
|
|
||||||
|
parameters._settings_main = default_parameters.settings_main
|
||||||
|
parameters._settings_special_pick = default_parameters.settings_special_pick
|
||||||
|
|
||||||
|
|
||||||
class FilterOptions(object):
|
class FilterOptions(object):
|
||||||
'''
|
'''
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
from obspy import UTCDateTime
|
from obspy import UTCDateTime
|
||||||
from obspy.core import event as ope
|
from obspy.core import event as ope
|
||||||
|
|
||||||
from pylot.core.util.utils import getLogin, getHash
|
from pylot.core.util.utils import get_login, get_hash
|
||||||
|
|
||||||
|
|
||||||
def create_amplitude(pickID, amp, unit, category, cinfo):
|
def create_amplitude(pickID, amp, unit, category, cinfo):
|
||||||
@@ -61,7 +61,7 @@ def create_creation_info(agency_id=None, creation_time=None, author=None):
|
|||||||
:return:
|
:return:
|
||||||
'''
|
'''
|
||||||
if author is None:
|
if author is None:
|
||||||
author = getLogin()
|
author = get_login()
|
||||||
if creation_time is None:
|
if creation_time is None:
|
||||||
creation_time = UTCDateTime()
|
creation_time = UTCDateTime()
|
||||||
return ope.CreationInfo(agency_id=agency_id, author=author,
|
return ope.CreationInfo(agency_id=agency_id, author=author,
|
||||||
@@ -210,7 +210,7 @@ def create_resourceID(timetohash, restype, authority_id=None, hrstr=None):
|
|||||||
'''
|
'''
|
||||||
assert isinstance(timetohash, UTCDateTime), "'timetohash' is not an ObsPy" \
|
assert isinstance(timetohash, UTCDateTime), "'timetohash' is not an ObsPy" \
|
||||||
"UTCDateTime object"
|
"UTCDateTime object"
|
||||||
hid = getHash(timetohash)
|
hid = get_hash(timetohash)
|
||||||
if hrstr is None:
|
if hrstr is None:
|
||||||
resID = ope.ResourceIdentifier(restype + '/' + hid[0:6])
|
resID = ope.ResourceIdentifier(restype + '/' + hid[0:6])
|
||||||
else:
|
else:
|
||||||
|
|||||||
+15
-17
@@ -1,6 +1,7 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
import glob
|
import glob
|
||||||
|
import logging
|
||||||
import os
|
import os
|
||||||
import warnings
|
import warnings
|
||||||
|
|
||||||
@@ -16,7 +17,7 @@ from pylot.core.io.inputs import PylotParameter
|
|||||||
from pylot.core.io.location import create_event, \
|
from pylot.core.io.location import create_event, \
|
||||||
create_magnitude
|
create_magnitude
|
||||||
from pylot.core.pick.utils import select_for_phase, get_quality_class
|
from pylot.core.pick.utils import select_for_phase, get_quality_class
|
||||||
from pylot.core.util.utils import getOwner, full_range, four_digits, transformFilterString4Export, \
|
from pylot.core.util.utils import get_owner, full_range, four_digits, transformFilterString4Export, \
|
||||||
backtransformFilterString, loopIdentifyPhase, identifyPhase
|
backtransformFilterString, loopIdentifyPhase, identifyPhase
|
||||||
|
|
||||||
|
|
||||||
@@ -58,7 +59,7 @@ def readPILOTEvent(phasfn=None, locfn=None, authority_id='RUB', **kwargs):
|
|||||||
if phasfn is not None and os.path.isfile(phasfn):
|
if phasfn is not None and os.path.isfile(phasfn):
|
||||||
phases = sio.loadmat(phasfn)
|
phases = sio.loadmat(phasfn)
|
||||||
phasctime = UTCDateTime(os.path.getmtime(phasfn))
|
phasctime = UTCDateTime(os.path.getmtime(phasfn))
|
||||||
phasauthor = getOwner(phasfn)
|
phasauthor = get_owner(phasfn)
|
||||||
else:
|
else:
|
||||||
phases = None
|
phases = None
|
||||||
phasctime = None
|
phasctime = None
|
||||||
@@ -66,7 +67,7 @@ def readPILOTEvent(phasfn=None, locfn=None, authority_id='RUB', **kwargs):
|
|||||||
if locfn is not None and os.path.isfile(locfn):
|
if locfn is not None and os.path.isfile(locfn):
|
||||||
loc = sio.loadmat(locfn)
|
loc = sio.loadmat(locfn)
|
||||||
locctime = UTCDateTime(os.path.getmtime(locfn))
|
locctime = UTCDateTime(os.path.getmtime(locfn))
|
||||||
locauthor = getOwner(locfn)
|
locauthor = get_owner(locfn)
|
||||||
else:
|
else:
|
||||||
loc = None
|
loc = None
|
||||||
locctime = None
|
locctime = None
|
||||||
@@ -217,7 +218,7 @@ def picksdict_from_obs(fn):
|
|||||||
return picks
|
return picks
|
||||||
|
|
||||||
|
|
||||||
def picksdict_from_picks(evt):
|
def picksdict_from_picks(evt, parameter=None):
|
||||||
"""
|
"""
|
||||||
Takes an Event object and return the pick dictionary commonly used within
|
Takes an Event object and return the pick dictionary commonly used within
|
||||||
PyLoT
|
PyLoT
|
||||||
@@ -230,6 +231,7 @@ def picksdict_from_picks(evt):
|
|||||||
'auto': {}
|
'auto': {}
|
||||||
}
|
}
|
||||||
for pick in evt.picks:
|
for pick in evt.picks:
|
||||||
|
errors = None
|
||||||
phase = {}
|
phase = {}
|
||||||
station = pick.waveform_id.station_code
|
station = pick.waveform_id.station_code
|
||||||
if pick.waveform_id.channel_code is None:
|
if pick.waveform_id.channel_code is None:
|
||||||
@@ -273,33 +275,29 @@ def picksdict_from_picks(evt):
|
|||||||
phase['epp'] = epp
|
phase['epp'] = epp
|
||||||
phase['lpp'] = lpp
|
phase['lpp'] = lpp
|
||||||
phase['spe'] = spe
|
phase['spe'] = spe
|
||||||
try:
|
weight = phase.get('weight')
|
||||||
phase['weight'] = weight
|
if not weight:
|
||||||
except:
|
if not parameter:
|
||||||
# get onset weight from uncertainty
|
logging.warning('Using ')
|
||||||
infile = os.path.join(os.path.expanduser('~'), '.pylot', 'pylot.in')
|
logging.warning('Using default input parameter')
|
||||||
print('Using default input file {}'.format(infile))
|
parameter = PylotParameter()
|
||||||
parameter = PylotParameter(infile)
|
|
||||||
pick.phase_hint = identifyPhase(pick.phase_hint)
|
pick.phase_hint = identifyPhase(pick.phase_hint)
|
||||||
if pick.phase_hint == 'P':
|
if pick.phase_hint == 'P':
|
||||||
errors = parameter['timeerrorsP']
|
errors = parameter['timeerrorsP']
|
||||||
elif pick.phase_hint == 'S':
|
elif pick.phase_hint == 'S':
|
||||||
errors = parameter['timeerrorsS']
|
errors = parameter['timeerrorsS']
|
||||||
|
if errors:
|
||||||
weight = get_quality_class(spe, errors)
|
weight = get_quality_class(spe, errors)
|
||||||
phase['weight'] = weight
|
phase['weight'] = weight
|
||||||
phase['channel'] = channel
|
phase['channel'] = channel
|
||||||
phase['network'] = network
|
phase['network'] = network
|
||||||
phase['picker'] = pick_method
|
phase['picker'] = pick_method
|
||||||
try:
|
|
||||||
if pick.polarity == 'positive':
|
if pick.polarity == 'positive':
|
||||||
phase['fm'] = 'U'
|
phase['fm'] = 'U'
|
||||||
elif pick.polarity == 'negative':
|
elif pick.polarity == 'negative':
|
||||||
phase['fm'] = 'D'
|
phase['fm'] = 'D'
|
||||||
else:
|
else:
|
||||||
phase['fm'] = 'N'
|
phase['fm'] = 'N'
|
||||||
except:
|
|
||||||
print("No FM info available!")
|
|
||||||
phase['fm'] = 'N'
|
|
||||||
phase['filter_id'] = filter_id if filter_id is not None else ''
|
phase['filter_id'] = filter_id if filter_id is not None else ''
|
||||||
|
|
||||||
onsets[pick.phase_hint] = phase.copy()
|
onsets[pick.phase_hint] = phase.copy()
|
||||||
@@ -514,7 +512,7 @@ def writephases(arrivals, fformat, filename, parameter=None, eventinfo=None):
|
|||||||
fid = open("%s" % filename, 'w')
|
fid = open("%s" % filename, 'w')
|
||||||
# write header
|
# write header
|
||||||
fid.write('# EQEVENT: %s Label: EQ%s Loc: X 0.00 Y 0.00 Z 10.00 OT 0.00 \n' %
|
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')))
|
(parameter.get('datapath'), parameter.get('eventID')))
|
||||||
arrivals = chooseArrivals(arrivals) # MP MP what is chooseArrivals? It is not defined anywhere
|
arrivals = chooseArrivals(arrivals) # MP MP what is chooseArrivals? It is not defined anywhere
|
||||||
for key in arrivals:
|
for key in arrivals:
|
||||||
# P onsets
|
# P onsets
|
||||||
@@ -667,7 +665,7 @@ def writephases(arrivals, fformat, filename, parameter=None, eventinfo=None):
|
|||||||
print("Writing phases to %s for HYPOSAT" % filename)
|
print("Writing phases to %s for HYPOSAT" % filename)
|
||||||
fid = open("%s" % filename, 'w')
|
fid = open("%s" % filename, 'w')
|
||||||
# write header
|
# write header
|
||||||
fid.write('%s, event %s \n' % (parameter.get('database'), parameter.get('eventID')))
|
fid.write('%s, event %s \n' % (parameter.get('datapath'), parameter.get('eventID')))
|
||||||
arrivals = chooseArrivals(arrivals) # MP MP what is chooseArrivals? It is not defined anywhere
|
arrivals = chooseArrivals(arrivals) # MP MP what is chooseArrivals? It is not defined anywhere
|
||||||
for key in arrivals:
|
for key in arrivals:
|
||||||
# P onsets
|
# P onsets
|
||||||
|
|||||||
@@ -20,9 +20,9 @@ from pylot.core.pick.charfuns import CharacteristicFunction
|
|||||||
from pylot.core.pick.charfuns import HOScf, AICcf, ARZcf, ARHcf, AR3Ccf
|
from pylot.core.pick.charfuns import HOScf, AICcf, ARZcf, ARHcf, AR3Ccf
|
||||||
from pylot.core.pick.picker import AICPicker, PragPicker
|
from pylot.core.pick.picker import AICPicker, PragPicker
|
||||||
from pylot.core.pick.utils import checksignallength, checkZ4S, earllatepicker, \
|
from pylot.core.pick.utils import checksignallength, checkZ4S, earllatepicker, \
|
||||||
getSNR, fmpicker, checkPonsets, wadaticheck, get_quality_class
|
getSNR, fmpicker, checkPonsets, wadaticheck, get_quality_class, PickingFailedException, MissingTraceException
|
||||||
from pylot.core.util.utils import getPatternLine, gen_Pool, \
|
from pylot.core.util.utils import getPatternLine, gen_Pool, \
|
||||||
get_bool, identifyPhaseID, get_None, correct_iplot
|
get_bool, identifyPhaseID, get_none, correct_iplot
|
||||||
|
|
||||||
|
|
||||||
def autopickevent(data, param, iplot=0, fig_dict=None, fig_dict_wadatijack=None, ncores=0, metadata=None, origin=None):
|
def autopickevent(data, param, iplot=0, fig_dict=None, fig_dict_wadatijack=None, ncores=0, metadata=None, origin=None):
|
||||||
@@ -232,20 +232,6 @@ class PickingContainer:
|
|||||||
self.Sflag = 0
|
self.Sflag = 0
|
||||||
|
|
||||||
|
|
||||||
class MissingTraceException(ValueError):
|
|
||||||
"""
|
|
||||||
Used to indicate missing traces in a obspy.core.stream.Stream object
|
|
||||||
"""
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class PickingFailedException(Exception):
|
|
||||||
"""
|
|
||||||
Raised when picking fails due to missing values etc.
|
|
||||||
"""
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class AutopickStation(object):
|
class AutopickStation(object):
|
||||||
|
|
||||||
def __init__(self, wfstream, pickparam, verbose, iplot=0, fig_dict=None, metadata=None, origin=None):
|
def __init__(self, wfstream, pickparam, verbose, iplot=0, fig_dict=None, metadata=None, origin=None):
|
||||||
@@ -272,7 +258,7 @@ class AutopickStation(object):
|
|||||||
self.pickparams = copy.deepcopy(pickparam)
|
self.pickparams = copy.deepcopy(pickparam)
|
||||||
self.verbose = verbose
|
self.verbose = verbose
|
||||||
self.iplot = correct_iplot(iplot)
|
self.iplot = correct_iplot(iplot)
|
||||||
self.fig_dict = get_None(fig_dict)
|
self.fig_dict = get_none(fig_dict)
|
||||||
self.metadata = metadata
|
self.metadata = metadata
|
||||||
self.origin = origin
|
self.origin = origin
|
||||||
|
|
||||||
@@ -660,7 +646,7 @@ class AutopickStation(object):
|
|||||||
ax1.set_ylim([-1.5, 1.5])
|
ax1.set_ylim([-1.5, 1.5])
|
||||||
ax1.set_ylabel('Normalized Counts')
|
ax1.set_ylabel('Normalized Counts')
|
||||||
|
|
||||||
if self.horizontal_traces_exist() and self.s_data.Sflag == 1:
|
if self.horizontal_traces_exist():# and self.s_data.Sflag == 1:
|
||||||
# plot E trace
|
# plot E trace
|
||||||
ax2 = fig.add_subplot(3, 1, 2, sharex=ax1)
|
ax2 = fig.add_subplot(3, 1, 2, sharex=ax1)
|
||||||
th1data = np.linspace(0, self.etrace.stats.endtime - self.etrace.stats.starttime,
|
th1data = np.linspace(0, self.etrace.stats.endtime - self.etrace.stats.starttime,
|
||||||
|
|||||||
@@ -20,6 +20,8 @@ import numpy as np
|
|||||||
from scipy import signal
|
from scipy import signal
|
||||||
from obspy.core import Stream
|
from obspy.core import Stream
|
||||||
|
|
||||||
|
from pylot.core.pick.utils import PickingFailedException
|
||||||
|
|
||||||
|
|
||||||
class CharacteristicFunction(object):
|
class CharacteristicFunction(object):
|
||||||
"""
|
"""
|
||||||
@@ -293,7 +295,7 @@ class HOScf(CharacteristicFunction):
|
|||||||
if j < 4:
|
if j < 4:
|
||||||
LTA[j] = 0
|
LTA[j] = 0
|
||||||
STA[j] = 0
|
STA[j] = 0
|
||||||
elif j <= ista:
|
elif j <= ista and self.getOrder() == 2:
|
||||||
lta = (y[j] + lta * (j - 1)) / j
|
lta = (y[j] + lta * (j - 1)) / j
|
||||||
if self.getOrder() == 2:
|
if self.getOrder() == 2:
|
||||||
sta = (y[j] + sta * (j - 1)) / j
|
sta = (y[j] + sta * (j - 1)) / j
|
||||||
@@ -488,6 +490,9 @@ class ARHcf(CharacteristicFunction):
|
|||||||
print('Calculating AR-prediction error from both horizontal traces ...')
|
print('Calculating AR-prediction error from both horizontal traces ...')
|
||||||
|
|
||||||
xnp = self.getDataArray(self.getCut())
|
xnp = self.getDataArray(self.getCut())
|
||||||
|
if len(xnp[0]) == 0:
|
||||||
|
raise PickingFailedException('calcCF: Found empty data trace for cut times. Return')
|
||||||
|
|
||||||
n0 = np.isnan(xnp[0].data)
|
n0 = np.isnan(xnp[0].data)
|
||||||
if len(n0) > 1:
|
if len(n0) > 1:
|
||||||
xnp[0].data[n0] = 0
|
xnp[0].data[n0] = 0
|
||||||
|
|||||||
@@ -178,7 +178,9 @@ class AICPicker(AutoPicker):
|
|||||||
aic = tap * self.cf + max(abs(self.cf))
|
aic = tap * self.cf + max(abs(self.cf))
|
||||||
# smooth AIC-CF
|
# smooth AIC-CF
|
||||||
ismooth = int(round(self.Tsmooth / self.dt))
|
ismooth = int(round(self.Tsmooth / self.dt))
|
||||||
aicsmooth = np.zeros(len(aic))
|
# MP MP better start with original data than zeros if array shall be smoothed, created artificial value before
|
||||||
|
# when starting with i in range(1...) loop below and subtracting offset afterwards
|
||||||
|
aicsmooth = np.copy(aic)
|
||||||
if len(aic) < ismooth:
|
if len(aic) < ismooth:
|
||||||
print('AICPicker: Tsmooth larger than CF!')
|
print('AICPicker: Tsmooth larger than CF!')
|
||||||
return
|
return
|
||||||
@@ -188,7 +190,7 @@ class AICPicker(AutoPicker):
|
|||||||
ii1 = i - ismooth
|
ii1 = i - ismooth
|
||||||
aicsmooth[i] = aicsmooth[i - 1] + (aic[i] - aic[ii1]) / ismooth
|
aicsmooth[i] = aicsmooth[i - 1] + (aic[i] - aic[ii1]) / ismooth
|
||||||
else:
|
else:
|
||||||
aicsmooth[i] = np.mean(aic[1: i])
|
aicsmooth[i] = np.mean(aic[0: i]) # MP MP created np.nan for i=1
|
||||||
# remove offset in AIC function
|
# remove offset in AIC function
|
||||||
offset = abs(min(aic) - min(aicsmooth))
|
offset = abs(min(aic) - min(aicsmooth))
|
||||||
aicsmooth = aicsmooth - offset
|
aicsmooth = aicsmooth - offset
|
||||||
@@ -197,7 +199,7 @@ class AICPicker(AutoPicker):
|
|||||||
# minimum in AIC function
|
# minimum in AIC function
|
||||||
icfmax = np.argmax(cf)
|
icfmax = np.argmax(cf)
|
||||||
|
|
||||||
# MP MP testing threshold
|
# TODO: If this shall be kept, maybe add thresh_factor to pylot parameters
|
||||||
thresh_hit = False
|
thresh_hit = False
|
||||||
thresh_factor = 0.7
|
thresh_factor = 0.7
|
||||||
thresh = thresh_factor * cf[icfmax]
|
thresh = thresh_factor * cf[icfmax]
|
||||||
@@ -209,7 +211,6 @@ class AICPicker(AutoPicker):
|
|||||||
if sample <= cf[index - 1]:
|
if sample <= cf[index - 1]:
|
||||||
icfmax = index - 1
|
icfmax = index - 1
|
||||||
break
|
break
|
||||||
# MP MP ---
|
|
||||||
|
|
||||||
# find minimum in AIC-CF front of maximum of HOS/AR-CF
|
# find minimum in AIC-CF front of maximum of HOS/AR-CF
|
||||||
lpickwindow = int(round(self.PickWindow / self.dt))
|
lpickwindow = int(round(self.PickWindow / self.dt))
|
||||||
|
|||||||
+25
-25
@@ -15,7 +15,7 @@ import numpy as np
|
|||||||
from obspy.core import Stream, UTCDateTime
|
from obspy.core import Stream, UTCDateTime
|
||||||
from scipy.signal import argrelmax
|
from scipy.signal import argrelmax
|
||||||
|
|
||||||
from pylot.core.util.utils import get_bool, get_None, SetChannelComponents
|
from pylot.core.util.utils import get_bool, get_none, SetChannelComponents
|
||||||
|
|
||||||
|
|
||||||
def earllatepicker(X, nfac, TSNR, Pick1, iplot=0, verbosity=1, fig=None, linecolor='k'):
|
def earllatepicker(X, nfac, TSNR, Pick1, iplot=0, verbosity=1, fig=None, linecolor='k'):
|
||||||
@@ -136,7 +136,7 @@ def earllatepicker(X, nfac, TSNR, Pick1, iplot=0, verbosity=1, fig=None, linecol
|
|||||||
PickError = symmetrize_error(diffti_te, diffti_tl)
|
PickError = symmetrize_error(diffti_te, diffti_tl)
|
||||||
|
|
||||||
if iplot > 1:
|
if iplot > 1:
|
||||||
if get_None(fig) is None:
|
if get_none(fig) is None:
|
||||||
fig = plt.figure() # iplot)
|
fig = plt.figure() # iplot)
|
||||||
plt_flag = 1
|
plt_flag = 1
|
||||||
fig._tight = True
|
fig._tight = True
|
||||||
@@ -275,7 +275,7 @@ def fmpicker(Xraw, Xfilt, pickwin, Pick, iplot=0, fig=None, linecolor='k'):
|
|||||||
try:
|
try:
|
||||||
P1 = np.polyfit(xslope1, xraw[islope1], 1)
|
P1 = np.polyfit(xslope1, xraw[islope1], 1)
|
||||||
datafit1 = np.polyval(P1, xslope1)
|
datafit1 = np.polyval(P1, xslope1)
|
||||||
except Exception as e:
|
except ValueError as e:
|
||||||
print("fmpicker: Problems with data fit! {}".format(e))
|
print("fmpicker: Problems with data fit! {}".format(e))
|
||||||
print("Skip first motion determination!")
|
print("Skip first motion determination!")
|
||||||
return FM
|
return FM
|
||||||
@@ -321,7 +321,7 @@ def fmpicker(Xraw, Xfilt, pickwin, Pick, iplot=0, fig=None, linecolor='k'):
|
|||||||
try:
|
try:
|
||||||
P2 = np.polyfit(xslope2, xfilt[islope2], 1)
|
P2 = np.polyfit(xslope2, xfilt[islope2], 1)
|
||||||
datafit2 = np.polyval(P2, xslope2)
|
datafit2 = np.polyval(P2, xslope2)
|
||||||
except Exception as e:
|
except ValueError as e:
|
||||||
emsg = 'fmpicker: polyfit failed: {}'.format(e)
|
emsg = 'fmpicker: polyfit failed: {}'.format(e)
|
||||||
print(emsg)
|
print(emsg)
|
||||||
return FM
|
return FM
|
||||||
@@ -344,7 +344,7 @@ def fmpicker(Xraw, Xfilt, pickwin, Pick, iplot=0, fig=None, linecolor='k'):
|
|||||||
print("fmpicker: Found polarity %s" % FM)
|
print("fmpicker: Found polarity %s" % FM)
|
||||||
|
|
||||||
if iplot > 1:
|
if iplot > 1:
|
||||||
if get_None(fig) is None:
|
if get_none(fig) is None:
|
||||||
fig = plt.figure() # iplot)
|
fig = plt.figure() # iplot)
|
||||||
plt_flag = 1
|
plt_flag = 1
|
||||||
fig._tight = True
|
fig._tight = True
|
||||||
@@ -868,7 +868,7 @@ def checksignallength(X, pick, minsiglength, pickparams, iplot=0, fig=None, line
|
|||||||
returnflag = 0
|
returnflag = 0
|
||||||
|
|
||||||
if iplot > 1:
|
if iplot > 1:
|
||||||
if get_None(fig) is None:
|
if get_none(fig) is None:
|
||||||
fig = plt.figure() # iplot)
|
fig = plt.figure() # iplot)
|
||||||
plt_flag = 1
|
plt_flag = 1
|
||||||
fig._tight = True
|
fig._tight = True
|
||||||
@@ -890,6 +890,8 @@ def checksignallength(X, pick, minsiglength, pickparams, iplot=0, fig=None, line
|
|||||||
input()
|
input()
|
||||||
except SyntaxError:
|
except SyntaxError:
|
||||||
pass
|
pass
|
||||||
|
except EOFError:
|
||||||
|
pass
|
||||||
plt.close(fig)
|
plt.close(fig)
|
||||||
|
|
||||||
return returnflag
|
return returnflag
|
||||||
@@ -1211,14 +1213,14 @@ def checkZ4S(X, pick, pickparams, iplot, fig=None, linecolor='k'):
|
|||||||
t = np.linspace(diff_dict[key], trace.stats.endtime - trace.stats.starttime + diff_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 i == 0:
|
||||||
if get_None(fig) is None:
|
if get_none(fig) is None:
|
||||||
fig = plt.figure() # self.iplot) ### WHY? MP MP
|
fig = plt.figure() # self.iplot) ### WHY? MP MP
|
||||||
plt_flag = 1
|
plt_flag = 1
|
||||||
ax1 = fig.add_subplot(3, 1, i + 1)
|
ax1 = fig.add_subplot(3, 1, i + 1)
|
||||||
ax = ax1
|
ax = ax1
|
||||||
ax.set_title('CheckZ4S, Station %s' % zdat[0].stats.station)
|
ax.set_title('CheckZ4S, Station %s' % zdat[0].stats.station)
|
||||||
else:
|
else:
|
||||||
if get_None(fig) is None:
|
if get_none(fig) is None:
|
||||||
fig = plt.figure() # self.iplot) ### WHY? MP MP
|
fig = plt.figure() # self.iplot) ### WHY? MP MP
|
||||||
plt_flag = 1
|
plt_flag = 1
|
||||||
ax = fig.add_subplot(3, 1, i + 1, sharex=ax1)
|
ax = fig.add_subplot(3, 1, i + 1, sharex=ax1)
|
||||||
@@ -1332,22 +1334,6 @@ def get_quality_class(uncertainty, weight_classes):
|
|||||||
return quality
|
return quality
|
||||||
|
|
||||||
|
|
||||||
def set_NaNs_to(data, nan_value):
|
|
||||||
"""
|
|
||||||
Replace all NaNs in data with nan_value
|
|
||||||
:param data: array holding data
|
|
||||||
:type data: `~numpy.ndarray`
|
|
||||||
:param nan_value: value which all NaNs are set to
|
|
||||||
:type nan_value: float, int
|
|
||||||
:return: data array with all NaNs replaced with nan_value
|
|
||||||
:rtype: `~numpy.ndarray`
|
|
||||||
"""
|
|
||||||
nn = np.isnan(data)
|
|
||||||
if np.any(nn):
|
|
||||||
data[nn] = nan_value
|
|
||||||
return data
|
|
||||||
|
|
||||||
|
|
||||||
def taper_cf(cf):
|
def taper_cf(cf):
|
||||||
"""
|
"""
|
||||||
Taper cf data to get rid off of side maximas
|
Taper cf data to get rid off of side maximas
|
||||||
@@ -1508,7 +1494,7 @@ def getQualityFromUncertainty(uncertainty, Errors):
|
|||||||
# set initial quality to 4 (worst) and change only if one condition is hit
|
# set initial quality to 4 (worst) and change only if one condition is hit
|
||||||
quality = 4
|
quality = 4
|
||||||
|
|
||||||
if get_None(uncertainty) is None:
|
if get_none(uncertainty) is None:
|
||||||
return quality
|
return quality
|
||||||
|
|
||||||
if uncertainty <= Errors[0]:
|
if uncertainty <= Errors[0]:
|
||||||
@@ -1532,3 +1518,17 @@ if __name__ == '__main__':
|
|||||||
import doctest
|
import doctest
|
||||||
|
|
||||||
doctest.testmod()
|
doctest.testmod()
|
||||||
|
|
||||||
|
|
||||||
|
class PickingFailedException(Exception):
|
||||||
|
"""
|
||||||
|
Raised when picking fails due to missing values etc.
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class MissingTraceException(ValueError):
|
||||||
|
"""
|
||||||
|
Used to indicate missing traces in a obspy.core.stream.Stream object
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|||||||
+105
-74
@@ -5,14 +5,16 @@ import traceback
|
|||||||
|
|
||||||
import cartopy.crs as ccrs
|
import cartopy.crs as ccrs
|
||||||
import cartopy.feature as cf
|
import cartopy.feature as cf
|
||||||
|
from cartopy.mpl.ticker import LongitudeFormatter, LatitudeFormatter
|
||||||
import matplotlib
|
import matplotlib
|
||||||
import matplotlib.patheffects as PathEffects
|
import matplotlib.patheffects as PathEffects
|
||||||
import matplotlib.pyplot as plt
|
import matplotlib.pyplot as plt
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import obspy
|
import obspy
|
||||||
from PySide2 import QtWidgets
|
from PySide2 import QtWidgets, QtGui
|
||||||
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
|
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
|
||||||
from mpl_toolkits.axes_grid1.inset_locator import inset_axes
|
from mpl_toolkits.axes_grid1.inset_locator import inset_axes
|
||||||
|
from pylot.core.util.utils import identifyPhaseID
|
||||||
from scipy.interpolate import griddata
|
from scipy.interpolate import griddata
|
||||||
|
|
||||||
from pylot.core.pick.utils import get_quality_class
|
from pylot.core.pick.utils import get_quality_class
|
||||||
@@ -23,10 +25,10 @@ matplotlib.use('Qt5Agg')
|
|||||||
|
|
||||||
class MplCanvas(FigureCanvas):
|
class MplCanvas(FigureCanvas):
|
||||||
|
|
||||||
def __init__(self, parent=None, extern_axes=None, width=5, height=4, dpi=100):
|
def __init__(self, extern_axes=None, projection=None, width=15, height=5, dpi=100):
|
||||||
if extern_axes is None:
|
if extern_axes is None:
|
||||||
self.fig = plt.figure(figsize=(width, height), dpi=dpi)
|
self.fig = plt.figure(figsize=(width, height), dpi=dpi)
|
||||||
self.axes = self.fig.add_subplot(111)
|
self.axes = self.fig.add_subplot(111, projection=projection)
|
||||||
else:
|
else:
|
||||||
self.fig = extern_axes.figure
|
self.fig = extern_axes.figure
|
||||||
self.axes = extern_axes
|
self.axes = extern_axes
|
||||||
@@ -62,20 +64,25 @@ class Array_map(QtWidgets.QWidget):
|
|||||||
self.highlighted_stations = []
|
self.highlighted_stations = []
|
||||||
|
|
||||||
# call functions to draw everything
|
# call functions to draw everything
|
||||||
|
self.projection = ccrs.PlateCarree()
|
||||||
self.init_graphics()
|
self.init_graphics()
|
||||||
|
self.ax = self.canvas.axes
|
||||||
|
self.ax.set_adjustable('datalim')
|
||||||
|
|
||||||
self.init_stations()
|
self.init_stations()
|
||||||
self.init_crtpyMap()
|
self.init_crtpyMap()
|
||||||
self.init_map()
|
self.init_map()
|
||||||
|
|
||||||
# set original map limits to fall back on when home button is pressed
|
# set original map limits to fall back on when home button is pressed
|
||||||
self.org_xlim = self.canvas.axes.get_xlim()
|
self.org_xlim = self.ax.get_xlim()
|
||||||
self.org_ylim = self.canvas.axes.get_ylim()
|
self.org_ylim = self.ax.get_ylim()
|
||||||
|
|
||||||
# initial map without event
|
# initial map without event
|
||||||
self.canvas.axes.set_xlim(self.org_xlim[0], self.org_xlim[1])
|
self.ax.set_xlim(self.org_xlim[0], self.org_xlim[1])
|
||||||
self.canvas.axes.set_ylim(self.org_ylim[0], self.org_ylim[1])
|
self.ax.set_ylim(self.org_ylim[0], self.org_ylim[1])
|
||||||
|
|
||||||
self._style = None if not hasattr(parent, '_style') else parent._style
|
self._style = None if not hasattr(parent, '_style') else parent._style
|
||||||
|
|
||||||
|
|
||||||
def init_map(self):
|
def init_map(self):
|
||||||
self.init_colormap()
|
self.init_colormap()
|
||||||
self.connectSignals()
|
self.connectSignals()
|
||||||
@@ -88,11 +95,11 @@ class Array_map(QtWidgets.QWidget):
|
|||||||
# initialize figure elements
|
# initialize figure elements
|
||||||
|
|
||||||
if self.extern_plot_axes is None:
|
if self.extern_plot_axes is None:
|
||||||
self.canvas = MplCanvas(self)
|
self.canvas = MplCanvas(projection=self.projection)
|
||||||
self.plotWidget = FigureCanvas(self.canvas.fig)
|
|
||||||
else:
|
else:
|
||||||
self.canvas = MplCanvas(self, extern_axes=self.extern_plot_axes)
|
self.canvas = MplCanvas(extern_axes=self.extern_plot_axes)
|
||||||
self.plotWidget = FigureCanvas(self.canvas.fig)
|
|
||||||
|
self.plotWidget = self.canvas
|
||||||
|
|
||||||
# initialize GUI elements
|
# initialize GUI elements
|
||||||
self.status_label = QtWidgets.QLabel()
|
self.status_label = QtWidgets.QLabel()
|
||||||
@@ -104,7 +111,7 @@ class Array_map(QtWidgets.QWidget):
|
|||||||
self.setLayout(self.main_box)
|
self.setLayout(self.main_box)
|
||||||
|
|
||||||
self.top_row = QtWidgets.QHBoxLayout()
|
self.top_row = QtWidgets.QHBoxLayout()
|
||||||
self.main_box.addLayout(self.top_row, 1)
|
self.main_box.addLayout(self.top_row, 0)
|
||||||
|
|
||||||
self.comboBox_phase = QtWidgets.QComboBox()
|
self.comboBox_phase = QtWidgets.QComboBox()
|
||||||
self.comboBox_phase.insertItem(0, 'P')
|
self.comboBox_phase.insertItem(0, 'P')
|
||||||
@@ -123,8 +130,8 @@ class Array_map(QtWidgets.QWidget):
|
|||||||
self.cmaps_box = QtWidgets.QComboBox()
|
self.cmaps_box = QtWidgets.QComboBox()
|
||||||
self.cmaps_box.setMaxVisibleItems(20)
|
self.cmaps_box.setMaxVisibleItems(20)
|
||||||
[self.cmaps_box.addItem(map_name) for map_name in sorted(plt.colormaps())]
|
[self.cmaps_box.addItem(map_name) for map_name in sorted(plt.colormaps())]
|
||||||
# try to set to hsv as default
|
# try to set to viridis as default
|
||||||
self.cmaps_box.setCurrentIndex(self.cmaps_box.findText('hsv'))
|
self.cmaps_box.setCurrentIndex(self.cmaps_box.findText('viridis'))
|
||||||
|
|
||||||
self.top_row.addWidget(QtWidgets.QLabel('Select a phase: '))
|
self.top_row.addWidget(QtWidgets.QLabel('Select a phase: '))
|
||||||
self.top_row.addWidget(self.comboBox_phase)
|
self.top_row.addWidget(self.comboBox_phase)
|
||||||
@@ -137,10 +144,10 @@ class Array_map(QtWidgets.QWidget):
|
|||||||
self.top_row.addWidget(self.auto_refresh_box)
|
self.top_row.addWidget(self.auto_refresh_box)
|
||||||
self.top_row.addWidget(self.refresh_button)
|
self.top_row.addWidget(self.refresh_button)
|
||||||
|
|
||||||
self.main_box.addWidget(self.plotWidget, 1)
|
self.main_box.addWidget(self.plotWidget, 10)
|
||||||
|
|
||||||
self.bot_row = QtWidgets.QHBoxLayout()
|
self.bot_row = QtWidgets.QHBoxLayout()
|
||||||
self.main_box.addLayout(self.bot_row, 0.3)
|
self.main_box.addLayout(self.bot_row, 0)
|
||||||
self.bot_row.addWidget(QtWidgets.QLabel(''), 5)
|
self.bot_row.addWidget(QtWidgets.QLabel(''), 5)
|
||||||
self.bot_row.addWidget(self.map_reset_button, 2)
|
self.bot_row.addWidget(self.map_reset_button, 2)
|
||||||
self.bot_row.addWidget(self.go2eq_button, 2)
|
self.bot_row.addWidget(self.go2eq_button, 2)
|
||||||
@@ -152,14 +159,12 @@ class Array_map(QtWidgets.QWidget):
|
|||||||
self.init_lat_lon_grid()
|
self.init_lat_lon_grid()
|
||||||
|
|
||||||
def init_crtpyMap(self):
|
def init_crtpyMap(self):
|
||||||
self.canvas.axes.cla()
|
self.ax.add_feature(cf.LAND)
|
||||||
self.canvas.axes = plt.axes(projection=ccrs.PlateCarree())
|
self.ax.add_feature(cf.OCEAN)
|
||||||
self.canvas.axes.add_feature(cf.LAND)
|
self.ax.add_feature(cf.COASTLINE, linewidth=1, edgecolor='gray')
|
||||||
self.canvas.axes.add_feature(cf.OCEAN)
|
self.ax.add_feature(cf.BORDERS, alpha=0.7)
|
||||||
self.canvas.axes.add_feature(cf.COASTLINE, linewidth=1, edgecolor='gray')
|
self.ax.add_feature(cf.LAKES, alpha=0.7)
|
||||||
self.canvas.axes.add_feature(cf.BORDERS, alpha=0.7)
|
self.ax.add_feature(cf.RIVERS, linewidth=1)
|
||||||
self.canvas.axes.add_feature(cf.LAKES, alpha=0.7)
|
|
||||||
self.canvas.axes.add_feature(cf.RIVERS, linewidth=1)
|
|
||||||
|
|
||||||
# parallels and meridians
|
# parallels and meridians
|
||||||
self.add_merid_paral()
|
self.add_merid_paral()
|
||||||
@@ -167,12 +172,8 @@ class Array_map(QtWidgets.QWidget):
|
|||||||
self.canvas.fig.tight_layout()
|
self.canvas.fig.tight_layout()
|
||||||
|
|
||||||
def add_merid_paral(self):
|
def add_merid_paral(self):
|
||||||
self.gridlines = self.canvas.axes.gridlines(draw_labels=False, alpha=0.6, color='gray',
|
self.gridlines = self.ax.gridlines(draw_labels=False, alpha=0.6, color='gray',
|
||||||
linewidth=self.linewidth / 2, zorder=7)
|
linewidth=self.linewidth / 2, zorder=7, crs=ccrs.PlateCarree())
|
||||||
# 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):
|
def remove_merid_paral(self):
|
||||||
if len(self.gridlines.xline_artists):
|
if len(self.gridlines.xline_artists):
|
||||||
@@ -180,24 +181,24 @@ class Array_map(QtWidgets.QWidget):
|
|||||||
self.gridlines.yline_artists[0].remove()
|
self.gridlines.yline_artists[0].remove()
|
||||||
|
|
||||||
def org_map_view(self):
|
def org_map_view(self):
|
||||||
self.canvas.axes.set_xlim(self.org_xlim[0], self.org_xlim[1])
|
self.ax.set_xlim(self.org_xlim[0], self.org_xlim[1])
|
||||||
self.canvas.axes.set_ylim(self.org_ylim[0], self.org_ylim[1])
|
self.ax.set_ylim(self.org_ylim[0], self.org_ylim[1])
|
||||||
# parallels and meridians
|
# parallels and meridians
|
||||||
self.remove_merid_paral()
|
#self.remove_merid_paral()
|
||||||
self.add_merid_paral()
|
#self.add_merid_paral()
|
||||||
|
|
||||||
self.canvas.axes.figure.canvas.draw_idle()
|
self.canvas.draw_idle()
|
||||||
|
|
||||||
def go2eq(self):
|
def go2eq(self):
|
||||||
if self.eventLoc:
|
if self.eventLoc:
|
||||||
lats, lons = self.eventLoc
|
lats, lons = self.eventLoc
|
||||||
self.canvas.axes.set_xlim(lons - 10, lons + 10)
|
self.ax.set_xlim(lons - 10, lons + 10)
|
||||||
self.canvas.axes.set_ylim(lats - 5, lats + 5)
|
self.ax.set_ylim(lats - 5, lats + 5)
|
||||||
# parallels and meridians
|
# parallels and meridians
|
||||||
self.remove_merid_paral()
|
#self.remove_merid_paral()
|
||||||
self.add_merid_paral()
|
#self.add_merid_paral()
|
||||||
|
|
||||||
self.canvas.axes.figure.canvas.draw_idle()
|
self.canvas.draw_idle()
|
||||||
|
|
||||||
else:
|
else:
|
||||||
self.status_label.setText('No event information available')
|
self.status_label.setText('No event information available')
|
||||||
@@ -219,21 +220,32 @@ class Array_map(QtWidgets.QWidget):
|
|||||||
|
|
||||||
# set mouse events -----------------------------------------------------
|
# set mouse events -----------------------------------------------------
|
||||||
def mouse_moved(self, event):
|
def mouse_moved(self, event):
|
||||||
if not event.inaxes == self.canvas.axes:
|
if not event.inaxes == self.ax:
|
||||||
return
|
return
|
||||||
|
else:
|
||||||
|
cont, inds = self.sc.contains(event)
|
||||||
lat = event.ydata
|
lat = event.ydata
|
||||||
lon = event.xdata
|
lon = event.xdata
|
||||||
self.status_label.setText('Latitude: {:3.5f}, Longitude: {:3.5f}'.format(lat, lon))
|
text = f'Longitude: {lon:3.3f}, Latitude: {lat:3.3f}'
|
||||||
|
|
||||||
|
if cont:
|
||||||
|
indices = inds['ind']
|
||||||
|
text += ' | Station: ' if len(indices) == 1 else ' | Stations: '
|
||||||
|
text += ' - '.join([self._station_onpick_ids[index] for index in indices[:5]])
|
||||||
|
if len(indices) > 5:
|
||||||
|
text += '...'
|
||||||
|
|
||||||
|
self.status_label.setText(text)
|
||||||
|
|
||||||
def mouse_scroll(self, event):
|
def mouse_scroll(self, event):
|
||||||
if not event.inaxes == self.canvas.axes:
|
if not event.inaxes == self.ax:
|
||||||
return
|
return
|
||||||
|
|
||||||
zoom = {'up': 1. / 2., 'down': 2.}
|
zoom = {'up': 1. / 2., 'down': 2.}
|
||||||
|
|
||||||
if event.button in zoom:
|
if event.button in zoom:
|
||||||
xlim = self.canvas.axes.get_xlim()
|
xlim = self.ax.get_xlim()
|
||||||
ylim = self.canvas.axes.get_ylim()
|
ylim = self.ax.get_ylim()
|
||||||
|
|
||||||
x, y = event.xdata, event.ydata
|
x, y = event.xdata, event.ydata
|
||||||
|
|
||||||
@@ -245,24 +257,24 @@ class Array_map(QtWidgets.QWidget):
|
|||||||
yb = y - 0.5 * ydiff
|
yb = y - 0.5 * ydiff
|
||||||
yt = y + 0.5 * ydiff
|
yt = y + 0.5 * ydiff
|
||||||
|
|
||||||
self.canvas.axes.set_xlim(xl, xr)
|
self.ax.set_xlim(xl, xr)
|
||||||
self.canvas.axes.set_ylim(yb, yt)
|
self.ax.set_ylim(yb, yt)
|
||||||
# parallels and meridians
|
# parallels and meridians
|
||||||
self.remove_merid_paral()
|
#self.remove_merid_paral()
|
||||||
self.add_merid_paral()
|
#self.add_merid_paral()
|
||||||
|
|
||||||
self.canvas.axes.figure.canvas.draw_idle()
|
self.ax.figure.canvas.draw_idle()
|
||||||
|
|
||||||
def mouseLeftPress(self, event):
|
def mouseLeftPress(self, event):
|
||||||
if not event.inaxes == self.canvas.axes:
|
if not event.inaxes == self.ax:
|
||||||
return
|
return
|
||||||
self.map_x = event.xdata
|
self.map_x = event.xdata
|
||||||
self.map_y = event.ydata
|
self.map_y = event.ydata
|
||||||
self.map_xlim = self.canvas.axes.get_xlim()
|
self.map_xlim = self.ax.get_xlim()
|
||||||
self.map_ylim = self.canvas.axes.get_ylim()
|
self.map_ylim = self.ax.get_ylim()
|
||||||
|
|
||||||
def mouseLeftRelease(self, event):
|
def mouseLeftRelease(self, event):
|
||||||
if not event.inaxes == self.canvas.axes:
|
if not event.inaxes == self.ax:
|
||||||
return
|
return
|
||||||
new_x = event.xdata
|
new_x = event.xdata
|
||||||
new_y = event.ydata
|
new_y = event.ydata
|
||||||
@@ -270,18 +282,21 @@ class Array_map(QtWidgets.QWidget):
|
|||||||
dx = new_x - self.map_x
|
dx = new_x - self.map_x
|
||||||
dy = new_y - self.map_y
|
dy = new_y - self.map_y
|
||||||
|
|
||||||
self.canvas.axes.set_xlim((self.map_xlim[0] - dx, self.map_xlim[1] - dx))
|
self.ax.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)
|
self.ax.set_ylim(self.map_ylim[0] - dy, self.map_ylim[1] - dy)
|
||||||
# parallels and meridians
|
# parallels and meridians
|
||||||
self.remove_merid_paral()
|
#self.remove_merid_paral()
|
||||||
self.add_merid_paral()
|
#self.add_merid_paral()
|
||||||
|
|
||||||
self.canvas.axes.figure.canvas.draw_idle()
|
self.ax.figure.canvas.draw_idle()
|
||||||
|
|
||||||
def onpick(self, event):
|
def onpick(self, event):
|
||||||
|
btn_msg = {1: ' in selection. Aborted', 2: ' to delete a pick on. Aborted', 3: ' to display info.'}
|
||||||
ind = event.ind
|
ind = event.ind
|
||||||
button = event.mouseevent.button
|
button = event.mouseevent.button
|
||||||
if ind == []:
|
msg_reason = None
|
||||||
|
if len(ind) > 1:
|
||||||
|
self._parent.update_status(f'Found more than one station {btn_msg.get(button)}')
|
||||||
return
|
return
|
||||||
if button == 1:
|
if button == 1:
|
||||||
self.openPickDlg(ind)
|
self.openPickDlg(ind)
|
||||||
@@ -384,7 +399,14 @@ class Array_map(QtWidgets.QWidget):
|
|||||||
try:
|
try:
|
||||||
station_name = st_id.split('.')[-1]
|
station_name = st_id.split('.')[-1]
|
||||||
# current_picks_dict: auto or manual
|
# current_picks_dict: auto or manual
|
||||||
pick = self.current_picks_dict()[station_name][phase]
|
station_picks = self.current_picks_dict().get(station_name)
|
||||||
|
if not station_picks:
|
||||||
|
continue
|
||||||
|
for phase_hint, pick in station_picks.items():
|
||||||
|
if identifyPhaseID(phase_hint) == phase:
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
continue
|
||||||
if pick['picker'] == 'auto':
|
if pick['picker'] == 'auto':
|
||||||
if not pick['spe']:
|
if not pick['spe']:
|
||||||
continue
|
continue
|
||||||
@@ -410,6 +432,9 @@ class Array_map(QtWidgets.QWidget):
|
|||||||
picks_rel[st_id] = pick
|
picks_rel[st_id] = pick
|
||||||
return picks_rel
|
return picks_rel
|
||||||
|
|
||||||
|
def get_picks_rel_mean_corr(picks):
|
||||||
|
return get_picks_rel(picks, func=np.nanmean)
|
||||||
|
|
||||||
self.picks, self.uncertainties = get_picks(self.stations_dict)
|
self.picks, self.uncertainties = get_picks(self.stations_dict)
|
||||||
self.picks_rel = get_picks_rel(self.picks)
|
self.picks_rel = get_picks_rel(self.picks)
|
||||||
|
|
||||||
@@ -458,13 +483,20 @@ class Array_map(QtWidgets.QWidget):
|
|||||||
stat_dict = self.stations_dict['{}.{}'.format(network, station)]
|
stat_dict = self.stations_dict['{}.{}'.format(network, station)]
|
||||||
lat = stat_dict['latitude']
|
lat = stat_dict['latitude']
|
||||||
lon = stat_dict['longitude']
|
lon = stat_dict['longitude']
|
||||||
self.highlighted_stations.append(self.canvas.axes.scatter(lon, lat, s=self.pointsize, edgecolors=color,
|
self.highlighted_stations.append(self.ax.scatter(lon, lat, s=self.pointsize, edgecolors=color,
|
||||||
facecolors='none', zorder=12,
|
facecolors='none', zorder=12,
|
||||||
transform=ccrs.PlateCarree(), label='deleted'))
|
transform=ccrs.PlateCarree(), label='deleted'))
|
||||||
|
|
||||||
def openPickDlg(self, ind):
|
def openPickDlg(self, ind):
|
||||||
|
try:
|
||||||
wfdata = self._parent.get_data().getWFData()
|
wfdata = self._parent.get_data().getWFData()
|
||||||
wfdata_comp = self._parent.get_data().getWFDataComp()
|
except AttributeError:
|
||||||
|
QtWidgets.QMessageBox.warning(
|
||||||
|
self, "PyLoT Warning",
|
||||||
|
"No waveform data found. Check if they were already loaded in Waveform plot tab."
|
||||||
|
)
|
||||||
|
return
|
||||||
|
wfdata_comp = self._parent.get_data().getAltWFdata()
|
||||||
for index in ind:
|
for index in ind:
|
||||||
network, station = self._station_onpick_ids[index].split('.')[:2]
|
network, station = self._station_onpick_ids[index].split('.')[:2]
|
||||||
pyl_mw = self._parent
|
pyl_mw = self._parent
|
||||||
@@ -479,7 +511,6 @@ class Array_map(QtWidgets.QWidget):
|
|||||||
picks=self._parent.get_current_event().getPick(station),
|
picks=self._parent.get_current_event().getPick(station),
|
||||||
autopicks=self._parent.get_current_event().getAutopick(station),
|
autopicks=self._parent.get_current_event().getAutopick(station),
|
||||||
filteroptions=self._parent.filteroptions, metadata=self.metadata,
|
filteroptions=self._parent.filteroptions, metadata=self.metadata,
|
||||||
model=self.parameter.get('taup_model'),
|
|
||||||
event=pyl_mw.get_current_event())
|
event=pyl_mw.get_current_event())
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
message = 'Could not generate Plot for station {st}.\n {er}'.format(st=station, er=e)
|
message = 'Could not generate Plot for station {st}.\n {er}'.format(st=station, er=e)
|
||||||
@@ -510,7 +541,7 @@ class Array_map(QtWidgets.QWidget):
|
|||||||
def draw_contour_filled(self, nlevel=50):
|
def draw_contour_filled(self, nlevel=50):
|
||||||
levels = np.linspace(self.get_min_from_picks(), self.get_max_from_picks(), nlevel)
|
levels = np.linspace(self.get_min_from_picks(), self.get_max_from_picks(), nlevel)
|
||||||
|
|
||||||
self.contourf = self.canvas.axes.contourf(self.longrid, self.latgrid, self.picksgrid_active, levels,
|
self.contourf = self.ax.contourf(self.longrid, self.latgrid, self.picksgrid_active, levels,
|
||||||
linewidths=self.linewidth * 5, transform=ccrs.PlateCarree(),
|
linewidths=self.linewidth * 5, transform=ccrs.PlateCarree(),
|
||||||
alpha=0.4, zorder=8, cmap=self.get_colormap())
|
alpha=0.4, zorder=8, cmap=self.get_colormap())
|
||||||
|
|
||||||
@@ -520,7 +551,7 @@ class Array_map(QtWidgets.QWidget):
|
|||||||
def scatter_all_stations(self):
|
def scatter_all_stations(self):
|
||||||
stations, lats, lons = self.get_st_lat_lon_for_plot()
|
stations, lats, lons = self.get_st_lat_lon_for_plot()
|
||||||
|
|
||||||
self.sc = self.canvas.axes.scatter(lons, lats, s=self.pointsize * 3, facecolor='none', marker='.',
|
self.sc = self.ax.scatter(lons, lats, s=self.pointsize * 3, facecolor='none', marker='.',
|
||||||
zorder=10, picker=True, edgecolor='0.5', label='Not Picked',
|
zorder=10, picker=True, edgecolor='0.5', label='Not Picked',
|
||||||
transform=ccrs.PlateCarree())
|
transform=ccrs.PlateCarree())
|
||||||
|
|
||||||
@@ -528,7 +559,7 @@ class Array_map(QtWidgets.QWidget):
|
|||||||
self._station_onpick_ids = stations
|
self._station_onpick_ids = stations
|
||||||
if self.eventLoc:
|
if self.eventLoc:
|
||||||
lats, lons = self.eventLoc
|
lats, lons = self.eventLoc
|
||||||
self.sc_event = self.canvas.axes.scatter(lons, lats, s=5 * self.pointsize, facecolor='red', zorder=11,
|
self.sc_event = self.ax.scatter(lons, lats, s=5 * self.pointsize, facecolor='red', zorder=11,
|
||||||
label='Event (might be outside map region)', marker='*',
|
label='Event (might be outside map region)', marker='*',
|
||||||
edgecolors='black',
|
edgecolors='black',
|
||||||
transform=ccrs.PlateCarree())
|
transform=ccrs.PlateCarree())
|
||||||
@@ -544,7 +575,7 @@ class Array_map(QtWidgets.QWidget):
|
|||||||
for uncertainty in uncertainties])
|
for uncertainty in uncertainties])
|
||||||
|
|
||||||
cmap = self.get_colormap()
|
cmap = self.get_colormap()
|
||||||
self.sc_picked = self.canvas.axes.scatter(lons, lats, s=sizes, edgecolors='white', cmap=cmap,
|
self.sc_picked = self.ax.scatter(lons, lats, s=sizes, edgecolors='white', cmap=cmap,
|
||||||
c=picks, zorder=11, label='Picked', transform=ccrs.PlateCarree())
|
c=picks, zorder=11, label='Picked', transform=ccrs.PlateCarree())
|
||||||
|
|
||||||
def annotate_ax(self):
|
def annotate_ax(self):
|
||||||
@@ -563,20 +594,20 @@ class Array_map(QtWidgets.QWidget):
|
|||||||
if st in self.marked_stations:
|
if st in self.marked_stations:
|
||||||
color = 'red'
|
color = 'red'
|
||||||
self.annotations.append(
|
self.annotations.append(
|
||||||
self.canvas.axes.annotate(' %s' % st, xy=(x + 0.003, y + 0.003), fontsize=self.pointsize / 4.,
|
self.ax.annotate(f'{st}', xy=(x + 0.003, y + 0.003), fontsize=self.pointsize / 4.,
|
||||||
fontweight='semibold', color=color, alpha=0.8,
|
fontweight='semibold', color=color, alpha=0.8,
|
||||||
transform=ccrs.PlateCarree(), zorder=14,
|
transform=ccrs.PlateCarree(), zorder=14,
|
||||||
path_effects=[PathEffects.withStroke(
|
path_effects=[PathEffects.withStroke(
|
||||||
linewidth=self.pointsize / 15., foreground='k')]))
|
linewidth=self.pointsize / 15., foreground='k')]))
|
||||||
|
|
||||||
self.legend = self.canvas.axes.legend(loc=1, framealpha=1)
|
self.legend = self.ax.legend(loc=1, framealpha=1)
|
||||||
self.legend.set_zorder(100)
|
self.legend.set_zorder(100)
|
||||||
self.legend.get_frame().set_facecolor((1, 1, 1, 0.95))
|
self.legend.get_frame().set_facecolor((1, 1, 1, 0.95))
|
||||||
|
|
||||||
def add_cbar(self, label):
|
def add_cbar(self, label):
|
||||||
self.cbax_bg = inset_axes(self.canvas.axes, width="6%", height="75%", loc=5)
|
self.cbax_bg = inset_axes(self.ax, width="6%", height="75%", loc=5)
|
||||||
cbax = inset_axes(self.canvas.axes, width='2%', height='70%', loc=5)
|
cbax = inset_axes(self.ax, width='2%', height='70%', loc=5)
|
||||||
cbar = self.canvas.axes.figure.colorbar(self.sc_picked, cax=cbax)
|
cbar = self.ax.figure.colorbar(self.sc_picked, cax=cbax)
|
||||||
cbar.set_label(label)
|
cbar.set_label(label)
|
||||||
cbax.yaxis.tick_left()
|
cbax.yaxis.tick_left()
|
||||||
cbax.yaxis.set_label_position('left')
|
cbax.yaxis.set_label_position('left')
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
import glob
|
import glob
|
||||||
|
import logging
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
@@ -189,7 +190,11 @@ class Metadata(object):
|
|||||||
metadata = self.get_metadata(seed_id, time)
|
metadata = self.get_metadata(seed_id, time)
|
||||||
if not metadata:
|
if not metadata:
|
||||||
return
|
return
|
||||||
|
try:
|
||||||
return metadata['data'].get_coordinates(seed_id, time)
|
return metadata['data'].get_coordinates(seed_id, time)
|
||||||
|
# no specific exception defined in obspy inventory
|
||||||
|
except Exception as e:
|
||||||
|
logging.warning(f'Could not get metadata for {seed_id}')
|
||||||
|
|
||||||
def get_all_coordinates(self):
|
def get_all_coordinates(self):
|
||||||
def stat_info_from_parser(parser):
|
def stat_info_from_parser(parser):
|
||||||
|
|||||||
@@ -22,14 +22,11 @@ class Event(ObsPyEvent):
|
|||||||
:param path: path to event directory
|
:param path: path to event directory
|
||||||
:type path: str
|
:type path: str
|
||||||
"""
|
"""
|
||||||
# TODO: remove rootpath and database
|
|
||||||
self.pylot_id = path.split('/')[-1]
|
self.pylot_id = path.split('/')[-1]
|
||||||
# initialize super class
|
# initialize super class
|
||||||
super(Event, self).__init__(resource_id=ResourceIdentifier('smi:local/' + self.pylot_id))
|
super(Event, self).__init__(resource_id=ResourceIdentifier('smi:local/' + self.pylot_id))
|
||||||
self.path = path
|
self.path = path
|
||||||
self.database = path.split('/')[-2]
|
|
||||||
self.datapath = os.path.split(path)[0] # 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_autopicks = {}
|
||||||
self.pylot_picks = {}
|
self.pylot_picks = {}
|
||||||
self.notes = ''
|
self.notes = ''
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ def main(project_file_path, manual=False, auto=True, file_format='png', f_ext=''
|
|||||||
for item in input_list:
|
for item in input_list:
|
||||||
array_map_worker(item)
|
array_map_worker(item)
|
||||||
else:
|
else:
|
||||||
pool = multiprocessing.Pool(ncores)
|
pool = multiprocessing.Pool(ncores, maxtasksperchild=1000)
|
||||||
pool.map(array_map_worker, input_list)
|
pool.map(array_map_worker, input_list)
|
||||||
pool.close()
|
pool.close()
|
||||||
pool.join()
|
pool.join()
|
||||||
|
|||||||
@@ -160,7 +160,7 @@ class MultiThread(QThread):
|
|||||||
try:
|
try:
|
||||||
if not self.ncores:
|
if not self.ncores:
|
||||||
self.ncores = multiprocessing.cpu_count()
|
self.ncores = multiprocessing.cpu_count()
|
||||||
pool = multiprocessing.Pool(self.ncores)
|
pool = multiprocessing.Pool(self.ncores, maxtasksperchild=1000)
|
||||||
self.data = pool.map_async(self.func, self.args, callback=self.emitDone)
|
self.data = pool.map_async(self.func, self.args, callback=self.emitDone)
|
||||||
# self.data = pool.apply_async(self.func, self.shotlist, callback=self.emitDone) #emit each time returned
|
# self.data = pool.apply_async(self.func, self.shotlist, callback=self.emitDone) #emit each time returned
|
||||||
pool.close()
|
pool.close()
|
||||||
|
|||||||
+265
-165
@@ -1,12 +1,15 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
import glob
|
||||||
import hashlib
|
import hashlib
|
||||||
|
import logging
|
||||||
import os
|
import os
|
||||||
import platform
|
import platform
|
||||||
import re
|
import re
|
||||||
import subprocess
|
import subprocess
|
||||||
import warnings
|
import warnings
|
||||||
|
from typing import Literal, Tuple, Type
|
||||||
|
from functools import lru_cache
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from obspy import UTCDateTime, read
|
from obspy import UTCDateTime, read
|
||||||
@@ -18,6 +21,10 @@ from pylot.core.io.inputs import PylotParameter, FilterOptions
|
|||||||
from pylot.core.util.obspyDMT_interface import check_obspydmt_eventfolder
|
from pylot.core.util.obspyDMT_interface import check_obspydmt_eventfolder
|
||||||
from pylot.styles import style_settings
|
from pylot.styles import style_settings
|
||||||
|
|
||||||
|
Rgba: Type[tuple] = Tuple[int, int, int, int]
|
||||||
|
Mplrgba: Type[tuple] = Tuple[float, float, float, float]
|
||||||
|
Mplrgbastr: Type[tuple] = Tuple[str, str, str, str]
|
||||||
|
|
||||||
|
|
||||||
def _pickle_method(m):
|
def _pickle_method(m):
|
||||||
if m.im_self is None:
|
if m.im_self is None:
|
||||||
@@ -81,25 +88,6 @@ def fit_curve(x, y):
|
|||||||
return splev, splrep(x, y)
|
return splev, splrep(x, y)
|
||||||
|
|
||||||
|
|
||||||
def getindexbounds(f, eta):
|
|
||||||
"""
|
|
||||||
Get indices of values closest below and above maximum value in an array
|
|
||||||
:param f: array
|
|
||||||
:type f: `~numpy.ndarray`
|
|
||||||
:param eta: look for value in array that is closes to max_value * eta
|
|
||||||
:type eta: float
|
|
||||||
:return: tuple containing index of max value, index of value closest below max value,
|
|
||||||
index of value closest above max value
|
|
||||||
:rtype: (int, int, int)
|
|
||||||
"""
|
|
||||||
mi = f.argmax() # get indices of max values
|
|
||||||
m = max(f) # get maximum value
|
|
||||||
b = m * eta #
|
|
||||||
l = find_nearest(f[:mi], b) # find closest value below max value
|
|
||||||
u = find_nearest(f[mi:], b) + mi # find closest value above max value
|
|
||||||
return mi, l, u
|
|
||||||
|
|
||||||
|
|
||||||
def gen_Pool(ncores=0):
|
def gen_Pool(ncores=0):
|
||||||
"""
|
"""
|
||||||
Generate mulitprocessing pool object utilizing ncores amount of cores
|
Generate mulitprocessing pool object utilizing ncores amount of cores
|
||||||
@@ -119,7 +107,7 @@ def gen_Pool(ncores=0):
|
|||||||
|
|
||||||
print('gen_Pool: Generated multiprocessing Pool with {} cores\n'.format(ncores))
|
print('gen_Pool: Generated multiprocessing Pool with {} cores\n'.format(ncores))
|
||||||
|
|
||||||
pool = multiprocessing.Pool(ncores)
|
pool = multiprocessing.Pool(ncores, maxtasksperchild=100)
|
||||||
return pool
|
return pool
|
||||||
|
|
||||||
|
|
||||||
@@ -165,11 +153,11 @@ def clims(lim1, lim2):
|
|||||||
"""
|
"""
|
||||||
takes two pairs of limits and returns one pair of common limts
|
takes two pairs of limits and returns one pair of common limts
|
||||||
:param lim1: limit 1
|
:param lim1: limit 1
|
||||||
:type lim1: int
|
:type lim1: List[int]
|
||||||
:param lim2: limit 2
|
:param lim2: limit 2
|
||||||
:type lim2: int
|
:type lim2: List[int]
|
||||||
:return: new upper and lower limit common to both given limits
|
:return: new upper and lower limit common to both given limits
|
||||||
:rtype: [int, int]
|
:rtype: List[int]
|
||||||
|
|
||||||
>>> clims([0, 4], [1, 3])
|
>>> clims([0, 4], [1, 3])
|
||||||
[0, 4]
|
[0, 4]
|
||||||
@@ -301,7 +289,7 @@ def fnConstructor(s):
|
|||||||
if type(s) is str:
|
if type(s) is str:
|
||||||
s = s.split(':')[-1]
|
s = s.split(':')[-1]
|
||||||
else:
|
else:
|
||||||
s = getHash(UTCDateTime())
|
s = get_hash(UTCDateTime())
|
||||||
|
|
||||||
badchars = re.compile(r'[^A-Za-z0-9_. ]+|^\.|\.$|^ | $|^$')
|
badchars = re.compile(r'[^A-Za-z0-9_. ]+|^\.|\.$|^ | $|^$')
|
||||||
badsuffix = re.compile(r'(aux|com[1-9]|con|lpt[1-9]|prn)(\.|$)')
|
badsuffix = re.compile(r'(aux|com[1-9]|con|lpt[1-9]|prn)(\.|$)')
|
||||||
@@ -313,15 +301,32 @@ def fnConstructor(s):
|
|||||||
return fn
|
return fn
|
||||||
|
|
||||||
|
|
||||||
def get_None(value):
|
def get_none(value):
|
||||||
"""
|
"""
|
||||||
Convert "None" to None
|
Convert "None" to None
|
||||||
:param value:
|
:param value:
|
||||||
:type value: str, bool
|
:type value: str, NoneType
|
||||||
:return:
|
:return:
|
||||||
:rtype: bool
|
:rtype: type(value) or NoneType
|
||||||
|
|
||||||
|
>>> st = read()
|
||||||
|
>>> print(get_none(st))
|
||||||
|
3 Trace(s) in Stream:
|
||||||
|
BW.RJOB..EHZ | 2009-08-24T00:20:03.000000Z - 2009-08-24T00:20:32.990000Z | 100.0 Hz, 3000 samples
|
||||||
|
BW.RJOB..EHN | 2009-08-24T00:20:03.000000Z - 2009-08-24T00:20:32.990000Z | 100.0 Hz, 3000 samples
|
||||||
|
BW.RJOB..EHE | 2009-08-24T00:20:03.000000Z - 2009-08-24T00:20:32.990000Z | 100.0 Hz, 3000 samples
|
||||||
|
>>> get_none('Stream')
|
||||||
|
'Stream'
|
||||||
|
>>> get_none(0)
|
||||||
|
0
|
||||||
|
>>> get_none(0.)
|
||||||
|
0.0
|
||||||
|
>>> print(get_none('None'))
|
||||||
|
None
|
||||||
|
>>> print(get_none(None))
|
||||||
|
None
|
||||||
"""
|
"""
|
||||||
if value == 'None':
|
if value is None or (type(value) is str and value == 'None'):
|
||||||
return None
|
return None
|
||||||
else:
|
else:
|
||||||
return value
|
return value
|
||||||
@@ -329,11 +334,30 @@ def get_None(value):
|
|||||||
|
|
||||||
def get_bool(value):
|
def get_bool(value):
|
||||||
"""
|
"""
|
||||||
Convert string representations of bools to their true boolean value
|
Convert string representations of bools to their true boolean value. Return value if it cannot be identified as bool.
|
||||||
:param value:
|
:param value:
|
||||||
:type value: str, bool
|
:type value: str, bool, int, float
|
||||||
:return: true boolean value
|
:return: true boolean value
|
||||||
:rtype: bool
|
:rtype: bool
|
||||||
|
|
||||||
|
>>> get_bool(True)
|
||||||
|
True
|
||||||
|
>>> get_bool(False)
|
||||||
|
False
|
||||||
|
>>> get_bool(0)
|
||||||
|
False
|
||||||
|
>>> get_bool(0.)
|
||||||
|
False
|
||||||
|
>>> get_bool(0.1)
|
||||||
|
True
|
||||||
|
>>> get_bool(2)
|
||||||
|
True
|
||||||
|
>>> get_bool(-1)
|
||||||
|
False
|
||||||
|
>>> get_bool(-0.3)
|
||||||
|
False
|
||||||
|
>>> get_bool(None)
|
||||||
|
None
|
||||||
"""
|
"""
|
||||||
if type(value) is bool:
|
if type(value) is bool:
|
||||||
return value
|
return value
|
||||||
@@ -341,8 +365,14 @@ def get_bool(value):
|
|||||||
return True
|
return True
|
||||||
elif value in ['False', 'false']:
|
elif value in ['False', 'false']:
|
||||||
return False
|
return False
|
||||||
|
elif isinstance(value, float) or isinstance(value, int):
|
||||||
|
if value > 0. or value > 0:
|
||||||
|
return True
|
||||||
else:
|
else:
|
||||||
return bool(value)
|
return False
|
||||||
|
else:
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
def four_digits(year):
|
def four_digits(year):
|
||||||
"""
|
"""
|
||||||
@@ -353,8 +383,8 @@ def four_digits(year):
|
|||||||
:return: four digit year correspondent
|
:return: four digit year correspondent
|
||||||
:rtype: int
|
:rtype: int
|
||||||
|
|
||||||
>>> four_digits(20)
|
>>> four_digits(75)
|
||||||
1920
|
1975
|
||||||
>>> four_digits(16)
|
>>> four_digits(16)
|
||||||
2016
|
2016
|
||||||
>>> four_digits(00)
|
>>> four_digits(00)
|
||||||
@@ -436,36 +466,53 @@ def backtransformFilterString(st):
|
|||||||
return st
|
return st
|
||||||
|
|
||||||
|
|
||||||
def getHash(time):
|
def get_hash(time):
|
||||||
"""
|
"""
|
||||||
takes a time object and returns the corresponding SHA1 hash of the formatted date string
|
takes a time object and returns the corresponding SHA1 hash of the formatted date string
|
||||||
:param time: time object for which a hash should be calculated
|
:param time: time object for which a hash should be calculated
|
||||||
:type time: `~obspy.core.utcdatetime.UTCDateTime`
|
:type time: `~obspy.core.utcdatetime.UTCDateTime`
|
||||||
:return: SHA1 hash
|
:return: SHA1 hash
|
||||||
:rtype: str
|
:rtype: str
|
||||||
|
|
||||||
|
>>> time = UTCDateTime(0)
|
||||||
|
>>> get_hash(time)
|
||||||
|
'7627cce3b1b58dd21b005dac008b34d18317dd15'
|
||||||
|
>>> get_hash(0)
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
AssertionError: 'time' is not an ObsPy UTCDateTime object
|
||||||
"""
|
"""
|
||||||
|
assert isinstance(time, UTCDateTime), '\'time\' is not an ObsPy UTCDateTime object'
|
||||||
hg = hashlib.sha1()
|
hg = hashlib.sha1()
|
||||||
hg.update(time.strftime('%Y-%m-%d %H:%M:%S.%f'))
|
hg.update(time.strftime('%Y-%m-%d %H:%M:%S.%f').encode('utf-8'))
|
||||||
return hg.hexdigest()
|
return hg.hexdigest()
|
||||||
|
|
||||||
|
|
||||||
def getLogin():
|
def get_login():
|
||||||
"""
|
"""
|
||||||
returns the actual user's login ID
|
returns the actual user's name
|
||||||
:return: login ID
|
:return: login name
|
||||||
:rtype: str
|
:rtype: str
|
||||||
"""
|
"""
|
||||||
import getpass
|
import getpass
|
||||||
return getpass.getuser()
|
return getpass.getuser()
|
||||||
|
|
||||||
|
|
||||||
def getOwner(fn):
|
def get_owner(fn):
|
||||||
"""
|
"""
|
||||||
takes a filename and return the login ID of the actual owner of the file
|
takes a filename and return the login ID of the actual owner of the file
|
||||||
:param fn: filename of the file tested
|
:param fn: filename of the file tested
|
||||||
:type fn: str
|
:type fn: str
|
||||||
:return: login ID of the file's owner
|
:return: login ID of the file's owner
|
||||||
:rtype: str
|
:rtype: str
|
||||||
|
|
||||||
|
>>> import tempfile
|
||||||
|
>>> with tempfile.NamedTemporaryFile() as tmpfile:
|
||||||
|
... tmpfile.write(b'') and True
|
||||||
|
... tmpfile.flush()
|
||||||
|
... get_owner(tmpfile.name) == os.path.expanduser('~').split('/')[-1]
|
||||||
|
0
|
||||||
|
True
|
||||||
"""
|
"""
|
||||||
system_name = platform.system()
|
system_name = platform.system()
|
||||||
if system_name in ["Linux", "Darwin"]:
|
if system_name in ["Linux", "Darwin"]:
|
||||||
@@ -511,6 +558,11 @@ def is_executable(fn):
|
|||||||
:param fn: path to the file to be tested
|
:param fn: path to the file to be tested
|
||||||
:return: True or False
|
:return: True or False
|
||||||
:rtype: bool
|
:rtype: bool
|
||||||
|
|
||||||
|
>>> is_executable('/bin/ls')
|
||||||
|
True
|
||||||
|
>>> is_executable('/var/log/system.log')
|
||||||
|
False
|
||||||
"""
|
"""
|
||||||
return os.path.isfile(fn) and os.access(fn, os.X_OK)
|
return os.path.isfile(fn) and os.access(fn, os.X_OK)
|
||||||
|
|
||||||
@@ -537,24 +589,36 @@ def isSorted(iterable):
|
|||||||
>>> isSorted([2,3,1,4])
|
>>> isSorted([2,3,1,4])
|
||||||
False
|
False
|
||||||
"""
|
"""
|
||||||
assert isIterable(iterable), 'object is not iterable; object: {' \
|
assert is_iterable(iterable), "object is not iterable; object: {}".format(iterable)
|
||||||
'}'.format(iterable)
|
|
||||||
if type(iterable) is str:
|
if type(iterable) is str:
|
||||||
iterable = [s for s in iterable]
|
iterable = [s for s in iterable]
|
||||||
return sorted(iterable) == iterable
|
return sorted(iterable) == iterable
|
||||||
|
|
||||||
|
|
||||||
def isIterable(obj):
|
def is_iterable(obj):
|
||||||
"""
|
"""
|
||||||
takes a python object and returns True is the object is iterable and
|
takes a python object and returns True is the object is iterable and
|
||||||
False otherwise
|
False otherwise
|
||||||
:param obj: a python object
|
:param obj: a python object
|
||||||
:type obj: object
|
:type obj: obj
|
||||||
:return: True of False
|
:return: True of False
|
||||||
:rtype: bool
|
:rtype: bool
|
||||||
|
|
||||||
|
>>> is_iterable(1)
|
||||||
|
False
|
||||||
|
>>> is_iterable(True)
|
||||||
|
False
|
||||||
|
>>> is_iterable(0.)
|
||||||
|
False
|
||||||
|
>>> is_iterable((0,1,3,4))
|
||||||
|
True
|
||||||
|
>>> is_iterable([1])
|
||||||
|
True
|
||||||
|
>>> is_iterable('a')
|
||||||
|
True
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
iterator = iter(obj)
|
iter(obj)
|
||||||
except TypeError as te:
|
except TypeError as te:
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
@@ -563,13 +627,19 @@ def isIterable(obj):
|
|||||||
def key_for_set_value(d):
|
def key_for_set_value(d):
|
||||||
"""
|
"""
|
||||||
takes a dictionary and returns the first key for which's value the
|
takes a dictionary and returns the first key for which's value the
|
||||||
boolean is True
|
boolean representation is True
|
||||||
:param d: dictionary containing values
|
:param d: dictionary containing values
|
||||||
:type d: dict
|
:type d: dict
|
||||||
:return: key to the first non-False value found; None if no value's
|
:return: key to the first non-False value found; None if no value's
|
||||||
boolean equals True
|
boolean equals True
|
||||||
:rtype:
|
:rtype: bool or NoneType
|
||||||
|
|
||||||
|
>>> key_for_set_value({'one': 0, 'two': 1})
|
||||||
|
'two'
|
||||||
|
>>> print(key_for_set_value({1: 0, 2: False}))
|
||||||
|
None
|
||||||
"""
|
"""
|
||||||
|
assert type(d) is dict, "Function only defined for inputs of type 'dict'."
|
||||||
r = None
|
r = None
|
||||||
for k, v in d.items():
|
for k, v in d.items():
|
||||||
if v:
|
if v:
|
||||||
@@ -577,32 +647,53 @@ def key_for_set_value(d):
|
|||||||
return r
|
return r
|
||||||
|
|
||||||
|
|
||||||
def prepTimeAxis(stime, trace, verbosity=0):
|
def prep_time_axis(offset, trace, verbosity=0):
|
||||||
"""
|
"""
|
||||||
takes a starttime and a trace object and returns a valid time axis for
|
takes an offset and a trace object and returns a valid time axis for
|
||||||
plotting
|
plotting
|
||||||
:param stime: start time of the actual seismogram as UTCDateTime
|
:param offset: offset of the actual seismogram on plotting axis
|
||||||
:type stime: `~obspy.core.utcdatetime.UTCDateTime`
|
:type offset: float or int
|
||||||
:param trace: seismic trace object
|
:param trace: seismic trace object
|
||||||
:type trace: `~obspy.core.trace.Trace`
|
:type trace: `~obspy.core.trace.Trace`
|
||||||
:param verbosity: if != 0, debug output will be written to console
|
:param verbosity: if != 0, debug output will be written to console
|
||||||
:type verbosity: int
|
:type verbosity: int
|
||||||
:return: valid numpy array with time stamps for plotting
|
:return: valid numpy array with time stamps for plotting
|
||||||
:rtype: `~numpy.ndarray`
|
:rtype: `~numpy.ndarray`
|
||||||
|
|
||||||
|
>>> tr = read()[0]
|
||||||
|
>>> prep_time_axis(0., tr)
|
||||||
|
array([0.00000000e+00, 1.00033344e-02, 2.00066689e-02, ...,
|
||||||
|
2.99799933e+01, 2.99899967e+01, 3.00000000e+01])
|
||||||
|
>>> prep_time_axis(22.5, tr)
|
||||||
|
array([22.5 , 22.51000333, 22.52000667, ..., 52.47999333,
|
||||||
|
52.48999667, 52.5 ])
|
||||||
|
>>> prep_time_axis(tr.stats.starttime, tr)
|
||||||
|
Traceback (most recent call last):
|
||||||
|
...
|
||||||
|
AssertionError: 'offset' is not of type 'float' or 'int'; type: <class 'obspy.core.utcdatetime.UTCDateTime'>
|
||||||
|
>>> tr.stats.npts -= 1
|
||||||
|
>>> prep_time_axis(0, tr)
|
||||||
|
array([0.00000000e+00, 1.00033356e-02, 2.00066711e-02, ...,
|
||||||
|
2.99699933e+01, 2.99799967e+01, 2.99900000e+01])
|
||||||
|
>>> tr.stats.npts += 2
|
||||||
|
>>> prep_time_axis(0, tr)
|
||||||
|
array([0.00000000e+00, 1.00033333e-02, 2.00066667e-02, ...,
|
||||||
|
2.99899933e+01, 2.99999967e+01, 3.00100000e+01])
|
||||||
"""
|
"""
|
||||||
|
assert isinstance(offset, (float, int)), "'offset' is not of type 'float' or 'int'; type: {}".format(type(offset))
|
||||||
nsamp = trace.stats.npts
|
nsamp = trace.stats.npts
|
||||||
srate = trace.stats.sampling_rate
|
srate = trace.stats.sampling_rate
|
||||||
tincr = trace.stats.delta
|
tincr = trace.stats.delta
|
||||||
etime = stime + nsamp / srate
|
etime = offset + nsamp / srate
|
||||||
time_ax = np.linspace(stime, etime, nsamp)
|
time_ax = np.linspace(offset, etime, nsamp)
|
||||||
if len(time_ax) < nsamp:
|
if len(time_ax) < nsamp:
|
||||||
if verbosity:
|
if verbosity:
|
||||||
print('elongate time axes by one datum')
|
print('elongate time axes by one datum')
|
||||||
time_ax = np.arange(stime, etime + tincr, tincr)
|
time_ax = np.arange(offset, etime + tincr, tincr)
|
||||||
elif len(time_ax) > nsamp:
|
elif len(time_ax) > nsamp:
|
||||||
if verbosity:
|
if verbosity:
|
||||||
print('shorten time axes by one datum')
|
print('shorten time axes by one datum')
|
||||||
time_ax = np.arange(stime, etime - tincr, tincr)
|
time_ax = np.arange(offset, etime - tincr, tincr)
|
||||||
if len(time_ax) != nsamp:
|
if len(time_ax) != nsamp:
|
||||||
print('Station {0}, {1} samples of data \n '
|
print('Station {0}, {1} samples of data \n '
|
||||||
'{2} length of time vector \n'
|
'{2} length of time vector \n'
|
||||||
@@ -618,13 +709,13 @@ def find_horizontals(data):
|
|||||||
:param data: waveform data
|
:param data: waveform data
|
||||||
:type data: `obspy.core.stream.Stream`
|
:type data: `obspy.core.stream.Stream`
|
||||||
:return: components list
|
:return: components list
|
||||||
:rtype: list
|
:rtype: List(str)
|
||||||
|
|
||||||
..example::
|
..example::
|
||||||
|
|
||||||
>>> st = read()
|
>>> st = read()
|
||||||
>>> find_horizontals(st)
|
>>> find_horizontals(st)
|
||||||
[u'N', u'E']
|
['N', 'E']
|
||||||
"""
|
"""
|
||||||
rval = []
|
rval = []
|
||||||
for tr in data:
|
for tr in data:
|
||||||
@@ -635,7 +726,7 @@ def find_horizontals(data):
|
|||||||
return rval
|
return rval
|
||||||
|
|
||||||
|
|
||||||
def pick_color(picktype, phase, quality=0):
|
def pick_color(picktype: Literal['manual', 'automatic'], phase: Literal['P', 'S'], quality: int = 0) -> Rgba:
|
||||||
"""
|
"""
|
||||||
Create pick color by modifying the base color by the quality.
|
Create pick color by modifying the base color by the quality.
|
||||||
|
|
||||||
@@ -648,7 +739,7 @@ def pick_color(picktype, phase, quality=0):
|
|||||||
:param quality: quality of pick. Decides the new intensity of the modifier color
|
:param quality: quality of pick. Decides the new intensity of the modifier color
|
||||||
:type quality: int
|
:type quality: int
|
||||||
:return: tuple containing modified rgba color values
|
:return: tuple containing modified rgba color values
|
||||||
:rtype: (int, int, int, int)
|
:rtype: Rgba
|
||||||
"""
|
"""
|
||||||
min_quality = 3
|
min_quality = 3
|
||||||
bpc = base_phase_colors(picktype, phase) # returns dict like {'modifier': 'g', 'rgba': (0, 0, 255, 255)}
|
bpc = base_phase_colors(picktype, phase) # returns dict like {'modifier': 'g', 'rgba': (0, 0, 255, 255)}
|
||||||
@@ -704,17 +795,17 @@ def pick_linestyle_plt(picktype, key):
|
|||||||
return linestyles[picktype][key]
|
return linestyles[picktype][key]
|
||||||
|
|
||||||
|
|
||||||
def modify_rgba(rgba, modifier, intensity):
|
def modify_rgba(rgba: Rgba, modifier: Literal['r', 'g', 'b'], intensity: float) -> Rgba:
|
||||||
"""
|
"""
|
||||||
Modify rgba color by adding the given intensity to the modifier color
|
Modify rgba color by adding the given intensity to the modifier color
|
||||||
:param rgba: tuple containing rgba values
|
:param rgba: tuple containing rgba values
|
||||||
:type rgba: (int, int, int, int)
|
:type rgba: Rgba
|
||||||
:param modifier: which color should be modified, eg. 'r', 'g', 'b'
|
:param modifier: which color should be modified; options: 'r', 'g', 'b'
|
||||||
:type modifier: str
|
:type modifier: Literal['r', 'g', 'b']
|
||||||
:param intensity: intensity to be added to selected color
|
:param intensity: intensity to be added to selected color
|
||||||
:type intensity: float
|
:type intensity: float
|
||||||
:return: tuple containing rgba values
|
:return: tuple containing rgba values
|
||||||
:rtype: (int, int, int, int)
|
:rtype: Rgba
|
||||||
"""
|
"""
|
||||||
rgba = list(rgba)
|
rgba = list(rgba)
|
||||||
index = {'r': 0,
|
index = {'r': 0,
|
||||||
@@ -748,18 +839,20 @@ def transform_colors_mpl_str(colors, no_alpha=False):
|
|||||||
Transforms rgba color values to a matplotlib string of color values with a range of [0, 1]
|
Transforms rgba color values to a matplotlib string of color values with a range of [0, 1]
|
||||||
:param colors: tuple of rgba color values ranging from [0, 255]
|
:param colors: tuple of rgba color values ranging from [0, 255]
|
||||||
:type colors: (float, float, float, float)
|
:type colors: (float, float, float, float)
|
||||||
:param no_alpha: Wether to return a alpha value in the matplotlib color string
|
:param no_alpha: Whether to return an alpha value in the matplotlib color string
|
||||||
:type no_alpha: bool
|
:type no_alpha: bool
|
||||||
:return: String containing r, g, b values and alpha value if no_alpha is False (default)
|
:return: String containing r, g, b values and alpha value if no_alpha is False (default)
|
||||||
:rtype: str
|
:rtype: str
|
||||||
|
|
||||||
|
>>> transform_colors_mpl_str((255., 255., 255., 255.), True)
|
||||||
|
'(1.0, 1.0, 1.0)'
|
||||||
|
>>> transform_colors_mpl_str((255., 255., 255., 255.))
|
||||||
|
'(1.0, 1.0, 1.0, 1.0)'
|
||||||
"""
|
"""
|
||||||
colors = list(colors)
|
|
||||||
colors_mpl = tuple([color / 255. for color in colors])
|
|
||||||
if no_alpha:
|
if no_alpha:
|
||||||
colors_mpl = '({}, {}, {})'.format(*colors_mpl)
|
return '({}, {}, {})'.format(*transform_colors_mpl(colors))
|
||||||
else:
|
else:
|
||||||
colors_mpl = '({}, {}, {}, {})'.format(*colors_mpl)
|
return '({}, {}, {}, {})'.format(*transform_colors_mpl(colors))
|
||||||
return colors_mpl
|
|
||||||
|
|
||||||
|
|
||||||
def transform_colors_mpl(colors):
|
def transform_colors_mpl(colors):
|
||||||
@@ -769,27 +862,16 @@ def transform_colors_mpl(colors):
|
|||||||
:type colors: (float, float, float, float)
|
:type colors: (float, float, float, float)
|
||||||
:return: tuple of rgba color values ranging from [0, 1]
|
:return: tuple of rgba color values ranging from [0, 1]
|
||||||
:rtype: (float, float, float, float)
|
:rtype: (float, float, float, float)
|
||||||
|
|
||||||
|
>>> transform_colors_mpl((127.5, 0., 63.75, 255.))
|
||||||
|
(0.5, 0.0, 0.25, 1.0)
|
||||||
|
>>> transform_colors_mpl(())
|
||||||
"""
|
"""
|
||||||
colors = list(colors)
|
colors = list(colors)
|
||||||
colors_mpl = tuple([color / 255. for color in colors])
|
colors_mpl = tuple([color / 255. for color in colors])
|
||||||
return colors_mpl
|
return colors_mpl
|
||||||
|
|
||||||
|
|
||||||
def remove_underscores(data):
|
|
||||||
"""
|
|
||||||
takes a `obspy.core.stream.Stream` object and removes all underscores
|
|
||||||
from station names
|
|
||||||
:param data: stream of seismic data
|
|
||||||
:type data: `~obspy.core.stream.Stream`
|
|
||||||
:return: data stream
|
|
||||||
:rtype: `~obspy.core.stream.Stream`
|
|
||||||
"""
|
|
||||||
# for tr in data:
|
|
||||||
# # remove underscores
|
|
||||||
# tr.stats.station = tr.stats.station.strip('_')
|
|
||||||
return data
|
|
||||||
|
|
||||||
|
|
||||||
def trim_station_components(data, trim_start=True, trim_end=True):
|
def trim_station_components(data, trim_start=True, trim_end=True):
|
||||||
"""
|
"""
|
||||||
cut a stream so only the part common to all three traces is kept to avoid dealing with offsets
|
cut a stream so only the part common to all three traces is kept to avoid dealing with offsets
|
||||||
@@ -818,19 +900,6 @@ def trim_station_components(data, trim_start=True, trim_end=True):
|
|||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
def merge_stream(stream):
|
|
||||||
gaps = stream.get_gaps()
|
|
||||||
if gaps:
|
|
||||||
# list of merged stations (seed_ids)
|
|
||||||
merged = ['{}.{}.{}.{}'.format(*gap[:4]) for gap in gaps]
|
|
||||||
stream.merge(method=1)
|
|
||||||
print('Merged the following stations because of gaps:')
|
|
||||||
for merged_station in merged:
|
|
||||||
print(merged_station)
|
|
||||||
|
|
||||||
return stream, gaps
|
|
||||||
|
|
||||||
|
|
||||||
def check4gapsAndRemove(data):
|
def check4gapsAndRemove(data):
|
||||||
"""
|
"""
|
||||||
check for gaps in Stream and remove them
|
check for gaps in Stream and remove them
|
||||||
@@ -851,12 +920,12 @@ def check4gapsAndRemove(data):
|
|||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
def check4gapsAndMerge(data):
|
def check_for_gaps_and_merge(data):
|
||||||
"""
|
"""
|
||||||
check for gaps in Stream and merge if gaps are found
|
check for gaps in Stream and merge if gaps are found
|
||||||
:param data: stream of seismic data
|
:param data: stream of seismic data
|
||||||
:type data: `~obspy.core.stream.Stream`
|
:type data: `~obspy.core.stream.Stream`
|
||||||
:return: data stream
|
:return: data stream, gaps returned from obspy get_gaps
|
||||||
:rtype: `~obspy.core.stream.Stream`
|
:rtype: `~obspy.core.stream.Stream`
|
||||||
"""
|
"""
|
||||||
gaps = data.get_gaps()
|
gaps = data.get_gaps()
|
||||||
@@ -867,7 +936,7 @@ def check4gapsAndMerge(data):
|
|||||||
for merged_station in merged:
|
for merged_station in merged:
|
||||||
print(merged_station)
|
print(merged_station)
|
||||||
|
|
||||||
return data
|
return data, gaps
|
||||||
|
|
||||||
|
|
||||||
def check4doubled(data):
|
def check4doubled(data):
|
||||||
@@ -897,13 +966,53 @@ def check4doubled(data):
|
|||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
def check_for_nan(data, nan_value=0.):
|
||||||
|
"""
|
||||||
|
Replace all NaNs in data with nan_value (in place)
|
||||||
|
:param data: stream of seismic data
|
||||||
|
:type data: `~obspy.core.stream.Stream`
|
||||||
|
:param nan_value: value which all NaNs are set to
|
||||||
|
:type nan_value: float, int
|
||||||
|
:return: None
|
||||||
|
"""
|
||||||
|
if not data:
|
||||||
|
return
|
||||||
|
for trace in data:
|
||||||
|
np.nan_to_num(trace.data, copy=False, nan=nan_value)
|
||||||
|
|
||||||
|
|
||||||
|
def get_pylot_eventfile_with_extension(event, fext):
|
||||||
|
if hasattr(event, 'path'):
|
||||||
|
eventpath = event.path
|
||||||
|
else:
|
||||||
|
logging.warning('No attribute path found for event.')
|
||||||
|
return
|
||||||
|
eventname = event.pylot_id #path.split('/')[-1] # or event.pylot_id
|
||||||
|
filename = os.path.join(eventpath, 'PyLoT_' + eventname + fext)
|
||||||
|
if os.path.isfile(filename):
|
||||||
|
return filename
|
||||||
|
|
||||||
|
|
||||||
|
def get_possible_pylot_eventfile_extensions(event, fext):
|
||||||
|
if hasattr(event, 'path'):
|
||||||
|
eventpath = event.path
|
||||||
|
else:
|
||||||
|
logging.warning('No attribute path found for event.')
|
||||||
|
return []
|
||||||
|
eventname = event.pylot_id
|
||||||
|
filename = os.path.join(eventpath, 'PyLoT_' + eventname + fext)
|
||||||
|
filenames = glob.glob(filename)
|
||||||
|
extensions = [os.path.split(path)[-1].split('PyLoT_' + eventname)[-1] for path in filenames]
|
||||||
|
return extensions
|
||||||
|
|
||||||
|
|
||||||
def get_stations(data):
|
def get_stations(data):
|
||||||
"""
|
"""
|
||||||
Get list of all station names in data stream
|
Get list of all station names in data-stream
|
||||||
:param data: stream containing seismic traces
|
:param data: stream containing seismic traces
|
||||||
:type data: `~obspy.core.stream.Stream`
|
:type data: `~obspy.core.stream.Stream`
|
||||||
:return: list of all station names in data, no duplicates
|
:return: list of all station names in data, no duplicates
|
||||||
:rtype: list of str
|
:rtype: List(str)
|
||||||
"""
|
"""
|
||||||
stations = []
|
stations = []
|
||||||
for tr in data:
|
for tr in data:
|
||||||
@@ -930,66 +1039,88 @@ def check4rotated(data, metadata=None, verbosity=1):
|
|||||||
:rtype: `~obspy.core.stream.Stream`
|
:rtype: `~obspy.core.stream.Stream`
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def rotate_components(wfstream, metadata=None):
|
def rotation_required(trace_ids):
|
||||||
|
"""
|
||||||
|
Derive if any rotation is required from the orientation code of the input.
|
||||||
|
|
||||||
|
:param trace_ids: string identifier of waveform data trace
|
||||||
|
:type trace_ids: List(str)
|
||||||
|
:return: boolean representing if rotation is necessary for any of the traces
|
||||||
|
:rtype: bool
|
||||||
|
"""
|
||||||
|
orientations = [trace_id[-1] for trace_id in trace_ids]
|
||||||
|
return any([orientation.isnumeric() for orientation in orientations])
|
||||||
|
|
||||||
|
def rotate_components(wfs_in, metadata=None):
|
||||||
"""
|
"""
|
||||||
Rotate components if orientation code is numeric (= non traditional orientation).
|
Rotate components if orientation code is numeric (= non traditional orientation).
|
||||||
|
|
||||||
Azimut and dip are fetched from metadata. To be rotated, traces of a station have to be cut to the same length.
|
Azimut and dip are fetched from metadata. To be rotated, traces of a station have to be cut to the same length.
|
||||||
Returns unrotated traces of no metadata is provided
|
Returns unrotated traces of no metadata is provided
|
||||||
:param wfstream: stream containing seismic traces of a station
|
:param wfs_in: stream containing seismic traces of a station
|
||||||
:type wfstream: `~obspy.core.stream.Stream`
|
:type wfs_in: `~obspy.core.stream.Stream`
|
||||||
:param metadata: tuple containing metadata type string and metadata parser object
|
:param metadata: tuple containing metadata type string and metadata parser object
|
||||||
:type metadata: (str, `~obspy.io.xseed.parser.Parser`)
|
:type metadata: (str, `~obspy.io.xseed.parser.Parser`)
|
||||||
:return: stream object with traditionally oriented traces (ZNE)
|
:return: stream object with traditionally oriented traces (ZNE)
|
||||||
:rtype: `~obspy.core.stream.Stream`
|
:rtype: `~obspy.core.stream.Stream`
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
if len(wfs_in) < 3:
|
||||||
|
print(f"Stream {wfs_in=}, has not enough components to rotate.")
|
||||||
|
return wfs_in
|
||||||
|
|
||||||
# check if any traces in this station need to be rotated
|
# check if any traces in this station need to be rotated
|
||||||
trace_ids = [trace.id for trace in wfstream]
|
trace_ids = [trace.id for trace in wfs_in]
|
||||||
orientations = [trace_id[-1] for trace_id in trace_ids]
|
if not rotation_required(trace_ids):
|
||||||
rotation_required = [orientation.isnumeric() for orientation in orientations]
|
logging.debug(f"Stream does not need any rotation: Traces are {trace_ids=}")
|
||||||
if any(rotation_required):
|
return wfs_in
|
||||||
t_start = full_range(wfstream)
|
|
||||||
|
# check metadata quality
|
||||||
|
t_start = full_range(wfs_in)
|
||||||
try:
|
try:
|
||||||
azimuts = []
|
azimuths = []
|
||||||
dips = []
|
dips = []
|
||||||
for tr_id in trace_ids:
|
for tr_id in trace_ids:
|
||||||
azimuts.append(metadata.get_coordinates(tr_id, t_start)['azimuth'])
|
azimuths.append(metadata.get_coordinates(tr_id, t_start)['azimuth'])
|
||||||
dips.append(metadata.get_coordinates(tr_id, t_start)['dip'])
|
dips.append(metadata.get_coordinates(tr_id, t_start)['dip'])
|
||||||
except (KeyError, TypeError) as e:
|
except (KeyError, TypeError) as err:
|
||||||
print('Failed to rotate trace {}, no azimuth or dip available in metadata'.format(tr_id))
|
logging.warning(f"Rotating not possible, not all azimuth and dip information "
|
||||||
return wfstream
|
f"available in metadata. Stream remains unchanged.")
|
||||||
if len(wfstream) < 3:
|
logging.debug(f"Rotating not possible, {err=}, {type(err)=}")
|
||||||
print('Failed to rotate Stream {}, not enough components available.'.format(wfstream))
|
return wfs_in
|
||||||
return wfstream
|
except Exception as err:
|
||||||
|
print(f"Unexpected {err=}, {type(err)=}")
|
||||||
|
raise
|
||||||
|
|
||||||
# to rotate all traces must have same length, so trim them
|
# to rotate all traces must have same length, so trim them
|
||||||
wfstream = trim_station_components(wfstream, trim_start=True, trim_end=True)
|
wfs_out = trim_station_components(wfs_in, trim_start=True, trim_end=True)
|
||||||
try:
|
try:
|
||||||
z, n, e = rotate2zne(wfstream[0], azimuts[0], dips[0],
|
z, n, e = rotate2zne(wfs_out[0], azimuths[0], dips[0],
|
||||||
wfstream[1], azimuts[1], dips[1],
|
wfs_out[1], azimuths[1], dips[1],
|
||||||
wfstream[2], azimuts[2], dips[2])
|
wfs_out[2], azimuths[2], dips[2])
|
||||||
print('check4rotated: rotated trace {} to ZNE'.format(trace_ids))
|
print('check4rotated: rotated trace {} to ZNE'.format(trace_ids))
|
||||||
# replace old data with rotated data, change the channel code to ZNE
|
# replace old data with rotated data, change the channel code to ZNE
|
||||||
z_index = dips.index(min(
|
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)
|
dips)) # get z-trace index, z has minimum dip of -90 (dip is measured from 0 to -90, with -90
|
||||||
wfstream[z_index].data = z
|
# being vertical)
|
||||||
wfstream[z_index].stats.channel = wfstream[z_index].stats.channel[0:-1] + 'Z'
|
wfs_out[z_index].data = z
|
||||||
|
wfs_out[z_index].stats.channel = wfs_out[z_index].stats.channel[0:-1] + 'Z'
|
||||||
del trace_ids[z_index]
|
del trace_ids[z_index]
|
||||||
for trace_id in trace_ids:
|
for trace_id in trace_ids:
|
||||||
coordinates = metadata.get_coordinates(trace_id, t_start)
|
coordinates = metadata.get_coordinates(trace_id, t_start)
|
||||||
dip, az = coordinates['dip'], coordinates['azimuth']
|
dip, az = coordinates['dip'], coordinates['azimuth']
|
||||||
trace = wfstream.select(id=trace_id)[0]
|
trace = wfs_out.select(id=trace_id)[0]
|
||||||
if az > 315 or az <= 45 or az > 135 and az <= 225:
|
if az > 315 or az <= 45 or 135 < az <= 225:
|
||||||
trace.data = n
|
trace.data = n
|
||||||
trace.stats.channel = trace.stats.channel[0:-1] + 'N'
|
trace.stats.channel = trace.stats.channel[0:-1] + 'N'
|
||||||
elif az > 45 and az <= 135 or az > 225 and az <= 315:
|
elif 45 < az <= 135 or 225 < az <= 315:
|
||||||
trace.data = e
|
trace.data = e
|
||||||
trace.stats.channel = trace.stats.channel[0:-1] + 'E'
|
trace.stats.channel = trace.stats.channel[0:-1] + 'E'
|
||||||
except (ValueError) as e:
|
except ValueError as err:
|
||||||
print(e)
|
print(f"{err=} Rotation failed. Stream remains unchanged.")
|
||||||
return wfstream
|
return wfs_in
|
||||||
|
|
||||||
return wfstream
|
return wfs_out
|
||||||
|
|
||||||
if metadata is None:
|
if metadata is None:
|
||||||
if verbosity:
|
if verbosity:
|
||||||
@@ -1003,38 +1134,6 @@ def check4rotated(data, metadata=None, verbosity=1):
|
|||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
def scaleWFData(data, factor=None, components='all'):
|
|
||||||
"""
|
|
||||||
produce scaled waveforms from given waveform data and a scaling factor,
|
|
||||||
waveform may be selected by their components name
|
|
||||||
:param data: waveform data to be scaled
|
|
||||||
:type data: `~obspy.core.stream.Stream` object
|
|
||||||
:param factor: scaling factor
|
|
||||||
:type factor: float
|
|
||||||
:param components: components labels for the traces in data to be scaled by
|
|
||||||
the scaling factor (optional, default: 'all')
|
|
||||||
:type components: tuple
|
|
||||||
:return: scaled waveform data
|
|
||||||
:rtype: `~obspy.core.stream.Stream` object
|
|
||||||
"""
|
|
||||||
if components != 'all':
|
|
||||||
for comp in components:
|
|
||||||
if factor is None:
|
|
||||||
max_val = np.max(np.abs(data.select(component=comp)[0].data))
|
|
||||||
data.select(component=comp)[0].data /= 2 * max_val
|
|
||||||
else:
|
|
||||||
data.select(component=comp)[0].data /= 2 * factor
|
|
||||||
else:
|
|
||||||
for tr in data:
|
|
||||||
if factor is None:
|
|
||||||
max_val = float(np.max(np.abs(tr.data)))
|
|
||||||
tr.data /= 2 * max_val
|
|
||||||
else:
|
|
||||||
tr.data /= 2 * factor
|
|
||||||
|
|
||||||
return data
|
|
||||||
|
|
||||||
|
|
||||||
def runProgram(cmd, parameter=None):
|
def runProgram(cmd, parameter=None):
|
||||||
"""
|
"""
|
||||||
run an external program specified by cmd with parameters input returning the
|
run an external program specified by cmd with parameters input returning the
|
||||||
@@ -1106,6 +1205,7 @@ def identifyPhase(phase):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
@lru_cache
|
||||||
def identifyPhaseID(phase):
|
def identifyPhaseID(phase):
|
||||||
"""
|
"""
|
||||||
Returns phase id (capital P or S)
|
Returns phase id (capital P or S)
|
||||||
|
|||||||
+230
-35
@@ -7,6 +7,8 @@ Created on Wed Mar 19 11:27:35 2014
|
|||||||
import copy
|
import copy
|
||||||
import datetime
|
import datetime
|
||||||
import getpass
|
import getpass
|
||||||
|
import glob
|
||||||
|
import logging
|
||||||
import multiprocessing
|
import multiprocessing
|
||||||
import os
|
import os
|
||||||
import subprocess
|
import subprocess
|
||||||
@@ -16,6 +18,7 @@ import traceback
|
|||||||
|
|
||||||
import matplotlib
|
import matplotlib
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
from pylot.core.io.phases import getQualitiesfromxml
|
||||||
|
|
||||||
matplotlib.use('QT5Agg')
|
matplotlib.use('QT5Agg')
|
||||||
|
|
||||||
@@ -36,7 +39,7 @@ from PySide2.QtWidgets import QAction, QApplication, QCheckBox, QComboBox, \
|
|||||||
QGridLayout, QLabel, QLineEdit, QMessageBox, \
|
QGridLayout, QLabel, QLineEdit, QMessageBox, \
|
||||||
QTabWidget, QToolBar, QVBoxLayout, QHBoxLayout, QWidget, \
|
QTabWidget, QToolBar, QVBoxLayout, QHBoxLayout, QWidget, \
|
||||||
QPushButton, QFileDialog, QInputDialog
|
QPushButton, QFileDialog, QInputDialog
|
||||||
from PySide2.QtCore import QSettings, Qt, QUrl, Signal
|
from PySide2.QtCore import QSettings, Qt, QUrl, Signal, QTimer
|
||||||
from PySide2.QtWebEngineWidgets import QWebEngineView as QWebView
|
from PySide2.QtWebEngineWidgets import QWebEngineView as QWebView
|
||||||
from obspy import Stream, Trace, UTCDateTime
|
from obspy import Stream, Trace, UTCDateTime
|
||||||
from obspy.core.util import AttribDict
|
from obspy.core.util import AttribDict
|
||||||
@@ -49,12 +52,12 @@ from pylot.core.pick.utils import getSNR, earllatepicker, getnoisewin, \
|
|||||||
from pylot.core.pick.compare import Comparison
|
from pylot.core.pick.compare import Comparison
|
||||||
from pylot.core.pick.autopick import fmpicker
|
from pylot.core.pick.autopick import fmpicker
|
||||||
from pylot.core.util.defaults import OUTPUTFORMATS, FILTERDEFAULTS
|
from pylot.core.util.defaults import OUTPUTFORMATS, FILTERDEFAULTS
|
||||||
from pylot.core.util.utils import prepTimeAxis, full_range, demeanTrace, isSorted, findComboBoxIndex, clims, \
|
from pylot.core.util.utils import prep_time_axis, full_range, demeanTrace, isSorted, findComboBoxIndex, clims, \
|
||||||
pick_linestyle_plt, pick_color_plt, \
|
pick_linestyle_plt, pick_color_plt, \
|
||||||
check4rotated, check4doubled, merge_stream, identifyPhase, \
|
check4rotated, check4doubled, check_for_gaps_and_merge, check_for_nan, identifyPhase, \
|
||||||
loopIdentifyPhase, trim_station_components, transformFilteroptions2String, \
|
loopIdentifyPhase, trim_station_components, transformFilteroptions2String, \
|
||||||
identifyPhaseID, get_bool, get_None, pick_color, getAutoFilteroptions, SetChannelComponents, \
|
identifyPhaseID, get_bool, get_none, pick_color, getAutoFilteroptions, SetChannelComponents, \
|
||||||
station_id_remove_channel
|
station_id_remove_channel, get_pylot_eventfile_with_extension, get_possible_pylot_eventfile_extensions
|
||||||
from autoPyLoT import autoPyLoT
|
from autoPyLoT import autoPyLoT
|
||||||
from pylot.core.util.thread import Thread
|
from pylot.core.util.thread import Thread
|
||||||
from pylot.core.util.dataprocessing import Metadata
|
from pylot.core.util.dataprocessing import Metadata
|
||||||
@@ -864,9 +867,24 @@ class WaveformWidgetPG(QtWidgets.QWidget):
|
|||||||
|
|
||||||
def plotWFData(self, wfdata, wfsyn=None, title=None, scaleddata=False, mapping=True,
|
def plotWFData(self, wfdata, wfsyn=None, title=None, scaleddata=False, mapping=True,
|
||||||
component='*', nth_sample=1, verbosity=0, method='normal', gain=1., shift_syn=0.2):
|
component='*', nth_sample=1, verbosity=0, method='normal', gain=1., shift_syn=0.2):
|
||||||
|
def station_sort(nslc):
|
||||||
|
"""Try to sort after station integer in case of a line array (e.g. active seismics)"""
|
||||||
|
try:
|
||||||
|
rval = sorted(nslc, key=lambda x: int(x.split('.')[1]))
|
||||||
|
return rval
|
||||||
|
except ValueError as e:
|
||||||
|
# this is the standard case for seismological stations
|
||||||
|
pass
|
||||||
|
except Exception as e:
|
||||||
|
print(f'Sorting by station integer failed with unknown exception: {e}')
|
||||||
|
|
||||||
|
# fallback to default sorting
|
||||||
|
return sorted(nslc)
|
||||||
|
|
||||||
if not wfdata:
|
if not wfdata:
|
||||||
print('Nothing to plot.')
|
print('Nothing to plot.')
|
||||||
return
|
return
|
||||||
|
|
||||||
self.title = title
|
self.title = title
|
||||||
self.clearPlotDict()
|
self.clearPlotDict()
|
||||||
self.wfstart, self.wfend = full_range(wfdata)
|
self.wfstart, self.wfend = full_range(wfdata)
|
||||||
@@ -884,14 +902,14 @@ class WaveformWidgetPG(QtWidgets.QWidget):
|
|||||||
else:
|
else:
|
||||||
st_select = wfdata
|
st_select = wfdata
|
||||||
|
|
||||||
st_select, gaps = merge_stream(st_select)
|
# st_select, gaps = check_for_gaps_and_merge(st_select) #MP MP commented because probably done twice
|
||||||
|
|
||||||
# list containing tuples of network, station, channel (for sorting)
|
# list containing tuples of network, station, channel (for sorting)
|
||||||
nslc = []
|
nslc = []
|
||||||
for trace in st_select:
|
for trace in st_select:
|
||||||
nslc.append(
|
nslc.append(
|
||||||
trace.get_id()) # (trace.stats.network, trace.stats.station, trace.stats.location trace.stats.channel))
|
trace.get_id()) # (trace.stats.network, trace.stats.station, trace.stats.location trace.stats.channel))
|
||||||
nslc.sort()
|
nslc = station_sort(nslc)
|
||||||
nslc.reverse()
|
nslc.reverse()
|
||||||
plots = []
|
plots = []
|
||||||
|
|
||||||
@@ -923,10 +941,10 @@ class WaveformWidgetPG(QtWidgets.QWidget):
|
|||||||
msg = 'plotting %s channel of station %s' % (channel, station)
|
msg = 'plotting %s channel of station %s' % (channel, station)
|
||||||
print(msg)
|
print(msg)
|
||||||
stime = trace.stats.starttime - self.wfstart
|
stime = trace.stats.starttime - self.wfstart
|
||||||
time_ax = prepTimeAxis(stime, trace)
|
time_ax = prep_time_axis(stime, trace)
|
||||||
if st_syn:
|
if st_syn:
|
||||||
stime_syn = trace_syn.stats.starttime - self.wfstart
|
stime_syn = trace_syn.stats.starttime - self.wfstart
|
||||||
time_ax_syn = prepTimeAxis(stime_syn, trace_syn)
|
time_ax_syn = prep_time_axis(stime_syn, trace_syn)
|
||||||
|
|
||||||
if method == 'fast':
|
if method == 'fast':
|
||||||
trace.data, time_ax = self.minMax(trace, time_ax)
|
trace.data, time_ax = self.minMax(trace, time_ax)
|
||||||
@@ -955,7 +973,7 @@ class WaveformWidgetPG(QtWidgets.QWidget):
|
|||||||
self.ylabel = ''
|
self.ylabel = ''
|
||||||
self.setXLims([0, self.wfend - self.wfstart])
|
self.setXLims([0, self.wfend - self.wfstart])
|
||||||
self.setYLims([0.5, nmax + 0.5])
|
self.setYLims([0.5, nmax + 0.5])
|
||||||
return plots, gaps
|
return plots
|
||||||
|
|
||||||
def minMax(self, trace, time_ax):
|
def minMax(self, trace, time_ax):
|
||||||
'''
|
'''
|
||||||
@@ -977,7 +995,7 @@ class WaveformWidgetPG(QtWidgets.QWidget):
|
|||||||
min_ = data.min(axis=1)
|
min_ = data.min(axis=1)
|
||||||
max_ = data.max(axis=1)
|
max_ = data.max(axis=1)
|
||||||
if remaining_samples:
|
if remaining_samples:
|
||||||
extreme_values = np.empty((npixel + 1, 2), dtype=np.float)
|
extreme_values = np.empty((npixel + 1, 2), dtype=float)
|
||||||
extreme_values[:-1, 0] = min_
|
extreme_values[:-1, 0] = min_
|
||||||
extreme_values[:-1, 1] = max_
|
extreme_values[:-1, 1] = max_
|
||||||
extreme_values[-1, 0] = \
|
extreme_values[-1, 0] = \
|
||||||
@@ -985,7 +1003,7 @@ class WaveformWidgetPG(QtWidgets.QWidget):
|
|||||||
extreme_values[-1, 1] = \
|
extreme_values[-1, 1] = \
|
||||||
trace.data[-remaining_samples:].max()
|
trace.data[-remaining_samples:].max()
|
||||||
else:
|
else:
|
||||||
extreme_values = np.empty((npixel, 2), dtype=np.float)
|
extreme_values = np.empty((npixel, 2), dtype=float)
|
||||||
extreme_values[:, 0] = min_
|
extreme_values[:, 0] = min_
|
||||||
extreme_values[:, 1] = max_
|
extreme_values[:, 1] = max_
|
||||||
data = extreme_values.flatten()
|
data = extreme_values.flatten()
|
||||||
@@ -1427,7 +1445,7 @@ class PylotCanvas(FigureCanvas):
|
|||||||
msg = 'plotting %s channel of station %s' % (channel, station)
|
msg = 'plotting %s channel of station %s' % (channel, station)
|
||||||
print(msg)
|
print(msg)
|
||||||
stime = trace.stats.starttime - wfstart
|
stime = trace.stats.starttime - wfstart
|
||||||
time_ax = prepTimeAxis(stime, trace)
|
time_ax = prep_time_axis(stime, trace)
|
||||||
if time_ax is not None:
|
if time_ax is not None:
|
||||||
if scaleToChannel:
|
if scaleToChannel:
|
||||||
st_scale = wfdata.select(channel=scaleToChannel)
|
st_scale = wfdata.select(channel=scaleToChannel)
|
||||||
@@ -1467,7 +1485,7 @@ class PylotCanvas(FigureCanvas):
|
|||||||
if not scaleddata:
|
if not scaleddata:
|
||||||
trace.detrend('constant')
|
trace.detrend('constant')
|
||||||
trace.normalize(np.max(np.abs(trace.data)) * 2)
|
trace.normalize(np.max(np.abs(trace.data)) * 2)
|
||||||
time_ax = prepTimeAxis(stime, trace)
|
time_ax = prep_time_axis(stime, trace)
|
||||||
times = [time for index, time in enumerate(time_ax) if not index % nth_sample]
|
times = [time for index, time in enumerate(time_ax) if not index % nth_sample]
|
||||||
p_data = compare_stream[0].data
|
p_data = compare_stream[0].data
|
||||||
# #normalize
|
# #normalize
|
||||||
@@ -1575,6 +1593,178 @@ class PylotCanvas(FigureCanvas):
|
|||||||
self.draw()
|
self.draw()
|
||||||
|
|
||||||
|
|
||||||
|
class SearchFileByExtensionDialog(QtWidgets.QDialog):
|
||||||
|
def __init__(self, parent=None, label='Text: ', default_text='.xml', events=None):
|
||||||
|
super(SearchFileByExtensionDialog, self).__init__(parent)
|
||||||
|
self.events = events
|
||||||
|
self.filepaths = []
|
||||||
|
self.file_extensions = []
|
||||||
|
self.check_all_state = True
|
||||||
|
self.merge_strategy = None
|
||||||
|
self.default_text = default_text
|
||||||
|
self.label = label
|
||||||
|
self.setButtons()
|
||||||
|
self.setupUi()
|
||||||
|
self.connectSignals()
|
||||||
|
self.showPaths()
|
||||||
|
self.refreshSelectionBox()
|
||||||
|
# self.refresh_timer = QTimer(self)
|
||||||
|
# self.refresh_timer.timeout.connect(self.showPaths)
|
||||||
|
# self.refresh_timer.start(10000)
|
||||||
|
|
||||||
|
self.resize(800, 450)
|
||||||
|
|
||||||
|
def setupUi(self):
|
||||||
|
ncol = 4
|
||||||
|
self.main_layout = QtWidgets.QVBoxLayout()
|
||||||
|
self.header_layout = QtWidgets.QHBoxLayout()
|
||||||
|
self.footer_layout = QtWidgets.QHBoxLayout()
|
||||||
|
#
|
||||||
|
self.setLayout(self.main_layout)
|
||||||
|
|
||||||
|
# widgets inside the dialog
|
||||||
|
self.textLabel = QtWidgets.QLabel(self.label)
|
||||||
|
self.comboBox = QtWidgets.QComboBox()
|
||||||
|
self.comboBox.addItem(self.default_text)
|
||||||
|
self.comboBox.setEditable(True)
|
||||||
|
|
||||||
|
# optional search button, currently disabled. List refreshed when text changes
|
||||||
|
self.searchButton = QtWidgets.QPushButton('Search')
|
||||||
|
self.searchButton.setVisible(False)
|
||||||
|
|
||||||
|
# check/uncheck button for table
|
||||||
|
self.checkAllButton = QtWidgets.QPushButton('Check/Uncheck all')
|
||||||
|
|
||||||
|
# radiobutton for merge selection
|
||||||
|
self.mergeRadioButtonGroup = QtWidgets.QButtonGroup()
|
||||||
|
self.merge_button = QtWidgets.QRadioButton('Merge')
|
||||||
|
self.overwrite_button = QtWidgets.QRadioButton('Overwrite')
|
||||||
|
self.mergeRadioButtonGroup.addButton(self.merge_button)
|
||||||
|
self.mergeRadioButtonGroup.addButton(self.overwrite_button)
|
||||||
|
self.merge_button.setChecked(True)
|
||||||
|
self.merge_strategy = self.merge_button.text()
|
||||||
|
|
||||||
|
# table
|
||||||
|
self.tableWidget = QtWidgets.QTableWidget()
|
||||||
|
tableWidget = self.tableWidget
|
||||||
|
tableWidget.setColumnCount(ncol)
|
||||||
|
tableWidget.setRowCount(len(self.events))
|
||||||
|
tableWidget.setHorizontalHeaderLabels(('', 'Event ID', 'Filename', 'Last modified'))
|
||||||
|
tableWidget.setEditTriggers(tableWidget.NoEditTriggers)
|
||||||
|
tableWidget.setSortingEnabled(True)
|
||||||
|
header = tableWidget.horizontalHeader()
|
||||||
|
header.setSectionResizeMode(QtWidgets.QHeaderView.ResizeToContents)
|
||||||
|
header.setStretchLastSection(True)
|
||||||
|
|
||||||
|
self.statusText = QtWidgets.QLabel()
|
||||||
|
|
||||||
|
self.header_layout.addWidget(self.textLabel)
|
||||||
|
self.header_layout.addWidget(self.comboBox)
|
||||||
|
self.header_layout.addWidget(self.searchButton)
|
||||||
|
|
||||||
|
self.footer_layout.addWidget(self.checkAllButton)
|
||||||
|
self.footer_layout.addWidget(self.statusText)
|
||||||
|
self.footer_layout.addWidget(self.merge_button)
|
||||||
|
self.footer_layout.addWidget(self.overwrite_button)
|
||||||
|
|
||||||
|
self.footer_layout.setStretch(0, 0)
|
||||||
|
self.footer_layout.setStretch(1, 1)
|
||||||
|
|
||||||
|
self.main_layout.addLayout(self.header_layout)
|
||||||
|
self.main_layout.addWidget(self.tableWidget)
|
||||||
|
self.main_layout.addLayout(self.footer_layout)
|
||||||
|
self.main_layout.addWidget(self._buttonbox)
|
||||||
|
|
||||||
|
def showPaths(self):
|
||||||
|
self.filepaths = []
|
||||||
|
fext = self.comboBox.currentText()
|
||||||
|
self.tableWidget.clearContents()
|
||||||
|
for index, event in enumerate(self.events):
|
||||||
|
filename = get_pylot_eventfile_with_extension(event, fext)
|
||||||
|
pf_selected_item = QtWidgets.QTableWidgetItem()
|
||||||
|
check_state = QtCore.Qt.Checked if filename else QtCore.Qt.Unchecked
|
||||||
|
pf_selected_item.setCheckState(check_state)
|
||||||
|
self.tableWidget.setItem(index, 0, pf_selected_item)
|
||||||
|
self.tableWidget.setItem(index, 1, QtWidgets.QTableWidgetItem(f'{event.pylot_id}'))
|
||||||
|
if filename:
|
||||||
|
self.filepaths.append(filename)
|
||||||
|
ts = int(os.path.getmtime(filename))
|
||||||
|
|
||||||
|
# create QTableWidgetItems of filepath and last modification time
|
||||||
|
fname_item = QtWidgets.QTableWidgetItem(f'{os.path.split(filename)[-1]}')
|
||||||
|
fname_item.setData(3, filename)
|
||||||
|
ts_item = QtWidgets.QTableWidgetItem(f'{datetime.datetime.fromtimestamp(ts)}')
|
||||||
|
self.tableWidget.setItem(index, 2, fname_item)
|
||||||
|
self.tableWidget.setItem(index, 3, ts_item)
|
||||||
|
|
||||||
|
self.update_status()
|
||||||
|
|
||||||
|
def refreshSelectionBox(self):
|
||||||
|
fext = self.comboBox.currentText()
|
||||||
|
self.file_extensions = [fext]
|
||||||
|
|
||||||
|
for event in self.events:
|
||||||
|
extensions = get_possible_pylot_eventfile_extensions(event, '*.xml')
|
||||||
|
for ext in extensions:
|
||||||
|
if not ext in self.file_extensions:
|
||||||
|
self.file_extensions.append(ext)
|
||||||
|
|
||||||
|
self.comboBox.clear()
|
||||||
|
for ext in sorted(self.file_extensions):
|
||||||
|
self.comboBox.addItem(ext)
|
||||||
|
|
||||||
|
def setButtons(self):
|
||||||
|
self._buttonbox = QDialogButtonBox(QDialogButtonBox.Ok |
|
||||||
|
QDialogButtonBox.Cancel)
|
||||||
|
|
||||||
|
def toggleCheckAll(self):
|
||||||
|
self.check_all_state = not self.check_all_state
|
||||||
|
self.checkAll(self.check_all_state)
|
||||||
|
|
||||||
|
def checkAll(self, state):
|
||||||
|
state = QtCore.Qt.Checked if state else QtCore.Qt.Unchecked
|
||||||
|
for row_ind in range(self.tableWidget.rowCount()):
|
||||||
|
item = self.tableWidget.item(row_ind, 0)
|
||||||
|
item.setCheckState(state)
|
||||||
|
|
||||||
|
def getChecked(self):
|
||||||
|
filepaths = []
|
||||||
|
for row_ind in range(self.tableWidget.rowCount()):
|
||||||
|
item_check = self.tableWidget.item(row_ind, 0)
|
||||||
|
if item_check.checkState() == QtCore.Qt.Checked:
|
||||||
|
item_fname = self.tableWidget.item(row_ind, 2)
|
||||||
|
if item_fname:
|
||||||
|
filepath = item_fname.data(3)
|
||||||
|
filepaths.append(filepath)
|
||||||
|
return filepaths
|
||||||
|
|
||||||
|
def update_status(self, row=None, col=None):
|
||||||
|
if col is not None and col != 0:
|
||||||
|
return
|
||||||
|
filepaths = self.getChecked()
|
||||||
|
if len(filepaths) > 0:
|
||||||
|
status_text = f"Found {len(filepaths)} eventfile{'s' if len(filepaths) > 1 else ''}. Do you want to load them?"
|
||||||
|
else:
|
||||||
|
status_text = 'Did not find/select any files for specified file mask.'
|
||||||
|
self.statusText.setText(status_text)
|
||||||
|
|
||||||
|
def update_merge_strategy(self):
|
||||||
|
for button in (self.merge_button, self.overwrite_button):
|
||||||
|
if button.isChecked():
|
||||||
|
self.merge_strategy = button.text()
|
||||||
|
|
||||||
|
def connectSignals(self):
|
||||||
|
self._buttonbox.accepted.connect(self.accept)
|
||||||
|
self._buttonbox.rejected.connect(self.reject)
|
||||||
|
self.comboBox.editTextChanged.connect(self.showPaths)
|
||||||
|
self.searchButton.clicked.connect(self.showPaths)
|
||||||
|
self.checkAllButton.clicked.connect(self.toggleCheckAll)
|
||||||
|
self.checkAllButton.clicked.connect(self.update_status)
|
||||||
|
self.tableWidget.cellClicked.connect(self.update_status)
|
||||||
|
self.merge_button.clicked.connect(self.update_merge_strategy)
|
||||||
|
self.overwrite_button.clicked.connect(self.update_merge_strategy)
|
||||||
|
|
||||||
|
|
||||||
class SingleTextLineDialog(QtWidgets.QDialog):
|
class SingleTextLineDialog(QtWidgets.QDialog):
|
||||||
def __init__(self, parent=None, label='Text: ', default_text='.xml'):
|
def __init__(self, parent=None, label='Text: ', default_text='.xml'):
|
||||||
super(SingleTextLineDialog, self).__init__(parent)
|
super(SingleTextLineDialog, self).__init__(parent)
|
||||||
@@ -1681,13 +1871,14 @@ class PickDlg(QDialog):
|
|||||||
|
|
||||||
def __init__(self, parent=None, data=None, data_compare=None, station=None, network=None, location=None, picks=None,
|
def __init__(self, parent=None, data=None, data_compare=None, station=None, network=None, location=None, picks=None,
|
||||||
autopicks=None, rotate=False, parameter=None, embedded=False, metadata=None, show_comp_data=False,
|
autopicks=None, rotate=False, parameter=None, embedded=False, metadata=None, show_comp_data=False,
|
||||||
event=None, filteroptions=None, model=None, wftype=None):
|
event=None, filteroptions=None, wftype=None):
|
||||||
super(PickDlg, self).__init__(parent, Qt.Window)
|
super(PickDlg, self).__init__(parent, Qt.Window)
|
||||||
self.orig_parent = parent
|
self.orig_parent = parent
|
||||||
self.setAttribute(Qt.WA_DeleteOnClose)
|
self.setAttribute(Qt.WA_DeleteOnClose)
|
||||||
|
|
||||||
# initialize attributes
|
# initialize attributes
|
||||||
self.parameter = parameter
|
self.parameter = parameter
|
||||||
|
model = self.parameter.get('taup_model')
|
||||||
self._embedded = embedded
|
self._embedded = embedded
|
||||||
self.showCompData = show_comp_data
|
self.showCompData = show_comp_data
|
||||||
self.station = station
|
self.station = station
|
||||||
@@ -2064,10 +2255,12 @@ class PickDlg(QDialog):
|
|||||||
station_id = trace.get_id()
|
station_id = trace.get_id()
|
||||||
starttime = trace.stats.starttime
|
starttime = trace.stats.starttime
|
||||||
station_coords = self.metadata.get_coordinates(station_id, starttime)
|
station_coords = self.metadata.get_coordinates(station_id, starttime)
|
||||||
|
if not station_coords:
|
||||||
|
print('get_arrivals: No station coordinates found. Return!')
|
||||||
|
return
|
||||||
origins = self.pylot_event.origins
|
origins = self.pylot_event.origins
|
||||||
if phases == ['None', 'None']:
|
if phases == ['None', 'None']:
|
||||||
print("get_arrivals: Creation info (manual or auto) not available!")
|
print("get_arrivals: Creation info (manual or auto) not available! Return!")
|
||||||
print("Return!")
|
|
||||||
return
|
return
|
||||||
if origins:
|
if origins:
|
||||||
source_origin = origins[0]
|
source_origin = origins[0]
|
||||||
@@ -2078,8 +2271,8 @@ class PickDlg(QDialog):
|
|||||||
arrivals = func[plot](source_origin.depth,
|
arrivals = func[plot](source_origin.depth,
|
||||||
source_origin.latitude,
|
source_origin.latitude,
|
||||||
source_origin.longitude,
|
source_origin.longitude,
|
||||||
station_coords['latitude'],
|
station_coords.get('latitude'),
|
||||||
station_coords['longitude'],
|
station_coords.get('longitude'),
|
||||||
phases)
|
phases)
|
||||||
self.arrivals = arrivals
|
self.arrivals = arrivals
|
||||||
|
|
||||||
@@ -2179,7 +2372,7 @@ class PickDlg(QDialog):
|
|||||||
|
|
||||||
# create action and add to menu
|
# create action and add to menu
|
||||||
# phase name transferred using lambda function
|
# phase name transferred using lambda function
|
||||||
slot = lambda phase=phase, phaseID=phaseID: phaseSelect[phaseID](phase)
|
slot = lambda ph=phase, phID=phaseID: phaseSelect[phID](ph)
|
||||||
picksAction = createAction(parent=self, text=phase,
|
picksAction = createAction(parent=self, text=phase,
|
||||||
slot=slot,
|
slot=slot,
|
||||||
shortcut=shortcut)
|
shortcut=shortcut)
|
||||||
@@ -2598,7 +2791,7 @@ class PickDlg(QDialog):
|
|||||||
for wfd in [wfdata, wfdata_comp]:
|
for wfd in [wfdata, wfdata_comp]:
|
||||||
if wfd:
|
if wfd:
|
||||||
for trace in wfd:
|
for trace in wfd:
|
||||||
t = prepTimeAxis(trace.stats.starttime - stime, trace)
|
t = prep_time_axis(trace.stats.starttime - stime, trace)
|
||||||
inoise = getnoisewin(t, ini_pick, noise_win, gap_win)
|
inoise = getnoisewin(t, ini_pick, noise_win, gap_win)
|
||||||
trace = demeanTrace(trace, inoise)
|
trace = demeanTrace(trace, inoise)
|
||||||
# upscale trace wfdata in a way that each trace is vertically zoomed to noiselevel*factor
|
# upscale trace wfdata in a way that each trace is vertically zoomed to noiselevel*factor
|
||||||
@@ -3624,8 +3817,9 @@ class TuneAutopicker(QWidget):
|
|||||||
# wfdat = remove_underscores(wfdat)
|
# wfdat = remove_underscores(wfdat)
|
||||||
# rotate misaligned stations to ZNE
|
# rotate misaligned stations to ZNE
|
||||||
# check for gaps and doubled channels
|
# check for gaps and doubled channels
|
||||||
wfdat, gaps = merge_stream(wfdat)
|
wfdat, _ = check_for_gaps_and_merge(wfdat)
|
||||||
# check4gaps(wfdat)
|
# check for nans
|
||||||
|
check_for_nan(wfdat)
|
||||||
check4doubled(wfdat)
|
check4doubled(wfdat)
|
||||||
wfdat = check4rotated(wfdat, self.parent().metadata, verbosity=0)
|
wfdat = check4rotated(wfdat, self.parent().metadata, verbosity=0)
|
||||||
# trim station components to same start value
|
# trim station components to same start value
|
||||||
@@ -3643,7 +3837,7 @@ class TuneAutopicker(QWidget):
|
|||||||
self.stb_names = ['aicARHfig', 'refSpick', 'el_S1pick', 'el_S2pick']
|
self.stb_names = ['aicARHfig', 'refSpick', 'el_S1pick', 'el_S2pick']
|
||||||
|
|
||||||
def add_parameters(self):
|
def add_parameters(self):
|
||||||
self.paraBox = PylotParaBox(self.parameter, parent=self, windowflag=Qt.Widget)
|
self.paraBox = PylotParameterWidget(self.parameter, parent=self, windowflag=Qt.Widget)
|
||||||
self.paraBox.set_tune_mode(True)
|
self.paraBox.set_tune_mode(True)
|
||||||
self.update_eventID()
|
self.update_eventID()
|
||||||
self.parameter_layout.addWidget(self.paraBox)
|
self.parameter_layout.addWidget(self.paraBox)
|
||||||
@@ -3782,6 +3976,7 @@ class TuneAutopicker(QWidget):
|
|||||||
st = self.data.getWFData()
|
st = self.data.getWFData()
|
||||||
tr = st.select(station=self.get_current_station())[0]
|
tr = st.select(station=self.get_current_station())[0]
|
||||||
starttime = tr.stats.starttime
|
starttime = tr.stats.starttime
|
||||||
|
# create two lists with figure names and subindices (for subplots) to get the correct axes
|
||||||
p_axes = [
|
p_axes = [
|
||||||
('mainFig', 0),
|
('mainFig', 0),
|
||||||
('aicFig', 0),
|
('aicFig', 0),
|
||||||
@@ -4009,7 +4204,7 @@ class TuneAutopicker(QWidget):
|
|||||||
self.qmb.show()
|
self.qmb.show()
|
||||||
|
|
||||||
|
|
||||||
class PylotParaBox(QtWidgets.QWidget):
|
class PylotParameterWidget(QtWidgets.QWidget):
|
||||||
accepted = QtCore.Signal(str)
|
accepted = QtCore.Signal(str)
|
||||||
rejected = QtCore.Signal(str)
|
rejected = QtCore.Signal(str)
|
||||||
|
|
||||||
@@ -4123,6 +4318,11 @@ class PylotParaBox(QtWidgets.QWidget):
|
|||||||
grid = QtWidgets.QGridLayout()
|
grid = QtWidgets.QGridLayout()
|
||||||
|
|
||||||
for index1, name in enumerate(parameter_names):
|
for index1, name in enumerate(parameter_names):
|
||||||
|
if name in ['rootpath', 'database']:
|
||||||
|
logging.warning(
|
||||||
|
f'Deprecated parameter loaded: {name}. Check if datapath is still correct in parameter widget.'
|
||||||
|
)
|
||||||
|
continue
|
||||||
default_item = self.parameter.get_defaults()[name]
|
default_item = self.parameter.get_defaults()[name]
|
||||||
tooltip = default_item['tooltip']
|
tooltip = default_item['tooltip']
|
||||||
tooltip += ' | type: {}'.format(default_item['type'])
|
tooltip += ' | type: {}'.format(default_item['type'])
|
||||||
@@ -4692,7 +4892,7 @@ class PropTab(QWidget):
|
|||||||
def getValues(self):
|
def getValues(self):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def resetValues(self, infile=None):
|
def resetValues(self, infile):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
@@ -4729,8 +4929,8 @@ class InputsTab(PropTab):
|
|||||||
self.tstopBox = QSpinBox()
|
self.tstopBox = QSpinBox()
|
||||||
for spinbox in [self.tstartBox, self.tstopBox]:
|
for spinbox in [self.tstartBox, self.tstopBox]:
|
||||||
spinbox.setRange(-99999, 99999)
|
spinbox.setRange(-99999, 99999)
|
||||||
self.tstartBox.setValue(float(settings.value('tstart')) if get_None(settings.value('tstart')) else 0)
|
self.tstartBox.setValue(float(settings.value('tstart')) if get_none(settings.value('tstart')) else 0)
|
||||||
self.tstopBox.setValue(float(settings.value('tstop')) if get_None(settings.value('tstop')) else 1e6)
|
self.tstopBox.setValue(float(settings.value('tstop')) if get_none(settings.value('tstop')) else 1e6)
|
||||||
self.cuttimesLayout.addWidget(self.tstartBox, 10)
|
self.cuttimesLayout.addWidget(self.tstartBox, 10)
|
||||||
self.cuttimesLayout.addWidget(QLabel('[s] and'), 0)
|
self.cuttimesLayout.addWidget(QLabel('[s] and'), 0)
|
||||||
self.cuttimesLayout.addWidget(self.tstopBox, 10)
|
self.cuttimesLayout.addWidget(self.tstopBox, 10)
|
||||||
@@ -4789,12 +4989,7 @@ class InputsTab(PropTab):
|
|||||||
else:
|
else:
|
||||||
index = 2
|
index = 2
|
||||||
datapath = para.get('datapath') if not para.get('datapath') is None else ''
|
datapath = para.get('datapath') if not para.get('datapath') is None else ''
|
||||||
rootpath = para.get('rootpath') if not para.get('rootpath') is None else ''
|
values = {"data/dataRoot": self.dataDirEdit.setText("%s" % datapath),
|
||||||
database = para.get('database') if not para.get('database') is None else ''
|
|
||||||
if isinstance(database, int):
|
|
||||||
database = str(database)
|
|
||||||
path = os.path.join(os.path.expanduser('~'), rootpath, datapath, database)
|
|
||||||
values = {"data/dataRoot": self.dataDirEdit.setText("%s" % path),
|
|
||||||
"user/FullName": self.fullNameEdit.text(),
|
"user/FullName": self.fullNameEdit.text(),
|
||||||
"data/Structure": self.structureSelect.setCurrentIndex(index),
|
"data/Structure": self.structureSelect.setCurrentIndex(index),
|
||||||
"tstart": self.tstartBox.setValue(0),
|
"tstart": self.tstartBox.setValue(0),
|
||||||
@@ -5726,7 +5921,7 @@ class ChooseWaveFormWindow(QWidget):
|
|||||||
#self.currentSpectro = self.traces[
|
#self.currentSpectro = self.traces[
|
||||||
# self.chooseBoxTraces.currentText()[3:]][self.chooseBoxComponent.currentText()].spectrogram(show=False, title=t)
|
# self.chooseBoxTraces.currentText()[3:]][self.chooseBoxComponent.currentText()].spectrogram(show=False, title=t)
|
||||||
#self.currentSpectro.show()
|
#self.currentSpectro.show()
|
||||||
applyFFT()
|
self.applyFFT()
|
||||||
|
|
||||||
def applyFFT(self, trace):
|
def applyFFT(self, trace):
|
||||||
tra = self.traces[self.chooseBoxTraces.currentText()[3:]]['Z']
|
tra = self.traces[self.chooseBoxTraces.currentText()[3:]]['Z']
|
||||||
|
|||||||
@@ -0,0 +1,2 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
############################# correlation parameters #####################################
|
||||||
|
# min_corr_stacking: minimum correlation coefficient for building beam trace
|
||||||
|
# min_corr_export: minimum correlation coefficient for pick export
|
||||||
|
# min_stack: minimum number of stations for building beam trace
|
||||||
|
# t_before: correlation window before pick
|
||||||
|
# t_after: correlation window after pick#
|
||||||
|
# cc_maxlag: maximum shift for initial correlation
|
||||||
|
# cc_maxlag2: maximum shift for second (final) correlation (also for calculating pick uncertainty)
|
||||||
|
# initial_pick_outlier_threshold: (hopefully) threshold for excluding large outliers of initial (AIC) picks
|
||||||
|
# export_threshold: automatically exclude all onsets which deviate more than this threshold from corrected taup onsets
|
||||||
|
# min_picks_export: minimum number of correlated picks for export
|
||||||
|
# min_picks_autopylot: minimum number of reference autopicks picks to continue with event
|
||||||
|
# check_RMS: do RMS check to search for restitution errors (very experimental)
|
||||||
|
# use_taupy_onsets: use taupy onsets as reference picks instead of external picks
|
||||||
|
# station_list: use the following stations as reference for stacking
|
||||||
|
# use_stacked_trace: use existing stacked trace if found (spare re-computation)
|
||||||
|
# data_dir: obspyDMT data subdirectory (e.g. 'raw', 'processed')
|
||||||
|
# pickfile_extension: use quakeML files (PyLoT output) with the following extension, e.g. '_autopylot' for pickfiles
|
||||||
|
# such as 'PyLoT_20170501_141822_autopylot.xml'
|
||||||
|
|
||||||
|
logging: info
|
||||||
|
pick_phases: ['P', 'S']
|
||||||
|
|
||||||
|
# P-phase
|
||||||
|
P:
|
||||||
|
min_corr_stacking: 0.8
|
||||||
|
min_corr_export: 0.6
|
||||||
|
min_stack: 20
|
||||||
|
t_before: 30.
|
||||||
|
t_after: 50.
|
||||||
|
cc_maxlag: 50.
|
||||||
|
cc_maxlag2: 5.
|
||||||
|
initial_pick_outlier_threshold: 30.
|
||||||
|
export_threshold: 2.5
|
||||||
|
min_picks_export: 100
|
||||||
|
min_picks_autopylot: 50
|
||||||
|
check_RMS: True
|
||||||
|
use_taupy_onsets: False
|
||||||
|
station_list: ['HU.MORH', 'HU.TIH', 'OX.FUSE', 'OX.BAD']
|
||||||
|
use_stacked_trace: False
|
||||||
|
data_dir: 'processed'
|
||||||
|
pickfile_extension: '_autopylot'
|
||||||
|
dt_stacking: [250, 250]
|
||||||
|
|
||||||
|
# filter for first correlation (rough)
|
||||||
|
filter_options:
|
||||||
|
freqmax: 0.5
|
||||||
|
freqmin: 0.03
|
||||||
|
# filter for second correlation (fine)
|
||||||
|
filter_options_final:
|
||||||
|
freqmax: 0.5
|
||||||
|
freqmin: 0.03
|
||||||
|
|
||||||
|
filter_type: bandpass
|
||||||
|
sampfreq: 20.0
|
||||||
|
|
||||||
|
# S-phase
|
||||||
|
S:
|
||||||
|
min_corr_stacking: 0.7
|
||||||
|
min_corr_export: 0.6
|
||||||
|
min_stack: 20
|
||||||
|
t_before: 60.
|
||||||
|
t_after: 60.
|
||||||
|
cc_maxlag: 100.
|
||||||
|
cc_maxlag2: 25.
|
||||||
|
initial_pick_outlier_threshold: 30.
|
||||||
|
export_threshold: 5.0
|
||||||
|
min_picks_export: 200
|
||||||
|
min_picks_autopylot: 50
|
||||||
|
check_RMS: True
|
||||||
|
use_taupy_onsets: False
|
||||||
|
station_list: ['HU.MORH','HU.TIH', 'OX.FUSE', 'OX.BAD']
|
||||||
|
use_stacked_trace: False
|
||||||
|
data_dir: 'processed'
|
||||||
|
pickfile_extension: '_autopylot'
|
||||||
|
dt_stacking: [250, 250]
|
||||||
|
|
||||||
|
# filter for first correlation (rough)
|
||||||
|
filter_options:
|
||||||
|
freqmax: 0.1
|
||||||
|
freqmin: 0.01
|
||||||
|
|
||||||
|
# filter for second correlation (fine)
|
||||||
|
filter_options_final:
|
||||||
|
freqmax: 0.2
|
||||||
|
freqmin: 0.01
|
||||||
|
|
||||||
|
filter_type: bandpass
|
||||||
|
sampfreq: 20.0
|
||||||
|
|
||||||
File diff suppressed because it is too large
Load Diff
+40
@@ -0,0 +1,40 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
#ulimit -s 8192
|
||||||
|
#ulimit -v $(ulimit -v | awk '{printf("%d",$1*0.95)}')
|
||||||
|
#ulimit -v
|
||||||
|
|
||||||
|
#655360
|
||||||
|
|
||||||
|
source /opt/anaconda3/etc/profile.d/conda.sh
|
||||||
|
conda activate pylot_311
|
||||||
|
NSLOTS=20
|
||||||
|
|
||||||
|
#qsub -l low -cwd -l "os=*stretch" -pe smp 40 submit_pick_corr_correction.sh
|
||||||
|
#$ -l low
|
||||||
|
#$ -l h_vmem=6G
|
||||||
|
#$ -cwd
|
||||||
|
#$ -pe smp 20
|
||||||
|
#$ -N corr_pick
|
||||||
|
|
||||||
|
|
||||||
|
export PYTHONPATH="$PYTHONPATH:/home/marcel/git/pylot_tools/"
|
||||||
|
export PYTHONPATH="$PYTHONPATH:/home/marcel/git/"
|
||||||
|
export PYTHONPATH="$PYTHONPATH:/home/marcel/git/pylot/"
|
||||||
|
|
||||||
|
#export MKL_NUM_THREADS=${NSLOTS:=1}
|
||||||
|
#export NUMEXPR_NUM_THREADS=${NSLOTS:=1}
|
||||||
|
#export OMP_NUM_THREADS=${NSLOTS:=1}
|
||||||
|
|
||||||
|
#python pick_correlation_correction.py '/data/AlpArray_Data/dmt_database_mantle_M5.8-6.0' '/home/marcel/.pylot/pylot_alparray_mantle_corr_stack_0.03-0.5.in' -pd -n ${NSLOTS:=1} -istart 0 -istop 100
|
||||||
|
#python pick_correlation_correction.py '/data/AlpArray_Data/dmt_database_mantle_M5.8-6.0' '/home/marcel/.pylot/pylot_alparray_mantle_corr_stack_0.03-0.5.in' -pd -n ${NSLOTS:=1} -istart 100 -istop 200
|
||||||
|
#python pick_correlation_correction.py '/data/AlpArray_Data/dmt_database_mantle_M6.0-6.5' '/home/marcel/.pylot/pylot_alparray_mantle_corr_stack_0.03-0.5.in' -pd -n ${NSLOTS:=1} -istart 0 -istop 100
|
||||||
|
#python pick_correlation_correction.py '/data/AlpArray_Data/dmt_database_mantle_M5.8-6.0' '/home/marcel/.pylot/pylot_alparray_mantle_corr_stack_0.03-0.5.in' -pd -n ${NSLOTS:=1} -istart 100 -istop 200
|
||||||
|
#python pick_correlation_correction.py 'H:\sciebo\dmt_database' 'H:\Sciebo\dmt_database\pylot_alparray_mantle_corr_S_0.01-0.2.in' -pd -n 4 -t
|
||||||
|
|
||||||
|
pylot_infile='/home/marcel/.pylot/pylot_alparray_syn_fwi_mk6_it3.in'
|
||||||
|
#pylot_infile='/home/marcel/.pylot/pylot_adriaarray_corr_P_and_S.in'
|
||||||
|
|
||||||
|
# THIS SCRIPT SHOLD BE CALLED BY "submit_to_grid_engine.py" using the following line:
|
||||||
|
python pick_correlation_correction.py $1 $pylot_infile -pd -n ${NSLOTS:=1} -istart $2 --params 'parameters_fwi_mk6_it3.yaml'
|
||||||
|
#--event_blacklist eventlist.txt
|
||||||
Executable
+23
@@ -0,0 +1,23 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
fnames = [
|
||||||
|
('/data/AlpArray_Data/dmt_database_synth_model_mk6_it3_no_rotation', 0),
|
||||||
|
]
|
||||||
|
|
||||||
|
#fnames = [('/data/AlpArray_Data/dmt_database_mantle_0.01-0.2_SKS-phase', 0),
|
||||||
|
# ('/data/AlpArray_Data/dmt_database_mantle_0.01-0.2_S-phase', 0),]
|
||||||
|
|
||||||
|
####
|
||||||
|
script_location = '/home/marcel/VersionCtrl/git/code_base/correlation_picker/submit_pick_corr_correction.sh'
|
||||||
|
####
|
||||||
|
|
||||||
|
for fnin, istart in fnames:
|
||||||
|
input_cmds = f'qsub -q low.q@minos15,low.q@minos14,low.q@minos13,low.q@minos12,low.q@minos11 {script_location} {fnin} {istart}'
|
||||||
|
|
||||||
|
print(input_cmds)
|
||||||
|
print(subprocess.check_output(input_cmds.split()))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import os
|
||||||
|
import glob
|
||||||
|
import json
|
||||||
|
|
||||||
|
from obspy import read_events
|
||||||
|
|
||||||
|
from pylot.core.util.dataprocessing import Metadata
|
||||||
|
from pylot.core.util.obspyDMT_interface import qml_from_obspyDMT
|
||||||
|
|
||||||
|
|
||||||
|
def get_event_obspy_dmt(eventdir):
|
||||||
|
event_pkl_file = os.path.join(eventdir, 'info', 'event.pkl')
|
||||||
|
if not os.path.exists(event_pkl_file):
|
||||||
|
raise IOError('Could not find event path for event: {}'.format(eventdir))
|
||||||
|
event = qml_from_obspyDMT(event_pkl_file)
|
||||||
|
return event
|
||||||
|
|
||||||
|
|
||||||
|
def get_event_pylot(eventdir, extension=''):
|
||||||
|
event_id = get_event_id(eventdir)
|
||||||
|
filename = os.path.join(eventdir, 'PyLoT_{}{}.xml'.format(event_id, extension))
|
||||||
|
if not os.path.isfile(filename):
|
||||||
|
return
|
||||||
|
cat = read_events(filename)
|
||||||
|
return cat[0]
|
||||||
|
|
||||||
|
|
||||||
|
def get_event_id(eventdir):
|
||||||
|
event_id = os.path.split(eventdir)[-1]
|
||||||
|
return event_id
|
||||||
|
|
||||||
|
|
||||||
|
def get_picks(eventdir, extension=''):
|
||||||
|
event_id = get_event_id(eventdir)
|
||||||
|
filename = 'PyLoT_{}{}.xml'
|
||||||
|
filename = filename.format(event_id, extension)
|
||||||
|
fpath = os.path.join(eventdir, filename)
|
||||||
|
fpaths = glob.glob(fpath)
|
||||||
|
if len(fpaths) == 1:
|
||||||
|
cat = read_events(fpaths[0])
|
||||||
|
picks = cat[0].picks
|
||||||
|
return picks
|
||||||
|
elif len(fpaths) == 0:
|
||||||
|
print('get_picks: File not found: {}'.format(fpath))
|
||||||
|
return
|
||||||
|
print(f'WARNING: Ambiguous pick file specification. Found the following pick files {fpaths}\nFilemask: {fpath}')
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
def write_json(object, fname):
|
||||||
|
with open(fname, 'w') as outfile:
|
||||||
|
json.dump(object, outfile, sort_keys=True, indent=4)
|
||||||
|
|
||||||
|
|
||||||
|
def get_metadata(eventdir):
|
||||||
|
metadata_path = os.path.join(eventdir, 'resp')
|
||||||
|
metadata = Metadata(inventory=metadata_path, verbosity=0)
|
||||||
|
return metadata
|
||||||
+7
-12
@@ -1,12 +1,7 @@
|
|||||||
# This file may be used to create an environment using:
|
Cartopy==0.23.0
|
||||||
# $ conda create --name <env> --file <this file>
|
joblib==1.4.2
|
||||||
# platform: win-64
|
obspy==1.4.1
|
||||||
cartopy=0.20.2
|
pyaml==24.7.0
|
||||||
matplotlib-base=3.3.4
|
pyqtgraph==0.13.7
|
||||||
numpy=1.22.3
|
PySide2==5.15.8
|
||||||
obspy=1.3.0
|
pytest==8.3.2
|
||||||
pyqtgraph=0.12.4
|
|
||||||
pyside2=5.13.2
|
|
||||||
python=3.8.12
|
|
||||||
qt=5.12.9
|
|
||||||
scipy=1.8.0
|
|
||||||
@@ -0,0 +1,101 @@
|
|||||||
|
%This is a parameter input file for PyLoT/autoPyLoT.
|
||||||
|
%All main and special settings regarding data handling
|
||||||
|
%and picking are to be set here!
|
||||||
|
%Parameters are optimized for %extent data sets!
|
||||||
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||||
|
#main settings#
|
||||||
|
#rootpath# %project path
|
||||||
|
#datapath# %data path
|
||||||
|
#database# %name of data base
|
||||||
|
20171010_063224.a #eventID# %event ID for single event processing (* for all events found in database)
|
||||||
|
#invdir# %full path to inventory or dataless-seed file
|
||||||
|
PILOT #datastructure# %choose data structure
|
||||||
|
True #apverbose# %choose 'True' or 'False' for terminal output
|
||||||
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||||
|
#NLLoc settings#
|
||||||
|
None #nllocbin# %path to NLLoc executable
|
||||||
|
None #nllocroot# %root of NLLoc-processing directory
|
||||||
|
None #phasefile# %name of autoPyLoT-output phase file for NLLoc
|
||||||
|
None #ctrfile# %name of autoPyLoT-output control file for NLLoc
|
||||||
|
ttime #ttpatter# %pattern of NLLoc ttimes from grid
|
||||||
|
AUTOLOC_nlloc #outpatter# %pattern of NLLoc-output file
|
||||||
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||||
|
#parameters for seismic moment estimation#
|
||||||
|
3530.0 #vp# %average P-wave velocity
|
||||||
|
2500.0 #rho# %average rock density [kg/m^3]
|
||||||
|
300.0 0.8 #Qp# %quality factor for P waves (Qp*f^a); list(Qp, a)
|
||||||
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||||
|
#settings local magnitude#
|
||||||
|
1.0 1.0 1.0 #WAscaling# %Scaling relation (log(Ao)+Alog(r)+Br+C) of Wood-Anderson amplitude Ao [nm] If zeros are set, original Richter magnitude is calculated!
|
||||||
|
1.0 1.0 #magscaling# %Scaling relation for derived local magnitude [a*Ml+b]. If zeros are set, no scaling of network magnitude is applied!
|
||||||
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||||
|
#filter settings#
|
||||||
|
0.03 0.03 #minfreq# %Lower filter frequency [P, S]
|
||||||
|
0.5 0.5 #maxfreq# %Upper filter frequency [P, S]
|
||||||
|
4 4 #filter_order# %filter order [P, S]
|
||||||
|
bandpass bandpass #filter_type# %filter type (bandpass, bandstop, lowpass, highpass) [P, S]
|
||||||
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||||
|
#common settings picker#
|
||||||
|
global #extent# %extent of array ("local", "regional" or "global")
|
||||||
|
-100.0 #pstart# %start time [s] for calculating CF for P-picking (if TauPy: seconds relative to estimated onset)
|
||||||
|
50.0 #pstop# %end time [s] for calculating CF for P-picking (if TauPy: seconds relative to estimated onset)
|
||||||
|
-50.0 #sstart# %start time [s] relative to P-onset for calculating CF for S-picking
|
||||||
|
50.0 #sstop# %end time [s] after P-onset for calculating CF for S-picking
|
||||||
|
True #use_taup# %use estimated traveltimes from TauPy for calculating windows for CF
|
||||||
|
ak135 #taup_model# %Define TauPy model for traveltime estimation. Possible values: 1066a, 1066b, ak135, ak135f, herrin, iasp91, jb, prem, pwdk, sp6
|
||||||
|
P,Pdiff,S,SKS #taup_phases# %Specify possible phases for TauPy (comma separated). See Obspy TauPy documentation for possible values.
|
||||||
|
0.03 0.5 #bpz1# %lower/upper corner freq. of first band pass filter Z-comp. [Hz]
|
||||||
|
0.01 0.5 #bpz2# %lower/upper corner freq. of second band pass filter Z-comp. [Hz]
|
||||||
|
0.03 0.5 #bph1# %lower/upper corner freq. of first band pass filter H-comp. [Hz]
|
||||||
|
0.01 0.5 #bph2# %lower/upper corner freq. of second band pass filter z-comp. [Hz]
|
||||||
|
#special settings for calculating CF#
|
||||||
|
%!!Edit the following only if you know what you are doing!!%
|
||||||
|
#Z-component#
|
||||||
|
HOS #algoP# %choose algorithm for P-onset determination (HOS, ARZ, or AR3)
|
||||||
|
300.0 #tlta# %for HOS-/AR-AIC-picker, length of LTA window [s]
|
||||||
|
4 #hosorder# %for HOS-picker, order of Higher Order Statistics
|
||||||
|
2 #Parorder# %for AR-picker, order of AR process of Z-component
|
||||||
|
16.0 #tdet1z# %for AR-picker, length of AR determination window [s] for Z-component, 1st pick
|
||||||
|
10.0 #tpred1z# %for AR-picker, length of AR prediction window [s] for Z-component, 1st pick
|
||||||
|
12.0 #tdet2z# %for AR-picker, length of AR determination window [s] for Z-component, 2nd pick
|
||||||
|
6.0 #tpred2z# %for AR-picker, length of AR prediction window [s] for Z-component, 2nd pick
|
||||||
|
0.001 #addnoise# %add noise to seismogram for stable AR prediction
|
||||||
|
60.0 5.0 20.0 12.0 #tsnrz# %for HOS/AR, window lengths for SNR-and slope estimation [tnoise, tsafetey, tsignal, tslope] [s]
|
||||||
|
50.0 #pickwinP# %for initial AIC pick, length of P-pick window [s]
|
||||||
|
30.0 #Precalcwin# %for HOS/AR, window length [s] for recalculation of CF (relative to 1st pick)
|
||||||
|
2.0 #aictsmooth# %for HOS/AR, take average of samples for smoothing of AIC-function [s]
|
||||||
|
2.0 #tsmoothP# %for HOS/AR, take average of samples in this time window for smoothing CF [s]
|
||||||
|
0.006 #ausP# %for HOS/AR, artificial uplift of samples (aus) of CF (P)
|
||||||
|
2.0 #nfacP# %for HOS/AR, noise factor for noise level determination (P)
|
||||||
|
#H-components#
|
||||||
|
ARH #algoS# %choose algorithm for S-onset determination (ARH or AR3)
|
||||||
|
12.0 #tdet1h# %for HOS/AR, length of AR-determination window [s], H-components, 1st pick
|
||||||
|
6.0 #tpred1h# %for HOS/AR, length of AR-prediction window [s], H-components, 1st pick
|
||||||
|
8.0 #tdet2h# %for HOS/AR, length of AR-determinaton window [s], H-components, 2nd pick
|
||||||
|
4.0 #tpred2h# %for HOS/AR, length of AR-prediction window [s], H-components, 2nd pick
|
||||||
|
4 #Sarorder# %for AR-picker, order of AR process of H-components
|
||||||
|
100.0 #Srecalcwin# %for AR-picker, window length [s] for recalculation of CF (2nd pick) (H)
|
||||||
|
195.0 #pickwinS# %for initial AIC pick, length of S-pick window [s]
|
||||||
|
60.0 10.0 30.0 12.0 #tsnrh# %for ARH/AR3, window lengths for SNR-and slope estimation [tnoise, tsafetey, tsignal, tslope] [s]
|
||||||
|
22.0 #aictsmoothS# %for AIC-picker, take average of samples in this time window for smoothing of AIC-function [s]
|
||||||
|
20.0 #tsmoothS# %for AR-picker, take average of samples for smoothing CF [s] (S)
|
||||||
|
0.001 #ausS# %for HOS/AR, artificial uplift of samples (aus) of CF (S)
|
||||||
|
2.0 #nfacS# %for AR-picker, noise factor for noise level determination (S)
|
||||||
|
#first-motion picker#
|
||||||
|
1 #minfmweight# %minimum required P weight for first-motion determination
|
||||||
|
3.0 #minFMSNR# %miniumum required SNR for first-motion determination
|
||||||
|
10.0 #fmpickwin# %pick window [s] around P onset for calculating zero crossings
|
||||||
|
#quality assessment#
|
||||||
|
0.1 0.2 0.4 0.8 #timeerrorsP# %discrete time errors [s] corresponding to picking weights [0 1 2 3] for P
|
||||||
|
4.0 8.0 16.0 32.0 #timeerrorsS# %discrete time errors [s] corresponding to picking weights [0 1 2 3] for S
|
||||||
|
0.005 #minAICPslope# %below this slope [counts/s] the initial P pick is rejected
|
||||||
|
1.1 #minAICPSNR# %below this SNR the initial P pick is rejected
|
||||||
|
0.002 #minAICSslope# %below this slope [counts/s] the initial S pick is rejected
|
||||||
|
1.3 #minAICSSNR# %below this SNR the initial S pick is rejected
|
||||||
|
20.0 #minsiglength# %length of signal part for which amplitudes must exceed noiselevel [s]
|
||||||
|
1.0 #noisefactor# %noiselevel*noisefactor=threshold
|
||||||
|
10.0 #minpercent# %required percentage of amplitudes exceeding threshold
|
||||||
|
0.1 #zfac# %P-amplitude must exceed at least zfac times RMS-S amplitude
|
||||||
|
100.0 #mdttolerance# %maximum allowed deviation of P picks from median [s]
|
||||||
|
50.0 #wdttolerance# %maximum allowed deviation from Wadati-diagram
|
||||||
|
25.0 #jackfactor# %pick is removed if the variance of the subgroup with the pick removed is larger than the mean variance of all subgroups times safety factor
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
import os
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from autoPyLoT import autoPyLoT
|
||||||
|
|
||||||
|
|
||||||
|
class TestAutopickerGlobal():
|
||||||
|
def init(self):
|
||||||
|
self.params_infile = 'pylot_alparray_mantle_corr_stack_0.03-0.5.in'
|
||||||
|
self.test_event_dir = 'dmt_database_test'
|
||||||
|
|
||||||
|
if not os.path.isfile(self.params_infile):
|
||||||
|
print(f'Test input file {os.path.abspath(self.params_infile)} not found.')
|
||||||
|
return False
|
||||||
|
|
||||||
|
if not os.path.exists(self.test_event_dir):
|
||||||
|
print(
|
||||||
|
f'Test event directory not found at location "{os.path.abspath(self.test_event_dir)}". '
|
||||||
|
f'Make sure to load it from the website first.'
|
||||||
|
)
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def test_autopicker(self):
|
||||||
|
assert self.init(), 'Initialization failed due to missing input files.'
|
||||||
|
#autoPyLoT(inputfile=self.params_infile, eventid='20171010_063224.a')
|
||||||
@@ -4,10 +4,8 @@
|
|||||||
%Parameters are optimized for %extent data sets!
|
%Parameters are optimized for %extent data sets!
|
||||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||||
#main settings#
|
#main settings#
|
||||||
/home/darius #rootpath# %project path
|
/home/darius/alparray/waveforms_used #datapath# %data path
|
||||||
alparray #datapath# %data path
|
e0093.173.16 #eventID# %event ID for single event processing (* for all events found in datapath)
|
||||||
waveforms_used #database# %name of data base
|
|
||||||
e0093.173.16 #eventID# %event ID for single event processing (* for all events found in database)
|
|
||||||
/home/darius/alparray/metadata #invdir# %full path to inventory or dataless-seed file
|
/home/darius/alparray/metadata #invdir# %full path to inventory or dataless-seed file
|
||||||
PILOT #datastructure# %choose data structure
|
PILOT #datastructure# %choose data structure
|
||||||
True #apverbose# %choose 'True' or 'False' for terminal output
|
True #apverbose# %choose 'True' or 'False' for terminal output
|
||||||
|
|||||||
@@ -4,10 +4,8 @@
|
|||||||
%Parameters are optimized for %extent data sets!
|
%Parameters are optimized for %extent data sets!
|
||||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||||
#main settings#
|
#main settings#
|
||||||
/home/darius #rootpath# %project path
|
/home/darius/alparray/waveforms_used #datapath# %data path
|
||||||
alparray #datapath# %data path
|
e0093.173.16 #eventID# %event ID for single event processing (* for all events found in datapath)
|
||||||
waveforms_used #database# %name of data base
|
|
||||||
e0093.173.16 #eventID# %event ID for single event processing (* for all events found in database)
|
|
||||||
/home/darius/alparray/metadata #invdir# %full path to inventory or dataless-seed file
|
/home/darius/alparray/metadata #invdir# %full path to inventory or dataless-seed file
|
||||||
PILOT #datastructure# %choose data structure
|
PILOT #datastructure# %choose data structure
|
||||||
True #apverbose# %choose 'True' or 'False' for terminal output
|
True #apverbose# %choose 'True' or 'False' for terminal output
|
||||||
|
|||||||
@@ -0,0 +1,76 @@
|
|||||||
|
import pytest
|
||||||
|
from obspy import read, Trace, UTCDateTime
|
||||||
|
|
||||||
|
from pylot.correlation.pick_correlation_correction import XCorrPickCorrection
|
||||||
|
|
||||||
|
|
||||||
|
class TestXCorrPickCorrection():
|
||||||
|
def setup(self):
|
||||||
|
self.make_test_traces()
|
||||||
|
self.make_test_picks()
|
||||||
|
self.t_before = 2.
|
||||||
|
self.t_after = 2.
|
||||||
|
self.cc_maxlag = 0.5
|
||||||
|
|
||||||
|
def make_test_traces(self):
|
||||||
|
# take first trace of test Stream from obspy
|
||||||
|
tr1 = read()[0]
|
||||||
|
# filter trace
|
||||||
|
tr1.filter('bandpass', freqmin=1, freqmax=20)
|
||||||
|
# make a copy and shift the copy by 0.1 s
|
||||||
|
tr2 = tr1.copy()
|
||||||
|
tr2.stats.starttime += 0.1
|
||||||
|
|
||||||
|
self.trace1 = tr1
|
||||||
|
self.trace2 = tr2
|
||||||
|
|
||||||
|
def make_test_picks(self):
|
||||||
|
# create an artificial reference pick on reference trace (trace1) and another one on the 0.1 s shifted trace
|
||||||
|
self.tpick1 = UTCDateTime('2009-08-24T00:20:07.7')
|
||||||
|
# shift the second pick by 0.2 s, the correction should be around 0.1 s now
|
||||||
|
self.tpick2 = self.tpick1 + 0.2
|
||||||
|
|
||||||
|
def test_slice_trace_okay(self):
|
||||||
|
|
||||||
|
self.setup()
|
||||||
|
xcpc = XCorrPickCorrection(UTCDateTime(), Trace(), UTCDateTime(), Trace(),
|
||||||
|
t_before=self.t_before, t_after=self.t_after, cc_maxlag=self.cc_maxlag)
|
||||||
|
|
||||||
|
test_trace = self.trace1
|
||||||
|
pick_time = self.tpick2
|
||||||
|
|
||||||
|
sliced_trace = xcpc.slice_trace(test_trace, pick_time)
|
||||||
|
assert ((sliced_trace.stats.starttime == pick_time - self.t_before - self.cc_maxlag / 2)
|
||||||
|
and (sliced_trace.stats.endtime == pick_time + self.t_after + self.cc_maxlag / 2))
|
||||||
|
|
||||||
|
def test_slice_trace_fails(self):
|
||||||
|
self.setup()
|
||||||
|
|
||||||
|
test_trace = self.trace1
|
||||||
|
pick_time = self.tpick1
|
||||||
|
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
xcpc = XCorrPickCorrection(UTCDateTime(), Trace(), UTCDateTime(), Trace(),
|
||||||
|
t_before=self.t_before + 20, t_after=self.t_after, cc_maxlag=self.cc_maxlag)
|
||||||
|
xcpc.slice_trace(test_trace, pick_time)
|
||||||
|
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
xcpc = XCorrPickCorrection(UTCDateTime(), Trace(), UTCDateTime(), Trace(),
|
||||||
|
t_before=self.t_before, t_after=self.t_after + 50, cc_maxlag=self.cc_maxlag)
|
||||||
|
xcpc.slice_trace(test_trace, pick_time)
|
||||||
|
|
||||||
|
def test_cross_correlation(self):
|
||||||
|
self.setup()
|
||||||
|
|
||||||
|
# create XCorrPickCorrection object
|
||||||
|
xcpc = XCorrPickCorrection(self.tpick1, self.trace1, self.tpick2, self.trace2, t_before=self.t_before,
|
||||||
|
t_after=self.t_after, cc_maxlag=self.cc_maxlag)
|
||||||
|
|
||||||
|
# execute correlation
|
||||||
|
correction, cc_max, uncert, fwfm = xcpc.cross_correlation(False, '', '')
|
||||||
|
|
||||||
|
# define awaited test result
|
||||||
|
test_result = (-0.09983091718314982, 0.9578431835689154, 0.0015285160561610929, 0.03625786256084631)
|
||||||
|
|
||||||
|
# check results
|
||||||
|
assert pytest.approx(test_result, rel=1e-6) == (correction, cc_max, uncert, fwfm)
|
||||||
Reference in New Issue
Block a user