source: trunk/misc/tools/event_selection/EventSelection.py @ 8523

Last change on this file since 8523 was 7042, checked in by rwilson, 15 years ago

Cosmetic change to error message.

File size: 38.5 KB
Line 
1#!/usr/bin/env python
2################################################################################
3# Earthquake Event Selection.
4################################################################################
5
6
7import os
8import os.path
9import sys
10import re
11import shutil
12import time
13
14import tempfile
15try:
16    import cpickle as pickle
17except:
18    import pickle
19
20try:
21    import wx
22except ImportError:
23    import tkinter_error
24    msg = ('Sorry, you have to install WxPython.\n'
25           'Get it from either:\n'
26           r'   N:\georisk\downloads\wxpython, or' '\n'
27           '   [http://www.wxpython.org/download.php#binaries].')
28    tkinter_error.tkinter_error(msg)
29    print msg
30    sys.exit(1)
31
32   
33Imported_PyEmbeddedImage = True
34try:
35    from wx.lib.embeddedimage import PyEmbeddedImage
36except ImportError:
37    Imported_PyEmbeddedImage = False
38
39
40######
41# Various constants
42######
43
44# where the data lives
45if sys.platform == 'win32':
46    BaseDirectory = r'N:\georisk_models\tsunami\models\PTHA'
47else:
48    BaseDirectory = '/nas/gemd/georisk_models/tsunami/models/PTHA'
49
50# name of the fault name file (in multimux directory)
51FaultNameFilename = 'fault_list.txt'
52
53# program name and version
54APP_NAME = 'EventSelection'
55APP_VERSION = '1.1'
56
57# name of the configuration filename
58# GUI values are saved in this file for next time
59ConfigFilename = 'EventSelection.cfg'
60
61# name of the 'fault XY' output file
62FaultXYFilename = 'fault.xy'
63
64# name of the quake probability output file
65QuakeProbFilename = 'quake_prob.txt'
66
67# name of the output URS data module
68# note: the %d is where the event number goes
69URSDataFilename = 'urs_data_%6.6d.py'
70
71# name of the output bash params file
72# note: the %d is where the event number goes
73GridBashScriptName = 'faults_%6.6d.params'
74
75# map display name to directory name
76Regions = [ ('Australia',  'Australia'),
77            ('India',      'Indian'),
78            ('SW Pacific', 'SW_Pacific')
79          ]
80
81# pattern string used to split multimax data
82SpacesPatternString = ' +'
83
84# generate 're' pattern for 'any number of spaces'
85SpacesPattern = re.compile(SpacesPatternString)
86
87# Global holding current results sub-directory
88ResultSubdir = ''
89
90# a small value to perturb the user limits
91Epsilon = 1.0E-6
92
93# GUI definitions
94BOX_WIDTH = 400
95BOX_HEIGHT = 360
96BOX2_HEIGHT = 285
97
98
99FORM_WIDTH = BOX_WIDTH + 15
100FORM_HEIGHT = 755
101
102START_YOFFSET = 7
103OUT_TEXT_XOFFSET = 8
104OUT_TEXT_YOFFSET = 11
105OUTPUT_BOX_HEIGHT = 45
106
107GEN_YOFFSET = 15
108GEN_DELTAY = 25
109GEN_LABELXOFFSET = 5
110GEN_TEXTXOFFSET = BOX_WIDTH/2 + GEN_LABELXOFFSET + 10
111
112TEXTLIST_WIDTH = BOX_WIDTH - 10
113TEXTLIST_HEIGHT = 200
114
115CTL_WIDTH = BOX_WIDTH / 2
116CTL_HEIGHT = 22
117
118BUTTON_WIDTH = 100
119BUTTON_HEIGHT = 30
120
121DOUBLE_BUTTON_OFFSET = 8
122
123COMBO_FUDGE = 2
124
125# Copies of various parameters
126Region = ''
127GaugeNumber = ''
128MinimumHeight = ''
129MaximumHeight = ''
130
131
132################################################################################
133# This code was generated by img2py.py
134# Embed the ICON image here, so we don't need an external *.ico file.
135# Icon image inspired by Hokusai's print "The Great Wave off Kanagawa"
136# (Kanagawa okinami ura) from the "Thirty-Six Views of Mount Fuji" series.
137# http://commons.wikimedia.org/wiki/Image:The_Great_Wave_off_Kanagawa.jpg
138################################################################################
139
140if Imported_PyEmbeddedImage:
141    def getIcon():
142        return PyEmbeddedImage(
143          "iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABHNCSVQICAgIfAhkiAAABDBJ"
144          "REFUWIXtlmtsFFUUx393dnbbbh+hlPQVKEEBKSLyEEEhqFQSNYAQNahAIJJUidEEtUQIRgk+"
145          "GkSMJBJjUEADCVghtipSjeWhWIoh5VXoBqgU2aWvLbt0H52dmesH6La1U7OVbvCD59PcmTPn"
146          "/M69/3vuFYDkFppyK5P/D/CfAFD//kIgcGbkkpk/GYczjat/umiqrUIaBgA2RwKG1h71lxZP"
147          "/xJAkJiazn3LNnD7g8+gqI5o0Iaawxza+CJ3zlnGiIKF+C6f5+iWVTicaaRkDSXY4uHCwa/Q"
148          "20N9BhGAdA7IIjVnGNNf3Uz60NEghYWr7PJLz3GguZ6Tuz/C8/teQoFrDBoxEefAbPyeOtzV"
149          "FZhGxBpg9voDMnvMVIRi6xN57xVJJF0LkDScreSHVbNob/P28Fdyxk7vt+TX0wmuz46k/si3"
150          "uMq3kTVqCmPmvWTp30OE/WWu8q1UbV5J2N9C7rgZvS5BXACklFTveI9BIycweNKj1FeWcWrP"
151          "RojOTqfdVB8QvSheILEnp6EF/OgBPwNvG8v8LS6e3lrLhAWrUZTOukVhuYzLYSRNo4u2ZLfi"
152          "qza/zvFd7yOR8euEXZNfOFjCzsV3sOu5fLwXTzP84UXRuYt7K24+d4yK4oUEvW600DXqDpTQ"
153          "1ngxulHjtgs67OLhUgxDx56cxl1zX0ZKk0MbCqPf46aBDpPSIBJqI3y1Gb/7HJ4T+6nd+xlB"
154          "X1NXAAlYtV/LkN18TSNCqLUR58DsmBtaqNXD9ysfwVt3skMDsSa/7msaEa6c/pWg14PeHmTn"
155          "khGUvTKda1cuWAI3nj3CH7/soaHmNwy9naT0bKYUrgdE7yIUwmplJH73OUqXT+OnNU9wfNc6"
156          "bPZEFNVO45lKKtYtBiRhfzOGFqZj39WUbuLHtU9SunwquwvHEWhxkzF8AopN7R1AWpyIquZn"
157          "3xuz8F2qZdKSNeTdM5MzO9/BnpBE7riHULU2VAWqd7zL1y+Mp7W+BhDcPX8FAsHo2cuItAcJ"
158          "eT00uaqQht43Edbu+5wDHyzt8T5j2BimLV2Lt+EyWijAsS/ewjQN0vPymfvxUYRQ2P7sEOyJ"
159          "yeTPep6w30tN2Sa0gK9v2/D8z9u7z4iqous6LXWn+Gb1PBwOB5qmkZiaQcGKbZS/+Tju6goG"
160          "T5yJPSkV32UXlZ8WdYsRcyMSSPRQoHMsBKZpdvPRNA2AlMwhaG1eMkdNJiUrj5CviUDTJcu4"
161          "MQNIIDlraOdYyh4AHdZSd4JIOMhjxfsAyf7iRTdEaVFYrBqwKZLqkg+p/OQ1ZAz3PiEENjUB"
162          "U9cwpTUogBLrJVJKGD3jKewJSTH6S/RI+B+T3wCIrQmZUmAbMJh7F6xAtfXfEdLH01Awcl4R"
163          "eeMfwOFw3AoAUBOc3F/0JZnDx5OUFNty9CsAgDM9h4K3vyNvyhxSUlJvCuAvPDCzruPdn6UA"
164          "AAAASUVORK5CYII=")
165
166
167################################################################################
168# An object that behaves like a dictionary but is pickled/unpickled from a file.
169################################################################################
170
171class Config(object):
172    """An object that behaves like a dictionary but is persistent
173
174        cfg = Config('filename')
175        cfg['save'] = 'A saved value'
176        cfg[123] = 'value 123'
177        print cfg['save']
178
179    The values stored in the file 'filename' will be available to any
180    application using that config file.
181    """
182
183    def __init__(self, configfile=None):
184        """__init__(self, String filename=None) -> Config"""
185
186        self.delconf = False
187        self.cfgdict = {}
188        self.changed = False
189        if not configfile:
190            self.delconf = True
191            configfile =tempfile.mktemp()
192        self.configfile = os.path.abspath(configfile)
193        if os.path.exists(self.configfile):
194            try:
195                f = open(configfile, "r")
196                u = pickle.Unpickler(f)
197                self.cfgdict = u.load()
198            except pickle.UnpicklingError, e:
199                print e
200            except:
201                pass
202            else:
203                f.close()
204
205    def __setitem__(self, key, value):
206        """Override to allow: cfg[<key>] = <value>"""
207       
208        self.cfgdict[key] = value
209        self.changed = True
210
211    def __getitem__(self, key):
212        """Override to allow: <var> = cfg[<key>]"""
213       
214        return self.cfgdict.get(key, None)
215
216    def __str__(self):
217        """__str__(self) -> String"""
218       
219        return "<config object at %s>" % hex(id(self))
220
221    def getfilename(self):
222        """getfilename(self) -> String filename"""
223       
224        return self.configfile
225
226    def setdeleted(self):
227        """setdeleted(self)"""
228       
229        self.delconf = True
230
231    def save(self):
232        """save(self) -> save data to a file"""
233       
234        try:
235            f = open(self.configfile, "w")
236            p = pickle.Pickler(f)
237            p.dump(self.cfgdict)
238        except pickle.PicklingError, e:
239            print e
240        else:
241            f.close()
242        self.changed = False
243
244    def close(self):
245        """close(self) - close the save file"""
246        if self.changed:
247            self.save()
248        if self.delconf:
249            if os.path.exists(self.configfile):
250                os.remove(self.configfile)
251
252    def __del__(self):
253        """__del__(self) - ensure save file is closed"""
254       
255        self.close()
256
257################################################################################
258# This code implements the logic of David Burbidge's 'list_quakes' program.
259# Except the two output files are now in CSV format.
260#################################################################################
261
262##
263# @brief Function to do all the work - list the quakes selected.
264def list_quakes(event_num, min_height, max_height, InvallFilename,
265                TStarFilename, FaultXYFilename, QuakeProbFilename):
266    ##
267    # @brief Class to hold i_invall data
268    class Inval(object):
269        def __init__(self, lon, lat):
270            self.lon = lon
271            self.lat = lat
272
273        def __str__(self):
274            return '.lon=%g, .lat=%g' % (self.lon, self.lat)
275
276
277    ##
278    # @brief Class to hold T-**** data
279    class TStar(object):
280        def __init__(self, ipt, zquake, zprob, mag, slip, ng, ng_data):
281            self.ipt = ipt
282            self.zquake = zquake
283            self.zprob = zprob
284            self.mag = mag
285            self.slip = slip
286            self.ng_data = [int(i) for i in SpacesPattern.split(ng_data)]
287            if len(self.ng_data) != ng:
288                raise RuntimeError(1, "Error parsing T-***** data: %s" % msg)
289
290        def __str__(self):
291            return ('.ipt=%d, .zquake=%g, .zprob=%g, .mag=%g, .slip=%g, .ng_data=%s' %
292                    (self.ipt, self.zquake, self.zprob, self.mag,
293                     self.slip, str(self.ng_data)))
294
295
296    # read i_invall file
297    try:
298        fd = open(InvallFilename, "r")
299        invall_lines = fd.readlines()
300        fd.close()
301    except IOError, msg:
302        raise RuntimeError(1, "Error reading file '%s': %s" % 
303                           (InvallFilename, msg))
304
305    # trash the first three lines
306    invall_lines = invall_lines[3:]
307
308    # split i_invall data into fields
309    invall_data = []
310    for line in invall_lines:
311        l = line.strip()
312        (lon, lat, _) = SpacesPattern.split(l, maxsplit=2)
313        invall_data.append(Inval(float(lon), float(lat)))
314
315    del invall_lines
316
317    # now read T-**** file, creating filename from event #
318    try:
319        fd = open(TStarFilename, "r")
320        tstar_lines = fd.readlines()
321        fd.close()
322    except IOError, msg:
323        raise RuntimeError(1, "Error reading file: %s" % msg)
324
325    # trash the first line of T-**** data
326    tstar_lines = tstar_lines[1:]
327
328    # get the data from tstar_lines
329    tstar_data = []
330    min_wave = min_height - Epsilon
331    max_wave = max_height + Epsilon
332    for (ipt, line) in enumerate(tstar_lines):
333        l = line.strip()
334        (zquake, zprob, mag, slip, ng, ng_data) = SpacesPattern.split(l, maxsplit=5)
335        zquake = float(zquake)
336        zprob = float(zprob)
337        mag = float(mag)
338        slip = float(slip)
339        ng = int(ng)
340        ng_data = ng_data.strip()
341        # only remember the data if zquake in wave height range and zprob > 0.0
342        if zprob > 0.0 and (min_wave <= zquake <= max_wave):
343            tstar_data.append(TStar(ipt+1, zquake, zprob, mag, slip, ng, ng_data))
344    del tstar_lines
345
346    # write out lines joining centroids
347    try:
348        outfd = open(FaultXYFilename, "w")
349    except IOError, msg:
350        raise RuntimeError(1, "Error opening output file: %s" % msg)
351
352    outfd.write('Lon,Lat,Quake_ID,Subfault_ID\n')
353    for t in tstar_data:
354        for n in t.ng_data:
355            outfd.write('%.4f,%.4f,%d,%d\n' %
356                        (invall_data[n].lon, invall_data[n].lat, t.ipt, n))
357    outfd.close()
358
359    # write out quake probabilities
360    try:
361        outfd = open(QuakeProbFilename, "w")
362    except IOError, msg:
363        raise RuntimeError(1, "Error opening output file: %s" % msg)
364
365    outfd.write('Quake_ID,Ann_Prob,z_max(m),Mag\n')
366    for t in tstar_data:
367        outfd.write('%d,%.5G,%.5f,%.2f\n' % (t.ipt, t.zprob, t.zquake, t.mag))
368    outfd.close()
369
370################################################################################
371# This code does exactly what David Burbidge's 'get_multimux' program does.
372################################################################################
373
374def get_multimux(quake_ID, multimux_dir, fault_event_filename):
375    # get the fault name data
376    filename = os.path.join(multimux_dir, FaultNameFilename)
377    try:
378        fd = open(filename, "r")
379        fault_names = [ fn.strip() for fn in fd.readlines() ]
380        fd.close()
381    except IOError, msg:
382        raise RuntimeError(1, "Error reading file: %s" % msg)
383
384    # open the output file
385    try:
386        outfd = open(fault_event_filename, "w")
387    except IOError, msg:
388        raise RuntimeError(1, "Error opening output file: %s" % msg)
389
390    # handle each fault
391    nquake = 0
392    for fn in fault_names:
393        # create the filename for the multimux data file
394        mmx_filename = 'i_multimux-%s' % fn
395        mmx_filename = os.path.join(multimux_dir, mmx_filename)
396
397        # Read all data in file, checking as we go
398        try:
399            infd = open(mmx_filename, "r")
400        except IOError, msg:
401            raise RuntimeError(1, "Error opening file: %s" % msg)
402
403        # check fault name in file is as expected
404        mux_faultname = infd.readline().strip()
405        if mux_faultname != fn:
406            raise RuntimeError(1, "Error reading file")
407
408        # read data
409        while True:
410            # get number of subfaults, EOF means finished
411            try:
412                nsubfault = infd.readline()
413            except IOError:
414                raise RuntimeError(1, "Error reading file")
415
416            if not nsubfault:
417                break
418            nsubfault = int(nsubfault)
419
420            nquake += 1
421            if nquake == quake_ID:
422                outfd.write(' %d\n' % nsubfault)
423                for i in range(nsubfault):
424                    line = infd.readline()
425                    (subfaultname, slip, prob, mag, _) = \
426                                   SpacesPattern.split(line, maxsplit=4)
427                    subfaultname = subfaultname.strip()
428                    slip = float(slip)
429                    outfd.write(" %s %g\n" % (subfaultname, slip))
430            else:
431                for i in range(nsubfault):
432                    try:
433                        infd.readline()
434                    except IOError:
435                        raise RuntimeError(1,
436                                           "Something wrong at bottom of file %s" %
437                                           mux_faultname)
438
439        infd.close()
440    outfd.close()
441
442
443################################################################################
444# Code to do the GUI
445#
446# The Frame code is buried under the "if __name__ ..." test since we are going
447# to import this file into verify.py to test the list_quake() and get_multimux()
448# functions.
449################################################################################
450
451if __name__ == '__main__':
452   
453    class MyFrame(wx.Frame):
454        def __init__(self, parent, id, title, pos=wx.DefaultPosition,
455                        size=wx.DefaultSize, style=wx.DEFAULT_FRAME_STYLE):
456            # Make the frame
457            wx.Frame.__init__(self, parent, id, title, pos=(50, 50),
458                                size=(FORM_WIDTH, FORM_HEIGHT),
459                                style=(wx.DEFAULT_FRAME_STYLE &
460                                       ~ (wx.RESIZE_BOX | wx.MAXIMIZE_BOX |
461                                          wx.RESIZE_BORDER)))
462
463            p = self.panel = wx.Panel(self, -1)
464
465            if Imported_PyEmbeddedImage:
466                tsunami = getIcon()
467                icon = tsunami.GetIcon()
468                self.SetIcon(icon)
469           
470            self.Center()
471            self.Show(True)
472
473            # place the 'output dir' textbox
474            Y_OFFSET = START_YOFFSET
475            wx.StaticBox(p, -1, 'output base directory', (3, 1),
476                         size=(BOX_WIDTH, OUTPUT_BOX_HEIGHT))
477            self.txtOutputDir = wx.TextCtrl(p, -1, size=(BOX_WIDTH-10, 20),
478                                            pos=(OUT_TEXT_XOFFSET,
479                                                 Y_OFFSET+OUT_TEXT_YOFFSET))
480            self.txtOutputDir.SetToolTip(wx.ToolTip("Where your data files will be written"))
481            self.txtOutputDir.Bind(wx.EVT_LEFT_DOWN, self.OnOutputDirSelect)
482            Y_OFFSET += 50
483           
484            # start laying out list_quake controls
485            wx.StaticBox(p, -1, 'list_quakes', (3, Y_OFFSET),
486                         size=(BOX_WIDTH, BOX_HEIGHT))
487            sampleList = [ x for (x, y) in Regions ]
488            Y_OFFSET += GEN_YOFFSET
489            wx.StaticText(p, -1, 'Region', pos=(GEN_LABELXOFFSET, Y_OFFSET),
490                          size=(CTL_WIDTH, CTL_HEIGHT), style=wx.ALIGN_RIGHT)
491            self.cbRegion = wx.ComboBox(p, 500, 
492                                        pos=(GEN_TEXTXOFFSET, Y_OFFSET-COMBO_FUDGE),
493                                        size=(100,20),
494                                        choices=sampleList,
495                                        style=wx.CB_DROPDOWN)
496            self.cbRegion.SetEditable(False)
497            Y_OFFSET += GEN_DELTAY
498            wx.StaticText(p, -1, 'Hazard index',
499                          pos=(GEN_LABELXOFFSET, Y_OFFSET),
500                          size=(CTL_WIDTH, CTL_HEIGHT), style=wx.ALIGN_RIGHT)
501            self.txtTGN = wx.TextCtrl(p, -1, size=(100, 20),
502                                      pos=(GEN_TEXTXOFFSET, Y_OFFSET))
503            Y_OFFSET += GEN_DELTAY
504            wx.StaticText(p, -1, 'Minimum wave height',
505                          pos=(GEN_LABELXOFFSET, Y_OFFSET),
506                          size=(CTL_WIDTH, CTL_HEIGHT), style=wx.ALIGN_RIGHT)
507            self.txtMinH = wx.TextCtrl(p, -1, size=(100, 20),
508                                       pos=(GEN_TEXTXOFFSET, Y_OFFSET))
509            Y_OFFSET += GEN_DELTAY
510            wx.StaticText(p, -1, 'Maximum wave height',
511                          pos=(GEN_LABELXOFFSET, Y_OFFSET),
512                          size=(CTL_WIDTH, CTL_HEIGHT), style=wx.ALIGN_RIGHT)
513            self.txtMaxH = wx.TextCtrl(p, -1, size=(100, 20),
514                                       pos=(GEN_TEXTXOFFSET, Y_OFFSET))
515            Y_OFFSET += GEN_DELTAY
516            x = FORM_WIDTH/2 - BUTTON_WIDTH/2
517            self.btnGenerate = wx.Button(p, label="List",
518                                         pos=(x, Y_OFFSET),
519                                         size=(100, BUTTON_HEIGHT))
520            Y_OFFSET += GEN_DELTAY + BUTTON_HEIGHT/2
521
522            self.lstGenerate = wx.ListCtrl(p, -1, pos=(8, Y_OFFSET),
523                                           size=(TEXTLIST_WIDTH,TEXTLIST_HEIGHT),
524                                           style=wx.LC_REPORT|wx.LC_SINGLE_SEL)
525           
526            Y_OFFSET += TEXTLIST_HEIGHT + 20
527            self.Bind(wx.EVT_BUTTON, self.GenerateClick, self.btnGenerate)
528            self.lstGenerate.Bind(wx.EVT_LIST_ITEM_SELECTED,
529                                  self.OnItemSelected, self.lstGenerate)
530
531            # start laying out get_multimux controls
532            wx.StaticBox(p, -1, 'multimux_grid', (3, Y_OFFSET),
533                         size=(BOX_WIDTH, BOX2_HEIGHT))
534            Y_OFFSET += 18
535            wx.StaticText(p, -1, 'Quake ID', pos=(GEN_LABELXOFFSET, Y_OFFSET),
536                          size=(CTL_WIDTH, CTL_HEIGHT), style=wx.ALIGN_RIGHT)
537            self.txtQuakeID = wx.TextCtrl(p, -1, size=(100, 20),
538                                          pos=(GEN_TEXTXOFFSET, Y_OFFSET),
539                                          style=wx.TE_READONLY)
540            Y_OFFSET += GEN_DELTAY
541            x = FORM_WIDTH/2 - BUTTON_WIDTH - DOUBLE_BUTTON_OFFSET
542            self.btnMultimux = wx.Button(p, label="Multimux", pos=(x, Y_OFFSET),
543                                         size=(100, BUTTON_HEIGHT))
544            x = FORM_WIDTH/2 + DOUBLE_BUTTON_OFFSET
545            self.btnGrid = wx.Button(p, label="Grid", pos=(x, Y_OFFSET),
546                                         size=(100, BUTTON_HEIGHT))
547            Y_OFFSET += GEN_DELTAY + BUTTON_HEIGHT/2
548            self.lstMultimux = wx.ListCtrl(p, -1, pos=(8, Y_OFFSET),
549                                           size=(TEXTLIST_WIDTH,TEXTLIST_HEIGHT),
550                                           style=wx.LC_REPORT|wx.LC_SINGLE_SEL)
551            Y_OFFSET += TEXTLIST_HEIGHT + 20
552            self.Bind(wx.EVT_BUTTON, self.MultimuxClick, self.btnMultimux)
553            self.Bind(wx.EVT_BUTTON, self.GridClick, self.btnGrid)
554
555            self.Bind(wx.EVT_CLOSE, self.doStateSave)
556            self.doStateRestore()
557
558        def OnOutputDirSelect(self, event):
559            dlg = wx.DirDialog(self, "Choose an output directory:",
560                  style=wx.DD_DEFAULT_STYLE
561                   #| wx.DD_DIR_MUST_EXIST
562                   #| wx.DD_CHANGE_DIR
563                   )
564
565            # If the user selects OK, then we process the dialog's data.
566            # This is done by getting the path data from the dialog - BEFORE
567            # we destroy it.
568            if dlg.ShowModal() == wx.ID_OK:
569                self.txtOutputDir.Clear()
570                self.txtOutputDir.WriteText(dlg.GetPath())
571            self.cbRegion.SetFocus()
572            # Only destroy a dialog after you're done with it.
573            dlg.Destroy()
574
575
576        def doStateSave(self, event):
577            """
578            Save state here.
579            """
580            self.cfg['OutputDirectory'] = self.txtOutputDir.GetValue()
581            self.cfg['Region'] = self.cbRegion.GetValue()
582            self.cfg['GaugeNumber'] = self.txtTGN.GetValue()
583            self.cfg['MinimumHeight'] = self.txtMinH.GetValue()
584            self.cfg['MaximumHeight'] = self.txtMaxH.GetValue()
585            self.cfg.save()
586           
587            event.Skip(True)
588
589        def doStateRestore(self):
590            """
591            Restore state here - globals have been set and tested sane
592            """
593            global Region, GaugeNumber, MinimumHeight, MaximumHeight
594
595            cfg = Config(ConfigFilename)
596            output_dir = cfg['OutputDirectory']
597            Region = cfg['Region']
598            GaugeNumber = cfg['GaugeNumber']
599            if GaugeNumber:
600                GaugeNumber = int(GaugeNumber)
601            MinimumHeight = cfg['MinimumHeight']
602            if MinimumHeight:
603                MinimumHeight = float(MinimumHeight)
604            MaximumHeight = cfg['MaximumHeight']
605            if MaximumHeight:
606                MaximumHeight = float(MaximumHeight)
607
608            self.cfg = cfg
609
610            region_name = None
611            for (name, dir) in Regions:
612                if name == Region:
613                    region_name = name
614            if not region_name:
615                Region = ''
616
617            if output_dir:
618                self.txtOutputDir.WriteText(output_dir)
619            self.cbRegion.SetStringSelection(Region)
620            if GaugeNumber:
621                self.txtTGN.WriteText(str(GaugeNumber))
622            if MinimumHeight:
623                self.txtMinH.WriteText(str(MinimumHeight))
624            if MaximumHeight:
625                self.txtMaxH.WriteText(str(MaximumHeight))
626           
627
628        def OnItemSelected(self, event):
629            item = self.lstGenerate.GetItem(event.m_itemIndex, 0)
630            self.txtQuakeID.SetValue(item.GetText())
631
632        def error(self, msg):
633            dlg = wx.MessageDialog(self, msg, 'Error',
634                                   wx.OK | wx.ICON_INFORMATION)
635            dlg.ShowModal()
636            dlg.Destroy()
637
638        ######
639        # Call the list_quakes function.
640        ######
641        def GenerateClick(self, event):
642            global ResultSubdir, Region, GaugeNumber, MinimumHeight, MaximumHeight
643           
644            # check that everything has been entered correctly
645            output_base_dir = self.txtOutputDir.GetValue()
646            if len(output_base_dir) == 0:
647                self.error('You must select an output base directory first')
648                return
649
650            region_select = self.cbRegion.GetValue()
651            if len(region_select) == 0:
652                self.error('You must select a region first')
653                return
654
655            try:
656                gauge_number = int(self.txtTGN.GetValue())
657            except:
658                self.error("You must enter a Hazard Index Number")
659                return
660
661            try:
662                min_height = float(self.txtMinH .GetValue())
663            except:
664                self.error("You must enter a minimum wave height in metres (eg, 1.3)")
665                return
666
667            try:
668                max_height = float(self.txtMaxH.GetValue())
669            except:
670                self.error("You must enter a maximum wave height in metres (eg, 1.8)")
671                return
672            if min_height > max_height:
673                self.error("Minimum wave height must be less than maximum height")
674                return
675
676            # set directory names
677            basename = None
678            region_name = None
679            for (name, dir) in Regions:
680                if name == region_select:
681                    region_name = name
682                    basename = dir
683            base_directory = os.path.join(BaseDirectory, basename)
684            if not os.path.exists(base_directory):
685                self.error("Sorry, region %s doesn't exist yet (%s)" %
686                           (region_name, base_directory))
687                return
688            tFilesDir = os.path.join(base_directory, 'Tfiles')
689            multimuxDir = os.path.join(base_directory, 'multimux')
690
691            # create a sub-directory for the result files
692            ResultSubdir = os.path.join(output_base_dir,
693                                        'Results_%s_%d_%.2f_%.2f' %
694                                        (region_select, gauge_number,
695                                         min_height, max_height))
696            try:
697                shutil.rmtree(ResultSubdir)
698            except:
699                pass
700            try:
701                os.mkdir(ResultSubdir)
702            except WindowsError, msg:
703                self.error("Sorry, don't seem to be able to make "
704                           "the result directory '%s':\n%s" %
705                           (ResultSubdir, str(msg)))
706                return
707            except:
708                self.error("Some sort of error making the result directory '%s'" %
709                           ResultSubdir)
710                return
711
712            # set names of the output files (in dir '<wherever>/Results*')
713            faultxy_filename = os.path.join(ResultSubdir, FaultXYFilename)
714            quakeprob_filename = os.path.join(ResultSubdir, QuakeProbFilename)
715
716            # now check that gauge number is valid
717            gaugeFile = os.path.join(tFilesDir, 'T-%05d' % gauge_number)
718            if not os.path.exists(gaugeFile):
719                self.error("T file for hazard %d doesn't exist" % gauge_number)
720                return
721
722            # check we have an i_invall file
723            invallFile = os.path.join(multimuxDir, 'i_invall')
724            if not os.path.exists(invallFile):
725                self.error("'i_invall' file doesn't exist: %s" % invallFile)
726                return
727
728            # save region, hazard #, min and max heights to globals
729            Region = region_select
730            GaugeNumber = gauge_number
731            MinimumHeight = min_height
732            MaximumHeight = max_height
733
734            # clear the Quake ID textbox
735            self.txtQuakeID.SetValue('')
736           
737            # now process the data we have
738            # clear output list
739            self.lstGenerate.ClearAll()
740            # delete output files
741            try:
742                os.unlink(FaultXYFilename)
743            except:
744                pass
745            try:
746                os.unlink(QuakeProbFilename)
747            except:
748                pass
749
750            ####################################################################
751            # reset button label, hourglass the cursor
752            self.btnGenerate.SetLabel('Listing ...')
753            self.panel.SetCursor(wx.StockCursor(wx.CURSOR_WAIT))
754           
755            wx.Yield()          # allow GUI refresh
756
757            # generate output files
758            try:
759                list_quakes(gauge_number, min_height, max_height, invallFile,
760                            gaugeFile, faultxy_filename, quakeprob_filename)       
761            except RuntimeError, msg:
762                self.error("Error in list_quakes module: %s" % msg)
763                self.panel.SetCursor(wx.StockCursor(wx.CURSOR_ARROW))
764                self.btnGenerate.SetLabel('List')
765                return
766            # put cursor back to normal, button label back to normal
767            self.panel.SetCursor(wx.StockCursor(wx.CURSOR_ARROW))
768            self.btnGenerate.SetLabel('List')
769            ####################################################################
770
771            # list result file, if any
772            if not os.path.exists(quakeprob_filename):
773                self.error("Error generating data: file '%s' not found" %
774                           quakeprob_filename)
775                return
776            in_fd = open(quakeprob_filename, 'r')
777            lines = in_fd.readlines()
778            in_fd.close()
779            hdr = lines[0].split(',')
780            for (i, h) in enumerate(hdr):
781                self.lstGenerate.InsertColumn(i, h.strip())
782            for (i, l) in enumerate(lines[1:]):
783                l = l.split(',')
784                index = self.lstGenerate.InsertStringItem(sys.maxint, l[0].strip())
785                for (ii, ll) in enumerate(l[1:]):
786                    self.lstGenerate.SetStringItem(index, ii+1, ll.strip())
787
788            column_width = FORM_WIDTH / len(hdr) - 20
789            for i in range(len(hdr)):
790                self.lstGenerate.SetColumnWidth(i, column_width)
791
792        ######
793        # Call the get_multimux function.
794        ######
795        def MultimuxClick(self, event):
796            # check we have an output base directory
797            output_base_dir = self.txtOutputDir.GetValue()
798            if len(output_base_dir) == 0:
799                self.error('You must select an output base directory first')
800                return
801
802            # check we have a quake ID
803            quake_ID = self.txtQuakeID.GetValue()
804            if len(quake_ID) == 0:
805                self.error("You must enter a quake ID by clicking in "
806                           "the 'list' result box")
807                return
808            quake_ID = int(quake_ID)
809
810            region_select = self.cbRegion.GetValue()
811            if len(region_select) == 0:
812                self.error('You must select a region first')
813                return
814
815            region_dir_name = None
816            for (disp, real) in Regions:
817                if disp == region_select:
818                    region_dir_name = real
819                    break
820            if region_dir_name is None:
821                self.error('INTERNAL ERROR: region_dir_name is None?!')
822                return
823
824            multimux_dir = os.path.join(BaseDirectory, region_dir_name, 'multimux')
825
826            ####################################################################
827            # clear list, set button label, hourglass the cursor
828            self.lstMultimux.ClearAll()
829            self.lstMultimux.InsertColumn(0, '')
830            self.btnMultimux.SetLabel('Multimuxing ...')
831            self.panel.SetCursor(wx.StockCursor(wx.CURSOR_WAIT))
832
833            fault_event_dir = os.path.join(output_base_dir, str(quake_ID))
834            if not os.path.exists(fault_event_dir):
835                os.mkdir(fault_event_dir)
836            fault_event_filename = os.path.join(fault_event_dir, 'event.list')
837
838            wx.Yield()          # allow GUI refresh
839
840            try:
841                get_multimux(quake_ID, multimux_dir, fault_event_filename)
842            except RuntimeError, msg:
843                self.error("Error in get_multimux module: %s" % msg)
844                self.panel.SetCursor(wx.StockCursor(wx.CURSOR_ARROW))
845                self.btnMultimux.SetLabel('Multimux')
846                return
847            # cursor and button label to normal
848            self.panel.SetCursor(wx.StockCursor(wx.CURSOR_ARROW))
849            self.btnMultimux.SetLabel('Multimux')
850            ####################################################################
851
852            # list resultant files, if any
853            if not os.path.exists(fault_event_filename):
854                self.error("Error generating data: file %s not found" %
855                           fault_event_filename)
856                return
857           
858            in_fd = open(fault_event_filename, 'r')
859            lines = in_fd.readlines()
860            in_fd.close()
861            hdr = lines[0].strip()
862            hdr = SpacesPattern.split(hdr)
863            for (i, h) in enumerate(hdr):
864                self.lstMultimux.InsertColumn(i, h)
865            for (i, l) in enumerate(lines[1:]):
866                l = l.strip()
867                l = SpacesPattern.split(l)
868                index = self.lstMultimux.InsertStringItem(sys.maxint, l[0])
869                for (ii, ll) in enumerate(l[1:]):
870                    self.lstMultimux.SetStringItem(index, ii+1, ll)
871            column_width = FORM_WIDTH / 2 - 30
872            for i in range(2):
873                self.lstMultimux.SetColumnWidth(i, column_width)
874
875            # generate the urs_filenames.py module from 'lines' data
876            # get data from lines we read previously
877            last_weight = None
878            filenames = []
879            for line in lines[1:]:
880                (_, filename, weight) = SpacesPattern.split(line)
881                weight = float(weight)
882                if last_weight:
883                    if last_weight != weight:
884                        self.error("In file '%s', inconsistent weight, "
885                                   "was %.4f, expected %.4f" % (weight, last_weight))
886                        return
887                last_weight = weight
888                filenames.append(filename)
889               
890        ######
891        # Call the get_grid function.
892        ######
893        def GridClick(self, event):
894            # check we have a quake ID
895            quake_ID = self.txtQuakeID.GetValue()
896            if len(quake_ID) == 0:
897                self.error("You must enter a quake ID by clicking in "
898                           "the 'list' result box")
899                return
900            quake_ID = int(quake_ID)
901
902            region_select = self.cbRegion.GetValue()
903            if len(region_select) == 0:
904                self.error('You must select a region first')
905                return
906
907            region_dir_name = None
908            for (disp, real) in Regions:
909                if disp == region_select:
910                    region_dir_name = real
911                    break
912            if region_dir_name is None:
913                self.error('INTERNAL ERROR: region_dir_name is None?!')
914                return
915
916            multimux_dir = os.path.join(BaseDirectory, region_dir_name, 'multimux')
917            grid_dir = os.path.join(BaseDirectory, region_dir_name, 'static_2m')
918
919            # check that the sub-directory has been created
920            if ResultSubdir == '' or not os.path.isdir(ResultSubdir):
921                self.error("Sorry, you must 'list' earthquakes first.")
922                return
923
924            ####################################################################
925            # clear list, set button label, hourglass the cursor
926            self.lstMultimux.ClearAll()
927            self.lstMultimux.InsertColumn(0, '')
928            self.btnGrid.SetLabel('Gridding ...')
929            self.panel.SetCursor(wx.StockCursor(wx.CURSOR_WAIT))
930
931            fault_event_filename = ('%s/event_%6.6d.list' %
932                                    (ResultSubdir, quake_ID))
933            wx.Yield()          # allow GUI refresh
934
935            try:
936                get_multimux(quake_ID, multimux_dir, fault_event_filename)
937            except RuntimeError, msg:
938                self.error("Error in get_multimux module: %s" % msg)
939                self.panel.SetCursor(wx.StockCursor(wx.CURSOR_ARROW))
940                self.btnGrid.SetLabel('Grid')
941                return
942            # cursor and button label to normal
943            self.panel.SetCursor(wx.StockCursor(wx.CURSOR_ARROW))
944            self.btnGrid.SetLabel('Grid')
945            ####################################################################
946
947            # list resultant files, if any
948            if not os.path.exists(fault_event_filename):
949                self.error("Error generating data: file %s not found" %
950                           fault_event_filename)
951                return
952
953            # read output data file
954            in_fd = open(fault_event_filename, 'r')
955            lines = in_fd.readlines()
956            in_fd.close()
957            # get header items, display them
958            hdr = lines[0].strip()
959            hdr = SpacesPattern.split(hdr)
960            for (i, h) in enumerate(hdr):
961                self.lstMultimux.InsertColumn(i, h)
962            # display resultant data, collect filenames and weight_factor               
963            weight_factor = None
964            wf_error = False        # True if there was a weight_factor error
965            filenames = []
966            for (i, l) in enumerate(lines[1:]):
967                l = l.strip()
968                l = SpacesPattern.split(l)
969                fname = l[0].replace('.grd-z-mux2', '.grd')
970                filenames.append(fname)
971                index = self.lstMultimux.InsertStringItem(sys.maxint, fname)
972                for (ii, ll) in enumerate(l[1:]):
973                    self.lstMultimux.SetStringItem(index, ii+1, ll)
974                if weight_factor:
975                    if weight_factor != float(l[1]):
976                        wf_error = True
977                else:
978                    weight_factor = float(l[1])
979            column_width = FORM_WIDTH / 2 - 30
980            for i in range(2):
981                self.lstMultimux.SetColumnWidth(i, column_width)
982
983            if wf_error:
984                self.error('Weight factor is not the same for all files!')
985                return
986
987            if not filenames:
988                self.error('No grid files found!')
989                return
990
991            # generate the bash script param_file.
992            out_name = GridBashScriptName % quake_ID
993            grid_filename = '%s/%s' % (ResultSubdir, out_name)
994           
995            try:
996                out_fd = open(grid_filename, "w")
997            except IOError, msg:
998                self.error("Error opening output file %s': %s" %
999                           (grid_filename, msg))
1000                return
1001
1002            out_fd.write('GENERATED=%s\n' % time.asctime(time.localtime()))
1003            out_fd.write('REGION=%s\n' % Region)
1004            out_fd.write('HAZARD_INDEX=%d\n' % GaugeNumber)
1005            out_fd.write('WAVE_HEIGHTS=[%.2f,%.2f]\n' % (MinimumHeight, MaximumHeight))
1006            out_fd.write('QUAKE_ID=%d\n' % quake_ID)
1007            out_fd.write('WEIGHT_FACTOR=%f\n' % weight_factor)
1008            out_fd.write('DATA_PATH=%s\n' % grid_dir)
1009            #for fn in filenames[1:]:
1010            for fn in filenames:
1011                owning_dir = filenames[0].split('-')[0]
1012                out_fd.write('GRID=%s\n' % (os.path.join(owning_dir, fn)))
1013            out_fd.write('FAULT=%s\n' % owning_dir)
1014   
1015    app = wx.App()
1016    frame = MyFrame(None, -1, '%s %s' % (APP_NAME, APP_VERSION),
1017                    size=(FORM_WIDTH, FORM_HEIGHT))
1018    app.SetTopWindow(frame)
1019    app.MainLoop()
1020
Note: See TracBrowser for help on using the repository browser.