diff --git a/QtPyLoT.py b/QtPyLoT.py index 8209d464..689e6988 100755 --- a/QtPyLoT.py +++ b/QtPyLoT.py @@ -35,8 +35,9 @@ from PySide.QtCore import QCoreApplication, QSettings, Signal, QFile, \ from PySide.QtGui import QMainWindow, QInputDialog, QIcon, QFileDialog, \ QWidget, QHBoxLayout, QStyle, QKeySequence, QLabel, QFrame, QAction, \ QDialog, QErrorMessage, QApplication, QPixmap, QMessageBox, QSplashScreen, \ - QActionGroup, QListWidget, QDockWidget + QActionGroup, QListWidget, QDockWidget, QLineEdit import numpy as np +import subprocess from obspy import UTCDateTime from pylot.core.io.data import Data @@ -44,7 +45,7 @@ from pylot.core.io.inputs import FilterOptions, AutoPickParameter from pylot.core.pick.autopick import autopickevent from pylot.core.pick.compare import Comparison from pylot.core.io.phases import picksdict_from_picks -from pylot.core.loc.nll import locate as locateNll +import pylot.core.loc.nll as nll from pylot.core.util.defaults import FILTERDEFAULTS, COMPNAME_MAP, \ AUTOMATIC_DEFAULTS from pylot.core.util.errors import FormatError, DatastructureError, \ @@ -61,7 +62,7 @@ from pylot.core.util.thread import AutoPickThread from pylot.core.util.version import get_git_version as _getVersionString import icons_rc -locateTool = dict(nll=locateNll) +locateTool = dict(nll=nll) class MainWindow(QMainWindow): @@ -630,6 +631,8 @@ class MainWindow(QMainWindow): ans = self.data.setWFData(self.fnames) elif self.fnames is None and self.okToContinue(): ans = self.data.setWFData(self.getWFFnames()) + else: + ans = False self._stime = getGlobalTimes(self.getData().getWFData())[0] if ans: self.plotWaveformData() @@ -889,12 +892,59 @@ class MainWindow(QMainWindow): raise TypeError('Unknow picktype {0}'.format(picktype)) def locateEvent(self): + """ + locate event using the manually picked phases + :return: + """ + if not self.okToContinue(): + return settings = QSettings() + # get location tool hook loctool = settings.value("loc/tool", "nll") - extlocpath = settings.value("%s/binPath".format(loctool), None) - locroot = settings.value("%s/rootPath".format(loctool), None) - if extlocpath is None or locroot is None: + lt = locateTool[loctool] + # get working directory + locroot = settings.value("{0}/rootPath".format(loctool), None) + if locroot is None: self.PyLoTprefs() + self.locateEvent() + + infile = settings.value("{0}/inputFile".format(loctool), None) + + if not infile: + caption = 'Select {0} input file'.format(loctool) + filt = "Supported file formats" \ + " (*.in *.ini *.conf *.cfg)" + ans = QFileDialog().getOpenFileName(self, caption=caption, + filter=filt, dir=locroot) + infile = ans[0] + settings.setValue("{0}/inputFile".format(loctool), infile) + settings.sync() + if loctool == 'nll': + ttt = settings.value("{0}/travelTimeTables", None) + ok = False + if ttt is None: + while not ok: + text, ok = QInputDialog.getText(self, 'Pattern for travel time tables', + 'Base name of travel time tables', + echo=QLineEdit.Normal, + text="ttime") + ttt = text + + outfile = settings.value("{0}/outputFile".format(loctool), + os.path.split(os.tempnam())[-1]) + phasefile = os.path.split(os.tempnam())[-1] + phasepath = os.path.join(locroot, 'obs', phasefile) + locpath = os.path.join(locroot, 'loc', outfile) + lt.export(self.getPicks(), phasepath) + lt.modify_inputs(infile, locroot, outfile, phasefile, ttt) + try: + lt.locate(infile) + except RuntimeError as e: + print(e.message) + finally: + os.remove(phasepath) + + self.getData().applyEVTData(lt.read_location(locpath), type='event') def check4Loc(self): return self.picksNum() > 4 diff --git a/autoPyLoT.py b/autoPyLoT.py index 89898ec9..01b430ab 100755 --- a/autoPyLoT.py +++ b/autoPyLoT.py @@ -6,13 +6,15 @@ from __future__ import print_function import argparse import glob import string +import os import numpy as np from pylot.core.analysis.magnitude import M0Mw from pylot.core.io.data import Data from pylot.core.io.inputs import AutoPickParameter -from pylot.core.loc.nll import * +import pylot.core.loc.nll as nll +import pylot.core.loc.hsat as hsat from pylot.core.pick.autopick import autopickevent, iteratepicker from pylot.core.util.structure import DATASTRUCTURE from pylot.core.util.version import get_git_version as _getVersionString @@ -112,16 +114,17 @@ def autoPyLoT(inputfile): # locating if locflag == 1: # write phases to NLLoc-phase file - picksExport(picks, 'NLLoc', phasefile) + nll.export(picks, phasefile) # For locating the event the NLLoc-control file has to be modified! evID = event[string.rfind(event, "/") + 1: len(events) - 1] nllocout = '%s_%s' % (evID, nllocoutpatter) # create comment line for NLLoc-control file - modifyInputFile(ctrf, nllocroot, nllocout, phasef, ttpat) + nll.modify_inputs(ctrf, nllocroot, nllocout, phasef, + ttpat) # locate the event - locate(nlloccall, ctrfile) + nll.locate(ctrfile) # !iterative picking if traces remained unpicked or occupied with bad picks! # get theoretical onset times for picks with weights >= 4 @@ -162,11 +165,11 @@ def autoPyLoT(inputfile): print("autoPyLoT: Starting with iteration No. %d ..." % nlloccounter) picks = iteratepicker(wfdat, nllocfile, picks, badpicks, parameter) # write phases to NLLoc-phase file - picksExport(picks, 'NLLoc', phasefile) + nll.export(picks, phasefile) # remove actual NLLoc-location file to keep only the last os.remove(nllocfile) # locate the event - locate(nlloccall, ctrfile) + nll.locate(ctrfile) print("autoPyLoT: Iteration No. %d finished." % nlloccounter) # get updated NLLoc-location file nllocfile = max(glob.glob(locsearch), key=os.path.getctime) @@ -199,15 +202,11 @@ def autoPyLoT(inputfile): # write phase files for various location routines # HYPO71 hypo71file = '%s/autoPyLoT_HYPO71.pha' % event - if hasattr(finalpicks, 'getpicdic'): - if finalpicks.getpicdic() is not None: - writephases(finalpicks.getpicdic(), 'HYPO71', hypo71file) - data.applyEVTData(finalpicks.getpicdic()) - else: - writephases(picks, 'HYPO71', hypo71file) - data.applyEVTData(picks) + if hasattr(finalpicks, 'getpicdic') and finalpicks.getpicdic() is not None: + hsat.export(finalpicks.getpicdic(), hypo71file) + data.applyEVTData(finalpicks.getpicdic()) else: - writephases(picks, 'HYPO71', hypo71file) + hsat.export(picks, hypo71file) data.applyEVTData(picks) fnqml = '%s/autoPyLoT' % event data.exportEvent(fnqml) @@ -235,15 +234,15 @@ def autoPyLoT(inputfile): # locating if locflag == 1: # write phases to NLLoc-phase file - picksExport(picks, 'NLLoc', phasefile) + nll.export(picks, phasefile) # For locating the event the NLLoc-control file has to be modified! nllocout = '%s_%s' % (parameter.get('eventID'), nllocoutpatter) # create comment line for NLLoc-control file - modifyInputFile(ctrf, nllocroot, nllocout, phasef, ttpat) + nll.modify_inputs(ctrf, nllocroot, nllocout, phasef, ttpat) # locate the event - locate(nlloccall, ctrfile) + nll.locate(ctrfile) # !iterative picking if traces remained unpicked or occupied with bad picks! # get theoretical onset times for picks with weights >= 4 # in order to reprocess them using smaller time windows around theoretical onset @@ -283,11 +282,11 @@ def autoPyLoT(inputfile): print("autoPyLoT: Starting with iteration No. %d ..." % nlloccounter) picks = iteratepicker(wfdat, nllocfile, picks, badpicks, parameter) # write phases to NLLoc-phase file - picksExport(picks, 'NLLoc', phasefile) + nll.export(picks, phasefile) # remove actual NLLoc-location file to keep only the last os.remove(nllocfile) # locate the event - locate(nlloccall, ctrfile) + nll.locate(ctrfile) print("autoPyLoT: Iteration No. %d finished." % nlloccounter) # get updated NLLoc-location file nllocfile = max(glob.glob(locsearch), key=os.path.getctime) @@ -320,15 +319,11 @@ def autoPyLoT(inputfile): # write phase files for various location routines # HYPO71 hypo71file = '%s/%s/autoPyLoT_HYPO71.pha' % (datapath, parameter.get('eventID')) - if hasattr(finalpicks, 'getpicdic'): - if finalpicks.getpicdic() is not None: - writephases(finalpicks.getpicdic(), 'HYPO71', hypo71file) - data.applyEVTData(finalpicks.getpicdic()) - else: - writephases(picks, 'HYPO71', hypo71file) - data.applyEVTData(picks) + if hasattr(finalpicks, 'getpicdic') and finalpicks.getpicdic() is not None: + hsat.export(finalpicks.getpicdic(), hypo71file) + data.applyEVTData(finalpicks.getpicdic()) else: - writephases(picks, 'HYPO71', hypo71file) + hsat.export(picks, hypo71file) data.applyEVTData(picks) fnqml = '%s/%s/autoPyLoT' % (datapath, parameter.get('eventID')) data.exportEvent(fnqml) diff --git a/pylot/core/io/data.py b/pylot/core/io/data.py index 71c6371e..1f4a6008 100644 --- a/pylot/core/io/data.py +++ b/pylot/core/io/data.py @@ -1,6 +1,7 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- +import copy import glob import os from obspy import read_events, read_inventory @@ -434,7 +435,8 @@ class Data(object): #firstonset = find_firstonset(picks) if self.getEvtData().picks: raise OverwriteError('Actual picks would be overwritten!') - picks = picks_from_picksdict(picks) + else: + picks = picks_from_picksdict(picks) self.getEvtData().picks = picks # if 'smi:local' in self.getID() and firstonset: # fonset_str = firstonset.strftime('%Y_%m_%d_%H_%M_%S') @@ -443,25 +445,22 @@ class Data(object): # self.getEvtData().resource_id = ID - def applyArrivals(arrivals): - """ - - :param arrivals: - """ - pass - def applyEvent(event): """ - + takes an `obspy.core.event.Event` object and applies all new + information on the event to the actual data :param event: """ if not self.isNew(): self.setEvtData(event) else: - raise OverwriteError('Acutal event would be overwritten!') + # prevent overwriting uncertainty information + picks = copy.deepcopy(self.getEvtData().picks) + event.picks = picks + # apply event information from location + self.getEvtData().update(event) applydata = {'pick': applyPicks, - 'arrival': applyArrivals, 'event': applyEvent} applydata[type](data) diff --git a/pylot/core/io/phases.py b/pylot/core/io/phases.py index 60d4d180..0de1d40a 100644 --- a/pylot/core/io/phases.py +++ b/pylot/core/io/phases.py @@ -117,7 +117,7 @@ def picksdict_from_pilot(fn): except IndexError as e: print(e.message + '\ntake two times the largest default error value') spe = timeerrors[onset_name][-1] * 2 - phases[onset_name] = dict(mpp=pick, spe=spe) + phases[onset_name] = dict(mpp=pick, spe=spe, weight=ierror) picks[station] = phases return picks @@ -395,7 +395,11 @@ def writephases(arrivals, fformat, filename): for key in arrivals: # P onsets if arrivals[key]['P']: - fm = arrivals[key]['P']['fm'] + try: + fm = arrivals[key]['P']['fm'] + except KeyError as e: + print(e) + fm = None if fm == None: fm = '?' onset = arrivals[key]['P']['mpp'] @@ -407,10 +411,12 @@ def writephases(arrivals, fformat, filename): ss = onset.second ms = onset.microsecond ss_ms = ss + ms / 1000000.0 - if arrivals[key]['P']['weight'] < 4: - pweight = 1 # use pick - else: - pweight = 0 # do not use pick + pweight = 1 # use pick + try: + if arrivals[key]['P']['weight'] >= 4: + pweight = 0 # do not use pick + except KeyError as e: + print(e.message + '; no weight set during processing') fid.write('%s ? ? ? P %s %d%02d%02d %02d%02d %7.4f GAU 0 0 0 0 %d \n' % (key, fm, year, @@ -421,7 +427,7 @@ def writephases(arrivals, fformat, filename): ss_ms, pweight)) # S onsets - if arrivals[key]['S']: + if arrivals[key].has_key('S') and arrivals[key]['S']: fm = '?' onset = arrivals[key]['S']['mpp'] year = onset.year @@ -432,10 +438,12 @@ def writephases(arrivals, fformat, filename): ss = onset.second ms = onset.microsecond ss_ms = ss + ms / 1000000.0 - if arrivals[key]['S']['weight'] < 4: - sweight = 1 # use pick - else: - sweight = 0 # do not use pick + sweight = 1 # use pick + try: + if arrivals[key]['S']['weight'] >= 4: + sweight = 0 # do not use pick + except KeyError as e: + print(str(e) + '; no weight set during processing') fid.write('%s ? ? ? S %s %d%02d%02d %02d%02d %7.4f GAU 0 0 0 0 %d \n' % (key, fm, year, diff --git a/pylot/core/loc/hsat.py b/pylot/core/loc/hsat.py index faa18be5..b191f21b 100644 --- a/pylot/core/loc/hsat.py +++ b/pylot/core/loc/hsat.py @@ -1,2 +1,21 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- + +from pylot.core.io.phases import writephases +from pylot.core.util.version import get_git_version as _getVersionString + +__version__ = _getVersionString() + +def export(picks, fnout): + ''' + Take dictionary and exports picking data to a NLLOC-obs + without creating an ObsPy event object. + + :param picks: picking data dictionary + :type picks: dict + + :param fnout: complete path to the exporting obs file + :type fnout: str + ''' + # write phases to NLLoc-phase file + writephases(picks, 'HYPO71', fnout) diff --git a/pylot/core/loc/nll.py b/pylot/core/loc/nll.py index b5a5522a..f9b36e76 100644 --- a/pylot/core/loc/nll.py +++ b/pylot/core/loc/nll.py @@ -3,14 +3,18 @@ import subprocess import os +import glob +from obspy import read_events from pylot.core.io.phases import writephases -from pylot.core.util.utils import getPatternLine, runProgram +from pylot.core.util.utils import getPatternLine, runProgram, which from pylot.core.util.version import get_git_version as _getVersionString __version__ = _getVersionString() +class NLLocError(EnvironmentError): + pass -def picksExport(picks, locrt, phasefile): +def export(picks, fnout): ''' Take dictionary and exports picking data to a NLLOC-obs without creating an ObsPy event object. @@ -18,17 +22,14 @@ def picksExport(picks, locrt, phasefile): :param picks: picking data dictionary :type picks: dict - :param locrt: choose location routine - :type locrt: str - - :param phasefile: complete path to the exporting obs file - :type phasefile: str + :param fnout: complete path to the exporting obs file + :type fnout: str ''' # write phases to NLLoc-phase file - writephases(picks, locrt, phasefile) + writephases(picks, 'NLLoc', fnout) -def modifyInputFile(ctrfn, root, nllocoutn, phasefn, tttn): +def modify_inputs(ctrfn, root, nllocoutn, phasefn, tttn): ''' :param ctrfn: name of NLLoc-control file :type: str @@ -66,24 +67,32 @@ def modifyInputFile(ctrfn, root, nllocoutn, phasefn, tttn): nllfile.close() -def locate(call, fnin): - ''' - Takes paths to NLLoc executable and input parameter file - and starts the location calculation. +def locate(fnin): + """ + takes an external program name + :param fnin: + :return: + """ - :param call: full path to NLLoc executable - :type call: str + exe_path = which('NLLoc') + if exe_path is None: + raise NLLocError('NonLinLoc executable not found; check your ' + 'environment variables') - :param fnin: full path to input parameter file - :type fnin: str - ''' - - # locate the event - runProgram(call, fnin) + # locate the event utilizing external NonLinLoc installation + try: + runProgram(exe_path, fnin) + except subprocess.CalledProcessError as e: + raise RuntimeError(e.output) -def readLocation(fn): - pass +def read_location(fn): + path, file = os.path.split(fn) + file = glob.glob1(path, file + '.[0-9]*.grid0.loc.hyp') + if len(file) > 1: + raise IOError('ambiguous location name {0}'.format(file)) + fn = os.path.join(path, file[0]) + return read_events(fn)[0] if __name__ == '__main__': diff --git a/pylot/core/util/utils.py b/pylot/core/util/utils.py index 9f7697dc..8aafbd9b 100644 --- a/pylot/core/util/utils.py +++ b/pylot/core/util/utils.py @@ -277,6 +277,15 @@ def getPatternLine(fn, pattern): return None +def is_executable(fn): + """ + takes a filename and returns True if the file is executable on the system + and False otherwise + :param fn: path to the file to be tested + :return: True or False + """ + return os.path.isfile(fn) and os.access(fn, os.X_OK) + def isSorted(iterable): ''' @@ -393,9 +402,44 @@ def runProgram(cmd, parameter=None): cmd.strip() cmd += ' %s 2>&1' % parameter - output = subprocess.check_output('{} | tee /dev/stderr'.format(cmd), - shell=True) + subprocess.check_output('{} | tee /dev/stderr'.format(cmd), shell=True) +def which(program): + """ + takes a program name and returns the full path to the executable or None + modified after: http://stackoverflow.com/questions/377017/test-if-executable-exists-in-python + :param program: name of the desired external program + :return: full path of the executable file + """ + try: + from PySide.QtCore import QSettings + settings = QSettings() + for key in settings.allKeys(): + if 'binPath' in key: + os.environ['PATH'] += ':{0}'.format(settings.value(key)) + except ImportError as e: + print(e.message) + + def is_exe(fpath): + return os.path.exists(fpath) and os.access(fpath, os.X_OK) + + def ext_candidates(fpath): + yield fpath + for ext in os.environ.get("PATHEXT", "").split(os.pathsep): + yield fpath + ext + + fpath, fname = os.path.split(program) + if fpath: + if is_exe(program): + return program + else: + for path in os.environ["PATH"].split(os.pathsep): + exe_file = os.path.join(path, program) + for candidate in ext_candidates(exe_file): + if is_exe(candidate): + return candidate + + return None if __name__ == "__main__": import doctest diff --git a/pylot/core/util/widgets.py b/pylot/core/util/widgets.py index 28cc1e85..a1e7f8c4 100644 --- a/pylot/core/util/widgets.py +++ b/pylot/core/util/widgets.py @@ -1337,8 +1337,8 @@ class LocalisationTab(PropTab): self.locToolComboBox.setCurrentIndex(toolind) - curroot = settings.value("%s/rootPath".format(curtool), None) - curbin = settings.value("%s/binPath".format(curtool), None) + curroot = settings.value("{0}/rootPath".format(curtool), None) + curbin = settings.value("{0}/binPath".format(curtool), None) self.rootlabel = QLabel("root directory") self.binlabel = QLabel("bin directory") @@ -1385,8 +1385,8 @@ class LocalisationTab(PropTab): def getValues(self): loctool = self.locToolComboBox.currentText() - values = {"%s/rootPath".format(loctool): self.rootedit.text(), - "%s/binPath".format(loctool): self.binedit.text(), + values = {"{0}/rootPath".format(loctool): self.rootedit.text(), + "{0}/binPath".format(loctool): self.binedit.text(), "loc/tool": loctool} return values