refactor: restructure data objects
This commit is contained in:
parent
0709fb04a5
commit
65b456b8ab
34
PyLoT.py
34
PyLoT.py
@ -1773,7 +1773,7 @@ class MainWindow(QMainWindow):
|
|||||||
|
|
||||||
def getStime(self):
|
def getStime(self):
|
||||||
if self.get_data():
|
if self.get_data():
|
||||||
return full_range(self.get_data().getWFData())[0]
|
return full_range(self.get_data().get_wf_data())[0]
|
||||||
|
|
||||||
def addActions(self, target, actions):
|
def addActions(self, target, actions):
|
||||||
for action in actions:
|
for action in actions:
|
||||||
@ -1983,7 +1983,7 @@ class MainWindow(QMainWindow):
|
|||||||
tstart = None
|
tstart = None
|
||||||
tstop = None
|
tstop = None
|
||||||
|
|
||||||
self.data.setWFData(self.fnames,
|
self.data.set_wf_data(self.fnames,
|
||||||
self.fnames_comp,
|
self.fnames_comp,
|
||||||
checkRotated=True,
|
checkRotated=True,
|
||||||
metadata=self.metadata,
|
metadata=self.metadata,
|
||||||
@ -2035,7 +2035,7 @@ class MainWindow(QMainWindow):
|
|||||||
def get_npts_to_plot(self):
|
def get_npts_to_plot(self):
|
||||||
if not hasattr(self.data, 'wfdata'):
|
if not hasattr(self.data, 'wfdata'):
|
||||||
return 0
|
return 0
|
||||||
return sum(trace.stats.npts for trace in self.data.getWFData())
|
return sum(trace.stats.npts for trace in self.data.get_wf_data())
|
||||||
|
|
||||||
def connectWFplotEvents(self):
|
def connectWFplotEvents(self):
|
||||||
'''
|
'''
|
||||||
@ -2248,14 +2248,14 @@ class MainWindow(QMainWindow):
|
|||||||
zne_text = {'Z': 'vertical', 'N': 'north-south', 'E': 'east-west'}
|
zne_text = {'Z': 'vertical', 'N': 'north-south', 'E': 'east-west'}
|
||||||
comp = self.getComponent()
|
comp = self.getComponent()
|
||||||
title = 'section: {0} components'.format(zne_text[comp])
|
title = 'section: {0} components'.format(zne_text[comp])
|
||||||
wfst = self.get_data().getWFData()
|
wfst = self.get_data().get_wf_data()
|
||||||
wfsyn = self.get_data().getAltWFdata()
|
wfsyn = self.get_data().getAltWFdata()
|
||||||
if self.filterActionP.isChecked() and filter:
|
if self.filterActionP.isChecked() and filter:
|
||||||
self.filterWaveformData(plot=False, phase='P')
|
self.filterWaveformData(plot=False, phase='P')
|
||||||
elif self.filterActionS.isChecked() and filter:
|
elif self.filterActionS.isChecked() and filter:
|
||||||
self.filterWaveformData(plot=False, phase='S')
|
self.filterWaveformData(plot=False, phase='S')
|
||||||
# wfst = self.get_data().getWFData().select(component=comp)
|
# wfst = self.get_data().get_wf_data().select(component=comp)
|
||||||
# wfst += self.get_data().getWFData().select(component=alter_comp)
|
# wfst += self.get_data().get_wf_data().select(component=alter_comp)
|
||||||
plotWidget = self.getPlotWidget()
|
plotWidget = self.getPlotWidget()
|
||||||
self.adjustPlotHeight()
|
self.adjustPlotHeight()
|
||||||
if get_bool(settings.value('large_dataset')) == True:
|
if get_bool(settings.value('large_dataset')) == True:
|
||||||
@ -2270,7 +2270,7 @@ class MainWindow(QMainWindow):
|
|||||||
def adjustPlotHeight(self):
|
def adjustPlotHeight(self):
|
||||||
if self.pg:
|
if self.pg:
|
||||||
return
|
return
|
||||||
height_need = len(self.data.getWFData()) * self.height_factor
|
height_need = len(self.data.get_wf_data()) * self.height_factor
|
||||||
plotWidget = self.getPlotWidget()
|
plotWidget = self.getPlotWidget()
|
||||||
if self.tabs.widget(0).frameSize().height() < height_need:
|
if self.tabs.widget(0).frameSize().height() < height_need:
|
||||||
plotWidget.setMinimumHeight(height_need)
|
plotWidget.setMinimumHeight(height_need)
|
||||||
@ -2290,24 +2290,24 @@ class MainWindow(QMainWindow):
|
|||||||
self.plotWaveformDataThread()
|
self.plotWaveformDataThread()
|
||||||
|
|
||||||
def pushFilterWF(self, param_args):
|
def pushFilterWF(self, param_args):
|
||||||
self.get_data().filterWFData(param_args)
|
self.get_data().filter_wf_data(param_args)
|
||||||
|
|
||||||
def filterP(self):
|
def filterP(self):
|
||||||
self.filterActionS.setChecked(False)
|
self.filterActionS.setChecked(False)
|
||||||
if self.filterActionP.isChecked():
|
if self.filterActionP.isChecked():
|
||||||
self.filterWaveformData(phase='P')
|
self.filterWaveformData(phase='P')
|
||||||
else:
|
else:
|
||||||
self.resetWFData()
|
self.reset_wf_data()
|
||||||
|
|
||||||
def filterS(self):
|
def filterS(self):
|
||||||
self.filterActionP.setChecked(False)
|
self.filterActionP.setChecked(False)
|
||||||
if self.filterActionS.isChecked():
|
if self.filterActionS.isChecked():
|
||||||
self.filterWaveformData(phase='S')
|
self.filterWaveformData(phase='S')
|
||||||
else:
|
else:
|
||||||
self.resetWFData()
|
self.reset_wf_data()
|
||||||
|
|
||||||
def resetWFData(self):
|
def reset_wf_data(self):
|
||||||
self.get_data().resetWFData()
|
self.get_data().reset_wf_data()
|
||||||
self.plotWaveformDataThread()
|
self.plotWaveformDataThread()
|
||||||
|
|
||||||
def filterWaveformData(self, plot=True, phase=None):
|
def filterWaveformData(self, plot=True, phase=None):
|
||||||
@ -2326,11 +2326,11 @@ class MainWindow(QMainWindow):
|
|||||||
kwargs = self.getFilterOptions()[phase].parseFilterOptions()
|
kwargs = self.getFilterOptions()[phase].parseFilterOptions()
|
||||||
self.pushFilterWF(kwargs)
|
self.pushFilterWF(kwargs)
|
||||||
else:
|
else:
|
||||||
self.get_data().resetWFData()
|
self.get_data().reset_wf_data()
|
||||||
elif self.filterActionP.isChecked() or self.filterActionS.isChecked():
|
elif self.filterActionP.isChecked() or self.filterActionS.isChecked():
|
||||||
self.adjustFilterOptions()
|
self.adjustFilterOptions()
|
||||||
else:
|
else:
|
||||||
self.get_data().resetWFData()
|
self.get_data().reset_wf_data()
|
||||||
if plot:
|
if plot:
|
||||||
self.plotWaveformDataThread(filter=False)
|
self.plotWaveformDataThread(filter=False)
|
||||||
|
|
||||||
@ -2531,10 +2531,10 @@ class MainWindow(QMainWindow):
|
|||||||
show_comp_data=self.dataPlot.comp_checkbox.isChecked())
|
show_comp_data=self.dataPlot.comp_checkbox.isChecked())
|
||||||
if self.filterActionP.isChecked():
|
if self.filterActionP.isChecked():
|
||||||
pickDlg.currentPhase = "P"
|
pickDlg.currentPhase = "P"
|
||||||
pickDlg.filterWFData()
|
pickDlg.filter_wf_data()
|
||||||
elif self.filterActionS.isChecked():
|
elif self.filterActionS.isChecked():
|
||||||
pickDlg.currentPhase = "S"
|
pickDlg.currentPhase = "S"
|
||||||
pickDlg.filterWFData()
|
pickDlg.filter_wf_data()
|
||||||
pickDlg.nextStation.setChecked(self.nextStation)
|
pickDlg.nextStation.setChecked(self.nextStation)
|
||||||
pickDlg.nextStation.stateChanged.connect(self.toggle_next_station)
|
pickDlg.nextStation.stateChanged.connect(self.toggle_next_station)
|
||||||
if pickDlg.exec_():
|
if pickDlg.exec_():
|
||||||
@ -3478,7 +3478,7 @@ class MainWindow(QMainWindow):
|
|||||||
if not self.metadata:
|
if not self.metadata:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
wf_copy = self.get_data().getWFData().copy()
|
wf_copy = self.get_data().get_wf_data().copy()
|
||||||
|
|
||||||
wf_select = Stream()
|
wf_select = Stream()
|
||||||
# restitute only picked traces
|
# restitute only picked traces
|
||||||
|
@ -243,7 +243,7 @@ def autoPyLoT(input_dict=None, parameter=None, inputfile=None, fnames=None, even
|
|||||||
pylot_event = Event(eventpath) # event should be path to event directory
|
pylot_event = Event(eventpath) # event should be path to event directory
|
||||||
data.setEvtData(pylot_event)
|
data.setEvtData(pylot_event)
|
||||||
if fnames == 'None':
|
if fnames == 'None':
|
||||||
data.setWFData(glob.glob(os.path.join(datapath, event_datapath, '*')))
|
data.set_wf_data(glob.glob(os.path.join(datapath, event_datapath, '*')))
|
||||||
# the following is necessary because within
|
# the following is necessary because within
|
||||||
# multiple event processing no event ID is provided
|
# multiple event processing no event ID is provided
|
||||||
# in autopylot.in
|
# in autopylot.in
|
||||||
@ -258,7 +258,7 @@ def autoPyLoT(input_dict=None, parameter=None, inputfile=None, fnames=None, even
|
|||||||
now.minute)
|
now.minute)
|
||||||
parameter.setParam(eventID=eventID)
|
parameter.setParam(eventID=eventID)
|
||||||
else:
|
else:
|
||||||
data.setWFData(fnames)
|
data.set_wf_data(fnames)
|
||||||
|
|
||||||
eventpath = events[0]
|
eventpath = events[0]
|
||||||
# now = datetime.datetime.now()
|
# now = datetime.datetime.now()
|
||||||
@ -268,7 +268,7 @@ def autoPyLoT(input_dict=None, parameter=None, inputfile=None, fnames=None, even
|
|||||||
# now.hour,
|
# now.hour,
|
||||||
# now.minute)
|
# now.minute)
|
||||||
parameter.setParam(eventID=eventid)
|
parameter.setParam(eventID=eventid)
|
||||||
wfdat = data.getWFData() # all available streams
|
wfdat = data.get_wf_data() # all available streams
|
||||||
if not station == 'all':
|
if not station == 'all':
|
||||||
wfdat = wfdat.select(station=station)
|
wfdat = wfdat.select(station=station)
|
||||||
if not wfdat:
|
if not wfdat:
|
||||||
|
@ -9,11 +9,13 @@ from obspy import UTCDateTime
|
|||||||
|
|
||||||
from pylot.core.io.event import EventData
|
from pylot.core.io.event import EventData
|
||||||
from pylot.core.io.waveformdata import WaveformData
|
from pylot.core.io.waveformdata import WaveformData
|
||||||
|
from pylot.core.util.dataprocessing import Metadata
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class Data:
|
class Data:
|
||||||
event_data: EventData = field(default_factory=EventData)
|
event_data: EventData = field(default_factory=EventData)
|
||||||
waveform_data: WaveformData = field(default_factory=WaveformData)
|
waveform_data: WaveformData = field(default_factory=WaveformData)
|
||||||
|
metadata: Metadata = field(default_factory=Metadata)
|
||||||
_parent: Union[None, 'QtWidgets.QWidget'] = None
|
_parent: Union[None, 'QtWidgets.QWidget'] = None
|
||||||
|
|
||||||
def __init__(self, parent=None, evtdata=None):
|
def __init__(self, parent=None, evtdata=None):
|
||||||
@ -52,10 +54,17 @@ class Data:
|
|||||||
self.waveform_data.dirty = True
|
self.waveform_data.dirty = True
|
||||||
|
|
||||||
def set_wf_data(self, fnames: List[str], fnames_alt: List[str] = None, check_rotated=False, metadata=None, tstart=0, tstop=0):
|
def set_wf_data(self, fnames: List[str], fnames_alt: List[str] = None, check_rotated=False, metadata=None, tstart=0, tstop=0):
|
||||||
return self.waveform_data.set_wf_data(fnames, fnames_alt, check_rotated, metadata, tstart, tstop)
|
return self.waveform_data.load_waveforms(fnames, fnames_alt, check_rotated, metadata, tstart, tstop)
|
||||||
|
|
||||||
def reset_wf_data(self):
|
def reset_wf_data(self):
|
||||||
self.waveform_data.reset_wf_data()
|
self.waveform_data.reset()
|
||||||
|
|
||||||
|
def get_wf_data(self):
|
||||||
|
return self.waveform_data.wfdata
|
||||||
|
|
||||||
|
def rotate_wf_data(self):
|
||||||
|
self.waveform_data.rotate_zne(self.metadata)
|
||||||
|
|
||||||
|
|
||||||
class GenericDataStructure(object):
|
class GenericDataStructure(object):
|
||||||
"""
|
"""
|
||||||
|
13
pylot/core/io/utils.py
Normal file
13
pylot/core/io/utils.py
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import os
|
||||||
|
from typing import List
|
||||||
|
|
||||||
|
|
||||||
|
def validate_filenames(filenames: List[str]) -> List[str]:
|
||||||
|
"""
|
||||||
|
validate a list of filenames for file abundance
|
||||||
|
:param filenames: list of possible filenames
|
||||||
|
:type filenames: List[str]
|
||||||
|
:return: list of valid filenames
|
||||||
|
:rtype: List[str]
|
||||||
|
"""
|
||||||
|
return [fn for fn in filenames if os.path.isfile(fn)]
|
@ -1,14 +1,13 @@
|
|||||||
import logging
|
import logging
|
||||||
import os
|
|
||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
from typing import Union, List
|
from typing import Union, List
|
||||||
|
|
||||||
import numpy as np
|
|
||||||
from obspy import Stream, read
|
from obspy import Stream, read
|
||||||
from obspy.io.sac import SacIOError
|
from obspy.io.sac import SacIOError
|
||||||
from obspy.signal.rotate import rotate2zne
|
|
||||||
|
|
||||||
from pylot.core.util.utils import full_range, get_stations
|
from pylot.core.io.utils import validate_filenames
|
||||||
|
from pylot.core.util.dataprocessing import Metadata
|
||||||
|
from pylot.core.util.utils import get_stations, check_for_nan, check4rotated
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
@ -18,26 +17,39 @@ class WaveformData:
|
|||||||
wf_alt: Stream = field(default_factory=Stream)
|
wf_alt: Stream = field(default_factory=Stream)
|
||||||
dirty: bool = False
|
dirty: bool = False
|
||||||
|
|
||||||
def set_wf_data(self, fnames: List[str], fnames_alt: List[str] = None, check_rotated=False, metadata=None, tstart=0, tstop=0):
|
def load_waveforms(self, fnames: List[str], fnames_alt: List[str] = None, check_rotated=False, metadata=None, tstart=0, tstop=0):
|
||||||
self.clear_data()
|
fn_list = validate_filenames(fnames)
|
||||||
fnames = self.check_fname_exists(fnames)
|
if not fn_list:
|
||||||
fnames_alt = self.check_fname_exists(fnames_alt)
|
logging.warning('No valid filenames given for loading waveforms')
|
||||||
|
else:
|
||||||
|
self.clear()
|
||||||
|
self.add_waveforms(fn_list)
|
||||||
|
|
||||||
if fnames:
|
if fnames_alt is None:
|
||||||
self.append_wf_data(fnames)
|
pass
|
||||||
if fnames_alt:
|
else:
|
||||||
self.append_wf_data(fnames_alt, alternative=True)
|
alt_fn_list = validate_filenames(fnames_alt)
|
||||||
self.wfdata, _ = self.check_for_gaps_and_merge(self.wfdata)
|
if not alt_fn_list:
|
||||||
self.check_for_nan(self.wfdata)
|
logging.warning('No valid alternative filenames given for loading waveforms')
|
||||||
if check_rotated and metadata:
|
else:
|
||||||
self.wfdata = self.check4rotated(self.wfdata, metadata, verbosity=0)
|
self.add_waveforms(alt_fn_list, alternative=True)
|
||||||
self.trim_station_components(self.wfdata, trim_start=True, trim_end=False)
|
|
||||||
self.wforiginal = self.wfdata.copy()
|
|
||||||
self.dirty = False
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
def append_wf_data(self, fnames: List[str], alternative: bool = False):
|
if not fn_list and not alt_fn_list:
|
||||||
|
logging.error('No filenames or alternative filenames given for loading waveforms')
|
||||||
|
return False
|
||||||
|
|
||||||
|
self.merge()
|
||||||
|
self.replace_nan()
|
||||||
|
if not check_rotated or not metadata:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
self.rotate_zne()
|
||||||
|
self.trim_station_traces()
|
||||||
|
self.wforiginal = self.wfdata.copy()
|
||||||
|
self.dirty = False
|
||||||
|
return True
|
||||||
|
|
||||||
|
def add_waveforms(self, fnames: List[str], alternative: bool = False):
|
||||||
data_stream = self.wf_alt if alternative else self.wfdata
|
data_stream = self.wf_alt if alternative else self.wfdata
|
||||||
warnmsg = ''
|
warnmsg = ''
|
||||||
for fname in set(fnames):
|
for fname in set(fnames):
|
||||||
@ -55,189 +67,57 @@ class WaveformData:
|
|||||||
warnmsg += f'{fname}\n{se}\n'
|
warnmsg += f'{fname}\n{se}\n'
|
||||||
|
|
||||||
if warnmsg:
|
if warnmsg:
|
||||||
print(f'WARNING in appendWFData: unable to read waveform data\n{warnmsg}')
|
print(f'WARNING in add_waveforms: unable to read waveform data\n{warnmsg}')
|
||||||
|
|
||||||
def clear_data(self):
|
def clear(self):
|
||||||
self.wfdata = Stream()
|
self.wfdata = Stream()
|
||||||
self.wforiginal = None
|
self.wforiginal = None
|
||||||
self.wf_alt = Stream()
|
self.wf_alt = Stream()
|
||||||
|
|
||||||
def reset_wf_data(self):
|
def reset(self):
|
||||||
|
"""
|
||||||
|
Resets the waveform data to its original state.
|
||||||
|
"""
|
||||||
if self.wforiginal:
|
if self.wforiginal:
|
||||||
self.wfdata = self.wforiginal.copy()
|
self.wfdata = self.wforiginal.copy()
|
||||||
else:
|
else:
|
||||||
self.wfdata = Stream()
|
self.wfdata = Stream()
|
||||||
self.dirty = False
|
self.dirty = False
|
||||||
|
|
||||||
def check_fname_exists(self, filenames: List[str]) -> List[str]:
|
def merge(self):
|
||||||
return [fn for fn in filenames if os.path.isfile(fn)]
|
|
||||||
|
|
||||||
def check_for_gaps_and_merge(self, stream):
|
|
||||||
"""
|
"""
|
||||||
check for gaps in Stream and merge if gaps are found
|
check for gaps in Stream and merge if gaps are found
|
||||||
:param stream: stream of seismic data
|
|
||||||
:type stream: `~obspy.core.stream.Stream`
|
|
||||||
:return: data stream, gaps returned from obspy get_gaps
|
|
||||||
:rtype: `~obspy.core.stream.Stream`
|
|
||||||
"""
|
"""
|
||||||
gaps = stream.get_gaps()
|
gaps = self.wfdata.get_gaps()
|
||||||
if gaps:
|
if gaps:
|
||||||
merged = ['{}.{}.{}.{}'.format(*gap[:4]) for gap in gaps]
|
merged = ['{}.{}.{}.{}'.format(*gap[:4]) for gap in gaps]
|
||||||
stream.merge(method=1)
|
self.wfdata.merge(method=1)
|
||||||
print('Merged the following stations because of gaps:')
|
logging.info('Merged the following stations because of gaps:')
|
||||||
for merged_station in merged:
|
for station in merged:
|
||||||
print(merged_station)
|
logging.info(station)
|
||||||
|
|
||||||
return stream, gaps
|
def replace_nan(self):
|
||||||
|
|
||||||
def check_for_nan(self, stream):
|
|
||||||
"""
|
"""
|
||||||
Replace all NaNs in data with nan_value (in place)
|
Replace all NaNs in data with 0. (in place)
|
||||||
:param stream: stream of seismic data
|
|
||||||
:type stream: `~obspy.core.stream.Stream`
|
|
||||||
:param nan_value: value which all NaNs are set to
|
|
||||||
:type nan_value: float, int
|
|
||||||
:return: None
|
|
||||||
"""
|
"""
|
||||||
if not stream:
|
self.wfdata = check_for_nan(self.wfdata)
|
||||||
return
|
|
||||||
for trace in stream:
|
|
||||||
np.nan_to_num(trace.data, copy=False, nan=0.)
|
|
||||||
|
|
||||||
|
def rotate_zne(self, metadata: Metadata = None):
|
||||||
def check4rotated(self, stream, metadata=None, verbosity=1):
|
|
||||||
"""
|
"""
|
||||||
Check all traces in data. If a trace is not in ZNE rotation (last symbol of channel code is numeric) and the trace
|
Check all traces in stream for rotation. If a trace is not in ZNE rotation (last symbol of channel code is numeric) and the trace
|
||||||
is in the metadata with azimuth and dip, rotate it to classical ZNE orientation.
|
is in the metadata with azimuth and dip, rotate it to classical ZNE orientation.
|
||||||
Rotating the traces requires them to be of the same length, so, all traces will be trimmed to a common length as a
|
Rotating the traces requires them to be of the same length, so, all traces will be trimmed to a common length as a
|
||||||
side effect.
|
side effect.
|
||||||
:param stream: stream object containing seismic traces
|
|
||||||
:type stream: `~obspy.core.stream.Stream`
|
|
||||||
:param metadata: tuple containing metadata type string and metadata parser object
|
|
||||||
:type metadata: (str, `~obspy.io.xseed.parser.Parser`)
|
|
||||||
:param verbosity: if 0 print no information at runtime
|
|
||||||
:type verbosity: int
|
|
||||||
:return: stream object with traditionally oriented traces (ZNE) for stations that had misaligned traces (123) before
|
|
||||||
:rtype: `~obspy.core.stream.Stream`
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def rotation_required(trace_ids):
|
self.wfdata = check4rotated(self.wfdata, metadata)
|
||||||
"""
|
|
||||||
Derive if any rotation is required from the orientation code of the input.
|
|
||||||
|
|
||||||
:param trace_ids: string identifier of waveform data trace
|
def trim_station_traces(self):
|
||||||
: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).
|
|
||||||
|
|
||||||
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
|
|
||||||
:param wfs_in: stream containing seismic traces of a station
|
|
||||||
:type wfs_in: `~obspy.core.stream.Stream`
|
|
||||||
:param metadata: tuple containing metadata type string and metadata parser object
|
|
||||||
:type metadata: (str, `~obspy.io.xseed.parser.Parser`)
|
|
||||||
:return: stream object with traditionally oriented traces (ZNE)
|
|
||||||
: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
|
|
||||||
trace_ids = [trace.id for trace in wfs_in]
|
|
||||||
if not rotation_required(trace_ids):
|
|
||||||
logging.debug(f"Stream does not need any rotation: Traces are {trace_ids=}")
|
|
||||||
return wfs_in
|
|
||||||
|
|
||||||
# check metadata quality
|
|
||||||
t_start = full_range(wfs_in)
|
|
||||||
try:
|
|
||||||
azimuths = []
|
|
||||||
dips = []
|
|
||||||
for tr_id in trace_ids:
|
|
||||||
azimuths.append(metadata.get_coordinates(tr_id, t_start)['azimuth'])
|
|
||||||
dips.append(metadata.get_coordinates(tr_id, t_start)['dip'])
|
|
||||||
except (KeyError, TypeError) as err:
|
|
||||||
logging.error(
|
|
||||||
f"{type(err)=} occurred: {err=} Rotating not possible, not all azimuth and dip information "
|
|
||||||
f"available in metadata. Stream remains unchanged.")
|
|
||||||
return wfs_in
|
|
||||||
except Exception as err:
|
|
||||||
print(f"Unexpected {err=}, {type(err)=}")
|
|
||||||
raise
|
|
||||||
|
|
||||||
# to rotate all traces must have same length, so trim them
|
|
||||||
wfs_out = self.trim_station_components(wfs_in, trim_start=True, trim_end=True)
|
|
||||||
try:
|
|
||||||
z, n, e = rotate2zne(wfs_out[0], azimuths[0], dips[0],
|
|
||||||
wfs_out[1], azimuths[1], dips[1],
|
|
||||||
wfs_out[2], azimuths[2], dips[2])
|
|
||||||
print('check4rotated: rotated trace {} to ZNE'.format(trace_ids))
|
|
||||||
# replace old data with rotated data, change the channel code to ZNE
|
|
||||||
z_index = dips.index(min(
|
|
||||||
dips)) # get z-trace index, z has minimum dip of -90 (dip is measured from 0 to -90, with -90
|
|
||||||
# being vertical)
|
|
||||||
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]
|
|
||||||
for trace_id in trace_ids:
|
|
||||||
coordinates = metadata.get_coordinates(trace_id, t_start)
|
|
||||||
dip, az = coordinates['dip'], coordinates['azimuth']
|
|
||||||
trace = wfs_out.select(id=trace_id)[0]
|
|
||||||
if az > 315 or az <= 45 or 135 < az <= 225:
|
|
||||||
trace.data = n
|
|
||||||
trace.stats.channel = trace.stats.channel[0:-1] + 'N'
|
|
||||||
elif 45 < az <= 135 or 225 < az <= 315:
|
|
||||||
trace.data = e
|
|
||||||
trace.stats.channel = trace.stats.channel[0:-1] + 'E'
|
|
||||||
except ValueError as err:
|
|
||||||
print(f"{err=} Rotation failed. Stream remains unchanged.")
|
|
||||||
return wfs_in
|
|
||||||
|
|
||||||
return wfs_out
|
|
||||||
|
|
||||||
if metadata is None:
|
|
||||||
if verbosity:
|
|
||||||
msg = 'Warning: could not rotate traces since no metadata was given\nset Inventory file!'
|
|
||||||
print(msg)
|
|
||||||
return stream
|
|
||||||
stations = get_stations(stream)
|
|
||||||
for station in stations: # loop through all stations and rotate data if neccessary
|
|
||||||
wf_station = stream.select(station=station)
|
|
||||||
rotate_components(wf_station, metadata)
|
|
||||||
return stream
|
|
||||||
|
|
||||||
def trim_station_components(stream, 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
|
trim data stream to common time window
|
||||||
:param stream: stream of seismic data
|
|
||||||
:type stream: `~obspy.core.stream.Stream`
|
|
||||||
:param trim_start: trim start of stream
|
|
||||||
:type trim_start: bool
|
|
||||||
:param trim_end: trim end of stream
|
|
||||||
:type trim_end: bool
|
|
||||||
:return: data stream
|
|
||||||
:rtype: `~obspy.core.stream.Stream`
|
|
||||||
"""
|
"""
|
||||||
starttime = {False: None}
|
|
||||||
endtime = {False: None}
|
|
||||||
|
|
||||||
stations = get_stations(stream)
|
for station in get_stations(self.wfdata):
|
||||||
|
station_traces = self.wfdata.select(station=station)
|
||||||
print('trim_station_components: Will trim stream for trim_start: {} and for '
|
station_traces.trim(starttime=max([trace.stats.starttime for trace in station_traces]),
|
||||||
'trim_end: {}.'.format(trim_start, trim_end))
|
endtime=min([trace.stats.endtime for trace in station_traces]))
|
||||||
for station in stations:
|
|
||||||
wf_station = stream.select(station=station)
|
|
||||||
starttime[True] = max([trace.stats.starttime for trace in wf_station])
|
|
||||||
endtime[True] = min([trace.stats.endtime for trace in wf_station])
|
|
||||||
wf_station.trim(starttime=starttime[trim_start], endtime=endtime[trim_end])
|
|
||||||
|
|
||||||
return stream
|
|
||||||
|
@ -474,8 +474,8 @@ class Array_map(QtWidgets.QWidget):
|
|||||||
transform=ccrs.PlateCarree(), label='deleted'))
|
transform=ccrs.PlateCarree(), label='deleted'))
|
||||||
|
|
||||||
def openPickDlg(self, ind):
|
def openPickDlg(self, ind):
|
||||||
wfdata = self._parent.get_data().getWFData()
|
wfdata = self._parent.get_data().get_wf_data()
|
||||||
wfdata_comp = self._parent.get_data().getWFDataComp()
|
wfdata_comp = self._parent.get_data().get_wf_dataComp()
|
||||||
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
|
||||||
|
@ -1912,7 +1912,7 @@ class PickDlg(QDialog):
|
|||||||
# set attribute holding data
|
# set attribute holding data
|
||||||
if data is None or not data:
|
if data is None or not data:
|
||||||
try:
|
try:
|
||||||
data = parent.get_data().getWFData().copy()
|
data = parent.get_data().get_wf_data().copy()
|
||||||
self.data = data.select(station=station)
|
self.data = data.select(station=station)
|
||||||
except AttributeError as e:
|
except AttributeError as e:
|
||||||
errmsg = 'You either have to put in a data or an appropriate ' \
|
errmsg = 'You either have to put in a data or an appropriate ' \
|
||||||
@ -1946,7 +1946,7 @@ class PickDlg(QDialog):
|
|||||||
self.cur_xlim = None
|
self.cur_xlim = None
|
||||||
self.cur_ylim = None
|
self.cur_ylim = None
|
||||||
|
|
||||||
self.stime, self.etime = full_range(self.getWFData())
|
self.stime, self.etime = full_range(self.get_wf_data())
|
||||||
|
|
||||||
# initialize plotting widget
|
# initialize plotting widget
|
||||||
self.multicompfig = PylotCanvas(parent=self, multicursor=True)
|
self.multicompfig = PylotCanvas(parent=self, multicursor=True)
|
||||||
@ -1960,7 +1960,7 @@ class PickDlg(QDialog):
|
|||||||
self.referenceChannel.addItem('-', None)
|
self.referenceChannel.addItem('-', None)
|
||||||
self.scaleChannel.addItem('individual', None)
|
self.scaleChannel.addItem('individual', None)
|
||||||
|
|
||||||
for trace in self.getWFData():
|
for trace in self.get_wf_data():
|
||||||
channel = trace.stats.channel
|
channel = trace.stats.channel
|
||||||
self.referenceChannel.addItem(channel, trace)
|
self.referenceChannel.addItem(channel, trace)
|
||||||
if not channel[-1] in ['Z', 'N', 'E', '1', '2', '3']:
|
if not channel[-1] in ['Z', 'N', 'E', '1', '2', '3']:
|
||||||
@ -1979,7 +1979,7 @@ class PickDlg(QDialog):
|
|||||||
if self.wftype is not None:
|
if self.wftype is not None:
|
||||||
title += ' | ({})'.format(self.wftype)
|
title += ' | ({})'.format(self.wftype)
|
||||||
|
|
||||||
self.multicompfig.plotWFData(wfdata=self.getWFData(), wfdata_compare=self.getWFDataComp(),
|
self.multicompfig.plotWFData(wfdata=self.get_wf_data(), wfdata_compare=self.get_wf_dataComp(),
|
||||||
title=title)
|
title=title)
|
||||||
|
|
||||||
self.multicompfig.setZoomBorders2content()
|
self.multicompfig.setZoomBorders2content()
|
||||||
@ -2514,7 +2514,7 @@ class PickDlg(QDialog):
|
|||||||
if self.autoFilterAction.isChecked():
|
if self.autoFilterAction.isChecked():
|
||||||
for filteraction in [self.filterActionP, self.filterActionS]:
|
for filteraction in [self.filterActionP, self.filterActionS]:
|
||||||
filteraction.setChecked(False)
|
filteraction.setChecked(False)
|
||||||
self.filterWFData()
|
self.filter_wf_data()
|
||||||
self.draw()
|
self.draw()
|
||||||
else:
|
else:
|
||||||
self.draw()
|
self.draw()
|
||||||
@ -2598,10 +2598,10 @@ class PickDlg(QDialog):
|
|||||||
def getGlobalLimits(self, ax, axis):
|
def getGlobalLimits(self, ax, axis):
|
||||||
return self.multicompfig.getGlobalLimits(ax, axis)
|
return self.multicompfig.getGlobalLimits(ax, axis)
|
||||||
|
|
||||||
def getWFData(self):
|
def get_wf_data(self):
|
||||||
return self.data
|
return self.data
|
||||||
|
|
||||||
def getWFDataComp(self):
|
def get_wf_dataComp(self):
|
||||||
if self.showCompData:
|
if self.showCompData:
|
||||||
return self.data_compare
|
return self.data_compare
|
||||||
else:
|
else:
|
||||||
@ -2616,17 +2616,17 @@ class PickDlg(QDialog):
|
|||||||
return tr
|
return tr
|
||||||
|
|
||||||
if component == 'E' or component == 'N':
|
if component == 'E' or component == 'N':
|
||||||
for trace in self.getWFData():
|
for trace in self.get_wf_data():
|
||||||
trace = selectTrace(trace, 'NE')
|
trace = selectTrace(trace, 'NE')
|
||||||
if trace:
|
if trace:
|
||||||
wfdata.append(trace)
|
wfdata.append(trace)
|
||||||
elif component == '1' or component == '2':
|
elif component == '1' or component == '2':
|
||||||
for trace in self.getWFData():
|
for trace in self.get_wf_data():
|
||||||
trace = selectTrace(trace, '12')
|
trace = selectTrace(trace, '12')
|
||||||
if trace:
|
if trace:
|
||||||
wfdata.append(trace)
|
wfdata.append(trace)
|
||||||
else:
|
else:
|
||||||
wfdata = self.getWFData().select(component=component)
|
wfdata = self.get_wf_data().select(component=component)
|
||||||
return wfdata
|
return wfdata
|
||||||
|
|
||||||
def getPicks(self, picktype='manual'):
|
def getPicks(self, picktype='manual'):
|
||||||
@ -2729,8 +2729,8 @@ class PickDlg(QDialog):
|
|||||||
stime = self.getStartTime()
|
stime = self.getStartTime()
|
||||||
|
|
||||||
# copy wfdata for plotting
|
# copy wfdata for plotting
|
||||||
wfdata = self.getWFData().copy()
|
wfdata = self.get_wf_data().copy()
|
||||||
wfdata_comp = self.getWFDataComp().copy()
|
wfdata_comp = self.get_wf_dataComp().copy()
|
||||||
wfdata = self.getPickPhases(wfdata, phase)
|
wfdata = self.getPickPhases(wfdata, phase)
|
||||||
wfdata_comp = self.getPickPhases(wfdata_comp, phase)
|
wfdata_comp = self.getPickPhases(wfdata_comp, phase)
|
||||||
for wfd in [wfdata, wfdata_comp]:
|
for wfd in [wfdata, wfdata_comp]:
|
||||||
@ -2839,7 +2839,7 @@ class PickDlg(QDialog):
|
|||||||
filteroptions = None
|
filteroptions = None
|
||||||
|
|
||||||
# copy and filter data for earliest and latest possible picks
|
# copy and filter data for earliest and latest possible picks
|
||||||
wfdata = self.getWFData().copy().select(channel=channel)
|
wfdata = self.get_wf_data().copy().select(channel=channel)
|
||||||
if filteroptions:
|
if filteroptions:
|
||||||
try:
|
try:
|
||||||
wfdata.detrend('linear')
|
wfdata.detrend('linear')
|
||||||
@ -2886,7 +2886,7 @@ class PickDlg(QDialog):
|
|||||||
minFMSNR = parameter.get('minFMSNR')
|
minFMSNR = parameter.get('minFMSNR')
|
||||||
quality = get_quality_class(spe, parameter.get('timeerrorsP'))
|
quality = get_quality_class(spe, parameter.get('timeerrorsP'))
|
||||||
if quality <= minFMweight and snr >= minFMSNR:
|
if quality <= minFMweight and snr >= minFMSNR:
|
||||||
FM = fmpicker(self.getWFData().select(channel=channel).copy(), wfdata.copy(), parameter.get('fmpickwin'),
|
FM = fmpicker(self.get_wf_data().select(channel=channel).copy(), wfdata.copy(), parameter.get('fmpickwin'),
|
||||||
pick - stime_diff)
|
pick - stime_diff)
|
||||||
|
|
||||||
# save pick times for actual phase
|
# save pick times for actual phase
|
||||||
@ -3178,7 +3178,7 @@ class PickDlg(QDialog):
|
|||||||
def togglePickBlocker(self):
|
def togglePickBlocker(self):
|
||||||
return not self.pick_block
|
return not self.pick_block
|
||||||
|
|
||||||
def filterWFData(self, phase=None):
|
def filter_wf_data(self, phase=None):
|
||||||
if not phase:
|
if not phase:
|
||||||
phase = self.currentPhase
|
phase = self.currentPhase
|
||||||
if self.getPhaseID(phase) == 'P':
|
if self.getPhaseID(phase) == 'P':
|
||||||
@ -3196,8 +3196,8 @@ class PickDlg(QDialog):
|
|||||||
self.cur_xlim = self.multicompfig.axes[0].get_xlim()
|
self.cur_xlim = self.multicompfig.axes[0].get_xlim()
|
||||||
self.cur_ylim = self.multicompfig.axes[0].get_ylim()
|
self.cur_ylim = self.multicompfig.axes[0].get_ylim()
|
||||||
# self.multicompfig.updateCurrentLimits()
|
# self.multicompfig.updateCurrentLimits()
|
||||||
wfdata = self.getWFData().copy()
|
wfdata = self.get_wf_data().copy()
|
||||||
wfdata_comp = self.getWFDataComp().copy()
|
wfdata_comp = self.get_wf_dataComp().copy()
|
||||||
title = self.getStation()
|
title = self.getStation()
|
||||||
if filter:
|
if filter:
|
||||||
filtoptions = None
|
filtoptions = None
|
||||||
@ -3234,14 +3234,14 @@ class PickDlg(QDialog):
|
|||||||
def filterP(self):
|
def filterP(self):
|
||||||
self.filterActionS.setChecked(False)
|
self.filterActionS.setChecked(False)
|
||||||
if self.filterActionP.isChecked():
|
if self.filterActionP.isChecked():
|
||||||
self.filterWFData('P')
|
self.filter_wf_data('P')
|
||||||
else:
|
else:
|
||||||
self.refreshPlot()
|
self.refreshPlot()
|
||||||
|
|
||||||
def filterS(self):
|
def filterS(self):
|
||||||
self.filterActionP.setChecked(False)
|
self.filterActionP.setChecked(False)
|
||||||
if self.filterActionS.isChecked():
|
if self.filterActionS.isChecked():
|
||||||
self.filterWFData('S')
|
self.filter_wf_data('S')
|
||||||
else:
|
else:
|
||||||
self.refreshPlot()
|
self.refreshPlot()
|
||||||
|
|
||||||
@ -3300,7 +3300,7 @@ class PickDlg(QDialog):
|
|||||||
if self.autoFilterAction.isChecked():
|
if self.autoFilterAction.isChecked():
|
||||||
self.filterActionP.setChecked(False)
|
self.filterActionP.setChecked(False)
|
||||||
self.filterActionS.setChecked(False)
|
self.filterActionS.setChecked(False)
|
||||||
# data = self.getWFData().copy()
|
# data = self.get_wf_data().copy()
|
||||||
# title = self.getStation()
|
# title = self.getStation()
|
||||||
filter = False
|
filter = False
|
||||||
phase = None
|
phase = None
|
||||||
@ -3800,8 +3800,8 @@ class TuneAutopicker(QWidget):
|
|||||||
fnames = self.station_ids[self.get_current_station_id()]
|
fnames = self.station_ids[self.get_current_station_id()]
|
||||||
if not fnames == self.fnames:
|
if not fnames == self.fnames:
|
||||||
self.fnames = fnames
|
self.fnames = fnames
|
||||||
self.data.setWFData(fnames)
|
self.data.set_wf_data(fnames)
|
||||||
wfdat = self.data.getWFData() # all available streams
|
wfdat = self.data.get_wf_data() # all available streams
|
||||||
# remove possible underscores in station names
|
# remove possible underscores in station names
|
||||||
# wfdat = remove_underscores(wfdat)
|
# wfdat = remove_underscores(wfdat)
|
||||||
# rotate misaligned stations to ZNE
|
# rotate misaligned stations to ZNE
|
||||||
@ -3906,8 +3906,8 @@ class TuneAutopicker(QWidget):
|
|||||||
network = None
|
network = None
|
||||||
location = None
|
location = None
|
||||||
|
|
||||||
wfdata = self.data.getWFData()
|
wfdata = self.data.get_wf_data()
|
||||||
wfdata_comp = self.data.getWFDataComp()
|
wfdata_comp = self.data.get_wf_dataComp()
|
||||||
metadata = self.parent().metadata
|
metadata = self.parent().metadata
|
||||||
event = self.get_current_event()
|
event = self.get_current_event()
|
||||||
filteroptions = self.parent().filteroptions
|
filteroptions = self.parent().filteroptions
|
||||||
@ -3962,7 +3962,7 @@ class TuneAutopicker(QWidget):
|
|||||||
for plotitem in self._manual_pick_plots:
|
for plotitem in self._manual_pick_plots:
|
||||||
self.clear_plotitem(plotitem)
|
self.clear_plotitem(plotitem)
|
||||||
self._manual_pick_plots = []
|
self._manual_pick_plots = []
|
||||||
st = self.data.getWFData()
|
st = self.data.get_wf_data()
|
||||||
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
|
# create two lists with figure names and subindices (for subplots) to get the correct axes
|
||||||
|
Loading…
x
Reference in New Issue
Block a user