plotting scheme restructured: the same widgets are used for plotting the overview and the station plots, the plotting method has been moved to the widget side which makes more sense than having it on the data side;

new functions in utils module: prepTimeAxis returns a proper time axis array for a given start time and an obspy trace; getGlobalTimes returns the minimum start time and the maximum end time (edges) of a given obspy stream object
This commit is contained in:
Sebastian Wehling-Benatelli 2015-03-29 08:07:46 +02:00
parent ae57381733
commit 27ecdb899b
5 changed files with 105 additions and 66 deletions

View File

@ -104,7 +104,7 @@ class MainWindow(QMainWindow):
self.setWindowTitle("PyLoT - do seismic processing the python way") self.setWindowTitle("PyLoT - do seismic processing the python way")
self.setWindowIcon(QIcon(":/icon.ico")) self.setWindowIcon(QIcon(":/icon.ico"))
xlab = self.startTime.strftime('seconds since %d %b %Y %H:%M:%S (%Z)') xlab = self.startTime.strftime('seconds since %Y/%m/%d %H:%M:%S (%Z)')
_widget = QWidget() _widget = QWidget()
_widget.setCursor(Qt.CrossCursor) _widget.setCursor(Qt.CrossCursor)
@ -365,7 +365,11 @@ class MainWindow(QMainWindow):
self.plotWaveformData() self.plotWaveformData()
def plotWaveformData(self): def plotWaveformData(self):
self.getData().plotWFData(self.getPlotWidget()) zne_text = {'Z': 'vertical', 'N': 'north-south', 'E': 'east-west'}
comp = self.getComponent()
title = 'overview: {0} components'.format(zne_text[comp])
wfst = self.getData().getWFData().select(component=comp)
self.getPlotWidget().plotWFData(wfdata=wfst, title=title)
def filterWaveformData(self): def filterWaveformData(self):
if self.getData(): if self.getData():
@ -455,10 +459,14 @@ class MainWindow(QMainWindow):
wfID = self.getWFID(event) wfID = self.getWFID(event)
station = self.getStationName(wfID) station = self.getStationName(wfID)
data = self.getData().getWFData()
pickDlg = PickDlg(self, data.select(station=station), station)
print 'picking on station {0}'.format(station) print 'picking on station {0}'.format(station)
pickDlg.exec_() data = self.getData().getWFData()
pickDlg = PickDlg(self, data=data.select(station=station),
station=station)
if pickDlg.exec_():
print 'picks accepted'
else:
print 'picks not saved and closed dialog'
def updateStatus(self, message): def updateStatus(self, message):

View File

@ -10,7 +10,8 @@ from obspy.core.event import (Event, Catalog)
from pylot.core.read import readPILOTEvent from pylot.core.read import readPILOTEvent
from pylot.core.util import fnConstructor, createEvent, FormatError from pylot.core.util import fnConstructor, createEvent, FormatError, \
prepTimeAxis, getGlobalTimes
class Data(object): class Data(object):
@ -63,14 +64,7 @@ class Data(object):
return self.cuttimes return self.cuttimes
def updateCutTimes(self): def updateCutTimes(self):
min_start = UTCDateTime() self.cuttimes = getGlobalTimes(self.getWFData())
max_end = None
for trace in self.getWFData().select(component=self.getComp()):
if trace.stats.starttime < min_start:
min_start = trace.stats.starttime
if max_end is None or trace.stats.endtime > max_end:
max_end = trace.stats.endtime
self.cuttimes = [min_start, max_end]
def exportEvent(self, fnout=None, evtformat='QUAKEML'): def exportEvent(self, fnout=None, evtformat='QUAKEML'):
@ -99,29 +93,6 @@ class Data(object):
raise KeyError('''{0} export format raise KeyError('''{0} export format
not implemented: {1}'''.format(evtformat, e)) not implemented: {1}'''.format(evtformat, e))
def plotWFData(self, widget):
wfst = self.getWFData().select(component=self.getComp())
widget.axes.cla()
for n, trace in enumerate(wfst):
stime = trace.stats.starttime - self.getCutTimes()[0]
etime = trace.stats.endtime - self.getCutTimes()[1]
srate = trace.stats.sampling_rate
nsamp = len(trace.data)
tincr = trace.stats.delta
station = trace.stats.station
time_ax = np.arange(stime, nsamp / srate, tincr)
trace.normalize(trace.data.max() * 2)
widget.axes.plot(time_ax, trace.data + n, 'k')
xlabel = 'seconds since {0}'.format(self.getCutTimes()[0])
ylabel = ''
zne_text = {'Z': 'vertical', 'N': 'north-south', 'E': 'east-west'}
title = 'overview: {0} components'.format(zne_text[self.getComp()])
widget.updateWidget(xlabel, ylabel, title)
widget.setPlotDict(n, station)
widget.axes.autoscale(tight=True)
def getComp(self): def getComp(self):
return self.comp return self.comp
@ -146,7 +117,8 @@ class Data(object):
def appendWFData(self, fnames): def appendWFData(self, fnames):
assert isinstance(fnames, list), "input parameter 'fnames' is " \ assert isinstance(fnames, list), "input parameter 'fnames' is " \
"supposed to be of type 'list' " \ "supposed to be of type 'list' " \
"but is actually".format(type(fnames)) "but is actually {0}".format(type(
fnames))
if self.dirty: if self.dirty:
self.resetWFData() self.resetWFData()

View File

@ -4,7 +4,8 @@ from pylot.core.util.errors import OptionsError, FormatError, DatastructureError
from pylot.core.util.layouts import layoutStationButtons from pylot.core.util.layouts import layoutStationButtons
from pylot.core.util.utils import fnConstructor, createArrival, createEvent,\ from pylot.core.util.utils import fnConstructor, createArrival, createEvent,\
createPick, createAmplitude, createOrigin, createMagnitude, getOwner, \ createPick, createAmplitude, createOrigin, createMagnitude, getOwner, \
getHash, getLogin, createCreationInfo, createResourceID getHash, getLogin, createCreationInfo, createResourceID, prepTimeAxis, \
getGlobalTimes
from pylot.core.util.widgets import PickDlg, HelpForm, FilterOptionsDialog,\ from pylot.core.util.widgets import PickDlg, HelpForm, FilterOptionsDialog,\
PropertiesDlg, NewEventDlg, MPLWidget, createAction PropertiesDlg, NewEventDlg, MPLWidget, createAction
from pylot.core.util.version import get_git_version as _getVersionString from pylot.core.util.version import get_git_version as _getVersionString

View File

@ -6,6 +6,7 @@ import os
import pwd import pwd
import re import re
import hashlib import hashlib
import numpy as np
from obspy.core import UTCDateTime from obspy.core import UTCDateTime
import obspy.core.event as ope import obspy.core.event as ope
@ -224,5 +225,30 @@ def createAmplitude(pickID, amp, unit, category, origintime, cinfo,
def getOwner(fn): def getOwner(fn):
return pwd.getpwuid(os.stat(fn).st_uid).pw_name return pwd.getpwuid(os.stat(fn).st_uid).pw_name
def prepTimeAxis(stime, trace):
nsamp = trace.stats.npts
srate = trace.stats.sampling_rate
tincr = trace.stats.delta
etime = stime + nsamp / srate
time_ax = np.arange(stime, etime, tincr)
if len(time_ax) < nsamp:
print 'elongate time axes by one datum'
time_ax = np.arange(stime, etime + tincr, tincr)
elif len(time_ax) > nsamp:
print 'shorten time axes by one datum'
time_ax = np.arange(stime, etime - tincr, tincr)
if len(time_ax) != nsamp:
raise ValueError('{0} samples of data \n '
'{1} length of time vector \n'
'delta: {2}'.format(nsamp, len(time_ax), tincr))
return time_ax
def getGlobalTimes(stream):
min_start = UTCDateTime()
max_end = None
for trace in stream:
if trace.stats.starttime < min_start:
min_start = trace.stats.starttime
if max_end is None or trace.stats.endtime > max_end:
max_end = trace.stats.endtime
return [min_start, max_end]

View File

@ -43,6 +43,7 @@ from PySide.QtCore import (QSettings,
from PySide.QtWebKit import QWebView from PySide.QtWebKit import QWebView
from pylot.core.read import FilterOptions from pylot.core.read import FilterOptions
from pylot.core.util.defaults import OUTPUTFORMATS from pylot.core.util.defaults import OUTPUTFORMATS
from pylot.core.util import prepTimeAxis, getGlobalTimes
def createAction(parent, text, slot=None, shortcut=None, icon=None, def createAction(parent, text, slot=None, shortcut=None, icon=None,
@ -70,13 +71,18 @@ class MPLWidget(FigureCanvas):
self._parent = None self._parent = None
self.setParent(parent) self.setParent(parent)
self.figure = Figure() self.figure = Figure()
# attribute plotdict is an dictionary connecting position and a name
self.plotdict = dict() self.plotdict = dict()
# create axes
self.axes = self.figure.add_subplot(111) self.axes = self.figure.add_subplot(111)
self._statID = None # clear axes each time plot is called
self.axes.hold(True)
# initialize super class
FigureCanvas.__init__(self, self.figure) FigureCanvas.__init__(self, self.figure)
# add an cursor for station selection
self.multiCursor = MultiCursor(self.figure.canvas, (self.axes,), horizOn=True, self.multiCursor = MultiCursor(self.figure.canvas, (self.axes,), horizOn=True,
color='m', lw=1) color='m', lw=1)
# update labels of the entire widget
self.updateWidget(xlabel, ylabel, title) self.updateWidget(xlabel, ylabel, title)
def getPlotDict(self): def getPlotDict(self):
@ -91,6 +97,25 @@ class MPLWidget(FigureCanvas):
def setParent(self, parent): def setParent(self, parent):
self._parent = parent self._parent = parent
def plotWFData(self, wfdata, title = None):
self.axes.lines = []
wfstart = getGlobalTimes(wfdata)[0]
for n, trace in enumerate(wfdata):
station = trace.stats.station
print('plotting station: %s' % station)
stime = trace.stats.starttime - wfstart
time_ax = prepTimeAxis(stime, trace)
trace.detrend()
trace.detrend('demean')
trace.normalize(trace.data.max() * 2)
self.axes.plot(time_ax, trace.data + n, 'k')
self.axes.hold(True)
xlabel = 'seconds since {0}'.format(wfstart)
ylabel = ''
self.updateWidget(xlabel, ylabel, title)
self.setPlotDict(n, station)
self.axes.autoscale(tight=True)
def updateXLabel(self, text): def updateXLabel(self, text):
self.axes.set_xlabel(text) self.axes.set_xlabel(text)
@ -105,6 +130,11 @@ class MPLWidget(FigureCanvas):
self.updateYLabel(ylabel) self.updateYLabel(ylabel)
self.updateTitle(title) self.updateTitle(title)
def insertLabel(self, pos, text):
pos = pos / max(self.axes.ylim)
axann = self.axes.annotate(text, xy=(.03, pos), xycoords='axes fraction')
axann.set_bbox(dict(facecolor='lightgrey', alpha=.6))
class multiComponentPlot(FigureCanvas): class multiComponentPlot(FigureCanvas):
@ -165,12 +195,8 @@ class multiComponentPlot(FigureCanvas):
self.axesdict = dict() self.axesdict = dict()
# prepare variables for plotting # prepare variables for plotting
trace = data[0] stime = getGlobalTimes(self.getData())[0]
stime = trace.stats.starttime
srate = trace.stats.sampling_rate
npts = trace.stats.npts
tincr = trace.stats.delta
time_ax = np.arange(0, npts / srate, tincr)
xlabel = 'time since {0} [s]'.format(stime) xlabel = 'time since {0} [s]'.format(stime)
# plot individual component traces in separate subplots # plot individual component traces in separate subplots
@ -181,8 +207,9 @@ class multiComponentPlot(FigureCanvas):
else: else:
subax = self.figure.add_subplot(nsub) subax = self.figure.add_subplot(nsub)
subax.autoscale(tight=True) subax.autoscale(tight=True)
subset = data.copy().select(component=comp)[0].data subset = data.copy().select(component=comp)[0]
subax.plot(time_ax, subset) time_ax = prepTimeAxis(subset.stats.starttime - stime, subset)
subax.plot(time_ax, subset.data)
self.axesdict[n] = subax self.axesdict[n] = subax
self.updateYLabel(n, comp) self.updateYLabel(n, comp)
if n == self.noc: if n == self.noc:
@ -190,12 +217,9 @@ class multiComponentPlot(FigureCanvas):
else: else:
self.updateXLabel(n, '') self.updateXLabel(n, '')
self.multiCursor = MultiCursor(self.figure.canvas, tuple(self.axesdict.values()), color='r', lw=1) self.multiCursor = MultiCursor(self.figure.canvas,
tuple(self.axesdict.values()),
def insertLabel(self, pos, text): color='r', lw=1)
subax = self.axesdict[pos]
axann = subax.annotate(text, xy=(.03, .97), xycoords='axes fraction')
axann.set_bbox(dict(facecolor='lightgrey', alpha=.6))
def updateXLabel(self, pos, text): def updateXLabel(self, pos, text):
self.axesdict[pos].set_xlabel(text) self.axesdict[pos].set_xlabel(text)
@ -214,6 +238,7 @@ class PickDlg(QDialog):
self.rotate = rotate self.rotate = rotate
self.components = 'ZNE' self.components = 'ZNE'
# set attribute holding data
if data is None: if data is None:
try: try:
data = parent.getData().getWFData().copy() data = parent.getData().getWFData().copy()
@ -225,10 +250,16 @@ class PickDlg(QDialog):
else: else:
self.data = data self.data = data
self.multicompfig = multiComponentPlot(data=data, parent=self, # initialize plotting widget
components=self.getComponents()) self.multicompfig = MPLWidget(self)
# setup ui
self.setupUi() self.setupUi()
# plot data
self.getPlotWidget().plotWFData(wfdata=self.getWFData(),
title=self.getStation())
def setupUi(self): def setupUi(self):
# create actions # create actions
@ -266,6 +297,9 @@ class PickDlg(QDialog):
def getComponents(self): def getComponents(self):
return self.components return self.components
def getStation(self):
return self.station
def getPlotWidget(self): def getPlotWidget(self):
return self.multicompfig return self.multicompfig
@ -273,8 +307,9 @@ class PickDlg(QDialog):
return self.data return self.data
def filterWFData(self): def filterWFData(self):
self.data.filter(type='bandpass', freqmin=.5, freqmax=15.) data = self.getWFData().copy().filter(type='bandpass', freqmin=.5, freqmax=15.)
self.getPlotWidget().resetPlot(self.getComponents(), self.getWFData()) title = self.getStation() + ' (filtered)'
self.getPlotWidget().plotWFData(wfdata=data, title=title)
class PropertiesDlg(QDialog): class PropertiesDlg(QDialog):
@ -300,12 +335,9 @@ class PropertiesDlg(QDialog):
layout.addWidget(self.buttonBox) layout.addWidget(self.buttonBox)
self.setLayout(layout) self.setLayout(layout)
self.connect(self.buttonBox, Signal("accepted()"), self, self.buttonBox.accepted.connect(self.accept)
Slot("accept()")) self.buttonBox.rejected.connect(self.reject)
self.connect(self.buttonBox.button(QDialogButtonBox.Apply), self.buttonBox.button(QDialogButtonBox.Apply).clicked.connect(self.apply)
Signal("clicked()"), self.apply)
self.connect(self.buttonBox, Signal("rejected()"),
self, Slot("reject()"))
def accept(self, *args, **kwargs): def accept(self, *args, **kwargs):
self.apply() self.apply()