From 71876638c8811743f81868f0b1cd722c5d10195c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Thu, 22 Jun 2017 15:04:16 +0200 Subject: [PATCH 01/35] Flexible calculation of local magnitude including station magntiude scaling as well as network magnitude scaling. auoPyLot-output-file names have prefix event ID. --- autoPyLoT.py | 38 +++++++++++++++----------- pylot/RELEASE-VERSION | 2 +- pylot/core/analysis/magnitude.py | 41 ++++++++++++++++++----------- pylot/core/io/default_parameters.py | 10 ++++--- 4 files changed, 55 insertions(+), 36 deletions(-) diff --git a/autoPyLoT.py b/autoPyLoT.py index 66db76a6..db3a83f6 100755 --- a/autoPyLoT.py +++ b/autoPyLoT.py @@ -18,7 +18,7 @@ import pylot.core.loc.nll as nll #from PySide.QtGui import QWidget, QInputDialog from pylot.core.analysis.magnitude import MomentMagnitude, LocalMagnitude from pylot.core.io.data import Data -from pylot.core.io.inputs import PylotParameter +from pylot.core.io.inputs import AutoPickParameter from pylot.core.pick.autopick import autopickevent, iteratepicker from pylot.core.util.dataprocessing import restitute_data, read_metadata, \ remove_underscores @@ -35,7 +35,7 @@ def autoPyLoT(input_dict=None, parameter=None, inputfile=None, fnames=None, even :param inputfile: path to the input file containing all parameter information for automatic picking (for formatting details, see. - `~pylot.core.io.inputs.PylotParameter` + `~pylot.core.io.inputs.AutoPickParameter` :type inputfile: str :return: @@ -71,13 +71,13 @@ def autoPyLoT(input_dict=None, parameter=None, inputfile=None, fnames=None, even if not parameter: if inputfile: - parameter = PylotParameter(inputfile) + parameter = AutoPickParameter(inputfile) iplot = parameter['iplot'] else: print('No parameters set and no input file given. Choose either of both.') return else: - if not type(parameter) == PylotParameter: + if not type(parameter) == AutoPickParameter: print('Wrong input type for parameter: {}'.format(type(parameter))) return if inputfile: @@ -252,12 +252,17 @@ def autoPyLoT(input_dict=None, parameter=None, inputfile=None, fnames=None, even for station, props in moment_mag.moment_props.items(): picks[station]['P'].update(props) evt = moment_mag.updated_event() + net_mw = moment_mag.net_magnitude() + print("Network moment magnitude: %4.1f" % net_mw.mag) + # calculate local (Richter) magntiude local_mag = LocalMagnitude(corr_dat, evt, parameter.get('sstop'), parameter.get('WAscaling'), \ True, iplot) for station, amplitude in local_mag.amplitudes.items(): picks[station]['S']['Ao'] = amplitude.generic_amplitude - evt = local_mag.updated_event() + evt = local_mag.updated_event(parameter.get('magscaling')) + net_ml = local_mag.net_magnitude() + print("Network local magnitude: %4.1f" % net_ml.mag) else: print("autoPyLoT: No NLLoc-location file available!") print("No source parameter estimation possible!") @@ -310,14 +315,17 @@ def autoPyLoT(input_dict=None, parameter=None, inputfile=None, fnames=None, even for station, props in moment_mag.moment_props.items(): picks[station]['P'].update(props) evt = moment_mag.updated_event() + net_mw = moment_mag.net_magnitude() + print("Network moment magnitude: %4.1f" % net_mw.mag) + # calculate local (Richter) magntiude local_mag = LocalMagnitude(corr_dat, evt, parameter.get('sstop'), parameter.get('WAscaling'), \ True, iplot) for station, amplitude in local_mag.amplitudes.items(): picks[station]['S']['Ao'] = amplitude.generic_amplitude - evt = local_mag.updated_event() - net_mw = moment_mag.net_magnitude() - print("Network moment magnitude: %4.1f" % net_mw.mag) + evt = local_mag.updated_event(parameter.get('magscaling')) + net_ml = local_mag.net_magnitude(parameter.get('magscaling')) + print("Network local magnitude: %4.1f" % net_ml.mag) else: print("autoPyLoT: No NLLoc-location file available! Stop iteration!") locflag = 9 @@ -328,26 +336,26 @@ def autoPyLoT(input_dict=None, parameter=None, inputfile=None, fnames=None, even data.applyEVTData(picks) if evt is not None: data.applyEVTData(evt, 'event') - fnqml = '%s/autoPyLoT' % event + fnqml = '%s/picks_%s' % (event, evID) data.exportEvent(fnqml) # HYPO71 - hypo71file = '%s/autoPyLoT_HYPO71_phases' % event + hypo71file = '%s/%s_HYPO71_phases' % (event, evID) hypo71.export(picks, hypo71file, parameter) # HYPOSAT - hyposatfile = '%s/autoPyLoT_HYPOSAT_phases' % event + hyposatfile = '%s/%s_HYPOSAT_phases' % (event, evID) hyposat.export(picks, hyposatfile, parameter) if locflag == 1: # VELEST - velestfile = '%s/autoPyLoT_VELEST_phases.cnv' % event + velestfile = '%s/%s_VELEST_phases.cnv' % (event, evID) velest.export(picks, velestfile, parameter, evt) # hypoDD - hypoddfile = '%s/autoPyLoT_hypoDD_phases.pha' % event + hypoddfile = '%s/%s_hypoDD_phases.pha' % (event, evID) hypodd.export(picks, hypoddfile, parameter, evt) # FOCMEC - focmecfile = '%s/autoPyLoT_FOCMEC.in' % event + focmecfile = '%s/%s_FOCMEC.in' % (event, evID) focmec.export(picks, focmecfile, parameter, evt) # HASH - hashfile = '%s/autoPyLoT_HASH' % event + hashfile = '%s/%s_HASH' % (event, evID) hash.export(picks, hashfile, parameter, evt) endsplash = '''------------------------------------------\n' diff --git a/pylot/RELEASE-VERSION b/pylot/RELEASE-VERSION index 6bf29797..31812905 100644 --- a/pylot/RELEASE-VERSION +++ b/pylot/RELEASE-VERSION @@ -1 +1 @@ -f91e1-dirty +8e8b-dirty diff --git a/pylot/core/analysis/magnitude.py b/pylot/core/analysis/magnitude.py index ff4dca18..867a302d 100644 --- a/pylot/core/analysis/magnitude.py +++ b/pylot/core/analysis/magnitude.py @@ -2,6 +2,7 @@ # -*- coding: utf-8 -*- """ Created autumn/winter 2015. +Revised/extended summer 2017. :author: Ludger Küperkoch / MAGS2 EP3 working group """ @@ -115,23 +116,30 @@ class Magnitude(object): def calc(self): pass - def updated_event(self): - self.event.magnitudes.append(self.net_magnitude()) + def updated_event(self, magscaling=None): + self.event.magnitudes.append(self.net_magnitude(magscaling)) return self.event - def net_magnitude(self): + def net_magnitude(self, magscaling=None): if self: - # TODO if an average Magnitude instead of the median is calculated - # StationMagnitudeContributions should be added to the returned - # Magnitude object - # mag_error => weights (magnitude error estimate from peak_to_peak, calcsourcespec?) - # weights => StationMagnitdeContribution - mag = ope.Magnitude( - mag=np.median([M.mag for M in self.magnitudes.values()]), - magnitude_type=self.type, - origin_id=self.origin_id, - station_count=len(self.magnitudes), - azimuthal_gap=self.origin_id.get_referred_object().quality.azimuthal_gap) + if magscaling is not None and str(magscaling) is not '[0.0, 0.0]': + # scaling necessary + print("Scaling network magnitude ...") + mag = ope.Magnitude( + mag=np.median([M.mag for M in self.magnitudes.values()]) *\ + magscaling[0] + magscaling[1], + magnitude_type=self.type, + origin_id=self.origin_id, + station_count=len(self.magnitudes), + azimuthal_gap=self.origin_id.get_referred_object().quality.azimuthal_gap) + else: + # no saling necessary + mag = ope.Magnitude( + mag=np.median([M.mag for M in self.magnitudes.values()]), + magnitude_type=self.type, + origin_id=self.origin_id, + station_count=len(self.magnitudes), + azimuthal_gap=self.origin_id.get_referred_object().quality.azimuthal_gap) return mag return None @@ -153,7 +161,7 @@ class LocalMagnitude(Magnitude): _amplitudes = dict() - def __init__(self, stream, event, calc_win, wascaling=None, verbosity=False, iplot=0): + def __init__(self, stream, event, calc_win, wascaling, verbosity=False, iplot=0): super(LocalMagnitude, self).__init__(stream, event, verbosity, iplot) self._calc_win = calc_win @@ -257,12 +265,13 @@ class LocalMagnitude(Magnitude): self.amplitudes = (station, amplitude) # using standard Gutenberg-Richter relation # or scale WA amplitude with given scaling relation - if self.wascaling == None: + if str(self.wascaling) == '[0.0, 0.0, 0.0]': print("Calculating original Richter magnitude ...") magnitude = ope.StationMagnitude(mag=np.log10(a0) \ + richter_magnitude_scaling(delta)) else: print("Calculating scaled local magnitude ...") + a0 = a0 * 1e03 # mm to nm (see Havskov & Ottemöller, 2010) magnitude = ope.StationMagnitude(mag=np.log10(a0) \ + self.wascaling[0] * np.log10(delta) + self.wascaling[1] * delta + self.wascaling[2]) diff --git a/pylot/core/io/default_parameters.py b/pylot/core/io/default_parameters.py index 93d55ae4..22d0268b 100644 --- a/pylot/core/io/default_parameters.py +++ b/pylot/core/io/default_parameters.py @@ -278,12 +278,14 @@ defaults = {'rootpath': {'type': str, 'value': 1.0}, 'WAscaling': {'type': (float, float, float), - 'tooltip': 'Scaling relation (log(Ao)+Alog(r)+Br+C) of Wood-Anderson amplitude Ao [nm]', - 'value': (1.0, 1.0, 1.0)}, + 'tooltip': 'Scaling relation (log(Ao)+Alog(r)+Br+C) of Wood-Anderson amplitude Ao [nm] \ + If zeros are set, original Richter magnitude is calculated!', + 'value': (0., 0., 0.)}, 'magscaling': {'type': (float, float), - 'tooltip': 'Scaling relation for derived local magnitude [a*Ml+b]', - 'value': (1.0, 1.0)} + 'tooltip': 'Scaling relation for derived local magnitude [a*Ml+b]. \ + If zeros are set, no scaling is of network magnitude is applied.', + 'value': (0., 0.)} } settings_main={ From 9f9d492b8b92bd997408c69c6685fbc711d5cab4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Thu, 22 Jun 2017 15:09:40 +0200 Subject: [PATCH 02/35] Restored former changes of Marcel (AutoPickParameter - PyLoTParameter). --- autoPyLoT.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/autoPyLoT.py b/autoPyLoT.py index db3a83f6..41db89ca 100755 --- a/autoPyLoT.py +++ b/autoPyLoT.py @@ -18,7 +18,7 @@ import pylot.core.loc.nll as nll #from PySide.QtGui import QWidget, QInputDialog from pylot.core.analysis.magnitude import MomentMagnitude, LocalMagnitude from pylot.core.io.data import Data -from pylot.core.io.inputs import AutoPickParameter +from pylot.core.io.inputs import PyLoTParameter from pylot.core.pick.autopick import autopickevent, iteratepicker from pylot.core.util.dataprocessing import restitute_data, read_metadata, \ remove_underscores @@ -35,7 +35,7 @@ def autoPyLoT(input_dict=None, parameter=None, inputfile=None, fnames=None, even :param inputfile: path to the input file containing all parameter information for automatic picking (for formatting details, see. - `~pylot.core.io.inputs.AutoPickParameter` + `~pylot.core.io.inputs.PyLoTParameter` :type inputfile: str :return: @@ -71,13 +71,13 @@ def autoPyLoT(input_dict=None, parameter=None, inputfile=None, fnames=None, even if not parameter: if inputfile: - parameter = AutoPickParameter(inputfile) + parameter = PyLoTParameter(inputfile) iplot = parameter['iplot'] else: print('No parameters set and no input file given. Choose either of both.') return else: - if not type(parameter) == AutoPickParameter: + if not type(parameter) == PyLoTParameter: print('Wrong input type for parameter: {}'.format(type(parameter))) return if inputfile: From 33d81eb5382af7ca207e94ed7758ffe651987a40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Thu, 22 Jun 2017 15:18:19 +0200 Subject: [PATCH 03/35] Bugfix: Wrong naming of module in previous commit: PyLoTParameter - PylotParameter. --- autoPyLoT.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/autoPyLoT.py b/autoPyLoT.py index 41db89ca..7841487d 100755 --- a/autoPyLoT.py +++ b/autoPyLoT.py @@ -18,7 +18,7 @@ import pylot.core.loc.nll as nll #from PySide.QtGui import QWidget, QInputDialog from pylot.core.analysis.magnitude import MomentMagnitude, LocalMagnitude from pylot.core.io.data import Data -from pylot.core.io.inputs import PyLoTParameter +from pylot.core.io.inputs import PylotParameter from pylot.core.pick.autopick import autopickevent, iteratepicker from pylot.core.util.dataprocessing import restitute_data, read_metadata, \ remove_underscores @@ -35,7 +35,7 @@ def autoPyLoT(input_dict=None, parameter=None, inputfile=None, fnames=None, even :param inputfile: path to the input file containing all parameter information for automatic picking (for formatting details, see. - `~pylot.core.io.inputs.PyLoTParameter` + `~pylot.core.io.inputs.PylotParameter` :type inputfile: str :return: @@ -71,13 +71,13 @@ def autoPyLoT(input_dict=None, parameter=None, inputfile=None, fnames=None, even if not parameter: if inputfile: - parameter = PyLoTParameter(inputfile) + parameter = PylotParameter(inputfile) iplot = parameter['iplot'] else: print('No parameters set and no input file given. Choose either of both.') return else: - if not type(parameter) == PyLoTParameter: + if not type(parameter) == PylotParameter: print('Wrong input type for parameter: {}'.format(type(parameter))) return if inputfile: From c44f3516360f0385110a1134290f57aa9086245f Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Thu, 22 Jun 2017 16:30:22 +0200 Subject: [PATCH 04/35] [add] new method to load multiple XML files at once for all events, WIP: picks loaded from XML files cannot be saved afterwards --- QtPyLoT.py | 47 +++++++++++++++++++++++++++++++++---------- pylot/RELEASE-VERSION | 2 +- 2 files changed, 37 insertions(+), 12 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index 51e3dd93..d15be5af 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -637,9 +637,31 @@ class MainWindow(QMainWindow): fname_dict = dict(phasfn=fn_phases, locfn=fn_loc) self.load_data(fname_dict, type=type) - def load_data(self, fname=None, type='manual', loc=False): + def load_multiple_data(self, type='manual'): if not self.okToContinue(): return + refresh=False + events = self.project.eventlist + fext = '.xml' + for event in events: + path = event.path + eventname = path.split('/')[-1] + filename = os.path.join(path, 'picks_'+eventname+fext) + if os.path.isfile(filename): + self.load_data(filename, draw=False, event=event, overwrite=True) + refresh=True + if not refresh: + return + if self.get_current_event().picks: + self.plotWaveformDataThread() + self.drawPicks(picktype=type) + self.draw() + self.setDirty(True) + + def load_data(self, fname=None, type='manual', loc=False, draw=True, event=None, overwrite=False): + if not overwrite: + if not self.okToContinue(): + return if fname is None: action = self.sender() if isinstance(action, QAction): @@ -647,13 +669,15 @@ class MainWindow(QMainWindow): self.set_fname(fname, type) data = dict(auto=self.autodata, manual=self.data) data[type] += Data(self, evtdata=fname) + print('Loading {} picks from file {}.'.format(type, fname)) if not loc: - self.updatePicks(type=type) - if self.get_current_event().picks: - self.plotWaveformDataThread() - self.drawPicks(picktype=type) - self.draw() - self.setDirty(True) + self.updatePicks(type=type, event=event) + if draw: + if self.get_current_event().picks: + self.plotWaveformDataThread() + self.drawPicks(picktype=type) + self.draw() + self.setDirty(True) def add_recentfile(self, event): self.recentfiles.insert(0, event) @@ -979,7 +1003,6 @@ class MainWindow(QMainWindow): if not exform and selected_filter or not exform in OUTPUTFORMATS: exform = selected_filter.split('*')[1][:-1] - if not exform in OUTPUTFORMATS: return fname, exform return fbasename, exform @@ -1723,13 +1746,15 @@ class MainWindow(QMainWindow): # raise Exception('FATAL: Should never occur!') # MP MP prompt redundant because new picks have to be accepted in the first place closing PickDlg - def updatePicks(self, type='manual'): + def updatePicks(self, type='manual', event=None): + if not event: + event = self.get_current_event() picks = picksdict_from_picks(evt=self.get_data(type).get_evt_data()) if type == 'manual': - self.get_current_event().addPicks(picks) + event.addPicks(picks) self.picks.update(picks) elif type == 'auto': - self.get_current_event().addAutopicks(picks) + event.addAutopicks(picks) self.autopicks.update(picks) self.check4Comparison() diff --git a/pylot/RELEASE-VERSION b/pylot/RELEASE-VERSION index 6bf29797..31812905 100644 --- a/pylot/RELEASE-VERSION +++ b/pylot/RELEASE-VERSION @@ -1 +1 @@ -f91e1-dirty +8e8b-dirty From 6ebad273a9c380837483a3acf6eedb1063e717ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Fri, 23 Jun 2017 10:10:45 +0200 Subject: [PATCH 05/35] Improved screen output. --- autoPyLoT.py | 36 ++++++++++++++++++++++++++---------- pylot/RELEASE-VERSION | 2 +- 2 files changed, 27 insertions(+), 11 deletions(-) diff --git a/autoPyLoT.py b/autoPyLoT.py index 7841487d..7569d814 100755 --- a/autoPyLoT.py +++ b/autoPyLoT.py @@ -243,7 +243,7 @@ def autoPyLoT(input_dict=None, parameter=None, inputfile=None, fnames=None, even # get latest NLLoc-location file if several are available nllocfile = max(glob.glob(locsearch), key=os.path.getctime) evt = read_events(nllocfile)[0] - # calculating seismic moment Mo and moment magnitude Mw + # calculate seismic moment Mo and moment magnitude Mw moment_mag = MomentMagnitude(corr_dat, evt, parameter.get('vp'), parameter.get('Qp'), parameter.get('rho'), True, \ @@ -255,14 +255,22 @@ def autoPyLoT(input_dict=None, parameter=None, inputfile=None, fnames=None, even net_mw = moment_mag.net_magnitude() print("Network moment magnitude: %4.1f" % net_mw.mag) # calculate local (Richter) magntiude + WAscaling = parameter.get('WAscaling') + magscaling = parameter.get('magscaling') local_mag = LocalMagnitude(corr_dat, evt, - parameter.get('sstop'), parameter.get('WAscaling'), \ - True, iplot) + parameter.get('sstop'), + WAscaling, True, iplot) for station, amplitude in local_mag.amplitudes.items(): picks[station]['S']['Ao'] = amplitude.generic_amplitude - evt = local_mag.updated_event(parameter.get('magscaling')) - net_ml = local_mag.net_magnitude() + print("Local station magnitudes scaled with:") + print("log(Ao) + %f * log(r) + %f * r + %f" % (WAscaling[0], + WAscaling[1], + WAscaling[2])) + evt = local_mag.updated_event(magscaling) + net_ml = local_mag.net_magnitude(magscaling) print("Network local magnitude: %4.1f" % net_ml.mag) + print("Network local magnitude scaled with:") + print("%f * Ml + %f" % (magscaling[0], magscaling[1])) else: print("autoPyLoT: No NLLoc-location file available!") print("No source parameter estimation possible!") @@ -306,7 +314,7 @@ def autoPyLoT(input_dict=None, parameter=None, inputfile=None, fnames=None, even nlloccounter = maxnumit evt = read_events(nllocfile)[0] if locflag < 2: - # calculating seismic moment Mo and moment magnitude Mw + # calculate seismic moment Mo and moment magnitude Mw moment_mag = MomentMagnitude(corr_dat, evt, parameter.get('vp'), parameter.get('Qp'), parameter.get('rho'), True, \ @@ -318,14 +326,22 @@ def autoPyLoT(input_dict=None, parameter=None, inputfile=None, fnames=None, even net_mw = moment_mag.net_magnitude() print("Network moment magnitude: %4.1f" % net_mw.mag) # calculate local (Richter) magntiude + WAscaling = parameter.get('WAscaling') + magscaling = parameter.get('magscaling') local_mag = LocalMagnitude(corr_dat, evt, - parameter.get('sstop'), parameter.get('WAscaling'), \ - True, iplot) + parameter.get('sstop'), + WAscaling, True, iplot) for station, amplitude in local_mag.amplitudes.items(): picks[station]['S']['Ao'] = amplitude.generic_amplitude - evt = local_mag.updated_event(parameter.get('magscaling')) - net_ml = local_mag.net_magnitude(parameter.get('magscaling')) + print("Local station magnitudes scaled with:") + print("log(Ao) + %f * log(r) + %f * r + %f" % (WAscaling[0], + WAscaling[1], + WAscaling[2])) + evt = local_mag.updated_event(magscaling) + net_ml = local_mag.net_magnitude(magscaling) print("Network local magnitude: %4.1f" % net_ml.mag) + print("Network local magnitude scaled with:") + print("%f * Ml + %f" % (magscaling[0], magscaling[1])) else: print("autoPyLoT: No NLLoc-location file available! Stop iteration!") locflag = 9 diff --git a/pylot/RELEASE-VERSION b/pylot/RELEASE-VERSION index 31812905..0d836703 100644 --- a/pylot/RELEASE-VERSION +++ b/pylot/RELEASE-VERSION @@ -1 +1 @@ -8e8b-dirty +33d8-dirty From 5958e05f3bdab30be1b30fea5afda941cf734c21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Fri, 23 Jun 2017 10:26:49 +0200 Subject: [PATCH 06/35] Cosmetics. --- pylot/core/io/default_parameters.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pylot/core/io/default_parameters.py b/pylot/core/io/default_parameters.py index 22d0268b..18baca6c 100644 --- a/pylot/core/io/default_parameters.py +++ b/pylot/core/io/default_parameters.py @@ -284,7 +284,7 @@ defaults = {'rootpath': {'type': str, 'magscaling': {'type': (float, float), 'tooltip': 'Scaling relation for derived local magnitude [a*Ml+b]. \ - If zeros are set, no scaling is of network magnitude is applied.', + If zeros are set, no scaling of network magnitude is applied!', 'value': (0., 0.)} } From 27597e6b4b4909e53506862c81d2ed8dd6c68a23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Fri, 23 Jun 2017 11:32:08 +0200 Subject: [PATCH 07/35] Take into account parameter extent when writing header for PyLoT-input file. --- pylot/RELEASE-VERSION | 2 +- pylot/core/io/inputs.py | 21 ++++++++++----------- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/pylot/RELEASE-VERSION b/pylot/RELEASE-VERSION index 0d836703..cd4206e8 100644 --- a/pylot/RELEASE-VERSION +++ b/pylot/RELEASE-VERSION @@ -1 +1 @@ -33d8-dirty +5958-dirty diff --git a/pylot/core/io/inputs.py b/pylot/core/io/inputs.py index 3653b828..9aa9b5d2 100644 --- a/pylot/core/io/inputs.py +++ b/pylot/core/io/inputs.py @@ -221,24 +221,23 @@ class PylotParameter(object): # for key, value in self.iteritems(): # lines.append('{key}\t{value}\n'.format(key=key, value=value)) # fid_out.writelines(lines) - header = ('%This is a parameter input file for PyLoT/autoPyLoT.\n'+ '%All main and special settings regarding data handling\n'+ '%and picking are to be set here!\n'+ - '%Parameters are optimized for local data sets!\n') - seperator = '%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n' + '%Parameters are optimized for %{} data sets!\n'.format(self.get_main_para_names()['pick'][0])) + separator = '%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n' fid_out.write(header) self.write_section(fid_out, self.get_main_para_names()['dirs'], - 'main settings', seperator) + 'main settings', separator) self.write_section(fid_out, self.get_main_para_names()['nlloc'], - 'NLLoc settings', seperator) + 'NLLoc settings', separator) self.write_section(fid_out, self.get_main_para_names()['smoment'], - 'parameters for seismic moment estimation', seperator) + 'parameters for seismic moment estimation', separator) self.write_section(fid_out, self.get_main_para_names()['localmag'], - 'settings local magnitude', seperator) + 'settings local magnitude', separator) self.write_section(fid_out, self.get_main_para_names()['pick'], - 'common settings picker', seperator) + 'common settings picker', separator) fid_out.write(('#special settings for calculating CF#\n'+ '%!!Edit the following only if you know what you are doing!!%\n')) self.write_section(fid_out, self.get_special_para_names()['z'], @@ -250,9 +249,9 @@ class PylotParameter(object): self.write_section(fid_out, self.get_special_para_names()['quality'], 'quality assessment', None) - def write_section(self, fid, names, title, seperator): - if seperator: - fid.write(seperator) + def write_section(self, fid, names, title, separator): + if separator: + fid.write(separator) fid.write('#{}#\n'.format(title)) l_val = 50 l_name = 15 From 7bb2d54f6e0ef62899322d471e091de0b08715e9 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Fri, 23 Jun 2017 12:04:26 +0200 Subject: [PATCH 08/35] [change] Event class now bases on ObsPy Event class which should be more efficient in future, origin time will be read from notes file if possible --- QtPyLoT.py | 29 +++++++++++++++++++---------- pylot/RELEASE-VERSION | 2 +- pylot/core/util/widgets.py | 14 ++++++++++---- 3 files changed, 30 insertions(+), 15 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index d15be5af..6acaa2b9 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -41,6 +41,8 @@ from PySide.QtGui import QMainWindow, QInputDialog, QIcon, QFileDialog, \ QTreeView, QComboBox, QTabWidget, QPushButton, QGridLayout import numpy as np from obspy import UTCDateTime +from obspy.core.event import Event as ObsPyEvent +from obspy.core.event import Origin try: import pyqtgraph as pg @@ -752,8 +754,8 @@ class MainWindow(QMainWindow): ''' if not eventbox: eventbox = self.eventBox - index = eventbox.currentIndex() - return eventbox.itemData(index) + path = eventbox.currentText() + return self.project.getEventFromPath(path) def get_current_event_path(self, eventbox=None): ''' @@ -953,7 +955,8 @@ class MainWindow(QMainWindow): '{} unequal {}.' .format(event.path, self.eventBox.itemText(id))) raise ValueError(message) - eventBox.setItemData(id, event) + #not working with obspy events + #eventBox.setItemData(id, event) eventBox.setCurrentIndex(index) self.refreshRefTestButtons() @@ -2468,11 +2471,13 @@ class Project(object): return project -class Event(object): +class Event(ObsPyEvent): ''' - Pickable class containing information on a single event. + Pickable class derived from ~obspy.core.event.Event containing information on a single event. ''' def __init__(self, path): + # initialize super class + super(Event, self).__init__() self.path = path self.database = path.split('/')[-2] self.datapath = path.split('/')[-3] @@ -2482,10 +2487,7 @@ class Event(object): self.notes = '' self._testEvent = False self._refEvent = False - try: - self.get_notes() - except: - pass + self.get_notes() def get_notes_path(self): notesfile = os.path.join(self.path, 'notes.txt') @@ -2495,8 +2497,15 @@ class Event(object): notesfile = self.get_notes_path() if os.path.isfile(notesfile): with open(notesfile) as infile: - text = '[eventInfo: '+str(infile.readlines()[0].split('\n')[0])+']' + path = str(infile.readlines()[0].split('\n')[0]) + text = '[eventInfo: '+path+']' self.addNotes(text) + try: + datetime = UTCDateTime(path.split('/')[-1]) + origin = Origin(time=datetime, latitude=0, longitude=0) + self.origins.append(origin) + except: + pass def addNotes(self, notes): self.notes = str(notes) diff --git a/pylot/RELEASE-VERSION b/pylot/RELEASE-VERSION index 31812905..5f414d75 100644 --- a/pylot/RELEASE-VERSION +++ b/pylot/RELEASE-VERSION @@ -1 +1 @@ -8e8b-dirty +410fe-dirty diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index 8fa59b71..23c168c7 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -1687,8 +1687,8 @@ class TuneAutopicker(QWidget): self.listWidget.scrollToBottom() def get_current_event(self): - index = self.eventBox.currentIndex() - return self.eventBox.itemData(index) + path = self.eventBox.currentText() + return self.parent.project.getEventFromPath(path) def get_current_event_name(self): return self.eventBox.currentText().split('/')[-1] @@ -1855,6 +1855,9 @@ class TuneAutopicker(QWidget): self.init_tab_names() def fill_eventbox(self): + project = self.parent.project + if not project: + return # update own list self.parent.fill_eventbox(eventBox=self.eventBox, select_events='ref') index_start = self.parent.eventBox.currentIndex() @@ -1862,10 +1865,13 @@ class TuneAutopicker(QWidget): if index == -1: index += 1 nevents = self.eventBox.model().rowCount() - if self.eventBox.itemData(index).isTestEvent(): + path = self.eventBox.itemText(index) + if project.getEventFromPath(path).isTestEvent(): for index in range(nevents): - if not self.eventBox.itemData(index).isTestEvent(): + path = self.eventBox.itemText(index) + if not project.getEventFromPath(index).isTestEvent(): break + #in case all events are marked as test events elif index == nevents - 1: index = -1 self.eventBox.setCurrentIndex(index) From c9f0b126dfb1ad748b41e45bccea0b142b3dea95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Fri, 23 Jun 2017 14:11:35 +0200 Subject: [PATCH 09/35] Equal xml-file name for both automatic and manual picks, i.e. PyLoT_evID.xml. --- autoPyLoT.py | 14 +++++++------- pylot/core/io/phases.py | 4 ++-- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/autoPyLoT.py b/autoPyLoT.py index 7569d814..84d24dea 100755 --- a/autoPyLoT.py +++ b/autoPyLoT.py @@ -352,26 +352,26 @@ def autoPyLoT(input_dict=None, parameter=None, inputfile=None, fnames=None, even data.applyEVTData(picks) if evt is not None: data.applyEVTData(evt, 'event') - fnqml = '%s/picks_%s' % (event, evID) + fnqml = '%s/PyLoT_%s' % (event, evID) data.exportEvent(fnqml) # HYPO71 - hypo71file = '%s/%s_HYPO71_phases' % (event, evID) + hypo71file = '%s/PyLoT_%s_HYPO71_phases' % (event, evID) hypo71.export(picks, hypo71file, parameter) # HYPOSAT - hyposatfile = '%s/%s_HYPOSAT_phases' % (event, evID) + hyposatfile = '%s/PyLoT_%s_HYPOSAT_phases' % (event, evID) hyposat.export(picks, hyposatfile, parameter) if locflag == 1: # VELEST - velestfile = '%s/%s_VELEST_phases.cnv' % (event, evID) + velestfile = '%s/PyLoT_%s_VELEST_phases.cnv' % (event, evID) velest.export(picks, velestfile, parameter, evt) # hypoDD - hypoddfile = '%s/%s_hypoDD_phases.pha' % (event, evID) + hypoddfile = '%s/PyLoT_%s_hypoDD_phases.pha' % (event, evID) hypodd.export(picks, hypoddfile, parameter, evt) # FOCMEC - focmecfile = '%s/%s_FOCMEC.in' % (event, evID) + focmecfile = '%s/PyLoT_%s_FOCMEC.in' % (event, evID) focmec.export(picks, focmecfile, parameter, evt) # HASH - hashfile = '%s/%s_HASH' % (event, evID) + hashfile = '%s/PyLoT_%s_HASH' % (event, evID) hash.export(picks, hashfile, parameter, evt) endsplash = '''------------------------------------------\n' diff --git a/pylot/core/io/phases.py b/pylot/core/io/phases.py index eda56cf7..43ff2de3 100644 --- a/pylot/core/io/phases.py +++ b/pylot/core/io/phases.py @@ -382,12 +382,12 @@ def reassess_pilot_event(root_dir, db_dir, event_id, out_dir=None, fn_param=None evt.picks = picks_from_picksdict(picks_dict) # write phase information to file if not out_dir: - fnout_prefix = os.path.join(root_dir, db_dir, event_id, '{0}.'.format(event_id)) + fnout_prefix = os.path.join(root_dir, db_dir, event_id, 'PyLoT_{0}.'.format(event_id)) else: out_dir = os.path.join(out_dir, db_dir) if not os.path.isdir(out_dir): os.makedirs(out_dir) - fnout_prefix = os.path.join(out_dir, '{0}.'.format(event_id)) + fnout_prefix = os.path.join(out_dir, 'PyLoT_{0}.'.format(event_id)) evt.write(fnout_prefix + 'xml', format='QUAKEML') #evt.write(fnout_prefix + 'cnv', format='VELEST') From 103ae716a25b02a339887532c3cfcece8b708c17 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Fri, 23 Jun 2017 14:14:42 +0200 Subject: [PATCH 10/35] [add] additional event information can now be read by project from a simple textfile and added to each event (time, location, magnitude) --- QtPyLoT.py | 66 +++++++++++++++++++++++++++++++++++++++---- pylot/RELEASE-VERSION | 2 +- 2 files changed, 62 insertions(+), 6 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index 6acaa2b9..298d6c6f 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -42,7 +42,7 @@ from PySide.QtGui import QMainWindow, QInputDialog, QIcon, QFileDialog, \ import numpy as np from obspy import UTCDateTime from obspy.core.event import Event as ObsPyEvent -from obspy.core.event import Origin +from obspy.core.event import Origin, Magnitude, ResourceIdentifier try: import pyqtgraph as pg @@ -804,7 +804,7 @@ class MainWindow(QMainWindow): dirs = { 'database': path.split('/')[-2], 'datapath': path.split('/')[-3], - 'rootpath': os.path.join(*path.split('/')[:-3]) + 'rootpath': '/'+os.path.join(*path.split('/')[:-3]) } except Exception as e: dirs = { @@ -2411,6 +2411,62 @@ class Project(object): self.setDirty() else: print('Skipping event with path {}. Already part of project.'.format(event.path)) + self.search_eventfile_info() + + def read_eventfile_info(self, filename, separator=','): + ''' + Try to read event information from file (:param:filename) comparing specific event datetimes. + File structure (each row): event, date, time, magnitude, latitude, longitude, depth + separated by :param:separator each. + ''' + infile = open(filename, 'r') + for line in infile.readlines(): + event, date, time, mag, lat, lon, depth = line.split(separator)[:7] + #skip first line + try: + month, day, year = date.split('/') + except: + continue + year = int(year) + #hardcoded, if year only consists of 2 digits (e.g. 16 instead of 2016) + if year<100: + year += 2000 + datetime = '{}-{}-{}T{}'.format(year, month, day, time) + try: + datetime = UTCDateTime(datetime) + except Exception as e: + print(e, datetime, filename) + continue + for event in self.eventlist: + if not event.origins: + continue + origin = event.origins[0] #should have only one origin + if origin.time == datetime: + origin.latitude = float(lat) + origin.longitude = float(lon) + origin.depth = float(depth) + event.magnitudes.append(Magnitude(resource_id=event.resource_id, + mag=float(mag), + mag_type='M')) + + def search_eventfile_info(self): + ''' + Search all datapaths in rootpath for filenames with given file extension fext + and try to read event info from it + ''' + datapaths = [] + fext='.csv' + for event in self.eventlist: + if not event.datapath in datapaths: + datapaths.append(event.datapath) + for datapath in datapaths: + datapath = os.path.join(self.rootpath, datapath) + for filename in os.listdir(datapath): + if os.path.isfile(os.path.join(datapath, filename)) and filename.endswith(fext): + try: + self.read_eventfile_info(filename) + except Exception as e: + print('Failed on reading eventfile info from file {}: {}'.format(filename, e)) def getPaths(self): ''' @@ -2477,11 +2533,11 @@ class Event(ObsPyEvent): ''' def __init__(self, path): # initialize super class - super(Event, self).__init__() + super(Event, self).__init__(resource_id=ResourceIdentifier(path.split('/')[-1])) self.path = path self.database = path.split('/')[-2] self.datapath = path.split('/')[-3] - self.rootpath = os.path.join(*path.split('/')[:-3]) + self.rootpath = '/' + os.path.join(*path.split('/')[:-3]) self.autopicks = {} self.picks = {} self.notes = '' @@ -2502,7 +2558,7 @@ class Event(ObsPyEvent): self.addNotes(text) try: datetime = UTCDateTime(path.split('/')[-1]) - origin = Origin(time=datetime, latitude=0, longitude=0) + origin = Origin(resource_id=self.resource_id, time=datetime, latitude=0, longitude=0) self.origins.append(origin) except: pass diff --git a/pylot/RELEASE-VERSION b/pylot/RELEASE-VERSION index 5f414d75..04102365 100644 --- a/pylot/RELEASE-VERSION +++ b/pylot/RELEASE-VERSION @@ -1 +1 @@ -410fe-dirty +929f-dirty From ed4c112e992dc7490dd4b1ea1f476e9c89fd2fc0 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Fri, 23 Jun 2017 14:56:56 +0200 Subject: [PATCH 11/35] [add] additional event information will be displayed in eventlist tab [bugfix] project.search_eventfile_info was using relative path for filename --- QtPyLoT.py | 60 +++++++++++++++++++++++++++++++------------ pylot/RELEASE-VERSION | 2 +- 2 files changed, 45 insertions(+), 17 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index 298d6c6f..6303e78e 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -43,6 +43,7 @@ import numpy as np from obspy import UTCDateTime from obspy.core.event import Event as ObsPyEvent from obspy.core.event import Origin, Magnitude, ResourceIdentifier +from obspy.core.util import AttribDict try: import pyqtgraph as pg @@ -1795,7 +1796,7 @@ class MainWindow(QMainWindow): for phase in stat_picks: picks = stat_picks[phase] - if type(stat_picks[phase]) is not dict: + if type(stat_picks[phase]) is not dict and type(stat_picks[phase]) is not AttribDict: return colors = phase_col[phase[0].upper()] @@ -2021,19 +2022,19 @@ class MainWindow(QMainWindow): # changes attributes of the corresponding event table = self.project._table event = self.project.getEventFromPath(table[row][0].text()) - if column == 3 or column == 4: + if column == 8 or column == 9: #toggle checked states (exclusive) - item_ref = table[row][3] - item_test = table[row][4] - if column == 3 and item_ref.checkState(): + item_ref = table[row][8] + item_test = table[row][9] + if column == 8 and item_ref.checkState(): item_test.setCheckState(QtCore.Qt.Unchecked) event.setRefEvent(True) - elif column == 3 and not item_ref.checkState(): + elif column == 8 and not item_ref.checkState(): event.setRefEvent(False) - elif column == 4 and item_test.checkState(): + elif column == 9 and item_test.checkState(): item_ref.setCheckState(QtCore.Qt.Unchecked) event.setTestEvent(True) - elif column == 4 and not item_test.checkState(): + elif column == 9 and not item_test.checkState(): event.setTestEvent(False) self.fill_eventbox() elif column == 5: @@ -2049,11 +2050,19 @@ class MainWindow(QMainWindow): # init new qtable self.event_table = QtGui.QTableWidget() - self.event_table.setColumnCount(6) + self.event_table.setColumnCount(11) self.event_table.setRowCount(len(eventlist)) - self.event_table.setHorizontalHeaderLabels(['Event', '[N] MP', - '[N] AP', 'Tuning Set', - 'Test Set', 'Notes']) + self.event_table.setHorizontalHeaderLabels(['Event', + 'Time', + 'Lat', + 'Lon', + 'Depth', + 'Mag', + '[N] MP', + '[N] AP', + 'Tuning Set', + 'Test Set', + 'Notes']) # iterate through eventlist and generate items for table rows self.project._table = [] @@ -2065,6 +2074,11 @@ class MainWindow(QMainWindow): if event.autopicks: event_nautopicks = len(event.autopicks) item_path = QtGui.QTableWidgetItem() + item_time = QtGui.QTableWidgetItem() + item_lat = QtGui.QTableWidgetItem() + item_lon = QtGui.QTableWidgetItem() + item_depth = QtGui.QTableWidgetItem() + item_mag = QtGui.QTableWidgetItem() item_nmp = QtGui.QTableWidgetItem(str(event_npicks)) item_nmp.setIcon(self.manupicksicon_small) item_nap = QtGui.QTableWidgetItem(str(event_nautopicks)) @@ -2076,7 +2090,19 @@ class MainWindow(QMainWindow): item_ref.setBackground(self._colors['ref']) item_test.setBackground(self._colors['test']) item_path.setText(event.path) - item_notes.setText(event.notes) + if hasattr(event, 'origins'): + if event.origins: + origin = event.origins[0] + item_time.setText(str(origin.time).split('.')[0]) + item_lon.setText(str(origin.longitude)) + item_lat.setText(str(origin.latitude)) + item_depth.setText(str(origin.depth)) + if hasattr(event, 'magnitudes'): + if event.magnitudes: + magnitude = event.magnitudes[0] + item_mag.setText(str(magnitude.mag)) + item_notes.setText(event.notes) + set_enabled(item_path, True, False) set_enabled(item_nmp, True, False) set_enabled(item_nap, True, False) @@ -2096,7 +2122,8 @@ class MainWindow(QMainWindow): else: item_test.setCheckState(QtCore.Qt.Unchecked) - column=[item_path, item_nmp, item_nap, item_ref, item_test, item_notes] + column=[item_path, item_time, item_lat, item_lon, item_depth, item_mag, + item_nmp, item_nap, item_ref, item_test, item_notes] self.project._table.append(column) for r_index, row in enumerate(self.project._table): @@ -2462,7 +2489,8 @@ class Project(object): for datapath in datapaths: datapath = os.path.join(self.rootpath, datapath) for filename in os.listdir(datapath): - if os.path.isfile(os.path.join(datapath, filename)) and filename.endswith(fext): + filename = os.path.join(datapath, filename) + if os.path.isfile(filename) and filename.endswith(fext): try: self.read_eventfile_info(filename) except Exception as e: @@ -2558,7 +2586,7 @@ class Event(ObsPyEvent): self.addNotes(text) try: datetime = UTCDateTime(path.split('/')[-1]) - origin = Origin(resource_id=self.resource_id, time=datetime, latitude=0, longitude=0) + origin = Origin(resource_id=self.resource_id, time=datetime, latitude=0, longitude=0, depth=0) self.origins.append(origin) except: pass diff --git a/pylot/RELEASE-VERSION b/pylot/RELEASE-VERSION index 04102365..cabd06a0 100644 --- a/pylot/RELEASE-VERSION +++ b/pylot/RELEASE-VERSION @@ -1 +1 @@ -929f-dirty +f409-dirty From 8e6c4d06ce13d4bcfe75eddffbe12a29a52ab2b9 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Fri, 23 Jun 2017 15:40:13 +0200 Subject: [PATCH 12/35] [add] array_map now plotting event location if given --- QtPyLoT.py | 6 ++++++ pylot/RELEASE-VERSION | 2 +- pylot/core/util/map_projection.py | 11 +++++++++-- 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index 6303e78e..f70573ae 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -1996,6 +1996,12 @@ class MainWindow(QMainWindow): if not self.array_map: return # refresh with new picks here!!! + event = self.get_current_event() + if hasattr(event, 'origins'): + if event.origins: + lat = event.origins[0].latitude + lon = event.origins[0].longitude + self.array_map.eventLoc = (lat, lon) self.array_map.refresh_drawings(self.get_current_event().getPicks()) self._eventChanged[1] = False diff --git a/pylot/RELEASE-VERSION b/pylot/RELEASE-VERSION index cabd06a0..d7691880 100644 --- a/pylot/RELEASE-VERSION +++ b/pylot/RELEASE-VERSION @@ -1 +1 @@ -f409-dirty +ed4c1-dirty diff --git a/pylot/core/util/map_projection.py b/pylot/core/util/map_projection.py index 64fd0444..8a577c83 100644 --- a/pylot/core/util/map_projection.py +++ b/pylot/core/util/map_projection.py @@ -22,6 +22,7 @@ class map_projection(QtGui.QWidget): self.parser = parent.metadata[1] self.picks = None self.picks_dict = None + self.eventLoc = None self.figure = figure self.init_graphics() self.init_stations() @@ -244,6 +245,10 @@ class map_projection(QtGui.QWidget): self.sc = self.basemap.scatter(self.lon, self.lat, s=50, facecolor='none', latlon=True, zorder=10, picker=True, edgecolor='m', label='Not Picked') self.cid = self.canvas.mpl_connect('pick_event', self.onpick) + if self.eventLoc: + lat, lon = self.eventLoc + self.sc_event = self.basemap.scatter(lon, lat, s=100, facecolor='red', + latlon=True, zorder=11, label='Event (might be outside map region)') def scatter_picked_stations(self): lon = self.lon_no_nan @@ -274,8 +279,7 @@ class map_projection(QtGui.QWidget): def refresh_drawings(self, picks=None): self.picks_dict = picks - self.remove_drawings() - self.draw_everything() + self._refresh_drawings() def _refresh_drawings(self): self.remove_drawings() @@ -303,6 +307,9 @@ class map_projection(QtGui.QWidget): if hasattr(self, 'sc_picked'): self.sc_picked.remove() del(self.sc_picked) + if hasattr(self, 'sc_event'): + self.sc_event.remove() + del(self.sc_event) if hasattr(self, 'cbar'): self.cbar.remove() del(self.cbar) From 9746913f8b737eb3130cda3e81dd9360b9cdc515 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Fri, 23 Jun 2017 15:45:45 +0200 Subject: [PATCH 13/35] Consistent prefix for all PyLoT-output. --- QtPyLoT.py | 4 ++-- pylot/RELEASE-VERSION | 2 +- pylot/core/io/data.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index 6acaa2b9..ea5af579 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -648,7 +648,7 @@ class MainWindow(QMainWindow): for event in events: path = event.path eventname = path.split('/')[-1] - filename = os.path.join(path, 'picks_'+eventname+fext) + filename = os.path.join(path, 'PyLoT_'+eventname+fext) if os.path.isfile(filename): self.load_data(filename, draw=False, event=event, overwrite=True) refresh=True @@ -992,7 +992,7 @@ class MainWindow(QMainWindow): print('warning: {0}'.format(e)) directory = self.get_current_event_path() eventname = self.get_current_event_name() - filename = 'picks_'+eventname + filename = 'PyLoT_'+eventname outpath = os.path.join(directory, filename) file_filter = "QuakeML file (*.xml);;VELEST observation file " \ "format (*.cnv);;NonLinLoc observation file (*.obs)" diff --git a/pylot/RELEASE-VERSION b/pylot/RELEASE-VERSION index 5f414d75..496682cc 100644 --- a/pylot/RELEASE-VERSION +++ b/pylot/RELEASE-VERSION @@ -1 +1 @@ -410fe-dirty +c9f0b-dirty diff --git a/pylot/core/io/data.py b/pylot/core/io/data.py index 358c597f..fb729132 100644 --- a/pylot/core/io/data.py +++ b/pylot/core/io/data.py @@ -95,7 +95,7 @@ class Data(object): def getPicksStr(self): picks_str = '' for pick in self.get_evt_data().picks: - picks_str += str(pick) + '\n' + picks_str += str(PyLoT) + '\n' return picks_str def getParent(self): From acaf592590e602b5180037239572bf1a112af928 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Mon, 26 Jun 2017 10:47:40 +0200 Subject: [PATCH 14/35] [move] class Event moved to pylot.core.util.utils --- QtPyLoT.py | 128 +-------------------------------------- pylot/core/io/data.py | 10 +-- pylot/core/util/utils.py | 125 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 132 insertions(+), 131 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index 5f005b9a..71a9a0d8 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -41,8 +41,7 @@ from PySide.QtGui import QMainWindow, QInputDialog, QIcon, QFileDialog, \ QTreeView, QComboBox, QTabWidget, QPushButton, QGridLayout import numpy as np from obspy import UTCDateTime -from obspy.core.event import Event as ObsPyEvent -from obspy.core.event import Origin, Magnitude, ResourceIdentifier +from obspy.core.event import Magnitude from obspy.core.util import AttribDict try: @@ -71,7 +70,7 @@ from pylot.core.util.errors import FormatError, DatastructureError, \ OverwriteError, ProcessingError from pylot.core.util.connection import checkurl from pylot.core.util.dataprocessing import read_metadata, restitute_data -from pylot.core.util.utils import fnConstructor, getLogin, \ +from pylot.core.util.utils import Event, fnConstructor, getLogin, \ full_range from pylot.core.io.location import create_creation_info, create_event from pylot.core.util.widgets import FilterOptionsDialog, NewEventDlg, \ @@ -2561,129 +2560,6 @@ class Project(object): return project -class Event(ObsPyEvent): - ''' - Pickable class derived from ~obspy.core.event.Event containing information on a single event. - ''' - def __init__(self, path): - # initialize super class - super(Event, self).__init__(resource_id=ResourceIdentifier(path.split('/')[-1])) - self.path = path - self.database = path.split('/')[-2] - self.datapath = path.split('/')[-3] - self.rootpath = '/' + os.path.join(*path.split('/')[:-3]) - self.autopicks = {} - self.picks = {} - self.notes = '' - self._testEvent = False - self._refEvent = False - self.get_notes() - - def get_notes_path(self): - notesfile = os.path.join(self.path, 'notes.txt') - return notesfile - - def get_notes(self): - notesfile = self.get_notes_path() - if os.path.isfile(notesfile): - with open(notesfile) as infile: - path = str(infile.readlines()[0].split('\n')[0]) - text = '[eventInfo: '+path+']' - self.addNotes(text) - try: - datetime = UTCDateTime(path.split('/')[-1]) - origin = Origin(resource_id=self.resource_id, time=datetime, latitude=0, longitude=0, depth=0) - self.origins.append(origin) - except: - pass - - def addNotes(self, notes): - self.notes = str(notes) - - def clearNotes(self): - self.notes = None - - def isRefEvent(self): - return self._refEvent - - def isTestEvent(self): - return self._testEvent - - def setRefEvent(self, bool): - self._refEvent = bool - if bool: self._testEvent = False - - def setTestEvent(self, bool): - self._testEvent = bool - if bool: self._refEvent = False - - def addPicks(self, picks): - for station in picks: - self.picks[station] = picks[station] - - def addAutopicks(self, autopicks): - for station in autopicks: - self.autopicks[station] = autopicks[station] - - def setPick(self, station, pick): - if pick: - self.picks[station] = pick - - def setPicks(self, picks): - self.picks = picks - - def getPick(self, station): - if station in self.picks.keys(): - return self.picks[station] - - def getPicks(self): - return self.picks - - def setAutopick(self, station, autopick): - if autopick: - self.autopicks[station] = autopick - - def setAutopicks(self, autopicks): - self.autopicks = autopicks - - def getAutopick(self, station): - if station in self.autopicks.keys(): - return self.autopicks[station] - - def getAutopicks(self): - return self.autopicks - - def save(self, filename): - ''' - Save PyLoT Event to a file. - Can be loaded by using event.load(filename). - ''' - try: - import cPickle - except ImportError: - import _pickle as cPickle - - try: - outfile = open(filename, 'wb') - cPickle.dump(self, outfile, -1) - except Exception as e: - print('Could not pickle PyLoT event. Reason: {}'.format(e)) - - @staticmethod - def load(filename): - ''' - Load project from filename. - ''' - try: - import cPickle - except ImportError: - import _pickle as cPickle - infile = open(filename, 'rb') - event = cPickle.load(infile) - print('Loaded %s' % filename) - return event - - class getExistingDirectories(QFileDialog): ''' File dialog with possibility to select multiple folders. diff --git a/pylot/core/io/data.py b/pylot/core/io/data.py index fb729132..14ed32eb 100644 --- a/pylot/core/io/data.py +++ b/pylot/core/io/data.py @@ -6,11 +6,11 @@ import os from obspy import read_events from obspy.core import read, Stream, UTCDateTime from obspy.io.sac import SacIOError -from obspy.core.event import Event +from obspy.core.event import Event as ObsPyEvent from pylot.core.io.phases import readPILOTEvent, picks_from_picksdict, \ picksdict_from_pilot, merge_picks from pylot.core.util.errors import FormatError, OverwriteError -from pylot.core.util.utils import fnConstructor, full_range +from pylot.core.util.utils import Event, fnConstructor, full_range class Data(object): """ @@ -33,7 +33,7 @@ class Data(object): self.comp = 'Z' self.wfdata = Stream() self._new = False - if isinstance(evtdata, Event): + if isinstance(evtdata, ObsPyEvent) or isinstance(evtdata, Event): pass elif isinstance(evtdata, dict): evt = readPILOTEvent(**evtdata) @@ -49,7 +49,7 @@ class Data(object): if 'Unknown format for file' in e.message: if 'PHASES' in evtdata: picks = picksdict_from_pilot(evtdata) - evtdata = Event() + evtdata = ObsPyEvent() evtdata.picks = picks_from_picksdict(picks) elif 'LOC' in evtdata: raise NotImplementedError('PILOT location information ' @@ -61,7 +61,7 @@ class Data(object): raise e else: # create an empty Event object self.setNew() - evtdata = Event() + evtdata = ObsPyEvent() evtdata.picks = [] self.evtdata = evtdata self.wforiginal = None diff --git a/pylot/core/util/utils.py b/pylot/core/util/utils.py index 46fa699a..06d1d48c 100644 --- a/pylot/core/util/utils.py +++ b/pylot/core/util/utils.py @@ -10,9 +10,134 @@ import re import warnings import subprocess from obspy import UTCDateTime, read +from obspy.core.event import Event as ObsPyEvent +from obspy.core.event import Origin, Magnitude, ResourceIdentifier from pylot.core.io.inputs import PylotParameter +class Event(ObsPyEvent): + ''' + Pickable class derived from ~obspy.core.event.Event containing information on a single event. + ''' + def __init__(self, path): + # initialize super class + super(Event, self).__init__(resource_id=ResourceIdentifier(path.split('/')[-1])) + self.path = path + self.database = path.split('/')[-2] + self.datapath = path.split('/')[-3] + self.rootpath = '/' + os.path.join(*path.split('/')[:-3]) + self.autopicks = {} + self.picks = {} + self.notes = '' + self._testEvent = False + self._refEvent = False + self.get_notes() + + def get_notes_path(self): + notesfile = os.path.join(self.path, 'notes.txt') + return notesfile + + def get_notes(self): + notesfile = self.get_notes_path() + if os.path.isfile(notesfile): + with open(notesfile) as infile: + path = str(infile.readlines()[0].split('\n')[0]) + text = '[eventInfo: '+path+']' + self.addNotes(text) + try: + datetime = UTCDateTime(path.split('/')[-1]) + origin = Origin(resource_id=self.resource_id, time=datetime, latitude=0, longitude=0, depth=0) + self.origins.append(origin) + except: + pass + + def addNotes(self, notes): + self.notes = str(notes) + + def clearNotes(self): + self.notes = None + + def isRefEvent(self): + return self._refEvent + + def isTestEvent(self): + return self._testEvent + + def setRefEvent(self, bool): + self._refEvent = bool + if bool: self._testEvent = False + + def setTestEvent(self, bool): + self._testEvent = bool + if bool: self._refEvent = False + + def addPicks(self, picks): + for station in picks: + self.picks[station] = picks[station] + + def addAutopicks(self, autopicks): + for station in autopicks: + self.autopicks[station] = autopicks[station] + + def setPick(self, station, pick): + if pick: + self.picks[station] = pick + + def setPicks(self, picks): + self.picks = picks + + def getPick(self, station): + if station in self.picks.keys(): + return self.picks[station] + + def getPicks(self): + return self.picks + + def setAutopick(self, station, autopick): + if autopick: + self.autopicks[station] = autopick + + def setAutopicks(self, autopicks): + self.autopicks = autopicks + + def getAutopick(self, station): + if station in self.autopicks.keys(): + return self.autopicks[station] + + def getAutopicks(self): + return self.autopicks + + def save(self, filename): + ''' + Save PyLoT Event to a file. + Can be loaded by using event.load(filename). + ''' + try: + import cPickle + except ImportError: + import _pickle as cPickle + + try: + outfile = open(filename, 'wb') + cPickle.dump(self, outfile, -1) + except Exception as e: + print('Could not pickle PyLoT event. Reason: {}'.format(e)) + + @staticmethod + def load(filename): + ''' + Load project from filename. + ''' + try: + import cPickle + except ImportError: + import _pickle as cPickle + infile = open(filename, 'rb') + event = cPickle.load(infile) + print('Loaded %s' % filename) + return event + + def _pickle_method(m): if m.im_self is None: return getattr, (m.im_class, m.im_func.func_name) From d08bd4cc9ef96fdb73d2a7c8cbd60c4849ac35e5 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Mon, 26 Jun 2017 12:03:04 +0200 Subject: [PATCH 15/35] [move] class Event finally moved to new module event --- pylot/core/util/event.py | 131 +++++++++++++++++++++++++++++++++++++++ pylot/core/util/utils.py | 125 ------------------------------------- 2 files changed, 131 insertions(+), 125 deletions(-) create mode 100644 pylot/core/util/event.py diff --git a/pylot/core/util/event.py b/pylot/core/util/event.py new file mode 100644 index 00000000..98d26e9a --- /dev/null +++ b/pylot/core/util/event.py @@ -0,0 +1,131 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import os + +from obspy import UTCDateTime +from obspy.core.event import Event as ObsPyEvent +from obspy.core.event import Origin, Magnitude, ResourceIdentifier + + +class Event(ObsPyEvent): + ''' + Pickable class derived from ~obspy.core.event.Event containing information on a single event. + ''' + def __init__(self, path): + # initialize super class + super(Event, self).__init__(resource_id=ResourceIdentifier(path.split('/')[-1])) + self.path = path + self.database = path.split('/')[-2] + self.datapath = path.split('/')[-3] + self.rootpath = '/' + os.path.join(*path.split('/')[:-3]) + self.pylot_autopicks = {} + self.pylot_picks = {} + self.notes = '' + self._testEvent = False + self._refEvent = False + self.get_notes() + + def get_notes_path(self): + notesfile = os.path.join(self.path, 'notes.txt') + return notesfile + + def get_notes(self): + notesfile = self.get_notes_path() + if os.path.isfile(notesfile): + with open(notesfile) as infile: + path = str(infile.readlines()[0].split('\n')[0]) + text = '[eventInfo: '+path+']' + self.addNotes(text) + try: + datetime = UTCDateTime(path.split('/')[-1]) + origin = Origin(resource_id=self.resource_id, time=datetime, latitude=0, longitude=0, depth=0) + self.origins.append(origin) + except: + pass + + def addNotes(self, notes): + self.notes = str(notes) + + def clearNotes(self): + self.notes = None + + def isRefEvent(self): + return self._refEvent + + def isTestEvent(self): + return self._testEvent + + def setRefEvent(self, bool): + self._refEvent = bool + if bool: self._testEvent = False + + def setTestEvent(self, bool): + self._testEvent = bool + if bool: self._refEvent = False + + def addPicks(self, picks): + for station in picks: + self.pylot_picks[station] = picks[station] + + def addAutopicks(self, autopicks): + for station in autopicks: + self.pylot_autopicks[station] = autopicks[station] + + def setPick(self, station, pick): + if pick: + self.pylot_picks[station] = pick + + def setPicks(self, picks): + self.pylot_picks = picks + + def getPick(self, station): + if station in self.pylot_picks.keys(): + return self.pylot_picks[station] + + def getPicks(self): + return self.pylot_picks + + def setAutopick(self, station, autopick): + if autopick: + self.pylot_autopicks[station] = autopick + + def setAutopicks(self, autopicks): + self.pylot_autopicks = autopicks + + def getAutopick(self, station): + if station in self.pylot_autopicks.keys(): + return self.pylot_autopicks[station] + + def getAutopicks(self): + return self.pylot_autopicks + + def save(self, filename): + ''' + Save PyLoT Event to a file. + Can be loaded by using event.load(filename). + ''' + try: + import cPickle + except ImportError: + import _pickle as cPickle + + try: + outfile = open(filename, 'wb') + cPickle.dump(self, outfile, -1) + except Exception as e: + print('Could not pickle PyLoT event. Reason: {}'.format(e)) + + @staticmethod + def load(filename): + ''' + Load project from filename. + ''' + try: + import cPickle + except ImportError: + import _pickle as cPickle + infile = open(filename, 'rb') + event = cPickle.load(infile) + print('Loaded %s' % filename) + return event diff --git a/pylot/core/util/utils.py b/pylot/core/util/utils.py index 06d1d48c..586a54c3 100644 --- a/pylot/core/util/utils.py +++ b/pylot/core/util/utils.py @@ -10,133 +10,8 @@ import re import warnings import subprocess from obspy import UTCDateTime, read -from obspy.core.event import Event as ObsPyEvent -from obspy.core.event import Origin, Magnitude, ResourceIdentifier from pylot.core.io.inputs import PylotParameter - -class Event(ObsPyEvent): - ''' - Pickable class derived from ~obspy.core.event.Event containing information on a single event. - ''' - def __init__(self, path): - # initialize super class - super(Event, self).__init__(resource_id=ResourceIdentifier(path.split('/')[-1])) - self.path = path - self.database = path.split('/')[-2] - self.datapath = path.split('/')[-3] - self.rootpath = '/' + os.path.join(*path.split('/')[:-3]) - self.autopicks = {} - self.picks = {} - self.notes = '' - self._testEvent = False - self._refEvent = False - self.get_notes() - - def get_notes_path(self): - notesfile = os.path.join(self.path, 'notes.txt') - return notesfile - - def get_notes(self): - notesfile = self.get_notes_path() - if os.path.isfile(notesfile): - with open(notesfile) as infile: - path = str(infile.readlines()[0].split('\n')[0]) - text = '[eventInfo: '+path+']' - self.addNotes(text) - try: - datetime = UTCDateTime(path.split('/')[-1]) - origin = Origin(resource_id=self.resource_id, time=datetime, latitude=0, longitude=0, depth=0) - self.origins.append(origin) - except: - pass - - def addNotes(self, notes): - self.notes = str(notes) - - def clearNotes(self): - self.notes = None - - def isRefEvent(self): - return self._refEvent - - def isTestEvent(self): - return self._testEvent - - def setRefEvent(self, bool): - self._refEvent = bool - if bool: self._testEvent = False - - def setTestEvent(self, bool): - self._testEvent = bool - if bool: self._refEvent = False - - def addPicks(self, picks): - for station in picks: - self.picks[station] = picks[station] - - def addAutopicks(self, autopicks): - for station in autopicks: - self.autopicks[station] = autopicks[station] - - def setPick(self, station, pick): - if pick: - self.picks[station] = pick - - def setPicks(self, picks): - self.picks = picks - - def getPick(self, station): - if station in self.picks.keys(): - return self.picks[station] - - def getPicks(self): - return self.picks - - def setAutopick(self, station, autopick): - if autopick: - self.autopicks[station] = autopick - - def setAutopicks(self, autopicks): - self.autopicks = autopicks - - def getAutopick(self, station): - if station in self.autopicks.keys(): - return self.autopicks[station] - - def getAutopicks(self): - return self.autopicks - - def save(self, filename): - ''' - Save PyLoT Event to a file. - Can be loaded by using event.load(filename). - ''' - try: - import cPickle - except ImportError: - import _pickle as cPickle - - try: - outfile = open(filename, 'wb') - cPickle.dump(self, outfile, -1) - except Exception as e: - print('Could not pickle PyLoT event. Reason: {}'.format(e)) - - @staticmethod - def load(filename): - ''' - Load project from filename. - ''' - try: - import cPickle - except ImportError: - import _pickle as cPickle - infile = open(filename, 'rb') - event = cPickle.load(infile) - print('Loaded %s' % filename) - return event - def _pickle_method(m): if m.im_self is None: From b10b79ea792e8132f2985fc051acf8e96cd653ce Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Mon, 26 Jun 2017 12:19:06 +0200 Subject: [PATCH 16/35] [change] renamed picks attribute for pylot to pylot_picks, picks attribute of events will now be used exclusively for ObsPy picks --- QtPyLoT.py | 69 +++++++++++++++++++------------------- pylot/core/io/data.py | 9 ++--- pylot/core/io/phases.py | 3 +- pylot/core/util/event.py | 18 ++++++++-- pylot/core/util/widgets.py | 14 ++++---- 5 files changed, 64 insertions(+), 49 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index 71a9a0d8..567d57e0 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -70,8 +70,9 @@ from pylot.core.util.errors import FormatError, DatastructureError, \ OverwriteError, ProcessingError from pylot.core.util.connection import checkurl from pylot.core.util.dataprocessing import read_metadata, restitute_data -from pylot.core.util.utils import Event, fnConstructor, getLogin, \ +from pylot.core.util.utils import fnConstructor, getLogin, \ full_range +from pylot.core.util.event import Event from pylot.core.io.location import create_creation_info, create_event from pylot.core.util.widgets import FilterOptionsDialog, NewEventDlg, \ WaveformWidget, WaveformWidgetPG, PropertiesDlg, HelpForm, createAction, PickDlg, \ @@ -174,8 +175,8 @@ class MainWindow(QMainWindow): self.setupUi() self.filteroptions = {} - self.picks = {} - self.autopicks = {} + self.pylot_picks = {} + self.pylot_autopicks = {} self.loc = False def setupUi(self): @@ -654,7 +655,7 @@ class MainWindow(QMainWindow): refresh=True if not refresh: return - if self.get_current_event().picks: + if self.get_current_event().pylot_picks: self.plotWaveformDataThread() self.drawPicks(picktype=type) self.draw() @@ -675,7 +676,7 @@ class MainWindow(QMainWindow): if not loc: self.updatePicks(type=type, event=event) if draw: - if self.get_current_event().picks: + if self.get_current_event().pylot_picks: self.plotWaveformDataThread() self.drawPicks(picktype=type) self.draw() @@ -702,8 +703,8 @@ class MainWindow(QMainWindow): def getWFFnames(self): try: evt = self.get_data().get_evt_data() - if evt.picks: - for pick in evt.picks: + if evt.pylot_picks: + for pick in evt.pylot_picks: try: if pick.waveform_id is not None: fname = pick.waveform_id.getSEEDstring() @@ -906,10 +907,10 @@ class MainWindow(QMainWindow): event_path = event.path event_npicks = 0 event_nautopicks = 0 - if event.picks: - event_npicks = len(event.picks) - if event.autopicks: - event_nautopicks = len(event.autopicks) + if event.pylot_picks: + event_npicks = len(event.pylot_picks) + if event.pylot_autopicks: + event_nautopicks = len(event.pylot_autopicks) event_ref = event.isRefEvent() event_test = event.isTestEvent() @@ -1013,7 +1014,7 @@ class MainWindow(QMainWindow): fbasename = self.getEventFileName() exform = settings.value('data/exportFormat', 'QUAKEML') try: - self.get_data().applyEVTData(self.getPicks()) + self.get_data().applyEVTData(self.get_current_event(), typ='event')#getPicks()) except OverwriteError: # msgBox = QMessageBox() # msgBox.setText("Picks have been modified!") @@ -1083,7 +1084,7 @@ class MainWindow(QMainWindow): return self.get_current_event().getPicks() if type == 'auto': return self.get_current_event().getAutopicks() - # rdict = dict(auto=self.autopicks, manual=self.picks) + # rdict = dict(auto=self.pylot_autopicks, manual=self.pylot_picks) # return rdict[type] def getPicksOnStation(self, station, type='manual'): @@ -1157,7 +1158,7 @@ class MainWindow(QMainWindow): if event: self.ref_event_button.setChecked(event.isRefEvent()) self.test_event_button.setChecked(event.isTestEvent()) - self.enableRefTestButtons(bool(self.get_current_event().picks)) + self.enableRefTestButtons(bool(self.get_current_event().pylot_picks)) return self.ref_event_button.setChecked(False) self.test_event_button.setChecked(False) @@ -1216,14 +1217,14 @@ class MainWindow(QMainWindow): if not event: return # update picks saved in GUI mainwindow (to be changed in future!!) MP MP - if not event.picks: - self.picks = {} + if not event.pylot_picks: + self.pylot_picks = {} else: - self.picks = event.picks - if not event.autopicks: - self.autopicks = {} + self.pylot_picks = event.pylot_picks + if not event.pylot_autopicks: + self.pylot_autopicks = {} else: - self.autopicks = event.autopicks + self.pylot_autopicks = event.pylot_autopicks # if current tab is waveformPlot-tab and the data in this tab was not yet refreshed if self.tabs.currentIndex() == 0: if self._eventChanged[0]: @@ -1369,12 +1370,12 @@ class MainWindow(QMainWindow): self.openautopicksaction.setEnabled(True) self.loadpilotevent.setEnabled(True) event = self.get_current_event() - if event.picks: - self.picks = event.picks + if event.pylot_picks: + self.pylot_picks = event.pylot_picks self.drawPicks(picktype='manual') self.enableSaveManualPicksAction() - if event.autopicks: - self.autopicks = event.autopicks + if event.pylot_autopicks: + self.pylot_autopicks = event.pylot_autopicks self.drawPicks(picktype='auto') self.compare_action.setEnabled(True) self.draw() @@ -1755,10 +1756,10 @@ class MainWindow(QMainWindow): picks = picksdict_from_picks(evt=self.get_data(type).get_evt_data()) if type == 'manual': event.addPicks(picks) - self.picks.update(picks) + self.pylot_picks.update(picks) elif type == 'auto': event.addAutopicks(picks) - self.autopicks.update(picks) + self.pylot_autopicks.update(picks) self.check4Comparison() def drawPicks(self, station=None, picktype='manual'): @@ -1909,8 +1910,8 @@ class MainWindow(QMainWindow): finally: os.remove(phasepath) - self.get_data().applyEVTData(lt.read_location(locpath), type='event') - self.get_data().applyEVTData(self.calc_magnitude(), type='event') + self.get_data().applyEVTData(lt.read_location(locpath), typ='event') + self.get_data().applyEVTData(self.calc_magnitude(), typ='event') def init_array_tab(self): ''' @@ -2074,10 +2075,10 @@ class MainWindow(QMainWindow): for index, event in enumerate(eventlist): event_npicks = 0 event_nautopicks = 0 - if event.picks: - event_npicks = len(event.picks) - if event.autopicks: - event_nautopicks = len(event.autopicks) + if event.pylot_picks: + event_npicks = len(event.pylot_picks) + if event.pylot_autopicks: + event_nautopicks = len(event.pylot_autopicks) item_path = QtGui.QTableWidgetItem() item_time = QtGui.QTableWidgetItem() item_lat = QtGui.QTableWidgetItem() @@ -2111,7 +2112,7 @@ class MainWindow(QMainWindow): set_enabled(item_path, True, False) set_enabled(item_nmp, True, False) set_enabled(item_nap, True, False) - if event.picks: + if event.pylot_picks: set_enabled(item_ref, True, True) set_enabled(item_test, True, True) else: @@ -2376,7 +2377,7 @@ class MainWindow(QMainWindow): self.setDirty(True) def setDirty(self, value): - self.saveProjectAction.setEnabled(value) + self.saveProjectAction.setEnabled(bool(self.get_current_event().picks)) self.saveProjectAsAction.setEnabled(True) self.project.setDirty(value) self.dirty = value diff --git a/pylot/core/io/data.py b/pylot/core/io/data.py index 14ed32eb..98ff0e39 100644 --- a/pylot/core/io/data.py +++ b/pylot/core/io/data.py @@ -10,7 +10,8 @@ from obspy.core.event import Event as ObsPyEvent from pylot.core.io.phases import readPILOTEvent, picks_from_picksdict, \ picksdict_from_pilot, merge_picks from pylot.core.util.errors import FormatError, OverwriteError -from pylot.core.util.utils import Event, fnConstructor, full_range +from pylot.core.util.utils import fnConstructor, full_range +from pylot.core.util.event import Event class Data(object): """ @@ -279,12 +280,12 @@ class Data(object): def setEvtData(self, event): self.evtdata = event - def applyEVTData(self, data, type='pick', authority_id='rub'): + def applyEVTData(self, data, typ='pick', authority_id='rub'): """ :param data: - :param type: + :param typ: :param authority_id: :raise OverwriteError: """ @@ -338,7 +339,7 @@ class Data(object): applydata = {'pick': applyPicks, 'event': applyEvent} - applydata[type](data) + applydata[typ](data) class GenericDataStructure(object): diff --git a/pylot/core/io/phases.py b/pylot/core/io/phases.py index 43ff2de3..30f8950c 100644 --- a/pylot/core/io/phases.py +++ b/pylot/core/io/phases.py @@ -7,6 +7,7 @@ import os import scipy.io as sio import warnings from obspy.core import UTCDateTime +from obspy.core.util import AttribDict from pylot.core.io.inputs import PylotParameter from pylot.core.io.location import create_arrival, create_event, \ @@ -231,7 +232,7 @@ def picks_from_picksdict(picks, creation_info=None): picks_list = list() for station, onsets in picks.items(): for label, phase in onsets.items(): - if not isinstance(phase, dict): + if not isinstance(phase, dict) and not isinstance(phase, AttribDict): continue onset = phase['mpp'] try: diff --git a/pylot/core/util/event.py b/pylot/core/util/event.py index 98d26e9a..48667a07 100644 --- a/pylot/core/util/event.py +++ b/pylot/core/util/event.py @@ -7,6 +7,8 @@ from obspy import UTCDateTime from obspy.core.event import Event as ObsPyEvent from obspy.core.event import Origin, Magnitude, ResourceIdentifier +from pylot.core.io.phases import picks_from_picksdict + class Event(ObsPyEvent): ''' @@ -65,8 +67,13 @@ class Event(ObsPyEvent): if bool: self._refEvent = False def addPicks(self, picks): + ''' + add pylot picks and overwrite existing + ''' for station in picks: self.pylot_picks[station] = picks[station] + #add ObsPy picks + self.picks = picks_from_picksdict(self.pylot_picks) def addAutopicks(self, autopicks): for station in autopicks: @@ -75,10 +82,15 @@ class Event(ObsPyEvent): def setPick(self, station, pick): if pick: self.pylot_picks[station] = pick - - def setPicks(self, picks): - self.pylot_picks = picks + self.picks = picks_from_picksdict(self.pylot_picks) + def setPicks(self, picks): + ''' + set pylot picks and delete and overwrite all existing + ''' + self.pylot_picks = picks + self.picks = picks_from_picksdict(self.pylot_picks) + def getPick(self, station): if station in self.pylot_picks.keys(): return self.pylot_picks[station] diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index 23c168c7..7cf891ed 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -1646,7 +1646,7 @@ class TuneAutopicker(QWidget): model = self.stationBox.model() for network, station in stations: item = QtGui.QStandardItem(network+'.'+station) - if station in self.get_current_event().picks: + if station in self.get_current_event().pylot_picks: item.setBackground(self.parent._colors['ref']) model.appendRow(item) @@ -1698,13 +1698,13 @@ class TuneAutopicker(QWidget): def get_current_event_picks(self, station): event = self.get_current_event() - if station in event.picks.keys(): - return event.picks[station] + if station in event.pylot_picks.keys(): + return event.pylot_picks[station] def get_current_event_autopicks(self, station): event = self.get_current_event() - if event.autopicks: - return event.autopicks[station] + if event.pylot_autopicks: + return event.pylot_autopicks[station] def get_current_station(self): return str(self.stationBox.currentText()).split('.')[-1] @@ -1917,8 +1917,8 @@ class TuneAutopicker(QWidget): self._warn('Could not execute picker:\n{}'.format( self.ap_thread._executedError)) return - self.picks = self.ap_thread.data - if not self.picks: + self.pylot_picks = self.ap_thread.data + if not self.pylot_picks: self._warn('No picks found. See terminal output.') return #renew tabs From 581a85b81f01f41560fe6c8ee6f8854224ab8518 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Mon, 26 Jun 2017 13:58:26 +0200 Subject: [PATCH 17/35] [bugfix] missing network code in picksdict_from_picks triggered silent Exception using picks generated by picks_from_picksdict --- pylot/core/io/data.py | 2 +- pylot/core/io/phases.py | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/pylot/core/io/data.py b/pylot/core/io/data.py index 98ff0e39..eb424094 100644 --- a/pylot/core/io/data.py +++ b/pylot/core/io/data.py @@ -61,7 +61,7 @@ class Data(object): else: raise e else: # create an empty Event object - self.setNew() + #self.setNew() evtdata = ObsPyEvent() evtdata.picks = [] self.evtdata = evtdata diff --git a/pylot/core/io/phases.py b/pylot/core/io/phases.py index 30f8950c..e513f4b6 100644 --- a/pylot/core/io/phases.py +++ b/pylot/core/io/phases.py @@ -196,6 +196,7 @@ def picksdict_from_picks(evt): phase = {} station = pick.waveform_id.station_code channel = pick.waveform_id.channel_code + network = pick.waveform_id.network_code try: onsets = picks[station] except KeyError as e: @@ -216,6 +217,7 @@ def picksdict_from_picks(evt): phase['lpp'] = lpp phase['spe'] = spe phase['channel'] = channel + phase['network'] = network try: picker = str(pick.method_id) if picker.startswith('smi:local/'): From c07642653d0b7bd424ed46f90f5a22092080daac Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Mon, 26 Jun 2017 14:08:17 +0200 Subject: [PATCH 18/35] [change] default open path for load_data is event folder --- QtPyLoT.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index 567d57e0..9a51873b 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -968,7 +968,7 @@ class MainWindow(QMainWindow): caption = "Open an event file" fname = QFileDialog().getOpenFileName(self, caption=caption, filter=filt, - dir=self.getRoot()) + dir=self.get_current_event_path()) fname = fname[0] else: fname = str(action.data().toString()) From 42c288ad82950ed856788b6d15db4fab8016e45b Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Mon, 26 Jun 2017 14:10:40 +0200 Subject: [PATCH 19/35] [bugfix] return when no file is selected on load_data --- QtPyLoT.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/QtPyLoT.py b/QtPyLoT.py index 9a51873b..e608cc75 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -669,6 +669,8 @@ class MainWindow(QMainWindow): action = self.sender() if isinstance(action, QAction): fname = self.filename_from_action(action) + if not fname: + return self.set_fname(fname, type) data = dict(auto=self.autodata, manual=self.data) data[type] += Data(self, evtdata=fname) From b0405cf2139bb6ce7328dac24fc69bc77d6b9615 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Mon, 26 Jun 2017 14:22:00 +0200 Subject: [PATCH 20/35] [change] load AUTO picks button will be reused now (old function deprecated as there is only one file for both types of picks) to automatically load all event information for all events given (TODO: currently only XML files!) --- QtPyLoT.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index e608cc75..1917265f 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -314,21 +314,21 @@ class MainWindow(QMainWindow): # self.createNewEvent, # QKeySequence.New, newIcon, # "Create a new event.") - self.openmanualpicksaction = self.createAction(self, "Load &manual picks ...", + self.openmanualpicksaction = self.createAction(self, "Load event ...", self.load_data, "Ctrl+M", manupicksicon, - "Load manual picks for " + "Load event information for " "the displayed event.") self.openmanualpicksaction.setEnabled(False) self.openmanualpicksaction.setData(None) - self.openautopicksaction = self.createAction(self, "Load &automatic picks ... ", - self.load_autopicks, + self.openautopicksaction = self.createAction(self, "Load event information &automatically ... ", + self.load_multiple_data, "Ctrl+A", autopicksicon, - "Load automatic picks " - "for the displayed event.") + "Load event data automatically " + "for for all events.") self.openautopicksaction.setEnabled(False) self.openautopicksaction.setData(None) From bec86049049393e8a0b638a14bda73859e4af69e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Mon, 26 Jun 2017 14:47:36 +0200 Subject: [PATCH 21/35] autoPyLoT appends manual picks to xml-file, and manualPyLoT appends automatic picks to xml-file. --- autoPyLoT.py | 2 +- pylot/core/io/data.py | 35 +++++++++++++++++++++++++++++------ 2 files changed, 30 insertions(+), 7 deletions(-) diff --git a/autoPyLoT.py b/autoPyLoT.py index 84d24dea..a5ca4035 100755 --- a/autoPyLoT.py +++ b/autoPyLoT.py @@ -353,7 +353,7 @@ def autoPyLoT(input_dict=None, parameter=None, inputfile=None, fnames=None, even if evt is not None: data.applyEVTData(evt, 'event') fnqml = '%s/PyLoT_%s' % (event, evID) - data.exportEvent(fnqml) + data.exportEvent(fnqml, fnext='.xml', fcheck='manual') # HYPO71 hypo71file = '%s/PyLoT_%s_HYPO71_phases' % (event, evID) hypo71.export(picks, hypo71file, parameter) diff --git a/pylot/core/io/data.py b/pylot/core/io/data.py index eb424094..4b1f5018 100644 --- a/pylot/core/io/data.py +++ b/pylot/core/io/data.py @@ -145,12 +145,13 @@ class Data(object): # handle forbidden filenames especially on windows systems return fnConstructor(str(ID)) - def exportEvent(self, fnout, fnext='.xml'): + def exportEvent(self, fnout, fnext='.xml', fcheck='auto'): """ :param fnout: :param fnext: + :param fcheck: :raise KeyError: """ from pylot.core.util.defaults import OUTPUTFORMATS @@ -161,13 +162,35 @@ class Data(object): errmsg = '{0}; selected file extension {1} not ' \ 'supported'.format(e, fnext) raise FormatError(errmsg) + + # check for already existing xml-file + if fnext == '.xml': + if os.path.isfile(fnout + fnext): + print("xml-file already exists! Check content ...") + cat_old = read_events(fnout + fnext) + checkflag = 0 + for j in range(len(cat_old.events[0].picks)): + if cat_old.events[0].picks[j].method_id.id.split('/')[1] == fcheck: + print("Found %s pick(s), append to new catalog." % fcheck) + checkflag = 1 + break + if checkflag == 1: + self.get_evt_data().write(fnout + fnext, format=evtformat) + cat_new = read_events(fnout + fnext) + cat_new.append(cat_old.events[0]) + cat_new.write(fnout + fnext, format=evtformat) + else: + self.get_evt_data().write(fnout + fnext, format=evtformat) + else: + self.get_evt_data().write(fnout + fnext, format=evtformat) # try exporting event via ObsPy - try: - self.get_evt_data().write(fnout + fnext, format=evtformat) - except KeyError as e: - raise KeyError('''{0} export format - not implemented: {1}'''.format(evtformat, e)) + else: + try: + self.get_evt_data().write(fnout + fnext, format=evtformat) + except KeyError as e: + raise KeyError('''{0} export format + not implemented: {1}'''.format(evtformat, e)) def getComp(self): """ From c56a11634a90d9027ff703653d0ba00d60c35cb0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Mon, 26 Jun 2017 16:10:18 +0200 Subject: [PATCH 22/35] Uncomment setNew, though still not sure, what its all about. --- pylot/core/io/data.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pylot/core/io/data.py b/pylot/core/io/data.py index 4b1f5018..f1838abf 100644 --- a/pylot/core/io/data.py +++ b/pylot/core/io/data.py @@ -61,7 +61,7 @@ class Data(object): else: raise e else: # create an empty Event object - #self.setNew() + self.setNew() evtdata = ObsPyEvent() evtdata.picks = [] self.evtdata = evtdata From a0633cafa41b96ba1f29be57dd9227eb960f0b8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Mon, 26 Jun 2017 16:19:57 +0200 Subject: [PATCH 23/35] Prevent export of phases when tuning picker. --- autoPyLoT.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/autoPyLoT.py b/autoPyLoT.py index a5ca4035..61634842 100755 --- a/autoPyLoT.py +++ b/autoPyLoT.py @@ -354,13 +354,13 @@ def autoPyLoT(input_dict=None, parameter=None, inputfile=None, fnames=None, even data.applyEVTData(evt, 'event') fnqml = '%s/PyLoT_%s' % (event, evID) data.exportEvent(fnqml, fnext='.xml', fcheck='manual') - # HYPO71 - hypo71file = '%s/PyLoT_%s_HYPO71_phases' % (event, evID) - hypo71.export(picks, hypo71file, parameter) - # HYPOSAT - hyposatfile = '%s/PyLoT_%s_HYPOSAT_phases' % (event, evID) - hyposat.export(picks, hyposatfile, parameter) if locflag == 1: + # HYPO71 + hypo71file = '%s/PyLoT_%s_HYPO71_phases' % (event, evID) + hypo71.export(picks, hypo71file, parameter) + # HYPOSAT + hyposatfile = '%s/PyLoT_%s_HYPOSAT_phases' % (event, evID) + hyposat.export(picks, hyposatfile, parameter) # VELEST velestfile = '%s/PyLoT_%s_VELEST_phases.cnv' % (event, evID) velest.export(picks, velestfile, parameter, evt) From 71da6ded2b16cb6aeda491fbe0d05c74461578f0 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Mon, 26 Jun 2017 17:40:36 +0200 Subject: [PATCH 24/35] [bugfix] Error when getEventFromPath returned None --- pylot/core/util/widgets.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index 7cf891ed..6eda72b8 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -1868,11 +1868,12 @@ class TuneAutopicker(QWidget): path = self.eventBox.itemText(index) if project.getEventFromPath(path).isTestEvent(): for index in range(nevents): - path = self.eventBox.itemText(index) - if not project.getEventFromPath(index).isTestEvent(): - break - #in case all events are marked as test events - elif index == nevents - 1: + path = self.eventBox.itemText(index) + if project.getEventFromPath(index): + if not project.getEventFromPath(index).isTestEvent(): + break + #in case all events are marked as test events and last event is reached + if index == nevents - 1: index = -1 self.eventBox.setCurrentIndex(index) if not index == index_start: From 30e2e1df84d6856b5a327f3b9b44ae99ae312c02 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Mon, 26 Jun 2017 17:41:08 +0200 Subject: [PATCH 25/35] [bugfix] query for data flag/attribute isNew changed --- QtPyLoT.py | 1 + pylot/core/io/data.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index 1917265f..3426fa51 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -146,6 +146,7 @@ class MainWindow(QMainWindow): self.data = Data(self, lastEvent) else: self.data = Data(self) + self.data._new = False self.autodata = Data(self) if settings.value("user/FullName", None) is None: diff --git a/pylot/core/io/data.py b/pylot/core/io/data.py index f1838abf..fc8387fc 100644 --- a/pylot/core/io/data.py +++ b/pylot/core/io/data.py @@ -350,7 +350,7 @@ class Data(object): information on the event to the actual data :param event: """ - if not self.isNew(): + if self.isNew(): self.setEvtData(event) else: # prevent overwriting original pick information From 53089a32795adf86104b027b9490dc9997906851 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Mon, 26 Jun 2017 18:02:07 +0200 Subject: [PATCH 26/35] [add] rootpath will be applied to QSettings as well --- QtPyLoT.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/QtPyLoT.py b/QtPyLoT.py index 3426fa51..a889d278 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -818,6 +818,10 @@ class MainWindow(QMainWindow): } print('Warning: Could not automatically init folder structure. ({})'.format(e)) + settings = QSettings() + settings.setValue("data/dataRoot", dirs['rootpath']) + settings.sync() + if not self.project.eventlist: #init parameter object self.setParameter(show=False) From a8bfc3c68fa61be26aec4d8ce901600f14d94edb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Tue, 27 Jun 2017 09:44:38 +0200 Subject: [PATCH 27/35] Parameter eventID uses wildcard (*) if all events of database supposed to be processed. --- autoPyLoT.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/autoPyLoT.py b/autoPyLoT.py index 61634842..8348a14f 100755 --- a/autoPyLoT.py +++ b/autoPyLoT.py @@ -98,8 +98,8 @@ def autoPyLoT(input_dict=None, parameter=None, inputfile=None, fnames=None, even exf = ['root', 'dpath', 'dbase'] - if parameter.hasParam('eventID') and fnames == 'None': - dsfields['eventID'] = parameter.get('eventID') + if parameter['eventID'] is not '*' and fnames == 'None': + dsfields['eventID'] = parameter['eventID'] exf.append('eventID') datastructure.modifyFields(**dsfields) @@ -133,13 +133,13 @@ def autoPyLoT(input_dict=None, parameter=None, inputfile=None, fnames=None, even if not input_dict: # started in production mode datapath = datastructure.expandDataPath() - if fnames == 'None' and not parameter['eventID']: + if fnames == 'None' and parameter['eventID'] is '*': # multiple event processing # read each event in database events = [events for events in glob.glob(os.path.join(datapath, '*')) if os.path.isdir(events)] - elif fnames == 'None' and parameter['eventID']: + elif fnames == 'None' and parameter['eventID'] is not '*': # single event processing - events = glob.glob(os.path.join(datapath, parameter.get('eventID'))) + events = glob.glob(os.path.join(datapath, parameter['eventID'])) else: # autoPyLoT was initialized from GUI events = [] From 4e735b3672a3524617eeb23386c7ed0ff7fd3f3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Tue, 27 Jun 2017 09:57:00 +0200 Subject: [PATCH 28/35] [Bugfix] If too few onsets are available, no export of phases (except xml). --- autoPyLoT.py | 1 + 1 file changed, 1 insertion(+) diff --git a/autoPyLoT.py b/autoPyLoT.py index 8348a14f..96e26e73 100755 --- a/autoPyLoT.py +++ b/autoPyLoT.py @@ -274,6 +274,7 @@ def autoPyLoT(input_dict=None, parameter=None, inputfile=None, fnames=None, even else: print("autoPyLoT: No NLLoc-location file available!") print("No source parameter estimation possible!") + locflag = 9 else: # get theoretical P-onset times from NLLoc-location file locsearch = '%s/loc/%s.????????.??????.grid?.loc.hyp' % (nllocroot, nllocout) From 987f5c4d6219b3c5e270ad220f70d33501b64da7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Tue, 27 Jun 2017 10:11:11 +0200 Subject: [PATCH 29/35] Additional info for user. --- pylot/core/io/default_parameters.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pylot/core/io/default_parameters.py b/pylot/core/io/default_parameters.py index 18baca6c..35a3a619 100644 --- a/pylot/core/io/default_parameters.py +++ b/pylot/core/io/default_parameters.py @@ -14,7 +14,7 @@ defaults = {'rootpath': {'type': str, 'value': ''}, 'eventID': {'type': str, - 'tooltip': 'event ID for single event processing', + 'tooltip': 'event ID for single event processing (* for all events found in database)', 'value': ''}, 'extent': {'type': str, From ab260e45f270e5e3150e4f93c96ce72b2c49bfe4 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Tue, 27 Jun 2017 10:39:44 +0200 Subject: [PATCH 30/35] [add/bugfix] data._new = False after first picks application --- autoPyLoT.py | 1 + 1 file changed, 1 insertion(+) diff --git a/autoPyLoT.py b/autoPyLoT.py index 96e26e73..55aed248 100755 --- a/autoPyLoT.py +++ b/autoPyLoT.py @@ -351,6 +351,7 @@ def autoPyLoT(input_dict=None, parameter=None, inputfile=None, fnames=None, even # and fault mechanism calculation routines # ObsPy event object data.applyEVTData(picks) + data._new = False if evt is not None: data.applyEVTData(evt, 'event') fnqml = '%s/PyLoT_%s' % (event, evID) From a90e0bd78b83e3d5280b2ebb4ef1fcd73ad0b7ac Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Tue, 27 Jun 2017 10:49:11 +0200 Subject: [PATCH 31/35] [change] data._new moved to applyEVTData --- autoPyLoT.py | 1 - pylot/core/io/data.py | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/autoPyLoT.py b/autoPyLoT.py index 55aed248..96e26e73 100755 --- a/autoPyLoT.py +++ b/autoPyLoT.py @@ -351,7 +351,6 @@ def autoPyLoT(input_dict=None, parameter=None, inputfile=None, fnames=None, even # and fault mechanism calculation routines # ObsPy event object data.applyEVTData(picks) - data._new = False if evt is not None: data.applyEVTData(evt, 'event') fnqml = '%s/PyLoT_%s' % (event, evID) diff --git a/pylot/core/io/data.py b/pylot/core/io/data.py index fc8387fc..2efcb4c9 100644 --- a/pylot/core/io/data.py +++ b/pylot/core/io/data.py @@ -363,6 +363,8 @@ class Data(object): 'event': applyEvent} applydata[typ](data) + self._new = False + class GenericDataStructure(object): From 68ef1276174a19bf08c5a069d819154ebeb017a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Tue, 27 Jun 2017 11:46:21 +0200 Subject: [PATCH 32/35] [Bugfix] Network code was missing. --- pylot/core/io/phases.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pylot/core/io/phases.py b/pylot/core/io/phases.py index e513f4b6..f737e630 100644 --- a/pylot/core/io/phases.py +++ b/pylot/core/io/phases.py @@ -838,9 +838,10 @@ def merge_picks(event, picks): err = pick.time_errors phase = pick.phase_hint station = pick.waveform_id.station_code + network = pick.waveform_id.network_code method = pick.method_id for p in event.picks: if p.waveform_id.station_code == station and p.phase_hint == phase: - p.time, p.time_errors, p.method_id = time, err, method - del time, err, phase, station, method + p.time, p.time_errors, p.waveform_id.network_code, p.method_id = time, err, network, method + del time, err, phase, station, network, method return event From 02da277f04aee86036a298d4857927b146ec6b13 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Tue, 27 Jun 2017 13:54:13 +0200 Subject: [PATCH 33/35] [bugfix] loading data from XML used same event multiple times as it was still part of current data object --- QtPyLoT.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index a889d278..dacfa693 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -657,9 +657,7 @@ class MainWindow(QMainWindow): if not refresh: return if self.get_current_event().pylot_picks: - self.plotWaveformDataThread() - self.drawPicks(picktype=type) - self.draw() + self.refreshEvents() self.setDirty(True) def load_data(self, fname=None, type='manual', loc=False, draw=True, event=None, overwrite=False): @@ -673,16 +671,18 @@ class MainWindow(QMainWindow): if not fname: return self.set_fname(fname, type) - data = dict(auto=self.autodata, manual=self.data) - data[type] += Data(self, evtdata=fname) + #data = dict(auto=self.autodata, manual=self.data) + if not event: + event = self.get_current_event() + data = Data(self, event) + data += Data(self, evtdata=fname) + self.data = data print('Loading {} picks from file {}.'.format(type, fname)) if not loc: self.updatePicks(type=type, event=event) if draw: if self.get_current_event().pylot_picks: - self.plotWaveformDataThread() - self.drawPicks(picktype=type) - self.draw() + self.refreshEvents() self.setDirty(True) def add_recentfile(self, event): From a3487b66eab1aef2b65df7edc03c73d32fc03762 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludger=20K=C3=BCperkoch?= Date: Tue, 27 Jun 2017 13:59:00 +0200 Subject: [PATCH 34/35] [Bugfix] Take care that chosen window lengths fit to length of time series. --- pylot/core/analysis/magnitude.py | 2 ++ pylot/core/pick/picker.py | 8 ++++++++ 2 files changed, 10 insertions(+) diff --git a/pylot/core/analysis/magnitude.py b/pylot/core/analysis/magnitude.py index 867a302d..c1eee9cd 100644 --- a/pylot/core/analysis/magnitude.py +++ b/pylot/core/analysis/magnitude.py @@ -215,6 +215,8 @@ class LocalMagnitude(Magnitude): th = np.arange(0, len(sqH) * dt, dt) # get maximum peak within pick window iwin = getsignalwin(th, t0 - stime, self.calc_win) + ii = min([iwin[len(iwin)-1], len(th)]) + iwin = iwin[0:ii] wapp = np.max(sqH[iwin]) if self.verbose: print("Determined Wood-Anderson peak-to-peak amplitude for station {0}: {1} " diff --git a/pylot/core/pick/picker.py b/pylot/core/pick/picker.py index a6cd7452..ebf737a5 100644 --- a/pylot/core/pick/picker.py +++ b/pylot/core/pick/picker.py @@ -212,6 +212,14 @@ class AICPicker(AutoPicker): self.Data[0].data = self.Data[0].data * 1000000 # get signal window isignal = getsignalwin(self.Tcf, self.Pick, self.TSNR[2]) + ii = min([isignal[len(isignal)-1], len(self.Tcf)]) + isignal = isignal[0:ii] + try: + aic[isignal] + except IndexError as e: + msg = "Time series out of bounds! {}".format(e) + print(msg) + return # calculate SNR from CF self.SNR = max(abs(aic[isignal] - np.mean(aic[isignal]))) / \ max(abs(aic[inoise] - np.mean(aic[inoise]))) From f1f929537002065c37cda1dfb67d3e7cf8948ca4 Mon Sep 17 00:00:00 2001 From: Marcel Paffrath Date: Tue, 27 Jun 2017 15:06:28 +0200 Subject: [PATCH 35/35] [add] user prompt when event_id not matching on load_data --- QtPyLoT.py | 19 ++++++++++++++++++- pylot/core/io/data.py | 14 +++++++++++--- pylot/core/util/event.py | 3 ++- 3 files changed, 31 insertions(+), 5 deletions(-) diff --git a/QtPyLoT.py b/QtPyLoT.py index dacfa693..e429307e 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -675,7 +675,24 @@ class MainWindow(QMainWindow): if not event: event = self.get_current_event() data = Data(self, event) - data += Data(self, evtdata=fname) + try: + data_new = Data(self, evtdata=fname) + data += data_new + except ValueError: + qmb = QMessageBox(self, icon=QMessageBox.Question, + text='Warning: Missmatch in event identifiers {} and {}. Continue?'.format( + data_new.get_evt_data().resource_id, + data.get_evt_data().resource_id), + windowTitle='PyLoT - Load data warning') + qmb.setStandardButtons(QMessageBox.Yes | QMessageBox.No) + qmb.setDefaultButton(QMessageBox.No) + ret = qmb.exec_() + if ret == qmb.Yes: + data_new.setNew() + data += data_new + else: + return + self.data = data print('Loading {} picks from file {}.'.format(type, fname)) if not loc: diff --git a/pylot/core/io/data.py b/pylot/core/io/data.py index 2efcb4c9..47e74053 100644 --- a/pylot/core/io/data.py +++ b/pylot/core/io/data.py @@ -74,6 +74,8 @@ class Data(object): def __add__(self, other): assert isinstance(other, Data), "operands must be of same type 'Data'" + rs_id = self.get_evt_data().get('resource_id') + rs_id_other = other.get_evt_data().get('resource_id') if other.isNew() and not self.isNew(): picks_to_add = other.get_evt_data().picks old_picks = self.get_evt_data().picks @@ -85,7 +87,7 @@ class Data(object): self.evtdata = new.get_evt_data() elif self.isNew() and other.isNew(): pass - elif self.get_evt_data().get('id') == other.get_evt_data().get('id'): + elif rs_id == rs_id_other: other.setNew() return self + other else: @@ -354,10 +356,16 @@ class Data(object): self.setEvtData(event) else: # prevent overwriting original pick information - picks = copy.deepcopy(self.get_evt_data().picks) + event_old = self.get_evt_data() + print(event_old.resource_id, event.resource_id) + if not event_old.resource_id == event.resource_id: + print("WARNING: Missmatch in event resource id's: {} and {}".format( + event_old.resource_id, + event.resource_id)) + picks = copy.deepcopy(event_old.picks) event = merge_picks(event, picks) # apply event information from location - self.get_evt_data().update(event) + event_old.update(event) applydata = {'pick': applyPicks, 'event': applyEvent} diff --git a/pylot/core/util/event.py b/pylot/core/util/event.py index 48667a07..1db5fac7 100644 --- a/pylot/core/util/event.py +++ b/pylot/core/util/event.py @@ -15,8 +15,9 @@ class Event(ObsPyEvent): Pickable class derived from ~obspy.core.event.Event containing information on a single event. ''' def __init__(self, path): + self.pylot_id = path.split('/')[-1] # initialize super class - super(Event, self).__init__(resource_id=ResourceIdentifier(path.split('/')[-1])) + super(Event, self).__init__(resource_id=ResourceIdentifier('smi:local/'+self.pylot_id)) self.path = path self.database = path.split('/')[-2] self.datapath = path.split('/')[-3]