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:
parent
ae57381733
commit
27ecdb899b
18
QtPyLoT.py
18
QtPyLoT.py
@ -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):
|
||||||
|
@ -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()
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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]
|
||||||
|
@ -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()
|
||||||
|
Loading…
Reference in New Issue
Block a user