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

Last change on this file since 4636 was 4636, checked in by nick, 17 years ago

get_all_files_with_extension and get_all_directories_with_name added. They are very useful in finding files and directories with certain names in them

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