source: nappy/trunk/cdms2na.py @ 3005

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/nappy/trunk/cdms2na.py@3337
Revision 3005, 35.5 KB checked in by astephen, 12 years ago (diff)

Consolidation of titania version under DX with proper source in trunk of SVN.

  • Property svn:eol-style set to native
  • Property svn:executable set to *
  • Property svn:keywords set to Author Date Id Revision
Line 
1#!/usr/bin/env python
2#   Copyright (C) 2004 CCLRC & NERC( Natural Environment Research Council ).
3#   This software may be distributed under the terms of the
4#   Q Public License, version 1.0 or later. http://ndg.nerc.ac.uk/public_docs/QPublic_license.txt
5
6helpMessage="""
7
8cdms2na.py
9==========
10
11Converts a CdmsFile object into one a NASA Ames file.
12
13Usage
14=====
15
16    cdms2na.py [<options>] -i <infilename> -o <outfilename>
17
18Where
19-----
20
21    infilename  - name of input file (NetCDF).
22    outfilename - name of output file (NASA Ames).
23    options     - list of options.
24   
25"""
26
27# Imports from python standard library
28import sys, os, time, string, fileinput, re
29if len(sys.argv)>0:
30    sys.path.append(os.path.join("..", ".."))
31    sys.path.append("..")
32
33# Import cdat modules
34if sys.platform.find("win")>-1:
35    pass
36else:
37    import cdms, cdtime, Numeric
38    # Define cdms variables
39    cdms.setAutoBounds("off")
40
41# Import nappy modules
42import cdmsMap
43import version
44import general
45import localRules
46from naCore import *
47from naError import *
48
49
50# Set up functions
51def compareAxes(ax1, ax2):
52    """
53    Takes 2 cmds axis objects returning 1 if they are essentially
54    the same and 0 if not.
55    """
56    for axtype in ("time", "level", "latitude", "longitude"):
57        if cdms.axisMatches(ax1, axtype) and not cdms.axisMatches(ax2, axtype):
58            return 0
59
60    # Check ids
61    if ax1.id!=ax2.id: return 0
62    # Check lengths
63    if len(ax1)!=len(ax2): return 0
64    # Check values
65    if ax1._data_!=ax2._data_: return 0
66    # Check units
67    if ax1.units!=ax2.units: return 0
68    # OK, I think they are the same axis!
69    return 1
70
71
72def compareVariables(var1, var2):
73    """
74    Compares two cdms variables to see if they are defined on identical
75    axes. Returns 1 if true, 0 if not.
76    """
77    try:
78        for i in range(len(var1.getAxisList())):
79            ax1=var1.getAxis(i)
80            ax2=var2.getAxis(i)       
81            if compareAxes(ax1, ax2)==0:
82                return 0
83    except:
84        return 0
85    return 1
86
87
88def isAuxAndVar(avar, var):
89    """
90    Compares two cdms variables and returns true if the first dimension of the
91    main variable is the only dimension the auxiliary variable is defined against.
92    """
93    if len(avar.shape)>1:
94        return 0
95    auxax=avar.getAxis(0)
96    varax=var.getAxis(0)
97    return compareAxes(auxax, varax)
98
99
100def arrayToList(array, inlist):
101    """
102    Takes an n-dimensional Numeric array and converts it to an
103    n-dimensional list object.
104    """
105    dimlist=array.shape
106    if len(dimlist[1:])>0:
107            for i in range(dimlist[0]):
108                arrayToList(inlist[i], array[i])
109    else:
110            for i in range(dimlist[0]):
111                inlist.append(array[i])
112    return inlist
113
114
115def listOfListsCreator(inlist, dimlist):
116    """
117    Creates a list of lists with dimensions defined in dimlist (a list of integers).
118    """
119    if len(dimlist[1:])>0:
120        for i in range(dimlist[0]):
121            inlist.append([])
122            listOfListsCreator(inlist[i], array, dimlist[1:])
123    return inlist
124
125
126def getBestName(var): 
127    """
128    Returns the most appropriate variable name for a NASA Ames header
129    """
130    name=None
131    if hasattr(var, "id"): name=var.id
132    if hasattr(var, "shortname"): name=var.short_name
133    if hasattr(var, "name"): name=var.name
134    if hasattr(var, "title"): name=var.title
135    if hasattr(var, "standard_name"): name=var.standard_name
136    if hasattr(var, "long_name"): name=var.long_name
137    #if hasattr(var, "standard_name"): name=var.standard_name
138   
139    if hasattr(var, "has_key"):
140        if var.has_key("id"): name=var["id"]
141        if var.has_key("short_name"): name=var["short_name"]   
142        if var.has_key("name"): name=var["name"]
143        if var.has_key("title"): name=var["title"]     
144        if var.has_key("standard_name"): name=var["standard_name"]
145        if var.has_key("long_name"): name=var["long_name"]
146        #if var.has_key("standard_name"): name=var["standard_name"]   
147   
148    if hasattr(var, "units") and not re.match("^\s+$", var.units): 
149        units=var.units.strip()
150        name="%s (%s)" % (name, units)   
151        # Do a check to see units are not duplicated
152        # [The regex below failed because it interpreted 'm**2' as
153        # regex characters]
154        #match=re.match("(.*\(%s\)\s*)\(%s\)(.*)$" % (var.units, var.units), name)
155        #if match:
156        #    name=match.groups()[0]+match.groups()[1]
157        if name.count("(%s)" % units)>1:
158            name=name.replace("(%s)" % units, "")
159            name="%s (%s)" % (name, units)
160       
161    if name[-2:]=="()": name=name[:-2]
162    return name
163
164
165def getMissingValue(var):
166    """
167    Returns the missing value or defaults to 1.E20.
168    """
169    miss=None
170    if hasattr(var, "missing_value"): miss=var.missing_value
171    if hasattr(var, "_fill_value"): miss=var._fill_value
172    # if hasattr(var, "fill_value"): miss=var.fill_value  # returns an array????
173    if hasattr(var, "_FillValue"): miss=var._FillValue
174    if miss==None:
175        try:
176            miss=var.getMissing()
177        except:
178            miss=1.E20
179    return miss
180
181
182def fixHeaderLengthNowDefunct(file):
183    """
184    Fixes length of header.
185    """
186    lines=open(file).readlines()[:200]
187    count=0
188    for line in lines:
189        count=count+1
190        if line=="###Data Section begins on the next line###\n":
191            break
192    # Now replace 1st line NLHEAD with count
193    firstline=lines[0]
194    newline="%s    %s" % (count, firstline.split()[-1])
195    for line in fileinput.input(file, inplace=1):
196        if line==firstline:
197          # write the changed line
198          sys.stdout.write(newline+"\n")
199        else:
200          # write the line unchanged
201          sys.stdout.write(line)
202
203def flatten2DTimeData(var, timevar):
204    """
205    Returns a flattened 2D array variable with a recalculated time axies.
206    """
207    import MV, cdtime
208    data=MV.ravel(var)
209    missing=var.getMissing()
210
211    timevalues=timevar._data
212    timeunit=timevar.units
213    samprate=var.getAxis(1)
214    rate=int(samprate.id[3:])
215    newTimeValues=[]
216
217    for t in timevalues:
218        for i in range(rate):
219            tvalue=t+((1./rate)*i)
220            newTimeValues.append(tvalue)
221
222    newTimeAx=cdms.createAxis(newTimeValues)
223    newTimeAx.units=timeunit
224    newTimeAx.designateTime()
225    newTimeAx.id=newTimeAx.long_name=newTimeAx.standard_name="time"
226
227    atts=var.attributes
228    newAtts={}
229    for att,value in atts.items():
230        if type(value) in (type((1,1)), type([1,2]), type(Numeric.array([1.]))) and len(value)==1:
231            value=value[0]
232        if type(value) in (type(1), type(1.), type(long(1))):
233            newAtts[att]=value
234        elif type(value)==type(""):
235            newAtts[att]=value.strip()
236
237    # Now create the variable
238    newvar=cdms.createVariable(data, id=var.id, fill_value=missing, axes=[newTimeAx], attributes=newAtts)
239    return newvar
240
241
242def modifyNADictCopy(indict, v_new, start, end, ivol, nvol):
243    """
244    Returns a copy of a dictionary with some modifications.
245    """
246    newDict={}
247    for key,value in indict.items(): 
248        if key=="X":
249            newlist=indict["X"][start:end]
250            newDict["X"]=newlist
251        elif key=="V":
252            newDict["V"]=v_new
253        elif key=="IVOL":
254            newDict["IVOL"]=ivol
255        elif key=="NVOL":
256            newDict["NVOL"]=nvol
257        else:
258            newDict[key]=value
259    return newDict
260
261
262def cdms2na(infilename, outfilenames, naVars={}, variables=None, nFilesOnly="no", 
263            rule=None, ffi="automatic", spacer="    ", floatFormat="%g", 
264            rules=None, sizeLimit=None):
265    """
266    Main conversion function that calls the appropriate classes and functions
267    to write a NASA Ames file.
268    """
269    #print infilename, outfilenames, nFilesOnly, naVars, variables
270    if type(outfilenames)==type(""): outfilenames=[outfilenames]
271   
272    allowedOverwriteMetadata=["DATE",  "RDATE", "ANAME", "MNAME",
273           "ONAME", "ORG", "SNAME", "VNAME"]
274    arrayArgs=["DATE", "RDATE", "ANAME", "VNAME"]
275    # ANAME[a] - array of 'a' x ANAME strings - aux var names
276    # DATE (array of three) - UT date at which the data within the file starts
277    # MNAME - mission name
278    # ONAME - name of originator(s)
279    # ORG - org or affiliation of originator(s)
280    # RDATE (array of three) - date of data reduction or revision
281    # SNAME - source of measurement or model output VNAME[n] - array of 'n' x
282    # VNAME strings - var names.
283    outputMessage=[]
284    msg="Reading data from: %s\n" % infilename
285    print msg
286    outputMessage.append(msg)
287    cdmsfile=cdms.open(infilename)
288    globals=cdmsfile.attributes
289   
290    vars=[]
291    if not variables:
292        variables=cdmsfile.listvariables()
293        #for var in cdmsfile.listvariables():
294            #vars.append(cdmsfile(var))   
295           
296    for variable in variables:
297        varObj=cdmsfile(variable)
298        # Deal with singleton variables
299        if not hasattr(varObj, "rank"):
300                varMetadata=cdmsfile[variable].attributes
301                varValue=varObj
302                #print varMetadata, varValue, varMetadata.keys(), varMetadata._obj_.id
303                varObj=cdms.createVariable(Numeric.array(varObj), id=getBestName(varMetadata).replace(" ", "_"), attributes=varMetadata)
304                #print varObj, dir(varObj); sys.exit()
305                varObj.value=varObj._data[0]
306                #varObj.rank=0
307               
308        #print varObj, varObj.attributes                         
309        vars.append(varObj)
310       
311    # Re-order variables if they have the attribute 'nasa_ames_var_number'
312    orderedVars=[None]*1000
313    otherVars=[]
314    for var in vars:
315        varMetadata=cdmsfile[var]
316        if hasattr(varMetadata, "nasa_ames_var_number"):
317            num=varMetadata.nasa_ames_var_number
318            orderedVars[num]=var
319        else:
320            otherVars.append(var)
321   
322    vars=[]
323    for var in orderedVars:
324        if var!=None:
325            vars.append(var)
326           
327    vars=vars+otherVars
328   
329    builder=CdmsToNABuilder(vars, globals, rule=rule, cdmsfile=cdmsfile)
330    #print builder.naDict["X"]
331    builtNADicts=[[builder.naDict, builder.varIDs]]
332    if builder.varIDs==None:
333        msg="\nNo files created after variables parsed."
334        print msg
335        outputMessage.append(msg)
336        return outputMessage
337
338    while len(builder.varBin)>0:
339        builder=CdmsToNABuilder(builder.varBin, globals, rule=rule, cdmsfile=cdmsfile)
340        outputMessage=outputMessage+builder.outputMessage
341        if builder.varIDs!=None:  builtNADicts.append([builder.naDict, builder.varIDs])
342
343    # Return only filenames if only want to know them now.
344    ncount=1
345    fileNames=[]
346    if nFilesOnly=="yes": 
347        for i in builtNADicts:
348            if len(builtNADicts)==1:
349                suffix=""
350            else:
351                suffix="_%s" % ncount
352            nameparts=outfilenames[0].split(".")   
353            newname=(".".join(nameparts[:-1]))+suffix+"."+nameparts[-1]
354            fileNames.append(newname)
355        ncount=ncount+1
356           
357        return fileNames
358               
359    msg="\n%s files to write" % len(builtNADicts)
360    print msg
361    outputMessage.append(msg)
362
363    count=1
364    ncount=1
365    for i in builtNADicts:
366        if len(outfilenames)==1:
367            if len(builtNADicts)==1:
368                suffix=""
369            else:
370                suffix="_%s" % ncount
371            nameparts=outfilenames[0].split(".")   
372            newname=(".".join(nameparts[:-1]))+suffix+"."+nameparts[-1]
373        else:
374            newname=outfilenames[count-1]
375 
376        msg="\nWriting output NASA Ames file: %s" % newname
377        print msg
378        outputMessage.append(msg)
379       
380        builtNADict=i[0]
381        for key in naVars.keys():
382            if key in allowedOverwriteMetadata:
383           
384                if key in arrayArgs:
385                    newItem=naVars[key].split()           
386                else:
387                    newItem=naVars[key]
388                                   
389                if newItem!=builtNADict[key]:
390                    builtNADict[key]=newItem
391                    msg="Metadata overwritten in output file: '%s' is now '%s'" % (key, builtNADict[key])
392                    print msg
393                    outputMessage.append(msg)
394       
395        fileList=[]
396        # Cope with size limits if specified and FFI is 1001
397        if sizeLimit and (builtNADict["FFI"]==1001 and len(builtNADict["V"][0])>sizeLimit):
398            varList=builtNADict["V"]
399            arrayLength=len(varList[0])
400            nvolInfo=divmod(arrayLength, sizeLimit)
401            nvol=nvolInfo[0]
402            if nvolInfo[1]>0: nvol=nvol+1
403            start=0
404            letterCount=0
405            ivol=0
406            while start<arrayLength:
407                ivol=ivol+1
408                end=start+sizeLimit
409                if end>arrayLength:
410                    end=arrayLength
411                currentBlock=[]
412                # Write new V array
413                for v in varList:
414                    currentBlock.append(v[start:end])
415
416                # Adjust X accordingly
417                NADictCopy=modifyNADictCopy(builtNADict, currentBlock, start, end, ivol, nvol)
418               
419                # Write data to output file
420                newnamePlusLetter="%s-%.3d.na" % (newname[:-3], ivol)
421                fileList.append(newnamePlusLetter)
422                general.openNAFile(newnamePlusLetter, 'w', NADictCopy, spacer=spacer, floatFormat=floatFormat)
423                msg="\nOutput files split on size limit: %s\nFilename used: %s" % (sizeLimit, newnamePlusLetter)
424                print msg
425                outputMessage.append(msg)
426                letterCount=letterCount+1
427                start=end
428
429
430        else:           
431            general.openNAFile(newname, 'w', builtNADict, spacer=spacer, floatFormat=floatFormat)
432
433        msg="\nWrote the following variables:"+"\n\t"+("\n\t".join(i[1][0]))
434        print msg
435        outputMessage.append(msg)
436       
437        if len(i[1][1])>0:
438            msg="\nWrote the following auxiliary variables:"
439            msg=msg+"\n\t"+("\n\t".join(i[1][1]))       
440           
441        if len(i[1][2])>0:
442            msg="\nWrote the following Singleton variables:"
443            msg=msg+"\n\t"+("\n\t".join(i[1][2]))
444
445        if len(fileList)>0:
446            msg=msg+("\n\nNASA Ames files written successfully: \n%s" % "\n".join(fileList))
447            count=count+len(fileList)
448        else:
449            msg=msg+"\n\nNASA Ames file written successfully: %s" % newname
450            count=count+1
451        ncount=ncount+1
452
453        print msg
454        outputMessage.append(msg)
455           
456    if (count-1)==1:
457        plural=""
458    else:
459        plural="s"           
460    msg="\n%s file%s written." % ((count-1), plural)
461    print msg
462    outputMessage.append(msg)
463    return outputMessage
464
465
466class CdmsToNABuilder(NACore):
467    """
468    Class to build a NASA Ames File object from a set of
469    CDMS variables and global attributes (optional).
470    """
471   
472    def __init__(self, vars, global_attributes={}, cdmsfile=None, rule=None):
473        """
474        Sets up instance variables and calls appropriate methods to
475        generate sections of NASA Ames file object.
476        """
477        self.rule=rule
478        self.cdmsfile=cdmsfile
479        self.outputMessage=[]
480        self.naDict={}
481        self.vars=vars
482        self.varIDs=None
483        self.globals=global_attributes 
484        self.rankZeroVars=[]
485        self.rankZeroVarIDs=[]
486        (self.orderedVars, auxVars)=self.analyseVariables()
487        if self.orderedVars==None:
488            self.varBin=[]
489        else:
490            #print "NAMELISTS:", [var.id for var in self.orderedVars],[var.id for var in auxVars]
491            self.varIDs=[[var.id for var in self.orderedVars],[var.id for var in auxVars], self.rankZeroVarIDs]
492       
493            self.naDict["NLHEAD"]="-999"
494       
495            #print [var.id for var in self.orderedVars]
496            #print [var.rank() for var in self.orderedVars]     
497            self.defineNAVars(self.orderedVars)
498            self.defineNAAuxVars(auxVars)
499            self.defineNAGlobals()
500            self.defineNAComments()
501            self.defineGeneralHeader()
502            # Quick fudge
503            if self.naDict["FFI"]==1001: self.naDict["X"]=self.naDict["X"][0]
504
505
506    def analyseVariables(self):
507        """
508        Method to examine the content of CDMS variables to return
509        a tuple of two lists containing variables and auxiliary variables
510        for the NASA Ames file object.
511        Variables not compatible with the first file are binned to be used next.
512        """
513        # Need to group the variables together in bins
514        self.varBin=[]
515        # Get largest ranked variable as the one we use as standard
516        highrank=-1
517        bestVar=None
518        count=0
519        for var in self.vars:
520            msg="Analysing: %s" % var.id
521            print msg
522            self.outputMessage.append(msg)
523            count=count+1
524
525            # get rank
526            rank=var.rank()
527
528            # Deal with specific datasets with special rules
529            if self.rule!=None and self.rule[0]=="aircraft":
530                var=self._useLocalRule(var, self.rule)
531                if type(var)==type(None): 
532                    continue 
533                rank=1
534
535            # Deal with singleton variables
536            if rank==0: 
537                self.rankZeroVars.append(var)
538                self.rankZeroVarIDs.append(var.id)
539                continue
540           
541            if rank>highrank:
542                highrank=rank
543                bestVar=var
544                bestVarIndex=count
545            elif rank==highrank:
546                if len(var.flat)>len(bestVar.flat):
547                    bestVar=var
548                    bestVarIndex=count
549       
550        if len(self.rankZeroVars)==len(self.vars):  return (None, None)
551        if not bestVar: 
552            print "No variables produced"
553            return (None, None)
554
555        vars4NA=[bestVar]
556        auxVars4NA=[]
557        shape=bestVar.shape
558        ndims=len(shape)
559        self.naDict["NIV"]=ndims
560
561        # Work out which File Format Index is appropriate
562        if ndims in (2,3,4):
563            self.naDict["FFI"]=10+(ndims*1000)
564        elif ndims>4:
565            raise "Cannot write variables defined against greater than 4 axes in NASA Ames format."
566        else:
567            if len(auxVars4NA)>0 or (self.naDict.has_key("NAUXV") and self.naDict["NAUXV"]>0):
568                self.naDict["FFI"]=1010
569            else:
570                self.naDict["FFI"]=1001
571        #print self.naDict["FFI"]
572        axes=bestVar.getAxisList()
573       
574        # Get other variable info
575        #print [v.id for v in self.vars], bestVarIndex
576        #print [v.id for v in self.vars[:bestVarIndex-1]+self.vars[bestVarIndex:]]
577        for var in self.vars[:bestVarIndex-1]+self.vars[bestVarIndex:]:
578            # Deal with specific datasets with special rules
579            if self.rule!=None and self.rule[0]=="aircraft":
580                if var.rank()==2:
581                    var=self._useLocalRule(var, self.rule)
582                    if type(var)==type(None): continue
583
584            #print self.rankZeroVars
585            #for rzv in self.rankZeroVars: 
586            #    if var.id==rzv.id and var[0]==rzv[0]: continue
587            #print [v.id for v in self.rankZeroVars]
588            if var.id in self.rankZeroVarIDs: continue
589            #print var.id, ndims, shape, len(var.shape), var.shape
590            if len(var.shape)!=ndims or var.shape!=shape: 
591                # Could it be an auxiliary variable
592                if len(var.shape)!=1: 
593                    self.varBin.append(var)
594                    continue
595                caxis=var.getAxis(0)
596                if compareAxes(axes[0], caxis)==0: 
597                    self.varBin.append(var)
598                    continue
599                # I think it is an auxiliary variable
600                auxVars4NA.append(var) 
601                # Also put it in var bin because auxiliary vars might be useful
602                self.varBin.append(var)
603            else:
604                caxes=var.getAxisList()
605                #print var.id, "here"
606                for i in range(ndims):           
607                    if compareAxes(axes[i], caxes[i])==0:
608                        self.varBin.append(var)
609                        continue
610                # OK, I think they are compatible
611                vars4NA.append(var)
612               
613        # Re-order if they previously came from NASA Ames files (i.e. including
614        # the attribute 'nasa_ames_var_number')
615        orderedVars=[None]*1000
616        otherVars=[]
617        for var in vars4NA:
618            if hasattr(var, "nasa_ames_var_number"):
619                orderedVars[var.nasa_ames_var_number[0]]=var
620            else:
621                otherVars.append(var)
622        # Remake vars4NA now in order
623        vars4NA=[]
624        for var in orderedVars:
625            if var!=None: vars4NA.append(var)
626        vars4NA=vars4NA+otherVars
627
628        # Now re-order the Auxiliary variables if they previously came from NASA
629        # Ames files (i.e. including the attribute 'nasa_ames_aux_var_number')
630
631        orderedAuxVars=[None]*1000
632        otherAuxVars=[]
633        for var in auxVars4NA:
634            if hasattr(var, "nasa_ames_aux_var_number"):
635                orderedAuxVars[var.nasa_ames_aux_var_number[0]]=var
636            else:
637                otherAuxVars.append(var)
638        # Remake auxVars4NA now in order
639        auxVars4NA=[]
640        for var in orderedAuxVars:
641            if var!=None: auxVars4NA.append(var)
642        auxVars4NA=auxVars4NA+otherAuxVars     
643        return (vars4NA, auxVars4NA)
644
645
646    def defineNAVars(self, vars):
647        """
648        Method to define NASA Ames file object variables and their
649        associated metadata.
650        """
651        self.naDict["NV"]=len(vars)
652        self.naDict["VNAME"]=[]
653        self.naDict["VMISS"]=[]
654        self.naDict["VSCAL"]=[]
655        self.naDict["V"]=[]
656        for var in vars:
657            name=getBestName(var)
658            self.naDict["VNAME"].append(name)
659            miss=getMissingValue(var)
660            if type(miss) not in (float, int, long):  miss=miss[0]
661            self.naDict["VMISS"].append(miss)
662            #print self.naDict["VMISS"]
663            self.naDict["VSCAL"].append(1)
664            # AND THE ARRAY
665            # Populate the variable list 
666            ######## NOTE - might not have to do this #####
667            ######## It  might handle writing from a Numeric array ########
668            self.naDict["V"].append(var._data)
669            #listOfListsCreator(inlist, var.shape)
670            #arrayToList(var, inlist)
671
672            if not self.naDict.has_key("X"):
673                self.naDict["NXDEF"]=[]
674                self.naDict["NX"]=[]
675                # Create independent variable information
676                #print var.id, var.getAxis(0)
677                self.ax0=var.getAxis(0)
678                self.naDict["X"]=[list(self.ax0._data_)]
679                self.naDict["XNAME"]=[getBestName(self.ax0)]
680                if len(self.ax0)==1:
681                    self.naDict["DX"]=[0]
682                else:
683                    incr=self.ax0[1]-self.ax0[0]
684                    # Set default increment as gap between first two
685                    self.naDict["DX"]=[incr]
686                    # Now overwrite it as zero if non-uniform interval in axis
687                    for i in range(1, len(self.ax0)):
688                        if (self.ax0[i]-self.ax0[i-1])!=incr:
689                            self.naDict["DX"]=[0]
690                            break
691
692                # Now sort the rest of the axes
693                for axis in var.getAxisList()[1:]:
694                    self.getAxisDefinition(axis)
695
696
697    def defineNAAuxVars(self, auxVars):
698        """
699        Method to define NASA Ames file object auxiliary variables and their
700        associated metadata.
701        """
702        self.naDict["NAUXV"]=len(auxVars)
703        self.naDict["ANAME"]=[]
704        self.naDict["AMISS"]=[]
705        self.naDict["ASCAL"]=[]
706        self.naDict["A"]=[]
707        for var in auxVars:
708            name=getBestName(var)
709            self.naDict["ANAME"].append(name)
710            miss=getMissingValue(var)
711            if type(miss)!=float:  miss=miss[0]
712            self.naDict["AMISS"].append(miss)
713            self.naDict["ASCAL"].append(1)
714            # AND THE ARRAY
715            # Populate the variable list 
716            ######## NOTE - might not have to do this #####
717            ######## It  might handle writing from a Numeric array ########
718            self.naDict["A"].append(var._data)
719            #listOfListsCreator(inlist, var.shape)
720            #arrayToList(var, inlist)     
721
722
723    def getAxisDefinition(self, axis):
724        """
725        Method to create the appropriate NASA Ames file object
726        items associated with an axis (independent variable in
727        NASA Ames).
728        """
729        length=len(axis)
730        self.naDict["NX"].append(length)
731        self.naDict["XNAME"].append(getBestName(axis))
732        # If only one item in axis values
733        if length<2:
734            self.naDict["DX"].append(0)
735            self.naDict["NXDEF"].append(length)
736            self.naDict["X"].append(list(axis._data_))       
737            return
738   
739        incr=axis[1]-axis[0]
740        for i in range(1, length):
741            if (axis[i]-axis[i-1])!=incr:
742                self.naDict["DX"].append(0)
743                self.naDict["NXDEF"].append(length)
744                self.naDict["X"].append(list(axis._data_))
745                break
746        else:
747            maxLength=length
748            if length>3: maxLength=3
749            self.naDict["DX"].append(incr)
750            self.naDict["NXDEF"].append(maxLength)
751            self.naDict["X"].append(axis[:maxLength])
752        return
753
754
755    def defineNAGlobals(self):
756        """
757        Maps CDMS (NetCDF) global attributes into NASA Ames Header fields.
758        """
759        # Get the global mapping dictionary
760        globalmap=cdmsMap.toNA
761        # Check if we should add to it with locally set rules
762        locGlobs=localRules.localGlobalAttributes
763        for att in locGlobs.keys():
764            if not globalmap.has_key(att):
765                globalmap[key]=locGlobs[key]
766
767        self.extra_comments=[[],[],[]]  # Normal comments, special comments, other comments
768        conventionOrReferenceComments=[]
769        for key in self.globals.keys():
770            if key!="first_valid_date_of_data" and type(self.globals[key]) not in (str, float, int): continue
771            if key in globalmap.keys():
772                if key=="history":
773                    timestring=time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(time.time()))
774                    history="History:\t%s - Converted to NASA Ames format using nappy-%s.\n\t%s" % (timestring, version.version, self.globals[key])
775                    history=history.split("\n") 
776                    self.history=[]
777                    for h in history:
778                        if h[:8]!="History:" and h[:1]!="\t": h="\t"+h
779                        self.history.append(h) 
780                   
781                elif key=="institution":
782                    # If fields came from NA then extract appropriate fields.
783                    match=re.match(r"(.*)\s+\(ONAME from NASA Ames file\);\s+(.*)\s+\(ORG from NASA Ames file\)\.", self.globals[key])
784                    if match:
785                        self.naDict["ONAME"]=match.groups()[0]
786                        self.naDict["ORG"]=match.groups()[1]
787                    else:
788                        self.naDict["ONAME"]=self.globals[key]
789                        self.naDict["ORG"]=self.globals[key]               
790                   
791                    # NOte: should probably do the following search and replace on all string lines
792                    self.naDict["ONAME"]=self.naDict["ONAME"].replace("\n", "  ")
793                    self.naDict["ORG"]=self.naDict["ORG"].replace("\n", "  ")
794                                   
795                elif key=="comment":
796                    # Need to work out if they are actually comments from NASA Ames in the first place
797                    #self.ncom=[self.globals[key]]
798                    comLines=self.globals[key].split("\n")
799                    normComms=[]
800                    normCommFlag=None
801                    specComms=[]
802                    specCommFlag=None
803                    for line in comLines:
804                        if line.find("###NASA Ames Special Comments follow###")>-1:
805                            specCommFlag=1
806                        elif line.find("###NASA Ames Special Comments end###")>-1:
807                            specCommFlag=None
808                        elif line.find("###NASA Ames Normal Comments follow###")>-1:
809                            normCommFlag=1
810                        elif line.find("###NASA Ames Normal Comments end###")>-1:
811                            normCommFlag=None   
812                        elif specCommFlag==1:
813                            specComms.append(line)
814                        elif normCommFlag==1:
815                            normComms.append(line)
816                        elif line.find("###Data Section begins on the next line###")>-1:
817                            pass
818                        else:
819                            normComms.append(line)         
820                   
821                    self.extra_comments=[specComms, normComms, []]                 
822                                   
823                elif key=="first_valid_date_of_data":
824                    self.naDict["DATE"]=self.globals[key]
825               
826                elif key in ("Conventions", "references"):
827                    #conventionOrReferenceComments.append("%s:   %s" % (key, self.globals[key]))
828                    self.extra_comments[2].append("%s:   %s" % (key, self.globals[key]))
829                else:
830                    self.naDict[globalmap[key]]=self.globals[key]
831            else:
832                self.extra_comments[2].append("%s:   %s" % (key, self.globals[key]))
833        #self.extra_comments
834        return
835
836
837    def defineNAComments(self, normal_comments=[], special_comments=[]):
838        """
839        Defines the Special and Normal comments sections in the NASA Ames file
840        object - including information gathered from the defineNAGlobals method.
841        """
842       
843        if hasattr(self, "ncom"):  normal_comments=self.ncom+normal_comments
844        NCOM=[]
845        for ncom in normal_comments:
846            NCOM.append(ncom)
847        if len(NCOM)>0:   NCOM.append("")
848       
849        if len(self.extra_comments[2])>0:
850            for excom in self.extra_comments[2]:
851                NCOM.append(excom)
852       
853        if len(self.extra_comments[1])>0: 
854            NCOM.append("Additional Global Attributes defined in the source file and not translated elsewhere:")
855            for excom in self.extra_comments[1]:
856                NCOM.append(excom)
857
858        if hasattr(self, "history"):
859            for h in self.history:
860                NCOM.append(h)
861       
862        if len(NCOM)>0:
863            NCOM.insert(0, "###NASA Ames Normal Comments follow###")
864            NCOM.append("")
865            NCOM.append("###NASA Ames Normal Comments end###")
866        NCOM.append("###Data Section begins on the next line###")
867
868        specCommentsFlag=None
869        SCOM=[]
870        special_comments=self.extra_comments[0]
871        if len(special_comments)>0: 
872            SCOM=["###NASA Ames Special Comments follow###"]
873            specCommentsFlag=1
874        for scom in special_comments:
875            SCOM.append(scom)
876
877
878        #used_var_atts=("name", "long_name", "standard_name", "id",
879            #    "missing_value", "fill_value", "units",
880                #"nasa_ames_var_number", "nasa_ames_aux_var_number")
881        used_var_atts=("id",  "missing_value", "fill_value", "units", 
882                   "nasa_ames_var_number", "nasa_ames_aux_var_number")
883        varCommentsFlag=None
884
885        # Create a string for the Special comments to hold rank-zero vars
886        rankZeroVarsString=[]
887        for var in self.rankZeroVars:
888            rankZeroVarsString.append("\tVariable %s: %s" % (var.id, getBestName(var)))
889            for att in var.attributes.keys():
890                value=var.attributes[att]
891                if type(value) in (str, float, int):
892                    rankZeroVarsString.append("\t\t%s = %s" % (att, var.attributes[att]))
893            #print "VALUES", dir(var), var._data ; rankZeroVarsString.append("\t\tvalue = %s" % var._data)
894       
895        if len(rankZeroVarsString)>0:
896            rankZeroVarsString.insert(0, "###Singleton Variables defined in the source file follow###")
897            rankZeroVarsString.append("###Singleton Variables defined in the source file end###")
898
899        for var in self.orderedVars:
900            varflag="unused"
901            name=getBestName(var)
902            for scom,value in var.attributes.items():
903                if type(value) in (type([]), type(Numeric.array([0]))) and len(value)==1:
904                    value=value[0]
905                if type(value) in (str, float, int) and scom not in used_var_atts:
906                    if varflag=="unused":
907                        if varCommentsFlag==None:
908                            varCommentsFlag=1
909                            if specCommentsFlag==None:
910                                SCOM=["###NASA Ames Special Comments follow###"]+rankZeroVarsString
911                            SCOM.append("Additional Variable Attributes defined in the source file and not translated elsewhere:")
912                            SCOM.append("###Variable attributes from source (NetCDF) file follow###")
913                        varflag="using" 
914                        SCOM.append("\tVariable %s: %s" % (var.id, name))
915                    SCOM.append("\t\t%s = %s" % (scom, value))
916
917        if varCommentsFlag==1:  SCOM.append("###Variable attributes from source (NetCDF) file end###")
918        if specCommentsFlag==1:
919            SCOM.append("###NASA Ames Special Comments end###")
920
921        """used_var_atts=("name", "long_name", "standard_name", "id", "missing_value", "fill_value", "units")
922        for var in self.vars:
923            for scom,value in var.attributes.items():
924                name=getBestName(var)
925                if type(value) in (str, float, int) and scom not in used_var_atts:
926                    SCOM.append("\t%s: %s - %s" % (name, scom, value))"""
927
928        # Strip out empty lines (or returns)
929        NCOM_cleaned=[]
930        SCOM_cleaned=[]
931        #hiddenNewLineCount1=0
932        for c in NCOM:
933            if c.strip() not in ("", " ", "  "):
934                #hiddenNewLineCount1=hiddenNewLineCount1+c.count("\n")
935                # Replace new lines within one attribute with a newline and tab so easier to read
936                lines=c.split("\n")
937                for line in lines:
938                    if line!=lines[0]: line="\t"+line
939                    NCOM_cleaned.append(line)
940               
941        #hiddenNewLineCount2=0 
942        for c in SCOM:
943            if c.strip() not in ("", " ", "  "):               
944                #hiddenNewLineCount2=hiddenNewLineCount2+c.count("\n")
945                # Replace new lines within one attribute with a newline and tab so easier to read
946                #c=c.replace("\n", "\n\t")
947                #SCOM_cleaned.append(c)
948                lines=c.split("\n")
949                for line in lines:
950                    if line!=lines[0]: line="\t"+line
951                    SCOM_cleaned.append(line)
952                   
953        self.naDict["NCOM"]=NCOM_cleaned
954        self.naDict["NNCOML"]=len(self.naDict["NCOM"])#+hiddenNewLineCount1
955        self.naDict["SCOM"]=SCOM_cleaned
956        self.naDict["NSCOML"]=len(self.naDict["SCOM"])#+hiddenNewLineCount2
957        return
958
959
960    def defineGeneralHeader(self, header_items={}):
961        """
962        Defines known header items and overwrites any with header_items
963        key/value pairs.
964        """
965        # Check if DATE field previously known in NASA Ames file
966        time_now=time.strftime("%Y %m %d", time.localtime(time.time())).split()
967        if not self.naDict.has_key("RDATE"):
968            self.naDict["RDATE"]=time_now
969       
970        if self.ax0.isTime():
971            # Get first date in list
972            try:
973                (unit, start_date)=re.match("(\w+)\s+?since\s+?(\d+-\d+-\d+)", self.ax0.units).groups()           
974                comptime=cdtime.s2c(start_date)
975                first_day=comptime.add(self.naDict["X"][0][0], getattr(cdtime, unit.capitalize()))
976                self.naDict["DATE"]=string.replace(str(first_day).split(" ")[0], "-", " ").split()
977            except:
978                msg="Nappy Warning: Could not get the first date in the file. You will need to manually edit the output file."
979                print msg
980                self.outputMessage.append(msg)
981                self.naDict["DATE"]=("DATE", "NOT", "KNOWN")
982        else: 
983            if not self.naDict.has_key("DATE"):
984                msg="Nappy Warning: Could not get the first date in the file. You will need to manually edit the output file."
985                print msg
986                self.outputMessage.append(msg)
987                self.naDict["DATE"]=("DATE", "NOT", "KNOWN")
988        self.naDict["IVOL"]=1
989        self.naDict["NVOL"]=1
990        for key in header_items.keys():
991             self.naDict[key]=header_items[key]
992        return
993
994
995    def _useLocalRule(self, var, ruleArgs):
996        """
997        Applies some logic based on a local rule.
998        """
999        ruleName=ruleArgs[0]
1000        rule=ruleArgs
1001        if ruleName=="aircraft":
1002            # Fixes aircraft data 2D time axis, missing values and does sub-selection by default
1003            flagVar=None
1004
1005            # return if variable is time
1006            if var.id=="Time":
1007                return None
1008
1009            if len(rule)<2: rule.append("av")
1010            if len(rule)<3: rule.append("flag")
1011            if rule[2]=="flag":
1012                # Only use flag var for processing real variable
1013                if var.id.strip()[-4:]=="FLAG": 
1014                    print "Ignore flag: %s" % var.id
1015                    return None 
1016
1017                flagID=var.id.strip()+"FLAG"
1018                try:
1019                    flagVar=self.cdmsfile(flagID)
1020                except:
1021                    raise "Cannot get flag variable for '%s'" % var.id
1022            elif rule[2]=="noflag":
1023                flagVar=None
1024
1025            timeVar=self.cdmsfile("Time")
1026            import localRules.aircraftData
1027            ruleArgs=[rule[1]]+rule[3:]
1028            fixedVar=localRules.aircraftData.AircraftData(var, timeVar, flagVar, ruleArgs).subSampledVar
1029            return fixedVar
1030        else:
1031            raise "Rule '%s' not yet defined." % ruleName
1032               
1033
1034usenc2nainstead="""if __name__=="__main__":
1035
1036    args=sys.argv[1:]
1037    if len(args)<4:
1038        print helpMessage
1039        print "Incorrect number of arguments used."
1040        sys.exit()
1041       
1042    for arg in args:
1043        if arg=="-i":
1044            infile=args[args.index(arg)+1]
1045        elif arg=="-o":
1046            outfile=args[args.index(arg)+1]
1047
1048    cdms2na(infile, outfile) """
Note: See TracBrowser for help on using the repository browser.