#!/usr/bin/env python ################################################################################ # Earthquake Event Selection. ################################################################################ import os import os.path import sys import re import shutil import time import tempfile try: import cpickle except: import pickle try: import wx except ImportError: import tkinter_error msg = ('Sorry, you have to install WxPython.\n' 'Get it from either:\n' r' N:\georisk\downloads\wxpython, or' '\n' ' [http://www.wxpython.org/download.php#binaries].') tkinter_error.tkinter_error(msg) print msg sys.exit(1) Imported_PyEmbeddedImage = True try: from wx.lib.embeddedimage import PyEmbeddedImage except ImportError: Imported_PyEmbeddedImage = False ###### # Various constants ###### # where the data lives if sys.platform == 'win32': BaseDirectory = r'N:\georisk_models\tsunami\models\PTHA' else: BaseDirectory = '/nas/gemd/georisk_models/tsunami/models/PTHA' # name of the fault name file (in multimux directory) FaultNameFilename = 'fault_list.txt' # program name and version APP_NAME = 'EventSelection' APP_VERSION = '1.0' # name of the configuration filename # GUI values are saved in this file for next time ConfigFilename = 'EventSelection.cfg' # name of the 'fault XY' output file FaultXYFilename = 'fault.xy' # name of the quake probability output file QuakeProbFilename = 'quake_prob.txt' # name of the output URS data module # note: the %d is where the event number goes URSDataFilename = 'urs_data_%6.6d.py' # name of the output bash params file # note: the %d is where the event number goes GridBashScriptName = 'faults_%6.6d.params' # map display name to directory name Regions = [ ('Australia', 'Australia'), ('India', 'Indian'), ('SW Pacific', 'SW_Pacific') ] # pattern string used to split multimax data SpacesPatternString = ' +' # generate 're' pattern for 'any number of spaces' SpacesPattern = re.compile(SpacesPatternString) # Global holding current results sub-directory ResultSubdir = '' # a small value to perturb the user limits Epsilon = 1.0E-6 # GUI definitions BOX_WIDTH = 400 BOX_HEIGHT = 360 BOX2_HEIGHT = 285 FORM_WIDTH = BOX_WIDTH + 15 FORM_HEIGHT = 755 START_YOFFSET = 7 OUT_TEXT_XOFFSET = 8 OUT_TEXT_YOFFSET = 11 OUTPUT_BOX_HEIGHT = 45 GEN_YOFFSET = 15 GEN_DELTAY = 25 GEN_LABELXOFFSET = 5 GEN_TEXTXOFFSET = BOX_WIDTH/2 + GEN_LABELXOFFSET + 10 TEXTLIST_WIDTH = BOX_WIDTH - 10 TEXTLIST_HEIGHT = 200 CTL_WIDTH = BOX_WIDTH / 2 CTL_HEIGHT = 22 BUTTON_WIDTH = 100 BUTTON_HEIGHT = 30 DOUBLE_BUTTON_OFFSET = 8 COMBO_FUDGE = 2 # Copies of various parameters Region = '' GaugeNumber = '' MinimumHeight = '' MaximumHeight = '' ################################################################################ # This code was generated by img2py.py # Embed the ICON image here, so we don't need an external *.ico file. # Icon image inspired by Hokusai's print "The Great Wave off Kanagawa" # (Kanagawa okinami ura) from the "Thirty-Six Views of Mount Fuji" series. # http://commons.wikimedia.org/wiki/Image:The_Great_Wave_off_Kanagawa.jpg ################################################################################ if Imported_PyEmbeddedImage: def getIcon(): return PyEmbeddedImage( "iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABHNCSVQICAgIfAhkiAAABDBJ" "REFUWIXtlmtsFFUUx393dnbbbh+hlPQVKEEBKSLyEEEhqFQSNYAQNahAIJJUidEEtUQIRgk+" "GkSMJBJjUEADCVghtipSjeWhWIoh5VXoBqgU2aWvLbt0H52dmesH6La1U7OVbvCD59PcmTPn" "/M69/3vuFYDkFppyK5P/D/CfAFD//kIgcGbkkpk/GYczjat/umiqrUIaBgA2RwKG1h71lxZP" "/xJAkJiazn3LNnD7g8+gqI5o0Iaawxza+CJ3zlnGiIKF+C6f5+iWVTicaaRkDSXY4uHCwa/Q" "20N9BhGAdA7IIjVnGNNf3Uz60NEghYWr7PJLz3GguZ6Tuz/C8/teQoFrDBoxEefAbPyeOtzV" "FZhGxBpg9voDMnvMVIRi6xN57xVJJF0LkDScreSHVbNob/P28Fdyxk7vt+TX0wmuz46k/si3" "uMq3kTVqCmPmvWTp30OE/WWu8q1UbV5J2N9C7rgZvS5BXACklFTveI9BIycweNKj1FeWcWrP" "RojOTqfdVB8QvSheILEnp6EF/OgBPwNvG8v8LS6e3lrLhAWrUZTOukVhuYzLYSRNo4u2ZLfi" "qza/zvFd7yOR8euEXZNfOFjCzsV3sOu5fLwXTzP84UXRuYt7K24+d4yK4oUEvW600DXqDpTQ" "1ngxulHjtgs67OLhUgxDx56cxl1zX0ZKk0MbCqPf46aBDpPSIBJqI3y1Gb/7HJ4T+6nd+xlB" "X1NXAAlYtV/LkN18TSNCqLUR58DsmBtaqNXD9ysfwVt3skMDsSa/7msaEa6c/pWg14PeHmTn" "khGUvTKda1cuWAI3nj3CH7/soaHmNwy9naT0bKYUrgdE7yIUwmplJH73OUqXT+OnNU9wfNc6" "bPZEFNVO45lKKtYtBiRhfzOGFqZj39WUbuLHtU9SunwquwvHEWhxkzF8AopN7R1AWpyIquZn" "3xuz8F2qZdKSNeTdM5MzO9/BnpBE7riHULU2VAWqd7zL1y+Mp7W+BhDcPX8FAsHo2cuItAcJ" "eT00uaqQht43Edbu+5wDHyzt8T5j2BimLV2Lt+EyWijAsS/ewjQN0vPymfvxUYRQ2P7sEOyJ" "yeTPep6w30tN2Sa0gK9v2/D8z9u7z4iqous6LXWn+Gb1PBwOB5qmkZiaQcGKbZS/+Tju6goG" "T5yJPSkV32UXlZ8WdYsRcyMSSPRQoHMsBKZpdvPRNA2AlMwhaG1eMkdNJiUrj5CviUDTJcu4" "MQNIIDlraOdYyh4AHdZSd4JIOMhjxfsAyf7iRTdEaVFYrBqwKZLqkg+p/OQ1ZAz3PiEENjUB" "U9cwpTUogBLrJVJKGD3jKewJSTH6S/RI+B+T3wCIrQmZUmAbMJh7F6xAtfXfEdLH01Awcl4R" "eeMfwOFw3AoAUBOc3F/0JZnDx5OUFNty9CsAgDM9h4K3vyNvyhxSUlJvCuAvPDCzruPdn6UA" "AAAASUVORK5CYII=") ################################################################################ # An object that behaves like a dictionary but is pickled/unpickled from a file. ################################################################################ class Config(object): """An object that behaves like a dictionary but is persistent cfg = Config('filename') cfg['save'] = 'A saved value' cfg[123] = 'value 123' print cfg['save'] The values stored in the file 'filename' will be available to any application using that config file. """ def __init__(self, configfile=None): """__init__(self, String filename=None) -> Config""" self.delconf = False self.cfgdict = {} self.changed = False if not configfile: self.delconf = True configfile =tempfile.mktemp() self.configfile = os.path.abspath(configfile) if os.path.exists(self.configfile): try: f = open(configfile, "r") u = pickle.Unpickler(f) self.cfgdict = u.load() except pickle.UnpicklingError, e: print e except: pass else: f.close() def __setitem__(self, key, value): """Override to allow: cfg[] = """ self.cfgdict[key] = value self.changed = True def __getitem__(self, key): """Override to allow: = cfg[]""" return self.cfgdict.get(key, None) def __str__(self): """__str__(self) -> String""" return "" % hex(id(self)) def getfilename(self): """getfilename(self) -> String filename""" return self.configfile def setdeleted(self): """setdeleted(self)""" self.delconf = True def save(self): """save(self) -> save data to a file""" try: f = open(self.configfile, "w") p = pickle.Pickler(f) p.dump(self.cfgdict) except pickle.PicklingError, e: print e else: f.close() self.changed = False def close(self): """close(self) - close the save file""" if self.changed: self.save() if self.delconf: if os.path.exists(self.configfile): os.remove(self.configfile) def __del__(self): """__del__(self) - ensure save file is closed""" self.close() ################################################################################ # This code implements the logic of David Burbidge's 'list_quakes' program. # Except the two output files are now in CSV format. ################################################################################# ## # @brief Function to do all the work - list the quakes selected. def list_quakes(event_num, min_height, max_height, InvallFilename, TStarFilename, FaultXYFilename, QuakeProbFilename): ## # @brief Class to hold i_invall data class Inval(object): def __init__(self, lon, lat): self.lon = lon self.lat = lat def __str__(self): return '.lon=%g, .lat=%g' % (self.lon, self.lat) ## # @brief Class to hold T-**** data class TStar(object): def __init__(self, ipt, zquake, zprob, mag, slip, ng, ng_data): self.ipt = ipt self.zquake = zquake self.zprob = zprob self.mag = mag self.slip = slip self.ng_data = [int(i) for i in SpacesPattern.split(ng_data)] if len(self.ng_data) != ng: raise RuntimeError(1, "Error parsing T-***** data: %s" % msg) def __str__(self): return ('.ipt=%d, .zquake=%g, .zprob=%g, .mag=%g, .slip=%g, .ng_data=%s' % (self.ipt, self.zquake, self.zprob, self.mag, self.slip, str(self.ng_data))) # read i_invall file try: fd = open(InvallFilename, "r") invall_lines = fd.readlines() fd.close() except IOError, msg: raise RuntimeError(1, "Error reading file '%s': %s" % (InvallFilename, msg)) # trash the first three lines invall_lines = invall_lines[3:] # split i_invall data into fields invall_data = [] for line in invall_lines: l = line.strip() (lon, lat, _) = SpacesPattern.split(l, maxsplit=2) invall_data.append(Inval(float(lon), float(lat))) del invall_lines # now read T-**** file, creating filename from event # try: fd = open(TStarFilename, "r") tstar_lines = fd.readlines() fd.close() except IOError, msg: raise RuntimeError(1, "Error reading file: %s" % msg) # trash the first line of T-**** data tstar_lines = tstar_lines[1:] # get the data from tstar_lines tstar_data = [] min_wave = min_height - Epsilon max_wave = max_height + Epsilon for (ipt, line) in enumerate(tstar_lines): l = line.strip() (zquake, zprob, mag, slip, ng, ng_data) = SpacesPattern.split(l, maxsplit=5) zquake = float(zquake) zprob = float(zprob) mag = float(mag) slip = float(slip) ng = int(ng) ng_data = ng_data.strip() # only remember the data if zquake in wave height range and zprob > 0.0 if zprob > 0.0 and (min_wave <= zquake <= max_wave): tstar_data.append(TStar(ipt+1, zquake, zprob, mag, slip, ng, ng_data)) del tstar_lines # write out lines joining centroids try: outfd = open(FaultXYFilename, "w") except IOError, msg: raise RuntimeError(1, "Error opening output file: %s" % msg) outfd.write('Lon,Lat,Quake_ID,Subfault_ID\n') for t in tstar_data: for n in t.ng_data: outfd.write('%.4f,%.4f,%d,%d\n' % (invall_data[n].lon, invall_data[n].lat, t.ipt, n)) outfd.close() # write out quake probabilities try: outfd = open(QuakeProbFilename, "w") except IOError, msg: raise RuntimeError(1, "Error opening output file: %s" % msg) outfd.write('Quake_ID,Ann_Prob,z_max(m),Mag\n') for t in tstar_data: outfd.write('%d,%.5G,%.5f,%.2f\n' % (t.ipt, t.zprob, t.zquake, t.mag)) outfd.close() ################################################################################ # This code does exactly what David Burbidge's 'get_multimux' program does. ################################################################################ def get_multimux(quake_ID, multimux_dir, fault_event_filename): # get the fault name data filename = os.path.join(multimux_dir, FaultNameFilename) try: fd = open(filename, "r") fault_names = [ fn.strip() for fn in fd.readlines() ] fd.close() except IOError, msg: raise RuntimeError(1, "Error reading file: %s" % msg) # open the output file try: outfd = open(fault_event_filename, "w") except IOError, msg: raise RuntimeError(1, "Error opening output file: %s" % msg) # handle each fault nquake = 0 for fn in fault_names: # create the filename for the multimux data file mmx_filename = 'i_multimux-%s' % fn mmx_filename = os.path.join(multimux_dir, mmx_filename) # Read all data in file, checking as we go try: infd = open(mmx_filename, "r") except IOError, msg: raise RuntimeError(1, "Error opening file: %s" % msg) # check fault name in file is as expected mux_faultname = infd.readline().strip() if mux_faultname != fn: raise RuntimeError(1, "Error reading file") # read data while True: # get number of subfaults, EOF means finished try: nsubfault = infd.readline() except IOError: raise RuntimeError(1, "Error reading file") if not nsubfault: break nsubfault = int(nsubfault) nquake += 1 if nquake == quake_ID: outfd.write(' %d\n' % nsubfault) for i in range(nsubfault): line = infd.readline() (subfaultname, slip, prob, mag, _) = \ SpacesPattern.split(line, maxsplit=4) subfaultname = subfaultname.strip() slip = float(slip) outfd.write(" %s %g\n" % (subfaultname, slip)) else: for i in range(nsubfault): try: infd.readline() except IOError: raise RuntimeError(1, "Something wrong at bottom of file %s" % mux_faultname) infd.close() outfd.close() ################################################################################ # Code to do the GUI # # The Frame code is buried under the "if __name__ ..." test since we are going # to import this file into verify.py to test the list_quake() and get_multimux() # functions. ################################################################################ if __name__ == '__main__': class MyFrame(wx.Frame): def __init__(self, parent, id, title, pos=wx.DefaultPosition, size=wx.DefaultSize, style=wx.DEFAULT_FRAME_STYLE): # Make the frame wx.Frame.__init__(self, parent, id, title, pos=(50, 50), size=(FORM_WIDTH, FORM_HEIGHT), style=(wx.DEFAULT_FRAME_STYLE & ~ (wx.RESIZE_BOX | wx.MAXIMIZE_BOX | wx.RESIZE_BORDER))) p = self.panel = wx.Panel(self, -1) if Imported_PyEmbeddedImage: tsunami = getIcon() icon = tsunami.GetIcon() self.SetIcon(icon) self.Center() self.Show(True) # place the 'output dir' textbox Y_OFFSET = START_YOFFSET wx.StaticBox(p, -1, 'output base directory', (3, 1), size=(BOX_WIDTH, OUTPUT_BOX_HEIGHT)) self.txtOutputDir = wx.TextCtrl(p, -1, size=(BOX_WIDTH-10, 20), pos=(OUT_TEXT_XOFFSET, Y_OFFSET+OUT_TEXT_YOFFSET)) self.txtOutputDir.SetToolTip(wx.ToolTip("Where your data files will be written")) self.txtOutputDir.Bind(wx.EVT_LEFT_DOWN, self.OnOutputDirSelect) Y_OFFSET += 50 # start laying out list_quake controls wx.StaticBox(p, -1, 'list_quakes', (3, Y_OFFSET), size=(BOX_WIDTH, BOX_HEIGHT)) sampleList = [ x for (x, y) in Regions ] Y_OFFSET += GEN_YOFFSET wx.StaticText(p, -1, 'Region', pos=(GEN_LABELXOFFSET, Y_OFFSET), size=(CTL_WIDTH, CTL_HEIGHT), style=wx.ALIGN_RIGHT) self.cbRegion = wx.ComboBox(p, 500, pos=(GEN_TEXTXOFFSET, Y_OFFSET-COMBO_FUDGE), size=(100,20), choices=sampleList, style=wx.CB_DROPDOWN) self.cbRegion.SetEditable(False) Y_OFFSET += GEN_DELTAY wx.StaticText(p, -1, 'Hazard index', pos=(GEN_LABELXOFFSET, Y_OFFSET), size=(CTL_WIDTH, CTL_HEIGHT), style=wx.ALIGN_RIGHT) self.txtTGN = wx.TextCtrl(p, -1, size=(100, 20), pos=(GEN_TEXTXOFFSET, Y_OFFSET)) Y_OFFSET += GEN_DELTAY wx.StaticText(p, -1, 'Minimum wave height', pos=(GEN_LABELXOFFSET, Y_OFFSET), size=(CTL_WIDTH, CTL_HEIGHT), style=wx.ALIGN_RIGHT) self.txtMinH = wx.TextCtrl(p, -1, size=(100, 20), pos=(GEN_TEXTXOFFSET, Y_OFFSET)) Y_OFFSET += GEN_DELTAY wx.StaticText(p, -1, 'Maximum wave height', pos=(GEN_LABELXOFFSET, Y_OFFSET), size=(CTL_WIDTH, CTL_HEIGHT), style=wx.ALIGN_RIGHT) self.txtMaxH = wx.TextCtrl(p, -1, size=(100, 20), pos=(GEN_TEXTXOFFSET, Y_OFFSET)) Y_OFFSET += GEN_DELTAY x = FORM_WIDTH/2 - BUTTON_WIDTH/2 self.btnGenerate = wx.Button(p, label="List", pos=(x, Y_OFFSET), size=(100, BUTTON_HEIGHT)) Y_OFFSET += GEN_DELTAY + BUTTON_HEIGHT/2 self.lstGenerate = wx.ListCtrl(p, -1, pos=(8, Y_OFFSET), size=(TEXTLIST_WIDTH,TEXTLIST_HEIGHT), style=wx.LC_REPORT|wx.LC_SINGLE_SEL) Y_OFFSET += TEXTLIST_HEIGHT + 20 self.Bind(wx.EVT_BUTTON, self.GenerateClick, self.btnGenerate) self.lstGenerate.Bind(wx.EVT_LIST_ITEM_SELECTED, self.OnItemSelected, self.lstGenerate) # start laying out get_multimux controls wx.StaticBox(p, -1, 'multimux_grid', (3, Y_OFFSET), size=(BOX_WIDTH, BOX2_HEIGHT)) Y_OFFSET += 18 wx.StaticText(p, -1, 'Quake ID', pos=(GEN_LABELXOFFSET, Y_OFFSET), size=(CTL_WIDTH, CTL_HEIGHT), style=wx.ALIGN_RIGHT) self.txtQuakeID = wx.TextCtrl(p, -1, size=(100, 20), pos=(GEN_TEXTXOFFSET, Y_OFFSET)) Y_OFFSET += GEN_DELTAY x = FORM_WIDTH/2 - BUTTON_WIDTH - DOUBLE_BUTTON_OFFSET self.btnMultimux = wx.Button(p, label="Multimux", pos=(x, Y_OFFSET), size=(100, BUTTON_HEIGHT)) x = FORM_WIDTH/2 + DOUBLE_BUTTON_OFFSET self.btnGrid = wx.Button(p, label="Grid", pos=(x, Y_OFFSET), size=(100, BUTTON_HEIGHT)) Y_OFFSET += GEN_DELTAY + BUTTON_HEIGHT/2 self.lstMultimux = wx.ListCtrl(p, -1, pos=(8, Y_OFFSET), size=(TEXTLIST_WIDTH,TEXTLIST_HEIGHT), style=wx.LC_REPORT|wx.LC_SINGLE_SEL) Y_OFFSET += TEXTLIST_HEIGHT + 20 self.Bind(wx.EVT_BUTTON, self.MultimuxClick, self.btnMultimux) self.Bind(wx.EVT_BUTTON, self.GridClick, self.btnGrid) self.Bind(wx.EVT_CLOSE, self.doStateSave) self.doStateRestore() def OnOutputDirSelect(self, event): dlg = wx.DirDialog(self, "Choose an output directory:", style=wx.DD_DEFAULT_STYLE #| wx.DD_DIR_MUST_EXIST #| wx.DD_CHANGE_DIR ) # If the user selects OK, then we process the dialog's data. # This is done by getting the path data from the dialog - BEFORE # we destroy it. if dlg.ShowModal() == wx.ID_OK: self.txtOutputDir.Clear() self.txtOutputDir.WriteText(dlg.GetPath()) self.cbRegion.SetFocus() # Only destroy a dialog after you're done with it. dlg.Destroy() def doStateSave(self, event): """ Save state here. """ self.cfg['OutputDirectory'] = self.txtOutputDir.GetValue() self.cfg['Region'] = self.cbRegion.GetValue() self.cfg['GaugeNumber'] = self.txtTGN.GetValue() self.cfg['MinimumHeight'] = self.txtMinH.GetValue() self.cfg['MaximumHeight'] = self.txtMaxH.GetValue() self.cfg.save() event.Skip(True) def doStateRestore(self): """ Restore state here - globals have been set and tested sane """ global Region, GaugeNumber, MinimumHeight, MaximumHeight cfg = Config(ConfigFilename) output_dir = cfg['OutputDirectory'] Region = cfg['Region'] GaugeNumber = cfg['GaugeNumber'] if GaugeNumber: GaugeNumber = int(GaugeNumber) MinimumHeight = cfg['MinimumHeight'] if MinimumHeight: MinimumHeight = float(MinimumHeight) MaximumHeight = cfg['MaximumHeight'] if MaximumHeight: MaximumHeight = float(MaximumHeight) self.cfg = cfg region_name = None for (name, dir) in Regions: if name == Region: region_name = name if not region_name: Region = '' if output_dir: self.txtOutputDir.WriteText(output_dir) self.cbRegion.SetStringSelection(Region) if GaugeNumber: self.txtTGN.WriteText(str(GaugeNumber)) if MinimumHeight: self.txtMinH.WriteText(str(MinimumHeight)) if MaximumHeight: self.txtMaxH.WriteText(str(MaximumHeight)) def OnItemSelected(self, event): item = self.lstGenerate.GetItem(event.m_itemIndex, 0) self.txtQuakeID.SetValue(item.GetText()) def error(self, msg): dlg = wx.MessageDialog(self, msg, 'Error', wx.OK | wx.ICON_INFORMATION) dlg.ShowModal() dlg.Destroy() ###### # Call the list_quakes function. ###### def GenerateClick(self, event): global ResultSubdir, Region, GaugeNumber, MinimumHeight, MaximumHeight # check that everything has been entered correctly output_base_dir = self.txtOutputDir.GetValue() if len(output_base_dir) == 0: self.error('You must select an output base directory first') return region_select = self.cbRegion.GetValue() if len(region_select) == 0: self.error('You must select a region first') return try: gauge_number = int(self.txtTGN.GetValue()) except: self.error("You must enter a Hazard Index Number") return try: min_height = float(self.txtMinH .GetValue()) except: self.error("You must enter a minimum wave height in metres (eg, 1.3)") return try: max_height = float(self.txtMaxH.GetValue()) except: self.error("You must enter a maximum wave height in metres (eg, 1.8)") return if min_height > max_height: self.error("Minimum wave height must be less than maximum height") return # set directory names basename = None region_name = None for (name, dir) in Regions: if name == region_select: region_name = name basename = dir base_directory = os.path.join(BaseDirectory, basename) if not os.path.exists(base_directory): self.error("Sorry, region %s doesn't exist yet (%s)" % (region_name, base_directory)) return tFilesDir = os.path.join(base_directory, 'Tfiles') multimuxDir = os.path.join(base_directory, 'multimux') # create a sub-directory for the result files ResultSubdir = os.path.join(output_base_dir, 'Results_%s_%d_%.2f_%.2f' % (region_select, gauge_number, min_height, max_height)) try: shutil.rmtree(ResultSubdir) except: pass try: os.mkdir(ResultSubdir) except WindowsError, msg: self.error("Sorry, don't seem to be able to make " "the result directory '%s':\n%s" % (ResultSubdir, str(msg))) return except: self.error("Some sort of error making the result directory '%s'" % ResultSubdir) return # set names of the output files (in dir '/Results*') faultxy_filename = os.path.join(ResultSubdir, FaultXYFilename) quakeprob_filename = os.path.join(ResultSubdir, QuakeProbFilename) # now check that gauge number is valid gaugeFile = os.path.join(tFilesDir, 'T-%05d' % gauge_number) if not os.path.exists(gaugeFile): self.error("T file for hazard %d doesn't exist" % gauge_number) return # check we have an i_invall file invallFile = os.path.join(multimuxDir, 'i_invall') if not os.path.exists(invallFile): self.error("'i_invall' file doesn't exist: %s" % invallFile) return # save region, hazard #, min and max heights to globals Region = region_select GaugeNumber = gauge_number MinimumHeight = min_height MaximumHeight = max_height # clear the Quake ID textbox self.txtQuakeID.SetValue('') # now process the data we have # clear output list self.lstGenerate.ClearAll() # delete output files try: os.unlink(FaultXYFilename) except: pass try: os.unlink(QuakeProbFilename) except: pass #################################################################### # reset button label, hourglass the cursor self.btnGenerate.SetLabel('Listing ...') self.panel.SetCursor(wx.StockCursor(wx.CURSOR_WAIT)) wx.Yield() # allow GUI refresh # generate output files try: list_quakes(gauge_number, min_height, max_height, invallFile, gaugeFile, faultxy_filename, quakeprob_filename) except RuntimeError, msg: self.error("Error in list_quakes module: %s" % msg) self.panel.SetCursor(wx.StockCursor(wx.CURSOR_ARROW)) self.btnGenerate.SetLabel('List') return # put cursor back to normal, button label back to normal self.panel.SetCursor(wx.StockCursor(wx.CURSOR_ARROW)) self.btnGenerate.SetLabel('List') #################################################################### # list result file, if any if not os.path.exists(quakeprob_filename): self.error("Error generating data: file '%s' not found" % quakeprob_filename) return in_fd = open(quakeprob_filename, 'r') lines = in_fd.readlines() in_fd.close() hdr = lines[0].split(',') for (i, h) in enumerate(hdr): self.lstGenerate.InsertColumn(i, h.strip()) for (i, l) in enumerate(lines[1:]): l = l.split(',') index = self.lstGenerate.InsertStringItem(sys.maxint, l[0].strip()) for (ii, ll) in enumerate(l[1:]): self.lstGenerate.SetStringItem(index, ii+1, ll.strip()) column_width = FORM_WIDTH / len(hdr) - 20 for i in range(len(hdr)): self.lstGenerate.SetColumnWidth(i, column_width) ###### # Call the get_multimux function. ###### def MultimuxClick(self, event): # check we have an output base directory output_base_dir = self.txtOutputDir.GetValue() if len(output_base_dir) == 0: self.error('You must select an output base directory first') return # check we have a quake ID quake_ID = self.txtQuakeID.GetValue() if len(quake_ID) == 0: self.error('You must enter a quake ID first') return try: quake_ID = int(quake_ID) except: self.error("You must enter a Quake ID integer") return region_select = self.cbRegion.GetValue() if len(region_select) == 0: self.error('You must select a region first') return region_dir_name = None for (disp, real) in Regions: if disp == region_select: region_dir_name = real break if region_dir_name is None: self.error('INTERNAL ERROR: region_dir_name is None?!') return multimux_dir = os.path.join(BaseDirectory, region_dir_name, 'multimux') #################################################################### # clear list, set button label, hourglass the cursor self.lstMultimux.ClearAll() self.lstMultimux.InsertColumn(0, '') self.btnMultimux.SetLabel('Multimuxing ...') self.panel.SetCursor(wx.StockCursor(wx.CURSOR_WAIT)) fault_event_dir = os.path.join(output_base_dir, str(quake_ID)) if not os.path.exists(fault_event_dir): os.mkdir(fault_event_dir) fault_event_filename = os.path.join(fault_event_dir, 'event.list') wx.Yield() # allow GUI refresh try: get_multimux(quake_ID, multimux_dir, fault_event_filename) except RuntimeError, msg: self.error("Error in get_multimux module: %s" % msg) self.panel.SetCursor(wx.StockCursor(wx.CURSOR_ARROW)) self.btnMultimux.SetLabel('Multimux') return # cursor and button label to normal self.panel.SetCursor(wx.StockCursor(wx.CURSOR_ARROW)) self.btnMultimux.SetLabel('Multimux') #################################################################### # list resultant files, if any if not os.path.exists(fault_event_filename): self.error("Error generating data: file %s not found" % fault_event_filename) return in_fd = open(fault_event_filename, 'r') lines = in_fd.readlines() in_fd.close() hdr = lines[0].strip() hdr = SpacesPattern.split(hdr) for (i, h) in enumerate(hdr): self.lstMultimux.InsertColumn(i, h) for (i, l) in enumerate(lines[1:]): l = l.strip() l = SpacesPattern.split(l) index = self.lstMultimux.InsertStringItem(sys.maxint, l[0]) for (ii, ll) in enumerate(l[1:]): self.lstMultimux.SetStringItem(index, ii+1, ll) column_width = FORM_WIDTH / 2 - 30 for i in range(2): self.lstMultimux.SetColumnWidth(i, column_width) # generate the urs_filenames.py module from 'lines' data # get data from lines we read previously last_weight = None filenames = [] for line in lines[1:]: (_, filename, weight) = SpacesPattern.split(line) weight = float(weight) if last_weight: if last_weight != weight: self.error("In file '%s', inconsistent weight, " "was %.4f, expected %.4f" % (weight, last_weight)) return last_weight = weight filenames.append(filename) ## No longer generate the python data file ## # write data to the file ## urs_filename = '%s/%s' % (ResultSubdir, URSDataFilename % quake_ID) ## try: ## out_fd = open(urs_filename, "w") ## except IOError, msg: ## self.error("Error opening output file %s': %s" % ## (urs_filename, msg)) ## return ## if last_weight and filenames: ## out_fd.write('# Data for region %s,\n' ## '# hazard %d,\n' ## '# min height %.2f,\n' ## '# max height %.2f,\n' ## '# quake ID %d\n' % ## (Region, GaugeNumber, MinimumHeight, ## MaximumHeight, quake_ID)) ## out_fd.write('# Generated on %s.\n' % time.asctime(time.localtime())) ## out_fd.write('\n') ## out_fd.write('import os.path\n') ## out_fd.write('\n') ## out_fd.write('def getInfo(base_directory):\n') ## out_fd.write(' """\n') ## out_fd.write(' Return the weight factor and list of URS data filenames\n') ## out_fd.write('\n') ## out_fd.write(' (weight_factor, [filenames]) <- get_urs_data(base_directory)\n') ## out_fd.write('\n') ## out_fd.write(' where base_directory is the path to the mux files.\n') ## out_fd.write(' """\n') ## out_fd.write('\n') ## out_fd.write(' weight_factor = %f\n' % last_weight) ## out_fd.write(' mux_filenames = [') ## if filenames: ## for fn in filenames[:-1]: ## out_fd.write('"%%s" %% os.path.join(base_directory, "%s"),\n ' % fn) ## out_fd.write('"%%s" %% os.path.join(base_directory, "%s")\n ' % filenames[-1]) ## out_fd.write(']\n') ## out_fd.write('\n') ## out_fd.write(' return (weight_factor, mux_filenames)\n') ## out_fd.close() ###### # Call the get_grid function. ###### def GridClick(self, event): # check we have a quake ID quake_ID = self.txtQuakeID.GetValue() if len(quake_ID) == 0: self.error('You must enter a quake ID first') return try: quake_ID = int(quake_ID) except: self.error("You must enter a Quake ID") return region_select = self.cbRegion.GetValue() if len(region_select) == 0: self.error('You must select a region first') return region_dir_name = None for (disp, real) in Regions: if disp == region_select: region_dir_name = real break if region_dir_name is None: self.error('INTERNAL ERROR: region_dir_name is None?!') return multimux_dir = os.path.join(BaseDirectory, region_dir_name, 'multimux') grid_dir = os.path.join(BaseDirectory, region_dir_name, 'static_2m') #################################################################### # clear list, set button label, hourglass the cursor self.lstMultimux.ClearAll() self.lstMultimux.InsertColumn(0, '') self.btnGrid.SetLabel('Gridding ...') self.panel.SetCursor(wx.StockCursor(wx.CURSOR_WAIT)) fault_event_filename = ('%s/event_%6.6d.list' % (ResultSubdir, quake_ID)) wx.Yield() # allow GUI refresh try: get_multimux(quake_ID, multimux_dir, fault_event_filename) except RuntimeError, msg: self.error("Error in get_multimux module: %s" % msg) self.panel.SetCursor(wx.StockCursor(wx.CURSOR_ARROW)) self.btnGrid.SetLabel('Grid') return # cursor and button label to normal self.panel.SetCursor(wx.StockCursor(wx.CURSOR_ARROW)) self.btnGrid.SetLabel('Grid') #################################################################### # list resultant files, if any if not os.path.exists(fault_event_filename): self.error("Error generating data: file %s not found" % fault_event_filename) return # read output data file in_fd = open(fault_event_filename, 'r') lines = in_fd.readlines() in_fd.close() # get header items, display them hdr = lines[0].strip() hdr = SpacesPattern.split(hdr) for (i, h) in enumerate(hdr): self.lstMultimux.InsertColumn(i, h) # display resultant data, collect filenames and weight_factor weight_factor = None wf_error = False # True if there was a weight_factor error filenames = [] for (i, l) in enumerate(lines[1:]): l = l.strip() l = SpacesPattern.split(l) fname = l[0].replace('.grd-z-mux2', '.grd') filenames.append(fname) index = self.lstMultimux.InsertStringItem(sys.maxint, fname) for (ii, ll) in enumerate(l[1:]): self.lstMultimux.SetStringItem(index, ii+1, ll) if weight_factor: if weight_factor != float(l[1]): wf_error = True else: weight_factor = float(l[1]) column_width = FORM_WIDTH / 2 - 30 for i in range(2): self.lstMultimux.SetColumnWidth(i, column_width) if wf_error: self.error('Weight factor is not the same for all files!') return if not filenames: self.error('No grid files found!') return # generate the bash script param_file. out_name = GridBashScriptName % quake_ID grid_filename = '%s/%s' % (ResultSubdir, out_name) try: out_fd = open(grid_filename, "w") except IOError, msg: self.error("Error opening output file %s': %s" % (grid_filename, msg)) return out_fd.write('GENERATED=%s\n' % time.asctime(time.localtime())) out_fd.write('REGION=%s\n' % Region) out_fd.write('HAZARD_INDEX=%d\n' % GaugeNumber) out_fd.write('WAVE_HEIGHTS=[%.2f,%.2f]\n' % (MinimumHeight, MaximumHeight)) out_fd.write('QUAKE_ID=%d\n' % quake_ID) out_fd.write('WEIGHT_FACTOR=%f\n' % weight_factor) out_fd.write('DATA_PATH=%s\n' % grid_dir) for fn in filenames[1:]: owning_dir = filenames[0].split('-')[0] out_fd.write('GRID=%s\n' % (os.path.join(owning_dir, fn))) out_fd.write('FAULT=%s\n' % owning_dir) app = wx.App() frame = MyFrame(None, -1, '%s %s' % (APP_NAME, APP_VERSION), size=(FORM_WIDTH, FORM_HEIGHT)) app.SetTopWindow(frame) app.MainLoop()