From 08240261598ef37472fc9f8df4d1c113db9f6b42 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Thu, 2 Apr 2015 15:45:38 +0200 Subject: [PATCH 1/7] standalone python files now import definitions from module pylot.core.pick.utils and parse arguments when called from shell (deleted "if __name__ == '__main__' :" clauses from the module) --- pylot/core/pick/earllatepicker.py | 132 +-------- pylot/core/pick/fmpicker.py | 184 +----------- pylot/core/pick/getSNR.py | 74 ++--- pylot/core/pick/getnoisewin.py | 15 + pylot/core/pick/getsignalwin.py | 14 + pylot/core/pick/utils.py | 446 ++++++++++++++---------------- 6 files changed, 282 insertions(+), 583 deletions(-) create mode 100644 pylot/core/pick/getnoisewin.py create mode 100644 pylot/core/pick/getsignalwin.py diff --git a/pylot/core/pick/earllatepicker.py b/pylot/core/pick/earllatepicker.py index f41a0503..ab241880 100755 --- a/pylot/core/pick/earllatepicker.py +++ b/pylot/core/pick/earllatepicker.py @@ -7,127 +7,21 @@ and latest possible pick is calculated based on noise measurements in front of the most likely pick and signal wavelength derived from zero crossings. - :author: Ludger Kueperkoch / MAGS2 EP3 working group + :author: Ludger Kueperkoch / MAGS2 EP3 working group """ -import numpy as np -import matplotlib.pyplot as plt -from obspy.core import Stream + import argparse +import obspy +from pylot.core.pick.utils import earllatepicker -def earllatepicker(X, nfac, TSNR, Pick1, iplot=None): - ''' - Function to derive earliest and latest possible pick after Diehl & Kissling (2009) - as reasonable uncertainties. Latest possible pick is based on noise level, - earliest possible pick is half a signal wavelength in front of most likely - pick given by PragPicker or manually set by analyst. Most likely pick - (initial pick Pick1) must be given. - - :param: X, time series (seismogram) - :type: `~obspy.core.stream.Stream` - - :param: nfac (noise factor), nfac times noise level to calculate latest possible pick - :type: int - - :param: TSNR, length of time windows around pick used to determine SNR [s] - :type: tuple (T_noise, T_gap, T_signal) - - :param: Pick1, initial (most likely) onset time, starting point for earllatepicker - :type: float - - :param: iplot, if given, results are plotted in figure(iplot) - :type: int - ''' - - assert isinstance(X, Stream), "%s is not a stream object" % str(X) - - LPick = None - EPick = None - PickError = None - print 'earllatepicker: Get earliest and latest possible pick relative to most likely pick ...' - - x = X[0].data - t = np.arange(0, X[0].stats.npts / X[0].stats.sampling_rate, X[0].stats.delta) - #some parameters needed: - tnoise = TSNR[0] #noise window length for calculating noise level - tsignal = TSNR[2] #signal window length - tsafety = TSNR[1] #safety gap between signal onset and noise window - - #get latest possible pick - #get noise window - inoise = np.where((t <= max([Pick1 - tsafety, 0])) \ - & (t >= max([Pick1 - tnoise - tsafety, 0]))) - #get signal window - isignal = np.where((t <= min([Pick1 + tsignal + tsafety, len(x)])) \ - & (t >= Pick1)) - #calculate noise level - nlevel = max(abs(x[inoise])) * nfac - #get time where signal exceeds nlevel - ilup = np.where(x[isignal] > nlevel) - ildown = np.where(x[isignal] < -nlevel) - if len(ilup[0]) <= 1 and len(ildown[0]) <= 1: - print 'earllatepicker: Signal lower than noise level, misspick?' - return - il = min([ilup[0][0], ildown[0][0]]) - LPick = t[isignal][il] - - #get earliest possible pick - #get next 2 zero crossings after most likely pick - #initial onset is assumed to be the first zero crossing - zc = [] - zc.append(Pick1) - i = 0 - for j in range(isignal[0][1],isignal[0][len(t[isignal]) - 1]): - i = i+ 1 - if x[j-1] <= 0 and x[j] >= 0: - zc.append(t[isignal][i]) - elif x[j-1] > 0 and x[j] <= 0: - zc.append(t[isignal][i]) - if len(zc) == 3: - break - #calculate maximum period T0 of signal out of zero crossings - T0 = max(np.diff(zc)) #this is half wave length! - #T0/4 is assumed as time difference between most likely and earliest possible pick! - EPick = Pick1 - T0/2 - - #get symmetric pick error as mean from earliest and latest possible pick - #by weighting latest possible pick two times earliest possible pick - diffti_tl = LPick - Pick1 - diffti_te = Pick1 - EPick - PickError = (diffti_te + 2 * diffti_tl) / 3 - - if iplot is not None: - plt.figure(iplot) - p1, = plt.plot(t, x, 'k') - p2, = plt.plot(t[inoise], x[inoise]) - p3, = plt.plot(t[isignal], x[isignal], 'r') - p4, = plt.plot([t[0], t[int(len(t)) - 1]], [nlevel, nlevel], '--k') - p5, = plt.plot(zc, [0, 0, 0], '*g', markersize=14) - plt.legend([p1, p2, p3, p4, p5], ['Data', 'Noise Window', 'Signal Window', 'Noise Level', 'Zero Crossings'], \ - loc='best') - plt.plot([t[0], t[int(len(t)) - 1]], [-nlevel, -nlevel], '--k') - plt.plot([Pick1, Pick1], [max(x), -max(x)], 'b', linewidth=2) - plt.plot([LPick, LPick], [max(x)/2, -max(x)/2], '--k') - plt.plot([EPick, EPick], [max(x)/2, -max(x)/2], '--k') - plt.plot([Pick1 + PickError, Pick1 + PickError], [max(x)/2, -max(x)/2], 'r--') - plt.plot([Pick1 - PickError, Pick1 - PickError], [max(x)/2, -max(x)/2], 'r--') - plt.xlabel('Time [s] since %s' % X[0].stats.starttime) - plt.yticks([]) - ax = plt.gca() - ax.set_xlim([t[inoise[0][0]] - 2, t[isignal[0][len(isignal) - 1]] + 3]) - plt.title('Earliest-/Latest Possible/Most Likely Pick & Symmetric Pick Error, %s' % X[0].stats.station) - plt.show() - raw_input() - plt.close(iplot) - - return EPick, LPick, PickError if __name__ == "__main__": - parser = argparse.ArgumentParser() - parser.add_argument('--X', type=~obspy.core.stream.Stream, help='time series (seismogram) read with obspy module read') - parser.add_argument('--nfac', type=int, help='(noise factor), nfac times noise level to calculate latest possible pick') - parser.add_argument('--TSNR', type=tuple, help='length of time windows around pick used to determine SNR \ - [s] (Tnoise, Tgap, Tsignal)') - parser.add_argument('--Pick1', type=float, help='Onset time of most likely pick') - parser.add_argument('--iplot', type=int, help='if set, figure no. iplot occurs') - args = parser.parse_args() - earllatepicker(args.X, args.nfac, args.TSNR, args.Pick1, args.iplot) + parser = argparse.ArgumentParser() + parser.add_argument('--X', type=~obspy.core.stream.Stream, help='time series (seismogram) read with obspy module read') + parser.add_argument('--nfac', type=int, help='(noise factor), nfac times noise level to calculate latest possible pick') + parser.add_argument('--TSNR', type=tuple, help='length of time windows around pick used to determine SNR \ + [s] (Tnoise, Tgap, Tsignal)') + parser.add_argument('--Pick1', type=float, help='Onset time of most likely pick') + parser.add_argument('--iplot', type=int, help='if set, figure no. iplot occurs') + args = parser.parse_args() + earllatepicker(args.X, args.nfac, args.TSNR, args.Pick1, args.iplot) diff --git a/pylot/core/pick/fmpicker.py b/pylot/core/pick/fmpicker.py index 6aee8eb3..0ccbc1d1 100755 --- a/pylot/core/pick/fmpicker.py +++ b/pylot/core/pick/fmpicker.py @@ -4,180 +4,20 @@ Created Mar 2015 Function to derive first motion (polarity) for given phase onset based on zero crossings. - :author: MAGS2 EP3 working group / Ludger Kueperkoch + :author: MAGS2 EP3 working group / Ludger Kueperkoch """ -import numpy as np -import matplotlib.pyplot as plt -from obspy.core import Stream + import argparse - -def fmpicker(Xraw, Xfilt, pickwin, Pick, iplot=None): - ''' - Function to derive first motion (polarity) of given phase onset Pick. - Calculation is based on zero crossings determined within time window pickwin - after given onset time. - - :param: Xraw, unfiltered time series (seismogram) - :type: `~obspy.core.stream.Stream` - - :param: Xfilt, filtered time series (seismogram) - :type: `~obspy.core.stream.Stream` - - :param: pickwin, time window after onset Pick within zero crossings are calculated - :type: float - - :param: Pick, initial (most likely) onset time, starting point for fmpicker - :type: float - - :param: iplot, if given, results are plotted in figure(iplot) - :type: int - ''' - - assert isinstance(Xraw, Stream), "%s is not a stream object" % str(Xraw) - assert isinstance(Xfilt, Stream), "%s is not a stream object" % str(Xfilt) - - FM = None - if Pick is not None: - print 'fmpicker: Get first motion (polarity) of onset using unfiltered seismogram...' - - xraw = Xraw[0].data - xfilt = Xfilt[0].data - t = np.arange(0, Xraw[0].stats.npts / Xraw[0].stats.sampling_rate, Xraw[0].stats.delta) - #get pick window - ipick = np.where((t <= min([Pick + pickwin, len(Xraw[0])])) & (t >= Pick)) - #remove mean - xraw[ipick] = xraw[ipick] - np.mean(xraw[ipick]) - xfilt[ipick] = xfilt[ipick] - np.mean(xfilt[ipick]) - - #get next zero crossing after most likely pick - #initial onset is assumed to be the first zero crossing - #first from unfiltered trace - zc1 = [] - zc1.append(Pick) - index1 = [] - i = 0 - for j in range(ipick[0][1],ipick[0][len(t[ipick]) - 1]): - i = i+ 1 - if xraw[j-1] <= 0 and xraw[j] >= 0: - zc1.append(t[ipick][i]) - index1.append(i) - elif xraw[j-1] > 0 and xraw[j] <= 0: - zc1.append(t[ipick][i]) - index1.append(i) - if len(zc1) == 3: - break - - #if time difference betweeen 1st and 2cnd zero crossing - #is too short, get time difference between 1st and 3rd - #to derive maximum - if zc1[1] - zc1[0] <= Xraw[0].stats.delta: - li1 = index1[1] - else: - li1 = index1[0] - if np.size(xraw[ipick[0][1]:ipick[0][li1]]) == 0: - print 'earllatepicker: Onset on unfiltered trace too emergent for first motion determination!' - P1 = None - else: - imax1 = np.argmax(abs(xraw[ipick[0][1]:ipick[0][li1]])) - islope1 = np.where((t >= Pick) & (t <= Pick + t[imax1])) - #calculate slope as polynomal fit of order 1 - xslope1 = np.arange(0, len(xraw[islope1]), 1) - P1 = np.polyfit(xslope1, xraw[islope1], 1) - datafit1 = np.polyval(P1, xslope1) - - #now using filterd trace - #next zero crossing after most likely pick - zc2 = [] - zc2.append(Pick) - index2 = [] - i = 0 - for j in range(ipick[0][1],ipick[0][len(t[ipick]) - 1]): - i = i+ 1 - if xfilt[j-1] <= 0 and xfilt[j] >= 0: - zc2.append(t[ipick][i]) - index2.append(i) - elif xfilt[j-1] > 0 and xfilt[j] <= 0: - zc2.append(t[ipick][i]) - index2.append(i) - if len(zc2) == 3: - break - - #if time difference betweeen 1st and 2cnd zero crossing - #is too short, get time difference between 1st and 3rd - #to derive maximum - if zc2[1] - zc2[0] <= Xfilt[0].stats.delta: - li2 = index2[1] - else: - li2 = index2[0] - if np.size(xfilt[ipick[0][1]:ipick[0][li2]]) == 0: - print 'earllatepicker: Onset on filtered trace too emergent for first motion determination!' - P2 = None - else: - imax2 = np.argmax(abs(xfilt[ipick[0][1]:ipick[0][li2]])) - islope2 = np.where((t >= Pick) & (t <= Pick + t[imax2])) - #calculate slope as polynomal fit of order 1 - xslope2 = np.arange(0, len(xfilt[islope2]), 1) - P2 = np.polyfit(xslope2, xfilt[islope2], 1) - datafit2 = np.polyval(P2, xslope2) - - #compare results - if P1 is not None and P2 is not None: - if P1[0] < 0 and P2[0] < 0: - FM = 'D' - elif P1[0] >= 0 and P2[0] < 0: - FM = '-' - elif P1[0] < 0 and P2[0]>= 0: - FM = '-' - elif P1[0] > 0 and P2[0] > 0: - FM = 'U' - elif P1[0] <= 0 and P2[0] > 0: - FM = '+' - elif P1[0] > 0 and P2[0] <= 0: - FM = '+' - - if iplot is not None: - plt.figure(iplot) - plt.subplot(2,1,1) - plt.plot(t, xraw, 'k') - p1, = plt.plot([Pick, Pick], [max(xraw), -max(xraw)], 'b', linewidth=2) - if P1 is not None: - p2, = plt.plot(t[islope1], xraw[islope1]) - p3, = plt.plot(zc1, np.zeros(len(zc1)), '*g', markersize=14) - p4, = plt.plot(t[islope1], datafit1, '--g', linewidth=2) - plt.legend([p1, p2, p3, p4], ['Pick', 'Slope Window', 'Zero Crossings', 'Slope'], \ - loc='best') - plt.text(Pick + 0.02, max(xraw) / 2, '%s' % FM, fontsize=14) - ax = plt.gca() - ax.set_xlim([t[islope1[0][0]] - 0.1, t[islope1[0][len(islope1) - 1]] + 0.3]) - plt.yticks([]) - plt.title('First-Motion Determination, %s, Unfiltered Data' % Xraw[0].stats.station) - - plt.subplot(2,1,2) - plt.title('First-Motion Determination, Filtered Data') - plt.plot(t, xfilt, 'k') - p1, = plt.plot([Pick, Pick], [max(xfilt), -max(xfilt)], 'b', linewidth=2) - if P2 is not None: - p2, = plt.plot(t[islope2], xfilt[islope2]) - p3, = plt.plot(zc2, np.zeros(len(zc2)), '*g', markersize=14) - p4, = plt.plot(t[islope2], datafit2, '--g', linewidth=2) - plt.text(Pick + 0.02, max(xraw) / 2, '%s' % FM, fontsize=14) - ax = plt.gca() - ax.set_xlim([t[islope2[0][0]] - 0.1, t[islope2[0][len(islope2) - 1]] + 0.3]) - plt.xlabel('Time [s] since %s' % Xraw[0].stats.starttime) - plt.yticks([]) - plt.show() - raw_input() - plt.close(iplot) - - return FM +import obspy +from pylot.core.pick.utils import fmpicker if __name__ == "__main__": - parser = argparse.ArgumentParser() - parser.add_argument('--Xraw', type=~obspy.core.stream.Stream, help='unfiltered time series (seismogram) read with obspy module read') - parser.add_argument('--Xfilt', type=~obspy.core.stream.Stream, help='filtered time series (seismogram) read with obspy module read') - parser.add_argument('--pickwin', type=float, help='length of pick window [s] for first motion determination') - parser.add_argument('--Pick', type=float, help='Onset time of most likely pick') - parser.add_argument('--iplot', type=int, help='if set, figure no. iplot occurs') - args = parser.parse_args() - earllatepicker(args.Xraw, args.Xfilt, args.pickwin, args.Pick, args.iplot) + parser = argparse.ArgumentParser() + parser.add_argument('--Xraw', type=~obspy.core.stream.Stream, help='unfiltered time series (seismogram) read with obspy module read') + parser.add_argument('--Xfilt', type=~obspy.core.stream.Stream, help='filtered time series (seismogram) read with obspy module read') + parser.add_argument('--pickwin', type=float, help='length of pick window [s] for first motion determination') + parser.add_argument('--Pick', type=float, help='Onset time of most likely pick') + parser.add_argument('--iplot', type=int, help='if set, figure no. iplot occurs') + args = parser.parse_args() + fmpicker(args.Xraw, args.Xfilt, args.pickwin, args.Pick, args.iplot) diff --git a/pylot/core/pick/getSNR.py b/pylot/core/pick/getSNR.py index 6c2b6872..bbfbdba5 100644 --- a/pylot/core/pick/getSNR.py +++ b/pylot/core/pick/getSNR.py @@ -2,65 +2,29 @@ # -*- coding: utf-8 -*- """ Created Mar/Apr 2015 - Function to calculate SNR of certain part of seismogram relativ + Function to calculate SNR of certain part of seismogram relative to given time. Returns SNR and SNR [dB]. :author: Ludger Kueperkoch /MAGS EP3 working group """ -from obspy.core import Stream -import numpy as np -def getSNR(X, TSNR, t1): - ''' - Function to calculate SNR of certain part of seismogram relative to - given time (onset) out of given noise and signal windows. A safety gap - between noise and signal part can be set. Returns SNR and SNR [dB]. - - :param: X, time series (seismogram) - :type: `~obspy.core.stream.Stream` - - :param: TSNR, length of time windows [s] around t1 (onset) used to determine SNR - :type: tuple (T_noise, T_gap, T_signal) - - :param: t1, initial time (onset) from which noise and signal windows are calculated - :type: float - ''' - - assert isinstance(X, Stream), "%s is not a stream object" % str(X) - - SNR = None - SNRdB = None - x = X[0].data - t = np.arange(0, X[0].stats.npts / X[0].stats.sampling_rate, X[0].stats.delta) - #some parameters needed: - tnoise = TSNR[0] #noise window length for calculating noise level - tsignal = TSNR[2] #signal window length - tsafety = TSNR[1] #safety gap between signal onset and noise window - #get noise window - inoise = np.where((t <= max([t1 - tsafety, 0])) \ - & (t >= max([t1 - tnoise - tsafety, 0]))) - #get signal window - isignal = np.where((t <= min([t1 + tsignal + tsafety, len(x)])) \ - & (t >= t1)) - if np.size(inoise) < 1: - print 'getSNR: Empty array inoise, check noise window!' - return - elif np.size(isignal) < 1: - print 'getSNR: Empty array isignal, check signal window!' - return - - #calculate ratios - SNR = max(abs(x[isignal])) / np.mean(abs(x[inoise])) - SNRdB = 20 * np.log10(SNR) - - return SNR, SNRdB +import argparse +import obspy +from pylot.core.pick.utils import getSNR if __name__ == "__main__": - parser = argparse.ArgumentParser() - parser.add_argument('--X', type=~obspy.core.stream.Stream, help='time series (seismogram) read with obspy module read') - parser.add_argument('--TSNR', type=tuple, help='length of time windows around pick used to determine SNR \ - [s] (Tnoise, Tgap, Tsignal)') - parser.add_argument('--t1', type=float, help='initial time from which noise and signal windows are calculated') - args = parser.parse_args() - getSNR(args.X, args.TSNR, args.t1) - + parser = argparse.ArgumentParser() + parser.add_argument('--data', '-d', type=~obspy.core.stream.Stream, + help='time series (seismogram) read with obspy module ' + 'read', + dest='data') + parser.add_argument('--tsnr', '-s', type=tuple, + help='length of time windows around pick used to ' + 'determine SNR [s] (Tnoise, Tgap, Tsignal)', + dest='tsnr') + parser.add_argument('--time', '-t', type=float, + help='initial time from which noise and signal windows ' + 'are calculated', + dest='time') + args = parser.parse_args() + print getSNR(args.data, args.tsnr, args.time) diff --git a/pylot/core/pick/getnoisewin.py b/pylot/core/pick/getnoisewin.py new file mode 100644 index 00000000..8c21913f --- /dev/null +++ b/pylot/core/pick/getnoisewin.py @@ -0,0 +1,15 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import argparse +import numpy +from pylot.core.pick.utils import getnoisewin + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument('--t', type=~numpy.array, help='numpy array of time stamps') + parser.add_argument('--t1', type=float, help='time from which relativ to it noise window is extracted') + parser.add_argument('--tnoise', type=float, help='length of time window [s] for noise part extraction') + parser.add_argument('--tgap', type=float, help='safety gap between signal (t1=onset) and noise') + args = parser.parse_args() + getnoisewin(args.t, args.t1, args.tnoise, args.tgap) diff --git a/pylot/core/pick/getsignalwin.py b/pylot/core/pick/getsignalwin.py new file mode 100644 index 00000000..4b3013b8 --- /dev/null +++ b/pylot/core/pick/getsignalwin.py @@ -0,0 +1,14 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import argparse +import numpy +from pylot.core.pick.utils import getsignalwin + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument('--t', type=~numpy.array, help='numpy array of time stamps') + parser.add_argument('--t1', type=float, help='time from which relativ to it signal window is extracted') + parser.add_argument('--tsignal', type=float, help='length of time window [s] for signal part extraction') + args = parser.parse_args() + getsignalwin(args.t, args.t1, args.tsignal) diff --git a/pylot/core/pick/utils.py b/pylot/core/pick/utils.py index 9bd5dfc9..15de2312 100644 --- a/pylot/core/pick/utils.py +++ b/pylot/core/pick/utils.py @@ -11,25 +11,25 @@ import numpy as np import matplotlib.pyplot as plt from obspy.core import Stream -import argparse + def earllatepicker(X, nfac, TSNR, Pick1, iplot=None): ''' - Function to derive earliest and latest possible pick after Diehl & Kissling (2009) + Function to derive earliest and latest possible pick after Diehl & Kissling (2009) as reasonable uncertainties. Latest possible pick is based on noise level, - earliest possible pick is half a signal wavelength in front of most likely + earliest possible pick is half a signal wavelength in front of most likely pick given by PragPicker or manually set by analyst. Most likely pick - (initial pick Pick1) must be given. - + (initial pick Pick1) must be given. + :param: X, time series (seismogram) :type: `~obspy.core.stream.Stream` - + :param: nfac (noise factor), nfac times noise level to calculate latest possible pick :type: int :param: TSNR, length of time windows around pick used to determine SNR [s] :type: tuple (T_noise, T_gap, T_signal) - + :param: Pick1, initial (most likely) onset time, starting point for earllatepicker :type: float @@ -39,14 +39,15 @@ def earllatepicker(X, nfac, TSNR, Pick1, iplot=None): assert isinstance(X, Stream), "%s is not a stream object" % str(X) - LPick = None + LPick = None EPick = None PickError = None print 'earllatepicker: Get earliest and latest possible pick relative to most likely pick ...' x = X[0].data - t = np.arange(0, X[0].stats.npts / X[0].stats.sampling_rate, X[0].stats.delta) - #get latest possible pick + t = np.arange(0, X[0].stats.npts / X[0].stats.sampling_rate, + X[0].stats.delta) + # get latest possible pick #get noise window inoise = getnoisewin(t, Pick1, TSNR[0], TSNR[1]) #get signal window @@ -57,8 +58,8 @@ def earllatepicker(X, nfac, TSNR, Pick1, iplot=None): ilup = np.where(x[isignal] > nlevel) ildown = np.where(x[isignal] < -nlevel) if len(ilup[0]) <= 1 and len(ildown[0]) <= 1: - print 'earllatepicker: Signal lower than noise level, misspick?' - return + print 'earllatepicker: Signal lower than noise level, misspick?' + return il = min([ilup[0][0], ildown[0][0]]) LPick = t[isignal][il] @@ -68,72 +69,67 @@ def earllatepicker(X, nfac, TSNR, Pick1, iplot=None): zc = [] zc.append(Pick1) i = 0 - for j in range(isignal[0][1],isignal[0][len(t[isignal]) - 1]): - i = i+ 1 - if x[j-1] <= 0 and x[j] >= 0: - zc.append(t[isignal][i]) - elif x[j-1] > 0 and x[j] <= 0: - zc.append(t[isignal][i]) + for j in range(isignal[0][1], isignal[0][len(t[isignal]) - 1]): + i = i + 1 + if x[j - 1] <= 0 and x[j] >= 0: + zc.append(t[isignal][i]) + elif x[j - 1] > 0 and x[j] <= 0: + zc.append(t[isignal][i]) if len(zc) == 3: break #calculate maximum period T0 of signal out of zero crossings - T0 = max(np.diff(zc)) #this is half wave length! + T0 = max(np.diff(zc)) #this is half wave length! #T0/4 is assumed as time difference between most likely and earliest possible pick! - EPick = Pick1 - T0/2 + EPick = Pick1 - T0 / 2 #get symmetric pick error as mean from earliest and latest possible pick #by weighting latest possible pick two times earliest possible pick - diffti_tl = LPick - Pick1 + diffti_tl = LPick - Pick1 diffti_te = Pick1 - EPick - PickError = (diffti_te + 2 * diffti_tl) / 3 + PickError = (diffti_te + 2 * diffti_tl) / 3 if iplot is not None: - plt.figure(iplot) - p1, = plt.plot(t, x, 'k') - p2, = plt.plot(t[inoise], x[inoise]) - p3, = plt.plot(t[isignal], x[isignal], 'r') - p4, = plt.plot([t[0], t[int(len(t)) - 1]], [nlevel, nlevel], '--k') - p5, = plt.plot(zc, [0, 0, 0], '*g', markersize=14) - plt.legend([p1, p2, p3, p4, p5], ['Data', 'Noise Window', 'Signal Window', 'Noise Level', 'Zero Crossings'], \ + plt.figure(iplot) + p1, = plt.plot(t, x, 'k') + p2, = plt.plot(t[inoise], x[inoise]) + p3, = plt.plot(t[isignal], x[isignal], 'r') + p4, = plt.plot([t[0], t[int(len(t)) - 1]], [nlevel, nlevel], '--k') + p5, = plt.plot(zc, [0, 0, 0], '*g', markersize=14) + plt.legend([p1, p2, p3, p4, p5], + ['Data', 'Noise Window', 'Signal Window', 'Noise Level', + 'Zero Crossings'], \ loc='best') - plt.plot([t[0], t[int(len(t)) - 1]], [-nlevel, -nlevel], '--k') - plt.plot([Pick1, Pick1], [max(x), -max(x)], 'b', linewidth=2) - plt.plot([LPick, LPick], [max(x)/2, -max(x)/2], '--k') - plt.plot([EPick, EPick], [max(x)/2, -max(x)/2], '--k') - plt.plot([Pick1 + PickError, Pick1 + PickError], [max(x)/2, -max(x)/2], 'r--') - plt.plot([Pick1 - PickError, Pick1 - PickError], [max(x)/2, -max(x)/2], 'r--') - plt.xlabel('Time [s] since %s' % X[0].stats.starttime) - plt.yticks([]) - ax = plt.gca() - ax.set_xlim([t[inoise[0][0]] - 2, t[isignal[0][len(isignal) - 1]] + 3]) - plt.title('Earliest-/Latest Possible/Most Likely Pick & Symmetric Pick Error, %s' % X[0].stats.station) - plt.show() - raw_input() - plt.close(iplot) + plt.plot([t[0], t[int(len(t)) - 1]], [-nlevel, -nlevel], '--k') + plt.plot([Pick1, Pick1], [max(x), -max(x)], 'b', linewidth=2) + plt.plot([LPick, LPick], [max(x) / 2, -max(x) / 2], '--k') + plt.plot([EPick, EPick], [max(x) / 2, -max(x) / 2], '--k') + plt.plot([Pick1 + PickError, Pick1 + PickError], + [max(x) / 2, -max(x) / 2], 'r--') + plt.plot([Pick1 - PickError, Pick1 - PickError], + [max(x) / 2, -max(x) / 2], 'r--') + plt.xlabel('Time [s] since %s' % X[0].stats.starttime) + plt.yticks([]) + ax = plt.gca() + ax.set_xlim([t[inoise[0][0]] - 2, t[isignal[0][len(isignal) - 1]] + 3]) + plt.title( + 'Earliest-/Latest Possible/Most Likely Pick & Symmetric Pick Error, %s' % + X[0].stats.station) + plt.show() + raw_input() + plt.close(iplot) return EPick, LPick, PickError -if __name__ == "__main__": - parser = argparse.ArgumentParser() - parser.add_argument('--X', type=~obspy.core.stream.Stream, help='time series (seismogram) read with obspy module read') - parser.add_argument('--nfac', type=int, help='(noise factor), nfac times noise level to calculate latest possible pick') - parser.add_argument('--TSNR', type=tuple, help='length of time windows around pick used to determine SNR \ - [s] (Tnoise, Tgap, Tsignal)') - parser.add_argument('--Pick1', type=float, help='Onset time of most likely pick') - parser.add_argument('--iplot', type=int, help='if set, figure no. iplot occurs') - args = parser.parse_args() - earllatepicker(args.X, args.nfac, args.TSNR, args.Pick1, args.iplot) - def fmpicker(Xraw, Xfilt, pickwin, Pick, iplot=None): ''' Function to derive first motion (polarity) of given phase onset Pick. Calculation is based on zero crossings determined within time window pickwin - after given onset time. - + after given onset time. + :param: Xraw, unfiltered time series (seismogram) :type: `~obspy.core.stream.Stream` - + :param: Xfilt, filtered time series (seismogram) :type: `~obspy.core.stream.Stream` @@ -152,149 +148,146 @@ def fmpicker(Xraw, Xfilt, pickwin, Pick, iplot=None): FM = None if Pick is not None: - print 'fmpicker: Get first motion (polarity) of onset using unfiltered seismogram...' + print 'fmpicker: Get first motion (polarity) of onset using unfiltered seismogram...' - xraw = Xraw[0].data - xfilt = Xfilt[0].data - t = np.arange(0, Xraw[0].stats.npts / Xraw[0].stats.sampling_rate, Xraw[0].stats.delta) - #get pick window - ipick = np.where((t <= min([Pick + pickwin, len(Xraw[0])])) & (t >= Pick)) - #remove mean - xraw[ipick] = xraw[ipick] - np.mean(xraw[ipick]) - xfilt[ipick] = xfilt[ipick] - np.mean(xfilt[ipick]) + xraw = Xraw[0].data + xfilt = Xfilt[0].data + t = np.arange(0, Xraw[0].stats.npts / Xraw[0].stats.sampling_rate, + Xraw[0].stats.delta) + # get pick window + ipick = np.where( + (t <= min([Pick + pickwin, len(Xraw[0])])) & (t >= Pick)) + #remove mean + xraw[ipick] = xraw[ipick] - np.mean(xraw[ipick]) + xfilt[ipick] = xfilt[ipick] - np.mean(xfilt[ipick]) - #get next zero crossing after most likely pick - #initial onset is assumed to be the first zero crossing - #first from unfiltered trace - zc1 = [] - zc1.append(Pick) - index1 = [] - i = 0 - for j in range(ipick[0][1],ipick[0][len(t[ipick]) - 1]): - i = i+ 1 - if xraw[j-1] <= 0 and xraw[j] >= 0: - zc1.append(t[ipick][i]) - index1.append(i) - elif xraw[j-1] > 0 and xraw[j] <= 0: - zc1.append(t[ipick][i]) - index1.append(i) - if len(zc1) == 3: - break + #get next zero crossing after most likely pick + #initial onset is assumed to be the first zero crossing + #first from unfiltered trace + zc1 = [] + zc1.append(Pick) + index1 = [] + i = 0 + for j in range(ipick[0][1], ipick[0][len(t[ipick]) - 1]): + i = i + 1 + if xraw[j - 1] <= 0 and xraw[j] >= 0: + zc1.append(t[ipick][i]) + index1.append(i) + elif xraw[j - 1] > 0 and xraw[j] <= 0: + zc1.append(t[ipick][i]) + index1.append(i) + if len(zc1) == 3: + break - #if time difference betweeen 1st and 2cnd zero crossing - #is too short, get time difference between 1st and 3rd - #to derive maximum - if zc1[1] - zc1[0] <= Xraw[0].stats.delta: - li1 = index1[1] - else: - li1 = index1[0] - if np.size(xraw[ipick[0][1]:ipick[0][li1]]) == 0: - print 'earllatepicker: Onset on unfiltered trace too emergent for first motion determination!' - P1 = None - else: - imax1 = np.argmax(abs(xraw[ipick[0][1]:ipick[0][li1]])) - islope1 = np.where((t >= Pick) & (t <= Pick + t[imax1])) - #calculate slope as polynomal fit of order 1 - xslope1 = np.arange(0, len(xraw[islope1]), 1) - P1 = np.polyfit(xslope1, xraw[islope1], 1) - datafit1 = np.polyval(P1, xslope1) + #if time difference betweeen 1st and 2cnd zero crossing + #is too short, get time difference between 1st and 3rd + #to derive maximum + if zc1[1] - zc1[0] <= Xraw[0].stats.delta: + li1 = index1[1] + else: + li1 = index1[0] + if np.size(xraw[ipick[0][1]:ipick[0][li1]]) == 0: + print 'earllatepicker: Onset on unfiltered trace too emergent for first motion determination!' + P1 = None + else: + imax1 = np.argmax(abs(xraw[ipick[0][1]:ipick[0][li1]])) + islope1 = np.where((t >= Pick) & (t <= Pick + t[imax1])) + #calculate slope as polynomal fit of order 1 + xslope1 = np.arange(0, len(xraw[islope1]), 1) + P1 = np.polyfit(xslope1, xraw[islope1], 1) + datafit1 = np.polyval(P1, xslope1) - #now using filterd trace - #next zero crossing after most likely pick - zc2 = [] - zc2.append(Pick) - index2 = [] - i = 0 - for j in range(ipick[0][1],ipick[0][len(t[ipick]) - 1]): - i = i+ 1 - if xfilt[j-1] <= 0 and xfilt[j] >= 0: - zc2.append(t[ipick][i]) - index2.append(i) - elif xfilt[j-1] > 0 and xfilt[j] <= 0: - zc2.append(t[ipick][i]) - index2.append(i) - if len(zc2) == 3: - break + #now using filterd trace + #next zero crossing after most likely pick + zc2 = [] + zc2.append(Pick) + index2 = [] + i = 0 + for j in range(ipick[0][1], ipick[0][len(t[ipick]) - 1]): + i = i + 1 + if xfilt[j - 1] <= 0 and xfilt[j] >= 0: + zc2.append(t[ipick][i]) + index2.append(i) + elif xfilt[j - 1] > 0 and xfilt[j] <= 0: + zc2.append(t[ipick][i]) + index2.append(i) + if len(zc2) == 3: + break - #if time difference betweeen 1st and 2cnd zero crossing - #is too short, get time difference between 1st and 3rd - #to derive maximum - if zc2[1] - zc2[0] <= Xfilt[0].stats.delta: - li2 = index2[1] - else: - li2 = index2[0] - if np.size(xfilt[ipick[0][1]:ipick[0][li2]]) == 0: - print 'earllatepicker: Onset on filtered trace too emergent for first motion determination!' - P2 = None - else: - imax2 = np.argmax(abs(xfilt[ipick[0][1]:ipick[0][li2]])) - islope2 = np.where((t >= Pick) & (t <= Pick + t[imax2])) - #calculate slope as polynomal fit of order 1 - xslope2 = np.arange(0, len(xfilt[islope2]), 1) - P2 = np.polyfit(xslope2, xfilt[islope2], 1) - datafit2 = np.polyval(P2, xslope2) - - #compare results - if P1 is not None and P2 is not None: - if P1[0] < 0 and P2[0] < 0: - FM = 'D' - elif P1[0] >= 0 and P2[0] < 0: - FM = '-' - elif P1[0] < 0 and P2[0]>= 0: - FM = '-' - elif P1[0] > 0 and P2[0] > 0: - FM = 'U' - elif P1[0] <= 0 and P2[0] > 0: - FM = '+' - elif P1[0] > 0 and P2[0] <= 0: - FM = '+' + #if time difference betweeen 1st and 2cnd zero crossing + #is too short, get time difference between 1st and 3rd + #to derive maximum + if zc2[1] - zc2[0] <= Xfilt[0].stats.delta: + li2 = index2[1] + else: + li2 = index2[0] + if np.size(xfilt[ipick[0][1]:ipick[0][li2]]) == 0: + print 'earllatepicker: Onset on filtered trace too emergent for first motion determination!' + P2 = None + else: + imax2 = np.argmax(abs(xfilt[ipick[0][1]:ipick[0][li2]])) + islope2 = np.where((t >= Pick) & (t <= Pick + t[imax2])) + #calculate slope as polynomal fit of order 1 + xslope2 = np.arange(0, len(xfilt[islope2]), 1) + P2 = np.polyfit(xslope2, xfilt[islope2], 1) + datafit2 = np.polyval(P2, xslope2) + + #compare results + if P1 is not None and P2 is not None: + if P1[0] < 0 and P2[0] < 0: + FM = 'D' + elif P1[0] >= 0 and P2[0] < 0: + FM = '-' + elif P1[0] < 0 and P2[0] >= 0: + FM = '-' + elif P1[0] > 0 and P2[0] > 0: + FM = 'U' + elif P1[0] <= 0 and P2[0] > 0: + FM = '+' + elif P1[0] > 0 and P2[0] <= 0: + FM = '+' if iplot is not None: - plt.figure(iplot) - plt.subplot(2,1,1) - plt.plot(t, xraw, 'k') - p1, = plt.plot([Pick, Pick], [max(xraw), -max(xraw)], 'b', linewidth=2) - if P1 is not None: - p2, = plt.plot(t[islope1], xraw[islope1]) - p3, = plt.plot(zc1, np.zeros(len(zc1)), '*g', markersize=14) - p4, = plt.plot(t[islope1], datafit1, '--g', linewidth=2) - plt.legend([p1, p2, p3, p4], ['Pick', 'Slope Window', 'Zero Crossings', 'Slope'], \ - loc='best') - plt.text(Pick + 0.02, max(xraw) / 2, '%s' % FM, fontsize=14) - ax = plt.gca() - ax.set_xlim([t[islope1[0][0]] - 0.1, t[islope1[0][len(islope1) - 1]] + 0.3]) - plt.yticks([]) - plt.title('First-Motion Determination, %s, Unfiltered Data' % Xraw[0].stats.station) + plt.figure(iplot) + plt.subplot(2, 1, 1) + plt.plot(t, xraw, 'k') + p1, = plt.plot([Pick, Pick], [max(xraw), -max(xraw)], 'b', linewidth=2) + if P1 is not None: + p2, = plt.plot(t[islope1], xraw[islope1]) + p3, = plt.plot(zc1, np.zeros(len(zc1)), '*g', markersize=14) + p4, = plt.plot(t[islope1], datafit1, '--g', linewidth=2) + plt.legend([p1, p2, p3, p4], + ['Pick', 'Slope Window', 'Zero Crossings', 'Slope'], \ + loc='best') + plt.text(Pick + 0.02, max(xraw) / 2, '%s' % FM, fontsize=14) + ax = plt.gca() + ax.set_xlim( + [t[islope1[0][0]] - 0.1, t[islope1[0][len(islope1) - 1]] + 0.3]) + plt.yticks([]) + plt.title('First-Motion Determination, %s, Unfiltered Data' % Xraw[ + 0].stats.station) - plt.subplot(2,1,2) - plt.title('First-Motion Determination, Filtered Data') - plt.plot(t, xfilt, 'k') - p1, = plt.plot([Pick, Pick], [max(xfilt), -max(xfilt)], 'b', linewidth=2) - if P2 is not None: - p2, = plt.plot(t[islope2], xfilt[islope2]) - p3, = plt.plot(zc2, np.zeros(len(zc2)), '*g', markersize=14) - p4, = plt.plot(t[islope2], datafit2, '--g', linewidth=2) - plt.text(Pick + 0.02, max(xraw) / 2, '%s' % FM, fontsize=14) - ax = plt.gca() - ax.set_xlim([t[islope2[0][0]] - 0.1, t[islope2[0][len(islope2) - 1]] + 0.3]) - plt.xlabel('Time [s] since %s' % Xraw[0].stats.starttime) - plt.yticks([]) - plt.show() - raw_input() - plt.close(iplot) + plt.subplot(2, 1, 2) + plt.title('First-Motion Determination, Filtered Data') + plt.plot(t, xfilt, 'k') + p1, = plt.plot([Pick, Pick], [max(xfilt), -max(xfilt)], 'b', + linewidth=2) + if P2 is not None: + p2, = plt.plot(t[islope2], xfilt[islope2]) + p3, = plt.plot(zc2, np.zeros(len(zc2)), '*g', markersize=14) + p4, = plt.plot(t[islope2], datafit2, '--g', linewidth=2) + plt.text(Pick + 0.02, max(xraw) / 2, '%s' % FM, fontsize=14) + ax = plt.gca() + ax.set_xlim( + [t[islope2[0][0]] - 0.1, t[islope2[0][len(islope2) - 1]] + 0.3]) + plt.xlabel('Time [s] since %s' % Xraw[0].stats.starttime) + plt.yticks([]) + plt.show() + raw_input() + plt.close(iplot) return FM -if __name__ == "__main__": - parser = argparse.ArgumentParser() - parser.add_argument('--Xraw', type=~obspy.core.stream.Stream, help='unfiltered time series (seismogram) read with obspy module read') - parser.add_argument('--Xfilt', type=~obspy.core.stream.Stream, help='filtered time series (seismogram) read with obspy module read') - parser.add_argument('--pickwin', type=float, help='length of pick window [s] for first motion determination') - parser.add_argument('--Pick', type=float, help='Onset time of most likely pick') - parser.add_argument('--iplot', type=int, help='if set, figure no. iplot occurs') - args = parser.parse_args() - earllatepicker(args.Xraw, args.Xfilt, args.pickwin, args.Pick, args.iplot) - def getSNR(X, TSNR, t1): ''' @@ -314,35 +307,28 @@ def getSNR(X, TSNR, t1): assert isinstance(X, Stream), "%s is not a stream object" % str(X) - SNR = None - SNRdB = None x = X[0].data - t = np.arange(0, X[0].stats.npts / X[0].stats.sampling_rate, X[0].stats.delta) - #get noise window + t = np.arange(0, X[0].stats.npts / X[0].stats.sampling_rate, + X[0].stats.delta) + + # get noise window inoise = getnoisewin(t, t1, TSNR[0], TSNR[1]) + #get signal window isignal = getsignalwin(t, t1, TSNR[2]) if np.size(inoise) < 1: - print 'getSNR: Empty array inoise, check noise window!' - return + print 'getSNR: Empty array inoise, check noise window!' + return elif np.size(isignal) < 1: - print 'getSNR: Empty array isignal, check signal window!' - return + print 'getSNR: Empty array isignal, check signal window!' + return #calculate ratios - SNR = max(abs(x[isignal])) / np.mean(abs(x[inoise])) + noiselevel = np.mean(abs(x[inoise])) + SNR = max(abs(x[isignal])) / noiselevel SNRdB = 20 * np.log10(SNR) - - return SNR, SNRdB -if __name__ == "__main__": - parser = argparse.ArgumentParser() - parser.add_argument('--X', type=~obspy.core.stream.Stream, help='time series (seismogram) read with obspy module read') - parser.add_argument('--TSNR', type=tuple, help='length of time windows around pick used to determine SNR \ - [s] (Tnoise, Tgap, Tsignal)') - parser.add_argument('--t1', type=float, help='initial time from which noise and signal windows are calculated') - args = parser.parse_args() - getSNR(args.X, args.TSNR, args.t1) + return SNR, SNRdB, noiselevel def getnoisewin(t, t1, tnoise, tgap): @@ -352,7 +338,7 @@ def getnoisewin(t, t1, tnoise, tgap): :param: t, array of time stamps :type: numpy array - + :param: t1, time from which relativ to it noise window is extracted :type: float @@ -361,26 +347,18 @@ def getnoisewin(t, t1, tnoise, tgap): :param: tgap, safety gap between t1 (onset) and noise window to ensure, that noise window contains no signal - :type: float + :type: float ''' - + inoise = None - #get noise window + # get noise window inoise = np.where((t <= max([t1 - tgap, 0])) \ - & (t >= max([t1 - tnoise - tgap, 0]))) + & (t >= max([t1 - tnoise - tgap, 0]))) if np.size(inoise) < 1: - print 'getnoisewin: Empty array inoise, check noise window!' - + print 'getnoisewin: Empty array inoise, check noise window!' + return inoise -if __name__ == "__main__": - parser = argparse.ArgumentParser() - parser.add_argument('--t', type=array, help='numpy array of time stamps') - parser.add_argument('--t1', type=float, help='time from which relativ to it noise window is extracted') - parser.add_argument('--tnoise', type=float, help='length of time window [s] for noise part extraction') - parser.add_argument('--tgap', type=float, help='safety gap between signal (t1=onset) and noise') - args = parser.parse_args() - getnoisewin(args.t, args.t1, args.tnoise, args.tgap) def getsignalwin(t, t1, tsignal): ''' @@ -389,27 +367,21 @@ def getsignalwin(t, t1, tsignal): :param: t, array of time stamps :type: numpy array - + :param: t1, time from which relativ to it signal window is extracted :type: float :param: tsignal, length of time window [s] for signal level calculation :type: float ''' - + inoise = None - #get signal window + # get signal window isignal = np.where((t <= min([t1 + tsignal, len(t)])) \ - & (t >= t1)) + & (t >= t1)) if np.size(isignal) < 1: - print 'getsignalwin: Empty array isignal, check signal window!' - + print 'getsignalwin: Empty array isignal, check signal window!' + return isignal - -if __name__ == "__main__": - parser = argparse.ArgumentParser() - parser.add_argument('--t', type=array, help='numpy array of time stamps') - parser.add_argument('--t1', type=float, help='time from which relativ to it signal window is extracted') - parser.add_argument('--tsignal', type=float, help='length of time window [s] for signal part extraction') - args = parser.parse_args() - getsignalwin(args.t, args.t1, args.tsignal) + + From e35d6b9371a05b2727f620f16a291da0c07de2f5 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Thu, 2 Apr 2015 18:36:21 +0200 Subject: [PATCH 2/7] bugfix: modified icon management and corrected usage of references --- QtPyLoT.py | 33 +++++++++++++++++++++++---------- resources.qrc => icons.qrc | 1 + icons/filter.png | Bin 0 -> 4700 bytes icons_rc.py | 21 +++++++++++++++++++++ pylot/core/util/widgets.py | 6 +++++- qrc_resources.py | 21 --------------------- 6 files changed, 50 insertions(+), 32 deletions(-) rename resources.qrc => icons.qrc (88%) create mode 100644 icons/filter.png create mode 100644 icons_rc.py delete mode 100644 qrc_resources.py diff --git a/QtPyLoT.py b/QtPyLoT.py index 909cf8fc..24d55da8 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -29,7 +29,7 @@ from PySide.QtCore import QCoreApplication, QSettings, Signal, QFile, \ QFileInfo, Qt from PySide.QtGui import QMainWindow, QInputDialog, QIcon, QFileDialog, \ QWidget, QHBoxLayout, QStyle, QKeySequence, QLabel, QFrame, QAction, \ - QDialog, QErrorMessage, QApplication + QDialog, QErrorMessage, QApplication, QPixmap from obspy.core import UTCDateTime from pylot.core.read import Data, FilterOptions @@ -38,12 +38,11 @@ from pylot.core.util import _getVersionString, FILTERDEFAULTS, fnConstructor, \ NewEventDlg, createEvent, MPLWidget, PropertiesDlg, HelpForm, \ DatastructureError, createAction, getLogin, createCreationInfo, PickDlg from pylot.core.util.structure import DATASTRUCTURE -import qrc_resources +import icons_rc # Version information __version__ = _getVersionString() - class MainWindow(QMainWindow): closing = Signal() @@ -101,8 +100,11 @@ class MainWindow(QMainWindow): except: self.startTime = UTCDateTime() + pylot_icon = QIcon() + pylot_icon.addPixmap(QPixmap(':/icons/pylot.ico')) + self.setWindowTitle("PyLoT - do seismic processing the python way") - self.setWindowIcon(QIcon(":/icon.ico")) + self.setWindowIcon(pylot_icon) xlab = self.startTime.strftime('seconds since %Y/%m/%d %H:%M:%S (%Z)') @@ -123,7 +125,15 @@ class MainWindow(QMainWindow): saveIcon = self.style().standardIcon(QStyle.SP_DriveHDIcon) helpIcon = self.style().standardIcon(QStyle.SP_DialogHelpButton) newIcon = self.style().standardIcon(QStyle.SP_FileIcon) - pickIcon = QIcon(':/pick.png') + + # create resource icons + p_icon = QIcon() + p_icon.addPixmap(QPixmap(':/icons/picon.png')) + s_icon = QIcon() + s_icon.addPixmap(QPixmap(':/icons/sicon.png')) + print_icon = QIcon() + print_icon.addPixmap(QPixmap(':/icons/printer.png')) + newEventAction = self.createAction(self, "&New event ...", self.createNewEvent, QKeySequence.New, newIcon, @@ -163,14 +173,14 @@ class MainWindow(QMainWindow): "Alt+F", QIcon(None), """Adjust filter parameters.""") self.selectPAction = self.createAction(self, "&P", self.alterPhase, "Alt+P", - QIcon(":/picon.png"), + p_icon, "Toggle P phase.", True) self.selectSAction = self.createAction(self, "&S", self.alterPhase, "Alt+S", - QIcon(":/sicon.png"), + s_icon, "Toggle S phase", True) printAction = self.createAction(self, "&Print event ...", self.printEvent, QKeySequence.Print, - QIcon(":/printer.png"), + print_icon, "Print waveform overview.") helpAction = self.createAction(self, "&Help ...", self.helpHelp, QKeySequence.HelpContents, helpIcon, @@ -517,13 +527,16 @@ class MainWindow(QMainWindow): def main(): # create the Qt application - pylot_app = QApplication(sys.argv[0]) + pylot_app = QApplication(sys.argv) + + app_icon = QIcon() + app_icon.addPixmap(QPixmap(':/icons/pick.png')) # set Application Information pylot_app.setOrganizationName("Ruhr-University Bochum / MAGS2") pylot_app.setOrganizationDomain("rub.de") pylot_app.setApplicationName("PyLoT") - pylot_app.setWindowIcon(QIcon(":/icon.ico")) + pylot_app.setWindowIcon(app_icon) # create the main window pylot_form = MainWindow() diff --git a/resources.qrc b/icons.qrc similarity index 88% rename from resources.qrc rename to icons.qrc index c15c7560..7ec95b9c 100644 --- a/resources.qrc +++ b/icons.qrc @@ -5,6 +5,7 @@ icons/picon.png icons/sicon.png icons/pick.png + icons/filter.png help/index.html diff --git a/icons/filter.png b/icons/filter.png new file mode 100644 index 0000000000000000000000000000000000000000..1098d3ac27a2c86136b60e2fb5e1b5e49bac6dfe GIT binary patch literal 4700 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 eLE_>60t^5tGhZa2hdEpT0000 Date: Thu, 2 Apr 2015 18:48:06 +0200 Subject: [PATCH 3/7] task: implementation of picking (work in progress) --- QtPyLoT.py | 25 +++-- pylot/core/util/widgets.py | 188 ++++++++++++++++++++++++++++++------- 2 files changed, 164 insertions(+), 49 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index 24d55da8..87e49811 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -150,10 +150,6 @@ class MainWindow(QMainWindow): "Ctrl+W", QIcon(":/wfIcon.png"), """Open waveform data (event will be closed).""") - selectStation = self.createAction(self, "Select station", - self.pickOnStation, "Alt+P", pickIcon, - "Select a station from overview " - "plot for picking") prefsEventAction = self.createAction(self, "Preferences", self.PyLoTprefs, QKeySequence.Preferences, @@ -214,10 +210,10 @@ class MainWindow(QMainWindow): phaseToolBar.setObjectName("PhaseTools") self.addActions(phaseToolBar, phaseToolActions) - pickToolBar = self.addToolBar("PickTools") - pickToolActions = (selectStation, ) - pickToolBar.setObjectName("PickTools") - self.addActions(pickToolBar, pickToolActions) + # pickToolBar = self.addToolBar("PickTools") + # pickToolActions = (selectStation, ) + # pickToolBar.setObjectName("PickTools") + # self.addActions(pickToolBar, pickToolActions) self.eventLabel = QLabel() self.eventLabel.setFrameStyle(QFrame.StyledPanel | QFrame.Sunken) @@ -346,9 +342,9 @@ class MainWindow(QMainWindow): def getPlotWidget(self): return self.DataPlot - def getWFID(self, event): + def getWFID(self, gui_event): - ycoord = event.ydata + ycoord = gui_event.ydata statID = int(round(ycoord)) @@ -380,6 +376,9 @@ class MainWindow(QMainWindow): title = 'overview: {0} components'.format(zne_text[comp]) wfst = self.getData().getWFData().select(component=comp) self.getPlotWidget().plotWFData(wfdata=wfst, title=title) + pos = self.getPlotWidget().getPlotDict().keys() + labels = [int(act) for act in pos] + self.getPlotWidget().setYTickLabels(pos, labels) def filterWaveformData(self): if self.getData(): @@ -454,7 +453,7 @@ class MainWindow(QMainWindow): return self.seismicPhase def getStationName(self, wfID): - return self.getPlotWidget().getPlotDict()[wfID] + return self.getPlotWidget().getPlotDict()[wfID][0] def alterPhase(self): pass @@ -464,9 +463,9 @@ class MainWindow(QMainWindow): self.updateStatus('Seismic phase changed to ' '{0}'.format(self.getSeismicPhase())) - def pickOnStation(self, event): + def pickOnStation(self, gui_event): - wfID = self.getWFID(event) + wfID = self.getWFID(gui_event) station = self.getStationName(wfID) print 'picking on station {0}'.format(station) diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index 3c91fc4d..25889981 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -15,33 +15,16 @@ matplotlib.rcParams['backend.qt4'] = 'PySide' from matplotlib.figure import Figure from matplotlib.backends.backend_qt4agg import FigureCanvas from matplotlib.widgets import MultiCursor -from PySide.QtGui import (QAction, - QApplication, - QComboBox, - QDateTimeEdit, - QDialog, - QDialogButtonBox, - QDoubleSpinBox, - QGroupBox, - QGridLayout, - QHBoxLayout, - QIcon, - QKeySequence, - QLabel, - QLineEdit, - QMessageBox, - QSpinBox, - QTabWidget, - QToolBar, - QVBoxLayout, - QWidget) -from PySide.QtCore import (QSettings, - Qt, - QUrl, - Signal, - Slot) +from PySide.QtGui import QAction, QApplication,QComboBox, QDateTimeEdit,\ + QDialog, QDialogButtonBox, QDoubleSpinBox, QGroupBox, QGridLayout,\ + QIcon, QKeySequence, QLabel, QLineEdit, QMessageBox, QPixmap, QSpinBox,\ + QTabWidget, QToolBar, QVBoxLayout, QWidget +from PySide.QtCore import QSettings, Qt, QUrl, Signal, Slot from PySide.QtWebKit import QWebView +from obspy import Stream, UTCDateTime +from obspy.core.event import Pick from pylot.core.read import FilterOptions +from pylot.core.pick.utils import getSNR from pylot.core.util.defaults import OUTPUTFORMATS from pylot.core.util import prepTimeAxis, getGlobalTimes @@ -91,39 +74,58 @@ class MPLWidget(FigureCanvas): def setPlotDict(self, key, value): self.plotdict[key] = value + def clearPlotDict(self): + self.plotdict = dict() + def getParent(self): return self._parent def setParent(self, parent): self._parent = parent - def plotWFData(self, wfdata, title = None): - self.axes.lines = [] + def plotWFData(self, wfdata, title=None, zoomx=None, zoomy=None): + self.axes.cla() + self.clearPlotDict() wfstart = getGlobalTimes(wfdata)[0] for n, trace in enumerate(wfdata): + channel = trace.stats.channel station = trace.stats.station - print('plotting station: %s' % station) + msg = 'plotting %s channel of station %s' % (channel, station) + print(msg) 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.setPlotDict(n, (station, channel)) self.axes.autoscale(tight=True) + if zoomx: + self.axes.set_xlim(zoomx) + if zoomy: + self.axes.set_ylim(zoomy) + self.draw() + + def setYTickLabels(self, pos, labels): + self.axes.set_yticks(pos) + self.axes.set_yticklabels(labels) + self.draw() def updateXLabel(self, text): self.axes.set_xlabel(text) + self.draw() + def updateYLabel(self, text): self.axes.set_ylabel(text) + self.draw() def updateTitle(self, text): self.axes.set_title(text) + self.draw() def updateWidget(self, xlabel, ylabel, title): self.updateXLabel(xlabel) @@ -237,6 +239,7 @@ class PickDlg(QDialog): self.station = station self.rotate = rotate self.components = 'ZNE' + self.picks = {} # set attribute holding data if data is None: @@ -260,6 +263,12 @@ class PickDlg(QDialog): self.getPlotWidget().plotWFData(wfdata=self.getWFData(), title=self.getStation()) + # set plot labels + self.setPlotLabels() + + # connect button press event to an action + self.cid = self.getPlotWidget().mpl_connect('button_press_event', self.setPick) + def setupUi(self): # create icons @@ -284,19 +293,22 @@ class PickDlg(QDialog): _dialtoolbar.addAction(self.filterAction) _dialtoolbar.addWidget(self.selectPhase) - _innerlayout = QHBoxLayout() + _innerlayout = QVBoxLayout() - _toolslayout = QVBoxLayout() - _toolslabel = QLabel('Place for Tools') - _toolslayout.addWidget(_toolslabel) - - _innerlayout.addLayout(_toolslayout) _innerlayout.addWidget(self.multicompfig) + _buttonbox = QDialogButtonBox(QDialogButtonBox.Apply | + QDialogButtonBox.Ok | + QDialogButtonBox.Cancel) + + _innerlayout.addWidget(_buttonbox) _outerlayout.addWidget(_dialtoolbar) _outerlayout.addLayout(_innerlayout) self.setLayout(_outerlayout) + def reconnect(self, event_name, slot): + self.getPlotWidget().mpl_disconnect(self.cid) + self.cid = self.getPlotWidget().mpl_connect(event_name, slot) def getComponents(self): return self.components @@ -307,14 +319,118 @@ class PickDlg(QDialog): def getPlotWidget(self): return self.multicompfig + def getChannelID(self, key): + return self.getPlotWidget().getPlotDict()[int(key)][1] + def getWFData(self): return self.data + def selectWFData(self, channel): + component = channel[-1].upper() + wfdata = Stream() + def selectTrace(trace, components): + if trace.stats.channel[-1].upper() in components: + return trace + + if component == 'E' or component == 'N': + for trace in self.getWFData(): + trace = selectTrace(trace, 'NE') + if trace: + wfdata.append(trace) + elif component == 'Z': + wfdata = self.getWFData().select(component=component) + return wfdata + + def getPicks(self): + return self.picks + + def setPick(self, gui_event): + channel = self.getChannelID(round(gui_event.ydata)) + wfdata = self.selectWFData(channel) + + ini_pick = gui_event.xdata + + # calculate the resolution window width from SNR + # SNR >= 3 -> 2 sec HRW + # 3 > SNR >= 2 -> 5 sec MRW + # 2 > SNR >= 1.5 -> 10 sec LRW + # 1.5 > SNR -> 15 sec VLRW + + res_wins = { + 'HRW' : 2., + 'MRW' : 5., + 'LRW' : 10., + 'VLRW' : 15. + } + + result = getSNR(wfdata, (5,.5,1), ini_pick) + + snr = result[0] + noiselevel = result[2] * 1.5 + + if snr < 1.5: + x_res = res_wins['VLRW'] + elif snr < 2.: + x_res = res_wins['LRW'] + elif snr < 3.: + x_res = res_wins['MRW'] + else: + x_res = res_wins['HRW'] + x_res /= 2 + + zoomx = [ini_pick - x_res, ini_pick + x_res] + zoomy = [noiselevel * 1.5, -noiselevel * 1.5] + self.getPlotWidget().plotWFData(wfdata=wfdata, + title=self.getStation() + + ' picking mode', + zoomx=zoomx, + zoomy=zoomy) + + self.getPlotWidget().axes.plot() + + # reset labels + self.setPlotLabels() + + self.reconnect('button_press_event', self.plotPick) + + def plotPick(self, gui_event): + pick = gui_event.xdata + ax = self.getPlotWidget().axes + + ylims = ax.get_ylim() + + ax.plot([pick, pick], ylims, 'r--') + self.getPlotWidget().draw() + def filterWFData(self): data = self.getWFData().copy().filter(type='bandpass', freqmin=.5, freqmax=15.) title = self.getStation() + ' (filtered)' self.getPlotWidget().plotWFData(wfdata=data, title=title) + self.setPlotLabels() + def setPlotLabels(self): + + # get channel labels + pos = self.getPlotWidget().getPlotDict().keys() + labels = [self.getPlotWidget().getPlotDict()[key][1] for key in pos] + + # set channel labels + self.getPlotWidget().setYTickLabels(pos, labels) + + def apply(self): + if self.getPicks(): + for phase, time in self.getPicks().iteritems(): + ope_pick = Pick() + ope_pick.time = time + ope_pick.phase_hint = phase + print ope_pick + + def reject(self): + QDialog.reject(self) + + def accept(self): + self.apply() + QDialog.accept(self) class PropertiesDlg(QDialog): From 043c45e02cf018c1aa92bb3a81306697da2a7730 Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Mon, 6 Apr 2015 11:42:21 +0200 Subject: [PATCH 4/7] bugfix: undo filtering when checkbox is unchecked code improvement: class PickDlg -> distinguish between setting the initial pick (for zooming) and setting the actual pick (phase onset); methods renamed -> setPick is now setIniPick and plotPick became setPick --- QtPyLoT.py | 1 + pylot/core/util/widgets.py | 47 +++++++++++++++++++++++++------------- 2 files changed, 32 insertions(+), 16 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index 87e49811..5cb074be 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -376,6 +376,7 @@ class MainWindow(QMainWindow): title = 'overview: {0} components'.format(zne_text[comp]) wfst = self.getData().getWFData().select(component=comp) self.getPlotWidget().plotWFData(wfdata=wfst, title=title) + self.getPlotWidget().draw() pos = self.getPlotWidget().getPlotDict().keys() labels = [int(act) for act in pos] self.getPlotWidget().setYTickLabels(pos, labels) diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index 25889981..2ce0cd49 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -22,7 +22,7 @@ from PySide.QtGui import QAction, QApplication,QComboBox, QDateTimeEdit,\ from PySide.QtCore import QSettings, Qt, QUrl, Signal, Slot from PySide.QtWebKit import QWebView from obspy import Stream, UTCDateTime -from obspy.core.event import Pick +from obspy.core.event import Pick, WaveformStreamID from pylot.core.read import FilterOptions from pylot.core.pick.utils import getSNR from pylot.core.util.defaults import OUTPUTFORMATS @@ -262,12 +262,13 @@ class PickDlg(QDialog): # plot data self.getPlotWidget().plotWFData(wfdata=self.getWFData(), title=self.getStation()) + self.apd = self.getWFData() # set plot labels self.setPlotLabels() # connect button press event to an action - self.cid = self.getPlotWidget().mpl_connect('button_press_event', self.setPick) + self.cid = self.getPlotWidget().mpl_connect('button_press_event', self.setIniPick) def setupUi(self): @@ -279,7 +280,8 @@ class PickDlg(QDialog): self.filterAction = createAction(parent=self, text='Filter', slot=self.filterWFData, icon=filter_icon, - tip='Filter waveforms', + tip='Toggle filtered/original' + ' waveforms', checkable=True) self.selectPhase = QComboBox() self.selectPhase.addItems(['Pn', 'Pg', 'P1', 'P2']) @@ -344,7 +346,13 @@ class PickDlg(QDialog): def getPicks(self): return self.picks - def setPick(self, gui_event): + def getAPD(self): + return self.apd + + def updateAPD(self, wfdata): + self.apd = wfdata + + def setIniPick(self, gui_event): channel = self.getChannelID(round(gui_event.ydata)) wfdata = self.selectWFData(channel) @@ -363,7 +371,7 @@ class PickDlg(QDialog): 'VLRW' : 15. } - result = getSNR(wfdata, (5,.5,1), ini_pick) + result = getSNR(wfdata, (5, .5, 1), ini_pick) snr = result[0] noiselevel = result[2] * 1.5 @@ -385,15 +393,16 @@ class PickDlg(QDialog): ' picking mode', zoomx=zoomx, zoomy=zoomy) + self.updateAPD(wfdata) self.getPlotWidget().axes.plot() # reset labels self.setPlotLabels() - self.reconnect('button_press_event', self.plotPick) + self.reconnect('button_press_event', self.setPick) - def plotPick(self, gui_event): + def setPick(self, gui_event): pick = gui_event.xdata ax = self.getPlotWidget().axes @@ -403,9 +412,18 @@ class PickDlg(QDialog): self.getPlotWidget().draw() def filterWFData(self): - data = self.getWFData().copy().filter(type='bandpass', freqmin=.5, freqmax=15.) - title = self.getStation() + ' (filtered)' - self.getPlotWidget().plotWFData(wfdata=data, title=title) + ax = self.getPlotWidget().axes + ylims = ax.get_ylim() + xlims = ax.get_xlim() + if self.filterAction.isChecked(): + data = self.getAPD().copy() + data.filter(type='bandpass', freqmin=.5, freqmax=15.) + title = self.getStation() + ' (filtered)' + else: + data = self.getAPD().copy() + title = self.getStation() + self.getPlotWidget().plotWFData(wfdata=data, title=title, zoomx=xlims, + zoomy=ylims) self.setPlotLabels() def setPlotLabels(self): @@ -418,12 +436,9 @@ class PickDlg(QDialog): self.getPlotWidget().setYTickLabels(pos, labels) def apply(self): - if self.getPicks(): - for phase, time in self.getPicks().iteritems(): - ope_pick = Pick() - ope_pick.time = time - ope_pick.phase_hint = phase - print ope_pick + picks = self.getPicks() + for pick in picks: + print pick def reject(self): QDialog.reject(self) From d21798f633ed5508878605ea4349227e7ea9a0ef Mon Sep 17 00:00:00 2001 From: Sebastian Wehling-Benatelli Date: Tue, 7 Apr 2015 15:00:00 +0200 Subject: [PATCH 5/7] test pickingdialog and prepare figures for the poster --- icons.qrc | 1 + icons/zoom.png | Bin 0 -> 7635 bytes icons_rc.py | 8 ++-- pylot/RELEASE-VERSION | 2 +- pylot/core/util/widgets.py | 84 +++++++++++++++++++++++++++++++++---- 5 files changed, 83 insertions(+), 12 deletions(-) create mode 100755 icons/zoom.png diff --git a/icons.qrc b/icons.qrc index 7ec95b9c..4f9c34a9 100644 --- a/icons.qrc +++ b/icons.qrc @@ -6,6 +6,7 @@ icons/sicon.png icons/pick.png icons/filter.png + icons/zoom.png help/index.html diff --git a/icons/zoom.png b/icons/zoom.png new file mode 100755 index 0000000000000000000000000000000000000000..d8faba4a0f4ac9cdda160815df8a20d55bf11aaa GIT binary patch literal 7635 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 Date: Tue, 7 Apr 2015 15:00:53 +0200 Subject: [PATCH 6/7] new zoom icon added --- icons/zoom_minus.png | Bin 0 -> 7037 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100755 icons/zoom_minus.png diff --git a/icons/zoom_minus.png b/icons/zoom_minus.png new file mode 100755 index 0000000000000000000000000000000000000000..5207ae7758e76f2a76eeba58fbdd6aff7081098a GIT binary patch literal 7037 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 bqV_qt9w1`}BjVb1@zX Date: Thu, 9 Apr 2015 08:49:11 +0200 Subject: [PATCH 7/7] bugfix: zooming with scroll wheel now working properly task: panning now available while in exploration mode, if a phase is selected initial pick can be set and afterwards the onset itself (procedure questionable as two picks are needed) panning not tested yet! --- pylot/core/util/widgets.py | 107 +++++++++++++++++++++++++++++-------- 1 file changed, 86 insertions(+), 21 deletions(-) diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index 904011c5..1d62c65e 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -243,6 +243,11 @@ class PickDlg(QDialog): self.components = 'ZNE' self.picks = {} + # initialize panning attributes + self.press = None + self.xpress = None + self.ypress = None + # set attribute holding data if data is None: try: @@ -269,12 +274,14 @@ class PickDlg(QDialog): self.apd = self.getWFData() # set plot labels + self.setPlotLabels() # connect button press event to an action - self.cidpress = self.connectPressEvent(self.setIniPick) - self.cidscroll = self.getPlotWidget().mpl_connect('scroll_event', - self.scrollZoom) + self.cidpress = self.connectPressEvent(self.panPress) + self.cidmotion = self.connectMotionEvent() + self.cidrelease = self.connectReleaseEvent() + self.cidscroll = self.connectScrollEvent() def setupUi(self): @@ -315,7 +322,6 @@ class PickDlg(QDialog): _dialtoolbar.addAction(self.filterAction) _dialtoolbar.addWidget(self.selectPhase) - #_dialtoolbar.addAction(self.zoomAction) _innerlayout = QVBoxLayout() @@ -328,6 +334,9 @@ class PickDlg(QDialog): _outerlayout.addWidget(_dialtoolbar) _outerlayout.addLayout(_innerlayout) + + self.selectPhase.currentIndexChanged.connect(self.verifyPhaseSelection) + self.setLayout(_outerlayout) def disconnectPressEvent(self): @@ -335,11 +344,43 @@ class PickDlg(QDialog): def connectPressEvent(self, slot): widget = self.getPlotWidget() - self.cidpress = widget.mpl_connect('button_press_event', slot) + return widget.mpl_connect('button_press_event', slot) def reconnectPressEvent(self, slot): self.disconnectPressEvent() - self.cidpress = self.connectPressEvent(slot) + return self.connectPressEvent(slot) + + def disconnectScrollEvent(self): + widget = self.getPlotWidget() + widget.mpl_disconnect(self.cidscroll) + + def connectScrollEvent(self): + widget = self.getPlotWidget() + return widget.mpl_connect('scroll_event', self.scrollZoom) + + def disconnectMotionEvent(self): + widget = self.getPlotWidget() + widget.mpl_disconnect(self.cidmotion) + + def connectMotionEvent(self): + widget = self.getPlotWidget() + return widget.mpl_connect('motion_notify_event', self.panMotion) + + def disconnectReleaseEvent(self): + widget = self.getPlotWidget() + widget.mpl_disconnect(self.cidrelease) + + def connectReleaseEvent(self): + widget = self.getPlotWidget() + return widget.mpl_connect('button_release_event', self.panRelease) + + def verifyPhaseSelection(self): + phase = self.selectPhase.currentText() + if phase: + self.disconnectReleaseEvent() + self.disconnectScrollEvent() + self.disconnectMotionEvent() + self.reconnectPressEvent(self.setIniPick) def getComponents(self): return self.components @@ -385,7 +426,9 @@ class PickDlg(QDialog): channel = self.getChannelID(round(gui_event.ydata)) wfdata = self.selectWFData(channel) - self.getPlotWidget().mpl_disconnect(self.cidscroll) + self.disconnectScrollEvent() + + self.cidpress = self.reconnectPressEvent(self.setPick) ini_pick = gui_event.xdata @@ -394,6 +437,7 @@ class PickDlg(QDialog): # 3 > SNR >= 2 -> 5 sec MRW # 2 > SNR >= 1.5 -> 10 sec LRW # 1.5 > SNR -> 15 sec VLRW + # see also Diehl et al. 2009 res_wins = { 'HRW' : 2., @@ -426,13 +470,9 @@ class PickDlg(QDialog): zoomy=zoomy) self.updateAPD(wfdata) - self.getPlotWidget().axes.plot() - # reset labels self.setPlotLabels() - self.reconnectPressEvent(self.setPick) - def setPick(self, gui_event): pick = gui_event.xdata ax = self.getPlotWidget().axes @@ -442,6 +482,32 @@ class PickDlg(QDialog): ax.plot([pick, pick], ylims, 'r--') self.getPlotWidget().draw() + def panPress(self, gui_event): + ax = self.getPlotWidget().axes + if gui_event.inaxes != ax: return + self.cur_xlim = ax.get_xlim() + self.cur_ylim = ax.get_ylim() + self.press = gui_event.xdata, gui_event.ydata + self.xpress, self.ypress = self.press + + def panRelease(self, gui_event): + ax = self.getPlotWidget().axes + self.press = None + ax.figure.canvas.draw() + + def panMotion(self, gui_event): + ax = self.getPlotWidget().axes + if self.press is None: return + if gui_event.inaxes != ax: return + dx = gui_event.xdata - self.xpress + dy = gui_event.ydata - self.ypress + self.cur_xlim -= dx + self.cur_ylim -= dy + ax.set_xlim(self.cur_xlim) + ax.set_ylim(self.cur_ylim) + + ax.figure.canvas.draw() + def filterWFData(self): ax = self.getPlotWidget().axes ylims = ax.get_ylim() @@ -473,7 +539,7 @@ class PickDlg(QDialog): else: self.connectPressEvent(self.setIniPick) - def scrollZoom(self, gui_event, factor=1.2): + def scrollZoom(self, gui_event, factor=2.): widget = self.getPlotWidget() @@ -490,16 +556,15 @@ class PickDlg(QDialog): scale_factor = 1 print gui_event.button - act_width = (curr_xlim[1]-curr_xlim[0])*.5 - act_height = (curr_ylim[1]-curr_ylim[0])*.5 + new_xlim = gui_event.xdata - scale_factor * (gui_event.xdata - curr_xlim) + new_ylim = gui_event.ydata - scale_factor * (gui_event.ydata - curr_ylim) - new_width = act_width*scale_factor - new_height= act_height*scale_factor - - new_xlim = [max(gui_event.xdata - new_width, self.limits['xlims'][0]), - min(gui_event.xdata + new_width, self.limits['xlims'][1])] - new_ylim = [max(gui_event.ydata - new_height, self.limits['ylims'][0]), - min(gui_event.ydata + new_height, self.limits['ylims'][1])] + new_xlim.sort() + new_xlim[0] = max(new_xlim[0], self.limits['xlims'][0]) + new_xlim[1] = min(new_xlim[1], self.limits['xlims'][1]) + new_ylim.sort() + new_ylim[0] = max(new_ylim[0], self.limits['ylims'][0]) + new_ylim[1] = min(new_ylim[1], self.limits['ylims'][1]) widget.axes.set_xlim(new_xlim) widget.axes.set_ylim(new_ylim)