source: anuga_core/source/anuga/shallow_water/data_manager_joaquims_patch.py @ 7736

Last change on this file since 7736 was 7711, checked in by hudson, 14 years ago

Refactored geometry classes to live in their own folder.

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