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

This commit is contained in:
Darius Arnold 2018-05-25 17:14:02 +02:00
commit 15722c436e
7 changed files with 265 additions and 59 deletions

121
PyLoT.py
View File

@ -46,6 +46,9 @@ from obspy import UTCDateTime
from obspy.core.event import Magnitude, Origin from obspy.core.event import Magnitude, Origin
from obspy.core.util import AttribDict from obspy.core.util import AttribDict
from pylot.core.util.obspyDMT_interface import check_obspydmt_structure
try: try:
import pyqtgraph as pg import pyqtgraph as pg
except Exception as e: except Exception as e:
@ -75,7 +78,8 @@ from pylot.core.util.dataprocessing import read_metadata, restitute_data
from pylot.core.util.utils import fnConstructor, getLogin, \ from pylot.core.util.utils import fnConstructor, getLogin, \
full_range, readFilterInformation, trim_station_components, check4gaps, make_pen, pick_color_plt, \ full_range, readFilterInformation, trim_station_components, check4gaps, make_pen, pick_color_plt, \
pick_linestyle_plt, remove_underscores, check4doubled, identifyPhaseID, excludeQualityClasses, has_spe, \ pick_linestyle_plt, remove_underscores, check4doubled, identifyPhaseID, excludeQualityClasses, has_spe, \
check4rotated, transform_colors_mpl, transform_colors_mpl_str, getAutoFilteroptions check4rotated, transform_colors_mpl, transform_colors_mpl_str, getAutoFilteroptions, check_all_obspy, \
check_all_pylot, real_Bool
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, \
@ -143,6 +147,7 @@ class MainWindow(QMainWindow):
# default factor for dataplot e.g. enabling/disabling scrollarea # default factor for dataplot e.g. enabling/disabling scrollarea
self.height_factor = 12 self.height_factor = 12
self.plot_method = 'normal'
# UI has to be set up before(!) children widgets are about to show up # UI has to be set up before(!) children widgets are about to show up
self.createAction = createAction self.createAction = createAction
@ -987,11 +992,13 @@ class MainWindow(QMainWindow):
''' '''
Return waveform filenames from event in eventbox. Return waveform filenames from event in eventbox.
''' '''
# TODO: add dataStructure class for obspyDMT here, this is just a workaround!
eventpath = self.get_current_event_path(eventbox)
basepath = eventpath.split(os.path.basename(eventpath))[0]
if self.dataStructure: if self.dataStructure:
directory = self.get_current_event_path(eventbox) if not eventpath:
if not directory:
return return
fnames = [os.path.join(directory, f) for f in os.listdir(directory)] fnames = [os.path.join(eventpath, f) for f in os.listdir(eventpath)]
else: else:
raise DatastructureError('not specified') raise DatastructureError('not specified')
return fnames return fnames
@ -1036,13 +1043,15 @@ class MainWindow(QMainWindow):
ed = getExistingDirectories(self, 'Select event directories...') ed = getExistingDirectories(self, 'Select event directories...')
if ed.exec_(): if ed.exec_():
eventlist = ed.selectedFiles() eventlist = ed.selectedFiles()
# select only folders that start with 'e', containin two dots and have length 12 basepath = eventlist[0].split(os.path.basename(eventlist[0]))[0]
eventlist = [item for item in eventlist if item.split('/')[-1].startswith('e') if check_obspydmt_structure(basepath):
and len(item.split('/')[-1].split('.')) == 3 print('Recognized obspyDMT structure in selected files.')
and len(item.split('/')[-1]) == 12] eventlist = check_all_obspy(eventlist)
else:
eventlist = check_all_pylot(eventlist)
if not eventlist: if not eventlist:
print('No events found! Expected structure for event folders: [eEVID.DOY.YR],\n' print('No events found! Expected structure for event folders: [eEVID.DOY.YR],\n'
' e.g. eventID=1, doy=2, yr=2016: e0001.002.16') ' e.g. eventID=1, doy=2, yr=2016: e0001.002.16 or obspyDMT database')
return return
else: else:
return return
@ -1649,35 +1658,48 @@ class MainWindow(QMainWindow):
# else: # else:
# ans = False # ans = False
self.fnames = self.getWFFnames_from_eventbox() self.fnames = self.getWFFnames_from_eventbox()
eventpath = self.get_current_event_path()
basepath = eventpath.split(os.path.basename(eventpath))[0]
obspy_dmt = check_obspydmt_structure(basepath)
self.data.setWFData(self.fnames, self.data.setWFData(self.fnames,
checkRotated=True, checkRotated=True,
metadata=self.metadata) metadata=self.metadata,
obspy_dmt=obspy_dmt)
def check_plot_quantity(self): def check_plot_quantity(self):
"""
Check the amount of samples to be plotted and ask user to reduce the amount if it is too large.
:rtype: None
"""
settings = QSettings() settings = QSettings()
nth_sample = settings.value("nth_sample") if settings.value("nth_sample") else 1 nth_sample = int(settings.value("nth_sample")) if settings.value("nth_sample") else 1
npts_max = 1e6 npts_max = 1e7
npts = self.get_npts_to_plot() npts = self.get_npts_to_plot()
npts2plot = npts/nth_sample npts2plot = npts/nth_sample
if npts2plot < npts_max: if npts2plot < npts_max:
return settings.setValue('large_dataset', False)
nth_sample_new = int(np.ceil(npts/npts_max)) else:
message = "You are about to plot a huge dataset with {npts} datapoints. With a current setting of " \ settings.setValue('large_dataset', True)
"nth_sample = {nth_sample} a total of {npts2plot} points will be plotted which is more " \ self.update_status('Dataset is very large. Using fast plotting method (MIN/MAX)', 10000)
"than the maximum setting of {npts_max}. " \
"PyLoT recommends to raise nth_sample from {nth_sample} to {nth_sample_new}. Continue?"
ans = QMessageBox.question(self, self.tr("Optimize plot performance..."),
self.tr(message.format(npts=npts,
nth_sample=nth_sample,
npts_max=npts_max,
nth_sample_new=nth_sample_new,
npts2plot=npts2plot)),
QMessageBox.Yes | QMessageBox.No,
QMessageBox.Yes)
if ans == QMessageBox.Yes:
settings.setValue("nth_sample", nth_sample_new)
settings.sync() settings.sync()
# nth_sample_new = int(np.ceil(npts/npts_max))
# message = "You are about to plot a huge dataset with {npts} datapoints. With a current setting of " \
# "nth_sample = {nth_sample} a total of {npts2plot} points will be plotted which is more " \
# "than the maximum setting of {npts_max}. " \
# "PyLoT recommends to raise nth_sample from {nth_sample} to {nth_sample_new}. Do you want "\
# "to change nth_sample to {nth_sample_new} now?"
#
# ans = QMessageBox.question(self, self.tr("Optimize plot performance..."),
# self.tr(message.format(npts=npts,
# nth_sample=nth_sample,
# npts_max=npts_max,
# nth_sample_new=nth_sample_new,
# npts2plot=npts2plot)),
# QMessageBox.Yes | QMessageBox.No,
# QMessageBox.Yes)
# if ans == QMessageBox.Yes:
# settings.setValue("nth_sample", nth_sample_new)
# settings.sync()
def get_npts_to_plot(self): def get_npts_to_plot(self):
return sum(trace.stats.npts for trace in self.data.getWFData()) return sum(trace.stats.npts for trace in self.data.getWFData())
@ -1736,9 +1758,12 @@ class MainWindow(QMainWindow):
def finish_pg_plot(self): def finish_pg_plot(self):
self.getPlotWidget().updateWidget() self.getPlotWidget().updateWidget()
plots = self.wfp_thread.data plots = self.wfp_thread.data
for times, data in plots: for times, data, times_syn, data_syn in plots:
self.dataPlot.plotWidget.getPlotItem().plot(times, data, self.dataPlot.plotWidget.getPlotItem().plot(times, data,
pen=self.dataPlot.pen_linecolor) pen=self.dataPlot.pen_linecolor)
if len(data_syn) > 0:
self.dataPlot.plotWidget.getPlotItem().plot(times_syn, data_syn,
pen=self.dataPlot.pen_linecolor_syn)
self.dataPlot.reinitMoveProxy() self.dataPlot.reinitMoveProxy()
self.dataPlot.plotWidget.showAxis('left') self.dataPlot.plotWidget.showAxis('left')
self.dataPlot.plotWidget.showAxis('bottom') self.dataPlot.plotWidget.showAxis('bottom')
@ -1866,6 +1891,7 @@ class MainWindow(QMainWindow):
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().getWFData()
wfsyn = self.get_data().getSynWFData()
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:
@ -1874,8 +1900,12 @@ 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()
plots = plotWidget.plotWFData(wfdata=wfst, title=title, mapping=False, component=comp, if real_Bool(settings.value('large_dataset')) == True:
nth_sample=int(nth_sample)) self.plot_method = 'fast'
else:
self.plot_method = 'normal'
plots = plotWidget.plotWFData(wfdata=wfst, wfsyn=wfsyn, title=title, mapping=False, component=comp,
nth_sample=int(nth_sample), method=self.plot_method)
return plots return plots
def adjustPlotHeight(self): def adjustPlotHeight(self):
@ -2787,6 +2817,16 @@ class MainWindow(QMainWindow):
event.addNotes(notes) event.addNotes(notes)
self.fill_eventbox() self.fill_eventbox()
def set_background_color(items, color):
for item in items:
item.setBackground(color)
def set_foreground_color(items, color):
for item in items:
item.setForeground(color)
current_event = self.get_current_event()
# generate delete icon # generate delete icon
del_icon = QIcon() del_icon = QIcon()
del_icon.addPixmap(QPixmap(':/icons/delete.png')) del_icon.addPixmap(QPixmap(':/icons/delete.png'))
@ -2850,9 +2890,6 @@ class MainWindow(QMainWindow):
item_test = QtGui.QTableWidgetItem() item_test = QtGui.QTableWidgetItem()
item_notes = QtGui.QTableWidgetItem() item_notes = QtGui.QTableWidgetItem()
# manipulate items
item_ref.setBackground(self._ref_test_colors['ref'])
item_test.setBackground(self._ref_test_colors['test'])
item_path.setText(event.path) item_path.setText(event.path)
if hasattr(event, 'origins'): if hasattr(event, 'origins'):
if event.origins: if event.origins:
@ -2891,6 +2928,16 @@ class MainWindow(QMainWindow):
item_nmp, item_nap, item_ref, item_test, item_notes] item_nmp, item_nap, item_ref, item_test, item_notes]
self.project._table.append(column) self.project._table.append(column)
if index%2:
set_background_color(column, QtGui.QColor(*(245, 245, 245, 255)))
if event == current_event:
set_foreground_color(column, QtGui.QColor(*(0, 143, 143, 255)))
# manipulate items
item_ref.setBackground(self._ref_test_colors['ref'])
item_test.setBackground(self._ref_test_colors['test'])
for r_index, row in enumerate(self.project._table): for r_index, row in enumerate(self.project._table):
for c_index, item in enumerate(row): for c_index, item in enumerate(row):
if type(item) == QtGui.QTableWidgetItem: if type(item) == QtGui.QTableWidgetItem:
@ -3149,6 +3196,10 @@ class MainWindow(QMainWindow):
def draw(self): def draw(self):
self.fill_eventbox() self.fill_eventbox()
self.getPlotWidget().draw() self.getPlotWidget().draw()
if self.plot_method == 'fast':
self.dataPlot.setPermText('MIN/MAX plot', color='red')
else:
self.dataPlot.setPermText()
def _setDirty(self): def _setDirty(self):
self.setDirty(True) self.setDirty(True)

View File

@ -368,7 +368,7 @@ class Data(object):
data.filter(**kwargs) data.filter(**kwargs)
self.dirty = True self.dirty = True
def setWFData(self, fnames, checkRotated=False, metadata=None): def setWFData(self, fnames, checkRotated=False, metadata=None, obspy_dmt=False):
""" """
Clear current waveform data and set given waveform data Clear current waveform data and set given waveform data
:param fnames: waveform data names to append :param fnames: waveform data names to append
@ -376,8 +376,22 @@ class Data(object):
""" """
self.wfdata = Stream() self.wfdata = Stream()
self.wforiginal = None self.wforiginal = None
if fnames is not None: self.wfsyn = Stream()
self.appendWFData(fnames) wffnames = None
wffnames_syn = None
wfdir = 'processed' if 'processed' in [fname.split('/')[-1] for fname in fnames] else 'raw'
if obspy_dmt:
for fpath in fnames:
if fpath.endswith(wfdir):
wffnames = [os.path.join(fpath, fname) for fname in os.listdir(fpath)]
if 'syngine' in fpath.split('/')[-1]:
wffnames_syn = [os.path.join(fpath, fname) for fname in os.listdir(fpath)]
else:
wffnames = fnames
if wffnames is not None:
self.appendWFData(wffnames)
if wffnames_syn is not None:
self.appendWFData(wffnames_syn, synthetic=True)
else: else:
return False return False
@ -399,8 +413,7 @@ class Data(object):
return True return True
def appendWFData(self, fnames, synthetic=False):
def appendWFData(self, fnames):
""" """
Read waveform data from fnames and append it to current wf data Read waveform data from fnames and append it to current wf data
:param fnames: waveform data to append :param fnames: waveform data to append
@ -413,13 +426,16 @@ class Data(object):
if self.dirty: if self.dirty:
self.resetWFData() self.resetWFData()
real_or_syn_data = {True: self.wfsyn,
False: self.wfdata}
warnmsg = '' warnmsg = ''
for fname in fnames: for fname in fnames:
try: try:
self.wfdata += read(fname) real_or_syn_data[synthetic] += read(fname)
except TypeError: except TypeError:
try: try:
self.wfdata += read(fname, format='GSE2') real_or_syn_data[synthetic] += read(fname, format='GSE2')
except Exception as e: except Exception as e:
warnmsg += '{0}\n{1}\n'.format(fname, e) warnmsg += '{0}\n{1}\n'.format(fname, e)
except SacIOError as se: except SacIOError as se:
@ -434,6 +450,9 @@ class Data(object):
def getOriginalWFData(self): def getOriginalWFData(self):
return self.wforiginal return self.wforiginal
def getSynWFData(self):
return self.wfsyn
def resetWFData(self): def resetWFData(self):
""" """
Set waveform data to original waveform data Set waveform data to original waveform data

View File

@ -192,7 +192,7 @@ def read_metadata(path_to_inventory):
robj = inv[invtype] robj = inv[invtype]
else: else:
print("Reading metadata information from inventory-xml file ...") print("Reading metadata information from inventory-xml file ...")
robj = inv[invtype] robj = read_inventory(inv[invtype])
return invtype, robj return invtype, robj

View File

@ -0,0 +1,28 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import os
from obspy import UTCDateTime
def check_obspydmt_structure(path):
'''
Check path for obspyDMT event structure.
:param path:
:return:
'''
ev_info = os.path.join(path, 'EVENTS-INFO')
if os.path.isdir(ev_info):
if os.path.isfile(os.path.join(ev_info, 'logger_command.txt')):
return True
return False
def check_obspydmt_eventfolder(folder):
try:
time = folder.split('.')[0]
time = time.replace('_', 'T')
time = UTCDateTime(time)
return True, time
except Exception as e:
return False, e
check_obspydmt_eventfolder('20110311_054623.a')

View File

@ -9,4 +9,4 @@ Created on Wed Jan 26 17:47:25 2015
from pylot.core.io.data import SeiscompDataStructure, PilotDataStructure from pylot.core.io.data import SeiscompDataStructure, PilotDataStructure
DATASTRUCTURE = {'PILOT': PilotDataStructure, 'SeisComP': SeiscompDataStructure, DATASTRUCTURE = {'PILOT': PilotDataStructure, 'SeisComP': SeiscompDataStructure,
None: None} 'obspyDMT': None, None: None}

View File

@ -6,6 +6,7 @@ import os
import platform import platform
import re import re
import subprocess import subprocess
import warnings
import numpy as np import numpy as np
from obspy import UTCDateTime, read from obspy import UTCDateTime, read
@ -13,6 +14,8 @@ from obspy.core import AttribDict
from obspy.signal.rotate import rotate2zne from obspy.signal.rotate import rotate2zne
from obspy.io.xseed.utils import SEEDParserException from obspy.io.xseed.utils import SEEDParserException
from pylot.core.util.obspyDMT_interface import check_obspydmt_eventfolder
from pylot.core.io.inputs import PylotParameter, FilterOptions from pylot.core.io.inputs import PylotParameter, FilterOptions
from pylot.styles import style_settings from pylot.styles import style_settings
@ -451,7 +454,8 @@ def getLogin():
:return: login ID :return: login ID
:rtype: str :rtype: str
""" """
return os.getlogin() import getpass
return getpass.getuser()
def getOwner(fn): def getOwner(fn):
@ -589,7 +593,7 @@ def prepTimeAxis(stime, trace, verbosity=0):
srate = trace.stats.sampling_rate srate = trace.stats.sampling_rate
tincr = trace.stats.delta tincr = trace.stats.delta
etime = stime + nsamp / srate etime = stime + nsamp / srate
time_ax = np.arange(stime, etime, tincr) time_ax = np.linspace(stime, 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')
@ -1216,6 +1220,46 @@ def has_spe(pick):
return pick['spe'] return pick['spe']
def check_all_obspy(eventlist):
ev_type = 'obspydmt'
return check_event_folders(eventlist, ev_type)
def check_all_pylot(eventlist):
ev_type = 'pylot'
return check_event_folders(eventlist, ev_type)
def check_event_folders(eventlist, ev_type):
checklist = []
clean_eventlist = []
for path in eventlist:
folder_check = check_event_folder(path)
if not folder_check:
warnings.warn('Unrecognized event folder: {}'.format(path))
continue
checklist.append(folder_check == ev_type)
clean_eventlist.append(path)
if all(checklist) or len(checklist) == 0:
return clean_eventlist
else:
warnings.warn('Not all selected folders of type {}'.format(ev_type))
return []
def check_event_folder(path):
ev_type = None
folder = path.split('/')[-1]
# for pylot: select only folders that start with 'e', containin two dots and have length 12
if (folder.startswith('e')
and len(folder.split('.')) == 3
and len(folder) == 12):
ev_type = 'pylot'
elif check_obspydmt_eventfolder(folder)[0]:
ev_type = 'obspydmt'
return ev_type
if __name__ == "__main__": if __name__ == "__main__":
import doctest import doctest

View File

@ -25,6 +25,7 @@ except ImportError:
from matplotlib.backends.backend_qt4agg import NavigationToolbar2QT from matplotlib.backends.backend_qt4agg import NavigationToolbar2QT
from matplotlib.widgets import MultiCursor from matplotlib.widgets import MultiCursor
from matplotlib.tight_layout import get_renderer, get_subplotspec_list, get_tight_layout_figure from matplotlib.tight_layout import get_renderer, get_subplotspec_list, get_tight_layout_figure
from scipy.signal import argrelmin, argrelmax
from PySide import QtCore, QtGui from PySide import QtCore, QtGui
from PySide.QtGui import QAction, QApplication, QCheckBox, QComboBox, \ from PySide.QtGui import QAction, QApplication, QCheckBox, QComboBox, \
@ -34,7 +35,7 @@ from PySide.QtGui import QAction, QApplication, QCheckBox, QComboBox, \
QPushButton, QFileDialog, QInputDialog, QKeySequence QPushButton, QFileDialog, QInputDialog, QKeySequence
from PySide.QtCore import QSettings, Qt, QUrl, Signal, Slot from PySide.QtCore import QSettings, Qt, QUrl, Signal, Slot
from PySide.QtWebKit import QWebView from PySide.QtWebKit import QWebView
from obspy import Stream, UTCDateTime from obspy import Stream, Trace, UTCDateTime
from obspy.core.util import AttribDict from obspy.core.util import AttribDict
from obspy.taup import TauPyModel from obspy.taup import TauPyModel
from obspy.taup.utils import get_phase_names from obspy.taup.utils import get_phase_names
@ -448,20 +449,25 @@ class WaveformWidgetPG(QtGui.QWidget):
self.plotdict = dict() self.plotdict = dict()
# create plot # create plot
self.main_layout = QtGui.QVBoxLayout() self.main_layout = QtGui.QVBoxLayout()
self.label = QtGui.QLabel() self.label_layout = QtGui.QHBoxLayout()
self.status_label = QtGui.QLabel()
self.perm_label = QtGui.QLabel()
self.setLayout(self.main_layout) self.setLayout(self.main_layout)
self.plotWidget = self.pg.PlotWidget(self.parent(), title=title) self.plotWidget = self.pg.PlotWidget(self.parent(), title=title)
self.main_layout.addWidget(self.plotWidget) self.main_layout.addWidget(self.plotWidget)
self.main_layout.addWidget(self.label) self.main_layout.addLayout(self.label_layout)
self.label_layout.addWidget(self.status_label)
self.label_layout.addWidget(self.perm_label)
self.plotWidget.showGrid(x=False, y=True, alpha=0.3) self.plotWidget.showGrid(x=False, y=True, alpha=0.3)
self.plotWidget.hideAxis('bottom') self.plotWidget.hideAxis('bottom')
self.plotWidget.hideAxis('left') self.plotWidget.hideAxis('left')
self.wfstart, self.wfend = 0, 0 self.wfstart, self.wfend = 0, 0
self.pen_multicursor = self.pg.mkPen(self.parent()._style['multicursor']['rgba']) self.pen_multicursor = self.pg.mkPen(self.parent()._style['multicursor']['rgba'])
self.pen_linecolor = self.pg.mkPen(self.parent()._style['linecolor']['rgba']) self.pen_linecolor = self.pg.mkPen(self.parent()._style['linecolor']['rgba'])
self.pen_linecolor_syn = self.pg.mkPen((100, 0, 255, 255))
self.reinitMoveProxy() self.reinitMoveProxy()
self._proxy = self.pg.SignalProxy(self.plotWidget.scene().sigMouseMoved, rateLimit=60, slot=self.mouseMoved) self._proxy = self.pg.SignalProxy(self.plotWidget.scene().sigMouseMoved, rateLimit=60, slot=self.mouseMoved)
self.plotWidget.getPlotItem().setDownsampling(auto=True) #self.plotWidget.getPlotItem().setDownsampling(auto=True)
def reinitMoveProxy(self): def reinitMoveProxy(self):
self.vLine = self.pg.InfiniteLine(angle=90, movable=False, pen=self.pen_multicursor) self.vLine = self.pg.InfiniteLine(angle=90, movable=False, pen=self.pen_multicursor)
@ -479,22 +485,27 @@ class WaveformWidgetPG(QtGui.QWidget):
station = self.orig_parent.getStationName(wfID) station = self.orig_parent.getStationName(wfID)
abstime = self.wfstart + x abstime = self.wfstart + x
if self.orig_parent.get_current_event(): if self.orig_parent.get_current_event():
self.label.setText("station = {}, T = {}, t = {} [s]".format(station, abstime, x)) self.status_label.setText("station = {}, T = {}, t = {} [s]".format(station, abstime, x))
self.vLine.setPos(mousePoint.x()) self.vLine.setPos(mousePoint.x())
self.hLine.setPos(mousePoint.y()) self.hLine.setPos(mousePoint.y())
def getPlotDict(self): def getPlotDict(self):
return self.plotdict return self.plotdict
def setPermText(self, text=None, color='black'):
self.perm_label.setText(text)
self.perm_label.setStyleSheet('color: {}'.format(color))
def setPlotDict(self, key, value): def setPlotDict(self, key, value):
self.plotdict[key] = value self.plotdict[key] = value
def clearPlotDict(self): def clearPlotDict(self):
self.plotdict = dict() self.plotdict = dict()
def plotWFData(self, wfdata, title=None, zoomx=None, zoomy=None, def plotWFData(self, wfdata, wfsyn=None, title=None, zoomx=None, zoomy=None,
noiselevel=None, scaleddata=False, mapping=True, noiselevel=None, scaleddata=False, mapping=True,
component='*', nth_sample=1, iniPick=None, verbosity=0): component='*', nth_sample=1, iniPick=None, verbosity=0,
method='normal'):
if not wfdata: if not wfdata:
print('Nothing to plot.') print('Nothing to plot.')
return return
@ -537,7 +548,10 @@ class WaveformWidgetPG(QtGui.QWidget):
for n, (network, station, channel) in enumerate(nsc): for n, (network, station, channel) in enumerate(nsc):
n+=1 n+=1
st = st_select.select(network=network, station=station, channel=channel) st = st_select.select(network=network, station=station, channel=channel)
trace = st[0] trace = st[0].copy()
st_syn = wfsyn.select(network=network, station=station, channel=channel)
if st_syn:
trace_syn = st_syn[0].copy()
if mapping: if mapping:
comp = channel[-1] comp = channel[-1]
n = compclass.getPlotPosition(str(comp)) n = compclass.getPlotPosition(str(comp))
@ -549,13 +563,28 @@ class WaveformWidgetPG(QtGui.QWidget):
print(msg) print(msg)
stime = trace.stats.starttime - self.wfstart stime = trace.stats.starttime - self.wfstart
time_ax = prepTimeAxis(stime, trace) time_ax = prepTimeAxis(stime, trace)
if time_ax is not None: if st_syn:
stime_syn = trace_syn.stats.starttime - self.wfstart
time_ax_syn = prepTimeAxis(stime_syn, trace_syn)
if method == 'fast':
trace.data, time_ax = self.minMax(trace, time_ax)
if time_ax not in [None, []]:
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)
times = [time for index, time in enumerate(time_ax) if not index % nth_sample] if st_syn:
data = [datum + n for index, datum in enumerate(trace.data) if not index % nth_sample] trace_syn.detrend('constant')
plots.append((times, data)) trace_syn.normalize(np.max(np.abs(trace_syn.data)) * 2)
# TODO: change this to numpy operations instead of lists?
times = np.array([time for index, time in enumerate(time_ax) if not index % nth_sample])
times_syn = np.array([time for index, time in enumerate(time_ax_syn) if not index % nth_sample] if st_syn else [])
trace.data = np.array([datum + n for index, datum in enumerate(trace.data) if not index % nth_sample])
trace.data_syn = np.array([datum + n for index, datum in enumerate(trace.data_syn)
if not index % nth_sample] if st_syn else [])
plots.append((times, trace.data,
times_syn, trace.data_syn))
self.setPlotDict(n, (station, channel, network)) self.setPlotDict(n, (station, channel, network))
self.xlabel = 'seconds since {0}'.format(self.wfstart) self.xlabel = 'seconds since {0}'.format(self.wfstart)
self.ylabel = '' self.ylabel = ''
@ -563,6 +592,41 @@ class WaveformWidgetPG(QtGui.QWidget):
self.setYLims([0.5, nmax + 0.5]) self.setYLims([0.5, nmax + 0.5])
return plots return plots
def minMax(self, trace, time_ax):
'''
create min/max array for fast plotting (approach based on obspy __plot_min_max function)
:returns data, time_ax
'''
npixel = self.width()
ndata = len(trace.data)
pts_per_pixel = ndata/npixel
if pts_per_pixel < 2:
return trace.data, time_ax
remaining_samples = ndata%pts_per_pixel
npixel = ndata//pts_per_pixel
if remaining_samples:
data = trace.data[:-remaining_samples]
else:
data = trace.data
data = data.reshape(npixel, pts_per_pixel)
min_ = data.min(axis=1)
max_ = data.max(axis=1)
if remaining_samples:
extreme_values = np.empty((npixel + 1, 2), dtype=np.float)
extreme_values[:-1, 0] = min_
extreme_values[:-1, 1] = max_
extreme_values[-1, 0] = \
trace.data[-remaining_samples:].min()
extreme_values[-1, 1] = \
trace.data[-remaining_samples:].max()
else:
extreme_values = np.empty((npixel, 2), dtype=np.float)
extreme_values[:, 0] = min_
extreme_values[:, 1] = max_
data = extreme_values.flatten()
time_ax = np.linspace(time_ax[0], time_ax[-1], num=len(data))
return data, time_ax
# def getAxes(self): # def getAxes(self):
# return self.axes # return self.axes