source: anuga_core/source/anuga/shallow_water/data_manager.py @ 4705

Last change on this file since 4705 was 4705, checked in by ole, 16 years ago

Some documentation of ticket:192 plus storage of restricting polygon and time_interval for extrema computations.

File size: 188.3 KB
Line 
1"""datamanager.py - input output for AnuGA
2
3
4This module takes care of reading and writing datafiles such as topograhies,
5model output, etc
6
7
8Formats used within AnuGA:
9
10.sww: Netcdf format for storing model output f(t,x,y)
11.tms: Netcdf format for storing time series f(t)
12
13.csv: ASCII format for storing arbitrary points and associated attributes
14.pts: NetCDF format for storing arbitrary points and associated attributes
15
16.asc: ASCII format of regular DEMs as output from ArcView
17.prj: Associated ArcView file giving more meta data for asc format
18.ers: ERMapper header format of regular DEMs for ArcView
19
20.dem: NetCDF representation of regular DEM data
21
22.tsh: ASCII format for storing meshes and associated boundary and region info
23.msh: NetCDF format for storing meshes and associated boundary and region info
24
25.nc: Native ferret NetCDF format
26.geo: Houdinis ascii geometry format (?)
27
28
29A typical dataflow can be described as follows
30
31Manually created files:
32ASC, PRJ:     Digital elevation models (gridded)
33TSH:          Triangular meshes (e.g. created from anuga.pmesh)
34NC            Model outputs for use as boundary conditions (e.g from MOST)
35
36
37AUTOMATICALLY CREATED FILES:
38
39ASC, PRJ  ->  DEM  ->  PTS: Conversion of DEM's to native pts file
40
41NC -> SWW: Conversion of MOST bundary files to boundary sww
42
43PTS + TSH -> TSH with elevation: Least squares fit
44
45TSH -> SWW:  Conversion of TSH to sww viewable using Swollen
46
47TSH + Boundary SWW -> SWW: Simluation using abstract_2d_finite_volumes
48
49"""
50
51import exceptions
52class TitleValueError(exceptions.Exception): pass
53class DataMissingValuesError(exceptions.Exception): pass
54class DataFileNotOpenError(exceptions.Exception): pass
55class DataTimeError(exceptions.Exception): pass
56class DataDomainError(exceptions.Exception): pass
57class NewQuantity(exceptions.Exception): pass
58
59
60
61import csv
62import os, sys
63import shutil
64from struct import unpack
65import array as p_array
66#import time, os
67from os import sep, path, remove, mkdir, access, F_OK, W_OK, getcwd
68
69
70from Numeric import concatenate, array, Float, Int, Int32, resize, sometrue, \
71     searchsorted, zeros, allclose, around, reshape, transpose, sort, \
72     NewAxis, ArrayType, compress, take, arange, argmax, alltrue,shape,Float
73
74import string
75
76from Scientific.IO.NetCDF import NetCDFFile
77#from shutil import copy
78from os.path import exists, basename, join
79from os import getcwd
80
81
82from anuga.coordinate_transforms.redfearn import redfearn, \
83     convert_from_latlon_to_utm
84from anuga.coordinate_transforms.geo_reference import Geo_reference, \
85     write_NetCDF_georeference, ensure_geo_reference
86from anuga.geospatial_data.geospatial_data import Geospatial_data,\
87     ensure_absolute
88from anuga.config import minimum_storable_height as default_minimum_storable_height
89from anuga.config import max_float
90from anuga.utilities.numerical_tools import ensure_numeric,  mean
91from anuga.caching.caching import myhash
92from anuga.utilities.anuga_exceptions import ANUGAError
93from anuga.shallow_water import Domain
94from anuga.abstract_2d_finite_volumes.pmesh2domain import \
95     pmesh_to_domain_instance
96from anuga.abstract_2d_finite_volumes.util import get_revision_number, \
97     remove_lone_verts, sww2timeseries, get_centroid_values
98from anuga.load_mesh.loadASCII import export_mesh_file
99# formula mappings
100
101quantity_formula = {'momentum':'(xmomentum**2 + ymomentum**2)**0.5',
102                    'depth':'stage-elevation',
103                    'speed': \
104 '(xmomentum**2 + ymomentum**2)**0.5/(stage-elevation+1.e-6/(stage-elevation))'}
105
106
107   
108def make_filename(s):
109    """Transform argument string into a Sexsuitable filename
110    """
111
112    s = s.strip()
113    s = s.replace(' ', '_')
114    s = s.replace('(', '')
115    s = s.replace(')', '')
116    s = s.replace('__', '_')
117
118    return s
119
120
121def check_dir(path, verbose=None):
122    """Check that specified path exists.
123    If path does not exist it will be created if possible
124
125    USAGE:
126       checkdir(path, verbose):
127
128    ARGUMENTS:
129        path -- Directory
130        verbose -- Flag verbose output (default: None)
131
132    RETURN VALUE:
133        Verified path including trailing separator
134
135    """
136
137    import os.path
138
139    if sys.platform in ['nt', 'dos', 'win32', 'what else?']:
140        unix = 0
141    else:
142        unix = 1
143
144
145    if path[-1] != os.sep:
146        path = path + os.sep  # Add separator for directories
147
148    path = os.path.expanduser(path) # Expand ~ or ~user in pathname
149    if not (os.access(path,os.R_OK and os.W_OK) or path == ''):
150        try:
151            exitcode=os.mkdir(path)
152
153            # Change access rights if possible
154            #
155            if unix:
156                exitcode=os.system('chmod 775 '+path)
157            else:
158                pass  # FIXME: What about acces rights under Windows?
159
160            if verbose: print 'MESSAGE: Directory', path, 'created.'
161
162        except:
163            print 'WARNING: Directory', path, 'could not be created.'
164            if unix:
165                path = '/tmp/'
166            else:
167                path = 'C:'
168
169            print 'Using directory %s instead' %path
170
171    return(path)
172
173
174
175def del_dir(path):
176    """Recursively delete directory path and all its contents
177    """
178
179    import os
180
181    if os.path.isdir(path):
182        for file in os.listdir(path):
183            X = os.path.join(path, file)
184
185
186            if os.path.isdir(X) and not os.path.islink(X):
187                del_dir(X)
188            else:
189                try:
190                    os.remove(X)
191                except:
192                    print "Could not remove file %s" %X
193
194        os.rmdir(path)
195       
196       
197# ANOTHER OPTION, IF NEED IN THE FUTURE, Nick B 7/2007   
198def rmgeneric(path, __func__,verbose=False):
199    ERROR_STR= """Error removing %(path)s, %(error)s """
200
201    try:
202        __func__(path)
203        if verbose: print 'Removed ', path
204    except OSError, (errno, strerror):
205        print ERROR_STR % {'path' : path, 'error': strerror }
206           
207def removeall(path,verbose=False):
208
209    if not os.path.isdir(path):
210        return
211   
212    files=os.listdir(path)
213
214    for x in files:
215        fullpath=os.path.join(path, x)
216        if os.path.isfile(fullpath):
217            f=os.remove
218            rmgeneric(fullpath, f)
219        elif os.path.isdir(fullpath):
220            removeall(fullpath)
221            f=os.rmdir
222            rmgeneric(fullpath, f,verbose)
223
224
225
226def create_filename(datadir, filename, format, size=None, time=None):
227
228    import os
229    #from anuga.config import data_dir
230
231    FN = check_dir(datadir) + filename
232
233    if size is not None:
234        FN += '_size%d' %size
235
236    if time is not None:
237        FN += '_time%.2f' %time
238
239    FN += '.' + format
240    return FN
241
242
243def get_files(datadir, filename, format, size):
244    """Get all file (names) with given name, size and format
245    """
246
247    import glob
248
249    import os
250    #from anuga.config import data_dir
251
252    dir = check_dir(datadir)
253
254    pattern = dir + os.sep + filename + '_size=%d*.%s' %(size, format)
255    return glob.glob(pattern)
256
257
258
259#Generic class for storing output to e.g. visualisation or checkpointing
260class Data_format:
261    """Generic interface to data formats
262    """
263
264
265    def __init__(self, domain, extension, mode = 'w'):
266        assert mode in ['r', 'w', 'a'], '''Mode %s must be either:''' %mode +\
267                                        '''   'w' (write)'''+\
268                                        '''   'r' (read)''' +\
269                                        '''   'a' (append)'''
270
271        #Create filename
272        self.filename = create_filename(domain.get_datadir(),
273                                        domain.get_name(), extension)
274
275        #print 'F', self.filename
276        self.timestep = 0
277        self.domain = domain
278       
279
280
281        # Exclude ghosts in case this is a parallel domain
282        self.number_of_nodes = domain.number_of_full_nodes       
283        self.number_of_volumes = domain.number_of_full_triangles
284        #self.number_of_volumes = len(domain)       
285
286
287
288
289        #FIXME: Should we have a general set_precision function?
290
291
292
293#Class for storing output to e.g. visualisation
294class Data_format_sww(Data_format):
295    """Interface to native NetCDF format (.sww) for storing model output
296
297    There are two kinds of data
298
299    1: Constant data: Vertex coordinates and field values. Stored once
300    2: Variable data: Conserved quantities. Stored once per timestep.
301
302    All data is assumed to reside at vertex locations.
303    """
304
305
306    def __init__(self, domain, mode = 'w',\
307                 max_size = 2000000000,
308                 recursion = False):
309        from Scientific.IO.NetCDF import NetCDFFile
310        from Numeric import Int, Float, Float32
311
312        self.precision = Float32 #Use single precision for quantities
313        if hasattr(domain, 'max_size'):
314            self.max_size = domain.max_size #file size max is 2Gig
315        else:
316            self.max_size = max_size
317        self.recursion = recursion
318        self.mode = mode
319
320        Data_format.__init__(self, domain, 'sww', mode)
321
322        if hasattr(domain, 'minimum_storable_height'):
323            self.minimum_storable_height = domain.minimum_storable_height
324        else:
325            self.minimum_storable_height = default_minimum_storable_height
326
327        # NetCDF file definition
328        fid = NetCDFFile(self.filename, mode)
329
330        if mode == 'w':
331            description = 'Output from anuga.abstract_2d_finite_volumes suitable for plotting'
332            self.writer = Write_sww()
333            self.writer.store_header(fid,
334                                     domain.starttime,
335                                     self.number_of_volumes,
336                                     self.domain.number_of_full_nodes,
337                                     description=description,
338                                     smoothing=domain.smooth,
339                                     order=domain.default_order)
340
341            # Extra optional information
342            if hasattr(domain, 'texture'):
343                fid.texture = domain.texture
344
345            if domain.quantities_to_be_monitored is not None:
346                fid.createDimension('singleton', 1)
347                fid.createDimension('two', 2)               
348
349                poly = domain.monitor_polygon
350                if poly is not None:
351                    N = len(poly)
352                    fid.createDimension('polygon_length', N)
353                    fid.createVariable('extrema:polygon',
354                                       self.precision,
355                                       ('polygon_length',
356                                        'two'))
357                    fid.variables['extrema:polygon'][:] = poly                                   
358                   
359                interval = domain.monitor_time_interval
360                if interval is not None:
361                    fid.createVariable('extrema:time_interval',
362                                       self.precision,
363                                       ('two',))
364                    fid.variables['extrema:time_interval'][:] = interval
365                   
366               
367
368               
369                for q in domain.quantities_to_be_monitored:
370                    #print 'doing', q
371                    fid.createVariable(q+':extrema', self.precision,
372                                       ('numbers_in_range',))
373                    fid.createVariable(q+':min_location', self.precision,
374                                       ('numbers_in_range',))
375                    fid.createVariable(q+':max_location', self.precision,
376                                       ('numbers_in_range',))
377                    fid.createVariable(q+':min_time', self.precision,
378                                       ('singleton',))
379                    fid.createVariable(q+':max_time', self.precision,
380                                       ('singleton',))
381
382                   
383        fid.close()
384
385
386    def store_connectivity(self):
387        """Specialisation of store_connectivity for net CDF format
388
389        Writes x,y,z coordinates of triangles constituting
390        the bed elevation.
391        """
392
393        from Scientific.IO.NetCDF import NetCDFFile
394
395        from Numeric import concatenate, Int
396
397        domain = self.domain
398
399        #Get NetCDF
400        fid = NetCDFFile(self.filename, 'a')  #Open existing file for append
401
402        # Get the variables
403        x = fid.variables['x']
404        y = fid.variables['y']
405        z = fid.variables['elevation']
406
407        volumes = fid.variables['volumes']
408
409        # Get X, Y and bed elevation Z
410        Q = domain.quantities['elevation']
411        X,Y,Z,V = Q.get_vertex_values(xy=True,
412                                      precision=self.precision)
413
414        #
415        points = concatenate( (X[:,NewAxis],Y[:,NewAxis]), axis=1 )
416        self.writer.store_triangulation(fid,
417                                        points,
418                                        V.astype(volumes.typecode()),
419                                        Z,
420                                        points_georeference= \
421                                        domain.geo_reference)
422
423        # Close
424        fid.close()
425
426
427    def store_timestep(self, names):
428        """Store time and named quantities to file
429        """
430        from Scientific.IO.NetCDF import NetCDFFile
431        import types
432        from time import sleep
433        from os import stat
434
435        from Numeric import choose
436       
437        # Get NetCDF       
438        retries = 0
439        file_open = False
440        while not file_open and retries < 10:
441            try:
442                fid = NetCDFFile(self.filename, 'a') # Open existing file
443            except IOError:
444                # This could happen if someone was reading the file.
445                # In that case, wait a while and try again
446                msg = 'Warning (store_timestep): File %s could not be opened'\
447                      %self.filename
448                msg += ' - trying step %s again' %self.domain.time
449                print msg
450                retries += 1
451                sleep(1)
452            else:
453                file_open = True
454
455        if not file_open:
456            msg = 'File %s could not be opened for append' %self.filename
457            raise DataFileNotOpenError, msg
458
459
460
461        # Check to see if the file is already too big:
462        time = fid.variables['time']
463        i = len(time)+1
464        file_size = stat(self.filename)[6]
465        file_size_increase =  file_size/i
466        if file_size + file_size_increase > self.max_size*(2**self.recursion):
467            # In order to get the file name and start time correct,
468            # I change the domain.filename and domain.starttime.
469            # This is the only way to do this without changing
470            # other modules (I think).
471
472            # Write a filename addon that won't break swollens reader
473            # (10.sww is bad)
474            filename_ext = '_time_%s'%self.domain.time
475            filename_ext = filename_ext.replace('.', '_')
476           
477            # Remember the old filename, then give domain a
478            # name with the extension
479            old_domain_filename = self.domain.get_name()
480            if not self.recursion:
481                self.domain.set_name(old_domain_filename+filename_ext)
482
483
484            # Change the domain starttime to the current time
485            old_domain_starttime = self.domain.starttime
486            self.domain.starttime = self.domain.time
487
488            # Build a new data_structure.
489            next_data_structure=\
490                Data_format_sww(self.domain, mode=self.mode,\
491                                max_size = self.max_size,\
492                                recursion = self.recursion+1)
493            if not self.recursion:
494                print '    file_size = %s'%file_size
495                print '    saving file to %s'%next_data_structure.filename
496            #set up the new data_structure
497            self.domain.writer = next_data_structure
498
499            #FIXME - could be cleaner to use domain.store_timestep etc.
500            next_data_structure.store_connectivity()
501            next_data_structure.store_timestep(names)
502            fid.sync()
503            fid.close()
504
505            #restore the old starttime and filename
506            self.domain.starttime = old_domain_starttime
507            self.domain.set_name(old_domain_filename)           
508        else:
509            self.recursion = False
510            domain = self.domain
511
512            # Get the variables
513            time = fid.variables['time']
514            stage = fid.variables['stage']
515            xmomentum = fid.variables['xmomentum']
516            ymomentum = fid.variables['ymomentum']
517            i = len(time)
518            if type(names) not in [types.ListType, types.TupleType]:
519                names = [names]
520
521            if 'stage' in names and 'xmomentum' in names and \
522               'ymomentum' in names:
523
524                # Get stage
525                Q = domain.quantities['stage']
526                A,_ = Q.get_vertex_values(xy = False,
527                                          precision = self.precision)               
528                z = fid.variables['elevation']
529                stage = choose(A-z[:] >= self.minimum_storable_height,
530                           (z[:], A))
531               
532                # Get xmomentum
533                Q = domain.quantities['xmomentum']
534                xmomentum, _ = Q.get_vertex_values(xy = False,
535                                          precision = self.precision)
536               
537                # Get ymomentum
538                Q = domain.quantities['ymomentum']
539                ymomentum, _ = Q.get_vertex_values(xy = False,
540                                          precision = self.precision)
541               
542                # Write quantities to NetCDF
543                self.writer.store_quantities(fid, 
544                                             time=self.domain.time,
545                                             precision=self.precision,
546                                             stage=stage,
547                                             xmomentum=xmomentum,
548                                             ymomentum=ymomentum)
549            else:
550                # This is producing a sww that is not standard.
551                # Store time
552                time[i] = self.domain.time
553               
554                for name in names:
555                    # Get quantity
556                    Q = domain.quantities[name]
557                    A,V = Q.get_vertex_values(xy = False,
558                                              precision = self.precision)
559
560                    # FIXME: Make this general (see below)
561                    if name == 'stage':
562                        z = fid.variables['elevation']
563                        A = choose(A-z[:] >= self.minimum_storable_height,
564                                   (z[:], A))
565                        stage[i,:] = A.astype(self.precision)
566                    elif name == 'xmomentum':
567                        xmomentum[i,:] = A.astype(self.precision)
568                    elif name == 'ymomentum':
569                        ymomentum[i,:] = A.astype(self.precision)
570
571                   #As in....
572                   #eval( name + '[i,:] = A.astype(self.precision)' )
573                   #FIXME (Ole): But we need a UNIT test for that before
574                   # refactoring
575
576
577
578            # Update extrema if requested
579            domain = self.domain
580            if domain.quantities_to_be_monitored is not None:
581                for q, info in domain.quantities_to_be_monitored.items():
582
583                    if info['min'] is not None:
584                        fid.variables[q + ':extrema'][0] = info['min']
585                        fid.variables[q + ':min_location'][:] =\
586                                        info['min_location']
587                        fid.variables[q + ':min_time'][0] = info['min_time']
588                       
589                    if info['max'] is not None:
590                        fid.variables[q + ':extrema'][1] = info['max']
591                        fid.variables[q + ':max_location'][:] =\
592                                        info['max_location']
593                        fid.variables[q + ':max_time'][0] = info['max_time']
594
595           
596
597            #Flush and close
598            fid.sync()
599            fid.close()
600
601
602
603# Class for handling checkpoints data
604class Data_format_cpt(Data_format):
605    """Interface to native NetCDF format (.cpt)
606    """
607
608
609    def __init__(self, domain, mode = 'w'):
610        from Scientific.IO.NetCDF import NetCDFFile
611        from Numeric import Int, Float, Float
612
613        self.precision = Float #Use full precision
614
615        Data_format.__init__(self, domain, 'sww', mode)
616
617
618        # NetCDF file definition
619        fid = NetCDFFile(self.filename, mode)
620
621        if mode == 'w':
622            #Create new file
623            fid.institution = 'Geoscience Australia'
624            fid.description = 'Checkpoint data'
625            #fid.smooth = domain.smooth
626            fid.order = domain.default_order
627
628            # dimension definitions
629            fid.createDimension('number_of_volumes', self.number_of_volumes)
630            fid.createDimension('number_of_vertices', 3)
631
632            #Store info at all vertices (no smoothing)
633            fid.createDimension('number_of_points', 3*self.number_of_volumes)
634            fid.createDimension('number_of_timesteps', None) #extensible
635
636            # variable definitions
637
638            #Mesh
639            fid.createVariable('x', self.precision, ('number_of_points',))
640            fid.createVariable('y', self.precision, ('number_of_points',))
641
642
643            fid.createVariable('volumes', Int, ('number_of_volumes',
644                                                'number_of_vertices'))
645
646            fid.createVariable('time', self.precision,
647                               ('number_of_timesteps',))
648
649            #Allocate space for all quantities
650            for name in domain.quantities.keys():
651                fid.createVariable(name, self.precision,
652                                   ('number_of_timesteps',
653                                    'number_of_points'))
654
655        #Close
656        fid.close()
657
658
659    def store_checkpoint(self):
660        """
661        Write x,y coordinates of triangles.
662        Write connectivity (
663        constituting
664        the bed elevation.
665        """
666
667        from Scientific.IO.NetCDF import NetCDFFile
668
669        from Numeric import concatenate
670
671        domain = self.domain
672
673        #Get NetCDF
674        fid = NetCDFFile(self.filename, 'a')  #Open existing file for append
675
676        # Get the variables
677        x = fid.variables['x']
678        y = fid.variables['y']
679
680        volumes = fid.variables['volumes']
681
682        # Get X, Y and bed elevation Z
683        Q = domain.quantities['elevation']
684        X,Y,Z,V = Q.get_vertex_values(xy=True,
685                      precision = self.precision)
686
687
688
689        x[:] = X.astype(self.precision)
690        y[:] = Y.astype(self.precision)
691        z[:] = Z.astype(self.precision)
692
693        volumes[:] = V
694
695        #Close
696        fid.close()
697
698
699    def store_timestep(self, name):
700        """Store time and named quantity to file
701        """
702        from Scientific.IO.NetCDF import NetCDFFile
703        from time import sleep
704
705        #Get NetCDF
706        retries = 0
707        file_open = False
708        while not file_open and retries < 10:
709            try:
710                fid = NetCDFFile(self.filename, 'a') #Open existing file
711            except IOError:
712                #This could happen if someone was reading the file.
713                #In that case, wait a while and try again
714                msg = 'Warning (store_timestep): File %s could not be opened'\
715                  %self.filename
716                msg += ' - trying again'
717                print msg
718                retries += 1
719                sleep(1)
720            else:
721                file_open = True
722
723        if not file_open:
724            msg = 'File %s could not be opened for append' %self.filename
725            raise DataFileNotOPenError, msg
726
727
728        domain = self.domain
729
730        # Get the variables
731        time = fid.variables['time']
732        stage = fid.variables['stage']
733        i = len(time)
734
735        #Store stage
736        time[i] = self.domain.time
737
738        # Get quantity
739        Q = domain.quantities[name]
740        A,V = Q.get_vertex_values(xy=False,
741                                  precision = self.precision)
742
743        stage[i,:] = A.astype(self.precision)
744
745        #Flush and close
746        fid.sync()
747        fid.close()
748
749
750#### NED is national exposure database (name changed to NEXIS)
751   
752LAT_TITLE = 'LATITUDE'
753LONG_TITLE = 'LONGITUDE'
754X_TITLE = 'x'
755Y_TITLE = 'y'
756class Exposure_csv:
757    def __init__(self,file_name, latitude_title=LAT_TITLE,
758                 longitude_title=LONG_TITLE, is_x_y_locations=None,
759                 x_title=X_TITLE, y_title=Y_TITLE,
760                 refine_polygon=None, title_check_list=None):
761        """
762        This class is for handling the exposure csv file.
763        It reads the file in and converts the lats and longs to a geospatial
764        data object.
765        Use the methods to read and write columns.
766
767        The format of the csv files it reads is;
768           The first row is a title row.
769           comma's are the delimiters
770           each column is a 'set' of data
771
772        Feel free to use/expand it to read other csv files.
773           
774           
775        It is not for adding and deleting rows
776       
777        Can geospatial handle string attributes? It's not made for them.
778        Currently it can't load and save string att's.
779
780        So just use geospatial to hold the x, y and georef? Bad, since
781        different att's are in diferent structures.  Not so bad, the info
782        to write if the .csv file is saved is in attribute_dic
783
784        The location info is in the geospatial attribute.
785       
786       
787        """
788        self._file_name = file_name
789        self._geospatial = None #
790
791        # self._attribute_dic is a dictionary.
792        #The keys are the column titles.
793        #The values are lists of column data
794       
795        # self._title_index_dic is a dictionary.
796        #The keys are the column titles.
797        #The values are the index positions of file columns.
798        self._attribute_dic, self._title_index_dic = \
799            csv2dict(self._file_name, title_check_list=title_check_list)
800        try:
801            #Have code here that handles caps or lower
802            lats = self._attribute_dic[latitude_title]
803            longs = self._attribute_dic[longitude_title]
804           
805        except KeyError:
806            # maybe a warning..
807            #Let's see if this works..
808            if False != is_x_y_locations:
809                is_x_y_locations = True
810            pass
811        else:
812            self._geospatial = Geospatial_data(latitudes = lats,
813                 longitudes = longs)
814
815        if is_x_y_locations is True:
816            if self._geospatial is not None:
817                pass #fixme throw an error
818            try:
819                xs = self._attribute_dic[x_title]
820                ys = self._attribute_dic[y_title]
821                points = [[float(i),float(j)] for i,j in map(None,xs,ys)]
822            except KeyError:
823                # maybe a warning..
824                msg = "Could not find location information."
825                raise TitleValueError, msg
826            else:
827                self._geospatial = Geospatial_data(data_points=points)
828           
829        # create a list of points that are in the refining_polygon
830        # described by a list of indexes representing the points
831
832    def __cmp__(self, other):
833        #print "self._attribute_dic",self._attribute_dic
834        #print "other._attribute_dic",other._attribute_dic
835        #print "self._title_index_dic", self._title_index_dic
836        #print "other._title_index_dic", other._title_index_dic
837       
838        #check that a is an instance of this class
839        if isinstance(self, type(other)):
840            result = cmp(self._attribute_dic, other._attribute_dic)
841            if result <>0:
842                return result
843            # The order of the columns is important. Therefore..
844            result = cmp(self._title_index_dic, other._title_index_dic)
845            if result <>0:
846                return result
847            for self_ls, other_ls in map(None,self._attribute_dic, \
848                    other._attribute_dic):
849                result = cmp(self._attribute_dic[self_ls],
850                             other._attribute_dic[other_ls])
851                if result <>0:
852                    return result
853            return 0
854        else:
855            return 1
856   
857
858    def get_column(self, column_name, use_refind_polygon=False):
859        """
860        Given a column name return a list of the column values
861
862        Note, the type of the values will be String!
863        do this to change a list of strings to a list of floats
864        time = [float(x) for x in time]
865       
866        Not implemented:
867        if use_refind_polygon is True, only return values in the
868        refined polygon
869        """
870        if not self._attribute_dic.has_key(column_name):
871            msg = 'Therer is  no column called %s!' %column_name
872            raise TitleValueError, msg
873        return self._attribute_dic[column_name]
874
875
876    def get_value(self, value_column_name,
877                  known_column_name,
878                  known_values,
879                  use_refind_polygon=False):
880        """
881        Do linear interpolation on the known_colum, using the known_value,
882        to return a value of the column_value_name.
883        """
884        pass
885
886
887    def get_location(self, use_refind_polygon=False):
888        """
889        Return a geospatial object which describes the
890        locations of the location file.
891
892        Note, if there is not location info, this returns None.
893       
894        Not implemented:
895        if use_refind_polygon is True, only return values in the
896        refined polygon
897        """
898        return self._geospatial
899
900    def set_column(self, column_name, column_values, overwrite=False):
901        """
902        Add a column to the 'end' (with the right most column being the end)
903        of the csv file.
904
905        Set overwrite to True if you want to overwrite a column.
906       
907        Note, in column_name white space is removed and case is not checked.
908        Precondition
909        The column_name and column_values cannot have comma's in it.
910        """
911       
912        value_row_count = \
913            len(self._attribute_dic[self._title_index_dic.keys()[0]])
914        if len(column_values) <> value_row_count: 
915            msg = 'The number of column values must equal the number of rows.'
916            raise DataMissingValuesError, msg
917       
918        if self._attribute_dic.has_key(column_name):
919            if not overwrite:
920                msg = 'Column name %s already in use!' %column_name
921                raise TitleValueError, msg
922        else:
923            # New title.  Add it to the title index.
924            self._title_index_dic[column_name] = len(self._title_index_dic)
925        self._attribute_dic[column_name] = column_values
926        #print "self._title_index_dic[column_name]",self._title_index_dic
927
928    def save(self, file_name=None):
929        """
930        Save the exposure csv file
931        """
932        if file_name is None:
933            file_name = self._file_name
934       
935        fd = open(file_name,'wb')
936        writer = csv.writer(fd)
937       
938        #Write the title to a cvs file
939        line = [None]* len(self._title_index_dic)
940        for title in self._title_index_dic.iterkeys():
941            line[self._title_index_dic[title]]= title
942        writer.writerow(line)
943       
944        # Write the values to a cvs file
945        value_row_count = \
946            len(self._attribute_dic[self._title_index_dic.keys()[0]])
947        for row_i in range(value_row_count):
948            line = [None]* len(self._title_index_dic)
949            for title in self._title_index_dic.iterkeys():
950                line[self._title_index_dic[title]]= \
951                     self._attribute_dic[title][row_i]
952            writer.writerow(line)
953
954
955def csv2dict(file_name, title_check_list=None):
956    """
957    Load in the csv as a dic, title as key and column info as value, .
958    Also, create a dic, title as key and column index as value,
959    to keep track of the column order.
960   
961    WARNING: Vaules are returned as strings.
962    do this to change a list of strings to a list of floats
963        time = [float(x) for x in time]
964    """
965   
966    #
967    attribute_dic = {}
968    title_index_dic = {}
969    titles_stripped = [] # list of titles
970    reader = csv.reader(file(file_name))
971
972    # Read in and manipulate the title info
973    titles = reader.next()
974    for i,title in enumerate(titles):
975        titles_stripped.append(title.strip())
976        title_index_dic[title.strip()] = i
977    title_count = len(titles_stripped)       
978    #print "title_index_dic",title_index_dic
979    if title_check_list is not None:
980        for title_check in title_check_list:
981            #msg = "Reading error.  This row is not present ", title_check
982            #assert title_index_dic.has_key(title_check), msg
983            if not title_index_dic.has_key(title_check):
984                #reader.close()
985                msg = "Reading error.  This row is not present ", \
986                      title_check                     
987                raise IOError, msg
988               
989   
990   
991    #create a dic of colum values, indexed by column title
992    for line in reader:
993        if len(line) <> title_count:
994            raise IOError #FIXME make this nicer
995        for i, value in enumerate(line):
996            attribute_dic.setdefault(titles_stripped[i],[]).append(value)
997       
998    return attribute_dic, title_index_dic
999
1000
1001#Auxiliary
1002def write_obj(filename,x,y,z):
1003    """Store x,y,z vectors into filename (obj format)
1004       Vectors are assumed to have dimension (M,3) where
1005       M corresponds to the number elements.
1006       triangles are assumed to be disconnected
1007
1008       The three numbers in each vector correspond to three vertices,
1009
1010       e.g. the x coordinate of vertex 1 of element i is in x[i,1]
1011
1012    """
1013    #print 'Writing obj to %s' % filename
1014
1015    import os.path
1016
1017    root, ext = os.path.splitext(filename)
1018    if ext == '.obj':
1019        FN = filename
1020    else:
1021        FN = filename + '.obj'
1022
1023
1024    outfile = open(FN, 'wb')
1025    outfile.write("# Triangulation as an obj file\n")
1026
1027    M, N = x.shape
1028    assert N==3  #Assuming three vertices per element
1029
1030    for i in range(M):
1031        for j in range(N):
1032            outfile.write("v %f %f %f\n" % (x[i,j],y[i,j],z[i,j]))
1033
1034    for i in range(M):
1035        base = i*N
1036        outfile.write("f %d %d %d\n" % (base+1,base+2,base+3))
1037
1038    outfile.close()
1039
1040
1041#########################################################
1042#Conversion routines
1043########################################################
1044
1045def sww2obj(basefilename, size):
1046    """Convert netcdf based data output to obj
1047    """
1048    from Scientific.IO.NetCDF import NetCDFFile
1049
1050    from Numeric import Float, zeros
1051
1052    #Get NetCDF
1053    FN = create_filename('.', basefilename, 'sww', size)
1054    print 'Reading from ', FN
1055    fid = NetCDFFile(FN, 'r')  #Open existing file for read
1056
1057
1058    # Get the variables
1059    x = fid.variables['x']
1060    y = fid.variables['y']
1061    z = fid.variables['elevation']
1062    time = fid.variables['time']
1063    stage = fid.variables['stage']
1064
1065    M = size  #Number of lines
1066    xx = zeros((M,3), Float)
1067    yy = zeros((M,3), Float)
1068    zz = zeros((M,3), Float)
1069
1070    for i in range(M):
1071        for j in range(3):
1072            xx[i,j] = x[i+j*M]
1073            yy[i,j] = y[i+j*M]
1074            zz[i,j] = z[i+j*M]
1075
1076    #Write obj for bathymetry
1077    FN = create_filename('.', basefilename, 'obj', size)
1078    write_obj(FN,xx,yy,zz)
1079
1080
1081    #Now read all the data with variable information, combine with
1082    #x,y info and store as obj
1083
1084    for k in range(len(time)):
1085        t = time[k]
1086        print 'Processing timestep %f' %t
1087
1088        for i in range(M):
1089            for j in range(3):
1090                zz[i,j] = stage[k,i+j*M]
1091
1092
1093        #Write obj for variable data
1094        #FN = create_filename(basefilename, 'obj', size, time=t)
1095        FN = create_filename('.', basefilename[:5], 'obj', size, time=t)
1096        write_obj(FN,xx,yy,zz)
1097
1098
1099def dat2obj(basefilename):
1100    """Convert line based data output to obj
1101    FIXME: Obsolete?
1102    """
1103
1104    import glob, os
1105    from anuga.config import data_dir
1106
1107
1108    #Get bathymetry and x,y's
1109    lines = open(data_dir+os.sep+basefilename+'_geometry.dat', 'r').readlines()
1110
1111    from Numeric import zeros, Float
1112
1113    M = len(lines)  #Number of lines
1114    x = zeros((M,3), Float)
1115    y = zeros((M,3), Float)
1116    z = zeros((M,3), Float)
1117
1118    ##i = 0
1119    for i, line in enumerate(lines):
1120        tokens = line.split()
1121        values = map(float,tokens)
1122
1123        for j in range(3):
1124            x[i,j] = values[j*3]
1125            y[i,j] = values[j*3+1]
1126            z[i,j] = values[j*3+2]
1127
1128        ##i += 1
1129
1130
1131    #Write obj for bathymetry
1132    write_obj(data_dir+os.sep+basefilename+'_geometry',x,y,z)
1133
1134
1135    #Now read all the data files with variable information, combine with
1136    #x,y info
1137    #and store as obj
1138
1139    files = glob.glob(data_dir+os.sep+basefilename+'*.dat')
1140
1141    for filename in files:
1142        print 'Processing %s' % filename
1143
1144        lines = open(data_dir+os.sep+filename,'r').readlines()
1145        assert len(lines) == M
1146        root, ext = os.path.splitext(filename)
1147
1148        #Get time from filename
1149        i0 = filename.find('_time=')
1150        if i0 == -1:
1151            #Skip bathymetry file
1152            continue
1153
1154        i0 += 6  #Position where time starts
1155        i1 = filename.find('.dat')
1156
1157        if i1 > i0:
1158            t = float(filename[i0:i1])
1159        else:
1160            raise DataTimeError, 'Hmmmm'
1161
1162
1163
1164        ##i = 0
1165        for i, line in enumerate(lines):
1166            tokens = line.split()
1167            values = map(float,tokens)
1168
1169            for j in range(3):
1170                z[i,j] = values[j]
1171
1172            ##i += 1
1173
1174        #Write obj for variable data
1175        write_obj(data_dir+os.sep+basefilename+'_time=%.4f' %t,x,y,z)
1176
1177
1178def filter_netcdf(filename1, filename2, first=0, last=None, step = 1):
1179    """Read netcdf filename1, pick timesteps first:step:last and save to
1180    nettcdf file filename2
1181    """
1182    from Scientific.IO.NetCDF import NetCDFFile
1183
1184    #Get NetCDF
1185    infile = NetCDFFile(filename1, 'r')  #Open existing file for read
1186    outfile = NetCDFFile(filename2, 'w')  #Open new file
1187
1188
1189    #Copy dimensions
1190    for d in infile.dimensions:
1191        outfile.createDimension(d, infile.dimensions[d])
1192
1193    for name in infile.variables:
1194        var = infile.variables[name]
1195        outfile.createVariable(name, var.typecode(), var.dimensions)
1196
1197
1198    #Copy the static variables
1199    for name in infile.variables:
1200        if name == 'time' or name == 'stage':
1201            pass
1202        else:
1203            #Copy
1204            outfile.variables[name][:] = infile.variables[name][:]
1205
1206    #Copy selected timesteps
1207    time = infile.variables['time']
1208    stage = infile.variables['stage']
1209
1210    newtime = outfile.variables['time']
1211    newstage = outfile.variables['stage']
1212
1213    if last is None:
1214        last = len(time)
1215
1216    selection = range(first, last, step)
1217    for i, j in enumerate(selection):
1218        print 'Copying timestep %d of %d (%f)' %(j, last-first, time[j])
1219        newtime[i] = time[j]
1220        newstage[i,:] = stage[j,:]
1221
1222    #Close
1223    infile.close()
1224    outfile.close()
1225
1226
1227#Get data objects
1228def get_dataobject(domain, mode='w'):
1229    """Return instance of class of given format using filename
1230    """
1231
1232    cls = eval('Data_format_%s' %domain.format)
1233    return cls(domain, mode)
1234
1235
1236
1237
1238def dem2pts(basename_in, basename_out=None,
1239            easting_min=None, easting_max=None,
1240            northing_min=None, northing_max=None,
1241            use_cache=False, verbose=False,):
1242    """Read Digitial Elevation model from the following NetCDF format (.dem)
1243
1244    Example:
1245
1246    ncols         3121
1247    nrows         1800
1248    xllcorner     722000
1249    yllcorner     5893000
1250    cellsize      25
1251    NODATA_value  -9999
1252    138.3698 137.4194 136.5062 135.5558 ..........
1253
1254    Convert to NetCDF pts format which is
1255
1256    points:  (Nx2) Float array
1257    elevation: N Float array
1258    """
1259
1260
1261
1262    kwargs = {'basename_out': basename_out,
1263              'easting_min': easting_min,
1264              'easting_max': easting_max,
1265              'northing_min': northing_min,
1266              'northing_max': northing_max,
1267              'verbose': verbose}
1268
1269    if use_cache is True:
1270        from caching import cache
1271        result = cache(_dem2pts, basename_in, kwargs,
1272                       dependencies = [basename_in + '.dem'],
1273                       verbose = verbose)
1274
1275    else:
1276        result = apply(_dem2pts, [basename_in], kwargs)
1277
1278    return result
1279
1280
1281def _dem2pts(basename_in, basename_out=None, verbose=False,
1282            easting_min=None, easting_max=None,
1283            northing_min=None, northing_max=None):
1284    """Read Digitial Elevation model from the following NetCDF format (.dem)
1285
1286    Internal function. See public function dem2pts for details.
1287    """
1288
1289    #FIXME: Can this be written feasibly using write_pts?
1290
1291    import os
1292    from Scientific.IO.NetCDF import NetCDFFile
1293    from Numeric import Float, zeros, reshape, sum
1294
1295    root = basename_in
1296
1297    #Get NetCDF
1298    infile = NetCDFFile(root + '.dem', 'r')  #Open existing netcdf file for read
1299
1300    if verbose: print 'Reading DEM from %s' %(root + '.dem')
1301
1302    ncols = infile.ncols[0]
1303    nrows = infile.nrows[0]
1304    xllcorner = infile.xllcorner[0]  #Easting of lower left corner
1305    yllcorner = infile.yllcorner[0]  #Northing of lower left corner
1306    cellsize = infile.cellsize[0]
1307    NODATA_value = infile.NODATA_value[0]
1308    dem_elevation = infile.variables['elevation']
1309
1310    zone = infile.zone[0]
1311    false_easting = infile.false_easting[0]
1312    false_northing = infile.false_northing[0]
1313
1314    #Text strings
1315    projection = infile.projection
1316    datum = infile.datum
1317    units = infile.units
1318
1319
1320    #Get output file
1321    if basename_out == None:
1322        ptsname = root + '.pts'
1323    else:
1324        ptsname = basename_out + '.pts'
1325
1326    if verbose: print 'Store to NetCDF file %s' %ptsname
1327    # NetCDF file definition
1328    outfile = NetCDFFile(ptsname, 'w')
1329
1330    #Create new file
1331    outfile.institution = 'Geoscience Australia'
1332    outfile.description = 'NetCDF pts format for compact and portable storage ' +\
1333                      'of spatial point data'
1334    #assign default values
1335    if easting_min is None: easting_min = xllcorner
1336    if easting_max is None: easting_max = xllcorner + ncols*cellsize
1337    if northing_min is None: northing_min = yllcorner
1338    if northing_max is None: northing_max = yllcorner + nrows*cellsize
1339
1340    #compute offsets to update georeferencing
1341    easting_offset = xllcorner - easting_min
1342    northing_offset = yllcorner - northing_min
1343
1344    #Georeferencing
1345    outfile.zone = zone
1346    outfile.xllcorner = easting_min #Easting of lower left corner
1347    outfile.yllcorner = northing_min #Northing of lower left corner
1348    outfile.false_easting = false_easting
1349    outfile.false_northing = false_northing
1350
1351    outfile.projection = projection
1352    outfile.datum = datum
1353    outfile.units = units
1354
1355
1356    #Grid info (FIXME: probably not going to be used, but heck)
1357    outfile.ncols = ncols
1358    outfile.nrows = nrows
1359
1360    dem_elevation_r = reshape(dem_elevation, (nrows, ncols))
1361    totalnopoints = nrows*ncols
1362
1363    # calculating number of NODATA_values for each row in clipped region
1364    #FIXME: use array operations to do faster
1365    nn = 0
1366    k = 0
1367    i1_0 = 0
1368    j1_0 = 0
1369    thisj = 0
1370    thisi = 0
1371    for i in range(nrows):
1372        y = (nrows-i-1)*cellsize + yllcorner
1373        for j in range(ncols):
1374            x = j*cellsize + xllcorner
1375            if easting_min <= x <= easting_max and \
1376               northing_min <= y <= northing_max:
1377                thisj = j
1378                thisi = i
1379                if dem_elevation_r[i,j] == NODATA_value: nn += 1
1380
1381                if k == 0:
1382                    i1_0 = i
1383                    j1_0 = j
1384                k += 1
1385
1386    index1 = j1_0
1387    index2 = thisj
1388
1389    # dimension definitions
1390    nrows_in_bounding_box = int(round((northing_max-northing_min)/cellsize))
1391    ncols_in_bounding_box = int(round((easting_max-easting_min)/cellsize))
1392
1393    clippednopoints = (thisi+1-i1_0)*(thisj+1-j1_0)
1394    nopoints = clippednopoints-nn
1395
1396    clipped_dem_elev = dem_elevation_r[i1_0:thisi+1,j1_0:thisj+1]
1397
1398    if verbose:
1399        print 'There are %d values in the elevation' %totalnopoints
1400        print 'There are %d values in the clipped elevation' %clippednopoints
1401        print 'There are %d NODATA_values in the clipped elevation' %nn
1402
1403    outfile.createDimension('number_of_points', nopoints)
1404    outfile.createDimension('number_of_dimensions', 2) #This is 2d data
1405
1406    # variable definitions
1407    outfile.createVariable('points', Float, ('number_of_points',
1408                                             'number_of_dimensions'))
1409    outfile.createVariable('elevation', Float, ('number_of_points',))
1410
1411    # Get handles to the variables
1412    points = outfile.variables['points']
1413    elevation = outfile.variables['elevation']
1414
1415    lenv = index2-index1+1
1416    #Store data
1417    global_index = 0
1418    #for i in range(nrows):
1419    for i in range(i1_0,thisi+1,1):
1420        if verbose and i%((nrows+10)/10)==0:
1421            print 'Processing row %d of %d' %(i, nrows)
1422
1423        lower_index = global_index
1424
1425        v = dem_elevation_r[i,index1:index2+1]
1426        no_NODATA = sum(v == NODATA_value)
1427        if no_NODATA > 0:
1428            newcols = lenv - no_NODATA #ncols_in_bounding_box - no_NODATA
1429        else:
1430            newcols = lenv #ncols_in_bounding_box
1431
1432        telev = zeros(newcols, Float)
1433        tpoints = zeros((newcols, 2), Float)
1434
1435        local_index = 0
1436
1437        y = (nrows-i-1)*cellsize + yllcorner
1438        #for j in range(ncols):
1439        for j in range(j1_0,index2+1,1):
1440
1441            x = j*cellsize + xllcorner
1442            if easting_min <= x <= easting_max and \
1443               northing_min <= y <= northing_max and \
1444               dem_elevation_r[i,j] <> NODATA_value:
1445                tpoints[local_index, :] = [x-easting_min,y-northing_min]
1446                telev[local_index] = dem_elevation_r[i, j]
1447                global_index += 1
1448                local_index += 1
1449
1450        upper_index = global_index
1451
1452        if upper_index == lower_index + newcols:
1453            points[lower_index:upper_index, :] = tpoints
1454            elevation[lower_index:upper_index] = telev
1455
1456    assert global_index == nopoints, 'index not equal to number of points'
1457
1458    infile.close()
1459    outfile.close()
1460
1461
1462
1463def _read_hecras_cross_sections(lines):
1464    """Return block of surface lines for each cross section
1465    Starts with SURFACE LINE,
1466    Ends with END CROSS-SECTION
1467    """
1468
1469    points = []
1470
1471    reading_surface = False
1472    for i, line in enumerate(lines):
1473
1474        if len(line.strip()) == 0:    #Ignore blanks
1475            continue
1476
1477        if lines[i].strip().startswith('SURFACE LINE'):
1478            reading_surface = True
1479            continue
1480
1481        if lines[i].strip().startswith('END') and reading_surface:
1482            yield points
1483            reading_surface = False
1484            points = []
1485
1486        if reading_surface:
1487            fields = line.strip().split(',')
1488            easting = float(fields[0])
1489            northing = float(fields[1])
1490            elevation = float(fields[2])
1491            points.append([easting, northing, elevation])
1492
1493
1494
1495
1496def hecras_cross_sections2pts(basename_in,
1497                              basename_out=None,
1498                              verbose=False):
1499    """Read HEC-RAS Elevation datal from the following ASCII format (.sdf)
1500
1501    Example:
1502
1503
1504# RAS export file created on Mon 15Aug2005 11:42
1505# by HEC-RAS Version 3.1.1
1506
1507BEGIN HEADER:
1508  UNITS: METRIC
1509  DTM TYPE: TIN
1510  DTM: v:\1\cit\perth_topo\river_tin
1511  STREAM LAYER: c:\local\hecras\21_02_03\up_canning_cent3d.shp
1512  CROSS-SECTION LAYER: c:\local\hecras\21_02_03\up_can_xs3d.shp
1513  MAP PROJECTION: UTM
1514  PROJECTION ZONE: 50
1515  DATUM: AGD66
1516  VERTICAL DATUM:
1517  NUMBER OF REACHES:  19
1518  NUMBER OF CROSS-SECTIONS:  14206
1519END HEADER:
1520
1521
1522Only the SURFACE LINE data of the following form will be utilised
1523
1524  CROSS-SECTION:
1525    STREAM ID:Southern-Wungong
1526    REACH ID:Southern-Wungong
1527    STATION:19040.*
1528    CUT LINE:
1529      405548.671603161 , 6438142.7594925
1530      405734.536092045 , 6438326.10404912
1531      405745.130459356 , 6438331.48627354
1532      405813.89633823 , 6438368.6272789
1533    SURFACE LINE:
1534     405548.67,   6438142.76,   35.37
1535     405552.24,   6438146.28,   35.41
1536     405554.78,   6438148.78,   35.44
1537     405555.80,   6438149.79,   35.44
1538     405559.37,   6438153.31,   35.45
1539     405560.88,   6438154.81,   35.44
1540     405562.93,   6438156.83,   35.42
1541     405566.50,   6438160.35,   35.38
1542     405566.99,   6438160.83,   35.37
1543     ...
1544   END CROSS-SECTION
1545
1546    Convert to NetCDF pts format which is
1547
1548    points:  (Nx2) Float array
1549    elevation: N Float array
1550    """
1551
1552    import os
1553    from Scientific.IO.NetCDF import NetCDFFile
1554    from Numeric import Float, zeros, reshape
1555
1556    root = basename_in
1557
1558    #Get ASCII file
1559    infile = open(root + '.sdf', 'r')  #Open SDF file for read
1560
1561    if verbose: print 'Reading DEM from %s' %(root + '.sdf')
1562
1563    lines = infile.readlines()
1564    infile.close()
1565
1566    if verbose: print 'Converting to pts format'
1567
1568    i = 0
1569    while lines[i].strip() == '' or lines[i].strip().startswith('#'):
1570        i += 1
1571
1572    assert lines[i].strip().upper() == 'BEGIN HEADER:'
1573    i += 1
1574
1575    assert lines[i].strip().upper().startswith('UNITS:')
1576    units = lines[i].strip().split()[1]
1577    i += 1
1578
1579    assert lines[i].strip().upper().startswith('DTM TYPE:')
1580    i += 1
1581
1582    assert lines[i].strip().upper().startswith('DTM:')
1583    i += 1
1584
1585    assert lines[i].strip().upper().startswith('STREAM')
1586    i += 1
1587
1588    assert lines[i].strip().upper().startswith('CROSS')
1589    i += 1
1590
1591    assert lines[i].strip().upper().startswith('MAP PROJECTION:')
1592    projection = lines[i].strip().split(':')[1]
1593    i += 1
1594
1595    assert lines[i].strip().upper().startswith('PROJECTION ZONE:')
1596    zone = int(lines[i].strip().split(':')[1])
1597    i += 1
1598
1599    assert lines[i].strip().upper().startswith('DATUM:')
1600    datum = lines[i].strip().split(':')[1]
1601    i += 1
1602
1603    assert lines[i].strip().upper().startswith('VERTICAL DATUM:')
1604    i += 1
1605
1606    assert lines[i].strip().upper().startswith('NUMBER OF REACHES:')
1607    i += 1
1608
1609    assert lines[i].strip().upper().startswith('NUMBER OF CROSS-SECTIONS:')
1610    number_of_cross_sections = int(lines[i].strip().split(':')[1])
1611    i += 1
1612
1613
1614    #Now read all points
1615    points = []
1616    elevation = []
1617    for j, entries in enumerate(_read_hecras_cross_sections(lines[i:])):
1618        for k, entry in enumerate(entries):
1619            points.append(entry[:2])
1620            elevation.append(entry[2])
1621
1622
1623    msg = 'Actual #number_of_cross_sections == %d, Reported as %d'\
1624          %(j+1, number_of_cross_sections)
1625    assert j+1 == number_of_cross_sections, msg
1626
1627    #Get output file
1628    if basename_out == None:
1629        ptsname = root + '.pts'
1630    else:
1631        ptsname = basename_out + '.pts'
1632
1633    geo_ref = Geo_reference(zone, 0, 0, datum, projection, units)
1634    geo = Geospatial_data(points, {"elevation":elevation},
1635                          verbose=verbose, geo_reference=geo_ref)
1636    geo.export_points_file(ptsname)
1637
1638def export_grid(basename_in, extra_name_out = None,
1639                quantities = None, # defaults to elevation
1640                timestep = None,
1641                reduction = None,
1642                cellsize = 10,
1643                NODATA_value = -9999,
1644                easting_min = None,
1645                easting_max = None,
1646                northing_min = None,
1647                northing_max = None,
1648                verbose = False,
1649                origin = None,
1650                datum = 'WGS84',
1651                format = 'ers'):
1652    """
1653   
1654    Wrapper for sww2dem. - see sww2dem to find out what most of the
1655    parameters do.
1656
1657    Quantities is a list of quantities.  Each quantity will be
1658    calculated for each sww file.
1659
1660    This returns the basenames of the files returned, which is made up
1661    of the dir and all of the file name, except the extension.
1662
1663    This function returns the names of the files produced.
1664
1665    It will also produce as many output files as there are input sww files.
1666    """
1667   
1668    if quantities is None:
1669        quantities = ['elevation']
1670       
1671    if type(quantities) is str:
1672            quantities = [quantities]
1673
1674    # How many sww files are there?
1675    dir, base = os.path.split(basename_in)
1676    #print "basename_in",basename_in
1677    #print "base",base
1678
1679    iterate_over = get_all_swwfiles(dir,base,verbose)
1680   
1681    if dir == "":
1682        dir = "." # Unix compatibility
1683   
1684    files_out = []
1685    #print 'sww_file',iterate_over
1686    for sww_file in iterate_over:
1687        for quantity in quantities:
1688            if extra_name_out is None:
1689                basename_out = sww_file + '_' + quantity
1690            else:
1691                basename_out = sww_file + '_' + quantity + '_' \
1692                               + extra_name_out
1693#            print "basename_out", basename_out
1694       
1695            file_out = sww2dem(dir+sep+sww_file, dir+sep+basename_out,
1696                               quantity, 
1697                               timestep,
1698                               reduction,
1699                               cellsize,
1700                               NODATA_value,
1701                               easting_min,
1702                               easting_max,
1703                               northing_min,
1704                               northing_max,
1705                               verbose,
1706                               origin,
1707                               datum,
1708                               format)
1709            files_out.append(file_out)
1710    #print "basenames_out after",basenames_out
1711    return files_out
1712
1713
1714def get_timeseries(production_dirs, output_dir, scenario_name, gauges_dir_name,
1715                   plot_quantity, generate_fig = False,
1716                   reportname = None, surface = False, time_min = None,
1717                   time_max = None, title_on = False, verbose = True,
1718                   nodes=None):
1719    """
1720    nodes - number of processes used.
1721
1722    warning - this function has no tests
1723    """
1724    if reportname == None:
1725        report = False
1726    else:
1727        report = True
1728       
1729    if nodes is None:
1730        is_parallel = False
1731    else:
1732        is_parallel = True
1733       
1734    # Generate figures
1735    swwfiles = {}
1736   
1737    if is_parallel is True:   
1738        for i in range(nodes):
1739            print 'Sending node %d of %d' %(i,nodes)
1740            swwfiles = {}
1741            if not reportname == None:
1742                reportname = report_name + '_%s' %(i)
1743            for label_id in production_dirs.keys():
1744                if label_id == 'boundaries':
1745                    swwfile = best_boundary_sww
1746                else:
1747                    file_loc = output_dir + label_id + sep
1748                    sww_extra = '_P%s_%s' %(i,nodes)
1749                    swwfile = file_loc + scenario_name + sww_extra + '.sww'
1750                    print 'swwfile',swwfile
1751                    swwfiles[swwfile] = label_id
1752
1753                texname, elev_output = sww2timeseries(swwfiles,
1754                                              gauges_dir_name,
1755                                              production_dirs,
1756                                              report = report,
1757                                              reportname = reportname,
1758                                              plot_quantity = plot_quantity,
1759                                              generate_fig = generate_fig,
1760                                              surface = surface,
1761                                              time_min = time_min,
1762                                              time_max = time_max,
1763                                              title_on = title_on,
1764                                              verbose = verbose)
1765    else:   
1766        for label_id in production_dirs.keys():       
1767            if label_id == 'boundaries':
1768                print 'boundaries'
1769                file_loc = project.boundaries_in_dir
1770                swwfile = project.boundaries_dir_name3 + '.sww'
1771                #  swwfile = boundary_dir_filename
1772            else:
1773                file_loc = output_dir + label_id + sep
1774                swwfile = file_loc + scenario_name + '.sww'
1775            swwfiles[swwfile] = label_id
1776       
1777        texname, elev_output = sww2timeseries(swwfiles,
1778                                              gauges_dir_name,
1779                                              production_dirs,
1780                                              report = report,
1781                                              reportname = reportname,
1782                                              plot_quantity = plot_quantity,
1783                                              generate_fig = generate_fig,
1784                                              surface = surface,
1785                                              time_min = time_min,
1786                                              time_max = time_max,
1787                                              title_on = title_on,
1788                                              verbose = verbose)
1789                                         
1790
1791   
1792def sww2dem(basename_in, basename_out = None,
1793            quantity = None, # defaults to elevation
1794            timestep = None,
1795            reduction = None,
1796            cellsize = 10,
1797            NODATA_value = -9999,
1798            easting_min = None,
1799            easting_max = None,
1800            northing_min = None,
1801            northing_max = None,
1802            verbose = False,
1803            origin = None,
1804            datum = 'WGS84',
1805            format = 'ers'):
1806
1807    """Read SWW file and convert to Digitial Elevation model format
1808    (.asc or .ers)
1809
1810    Example (ASC):
1811
1812    ncols         3121
1813    nrows         1800
1814    xllcorner     722000
1815    yllcorner     5893000
1816    cellsize      25
1817    NODATA_value  -9999
1818    138.3698 137.4194 136.5062 135.5558 ..........
1819
1820    Also write accompanying file with same basename_in but extension .prj
1821    used to fix the UTM zone, datum, false northings and eastings.
1822
1823    The prj format is assumed to be as
1824
1825    Projection    UTM
1826    Zone          56
1827    Datum         WGS84
1828    Zunits        NO
1829    Units         METERS
1830    Spheroid      WGS84
1831    Xshift        0.0000000000
1832    Yshift        10000000.0000000000
1833    Parameters
1834
1835
1836    The parameter quantity must be the name of an existing quantity or
1837    an expression involving existing quantities. The default is
1838    'elevation'. Quantity is not a list of quantities.
1839
1840    if timestep (an index) is given, output quantity at that timestep
1841
1842    if reduction is given use that to reduce quantity over all timesteps.
1843
1844    datum
1845
1846    format can be either 'asc' or 'ers'
1847    """
1848
1849    import sys
1850    from Numeric import array, Float, concatenate, NewAxis, zeros, reshape, \
1851         sometrue
1852    from Numeric import array2string
1853
1854    from anuga.utilities.polygon import inside_polygon, outside_polygon, \
1855         separate_points_by_polygon
1856    from anuga.abstract_2d_finite_volumes.util import \
1857         apply_expression_to_dictionary
1858
1859    msg = 'Format must be either asc or ers'
1860    assert format.lower() in ['asc', 'ers'], msg
1861
1862
1863    false_easting = 500000
1864    false_northing = 10000000
1865
1866    if quantity is None:
1867        quantity = 'elevation'
1868       
1869    if reduction is None:
1870        reduction = max
1871
1872    if basename_out is None:
1873        basename_out = basename_in + '_%s' %quantity
1874
1875    if quantity_formula.has_key(quantity):
1876        quantity = quantity_formula[quantity]
1877       
1878    swwfile = basename_in + '.sww'
1879    demfile = basename_out + '.' + format
1880    # Note the use of a .ers extension is optional (write_ermapper_grid will
1881    # deal with either option
1882   
1883    #if verbose: bye= nsuadsfd[0] # uncomment to check catching verbose errors
1884   
1885    # Read sww file
1886    if verbose: 
1887        print 'Reading from %s' %swwfile
1888        print 'Output directory is %s' %basename_out
1889   
1890    from Scientific.IO.NetCDF import NetCDFFile
1891    fid = NetCDFFile(swwfile)
1892
1893    #Get extent and reference
1894    x = fid.variables['x'][:]
1895    y = fid.variables['y'][:]
1896    volumes = fid.variables['volumes'][:]
1897    if timestep is not None:
1898        times = fid.variables['time'][timestep]
1899    else:
1900        times = fid.variables['time'][:]
1901
1902    number_of_timesteps = fid.dimensions['number_of_timesteps']
1903    number_of_points = fid.dimensions['number_of_points']
1904   
1905    if origin is None:
1906
1907        # Get geo_reference
1908        # sww files don't have to have a geo_ref
1909        try:
1910            geo_reference = Geo_reference(NetCDFObject=fid)
1911        except AttributeError, e:
1912            geo_reference = Geo_reference() # Default georef object
1913
1914        xllcorner = geo_reference.get_xllcorner()
1915        yllcorner = geo_reference.get_yllcorner()
1916        zone = geo_reference.get_zone()
1917    else:
1918        zone = origin[0]
1919        xllcorner = origin[1]
1920        yllcorner = origin[2]
1921
1922
1923
1924    # FIXME: Refactor using code from Interpolation_function.statistics
1925    # (in interpolate.py)
1926    # Something like print swwstats(swwname)
1927    if verbose:
1928        print '------------------------------------------------'
1929        print 'Statistics of SWW file:'
1930        print '  Name: %s' %swwfile
1931        print '  Reference:'
1932        print '    Lower left corner: [%f, %f]'\
1933              %(xllcorner, yllcorner)
1934        if timestep is not None:
1935            print '    Time: %f' %(times)
1936        else:
1937            print '    Start time: %f' %fid.starttime[0]
1938        print '  Extent:'
1939        print '    x [m] in [%f, %f], len(x) == %d'\
1940              %(min(x.flat), max(x.flat), len(x.flat))
1941        print '    y [m] in [%f, %f], len(y) == %d'\
1942              %(min(y.flat), max(y.flat), len(y.flat))
1943        if timestep is not None:
1944            print '    t [s] = %f, len(t) == %d' %(times, 1)
1945        else:
1946            print '    t [s] in [%f, %f], len(t) == %d'\
1947              %(min(times), max(times), len(times))
1948        print '  Quantities [SI units]:'
1949        for name in ['stage', 'xmomentum', 'ymomentum']:
1950            q = fid.variables[name][:].flat
1951            if timestep is not None:
1952                q = q[timestep*len(x):(timestep+1)*len(x)]
1953            if verbose: print '    %s in [%f, %f]' %(name, min(q), max(q))
1954        for name in ['elevation']:
1955            q = fid.variables[name][:].flat
1956            if verbose: print '    %s in [%f, %f]' %(name, min(q), max(q))
1957           
1958    # Get quantity and reduce if applicable
1959    if verbose: print 'Processing quantity %s' %quantity
1960
1961    # Turn NetCDF objects into Numeric arrays
1962    quantity_dict = {}
1963    for name in fid.variables.keys():
1964        quantity_dict[name] = fid.variables[name][:] 
1965
1966
1967    # Convert quantity expression to quantities found in sww file   
1968    q = apply_expression_to_dictionary(quantity, quantity_dict)
1969
1970    if len(q.shape) == 2:
1971        #q has a time component and needs to be reduced along
1972        #the temporal dimension
1973        if verbose: print 'Reducing quantity %s' %quantity
1974        q_reduced = zeros( number_of_points, Float )
1975       
1976        if timestep is not None:
1977            for k in range(number_of_points):
1978                q_reduced[k] = q[timestep,k] 
1979        else:
1980            for k in range(number_of_points):
1981                q_reduced[k] = reduction( q[:,k] )
1982
1983        q = q_reduced
1984
1985    #Post condition: Now q has dimension: number_of_points
1986    assert len(q.shape) == 1
1987    assert q.shape[0] == number_of_points
1988
1989
1990    if verbose:
1991        print 'Processed values for %s are in [%f, %f]' %(quantity, min(q), max(q))
1992
1993
1994    #Create grid and update xll/yll corner and x,y
1995
1996    #Relative extent
1997    if easting_min is None:
1998        xmin = min(x)
1999    else:
2000        xmin = easting_min - xllcorner
2001
2002    if easting_max is None:
2003        xmax = max(x)
2004    else:
2005        xmax = easting_max - xllcorner
2006
2007    if northing_min is None:
2008        ymin = min(y)
2009    else:
2010        ymin = northing_min - yllcorner
2011
2012    if northing_max is None:
2013        ymax = max(y)
2014    else:
2015        ymax = northing_max - yllcorner
2016
2017
2018
2019    if verbose: print 'Creating grid'
2020    ncols = int((xmax-xmin)/cellsize)+1
2021    nrows = int((ymax-ymin)/cellsize)+1
2022
2023
2024    #New absolute reference and coordinates
2025    newxllcorner = xmin+xllcorner
2026    newyllcorner = ymin+yllcorner
2027
2028    x = x+xllcorner-newxllcorner
2029    y = y+yllcorner-newyllcorner
2030   
2031    vertex_points = concatenate ((x[:, NewAxis] ,y[:, NewAxis]), axis = 1)
2032    assert len(vertex_points.shape) == 2
2033
2034    grid_points = zeros ( (ncols*nrows, 2), Float )
2035
2036
2037    for i in xrange(nrows):
2038        if format.lower() == 'asc':
2039            yg = i*cellsize
2040        else:
2041        #this will flip the order of the y values for ers
2042            yg = (nrows-i)*cellsize
2043
2044        for j in xrange(ncols):
2045            xg = j*cellsize
2046            k = i*ncols + j
2047
2048            grid_points[k,0] = xg
2049            grid_points[k,1] = yg
2050
2051    #Interpolate
2052    from anuga.fit_interpolate.interpolate import Interpolate
2053
2054    # Remove loners from vertex_points, volumes here
2055    vertex_points, volumes = remove_lone_verts(vertex_points, volumes)
2056    #export_mesh_file('monkey.tsh',{'vertices':vertex_points, 'triangles':volumes})
2057    #import sys; sys.exit()
2058    interp = Interpolate(vertex_points, volumes, verbose = verbose)
2059
2060    #Interpolate using quantity values
2061    if verbose: print 'Interpolating'
2062    grid_values = interp.interpolate(q, grid_points).flat
2063
2064
2065    if verbose:
2066        print 'Interpolated values are in [%f, %f]' %(min(grid_values),
2067                                                      max(grid_values))
2068
2069    #Assign NODATA_value to all points outside bounding polygon (from interpolation mesh)
2070    P = interp.mesh.get_boundary_polygon()
2071    outside_indices = outside_polygon(grid_points, P, closed=True)
2072
2073    for i in outside_indices:
2074        grid_values[i] = NODATA_value
2075
2076
2077
2078
2079    if format.lower() == 'ers':
2080        # setup ERS header information
2081        grid_values = reshape(grid_values,(nrows, ncols))
2082        header = {}
2083        header['datum'] = '"' + datum + '"'
2084        # FIXME The use of hardwired UTM and zone number needs to be made optional
2085        # FIXME Also need an automatic test for coordinate type (i.e. EN or LL)
2086        header['projection'] = '"UTM-' + str(zone) + '"'
2087        header['coordinatetype'] = 'EN'
2088        if header['coordinatetype'] == 'LL':
2089            header['longitude'] = str(newxllcorner)
2090            header['latitude'] = str(newyllcorner)
2091        elif header['coordinatetype'] == 'EN':
2092            header['eastings'] = str(newxllcorner)
2093            header['northings'] = str(newyllcorner)
2094        header['nullcellvalue'] = str(NODATA_value)
2095        header['xdimension'] = str(cellsize)
2096        header['ydimension'] = str(cellsize)
2097        header['value'] = '"' + quantity + '"'
2098        #header['celltype'] = 'IEEE8ByteReal'  #FIXME: Breaks unit test
2099
2100
2101        #Write
2102        if verbose: print 'Writing %s' %demfile
2103        import ermapper_grids
2104        ermapper_grids.write_ermapper_grid(demfile, grid_values, header)
2105
2106        fid.close()
2107    else:
2108        #Write to Ascii format
2109
2110        #Write prj file
2111        prjfile = basename_out + '.prj'
2112
2113        if verbose: print 'Writing %s' %prjfile
2114        prjid = open(prjfile, 'w')
2115        prjid.write('Projection    %s\n' %'UTM')
2116        prjid.write('Zone          %d\n' %zone)
2117        prjid.write('Datum         %s\n' %datum)
2118        prjid.write('Zunits        NO\n')
2119        prjid.write('Units         METERS\n')
2120        prjid.write('Spheroid      %s\n' %datum)
2121        prjid.write('Xshift        %d\n' %false_easting)
2122        prjid.write('Yshift        %d\n' %false_northing)
2123        prjid.write('Parameters\n')
2124        prjid.close()
2125
2126
2127
2128        if verbose: print 'Writing %s' %demfile
2129
2130        ascid = open(demfile, 'w')
2131
2132        ascid.write('ncols         %d\n' %ncols)
2133        ascid.write('nrows         %d\n' %nrows)
2134        ascid.write('xllcorner     %d\n' %newxllcorner)
2135        ascid.write('yllcorner     %d\n' %newyllcorner)
2136        ascid.write('cellsize      %f\n' %cellsize)
2137        ascid.write('NODATA_value  %d\n' %NODATA_value)
2138
2139
2140        #Get bounding polygon from mesh
2141        #P = interp.mesh.get_boundary_polygon()
2142        #inside_indices = inside_polygon(grid_points, P)
2143
2144        for i in range(nrows):
2145            if verbose and i%((nrows+10)/10)==0:
2146                print 'Doing row %d of %d' %(i, nrows)
2147
2148            base_index = (nrows-i-1)*ncols
2149
2150            slice = grid_values[base_index:base_index+ncols]
2151            s = array2string(slice, max_line_width=sys.maxint)
2152            ascid.write(s[1:-1] + '\n')
2153
2154
2155            #print
2156            #for j in range(ncols):
2157            #    index = base_index+j#
2158            #    print grid_values[index],
2159            #    ascid.write('%f ' %grid_values[index])
2160            #ascid.write('\n')
2161
2162        #Close
2163        ascid.close()
2164        fid.close()
2165        return basename_out
2166
2167#Backwards compatibility
2168def sww2asc(basename_in, basename_out = None,
2169            quantity = None,
2170            timestep = None,
2171            reduction = None,
2172            cellsize = 10,
2173            verbose = False,
2174            origin = None):
2175    print 'sww2asc will soon be obsoleted - please use sww2dem'
2176    sww2dem(basename_in,
2177            basename_out = basename_out,
2178            quantity = quantity,
2179            timestep = timestep,
2180            reduction = reduction,
2181            cellsize = cellsize,
2182            verbose = verbose,
2183            origin = origin,
2184        datum = 'WGS84',
2185        format = 'asc')
2186
2187def sww2ers(basename_in, basename_out = None,
2188            quantity = None,
2189            timestep = None,
2190            reduction = None,
2191            cellsize = 10,
2192            verbose = False,
2193            origin = None,
2194            datum = 'WGS84'):
2195    print 'sww2ers will soon be obsoleted - please use sww2dem'
2196    sww2dem(basename_in,
2197            basename_out = basename_out,
2198            quantity = quantity,
2199            timestep = timestep,
2200            reduction = reduction,
2201            cellsize = cellsize,
2202            verbose = verbose,
2203            origin = origin,
2204            datum = datum,
2205            format = 'ers')
2206################################# END COMPATIBILITY ##############
2207
2208
2209
2210def sww2pts(basename_in, basename_out=None,
2211            data_points=None,
2212            quantity=None,
2213            timestep=None,
2214            reduction=None,
2215            NODATA_value=-9999,
2216            verbose=False,
2217            origin=None):
2218            #datum = 'WGS84')
2219
2220
2221    """Read SWW file and convert to interpolated values at selected points
2222
2223    The parameter quantity' must be the name of an existing quantity or
2224    an expression involving existing quantities. The default is
2225    'elevation'.
2226
2227    if timestep (an index) is given, output quantity at that timestep
2228
2229    if reduction is given use that to reduce quantity over all timesteps.
2230
2231    data_points (Nx2 array) give locations of points where quantity is to be computed.
2232   
2233    """
2234
2235    import sys
2236    from Numeric import array, Float, concatenate, NewAxis, zeros, reshape, sometrue
2237    from Numeric import array2string
2238
2239    from anuga.utilities.polygon import inside_polygon, outside_polygon, separate_points_by_polygon
2240    from anuga.abstract_2d_finite_volumes.util import apply_expression_to_dictionary
2241
2242    from anuga.geospatial_data.geospatial_data import Geospatial_data
2243
2244    if quantity is None:
2245        quantity = 'elevation'
2246
2247    if reduction is None:
2248        reduction = max
2249
2250    if basename_out is None:
2251        basename_out = basename_in + '_%s' %quantity
2252
2253    swwfile = basename_in + '.sww'
2254    ptsfile = basename_out + '.pts'
2255
2256    # Read sww file
2257    if verbose: print 'Reading from %s' %swwfile
2258    from Scientific.IO.NetCDF import NetCDFFile
2259    fid = NetCDFFile(swwfile)
2260
2261    # Get extent and reference
2262    x = fid.variables['x'][:]
2263    y = fid.variables['y'][:]
2264    volumes = fid.variables['volumes'][:]
2265
2266    number_of_timesteps = fid.dimensions['number_of_timesteps']
2267    number_of_points = fid.dimensions['number_of_points']
2268    if origin is None:
2269
2270        # Get geo_reference
2271        # sww files don't have to have a geo_ref
2272        try:
2273            geo_reference = Geo_reference(NetCDFObject=fid)
2274        except AttributeError, e:
2275            geo_reference = Geo_reference() #Default georef object
2276
2277        xllcorner = geo_reference.get_xllcorner()
2278        yllcorner = geo_reference.get_yllcorner()
2279        zone = geo_reference.get_zone()
2280    else:
2281        zone = origin[0]
2282        xllcorner = origin[1]
2283        yllcorner = origin[2]
2284
2285
2286
2287    # FIXME: Refactor using code from file_function.statistics
2288    # Something like print swwstats(swwname)
2289    if verbose:
2290        x = fid.variables['x'][:]
2291        y = fid.variables['y'][:]
2292        times = fid.variables['time'][:]
2293        print '------------------------------------------------'
2294        print 'Statistics of SWW file:'
2295        print '  Name: %s' %swwfile
2296        print '  Reference:'
2297        print '    Lower left corner: [%f, %f]'\
2298              %(xllcorner, yllcorner)
2299        print '    Start time: %f' %fid.starttime[0]
2300        print '  Extent:'
2301        print '    x [m] in [%f, %f], len(x) == %d'\
2302              %(min(x.flat), max(x.flat), len(x.flat))
2303        print '    y [m] in [%f, %f], len(y) == %d'\
2304              %(min(y.flat), max(y.flat), len(y.flat))
2305        print '    t [s] in [%f, %f], len(t) == %d'\
2306              %(min(times), max(times), len(times))
2307        print '  Quantities [SI units]:'
2308        for name in ['stage', 'xmomentum', 'ymomentum', 'elevation']:
2309            q = fid.variables[name][:].flat
2310            print '    %s in [%f, %f]' %(name, min(q), max(q))
2311
2312
2313
2314    # Get quantity and reduce if applicable
2315    if verbose: print 'Processing quantity %s' %quantity
2316
2317    # Turn NetCDF objects into Numeric arrays
2318    quantity_dict = {}
2319    for name in fid.variables.keys():
2320        quantity_dict[name] = fid.variables[name][:]
2321
2322
2323
2324    # Convert quantity expression to quantities found in sww file   
2325    q = apply_expression_to_dictionary(quantity, quantity_dict)
2326
2327
2328
2329    if len(q.shape) == 2:
2330        # q has a time component and needs to be reduced along
2331        # the temporal dimension
2332        if verbose: print 'Reducing quantity %s' %quantity
2333        q_reduced = zeros( number_of_points, Float )
2334
2335        for k in range(number_of_points):
2336            q_reduced[k] = reduction( q[:,k] )
2337
2338        q = q_reduced
2339
2340    # Post condition: Now q has dimension: number_of_points
2341    assert len(q.shape) == 1
2342    assert q.shape[0] == number_of_points
2343
2344
2345    if verbose:
2346        print 'Processed values for %s are in [%f, %f]' %(quantity, min(q), max(q))
2347
2348
2349    # Create grid and update xll/yll corner and x,y
2350    vertex_points = concatenate ((x[:, NewAxis] ,y[:, NewAxis]), axis = 1)
2351    assert len(vertex_points.shape) == 2
2352
2353    # Interpolate
2354    from anuga.fit_interpolate.interpolate import Interpolate
2355    interp = Interpolate(vertex_points, volumes, verbose = verbose)
2356
2357    # Interpolate using quantity values
2358    if verbose: print 'Interpolating'
2359    interpolated_values = interp.interpolate(q, data_points).flat
2360
2361
2362    if verbose:
2363        print 'Interpolated values are in [%f, %f]' %(min(interpolated_values),
2364                                                      max(interpolated_values))
2365
2366    # Assign NODATA_value to all points outside bounding polygon (from interpolation mesh)
2367    P = interp.mesh.get_boundary_polygon()
2368    outside_indices = outside_polygon(data_points, P, closed=True)
2369
2370    for i in outside_indices:
2371        interpolated_values[i] = NODATA_value
2372
2373
2374    # Store results   
2375    G = Geospatial_data(data_points=data_points,
2376                        attributes=interpolated_values)
2377
2378    G.export_points_file(ptsfile, absolute = True)
2379
2380    fid.close()
2381
2382
2383def convert_dem_from_ascii2netcdf(basename_in, basename_out = None,
2384                                  use_cache = False,
2385                                  verbose = False):
2386    """Read Digitial Elevation model from the following ASCII format (.asc)
2387
2388    Example:
2389
2390    ncols         3121
2391    nrows         1800
2392    xllcorner     722000
2393    yllcorner     5893000
2394    cellsize      25
2395    NODATA_value  -9999
2396    138.3698 137.4194 136.5062 135.5558 ..........
2397
2398    Convert basename_in + '.asc' to NetCDF format (.dem)
2399    mimicking the ASCII format closely.
2400
2401
2402    An accompanying file with same basename_in but extension .prj must exist
2403    and is used to fix the UTM zone, datum, false northings and eastings.
2404
2405    The prj format is assumed to be as
2406
2407    Projection    UTM
2408    Zone          56
2409    Datum         WGS84
2410    Zunits        NO
2411    Units         METERS
2412    Spheroid      WGS84
2413    Xshift        0.0000000000
2414    Yshift        10000000.0000000000
2415    Parameters
2416    """
2417
2418
2419
2420    kwargs = {'basename_out': basename_out, 'verbose': verbose}
2421
2422    if use_cache is True:
2423        from caching import cache
2424        result = cache(_convert_dem_from_ascii2netcdf, basename_in, kwargs,
2425                       dependencies = [basename_in + '.asc',
2426                                       basename_in + '.prj'],
2427                       verbose = verbose)
2428
2429    else:
2430        result = apply(_convert_dem_from_ascii2netcdf, [basename_in], kwargs)
2431
2432    return result
2433
2434
2435
2436
2437
2438
2439def _convert_dem_from_ascii2netcdf(basename_in, basename_out = None,
2440                                  verbose = False):
2441    """Read Digitial Elevation model from the following ASCII format (.asc)
2442
2443    Internal function. See public function convert_dem_from_ascii2netcdf for details.
2444    """
2445
2446    import os
2447    from Scientific.IO.NetCDF import NetCDFFile
2448    from Numeric import Float, array
2449
2450    #root, ext = os.path.splitext(basename_in)
2451    root = basename_in
2452
2453    ###########################################
2454    # Read Meta data
2455    if verbose: print 'Reading METADATA from %s' %root + '.prj'
2456    metadatafile = open(root + '.prj')
2457    metalines = metadatafile.readlines()
2458    metadatafile.close()
2459
2460    L = metalines[0].strip().split()
2461    assert L[0].strip().lower() == 'projection'
2462    projection = L[1].strip()                   #TEXT
2463
2464    L = metalines[1].strip().split()
2465    assert L[0].strip().lower() == 'zone'
2466    zone = int(L[1].strip())
2467
2468    L = metalines[2].strip().split()
2469    assert L[0].strip().lower() == 'datum'
2470    datum = L[1].strip()                        #TEXT
2471
2472    L = metalines[3].strip().split()
2473    assert L[0].strip().lower() == 'zunits'     #IGNORE
2474    zunits = L[1].strip()                       #TEXT
2475
2476    L = metalines[4].strip().split()
2477    assert L[0].strip().lower() == 'units'
2478    units = L[1].strip()                        #TEXT
2479
2480    L = metalines[5].strip().split()
2481    assert L[0].strip().lower() == 'spheroid'   #IGNORE
2482    spheroid = L[1].strip()                     #TEXT
2483
2484    L = metalines[6].strip().split()
2485    assert L[0].strip().lower() == 'xshift'
2486    false_easting = float(L[1].strip())
2487
2488    L = metalines[7].strip().split()
2489    assert L[0].strip().lower() == 'yshift'
2490    false_northing = float(L[1].strip())
2491
2492    #print false_easting, false_northing, zone, datum
2493
2494
2495    ###########################################
2496    #Read DEM data
2497
2498    datafile = open(basename_in + '.asc')
2499
2500    if verbose: print 'Reading DEM from %s' %(basename_in + '.asc')
2501    lines = datafile.readlines()
2502    datafile.close()
2503
2504    if verbose: print 'Got', len(lines), ' lines'
2505
2506    ncols = int(lines[0].split()[1].strip())
2507    nrows = int(lines[1].split()[1].strip())
2508    xllcorner = float(lines[2].split()[1].strip())
2509    yllcorner = float(lines[3].split()[1].strip())
2510    cellsize = float(lines[4].split()[1].strip())
2511    NODATA_value = int(lines[5].split()[1].strip())
2512
2513    assert len(lines) == nrows + 6
2514
2515
2516    ##########################################
2517
2518
2519    if basename_out == None:
2520        netcdfname = root + '.dem'
2521    else:
2522        netcdfname = basename_out + '.dem'
2523
2524    if verbose: print 'Store to NetCDF file %s' %netcdfname
2525    # NetCDF file definition
2526    fid = NetCDFFile(netcdfname, 'w')
2527
2528    #Create new file
2529    fid.institution = 'Geoscience Australia'
2530    fid.description = 'NetCDF DEM format for compact and portable storage ' +\
2531                      'of spatial point data'
2532
2533    fid.ncols = ncols
2534    fid.nrows = nrows
2535    fid.xllcorner = xllcorner
2536    fid.yllcorner = yllcorner
2537    fid.cellsize = cellsize
2538    fid.NODATA_value = NODATA_value
2539
2540    fid.zone = zone
2541    fid.false_easting = false_easting
2542    fid.false_northing = false_northing
2543    fid.projection = projection
2544    fid.datum = datum
2545    fid.units = units
2546
2547
2548    # dimension definitions
2549    fid.createDimension('number_of_rows', nrows)
2550    fid.createDimension('number_of_columns', ncols)
2551
2552    # variable definitions
2553    fid.createVariable('elevation', Float, ('number_of_rows',
2554                                            'number_of_columns'))
2555
2556    # Get handles to the variables
2557    elevation = fid.variables['elevation']
2558
2559    #Store data
2560    n = len(lines[6:])
2561    for i, line in enumerate(lines[6:]):
2562        fields = line.split()
2563        if verbose and i%((n+10)/10)==0:
2564            print 'Processing row %d of %d' %(i, nrows)
2565
2566        elevation[i, :] = array([float(x) for x in fields])
2567
2568    fid.close()
2569
2570
2571
2572
2573
2574def ferret2sww(basename_in, basename_out = None,
2575               verbose = False,
2576               minlat = None, maxlat = None,
2577               minlon = None, maxlon = None,
2578               mint = None, maxt = None, mean_stage = 0,
2579               origin = None, zscale = 1,
2580               fail_on_NaN = True,
2581               NaN_filler = 0,
2582               elevation = None,
2583               inverted_bathymetry = True
2584               ): #FIXME: Bathymetry should be obtained
2585                                  #from MOST somehow.
2586                                  #Alternatively from elsewhere
2587                                  #or, as a last resort,
2588                                  #specified here.
2589                                  #The value of -100 will work
2590                                  #for the Wollongong tsunami
2591                                  #scenario but is very hacky
2592    """Convert MOST and 'Ferret' NetCDF format for wave propagation to
2593    sww format native to abstract_2d_finite_volumes.
2594
2595    Specify only basename_in and read files of the form
2596    basefilename_ha.nc, basefilename_ua.nc, basefilename_va.nc containing
2597    relative height, x-velocity and y-velocity, respectively.
2598
2599    Also convert latitude and longitude to UTM. All coordinates are
2600    assumed to be given in the GDA94 datum.
2601
2602    min's and max's: If omitted - full extend is used.
2603    To include a value min may equal it, while max must exceed it.
2604    Lat and lon are assuemd to be in decimal degrees
2605
2606    origin is a 3-tuple with geo referenced
2607    UTM coordinates (zone, easting, northing)
2608
2609    nc format has values organised as HA[TIME, LATITUDE, LONGITUDE]
2610    which means that longitude is the fastest
2611    varying dimension (row major order, so to speak)
2612
2613    ferret2sww uses grid points as vertices in a triangular grid
2614    counting vertices from lower left corner upwards, then right
2615    """
2616
2617    import os
2618    from Scientific.IO.NetCDF import NetCDFFile
2619    from Numeric import Float, Int, Int32, searchsorted, zeros, array
2620    from Numeric import allclose, around
2621
2622    precision = Float
2623
2624    msg = 'Must use latitudes and longitudes for minlat, maxlon etc'
2625
2626    if minlat != None:
2627        assert -90 < minlat < 90 , msg
2628    if maxlat != None:
2629        assert -90 < maxlat < 90 , msg
2630        if minlat != None:
2631            assert maxlat > minlat
2632    if minlon != None:
2633        assert -180 < minlon < 180 , msg
2634    if maxlon != None:
2635        assert -180 < maxlon < 180 , msg
2636        if minlon != None:
2637            assert maxlon > minlon
2638       
2639
2640
2641    #Get NetCDF data
2642    if verbose: print 'Reading files %s_*.nc' %basename_in
2643    #print "basename_in + '_ha.nc'",basename_in + '_ha.nc'
2644    file_h = NetCDFFile(basename_in + '_ha.nc', 'r') #Wave amplitude (cm)
2645    file_u = NetCDFFile(basename_in + '_ua.nc', 'r') #Velocity (x) (cm/s)
2646    file_v = NetCDFFile(basename_in + '_va.nc', 'r') #Velocity (y) (cm/s)
2647    file_e = NetCDFFile(basename_in + '_e.nc', 'r')  #Elevation (z) (m)
2648
2649    if basename_out is None:
2650        swwname = basename_in + '.sww'
2651    else:
2652        swwname = basename_out + '.sww'
2653
2654    # Get dimensions of file_h
2655    for dimension in file_h.dimensions.keys():
2656        if dimension[:3] == 'LON':
2657            dim_h_longitude = dimension
2658        if dimension[:3] == 'LAT':
2659            dim_h_latitude = dimension
2660        if dimension[:4] == 'TIME':
2661            dim_h_time = dimension
2662
2663#    print 'long:', dim_h_longitude
2664#    print 'lats:', dim_h_latitude
2665#    print 'times:', dim_h_time
2666
2667    times = file_h.variables[dim_h_time]
2668    latitudes = file_h.variables[dim_h_latitude]
2669    longitudes = file_h.variables[dim_h_longitude]
2670   
2671    # get dimensions for file_e
2672    for dimension in file_e.dimensions.keys():
2673        if dimension[:3] == 'LON':
2674            dim_e_longitude = dimension
2675        if dimension[:3] == 'LAT':
2676            dim_e_latitude = dimension
2677
2678    # get dimensions for file_u
2679    for dimension in file_u.dimensions.keys():
2680        if dimension[:3] == 'LON':
2681            dim_u_longitude = dimension
2682        if dimension[:3] == 'LAT':
2683            dim_u_latitude = dimension
2684        if dimension[:4] == 'TIME':
2685            dim_u_time = dimension
2686           
2687    # get dimensions for file_v
2688    for dimension in file_v.dimensions.keys():
2689        if dimension[:3] == 'LON':
2690            dim_v_longitude = dimension
2691        if dimension[:3] == 'LAT':
2692            dim_v_latitude = dimension
2693        if dimension[:4] == 'TIME':
2694            dim_v_time = dimension
2695
2696
2697    # Precision used by most for lat/lon is 4 or 5 decimals
2698    e_lat = around(file_e.variables[dim_e_latitude][:], 5)
2699    e_lon = around(file_e.variables[dim_e_longitude][:], 5)
2700
2701    # Check that files are compatible
2702    assert allclose(latitudes, file_u.variables[dim_u_latitude])
2703    assert allclose(latitudes, file_v.variables[dim_v_latitude])
2704    assert allclose(latitudes, e_lat)
2705
2706    assert allclose(longitudes, file_u.variables[dim_u_longitude])
2707    assert allclose(longitudes, file_v.variables[dim_v_longitude])
2708    assert allclose(longitudes, e_lon)
2709
2710    if mint is None:
2711        jmin = 0
2712        mint = times[0]       
2713    else:
2714        jmin = searchsorted(times, mint)
2715       
2716    if maxt is None:
2717        jmax = len(times)
2718        maxt = times[-1]
2719    else:
2720        jmax = searchsorted(times, maxt)
2721
2722    #print "latitudes[:]",latitudes[:]
2723    #print "longitudes[:]",longitudes [:]
2724    kmin, kmax, lmin, lmax = _get_min_max_indexes(latitudes[:],
2725                                                  longitudes[:],
2726                                                  minlat, maxlat,
2727                                                  minlon, maxlon)
2728
2729
2730    times = times[jmin:jmax]
2731    latitudes = latitudes[kmin:kmax]
2732    longitudes = longitudes[lmin:lmax]
2733
2734    #print "latitudes[:]",latitudes[:]
2735    #print "longitudes[:]",longitudes [:]
2736
2737    if verbose: print 'cropping'
2738    zname = 'ELEVATION'
2739
2740    amplitudes = file_h.variables['HA'][jmin:jmax, kmin:kmax, lmin:lmax]
2741    uspeed = file_u.variables['UA'][jmin:jmax, kmin:kmax, lmin:lmax] #Lon
2742    vspeed = file_v.variables['VA'][jmin:jmax, kmin:kmax, lmin:lmax] #Lat
2743    elevations = file_e.variables[zname][kmin:kmax, lmin:lmax]
2744
2745    #    if latitudes2[0]==latitudes[0] and latitudes2[-1]==latitudes[-1]:
2746    #        elevations = file_e.variables['ELEVATION'][kmin:kmax, lmin:lmax]
2747    #    elif latitudes2[0]==latitudes[-1] and latitudes2[-1]==latitudes[0]:
2748    #        from Numeric import asarray
2749    #        elevations=elevations.tolist()
2750    #        elevations.reverse()
2751    #        elevations=asarray(elevations)
2752    #    else:
2753    #        from Numeric import asarray
2754    #        elevations=elevations.tolist()
2755    #        elevations.reverse()
2756    #        elevations=asarray(elevations)
2757    #        'print hmmm'
2758
2759
2760
2761    #Get missing values
2762    nan_ha = file_h.variables['HA'].missing_value[0]
2763    nan_ua = file_u.variables['UA'].missing_value[0]
2764    nan_va = file_v.variables['VA'].missing_value[0]
2765    if hasattr(file_e.variables[zname],'missing_value'):
2766        nan_e  = file_e.variables[zname].missing_value[0]
2767    else:
2768        nan_e = None
2769
2770    #Cleanup
2771    from Numeric import sometrue
2772
2773    missing = (amplitudes == nan_ha)
2774    if sometrue (missing):
2775        if fail_on_NaN:
2776            msg = 'NetCDFFile %s contains missing values'\
2777                  %(basename_in+'_ha.nc')
2778            raise DataMissingValuesError, msg
2779        else:
2780            amplitudes = amplitudes*(missing==0) + missing*NaN_filler
2781
2782    missing = (uspeed == nan_ua)
2783    if sometrue (missing):
2784        if fail_on_NaN:
2785            msg = 'NetCDFFile %s contains missing values'\
2786                  %(basename_in+'_ua.nc')
2787            raise DataMissingValuesError, msg
2788        else:
2789            uspeed = uspeed*(missing==0) + missing*NaN_filler
2790
2791    missing = (vspeed == nan_va)
2792    if sometrue (missing):
2793        if fail_on_NaN:
2794            msg = 'NetCDFFile %s contains missing values'\
2795                  %(basename_in+'_va.nc')
2796            raise DataMissingValuesError, msg
2797        else:
2798            vspeed = vspeed*(missing==0) + missing*NaN_filler
2799
2800
2801    missing = (elevations == nan_e)
2802    if sometrue (missing):
2803        if fail_on_NaN:
2804            msg = 'NetCDFFile %s contains missing values'\
2805                  %(basename_in+'_e.nc')
2806            raise DataMissingValuesError, msg
2807        else:
2808            elevations = elevations*(missing==0) + missing*NaN_filler
2809
2810    #######
2811
2812
2813
2814    number_of_times = times.shape[0]
2815    number_of_latitudes = latitudes.shape[0]
2816    number_of_longitudes = longitudes.shape[0]
2817
2818    assert amplitudes.shape[0] == number_of_times
2819    assert amplitudes.shape[1] == number_of_latitudes
2820    assert amplitudes.shape[2] == number_of_longitudes
2821
2822    if verbose:
2823        print '------------------------------------------------'
2824        print 'Statistics:'
2825        print '  Extent (lat/lon):'
2826        print '    lat in [%f, %f], len(lat) == %d'\
2827              %(min(latitudes.flat), max(latitudes.flat),
2828                len(latitudes.flat))
2829        print '    lon in [%f, %f], len(lon) == %d'\
2830              %(min(longitudes.flat), max(longitudes.flat),
2831                len(longitudes.flat))
2832        print '    t in [%f, %f], len(t) == %d'\
2833              %(min(times.flat), max(times.flat), len(times.flat))
2834
2835        q = amplitudes.flat
2836        name = 'Amplitudes (ha) [cm]'
2837        print %s in [%f, %f]' %(name, min(q), max(q))
2838
2839        q = uspeed.flat
2840        name = 'Speeds (ua) [cm/s]'
2841        print %s in [%f, %f]' %(name, min(q), max(q))
2842
2843        q = vspeed.flat
2844        name = 'Speeds (va) [cm/s]'
2845        print %s in [%f, %f]' %(name, min(q), max(q))
2846
2847        q = elevations.flat
2848        name = 'Elevations (e) [m]'
2849        print %s in [%f, %f]' %(name, min(q), max(q))
2850
2851
2852    # print number_of_latitudes, number_of_longitudes
2853    number_of_points = number_of_latitudes*number_of_longitudes
2854    number_of_volumes = (number_of_latitudes-1)*(number_of_longitudes-1)*2
2855
2856
2857    file_h.close()
2858    file_u.close()
2859    file_v.close()
2860    file_e.close()
2861
2862
2863    # NetCDF file definition
2864    outfile = NetCDFFile(swwname, 'w')
2865
2866    description = 'Converted from Ferret files: %s, %s, %s, %s'\
2867                  %(basename_in + '_ha.nc',
2868                    basename_in + '_ua.nc',
2869                    basename_in + '_va.nc',
2870                    basename_in + '_e.nc')
2871   
2872    # Create new file
2873    starttime = times[0]
2874    sww = Write_sww()
2875    sww.store_header(outfile, times, number_of_volumes,
2876                     number_of_points, description=description,
2877                     verbose=verbose)
2878
2879    # Store
2880    from anuga.coordinate_transforms.redfearn import redfearn
2881    x = zeros(number_of_points, Float)  #Easting
2882    y = zeros(number_of_points, Float)  #Northing
2883
2884
2885    if verbose: print 'Making triangular grid'
2886
2887    # Check zone boundaries
2888    refzone, _, _ = redfearn(latitudes[0],longitudes[0])
2889
2890    vertices = {}
2891    i = 0
2892    for k, lat in enumerate(latitudes):       #Y direction
2893        for l, lon in enumerate(longitudes):  #X direction
2894
2895            vertices[l,k] = i
2896
2897            zone, easting, northing = redfearn(lat,lon)
2898
2899            msg = 'Zone boundary crossed at longitude =', lon
2900            #assert zone == refzone, msg
2901            #print '%7.2f %7.2f %8.2f %8.2f' %(lon, lat, easting, northing)
2902            x[i] = easting
2903            y[i] = northing
2904            i += 1
2905
2906    #Construct 2 triangles per 'rectangular' element
2907    volumes = []
2908    for l in range(number_of_longitudes-1):    #X direction
2909        for k in range(number_of_latitudes-1): #Y direction
2910            v1 = vertices[l,k+1]
2911            v2 = vertices[l,k]
2912            v3 = vertices[l+1,k+1]
2913            v4 = vertices[l+1,k]
2914
2915            volumes.append([v1,v2,v3]) #Upper element
2916            volumes.append([v4,v3,v2]) #Lower element
2917
2918    volumes = array(volumes)
2919
2920    if origin is None:
2921        origin = Geo_reference(refzone,min(x),min(y))
2922    geo_ref = write_NetCDF_georeference(origin, outfile)
2923   
2924    if elevation is not None:
2925        z = elevation
2926    else:
2927        if inverted_bathymetry:
2928            z = -1*elevations
2929        else:
2930            z = elevations
2931    #FIXME: z should be obtained from MOST and passed in here
2932
2933    from Numeric import resize
2934    z = resize(z,outfile.variables['z'][:].shape)
2935    outfile.variables['x'][:] = x - geo_ref.get_xllcorner()
2936    outfile.variables['y'][:] = y - geo_ref.get_yllcorner()
2937    outfile.variables['z'][:] = z             #FIXME HACK for bacwards compat.
2938    outfile.variables['elevation'][:] = z
2939    outfile.variables['volumes'][:] = volumes.astype(Int32) #For Opteron 64
2940
2941
2942
2943    #Time stepping
2944    stage = outfile.variables['stage']
2945    xmomentum = outfile.variables['xmomentum']
2946    ymomentum = outfile.variables['ymomentum']
2947
2948    if verbose: print 'Converting quantities'
2949    n = len(times)
2950    for j in range(n):
2951        if verbose and j%((n+10)/10)==0: print '  Doing %d of %d' %(j, n)
2952        i = 0
2953        for k in range(number_of_latitudes):      #Y direction
2954            for l in range(number_of_longitudes): #X direction
2955                w = zscale*amplitudes[j,k,l]/100 + mean_stage
2956                stage[j,i] = w
2957                h = w - z[i]
2958                xmomentum[j,i] = uspeed[j,k,l]/100*h
2959                ymomentum[j,i] = vspeed[j,k,l]/100*h
2960                i += 1
2961
2962    #outfile.close()
2963
2964    #FIXME: Refactor using code from file_function.statistics
2965    #Something like print swwstats(swwname)
2966    if verbose:
2967        x = outfile.variables['x'][:]
2968        y = outfile.variables['y'][:]
2969        print '------------------------------------------------'
2970        print 'Statistics of output file:'
2971        print '  Name: %s' %swwname
2972        print '  Reference:'
2973        print '    Lower left corner: [%f, %f]'\
2974              %(geo_ref.get_xllcorner(), geo_ref.get_yllcorner())
2975        print ' Start time: %f' %starttime
2976        print '    Min time: %f' %mint
2977        print '    Max time: %f' %maxt
2978        print '  Extent:'
2979        print '    x [m] in [%f, %f], len(x) == %d'\
2980              %(min(x.flat), max(x.flat), len(x.flat))
2981        print '    y [m] in [%f, %f], len(y) == %d'\
2982              %(min(y.flat), max(y.flat), len(y.flat))
2983        print '    t [s] in [%f, %f], len(t) == %d'\
2984              %(min(times), max(times), len(times))
2985        print '  Quantities [SI units]:'
2986        for name in ['stage', 'xmomentum', 'ymomentum', 'elevation']:
2987            q = outfile.variables[name][:].flat
2988            print '    %s in [%f, %f]' %(name, min(q), max(q))
2989
2990
2991
2992    outfile.close()
2993
2994
2995
2996
2997
2998def timefile2netcdf(filename, quantity_names=None, time_as_seconds=False):
2999    """Template for converting typical text files with time series to
3000    NetCDF tms file.
3001
3002
3003    The file format is assumed to be either two fields separated by a comma:
3004
3005        time [DD/MM/YY hh:mm:ss], value0 value1 value2 ...
3006
3007    E.g
3008
3009      31/08/04 00:00:00, 1.328223 0 0
3010      31/08/04 00:15:00, 1.292912 0 0
3011
3012    or time (seconds), value0 value1 value2 ...
3013   
3014      0.0, 1.328223 0 0
3015      0.1, 1.292912 0 0
3016
3017    will provide a time dependent function f(t) with three attributes
3018
3019    filename is assumed to be the rootname with extenisons .txt and .sww
3020    """
3021
3022    import time, calendar
3023    from Numeric import array
3024    from anuga.config import time_format
3025    from anuga.utilities.numerical_tools import ensure_numeric
3026
3027
3028
3029    fid = open(filename + '.txt')
3030    line = fid.readline()
3031    fid.close()
3032
3033    fields = line.split(',')
3034    msg = 'File %s must have the format date, value0 value1 value2 ...'
3035    assert len(fields) == 2, msg
3036
3037    if not time_as_seconds:
3038        try:
3039            starttime = calendar.timegm(time.strptime(fields[0], time_format))
3040        except ValueError:
3041            msg = 'First field in file %s must be' %filename
3042            msg += ' date-time with format %s.\n' %time_format
3043            msg += 'I got %s instead.' %fields[0]
3044            raise DataTimeError, msg
3045    else:
3046        try:
3047            starttime = float(fields[0])
3048        except Error:
3049            msg = "Bad time format"
3050            raise DataTimeError, msg
3051
3052
3053    #Split values
3054    values = []
3055    for value in fields[1].split():
3056        values.append(float(value))
3057
3058    q = ensure_numeric(values)
3059
3060    msg = 'ERROR: File must contain at least one independent value'
3061    assert len(q.shape) == 1, msg
3062
3063
3064
3065    #Read times proper
3066    from Numeric import zeros, Float, alltrue
3067    from anuga.config import time_format
3068    import time, calendar
3069
3070    fid = open(filename + '.txt')
3071    lines = fid.readlines()
3072    fid.close()
3073
3074    N = len(lines)
3075    d = len(q)
3076
3077    T = zeros(N, Float)       #Time
3078    Q = zeros((N, d), Float)  #Values
3079
3080    for i, line in enumerate(lines):
3081        fields = line.split(',')
3082        if not time_as_seconds:
3083            realtime = calendar.timegm(time.strptime(fields[0], time_format))
3084        else:
3085             realtime = float(fields[0])
3086        T[i] = realtime - starttime
3087
3088        for j, value in enumerate(fields[1].split()):
3089            Q[i, j] = float(value)
3090
3091    msg = 'File %s must list time as a monotonuosly ' %filename
3092    msg += 'increasing sequence'
3093    assert alltrue( T[1:] - T[:-1] > 0 ), msg
3094
3095    #Create NetCDF file
3096    from Scientific.IO.NetCDF import NetCDFFile
3097
3098    fid = NetCDFFile(filename + '.tms', 'w')
3099
3100
3101    fid.institution = 'Geoscience Australia'
3102    fid.description = 'Time series'
3103
3104
3105    #Reference point
3106    #Start time in seconds since the epoch (midnight 1/1/1970)
3107    #FIXME: Use Georef
3108    fid.starttime = starttime
3109
3110    # dimension definitions
3111    #fid.createDimension('number_of_volumes', self.number_of_volumes)
3112    #fid.createDimension('number_of_vertices', 3)
3113
3114
3115    fid.createDimension('number_of_timesteps', len(T))
3116
3117    fid.createVariable('time', Float, ('number_of_timesteps',))
3118
3119    fid.variables['time'][:] = T
3120
3121    for i in range(Q.shape[1]):
3122        try:
3123            name = quantity_names[i]
3124        except:
3125            name = 'Attribute%d'%i
3126
3127        fid.createVariable(name, Float, ('number_of_timesteps',))
3128        fid.variables[name][:] = Q[:,i]
3129
3130    fid.close()
3131
3132
3133def extent_sww(file_name):
3134    """
3135    Read in an sww file.
3136
3137    Input;
3138    file_name - the sww file
3139
3140    Output;
3141    z - Vector of bed elevation
3142    volumes - Array.  Each row has 3 values, representing
3143    the vertices that define the volume
3144    time - Vector of the times where there is stage information
3145    stage - array with respect to time and vertices (x,y)
3146    """
3147
3148
3149    from Scientific.IO.NetCDF import NetCDFFile
3150
3151    #Check contents
3152    #Get NetCDF
3153    fid = NetCDFFile(file_name, 'r')
3154
3155    # Get the variables
3156    x = fid.variables['x'][:]
3157    y = fid.variables['y'][:]
3158    stage = fid.variables['stage'][:]
3159    #print "stage",stage
3160    #print "stage.shap",stage.shape
3161    #print "min(stage.flat), mpythonax(stage.flat)",min(stage.flat), max(stage.flat)
3162    #print "min(stage)",min(stage)
3163
3164    fid.close()
3165
3166    return [min(x),max(x),min(y),max(y),min(stage.flat),max(stage.flat)]
3167
3168
3169def sww2domain(filename,boundary=None,t=None,\
3170               fail_if_NaN=True,NaN_filler=0\
3171               ,verbose = False,very_verbose = False):
3172    """
3173    Usage: domain = sww2domain('file.sww',t=time (default = last time in file))
3174
3175    Boundary is not recommended if domain.smooth is not selected, as it
3176    uses unique coordinates, but not unique boundaries. This means that
3177    the boundary file will not be compatable with the coordinates, and will
3178    give a different final boundary, or crash.
3179    """
3180    NaN=9.969209968386869e+036
3181    #initialise NaN.
3182
3183    from Scientific.IO.NetCDF import NetCDFFile
3184    from shallow_water import Domain
3185    from Numeric import asarray, transpose, resize
3186
3187    if verbose: print 'Reading from ', filename
3188    fid = NetCDFFile(filename, 'r')    #Open existing file for read
3189    time = fid.variables['time']       #Timesteps
3190    if t is None:
3191        t = time[-1]
3192    time_interp = get_time_interp(time,t)
3193
3194    # Get the variables as Numeric arrays
3195    x = fid.variables['x'][:]             #x-coordinates of vertices
3196    y = fid.variables['y'][:]             #y-coordinates of vertices
3197    elevation = fid.variables['elevation']     #Elevation
3198    stage = fid.variables['stage']     #Water level
3199    xmomentum = fid.variables['xmomentum']   #Momentum in the x-direction
3200    ymomentum = fid.variables['ymomentum']   #Momentum in the y-direction
3201
3202    starttime = fid.starttime[0]
3203    volumes = fid.variables['volumes'][:] #Connectivity
3204    coordinates=transpose(asarray([x.tolist(),y.tolist()]))
3205
3206    conserved_quantities = []
3207    interpolated_quantities = {}
3208    other_quantities = []
3209
3210    # get geo_reference
3211    #sww files don't have to have a geo_ref
3212    try:
3213        geo_reference = Geo_reference(NetCDFObject=fid)
3214    except: #AttributeError, e:
3215        geo_reference = None
3216
3217    if verbose: print '    getting quantities'
3218    for quantity in fid.variables.keys():
3219        dimensions = fid.variables[quantity].dimensions
3220        if 'number_of_timesteps' in dimensions:
3221            conserved_quantities.append(quantity)
3222            interpolated_quantities[quantity]=\
3223                  interpolated_quantity(fid.variables[quantity][:],time_interp)
3224        else: other_quantities.append(quantity)
3225
3226    other_quantities.remove('x')
3227    other_quantities.remove('y')
3228    other_quantities.remove('z')
3229    other_quantities.remove('volumes')
3230    try:
3231        other_quantities.remove('stage_range')
3232        other_quantities.remove('xmomentum_range')
3233        other_quantities.remove('ymomentum_range')
3234        other_quantities.remove('elevation_range')
3235    except:
3236        pass
3237       
3238
3239    conserved_quantities.remove('time')
3240
3241    if verbose: print '    building domain'
3242    #    From domain.Domain:
3243    #    domain = Domain(coordinates, volumes,\
3244    #                    conserved_quantities = conserved_quantities,\
3245    #                    other_quantities = other_quantities,zone=zone,\
3246    #                    xllcorner=xllcorner, yllcorner=yllcorner)
3247
3248    #   From shallow_water.Domain:
3249    coordinates=coordinates.tolist()
3250    volumes=volumes.tolist()
3251    #FIXME:should this be in mesh?(peter row)
3252    if fid.smoothing == 'Yes': unique = False
3253    else: unique = True
3254    if unique:
3255        coordinates,volumes,boundary=weed(coordinates,volumes,boundary)
3256
3257
3258    try:
3259        domain = Domain(coordinates, volumes, boundary)
3260    except AssertionError, e:
3261        fid.close()
3262        msg = 'Domain could not be created: %s. Perhaps use "fail_if_NaN=False and NaN_filler = ..."' %e
3263        raise DataDomainError, msg
3264
3265    if not boundary is None:
3266        domain.boundary = boundary
3267
3268    domain.geo_reference = geo_reference
3269
3270    domain.starttime=float(starttime)+float(t)
3271    domain.time=0.0
3272
3273    for quantity in other_quantities:
3274        try:
3275            NaN = fid.variables[quantity].missing_value
3276        except:
3277            pass #quantity has no missing_value number
3278        X = fid.variables[quantity][:]
3279        if very_verbose:
3280            print '       ',quantity
3281            print '        NaN =',NaN
3282            print '        max(X)'
3283            print '       ',max(X)
3284            print '        max(X)==NaN'
3285            print '       ',max(X)==NaN
3286            print ''
3287        if (max(X)==NaN) or (min(X)==NaN):
3288            if fail_if_NaN:
3289                msg = 'quantity "%s" contains no_data entry'%quantity
3290                raise DataMissingValuesError, msg
3291            else:
3292                data = (X<>NaN)
3293                X = (X*data)+(data==0)*NaN_filler
3294        if unique:
3295            X = resize(X,(len(X)/3,3))
3296        domain.set_quantity(quantity,X)
3297    #
3298    for quantity in conserved_quantities:
3299        try:
3300            NaN = fid.variables[quantity].missing_value
3301        except:
3302            pass #quantity has no missing_value number
3303        X = interpolated_quantities[quantity]
3304        if very_verbose:
3305            print '       ',quantity
3306            print '        NaN =',NaN
3307            print '        max(X)'
3308            print '       ',max(X)
3309            print '        max(X)==NaN'
3310            print '       ',max(X)==NaN
3311            print ''
3312        if (max(X)==NaN) or (min(X)==NaN):
3313            if fail_if_NaN:
3314                msg = 'quantity "%s" contains no_data entry'%quantity
3315                raise DataMissingValuesError, msg
3316            else:
3317                data = (X<>NaN)
3318                X = (X*data)+(data==0)*NaN_filler
3319        if unique:
3320            X = resize(X,(X.shape[0]/3,3))
3321        domain.set_quantity(quantity,X)
3322
3323    fid.close()
3324    return domain
3325
3326def interpolated_quantity(saved_quantity,time_interp):
3327
3328    #given an index and ratio, interpolate quantity with respect to time.
3329    index,ratio = time_interp
3330    Q = saved_quantity
3331    if ratio > 0:
3332        q = (1-ratio)*Q[index]+ ratio*Q[index+1]
3333    else:
3334        q = Q[index]
3335    #Return vector of interpolated values
3336    return q
3337
3338def get_time_interp(time,t=None):
3339    #Finds the ratio and index for time interpolation.
3340    #It is borrowed from previous abstract_2d_finite_volumes code.
3341    if t is None:
3342        t=time[-1]
3343        index = -1
3344        ratio = 0.
3345    else:
3346        T = time
3347        tau = t
3348        index=0
3349        msg = 'Time interval derived from file %s [%s:%s]'\
3350            %('FIXMEfilename', T[0], T[-1])
3351        msg += ' does not match model time: %s' %tau
3352        if tau < time[0]: raise DataTimeError, msg
3353        if tau > time[-1]: raise DataTimeError, msg
3354        while tau > time[index]: index += 1
3355        while tau < time[index]: index -= 1
3356        if tau == time[index]:
3357            #Protect against case where tau == time[-1] (last time)
3358            # - also works in general when tau == time[i]
3359            ratio = 0
3360        else:
3361            #t is now between index and index+1
3362            ratio = (tau - time[index])/(time[index+1] - time[index])
3363    return (index,ratio)
3364
3365
3366def weed(coordinates,volumes,boundary = None):
3367    if type(coordinates)==ArrayType:
3368        coordinates = coordinates.tolist()
3369    if type(volumes)==ArrayType:
3370        volumes = volumes.tolist()
3371
3372    unique = False
3373    point_dict = {}
3374    same_point = {}
3375    for i in range(len(coordinates)):
3376        point = tuple(coordinates[i])
3377        if point_dict.has_key(point):
3378            unique = True
3379            same_point[i]=point
3380            #to change all point i references to point j
3381        else:
3382            point_dict[point]=i
3383            same_point[i]=point
3384
3385    coordinates = []
3386    i = 0
3387    for point in point_dict.keys():
3388        point = tuple(point)
3389        coordinates.append(list(point))
3390        point_dict[point]=i
3391        i+=1
3392
3393
3394    for volume in volumes:
3395        for i in range(len(volume)):
3396            index = volume[i]
3397            if index>-1:
3398                volume[i]=point_dict[same_point[index]]
3399
3400    new_boundary = {}
3401    if not boundary is None:
3402        for segment in boundary.keys():
3403            point0 = point_dict[same_point[segment[0]]]
3404            point1 = point_dict[same_point[segment[1]]]
3405            label = boundary[segment]
3406            #FIXME should the bounday attributes be concaterated
3407            #('exterior, pond') or replaced ('pond')(peter row)
3408
3409            if new_boundary.has_key((point0,point1)):
3410                new_boundary[(point0,point1)]=new_boundary[(point0,point1)]#\
3411                                              #+','+label
3412
3413            elif new_boundary.has_key((point1,point0)):
3414                new_boundary[(point1,point0)]=new_boundary[(point1,point0)]#\
3415                                              #+','+label
3416            else: new_boundary[(point0,point1)]=label
3417
3418        boundary = new_boundary
3419
3420    return coordinates,volumes,boundary
3421
3422
3423def decimate_dem(basename_in, stencil, cellsize_new, basename_out=None,
3424                 verbose=False):
3425    """Read Digitial Elevation model from the following NetCDF format (.dem)
3426
3427    Example:
3428
3429    ncols         3121
3430    nrows         1800
3431    xllcorner     722000
3432    yllcorner     5893000
3433    cellsize      25
3434    NODATA_value  -9999
3435    138.3698 137.4194 136.5062 135.5558 ..........
3436
3437    Decimate data to cellsize_new using stencil and write to NetCDF dem format.
3438    """
3439
3440    import os
3441    from Scientific.IO.NetCDF import NetCDFFile
3442    from Numeric import Float, zeros, sum, reshape, equal
3443
3444    root = basename_in
3445    inname = root + '.dem'
3446
3447    #Open existing netcdf file to read
3448    infile = NetCDFFile(inname, 'r')
3449    if verbose: print 'Reading DEM from %s' %inname
3450
3451    #Read metadata
3452    ncols = infile.ncols[0]
3453    nrows = infile.nrows[0]
3454    xllcorner = infile.xllcorner[0]
3455    yllcorner = infile.yllcorner[0]
3456    cellsize = infile.cellsize[0]
3457    NODATA_value = infile.NODATA_value[0]
3458    zone = infile.zone[0]
3459    false_easting = infile.false_easting[0]
3460    false_northing = infile.false_northing[0]
3461    projection = infile.projection
3462    datum = infile.datum
3463    units = infile.units
3464
3465    dem_elevation = infile.variables['elevation']
3466
3467    #Get output file name
3468    if basename_out == None:
3469        outname = root + '_' + repr(cellsize_new) + '.dem'
3470    else:
3471        outname = basename_out + '.dem'
3472
3473    if verbose: print 'Write decimated NetCDF file to %s' %outname
3474
3475    #Determine some dimensions for decimated grid
3476    (nrows_stencil, ncols_stencil) = stencil.shape
3477    x_offset = ncols_stencil / 2
3478    y_offset = nrows_stencil / 2
3479    cellsize_ratio = int(cellsize_new / cellsize)
3480    ncols_new = 1 + (ncols - ncols_stencil) / cellsize_ratio
3481    nrows_new = 1 + (nrows - nrows_stencil) / cellsize_ratio
3482
3483    #Open netcdf file for output
3484    outfile = NetCDFFile(outname, 'w')
3485
3486    #Create new file
3487    outfile.institution = 'Geoscience Australia'
3488    outfile.description = 'NetCDF DEM format for compact and portable storage ' +\
3489                           'of spatial point data'
3490    #Georeferencing
3491    outfile.zone = zone
3492    outfile.projection = projection
3493    outfile.datum = datum
3494    outfile.units = units
3495
3496    outfile.cellsize = cellsize_new
3497    outfile.NODATA_value = NODATA_value
3498    outfile.false_easting = false_easting
3499    outfile.false_northing = false_northing
3500
3501    outfile.xllcorner = xllcorner + (x_offset * cellsize)
3502    outfile.yllcorner = yllcorner + (y_offset * cellsize)
3503    outfile.ncols = ncols_new
3504    outfile.nrows = nrows_new
3505
3506    # dimension definition
3507    outfile.createDimension('number_of_points', nrows_new*ncols_new)
3508
3509    # variable definition
3510    outfile.createVariable('elevation', Float, ('number_of_points',))
3511
3512    # Get handle to the variable
3513    elevation = outfile.variables['elevation']
3514
3515    dem_elevation_r = reshape(dem_elevation, (nrows, ncols))
3516
3517    #Store data
3518    global_index = 0
3519    for i in range(nrows_new):
3520        if verbose: print 'Processing row %d of %d' %(i, nrows_new)
3521        lower_index = global_index
3522        telev =  zeros(ncols_new, Float)
3523        local_index = 0
3524        trow = i * cellsize_ratio
3525
3526        for j in range(ncols_new):
3527            tcol = j * cellsize_ratio
3528            tmp = dem_elevation_r[trow:trow+nrows_stencil, tcol:tcol+ncols_stencil]
3529
3530            #if dem contains 1 or more NODATA_values set value in
3531            #decimated dem to NODATA_value, else compute decimated
3532            #value using stencil
3533            if sum(sum(equal(tmp, NODATA_value))) > 0:
3534                telev[local_index] = NODATA_value
3535            else:
3536                telev[local_index] = sum(sum(tmp * stencil))
3537
3538            global_index += 1
3539            local_index += 1
3540
3541        upper_index = global_index
3542
3543        elevation[lower_index:upper_index] = telev
3544
3545    assert global_index == nrows_new*ncols_new, 'index not equal to number of points'
3546
3547    infile.close()
3548    outfile.close()
3549
3550
3551
3552
3553def tsh2sww(filename, verbose=False): #test_tsh2sww
3554    """
3555    to check if a tsh/msh file 'looks' good.
3556    """
3557
3558
3559    if verbose == True:print 'Creating domain from', filename
3560    domain = pmesh_to_domain_instance(filename, Domain)
3561    if verbose == True:print "Number of triangles = ", len(domain)
3562
3563    domain.smooth = True
3564    domain.format = 'sww'   #Native netcdf visualisation format
3565    file_path, filename = path.split(filename)
3566    filename, ext = path.splitext(filename)
3567    domain.set_name(filename)   
3568    domain.reduction = mean
3569    if verbose == True:print "file_path",file_path
3570    if file_path == "":file_path = "."
3571    domain.set_datadir(file_path)
3572
3573    if verbose == True:
3574        print "Output written to " + domain.get_datadir() + sep + \
3575              domain.get_name() + "." + domain.format
3576    sww = get_dataobject(domain)
3577    sww.store_connectivity()
3578    sww.store_timestep('stage')
3579
3580
3581def asc_csiro2sww(bath_dir,
3582                  elevation_dir,
3583                  ucur_dir,
3584                  vcur_dir,
3585                  sww_file,
3586                  minlat = None, maxlat = None,
3587                  minlon = None, maxlon = None,
3588                  zscale=1,
3589                  mean_stage = 0,
3590                  fail_on_NaN = True,
3591                  elevation_NaN_filler = 0,
3592                  bath_prefix='ba',
3593                  elevation_prefix='el',
3594                  verbose=False):
3595    """
3596    Produce an sww boundary file, from esri ascii data from CSIRO.
3597
3598    Also convert latitude and longitude to UTM. All coordinates are
3599    assumed to be given in the GDA94 datum.
3600
3601    assume:
3602    All files are in esri ascii format
3603
3604    4 types of information
3605    bathymetry
3606    elevation
3607    u velocity
3608    v velocity
3609
3610    Assumptions
3611    The metadata of all the files is the same
3612    Each type is in a seperate directory
3613    One bath file with extention .000
3614    The time period is less than 24hrs and uniform.
3615    """
3616    from Scientific.IO.NetCDF import NetCDFFile
3617
3618    from anuga.coordinate_transforms.redfearn import redfearn
3619
3620    precision = Float # So if we want to change the precision its done here
3621
3622    # go in to the bath dir and load the only file,
3623    bath_files = os.listdir(bath_dir)
3624
3625    bath_file = bath_files[0]
3626    bath_dir_file =  bath_dir + os.sep + bath_file
3627    bath_metadata,bath_grid =  _read_asc(bath_dir_file)
3628
3629    #Use the date.time of the bath file as a basis for
3630    #the start time for other files
3631    base_start = bath_file[-12:]
3632
3633    #go into the elevation dir and load the 000 file
3634    elevation_dir_file = elevation_dir  + os.sep + elevation_prefix \
3635                         + base_start
3636
3637    elevation_files = os.listdir(elevation_dir)
3638    ucur_files = os.listdir(ucur_dir)
3639    vcur_files = os.listdir(vcur_dir)
3640    elevation_files.sort()
3641    # the first elevation file should be the
3642    # file with the same base name as the bath data
3643    assert elevation_files[0] == 'el' + base_start
3644
3645    number_of_latitudes = bath_grid.shape[0]
3646    number_of_longitudes = bath_grid.shape[1]
3647    number_of_volumes = (number_of_latitudes-1)*(number_of_longitudes-1)*2
3648
3649    longitudes = [bath_metadata['xllcorner']+x*bath_metadata['cellsize'] \
3650                  for x in range(number_of_longitudes)]
3651    latitudes = [bath_metadata['yllcorner']+y*bath_metadata['cellsize'] \
3652                 for y in range(number_of_latitudes)]
3653
3654     # reverse order of lat, so the fist lat represents the first grid row
3655    latitudes.reverse()
3656
3657    kmin, kmax, lmin, lmax = _get_min_max_indexes(latitudes[:],longitudes[:],
3658                                                 minlat=minlat, maxlat=maxlat,
3659                                                 minlon=minlon, maxlon=maxlon)
3660
3661
3662    bath_grid = bath_grid[kmin:kmax,lmin:lmax]
3663    latitudes = latitudes[kmin:kmax]
3664    longitudes = longitudes[lmin:lmax]
3665    number_of_latitudes = len(latitudes)
3666    number_of_longitudes = len(longitudes)
3667    number_of_times = len(os.listdir(elevation_dir))
3668    number_of_points = number_of_latitudes*number_of_longitudes
3669    number_of_volumes = (number_of_latitudes-1)*(number_of_longitudes-1)*2
3670
3671    #Work out the times
3672    if len(elevation_files) > 1:
3673        # Assume: The time period is less than 24hrs.
3674        time_period = (int(elevation_files[1][-3:]) - \
3675                      int(elevation_files[0][-3:]))*60*60
3676        times = [x*time_period for x in range(len(elevation_files))]
3677    else:
3678        times = [0.0]
3679
3680
3681    if verbose:
3682        print '------------------------------------------------'
3683        print 'Statistics:'
3684        print '  Extent (lat/lon):'
3685        print '    lat in [%f, %f], len(lat) == %d'\
3686              %(min(latitudes), max(latitudes),
3687                len(latitudes))
3688        print '    lon in [%f, %f], len(lon) == %d'\
3689              %(min(longitudes), max(longitudes),
3690                len(longitudes))
3691        print '    t in [%f, %f], len(t) == %d'\
3692              %(min(times), max(times), len(times))
3693
3694    ######### WRITE THE SWW FILE #############
3695    # NetCDF file definition
3696    outfile = NetCDFFile(sww_file, 'w')
3697
3698    #Create new file
3699    outfile.institution = 'Geoscience Australia'
3700    outfile.description = 'Converted from XXX'
3701
3702
3703    #For sww compatibility
3704    outfile.smoothing = 'Yes'
3705    outfile.order = 1
3706
3707    #Start time in seconds since the epoch (midnight 1/1/1970)
3708    outfile.starttime = starttime = times[0]
3709
3710
3711    # dimension definitions
3712    outfile.createDimension('number_of_volumes', number_of_volumes)
3713
3714    outfile.createDimension('number_of_vertices', 3)
3715    outfile.createDimension('number_of_points', number_of_points)
3716    outfile.createDimension('number_of_timesteps', number_of_times)
3717
3718    # variable definitions
3719    outfile.createVariable('x', precision, ('number_of_points',))
3720    outfile.createVariable('y', precision, ('number_of_points',))
3721    outfile.createVariable('elevation', precision, ('number_of_points',))
3722
3723    #FIXME: Backwards compatibility
3724    outfile.createVariable('z', precision, ('number_of_points',))
3725    #################################
3726
3727    outfile.createVariable('volumes', Int, ('number_of_volumes',
3728                                            'number_of_vertices'))
3729
3730    outfile.createVariable('time', precision,
3731                           ('number_of_timesteps',))
3732
3733    outfile.createVariable('stage', precision,
3734                           ('number_of_timesteps',
3735                            'number_of_points'))
3736
3737    outfile.createVariable('xmomentum', precision,
3738                           ('number_of_timesteps',
3739                            'number_of_points'))
3740
3741    outfile.createVariable('ymomentum', precision,
3742                           ('number_of_timesteps',
3743                            'number_of_points'))
3744
3745    #Store
3746    from anuga.coordinate_transforms.redfearn import redfearn
3747    x = zeros(number_of_points, Float)  #Easting
3748    y = zeros(number_of_points, Float)  #Northing
3749
3750    if verbose: print 'Making triangular grid'
3751    #Get zone of 1st point.
3752    refzone, _, _ = redfearn(latitudes[0],longitudes[0])
3753
3754    vertices = {}
3755    i = 0
3756    for k, lat in enumerate(latitudes):
3757        for l, lon in enumerate(longitudes):
3758
3759            vertices[l,k] = i
3760
3761            zone, easting, northing = redfearn(lat,lon)
3762
3763            msg = 'Zone boundary crossed at longitude =', lon
3764            #assert zone == refzone, msg
3765            #print '%7.2f %7.2f %8.2f %8.2f' %(lon, lat, easting, northing)
3766            x[i] = easting
3767            y[i] = northing
3768            i += 1
3769
3770
3771    #Construct 2 triangles per 'rectangular' element
3772    volumes = []
3773    for l in range(number_of_longitudes-1):    #X direction
3774        for k in range(number_of_latitudes-1): #Y direction
3775            v1 = vertices[l,k+1]
3776            v2 = vertices[l,k]
3777            v3 = vertices[l+1,k+1]
3778            v4 = vertices[l+1,k]
3779
3780            #Note, this is different to the ferrit2sww code
3781            #since the order of the lats is reversed.
3782            volumes.append([v1,v3,v2]) #Upper element
3783            volumes.append([v4,v2,v3]) #Lower element
3784
3785    volumes = array(volumes)
3786
3787    geo_ref = Geo_reference(refzone,min(x),min(y))
3788    geo_ref.write_NetCDF(outfile)
3789
3790    # This will put the geo ref in the middle
3791    #geo_ref = Geo_reference(refzone,(max(x)+min(x))/2.0,(max(x)+min(y))/2.)
3792
3793
3794    if verbose:
3795        print '------------------------------------------------'
3796        print 'More Statistics:'
3797        print '  Extent (/lon):'
3798        print '    x in [%f, %f], len(lat) == %d'\
3799              %(min(x), max(x),
3800                len(x))
3801        print '    y in [%f, %f], len(lon) == %d'\
3802              %(min(y), max(y),
3803                len(y))
3804        print 'geo_ref: ',geo_ref
3805
3806    z = resize(bath_grid,outfile.variables['z'][:].shape)
3807    outfile.variables['x'][:] = x - geo_ref.get_xllcorner()
3808    outfile.variables['y'][:] = y - geo_ref.get_yllcorner()
3809    outfile.variables['z'][:] = z
3810    outfile.variables['elevation'][:] = z  #FIXME HACK
3811    outfile.variables['volumes'][:] = volumes.astype(Int32) #On Opteron 64
3812
3813    stage = outfile.variables['stage']
3814    xmomentum = outfile.variables['xmomentum']
3815    ymomentum = outfile.variables['ymomentum']
3816
3817    outfile.variables['time'][:] = times   #Store time relative
3818
3819    if verbose: print 'Converting quantities'
3820    n = number_of_times
3821    for j in range(number_of_times):
3822        # load in files
3823        elevation_meta, elevation_grid = \
3824           _read_asc(elevation_dir + os.sep + elevation_files[j])
3825
3826        _, u_momentum_grid =  _read_asc(ucur_dir + os.sep + ucur_files[j])
3827        _, v_momentum_grid =  _read_asc(vcur_dir + os.sep + vcur_files[j])
3828
3829        #cut matrix to desired size
3830        elevation_grid = elevation_grid[kmin:kmax,lmin:lmax]
3831        u_momentum_grid = u_momentum_grid[kmin:kmax,lmin:lmax]
3832        v_momentum_grid = v_momentum_grid[kmin:kmax,lmin:lmax]
3833       
3834        # handle missing values
3835        missing = (elevation_grid == elevation_meta['NODATA_value'])
3836        if sometrue (missing):
3837            if fail_on_NaN:
3838                msg = 'File %s contains missing values'\
3839                      %(elevation_files[j])
3840                raise DataMissingValuesError, msg
3841            else:
3842                elevation_grid = elevation_grid*(missing==0) + \
3843                                 missing*elevation_NaN_filler
3844
3845
3846        if verbose and j%((n+10)/10)==0: print '  Doing %d of %d' %(j, n)
3847        i = 0
3848        for k in range(number_of_latitudes):      #Y direction
3849            for l in range(number_of_longitudes): #X direction
3850                w = zscale*elevation_grid[k,l] + mean_stage
3851                stage[j,i] = w
3852                h = w - z[i]
3853                xmomentum[j,i] = u_momentum_grid[k,l]*h
3854                ymomentum[j,i] = v_momentum_grid[k,l]*h
3855                i += 1
3856    outfile.close()
3857
3858def _get_min_max_indexes(latitudes_ref,longitudes_ref,
3859                        minlat=None, maxlat=None,
3860                        minlon=None, maxlon=None):
3861    """
3862    return max, min indexes (for slicing) of the lat and long arrays to cover the area
3863    specified with min/max lat/long
3864
3865    Think of the latitudes and longitudes describing a 2d surface.
3866    The area returned is, if possible, just big enough to cover the
3867    inputed max/min area. (This will not be possible if the max/min area
3868    has a section outside of the latitudes/longitudes area.)
3869
3870    asset  longitudes are sorted,
3871    long - from low to high (west to east, eg 148 - 151)
3872    assert latitudes are sorted, ascending or decending
3873    """
3874    latitudes = latitudes_ref[:]
3875    longitudes = longitudes_ref[:]
3876
3877    latitudes = ensure_numeric(latitudes)
3878    longitudes = ensure_numeric(longitudes)
3879   
3880    assert allclose(sort(longitudes), longitudes)
3881   
3882    lat_ascending = True
3883    if not allclose(sort(latitudes), latitudes):
3884        lat_ascending = False
3885        # reverse order of lat, so it's in ascending order         
3886        latitudes = latitudes[::-1]
3887        assert allclose(sort(latitudes), latitudes)
3888    #print "latitudes  in funct", latitudes
3889   
3890    largest_lat_index = len(latitudes)-1
3891    #Cut out a smaller extent.
3892    if minlat == None:
3893        lat_min_index = 0
3894    else:
3895        lat_min_index = searchsorted(latitudes, minlat)-1
3896        if lat_min_index <0:
3897            lat_min_index = 0
3898
3899
3900    if maxlat == None:
3901        lat_max_index = largest_lat_index #len(latitudes)
3902    else:
3903        lat_max_index = searchsorted(latitudes, maxlat)
3904        if lat_max_index > largest_lat_index:
3905            lat_max_index = largest_lat_index
3906
3907    if minlon == None:
3908        lon_min_index = 0
3909    else:
3910        lon_min_index = searchsorted(longitudes, minlon)-1
3911        if lon_min_index <0:
3912            lon_min_index = 0
3913
3914    if maxlon == None:
3915        lon_max_index = len(longitudes)
3916    else:
3917        lon_max_index = searchsorted(longitudes, maxlon)
3918
3919    # Reversing the indexes, if the lat array is decending
3920    if lat_ascending is False:
3921        lat_min_index, lat_max_index = largest_lat_index - lat_max_index , \
3922                                       largest_lat_index - lat_min_index
3923    lat_max_index = lat_max_index + 1 # taking into account how slicing works
3924    lon_max_index = lon_max_index + 1 # taking into account how slicing works
3925
3926    return lat_min_index, lat_max_index, lon_min_index, lon_max_index
3927
3928
3929def _read_asc(filename, verbose=False):
3930    """Read esri file from the following ASCII format (.asc)
3931
3932    Example:
3933
3934    ncols         3121
3935    nrows         1800
3936    xllcorner     722000
3937    yllcorner     5893000
3938    cellsize      25
3939    NODATA_value  -9999
3940    138.3698 137.4194 136.5062 135.5558 ..........
3941
3942    """
3943
3944    datafile = open(filename)
3945
3946    if verbose: print 'Reading DEM from %s' %(filename)
3947    lines = datafile.readlines()
3948    datafile.close()
3949
3950    if verbose: print 'Got', len(lines), ' lines'
3951
3952    ncols = int(lines.pop(0).split()[1].strip())
3953    nrows = int(lines.pop(0).split()[1].strip())
3954    xllcorner = float(lines.pop(0).split()[1].strip())
3955    yllcorner = float(lines.pop(0).split()[1].strip())
3956    cellsize = float(lines.pop(0).split()[1].strip())
3957    NODATA_value = float(lines.pop(0).split()[1].strip())
3958
3959    assert len(lines) == nrows
3960
3961    #Store data
3962    grid = []
3963
3964    n = len(lines)
3965    for i, line in enumerate(lines):
3966        cells = line.split()
3967        assert len(cells) == ncols
3968        grid.append(array([float(x) for x in cells]))
3969    grid = array(grid)
3970
3971    return {'xllcorner':xllcorner,
3972            'yllcorner':yllcorner,
3973            'cellsize':cellsize,
3974            'NODATA_value':NODATA_value}, grid
3975
3976
3977
3978    ####  URS 2 SWW  ###
3979
3980lon_name = 'LON'
3981lat_name = 'LAT'
3982time_name = 'TIME'
3983precision = Float # So if we want to change the precision its done here       
3984class Write_nc:
3985    """
3986    Write an nc file.
3987   
3988    Note, this should be checked to meet cdc netcdf conventions for gridded
3989    data. http://www.cdc.noaa.gov/cdc/conventions/cdc_netcdf_standard.shtml
3990   
3991    """
3992    def __init__(self,
3993                 quantity_name,
3994                 file_name,
3995                 time_step_count,
3996                 time_step,
3997                 lon,
3998                 lat):
3999        """
4000        time_step_count is the number of time steps.
4001        time_step is the time step size
4002       
4003        pre-condition: quantity_name must be 'HA' 'UA'or 'VA'.
4004        """
4005        self.quantity_name = quantity_name
4006        quantity_units = {'HA':'CENTIMETERS',
4007                              'UA':'CENTIMETERS/SECOND',
4008                              'VA':'CENTIMETERS/SECOND'}       
4009       
4010        multiplier_dic = {'HA':100.0, # To convert from m to cm
4011                              'UA':100.0,  #  m/s to cm/sec
4012                              'VA':-100.0}  # MUX files have positve x in the
4013        # Southern direction.  This corrects for it, when writing nc files.
4014       
4015        self.quantity_multiplier =  multiplier_dic[self.quantity_name]
4016       
4017        #self.file_name = file_name
4018        self.time_step_count = time_step_count
4019        self.time_step = time_step
4020
4021        # NetCDF file definition
4022        self.outfile = NetCDFFile(file_name, 'w')
4023        outfile = self.outfile       
4024
4025        #Create new file
4026        nc_lon_lat_header(outfile, lon, lat)
4027   
4028        # TIME
4029        outfile.createDimension(time_name, None)
4030        outfile.createVariable(time_name, precision, (time_name,))
4031
4032        #QUANTITY
4033        outfile.createVariable(self.quantity_name, precision,
4034                               (time_name, lat_name, lon_name))
4035        outfile.variables[self.quantity_name].missing_value=-1.e+034
4036        outfile.variables[self.quantity_name].units= \
4037                                 quantity_units[self.quantity_name]
4038        outfile.variables[lon_name][:]= ensure_numeric(lon)
4039        outfile.variables[lat_name][:]= ensure_numeric(lat)
4040
4041        #Assume no one will be wanting to read this, while we are writing
4042        #outfile.close()
4043       
4044    def store_timestep(self, quantity_slice):
4045        """
4046        Write a time slice of quantity info
4047        quantity_slice is the data to be stored at this time step
4048        """
4049       
4050        outfile = self.outfile
4051       
4052        # Get the variables
4053        time = outfile.variables[time_name]
4054        quantity = outfile.variables[self.quantity_name]
4055           
4056        i = len(time)
4057
4058        #Store time
4059        time[i] = i*self.time_step #self.domain.time
4060        quantity[i,:] = quantity_slice* self.quantity_multiplier
4061       
4062    def close(self):
4063        self.outfile.close()
4064
4065def urs2sww(basename_in='o', basename_out=None, verbose=False,
4066            remove_nc_files=True,
4067            minlat=None, maxlat=None,
4068            minlon= None, maxlon=None,
4069            mint=None, maxt=None,
4070            mean_stage=0,
4071            origin = None,
4072            zscale=1,
4073            fail_on_NaN=True,
4074            NaN_filler=0,
4075            elevation=None):
4076    """
4077    Convert URS C binary format for wave propagation to
4078    sww format native to abstract_2d_finite_volumes.
4079
4080    Specify only basename_in and read files of the form
4081    basefilename_velocity-z-mux, basefilename_velocity-e-mux and
4082    basefilename_waveheight-n-mux containing relative height,
4083    x-velocity and y-velocity, respectively.
4084
4085    Also convert latitude and longitude to UTM. All coordinates are
4086    assumed to be given in the GDA94 datum. The latitude and longitude
4087    information is for  a grid.
4088
4089    min's and max's: If omitted - full extend is used.
4090    To include a value min may equal it, while max must exceed it.
4091    Lat and lon are assumed to be in decimal degrees.
4092    NOTE: minlon is the most east boundary.
4093   
4094    origin is a 3-tuple with geo referenced
4095    UTM coordinates (zone, easting, northing)
4096    It will be the origin of the sww file. This shouldn't be used,
4097    since all of anuga should be able to handle an arbitary origin.
4098
4099
4100    URS C binary format has data orgainised as TIME, LONGITUDE, LATITUDE
4101    which means that latitude is the fastest
4102    varying dimension (row major order, so to speak)
4103
4104    In URS C binary the latitudes and longitudes are in assending order.
4105    """
4106    if basename_out == None:
4107        basename_out = basename_in
4108    files_out = urs2nc(basename_in, basename_out)
4109    ferret2sww(basename_out,
4110               minlat=minlat,
4111               maxlat=maxlat,
4112               minlon=minlon,
4113               maxlon=maxlon,
4114               mint=mint,
4115               maxt=maxt,
4116               mean_stage=mean_stage,
4117               origin=origin,
4118               zscale=zscale,
4119               fail_on_NaN=fail_on_NaN,
4120               NaN_filler=NaN_filler,
4121               inverted_bathymetry=True,
4122               verbose=verbose)
4123    #print "files_out",files_out
4124    if remove_nc_files:
4125        for file_out in files_out:
4126            os.remove(file_out)
4127   
4128def urs2nc(basename_in = 'o', basename_out = 'urs'):
4129    """
4130    Convert the 3 urs files to 4 nc files.
4131
4132    The name of the urs file names must be;
4133    [basename_in]_velocity-z-mux
4134    [basename_in]_velocity-e-mux
4135    [basename_in]_waveheight-n-mux
4136   
4137    """
4138   
4139    files_in = [basename_in + WAVEHEIGHT_MUX_LABEL,
4140                basename_in + EAST_VELOCITY_LABEL,
4141                basename_in + NORTH_VELOCITY_LABEL]
4142    files_out = [basename_out+'_ha.nc',
4143                 basename_out+'_ua.nc',
4144                 basename_out+'_va.nc']
4145    quantities = ['HA','UA','VA']
4146
4147    for file_name in files_in:
4148        if os.access(file_name, os.F_OK) == 0 :
4149            msg = 'File %s does not exist or is not accessible' %file_name
4150            raise IOError, msg
4151       
4152    hashed_elevation = None
4153    for file_in, file_out, quantity in map(None, files_in,
4154                                           files_out,
4155                                           quantities):
4156        lonlatdep, lon, lat, depth = _binary_c2nc(file_in,
4157                                         file_out,
4158                                         quantity)
4159        #print "lonlatdep", lonlatdep
4160        if hashed_elevation == None:
4161            elevation_file = basename_out+'_e.nc'
4162            write_elevation_nc(elevation_file,
4163                                lon,
4164                                lat,
4165                                depth)
4166            hashed_elevation = myhash(lonlatdep)
4167        else:
4168            msg = "The elevation information in the mux files is inconsistent"
4169            assert hashed_elevation == myhash(lonlatdep), msg
4170    files_out.append(elevation_file)
4171    return files_out
4172   
4173def _binary_c2nc(file_in, file_out, quantity):
4174    """
4175    Reads in a quantity urs file and writes a quantity nc file.
4176    additionally, returns the depth and lat, long info,
4177    so it can be written to a file.
4178    """
4179    columns = 3 # long, lat , depth
4180    mux_file = open(file_in, 'rb')
4181   
4182    # Number of points/stations
4183    (points_num,)= unpack('i',mux_file.read(4))
4184
4185    # nt, int - Number of time steps
4186    (time_step_count,)= unpack('i',mux_file.read(4))
4187
4188    #dt, float - time step, seconds
4189    (time_step,) = unpack('f', mux_file.read(4))
4190   
4191    msg = "Bad data in the mux file."
4192    if points_num < 0:
4193        mux_file.close()
4194        raise ANUGAError, msg
4195    if time_step_count < 0:
4196        mux_file.close()
4197        raise ANUGAError, msg
4198    if time_step < 0:
4199        mux_file.close()
4200        raise ANUGAError, msg
4201   
4202    lonlatdep = p_array.array('f')
4203    lonlatdep.read(mux_file, columns * points_num)
4204    lonlatdep = array(lonlatdep, typecode=Float)   
4205    lonlatdep = reshape(lonlatdep, (points_num, columns))
4206   
4207    lon, lat, depth = lon_lat2grid(lonlatdep)
4208    lon_sorted = list(lon)
4209    lon_sorted.sort()
4210
4211    if not lon == lon_sorted:
4212        msg = "Longitudes in mux file are not in ascending order"
4213        raise IOError, msg
4214    lat_sorted = list(lat)
4215    lat_sorted.sort()
4216
4217    if not lat == lat_sorted:
4218        msg = "Latitudes in mux file are not in ascending order"
4219   
4220    nc_file = Write_nc(quantity,
4221                       file_out,
4222                       time_step_count,
4223                       time_step,
4224                       lon,
4225                       lat)
4226
4227    for i in range(time_step_count):
4228        #Read in a time slice  from mux file 
4229        hz_p_array = p_array.array('f')
4230        hz_p_array.read(mux_file, points_num)
4231        hz_p = array(hz_p_array, typecode=Float)
4232        hz_p = reshape(hz_p, (len(lon), len(lat)))
4233        hz_p = transpose(hz_p) #mux has lat varying fastest, nc has long v.f.
4234
4235        #write time slice to nc file
4236        nc_file.store_timestep(hz_p)
4237    mux_file.close()
4238    nc_file.close()
4239
4240    return lonlatdep, lon, lat, depth
4241   
4242
4243def write_elevation_nc(file_out, lon, lat, depth_vector):
4244    """
4245    Write an nc elevation file.
4246    """
4247   
4248    # NetCDF file definition
4249    outfile = NetCDFFile(file_out, 'w')
4250
4251    #Create new file
4252    nc_lon_lat_header(outfile, lon, lat)
4253   
4254    # ELEVATION
4255    zname = 'ELEVATION'
4256    outfile.createVariable(zname, precision, (lat_name, lon_name))
4257    outfile.variables[zname].units='CENTIMETERS'
4258    outfile.variables[zname].missing_value=-1.e+034
4259
4260    outfile.variables[lon_name][:]= ensure_numeric(lon)
4261    outfile.variables[lat_name][:]= ensure_numeric(lat)
4262
4263    depth = reshape(depth_vector, ( len(lat), len(lon)))
4264    outfile.variables[zname][:]= depth
4265   
4266    outfile.close()
4267   
4268def nc_lon_lat_header(outfile, lon, lat):
4269    """
4270    outfile is the netcdf file handle.
4271    lon - a list/array of the longitudes
4272    lat - a list/array of the latitudes
4273    """
4274   
4275    outfile.institution = 'Geoscience Australia'
4276    outfile.description = 'Converted from URS binary C'
4277   
4278    # Longitude
4279    outfile.createDimension(lon_name, len(lon))
4280    outfile.createVariable(lon_name, precision, (lon_name,))
4281    outfile.variables[lon_name].point_spacing='uneven'
4282    outfile.variables[lon_name].units='degrees_east'
4283    outfile.variables[lon_name].assignValue(lon)
4284
4285
4286    # Latitude
4287    outfile.createDimension(lat_name, len(lat))
4288    outfile.createVariable(lat_name, precision, (lat_name,))
4289    outfile.variables[lat_name].point_spacing='uneven'
4290    outfile.variables[lat_name].units='degrees_north'
4291    outfile.variables[lat_name].assignValue(lat)
4292
4293
4294   
4295def lon_lat2grid(long_lat_dep):
4296    """
4297    given a list of points that are assumed to be an a grid,
4298    return the long's and lat's of the grid.
4299    long_lat_dep is an array where each row is a position.
4300    The first column is longitudes.
4301    The second column is latitudes.
4302
4303    The latitude is the fastest varying dimension - in mux files
4304    """
4305    LONG = 0
4306    LAT = 1
4307    QUANTITY = 2
4308
4309    long_lat_dep = ensure_numeric(long_lat_dep, Float)
4310   
4311    num_points = long_lat_dep.shape[0]
4312    this_rows_long = long_lat_dep[0,LONG]
4313
4314    # Count the length of unique latitudes
4315    i = 0
4316    while long_lat_dep[i,LONG] == this_rows_long and i < num_points:
4317        i += 1
4318    # determine the lats and longsfrom the grid
4319    lat = long_lat_dep[:i, LAT]       
4320    long = long_lat_dep[::i, LONG]
4321   
4322    lenlong = len(long)
4323    lenlat = len(lat)
4324    #print 'len lat', lat, len(lat)
4325    #print 'len long', long, len(long)
4326         
4327    msg = 'Input data is not gridded'     
4328    assert num_points % lenlat == 0, msg
4329    assert num_points % lenlong == 0, msg
4330         
4331    # Test that data is gridded       
4332    for i in range(lenlong):
4333        msg = 'Data is not gridded.  It must be for this operation'
4334        first = i*lenlat
4335        last = first + lenlat
4336               
4337        assert allclose(long_lat_dep[first:last,LAT], lat), msg
4338        assert allclose(long_lat_dep[first:last,LONG], long[i]), msg
4339   
4340   
4341#    print 'range long', min(long), max(long)
4342#    print 'range lat', min(lat), max(lat)
4343#    print 'ref long', min(long_lat_dep[:,0]), max(long_lat_dep[:,0])
4344#    print 'ref lat', min(long_lat_dep[:,1]), max(long_lat_dep[:,1])
4345   
4346   
4347   
4348    msg = 'Out of range latitudes/longitudes'
4349    for l in lat:assert -90 < l < 90 , msg
4350    for l in long:assert -180 < l < 180 , msg
4351
4352    # Changing quantity from lat being the fastest varying dimension to
4353    # long being the fastest varying dimension
4354    # FIXME - make this faster/do this a better way
4355    # use numeric transpose, after reshaping the quantity vector
4356#    quantity = zeros(len(long_lat_dep), Float)
4357    quantity = zeros(num_points, Float)
4358   
4359#    print 'num',num_points
4360    for lat_i, _ in enumerate(lat):
4361        for long_i, _ in enumerate(long):
4362            q_index = lat_i*lenlong+long_i
4363            lld_index = long_i*lenlat+lat_i
4364#            print 'lat_i', lat_i, 'long_i',long_i, 'q_index', q_index, 'lld_index', lld_index
4365            temp = long_lat_dep[lld_index, QUANTITY]
4366            quantity[q_index] = temp
4367           
4368    return long, lat, quantity
4369
4370    ####  END URS 2 SWW  ###
4371
4372    #### URS UNGRIDDED 2 SWW ###
4373
4374    ### PRODUCING THE POINTS NEEDED FILE ###
4375LL_LAT = -50.0
4376LL_LONG = 80.0
4377GRID_SPACING = 1.0/60.0
4378LAT_AMOUNT = 4800
4379LONG_AMOUNT = 3600
4380def URS_points_needed_to_file(file_name, boundary_polygon, zone,
4381                              ll_lat=LL_LAT, ll_long=LL_LONG,
4382                              grid_spacing=GRID_SPACING, 
4383                              lat_amount=LAT_AMOUNT, long_amount=LONG_AMOUNT,
4384                              export_csv=False, use_cache=False,
4385                              verbose=False):
4386    """
4387    file_name - name of the urs file produced for David.
4388    boundary_polygon - a list of points that describes a polygon.
4389                      The last point is assumed ot join the first point.
4390                      This is in UTM (lat long would be better though)
4391
4392    ll_lat - lower left latitude, in decimal degrees
4393    ll-long - lower left longitude, in decimal degrees
4394    grid_spacing - in deciamal degrees
4395
4396
4397    Don't add the file extension.  It will be added.
4398    """
4399    geo = URS_points_needed(boundary_polygon, zone, ll_lat, ll_long,
4400                            grid_spacing, 
4401                      lat_amount, long_amount,use_cache, verbose)
4402    if not file_name[-4:] == ".urs":
4403        file_name += ".urs"
4404    geo.export_points_file(file_name)
4405    if export_csv:
4406        if file_name[-4:] == ".urs":
4407            file_name = file_name[:-4] + ".csv"
4408        geo.export_points_file(file_name)
4409
4410def URS_points_needed(boundary_polygon, zone, ll_lat=LL_LAT,
4411                      ll_long=LL_LONG, grid_spacing=GRID_SPACING, 
4412                      lat_amount=LAT_AMOUNT, long_amount=LONG_AMOUNT,
4413                      use_cache=False, verbose=False):
4414    args = (boundary_polygon,
4415                      zone)
4416    kwargs = {'ll_lat': ll_lat,
4417              'll_long': ll_long,
4418              'grid_spacing': grid_spacing,
4419              'lat_amount': lat_amount,
4420              'long_amount': long_amount} 
4421    if use_cache is True:
4422        try:
4423            from anuga.caching import cache
4424        except:
4425            msg = 'Caching was requested, but caching module'+\
4426                  'could not be imported'
4427            raise msg
4428
4429
4430        geo = cache(_URS_points_needed,
4431                  args, kwargs,
4432                  verbose=verbose,
4433                  compression=False)
4434    else:
4435        #I was getting 'got multiple values for keyword argument' errors
4436        #geo = apply(_URS_points_needed, args, kwargs)
4437        geo = _URS_points_needed(boundary_polygon,
4438                      zone, ll_lat,
4439                      ll_long, grid_spacing, 
4440                      lat_amount, long_amount)
4441
4442    return geo   
4443def _URS_points_needed(boundary_polygon,
4444                      zone, ll_lat=LL_LAT,
4445                      ll_long=LL_LONG, grid_spacing=GRID_SPACING, 
4446                      lat_amount=LAT_AMOUNT, long_amount=LONG_AMOUNT):
4447    """
4448
4449    boundary_polygon - a list of points that describes a polygon.
4450                      The last point is assumed ot join the first point.
4451                      This is in UTM (lat long would be better though)
4452
4453    ll_lat - lower left latitude, in decimal degrees
4454    ll-long - lower left longitude, in decimal degrees
4455    grid_spacing - in deciamal degrees
4456
4457    """
4458   
4459    from sets import ImmutableSet
4460   
4461    msg = "grid_spacing can not be zero"
4462    assert not grid_spacing ==0, msg
4463    a = boundary_polygon
4464    # List of segments.  Each segment is two points.
4465    segs = [i and [a[i-1], a[i]] or [a[len(a)-1], a[0]] for i in range(len(a))]
4466
4467    # convert the segs to Lat's and longs.
4468   
4469    # Don't assume the zone of the segments is the same as the lower left
4470    # corner of the lat long data!!  They can easily be in different zones
4471   
4472    lat_long_set = ImmutableSet()
4473    for seg in segs:
4474        points_lat_long = points_needed(seg, ll_lat, ll_long, grid_spacing, 
4475                      lat_amount, long_amount, zone)
4476        lat_long_set |= ImmutableSet(points_lat_long)
4477    #print "lat_long_set",lat_long_set
4478    geo = Geospatial_data(data_points=list(lat_long_set),
4479                              points_are_lats_longs=True)
4480    return geo
4481   
4482def points_needed(seg, ll_lat, ll_long, grid_spacing, 
4483                  lat_amount, long_amount, zone):
4484    """
4485    return a list of the points, in lats and longs that are needed to
4486    interpolate any point on the segment.
4487    """
4488    from math import sqrt
4489    #print "zone",zone
4490    geo_reference = Geo_reference(zone=zone)
4491    #print "seg",seg
4492    geo = Geospatial_data(seg,geo_reference=geo_reference)
4493    seg_lat_long = geo.get_data_points(as_lat_long=True)
4494    #print "seg_lat_long", seg_lat_long
4495    # 1.415 = 2^0.5, rounded up....
4496    sqrt_2_rounded_up = 1.415
4497    buffer = sqrt_2_rounded_up * grid_spacing
4498   
4499    max_lat = max(seg_lat_long[0][0], seg_lat_long[1][0]) + buffer
4500    max_long = max(seg_lat_long[0][1], seg_lat_long[1][1]) + buffer
4501    min_lat = min(seg_lat_long[0][0], seg_lat_long[1][0]) - buffer
4502    min_long = min(seg_lat_long[0][1], seg_lat_long[1][1]) - buffer
4503
4504    first_row = (min_long - ll_long)/grid_spacing
4505    # To round up
4506    first_row_long = int(round(first_row + 0.5))
4507    #print "first_row", first_row_long
4508
4509    last_row = (max_long - ll_long)/grid_spacing # round down
4510    last_row_long = int(round(last_row))
4511    #print "last_row",last_row _long
4512   
4513    first_row = (min_lat - ll_lat)/grid_spacing
4514    # To round up
4515    first_row_lat = int(round(first_row + 0.5))
4516    #print "first_row", first_row_lat
4517
4518    last_row = (max_lat - ll_lat)/grid_spacing # round down
4519    last_row_lat = int(round(last_row))
4520    #print "last_row",last_row_lat
4521
4522    # to work out the max distance -
4523    # 111120 - horizontal distance between 1 deg latitude.
4524    #max_distance = sqrt_2_rounded_up * 111120 * grid_spacing
4525    max_distance = 157147.4112 * grid_spacing
4526    #print "max_distance", max_distance #2619.12 m for 1 minute
4527    points_lat_long = []
4528    # Create a list of the lat long points to include.
4529    for index_lat in range(first_row_lat, last_row_lat + 1):
4530        for index_long in range(first_row_long, last_row_long + 1):
4531            lat = ll_lat + index_lat*grid_spacing
4532            long = ll_long + index_long*grid_spacing
4533
4534            #filter here to keep good points
4535            if keep_point(lat, long, seg, max_distance):
4536                points_lat_long.append((lat, long)) #must be hashable
4537    #print "points_lat_long", points_lat_long
4538
4539    # Now that we have these points, lets throw ones out that are too far away
4540    return points_lat_long
4541
4542def keep_point(lat, long, seg, max_distance):
4543    """
4544    seg is two points, UTM
4545    """
4546    from math import sqrt
4547    _ , x0, y0 = redfearn(lat, long)
4548    x1 = seg[0][0]
4549    y1 = seg[0][1]
4550    x2 = seg[1][0]
4551    y2 = seg[1][1]
4552
4553    x2_1 = x2-x1
4554    y2_1 = y2-y1
4555    d = abs((x2_1)*(y1-y0)-(x1-x0)*(y2_1))/sqrt((x2_1)*(x2_1)+(y2_1)*(y2_1))
4556    if d <= max_distance:
4557        return True
4558    else:
4559        return False
4560   
4561    #### CONVERTING UNGRIDDED URS DATA TO AN SWW FILE ####
4562   
4563WAVEHEIGHT_MUX_LABEL = '_waveheight-z-mux'
4564EAST_VELOCITY_LABEL =  '_velocity-e-mux'
4565NORTH_VELOCITY_LABEL =  '_velocity-n-mux' 
4566def urs_ungridded2sww(basename_in='o', basename_out=None, verbose=False,
4567                      mint=None, maxt=None,
4568                      mean_stage=0,
4569                      origin=None,
4570                      hole_points_UTM=None,
4571                      zscale=1):
4572    """   
4573    Convert URS C binary format for wave propagation to
4574    sww format native to abstract_2d_finite_volumes.
4575
4576
4577    Specify only basename_in and read files of the form
4578    basefilename_velocity-z-mux, basefilename_velocity-e-mux and
4579    basefilename_waveheight-n-mux containing relative height,
4580    x-velocity and y-velocity, respectively.
4581
4582    Also convert latitude and longitude to UTM. All coordinates are
4583    assumed to be given in the GDA94 datum. The latitude and longitude
4584    information is assumed ungridded grid.
4585
4586    min's and max's: If omitted - full extend is used.
4587    To include a value min ans max may equal it.
4588    Lat and lon are assumed to be in decimal degrees.
4589   
4590    origin is a 3-tuple with geo referenced
4591    UTM coordinates (zone, easting, northing)
4592    It will be the origin of the sww file. This shouldn't be used,
4593    since all of anuga should be able to handle an arbitary origin.
4594    The mux point info is NOT relative to this origin.
4595
4596
4597    URS C binary format has data orgainised as TIME, LONGITUDE, LATITUDE
4598    which means that latitude is the fastest
4599    varying dimension (row major order, so to speak)
4600
4601    In URS C binary the latitudes and longitudes are in assending order.
4602
4603    Note, interpolations of the resulting sww file will be different
4604    from results of urs2sww.  This is due to the interpolation
4605    function used, and the different grid structure between urs2sww
4606    and this function.
4607   
4608    Interpolating data that has an underlying gridded source can
4609    easily end up with different values, depending on the underlying
4610    mesh.
4611
4612    consider these 4 points
4613    50  -50
4614
4615    0     0
4616
4617    The grid can be
4618     -
4619    |\|    A
4620     -
4621     or;
4622      -
4623     |/|   B
4624      -
4625      If a point is just below the center of the midpoint, it will have a
4626      +ve value in grid A and a -ve value in grid B.
4627    """ 
4628    from anuga.pmesh.mesh import Mesh, NoTrianglesError
4629
4630    files_in = [basename_in + WAVEHEIGHT_MUX_LABEL,
4631                basename_in + EAST_VELOCITY_LABEL,
4632                basename_in + NORTH_VELOCITY_LABEL]
4633    quantities = ['HA','UA','VA']
4634
4635    # instanciate urs_points of the three mux files.
4636    mux = {}
4637    for quantity, file in map(None, quantities, files_in):
4638        mux[quantity] = Urs_points(file)
4639       
4640    # Could check that the depth is the same. (hashing)
4641
4642    # handle to a mux file to do depth stuff
4643    a_mux = mux[quantities[0]]
4644   
4645    # Convert to utm
4646    lat = a_mux.lonlatdep[:,1]
4647    long = a_mux.lonlatdep[:,0]
4648    points_utm, zone = convert_from_latlon_to_utm( \
4649        latitudes=lat, longitudes=long)
4650    #print "points_utm", points_utm
4651    #print "zone", zone
4652
4653    elevation = a_mux.lonlatdep[:,2] * -1 #
4654   
4655    # grid ( create a mesh from the selected points)
4656    # This mesh has a problem.  Triangles are streched over ungridded areas.
4657    #  If these areas could be described as holes in pmesh, that would be great
4658
4659    # I can't just get the user to selection a point in the middle.
4660    # A boundary is needed around these points.
4661    # But if the zone of points is obvious enough auto-segment should do
4662    # a good boundary.
4663    mesh = Mesh()
4664    mesh.add_vertices(points_utm)
4665    mesh.auto_segment(smooth_indents=True, expand_pinch=True)
4666    # To try and avoid alpha shape 'hugging' too much
4667    mesh.auto_segment( mesh.shape.get_alpha()*1.1 )
4668    if hole_points_UTM is not None:
4669        point = ensure_absolute(hole_points_UTM)
4670        mesh.add_hole(point[0], point[1])
4671    try:
4672        mesh.generate_mesh(minimum_triangle_angle=0.0, verbose=False)
4673    except NoTrianglesError:
4674        # This is a bit of a hack, going in and changing the
4675        # data structure.
4676        mesh.holes = []
4677        mesh.generate_mesh(minimum_triangle_angle=0.0, verbose=False)
4678    mesh_dic = mesh.Mesh2MeshList()
4679
4680    #mesh.export_mesh_file(basename_in + '_168.tsh')
4681    #import sys; sys.exit()
4682    # These are the times of the mux file
4683    mux_times = []
4684    for i in range(a_mux.time_step_count):
4685        mux_times.append(a_mux.time_step * i) 
4686    mux_times_start_i, mux_times_fin_i = mux2sww_time(mux_times, mint, maxt)
4687    times = mux_times[mux_times_start_i:mux_times_fin_i]
4688   
4689    if mux_times_start_i == mux_times_fin_i:
4690        # Close the mux files
4691        for quantity, file in map(None, quantities, files_in):
4692            mux[quantity].close()
4693        msg="Due to mint and maxt there's no time info in the boundary SWW."
4694        raise Exception, msg
4695       
4696    # If this raise is removed there is currently no downstream errors
4697           
4698    points_utm=ensure_numeric(points_utm)
4699    #print "mesh_dic['generatedpointlist']", mesh_dic['generatedpointlist']
4700    #print "points_utm", points_utm
4701    assert ensure_numeric(mesh_dic['generatedpointlist']) == \
4702           ensure_numeric(points_utm)
4703   
4704    volumes = mesh_dic['generatedtrianglelist']
4705   
4706    # write sww intro and grid stuff.   
4707    if basename_out is None:
4708        swwname = basename_in + '.sww'
4709    else:
4710        swwname = basename_out + '.sww'
4711
4712    if verbose: print 'Output to ', swwname
4713    outfile = NetCDFFile(swwname, 'w')
4714    # For a different way of doing this, check out tsh2sww
4715    # work out sww_times and the index range this covers
4716    sww = Write_sww()
4717    sww.store_header(outfile, times, len(volumes), len(points_utm),
4718                     verbose=verbose)
4719    outfile.mean_stage = mean_stage
4720    outfile.zscale = zscale
4721
4722    sww.store_triangulation(outfile, points_utm, volumes,
4723                            elevation, zone,  new_origin=origin,
4724                            verbose=verbose)
4725   
4726    if verbose: print 'Converting quantities'
4727    j = 0
4728    # Read in a time slice from each mux file and write it to the sww file
4729    for ha, ua, va in map(None, mux['HA'], mux['UA'], mux['VA']):
4730        if j >= mux_times_start_i and j < mux_times_fin_i:
4731            stage = zscale*ha + mean_stage
4732            h = stage - elevation
4733            xmomentum = ua*h
4734            ymomentum = -1*va*h # -1 since in mux files south is positive.
4735            sww.store_quantities(outfile, 
4736                                 slice_index=j - mux_times_start_i,
4737                                 verbose=verbose,
4738                                 stage=stage,
4739                                 xmomentum=xmomentum,
4740                                 ymomentum=ymomentum)
4741        j += 1
4742    if verbose: sww.verbose_quantities(outfile)
4743    outfile.close()
4744    #
4745    # Do some conversions while writing the sww file
4746
4747   
4748def mux2sww_time(mux_times, mint, maxt):
4749    """
4750    """
4751
4752    if mint == None:
4753        mux_times_start_i = 0
4754    else:
4755        mux_times_start_i = searchsorted(mux_times, mint)
4756       
4757    if maxt == None:
4758        mux_times_fin_i = len(mux_times)
4759    else:
4760        maxt += 0.5 # so if you specify a time where there is
4761                    # data that time will be included
4762        mux_times_fin_i = searchsorted(mux_times, maxt)
4763
4764    return mux_times_start_i, mux_times_fin_i
4765
4766
4767class Write_sww:
4768    from anuga.shallow_water.shallow_water_domain import Domain
4769
4770    # FIXME (Ole): Hardwiring the conserved quantities like
4771    # this could be a problem. I would prefer taking them from
4772    # the instantiation of Domain.
4773    sww_quantities = Domain.conserved_quantities
4774
4775
4776    RANGE = '_range'
4777    EXTREMA = ':extrema'
4778
4779    def __init__(self):
4780        pass
4781   
4782    def store_header(self,
4783                     outfile,
4784                     times,
4785                     number_of_volumes,
4786                     number_of_points,
4787                     description='Converted from XXX',
4788                     smoothing=True,
4789                     order=1, verbose=False):
4790        """
4791        outfile - the name of the file that will be written
4792        times - A list of the time slice times OR a start time
4793        Note, if a list is given the info will be made relative.
4794        number_of_volumes - the number of triangles
4795        """
4796   
4797        outfile.institution = 'Geoscience Australia'
4798        outfile.description = description
4799
4800        # For sww compatibility
4801        if smoothing is True:
4802            # Smoothing to be depreciated
4803            outfile.smoothing = 'Yes'
4804            outfile.vertices_are_stored_uniquely = 'False'
4805        else:
4806            # Smoothing to be depreciated
4807            outfile.smoothing = 'No'
4808            outfile.vertices_are_stored_uniquely = 'True'
4809        outfile.order = order
4810
4811        try:
4812            revision_number = get_revision_number()
4813        except:
4814            revision_number = None
4815        # Allow None to be stored as a string               
4816        outfile.revision_number = str(revision_number) 
4817
4818
4819       
4820        # times - A list or array of the time slice times OR a start time
4821        # times = ensure_numeric(times)
4822        # Start time in seconds since the epoch (midnight 1/1/1970)
4823
4824        # This is being used to seperate one number from a list.
4825        # what it is actually doing is sorting lists from numeric arrays.
4826        if type(times) is list or type(times) is ArrayType: 
4827            number_of_times = len(times)
4828            times = ensure_numeric(times) 
4829            if number_of_times == 0:
4830                starttime = 0
4831            else:
4832                starttime = times[0]
4833                times = times - starttime  #Store relative times
4834        else:
4835            number_of_times = 0
4836            starttime = times
4837            #times = ensure_numeric([])
4838        outfile.starttime = starttime
4839        # dimension definitions
4840        outfile.createDimension('number_of_volumes', number_of_volumes)
4841        outfile.createDimension('number_of_vertices', 3)
4842        outfile.createDimension('numbers_in_range', 2)
4843   
4844        if smoothing is True:
4845            outfile.createDimension('number_of_points', number_of_points)
4846       
4847            # FIXME(Ole): This will cause sww files for paralle domains to
4848            # have ghost nodes stored (but not used by triangles).
4849            # To clean this up, we have to change get_vertex_values and
4850            # friends in quantity.py (but I can't be bothered right now)
4851        else:
4852            outfile.createDimension('number_of_points', 3*number_of_volumes)
4853        outfile.createDimension('number_of_timesteps', number_of_times)
4854
4855        # variable definitions
4856        outfile.createVariable('x', precision, ('number_of_points',))
4857        outfile.createVariable('y', precision, ('number_of_points',))
4858        outfile.createVariable('elevation', precision, ('number_of_points',))
4859        q = 'elevation'
4860        outfile.createVariable(q+Write_sww.RANGE, precision,
4861                               ('numbers_in_range',))
4862
4863
4864        # Initialise ranges with small and large sentinels.
4865        # If this was in pure Python we could have used None sensibly
4866        outfile.variables[q+Write_sww.RANGE][0] = max_float  # Min               
4867        outfile.variables[q+Write_sww.RANGE][1] = -max_float # Max
4868
4869        # FIXME: Backwards compatibility
4870        outfile.createVariable('z', precision, ('number_of_points',))
4871        #################################
4872
4873        outfile.createVariable('volumes', Int, ('number_of_volumes',
4874                                                'number_of_vertices'))
4875       
4876        outfile.createVariable('time', precision,
4877                               ('number_of_timesteps',))
4878       
4879        for q in Write_sww.sww_quantities:
4880            outfile.createVariable(q, precision,
4881                                   ('number_of_timesteps',
4882                                    'number_of_points')) 
4883            outfile.createVariable(q+Write_sww.RANGE, precision,
4884                                   ('numbers_in_range',))
4885
4886            # Initialise ranges with small and large sentinels.
4887            # If this was in pure Python we could have used None sensibly
4888            outfile.variables[q+Write_sww.RANGE][0] = max_float  # Min               
4889            outfile.variables[q+Write_sww.RANGE][1] = -max_float # Max
4890           
4891        if type(times) is list or type(times) is ArrayType: 
4892            outfile.variables['time'][:] = times    #Store time relative
4893           
4894        if verbose:
4895            print '------------------------------------------------'
4896            print 'Statistics:'
4897            print '    t in [%f, %f], len(t) == %d'\
4898                  %(min(times.flat), max(times.flat), len(times.flat))
4899
4900       
4901    def store_triangulation(self,
4902                            outfile,
4903                            points_utm,
4904                            volumes,
4905                            elevation, zone=None, new_origin=None, 
4906                            points_georeference=None, verbose=False):
4907        """
4908       
4909        new_origin - qa georeference that the points can be set to. (Maybe
4910        do this before calling this function.)
4911       
4912        points_utm - currently a list or array of the points in UTM.
4913        points_georeference - the georeference of the points_utm
4914       
4915        How about passing new_origin and current_origin.
4916        If you get both, do a convertion from the old to the new.
4917       
4918        If you only get new_origin, the points are absolute,
4919        convert to relative
4920       
4921        if you only get the current_origin the points are relative, store
4922        as relative.
4923       
4924        if you get no georefs create a new georef based on the minimums of
4925        points_utm.  (Another option would be to default to absolute)
4926       
4927        Yes, and this is done in another part of the code.
4928        Probably geospatial.
4929       
4930        If you don't supply either geo_refs, then supply a zone. If not
4931        the default zone will be used.
4932       
4933       
4934        precon
4935       
4936        header has been called.
4937        """
4938       
4939        number_of_points = len(points_utm)   
4940        volumes = array(volumes) 
4941        points_utm = array(points_utm)
4942
4943        # given the two geo_refs and the points, do the stuff
4944        # described in the method header
4945        # if this is needed else where, pull out as a function
4946        points_georeference = ensure_geo_reference(points_georeference)
4947        new_origin = ensure_geo_reference(new_origin)
4948        if new_origin is None and points_georeference is not None:
4949            points = points_utm
4950            geo_ref = points_georeference
4951        else:
4952            if new_origin is None:
4953                new_origin = Geo_reference(zone,min(points_utm[:,0]),
4954                                           min(points_utm[:,1]))
4955            points = new_origin.change_points_geo_ref(points_utm,
4956                                                      points_georeference)
4957            geo_ref = new_origin
4958
4959        # At this stage I need a georef and points
4960        # the points are relative to the georef
4961        geo_ref.write_NetCDF(outfile)
4962   
4963        # This will put the geo ref in the middle
4964        #geo_ref=Geo_reference(refzone,(max(x)+min(x))/2.0,(max(x)+min(y))/2.)
4965       
4966        x =  points[:,0]
4967        y =  points[:,1]
4968        z = outfile.variables['z'][:]
4969   
4970        if verbose:
4971            print '------------------------------------------------'
4972            print 'More Statistics:'
4973            print '  Extent (/lon):'
4974            print '    x in [%f, %f], len(lat) == %d'\
4975                  %(min(x), max(x),
4976                    len(x))
4977            print '    y in [%f, %f], len(lon) == %d'\
4978                  %(min(y), max(y),
4979                    len(y))
4980            print '    z in [%f, %f], len(z) == %d'\
4981                  %(min(elevation), max(elevation),
4982                    len(elevation))
4983            print 'geo_ref: ',geo_ref
4984            print '------------------------------------------------'
4985           
4986        #z = resize(bath_grid,outfile.variables['z'][:].shape)
4987        outfile.variables['x'][:] = points[:,0] #- geo_ref.get_xllcorner()
4988        outfile.variables['y'][:] = points[:,1] #- geo_ref.get_yllcorner()
4989        outfile.variables['z'][:] = elevation
4990        outfile.variables['elevation'][:] = elevation  #FIXME HACK
4991        outfile.variables['volumes'][:] = volumes.astype(Int32) #On Opteron 64
4992
4993        q = 'elevation'
4994        # This updates the _range values
4995        outfile.variables[q+Write_sww.RANGE][0] = min(elevation)
4996        outfile.variables[q+Write_sww.RANGE][1] = max(elevation)
4997
4998
4999    def store_quantities(self, outfile, precision=Float,
5000                         slice_index=None, time=None,
5001                         verbose=False, **quant):
5002        """
5003        Write the quantity info.
5004
5005        **quant is extra keyword arguments passed in. These must be
5006          the sww quantities, currently; stage, xmomentum, ymomentum.
5007       
5008        if the time array is already been built, use the slice_index
5009        to specify the index.
5010       
5011        Otherwise, use time to increase the time dimension
5012
5013        Maybe make this general, but the viewer assumes these quantities,
5014        so maybe we don't want it general - unless the viewer is general
5015       
5016        precon
5017        triangulation and
5018        header have been called.
5019        """
5020
5021        if time is not None:
5022            file_time = outfile.variables['time']
5023            slice_index = len(file_time)
5024            file_time[slice_index] = time   
5025
5026        # write the conserved quantities from Domain.
5027        # Typically stage,  xmomentum, ymomentum
5028        # other quantities will be ignored, silently.
5029        for q in Write_sww.sww_quantities:
5030            if not quant.has_key(q):
5031                msg = 'SWW file can not write quantity %s' %q
5032                raise NewQuantity, msg
5033            else:
5034                q_values = quant[q]
5035                outfile.variables[q][slice_index] = q_values.astype(precision)
5036
5037                # This updates the _range values
5038                q_range = outfile.variables[q+Write_sww.RANGE][:]
5039                q_values_min = min(q_values)
5040                if q_values_min < q_range[0]:
5041                    outfile.variables[q+Write_sww.RANGE][0] = q_values_min
5042                q_values_max = max(q_values)
5043                if q_values_max > q_range[1]:
5044                    outfile.variables[q+Write_sww.RANGE][1] = q_values_max
5045
5046    def verbose_quantities(self, outfile):
5047        print '------------------------------------------------'
5048        print 'More Statistics:'
5049        for q in Write_sww.sww_quantities:
5050            print %s in [%f, %f]' %(q,
5051                                       outfile.variables[q+Write_sww.RANGE][0],
5052                                       outfile.variables[q+Write_sww.RANGE][1])
5053        print '------------------------------------------------'
5054
5055
5056       
5057def obsolete_write_sww_time_slices(outfile, has, uas, vas, elevation,
5058                         mean_stage=0, zscale=1,
5059                         verbose=False):   
5060    #Time stepping
5061    stage = outfile.variables['stage']
5062    xmomentum = outfile.variables['xmomentum']
5063    ymomentum = outfile.variables['ymomentum']
5064
5065    n = len(has)
5066    j=0
5067    for ha, ua, va in map(None, has, uas, vas):
5068        if verbose and j%((n+10)/10)==0: print '  Doing %d of %d' %(j, n)
5069        w = zscale*ha + mean_stage
5070        stage[j] = w
5071        h = w - elevation
5072        xmomentum[j] = ua*h
5073        ymomentum[j] = -1*va*#  -1 since in mux files south is positive.
5074        j += 1
5075   
5076def urs2txt(basename_in, location_index=None):
5077    """
5078    Not finished or tested
5079    """
5080   
5081    files_in = [basename_in + WAVEHEIGHT_MUX_LABEL,
5082                basename_in + EAST_VELOCITY_LABEL,
5083                basename_in + NORTH_VELOCITY_LABEL]
5084    quantities = ['HA','UA','VA']
5085
5086    d = ","
5087   
5088    # instanciate urs_points of the three mux files.
5089    mux = {}
5090    for quantity, file in map(None, quantities, files_in):
5091        mux[quantity] = Urs_points(file)
5092       
5093    # Could check that the depth is the same. (hashing)
5094
5095    # handle to a mux file to do depth stuff
5096    a_mux = mux[quantities[0]]
5097   
5098    # Convert to utm
5099    latitudes = a_mux.lonlatdep[:,1]
5100    longitudes = a_mux.lonlatdep[:,0]
5101    points_utm, zone = convert_from_latlon_to_utm( \
5102        latitudes=latitudes, longitudes=longitudes)
5103    #print "points_utm", points_utm
5104    #print "zone", zone
5105    depths = a_mux.lonlatdep[:,2]  #
5106   
5107    fid = open(basename_in + '.txt', 'w')
5108
5109    fid.write("zone: " + str(zone) + "\n")
5110
5111    if location_index is not None:
5112        #Title
5113        li = location_index
5114        fid.write('location_index'+d+'lat'+d+ 'long' +d+ 'Easting' +d+ \
5115                  'Northing' + "\n")
5116        fid.write(str(li) +d+ str(latitudes[li])+d+ \
5117              str(longitudes[li]) +d+ str(points_utm[li][0]) +d+ \
5118              str(points_utm[li][01]) + "\n")
5119
5120    # the non-time dependent stuff
5121    #Title
5122    fid.write('location_index'+d+'lat'+d+ 'long' +d+ 'Easting' +d+ \
5123                  'Northing' +d+ 'depth m' + "\n")
5124    i = 0
5125    for depth, point_utm, lat, long in map(None, depths,
5126                                               points_utm, latitudes,
5127                                               longitudes):
5128       
5129        fid.write(str(i) +d+ str(lat)+d+ str(long) +d+ str(point_utm[0]) +d+ \
5130                  str(point_utm[01]) +d+ str(depth) + "\n")
5131        i +=1
5132    #Time dependent
5133    if location_index is not None:
5134        time_step = a_mux.time_step
5135        i = 0
5136        #Title
5137        fid.write('time' +d+ 'HA depth m'+d+ \
5138                 'UA momentum East x m/sec' +d+ 'VA momentum North y m/sec' \
5139                      + "\n")
5140        for HA, UA, VA in map(None, mux['HA'], mux['UA'], mux['VA']):
5141            fid.write(str(i*time_step) +d+ str(HA[location_index])+d+ \
5142                      str(UA[location_index]) +d+ str(VA[location_index]) \
5143                      + "\n")
5144           
5145            i +=1
5146   
5147class Urs_points:
5148    """
5149    Read the info in URS mux files.
5150
5151    for the quantities heres a correlation between the file names and
5152    what they mean;
5153    z-mux is height above sea level, m
5154    e-mux is velocity is Eastern direction, m/s
5155    n-mux is velocity is Northern direction, m/s   
5156    """
5157    def __init__(self,urs_file):
5158        self.iterated = False
5159        columns = 3 # long, lat , depth
5160        mux_file = open(urs_file, 'rb')
5161       
5162        # Number of points/stations
5163        (self.points_num,)= unpack('i',mux_file.read(4))
5164       
5165        # nt, int - Number of time steps
5166        (self.time_step_count,)= unpack('i',mux_file.read(4))
5167        #print "self.time_step_count", self.time_step_count
5168        #dt, float - time step, seconds
5169        (self.time_step,) = unpack('f', mux_file.read(4))
5170        #print "self.time_step", self.time_step
5171        msg = "Bad data in the urs file."
5172        if self.points_num < 0:
5173            mux_file.close()
5174            raise ANUGAError, msg
5175        if self.time_step_count < 0:
5176            mux_file.close()
5177            raise ANUGAError, msg
5178        if self.time_step < 0:
5179            mux_file.close()
5180            raise ANUGAError, msg
5181
5182        # the depth is in meters, and it is the distance from the ocean
5183        # to the sea bottom.
5184        lonlatdep = p_array.array('f')
5185        lonlatdep.read(mux_file, columns * self.points_num)
5186        lonlatdep = array(lonlatdep, typecode=Float)   
5187        lonlatdep = reshape(lonlatdep, (self.points_num, columns))
5188        #print 'lonlatdep',lonlatdep
5189        self.lonlatdep = lonlatdep
5190       
5191        self.mux_file = mux_file
5192        # check this array
5193
5194    def __iter__(self):
5195        """
5196        iterate over quantity data which is with respect to time.
5197
5198        Note: You can only interate once over an object
5199       
5200        returns quantity infomation for each time slice
5201        """
5202        msg =  "You can only interate once over a urs file."
5203        assert not self.iterated, msg
5204        self.iter_time_step = 0
5205        self.iterated = True
5206        return self
5207   
5208    def next(self):
5209        if self.time_step_count == self.iter_time_step:
5210            self.close()
5211            raise StopIteration
5212        #Read in a time slice  from mux file 
5213        hz_p_array = p_array.array('f')
5214        hz_p_array.read(self.mux_file, self.points_num)
5215        hz_p = array(hz_p_array, typecode=Float)
5216        self.iter_time_step += 1
5217       
5218        return hz_p
5219
5220    def close(self):
5221        self.mux_file.close()
5222       
5223    #### END URS UNGRIDDED 2 SWW ###
5224
5225       
5226def start_screen_catcher(dir_name, myid='', numprocs='', extra_info='',
5227                         print_to_screen=False, verbose=False):
5228    """
5229    Used to store screen output and errors to file, if run on multiple
5230    processes eachprocessor will have its own output and error file.
5231   
5232    extra_info - is used as a string that can identify outputs with another
5233    string eg. '_other'
5234    """
5235    import sys
5236    dir_name = dir_name
5237    if access(dir_name,W_OK) == 0:
5238        if verbose: print 'Make directory %s' %dir_name
5239        if verbose: print "myid", myid
5240        mkdir (dir_name,0777)
5241    if myid <>'':
5242        myid = '_'+str(myid)
5243    if numprocs <>'':
5244        numprocs = '_'+str(numprocs)
5245    if extra_info <>'':
5246        extra_info = '_'+str(extra_info)
5247    screen_output_name = dir_name + "screen_output%s%s%s.txt" %(myid,
5248                                                                numprocs,
5249                                                                extra_info)
5250    screen_error_name = dir_name + "screen_error%s%s%s.txt" %(myid,
5251                                                              numprocs,
5252                                                              extra_info)
5253    print screen_output_name
5254    #used to catch screen output to file
5255    sys.stdout = Screen_Catcher(screen_output_name)
5256    sys.stderr = Screen_Catcher(screen_error_name)
5257
5258class Screen_Catcher:
5259    """this simply catches the screen output and stores it to file defined by
5260    start_screen_catcher (above)
5261    """
5262   
5263    def __init__(self, filename):
5264        self.filename = filename
5265 
5266        if exists(self.filename)is True:
5267            print'Old existing file "%s" has been deleted' %(self.filename)
5268            remove(self.filename)
5269
5270    def write(self, stuff):
5271        fid = open(self.filename, 'a')
5272        fid.write(stuff)
5273#        if print_to_screen: print stuff
5274
5275def copy_code_files(dir_name, filename1, filename2):
5276    """Copies "filename1" and "filename2" to "dir_name". Very useful for
5277    information management
5278    filename1 and filename2 are both absolute pathnames   
5279    """
5280
5281    if access(dir_name,F_OK) == 0:
5282        print 'Make directory %s' %dir_name
5283        mkdir (dir_name,0777)
5284    shutil.copy(filename1, dir_name + sep + basename(filename1))
5285    shutil.copy(filename2, dir_name + sep + basename(filename2))
5286#    copy (__file__, project.output_run_time_dir + basename(__file__))
5287    print 'Files %s and %s copied' %(filename1, filename2)
5288
5289def get_data_from_file(filename,separator_value = ','):
5290    """
5291    Read in data information from file and
5292   
5293    Returns:
5294        header_fields, a string? of the first line separated
5295        by the 'separator_value'
5296       
5297        data, a array (N data columns X M lines) in the file
5298        excluding the header
5299       
5300    NOTE: wont deal with columns with different lenghts and there must be
5301    no blank lines at the end.
5302    """
5303   
5304    fid = open(filename)
5305    lines = fid.readlines()
5306   
5307    fid.close()
5308   
5309    header_line = lines[0]
5310    header_fields = header_line.split(separator_value)
5311
5312    #array to store data, number in there is to allow float...
5313    #i'm sure there is a better way!
5314    data=array([],typecode=Float)
5315    data=resize(data,((len(lines)-1),len(header_fields)))
5316#    print 'number of fields',range(len(header_fields))
5317#    print 'number of lines',len(lines), shape(data)
5318#    print'data',data[1,1],header_line
5319
5320    array_number = 0
5321    line_number = 1
5322    while line_number < (len(lines)):
5323        for i in range(len(header_fields)): 
5324            #this get line below the header, explaining the +1
5325            #and also the line_number can be used as the array index
5326            fields = lines[line_number].split(separator_value)
5327            #assign to array
5328            data[array_number,i] = float(fields[i])
5329           
5330        line_number = line_number +1
5331        array_number = array_number +1
5332       
5333    return header_fields, data
5334
5335def store_parameters(verbose=False,**kwargs):
5336    """
5337    Store "kwargs" into a temp csv file, if "completed" is a kwargs csv file is
5338    kwargs[file_name] else it is kwargs[output_dir] + details_temp.csv
5339   
5340    Must have a file_name keyword arg, this is what is writing to.
5341    might be a better way to do this using CSV module Writer and writeDict
5342   
5343    writes file to "output_dir" unless "completed" is in kwargs, then
5344    it writes to "file_name" kwargs
5345
5346    """
5347    import types
5348#    import os
5349   
5350    # Check that kwargs is a dictionary
5351    if type(kwargs) != types.DictType:
5352        raise TypeError
5353   
5354    #is completed is kwargs?
5355    try:
5356        kwargs['completed']
5357        completed=True
5358    except:
5359        completed=False
5360 
5361    #get file name and removes from dict and assert that a file_name exists
5362    if completed:
5363        try:
5364            file = str(kwargs['file_name'])
5365        except:
5366            raise 'kwargs must have file_name'
5367    else:
5368        #write temp file in output directory
5369        try:
5370            file = str(kwargs['output_dir'])+'detail_temp.csv'
5371        except:
5372            raise 'kwargs must have output_dir'
5373       
5374    #extracts the header info and the new line info
5375    line=''
5376    header=''
5377    count=0
5378    keys = kwargs.keys()
5379    keys.sort()
5380   
5381    #used the sorted keys to create the header and line data
5382    for k in keys:
5383#        print "%s = %s" %(k, kwargs[k])
5384        header = header+str(k)
5385        line = line+str(kwargs[k])
5386        count+=1
5387        if count <len(kwargs):
5388            header = header+','
5389            line = line+','
5390    header+='\n'
5391    line+='\n'
5392
5393    # checks the header info, if the same, then write, if not create a new file
5394    #try to open!
5395    try:
5396        fid = open(file,"r")
5397        file_header=fid.readline()
5398        fid.close()
5399        if verbose: print 'read file header %s' %file_header
5400       
5401    except:
5402        msg = 'try to create new file',file
5403        if verbose: print msg
5404        #tries to open file, maybe directory is bad
5405        try:
5406            fid = open(file,"w")
5407            fid.write(header)
5408            fid.close()
5409            file_header=header
5410        except:
5411            msg = 'cannot create new file',file
5412            raise msg
5413           
5414    #if header is same or this is a new file
5415    if file_header==str(header):
5416        fid=open(file,"a")
5417        #write new line
5418        fid.write(line)
5419        fid.close()
5420    else:
5421        #backup plan,
5422        # if header is different and has completed will append info to
5423        #end of details_temp.cvs file in output directory
5424        file = str(kwargs['output_dir'])+'detail_temp.csv'
5425        fid=open(file,"a")
5426        fid.write(header)
5427        fid.write(line)
5428        fid.close()
5429        if verbose: print 'file',file_header.strip('\n')
5430        if verbose: print 'head',header.strip('\n')
5431        if file_header.strip('\n')==str(header): print 'they equal'
5432        msg = 'WARNING: File header does not match input info, the input variables have changed, suggest to change file name'
5433        print msg
5434
5435
5436
5437
5438
5439def get_maximum_inundation_elevation(filename,
5440                                     polygon=None,
5441                                     time_interval=None,
5442                                     verbose=False):
5443   
5444    """Return highest elevation where depth > 0
5445   
5446    Usage:
5447    max_runup = get_maximum_inundation_elevation(filename,
5448                                                 polygon=None,
5449                                                 time_interval=None,
5450                                                 verbose=False)
5451
5452    filename is a NetCDF sww file containing ANUGA model output.   
5453    Optional arguments polygon and time_interval restricts the maximum
5454    runup calculation
5455    to a points that lie within the specified polygon and time interval.
5456
5457    If no inundation is found within polygon and time_interval the return value
5458    is None signifying "No Runup" or "Everything is dry".
5459
5460    See general function get_maximum_inundation_data for details.
5461   
5462    """
5463   
5464    runup, _ = get_maximum_inundation_data(filename,
5465                                           polygon=polygon,
5466                                           time_interval=time_interval,
5467                                           verbose=verbose)
5468    return runup
5469
5470
5471
5472
5473def get_maximum_inundation_location(filename,
5474                                    polygon=None,
5475                                    time_interval=None,
5476                                    verbose=False):
5477    """Return location of highest elevation where h > 0
5478   
5479   
5480    Usage:
5481    max_runup_location = get_maximum_inundation_location(filename,
5482                                                         polygon=None,
5483                                                         time_interval=None,
5484                                                         verbose=False)
5485
5486    filename is a NetCDF sww file containing ANUGA model output.
5487    Optional arguments polygon and time_interval restricts the maximum
5488    runup calculation
5489    to a points that lie within the specified polygon and time interval.
5490
5491    If no inundation is found within polygon and time_interval the return value
5492    is None signifying "No Runup" or "Everything is dry".
5493
5494    See general function get_maximum_inundation_data for details.
5495    """
5496   
5497    _, max_loc = get_maximum_inundation_data(filename,
5498                                             polygon=polygon,
5499                                             time_interval=time_interval,
5500                                             verbose=verbose)
5501    return max_loc
5502   
5503
5504
5505def get_maximum_inundation_data(filename, polygon=None, time_interval=None,
5506                                use_centroid_values=False,
5507                                verbose=False):
5508    """Compute maximum run up height from sww file.
5509
5510
5511    Usage:
5512    runup, location = get_maximum_inundation_data(filename,
5513                                                  polygon=None,
5514                                                  time_interval=None,
5515                                                  verbose=False)
5516   
5517
5518    Algorithm is as in get_maximum_inundation_elevation from
5519    shallow_water_domain
5520    except that this function works with the sww file and computes the maximal
5521    runup height over multiple timesteps.
5522   
5523    Optional arguments polygon and time_interval restricts the
5524    maximum runup calculation
5525    to a points that lie within the specified polygon and time interval.
5526    Polygon is
5527    assumed to be in (absolute) UTM coordinates in the same zone as domain.
5528
5529    If no inundation is found within polygon and time_interval the return value
5530    is None signifying "No Runup" or "Everything is dry".
5531    """
5532
5533    # We are using nodal values here as that is what is stored in sww files.
5534
5535    # Water depth below which it is considered to be 0 in the model
5536    # FIXME (Ole): Allow this to be specified as a keyword argument as well
5537
5538    from anuga.utilities.polygon import inside_polygon   
5539    from anuga.config import minimum_allowed_height
5540    from Scientific.IO.NetCDF import NetCDFFile
5541
5542    dir, base = os.path.split(filename)
5543           
5544    iterate_over = get_all_swwfiles(dir,base)
5545   
5546    # Read sww file
5547    if verbose: 
5548        print 'Reading from %s' %filename
5549        # FIXME: Use general swwstats (when done)
5550   
5551    maximal_runup = None
5552    maximal_runup_location = None
5553   
5554    for file, swwfile in enumerate (iterate_over):
5555       
5556        # Read sww file
5557        filename = join(dir,swwfile+'.sww')
5558       
5559        if verbose: 
5560            print 'Reading from %s' %filename
5561            # FIXME: Use general swwstats (when done)
5562               
5563        fid = NetCDFFile(filename)
5564   
5565        # Get geo_reference
5566        # sww files don't have to have a geo_ref
5567        try:
5568            geo_reference = Geo_reference(NetCDFObject=fid)
5569        except AttributeError, e:
5570            geo_reference = Geo_reference() # Default georef object
5571           
5572        xllcorner = geo_reference.get_xllcorner()
5573        yllcorner = geo_reference.get_yllcorner()
5574        zone = geo_reference.get_zone()
5575       
5576        # Get extent
5577        volumes = fid.variables['volumes'][:]   
5578        x = fid.variables['x'][:] + xllcorner
5579        y = fid.variables['y'][:] + yllcorner
5580   
5581   
5582        # Get the relevant quantities
5583        elevation = fid.variables['elevation'][:] 
5584        stage = fid.variables['stage'][:]
5585   
5586   
5587        # Here's where one could convert nodal information to centroid
5588        # information
5589        # but is probably something we need to write in C.
5590        # Here's a Python thought which is NOT finished!!!
5591        if use_centroid_values is True:
5592            x = get_centroid_values(x, volumes)
5593            y = get_centroid_values(y, volumes)   
5594            elevation = get_centroid_values(elevation, volumes)   
5595   
5596   
5597        # Spatial restriction
5598        if polygon is not None:
5599            msg = 'polygon must be a sequence of points.'
5600            assert len(polygon[0]) == 2, msg
5601            # FIXME (Ole): Make a generic polygon input check in polygon.py
5602            # and call it here
5603           
5604            points = concatenate((x[:,NewAxis], y[:,NewAxis]), axis=1)
5605   
5606            point_indices = inside_polygon(points, polygon)
5607   
5608            # Restrict quantities to polygon
5609            elevation = take(elevation, point_indices)
5610            stage = take(stage, point_indices, axis=1)
5611   
5612            # Get info for location of maximal runup
5613            points_in_polygon = take(points, point_indices)
5614            x = points_in_polygon[:,0]
5615            y = points_in_polygon[:,1]       
5616        else:
5617            # Take all points
5618            point_indices = arange(len(x))
5619           
5620   
5621        # Temporal restriction
5622        time = fid.variables['time'][:]
5623        all_timeindices = arange(len(time))       
5624        if time_interval is not None:
5625           
5626            msg = 'time_interval must be a sequence of length 2.'
5627            assert len(time_interval) == 2, msg
5628            msg = 'time_interval %s must not be decreasing.' %(time_interval)
5629            assert time_interval[1] >= time_interval[0], msg
5630           
5631            msg = 'Specified time interval [%.8f:%.8f]' %tuple(time_interval)
5632            msg += ' must does not match model time interval: [%.8f, %.8f]\n'\
5633                   %(time[0], time[-1])
5634            if time_interval[1] < time[0]: raise ValueError(msg)
5635            if time_interval[0] > time[-1]: raise ValueError(msg)
5636   
5637            # Take time indices corresponding to interval (& is bitwise AND)
5638            timesteps = compress((time_interval[0] <= time) & (time <= time_interval[1]),
5639                                 all_timeindices)
5640   
5641   
5642            msg = 'time_interval %s did not include any model timesteps.' %(time_interval)       
5643            assert not alltrue(timesteps == 0), msg
5644   
5645   
5646        else:
5647            # Take them all
5648            timesteps = all_timeindices
5649       
5650   
5651        fid.close()
5652   
5653        # Compute maximal runup for each timestep
5654        #maximal_runup = None
5655        #maximal_runup_location = None
5656        #maximal_runups = [None]
5657        #maximal_runup_locations = [None]
5658       
5659        for i in timesteps:
5660            if use_centroid_values is True:
5661                stage_i = get_centroid_values(stage[i,:], volumes)   
5662            else:
5663                stage_i = stage[i,:]
5664               
5665            depth = stage_i  - elevation
5666       
5667            # Get wet nodes i.e. nodes with depth>0 within given region and timesteps
5668            wet_nodes = compress(depth > minimum_allowed_height, arange(len(depth)))
5669   
5670            if alltrue(wet_nodes == 0):
5671                runup = None
5672            else:   
5673                # Find maximum elevation among wet nodes
5674                wet_elevation = take(elevation, wet_nodes)
5675   
5676                runup_index = argmax(wet_elevation)
5677                runup = max(wet_elevation)
5678                assert wet_elevation[runup_index] == runup # Must always be True
5679            if runup > maximal_runup:
5680                maximal_runup = runup      # This works even if maximal_runups is None
5681                #print "NEW RUNUP",runup
5682   
5683                # Record location
5684                wet_x = take(x, wet_nodes)
5685                wet_y = take(y, wet_nodes)           
5686                maximal_runup_location = [wet_x[runup_index], wet_y[runup_index]]
5687   
5688    #print 'maximal_runup, maximal_runup_location',maximal_runup, maximal_runup_location
5689    return maximal_runup, maximal_runup_location
5690
5691def get_all_swwfiles(look_in_dir='',base_name='',verbose=False):
5692    '''
5693    Finds all the sww files in a "look_in_dir" which contains a "base_name".
5694    will accept base_name with or without the extension ".sww"
5695   
5696    Returns: a list of strings
5697       
5698    Usage:     iterate_over = get_all_swwfiles(dir, name)
5699    then
5700               for swwfile in iterate_over:
5701                   do stuff
5702                   
5703    Check "export_grids" and "get_maximum_inundation_data" for examples
5704    '''
5705   
5706    #plus tests the extension
5707    name, extension = os.path.splitext(base_name)
5708
5709    if extension <>'' and extension <> '.sww':
5710        msg = msg='file %s %s must be an NetCDF sww file!'%(base_name,extension)
5711        raise IOError, msg
5712
5713    if look_in_dir == "":
5714        look_in_dir = "." # Unix compatibility
5715   
5716    dir_ls = os.listdir(look_in_dir)
5717    #print 'dir_ls',dir_ls, base
5718    iterate_over = [x[:-4] for x in dir_ls if name in x and x[-4:] == '.sww']
5719    if len(iterate_over) == 0:
5720        msg = 'No files of the base name %s'\
5721              %(name)
5722        raise IOError, msg
5723    if verbose: print 'iterate over %s' %(iterate_over)
5724
5725    return iterate_over
5726
5727def get_all_files_with_extension(look_in_dir='',base_name='',extension='.sww',verbose=False):
5728    '''
5729    Finds all the sww files in a "look_in_dir" which contains a "base_name".
5730   
5731   
5732    Returns: a list of strings
5733       
5734    Usage:     iterate_over = get_all_swwfiles(dir, name)
5735    then
5736               for swwfile in iterate_over:
5737                   do stuff
5738                   
5739    Check "export_grids" and "get_maximum_inundation_data" for examples
5740    '''
5741   
5742    #plus tests the extension
5743    name, ext = os.path.splitext(base_name)
5744#    print 'look_in_dir',look_in_dir
5745
5746    if ext <>'' and ext <> extension:
5747        msg = msg='base_name %s must be an file with %s extension!'%(base_name,extension)
5748        raise IOError, msg
5749
5750    if look_in_dir == "":
5751        look_in_dir = "." # Unix compatibility
5752#    print 'look_in_dir',look_in_dir, getcwd()
5753    dir_ls = os.listdir(look_in_dir)
5754    #print 'dir_ls',dir_ls, base_name
5755    iterate_over = [x[:-4] for x in dir_ls if name in x and x[-4:] == extension]
5756    if len(iterate_over) == 0:
5757        msg = 'No files of the base name %s in %s'\
5758              %(name, look_in_dir)
5759        raise IOError, msg
5760    if verbose: print 'iterate over %s' %(iterate_over)
5761
5762    return iterate_over
5763
5764def get_all_directories_with_name(look_in_dir='',base_name='',verbose=False):
5765    '''
5766    Finds all the sww files in a "look_in_dir" which contains a "base_name".
5767   
5768   
5769    Returns: a list of strings
5770       
5771    Usage:     iterate_over = get_all_swwfiles(dir, name)
5772    then
5773               for swwfile in iterate_over:
5774                   do stuff
5775                   
5776    Check "export_grids" and "get_maximum_inundation_data" for examples
5777    '''
5778   
5779    #plus tests the extension
5780
5781    if look_in_dir == "":
5782        look_in_dir = "." # Unix compatibility
5783#    print 'look_in_dir',look_in_dir
5784    dir_ls = os.listdir(look_in_dir)
5785#    print 'dir_ls',dir_ls
5786    iterate_over = [x for x in dir_ls if base_name in x]
5787    if len(iterate_over) == 0:
5788        msg = 'No files of the base name %s'\
5789              %(name)
5790        raise IOError, msg
5791    if verbose: print 'iterate over %s' %(iterate_over)
5792
5793    return iterate_over
5794
5795
5796#-------------------------------------------------------------
5797if __name__ == "__main__":
5798    #setting umask from config to force permissions for all files and directories
5799    # created to the same. (it was noticed the "mpirun" doesn't honour the umask
5800    # set in your .bashrc etc file)
5801    from config import umask
5802    import os 
5803    os.umask(umask)
5804
Note: See TracBrowser for help on using the repository browser.