source: nappy/trunk/cdms2na.py @ 368

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

Updates for correctly converting and reducing volume of FAAM flight data using the localRules/aircraftData.py module.

  • 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="%8.3f", 
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        #nlhead=fixHeaderLength(newname) 
433
434        msg="\nWrote the following variables:"+"\n\t"+("\n\t".join(i[1][0]))
435        print msg
436        outputMessage.append(msg)
437       
438        if len(i[1][1])>0:
439            msg="\nWrote the following auxiliary variables:"
440            msg=msg+"\n\t"+("\n\t".join(i[1][1]))       
441           
442        if len(i[1][2])>0:
443            msg="\nWrote the following Singleton variables:"
444            msg=msg+"\n\t"+("\n\t".join(i[1][2]))
445
446        if len(fileList)>0:
447            msg=msg+("\n\nNASA Ames files written successfully: \n%s" % "\n".join(fileList))
448            count=count+len(fileList)
449        else:
450            msg=msg+"\n\nNASA Ames file written successfully: %s" % newname
451            count=count+1
452        ncount=ncount+1
453
454        print msg
455        outputMessage.append(msg)
456           
457    if (count-1)==1:
458        plural=""
459    else:
460        plural="s"           
461    msg="\n%s file%s written." % ((count-1), plural)
462    print msg
463    outputMessage.append(msg)
464    return outputMessage
465
466
467class CdmsToNABuilder(NACore):
468    """
469    Class to build a NASA Ames File object from a set of
470    CDMS variables and global attributes (optional).
471    """
472   
473    def __init__(self, vars, global_attributes={}, cdmsfile=None, rule=None):
474        """
475        Sets up instance variables and calls appropriate methods to
476        generate sections of NASA Ames file object.
477        """
478        self.rule=rule
479        self.cdmsfile=cdmsfile
480        self.outputMessage=[]
481        self.naDict={}
482        self.vars=vars
483        self.varIDs=None
484        self.globals=global_attributes 
485        self.rankZeroVars=[]
486        self.rankZeroVarIDs=[]
487        (self.orderedVars, auxVars)=self.analyseVariables()
488        if self.orderedVars==None:
489            self.varBin=[]
490        else:
491            #print "NAMELISTS:", [var.id for var in self.orderedVars],[var.id for var in auxVars]
492            self.varIDs=[[var.id for var in self.orderedVars],[var.id for var in auxVars], self.rankZeroVarIDs]
493       
494            self.naDict["NLHEAD"]="-999"
495       
496            #print [var.id for var in self.orderedVars]
497            #print [var.rank() for var in self.orderedVars]     
498            self.defineNAVars(self.orderedVars)
499            self.defineNAAuxVars(auxVars)
500            self.defineNAGlobals()
501            self.defineNAComments()
502            self.defineGeneralHeader()
503            # Quick fudge
504            if self.naDict["FFI"]==1001: self.naDict["X"]=self.naDict["X"][0]
505
506
507    def analyseVariables(self):
508        """
509        Method to examine the content of CDMS variables to return
510        a tuple of two lists containing variables and auxiliary variables
511        for the NASA Ames file object.
512        Variables not compatible with the first file are binned to be used next.
513        """
514        # Need to group the variables together in bins
515        self.varBin=[]
516        # Get largest ranked variable as the one we use as standard
517        highrank=-1
518        bestVar=None
519        count=0
520        for var in self.vars:
521            msg="Analysing: %s" % var.id
522            print msg
523            self.outputMessage.append(msg)
524            count=count+1
525
526            # get rank
527            rank=var.rank()
528
529            # Deal with specific datasets with special rules
530            if self.rule[0]=="aircraft":
531                var=self._useLocalRule(var, self.rule)
532                if type(var)==type(None): 
533                    continue 
534                rank=1
535
536            # Deal with singleton variables
537            if rank==0: 
538                self.rankZeroVars.append(var)
539                self.rankZeroVarIDs.append(var.id)
540                continue
541           
542            if rank>highrank:
543                highrank=rank
544                bestVar=var
545                bestVarIndex=count
546            elif rank==highrank:
547                if len(var.flat)>len(bestVar.flat):
548                    bestVar=var
549                    bestVarIndex=count
550       
551        if len(self.rankZeroVars)==len(self.vars):  return (None, None)
552        if not bestVar: 
553            print "No variables produced"
554            return (None, None)
555
556        vars4NA=[bestVar]
557        auxVars4NA=[]
558        shape=bestVar.shape
559        ndims=len(shape)
560        self.naDict["NIV"]=ndims
561
562        # Work out which File Format Index is appropriate
563        if ndims in (2,3,4):
564            self.naDict["FFI"]=10+(ndims*1000)
565        elif ndims>4:
566            raise "Cannot write variables defined against greater than 4 axes in NASA Ames format."
567        else:
568            if len(auxVars4NA)>0 or (self.naDict.has_key("NAUXV") and self.naDict["NAUXV"]>0):
569                self.naDict["FFI"]=1010
570            else:
571                self.naDict["FFI"]=1001
572        #print self.naDict["FFI"]
573        axes=bestVar.getAxisList()
574       
575        # Get other variable info
576        #print [v.id for v in self.vars], bestVarIndex
577        #print [v.id for v in self.vars[:bestVarIndex-1]+self.vars[bestVarIndex:]]
578        for var in self.vars[:bestVarIndex-1]+self.vars[bestVarIndex:]:
579            # Deal with specific datasets with special rules
580            if self.rule[0]=="aircraft":
581                if var.rank()==2:
582                    var=self._useLocalRule(var, self.rule)
583                    if type(var)==type(None): continue
584
585            #print self.rankZeroVars
586            #for rzv in self.rankZeroVars: 
587            #    if var.id==rzv.id and var[0]==rzv[0]: continue
588            #print [v.id for v in self.rankZeroVars]
589            if var.id in self.rankZeroVarIDs: continue
590            #print var.id, ndims, shape, len(var.shape), var.shape
591            if len(var.shape)!=ndims or var.shape!=shape: 
592                # Could it be an auxiliary variable
593                if len(var.shape)!=1: 
594                    self.varBin.append(var)
595                    continue
596                caxis=var.getAxis(0)
597                if compareAxes(axes[0], caxis)==0: 
598                    self.varBin.append(var)
599                    continue
600                # I think it is an auxiliary variable
601                auxVars4NA.append(var) 
602                # Also put it in var bin because auxiliary vars might be useful
603                self.varBin.append(var)
604            else:
605                caxes=var.getAxisList()
606                #print var.id, "here"
607                for i in range(ndims):           
608                    if compareAxes(axes[i], caxes[i])==0:
609                        self.varBin.append(var)
610                        continue
611                # OK, I think they are compatible
612                vars4NA.append(var)
613               
614        # Re-order if they previously came from NASA Ames files (i.e. including
615        # the attribute 'nasa_ames_var_number')
616        orderedVars=[None]*1000
617        otherVars=[]
618        for var in vars4NA:
619            if hasattr(var, "nasa_ames_var_number"):
620                orderedVars[var.nasa_ames_var_number[0]]=var
621            else:
622                otherVars.append(var)
623        # Remake vars4NA now in order
624        vars4NA=[]
625        for var in orderedVars:
626            if var!=None: vars4NA.append(var)
627        vars4NA=vars4NA+otherVars
628
629        # Now re-order the Auxiliary variables if they previously came from NASA
630        # Ames files (i.e. including the attribute 'nasa_ames_aux_var_number')
631
632        orderedAuxVars=[None]*1000
633        otherAuxVars=[]
634        for var in auxVars4NA:
635            if hasattr(var, "nasa_ames_aux_var_number"):
636                orderedAuxVars[var.nasa_ames_aux_var_number[0]]=var
637            else:
638                otherAuxVars.append(var)
639        # Remake auxVars4NA now in order
640        auxVars4NA=[]
641        for var in orderedAuxVars:
642            if var!=None: auxVars4NA.append(var)
643        auxVars4NA=auxVars4NA+otherAuxVars     
644        return (vars4NA, auxVars4NA)
645
646
647    def defineNAVars(self, vars):
648        """
649        Method to define NASA Ames file object variables and their
650        associated metadata.
651        """
652        self.naDict["NV"]=len(vars)
653        self.naDict["VNAME"]=[]
654        self.naDict["VMISS"]=[]
655        self.naDict["VSCAL"]=[]
656        self.naDict["V"]=[]
657        for var in vars:
658            name=getBestName(var)
659            self.naDict["VNAME"].append(name)
660            miss=getMissingValue(var)
661            if type(miss) not in (float, int, long):  miss=miss[0]
662            self.naDict["VMISS"].append(miss)
663            #print self.naDict["VMISS"]
664            self.naDict["VSCAL"].append(1)
665            # AND THE ARRAY
666            # Populate the variable list 
667            ######## NOTE - might not have to do this #####
668            ######## It  might handle writing from a Numeric array ########
669            self.naDict["V"].append(var._data)
670            #listOfListsCreator(inlist, var.shape)
671            #arrayToList(var, inlist)
672
673            if not self.naDict.has_key("X"):
674                self.naDict["NXDEF"]=[]
675                self.naDict["NX"]=[]
676                # Create independent variable information
677                #print var.id, var.getAxis(0)
678                self.ax0=var.getAxis(0)
679                self.naDict["X"]=[list(self.ax0._data_)]
680                self.naDict["XNAME"]=[getBestName(self.ax0)]
681                if len(self.ax0)==1:
682                    self.naDict["DX"]=[0]
683                else:
684                    incr=self.ax0[1]-self.ax0[0]
685                    # Set default increment as gap between first two
686                    self.naDict["DX"]=[incr]
687                    # Now overwrite it as zero if non-uniform interval in axis
688                    for i in range(1, len(self.ax0)):
689                        if (self.ax0[i]-self.ax0[i-1])!=incr:
690                            self.naDict["DX"]=[0]
691                            break
692
693                # Now sort the rest of the axes
694                for axis in var.getAxisList()[1:]:
695                    self.getAxisDefinition(axis)
696
697
698    def defineNAAuxVars(self, auxVars):
699        """
700        Method to define NASA Ames file object auxiliary variables and their
701        associated metadata.
702        """
703        self.naDict["NAUXV"]=len(auxVars)
704        self.naDict["ANAME"]=[]
705        self.naDict["AMISS"]=[]
706        self.naDict["ASCAL"]=[]
707        self.naDict["A"]=[]
708        for var in auxVars:
709            name=getBestName(var)
710            self.naDict["ANAME"].append(name)
711            miss=getMissingValue(var)
712            if type(miss)!=float:  miss=miss[0]
713            self.naDict["AMISS"].append(miss)
714            self.naDict["ASCAL"].append(1)
715            # AND THE ARRAY
716            # Populate the variable list 
717            ######## NOTE - might not have to do this #####
718            ######## It  might handle writing from a Numeric array ########
719            self.naDict["A"].append(var._data)
720            #listOfListsCreator(inlist, var.shape)
721            #arrayToList(var, inlist)     
722
723
724    def getAxisDefinition(self, axis):
725        """
726        Method to create the appropriate NASA Ames file object
727        items associated with an axis (independent variable in
728        NASA Ames).
729        """
730        length=len(axis)
731        self.naDict["NX"].append(length)
732        self.naDict["XNAME"].append(getBestName(axis))
733        # If only one item in axis values
734        if length<2:
735            self.naDict["DX"].append(0)
736            self.naDict["NXDEF"].append(length)
737            self.naDict["X"].append(list(axis._data_))       
738            return
739   
740        incr=axis[1]-axis[0]
741        for i in range(1, length):
742            if (axis[i]-axis[i-1])!=incr:
743                self.naDict["DX"].append(0)
744                self.naDict["NXDEF"].append(length)
745                self.naDict["X"].append(list(axis._data_))
746                break
747        else:
748            maxLength=length
749            if length>3: maxLength=3
750            self.naDict["DX"].append(incr)
751            self.naDict["NXDEF"].append(maxLength)
752            self.naDict["X"].append(axis[:maxLength])
753        return
754
755
756    def defineNAGlobals(self):
757        """
758        Maps CDMS (NetCDF) global attributes into NASA Ames Header fields.
759        """
760        # Get the global mapping dictionary
761        globalmap=cdmsMap.toNA
762        # Check if we should add to it with locally set rules
763        locGlobs=localRules.localGlobalAttributes
764        for att in locGlobs.keys():
765            if not globalmap.has_key(att):
766                globalmap[key]=locGlobs[key]
767
768        self.extra_comments=[[],[],[]]  # Normal comments, special comments, other comments
769        conventionOrReferenceComments=[]
770        for key in self.globals.keys():
771            if key!="first_valid_date_of_data" and type(self.globals[key]) not in (str, float, int): continue
772            if key in globalmap.keys():
773                if key=="history":
774                    timestring=time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(time.time()))
775                    history="History:\t%s - Converted to NASA Ames format using nappy-%s.\n\t%s" % (timestring, version.version, self.globals[key])
776                    history=history.split("\n") 
777                    self.history=[]
778                    for h in history:
779                        if h[:8]!="History:" and h[:1]!="\t": h="\t"+h
780                        self.history.append(h) 
781                   
782                elif key=="institution":
783                    # If fields came from NA then extract appropriate fields.
784                    match=re.match(r"(.*)\s+\(ONAME from NASA Ames file\);\s+(.*)\s+\(ORG from NASA Ames file\)\.", self.globals[key])
785                    if match:
786                        self.naDict["ONAME"]=match.groups()[0]
787                        self.naDict["ORG"]=match.groups()[1]
788                    else:
789                        self.naDict["ONAME"]=self.globals[key]
790                        self.naDict["ORG"]=self.globals[key]               
791                   
792                    # NOte: should probably do the following search and replace on all string lines
793                    self.naDict["ONAME"]=self.naDict["ONAME"].replace("\n", "  ")
794                    self.naDict["ORG"]=self.naDict["ORG"].replace("\n", "  ")
795                                   
796                elif key=="comment":
797                    # Need to work out if they are actually comments from NASA Ames in the first place
798                    #self.ncom=[self.globals[key]]
799                    comLines=self.globals[key].split("\n")
800                    normComms=[]
801                    normCommFlag=None
802                    specComms=[]
803                    specCommFlag=None
804                    for line in comLines:
805                        if line.find("###NASA Ames Special Comments follow###")>-1:
806                            specCommFlag=1
807                        elif line.find("###NASA Ames Special Comments end###")>-1:
808                            specCommFlag=None
809                        elif line.find("###NASA Ames Normal Comments follow###")>-1:
810                            normCommFlag=1
811                        elif line.find("###NASA Ames Normal Comments end###")>-1:
812                            normCommFlag=None   
813                        elif specCommFlag==1:
814                            specComms.append(line)
815                        elif normCommFlag==1:
816                            normComms.append(line)
817                        elif line.find("###Data Section begins on the next line###")>-1:
818                            pass
819                        else:
820                            normComms.append(line)         
821                   
822                    self.extra_comments=[specComms, normComms, []]                 
823                                   
824                elif key=="first_valid_date_of_data":
825                    self.naDict["DATE"]=self.globals[key]
826               
827                elif key in ("Conventions", "references"):
828                    #conventionOrReferenceComments.append("%s:   %s" % (key, self.globals[key]))
829                    self.extra_comments[2].append("%s:   %s" % (key, self.globals[key]))
830                else:
831                    self.naDict[globalmap[key]]=self.globals[key]
832            else:
833                self.extra_comments[2].append("%s:   %s" % (key, self.globals[key]))
834        #self.extra_comments
835        return
836
837
838    def defineNAComments(self, normal_comments=[], special_comments=[]):
839        """
840        Defines the Special and Normal comments sections in the NASA Ames file
841        object - including information gathered from the defineNAGlobals method.
842        """
843       
844        if hasattr(self, "ncom"):  normal_comments=self.ncom+normal_comments
845        NCOM=[]
846        for ncom in normal_comments:
847            NCOM.append(ncom)
848        if len(NCOM)>0:   NCOM.append("")
849       
850        if len(self.extra_comments[2])>0:
851            for excom in self.extra_comments[2]:
852                NCOM.append(excom)
853       
854        if len(self.extra_comments[1])>0: 
855            NCOM.append("Additional Global Attributes defined in the source file and not translated elsewhere:")
856            for excom in self.extra_comments[1]:
857                NCOM.append(excom)
858
859        if hasattr(self, "history"):
860            for h in self.history:
861                NCOM.append(h)
862       
863        if len(NCOM)>0:
864            NCOM.insert(0, "###NASA Ames Normal Comments follow###")
865            NCOM.append("")
866            NCOM.append("###NASA Ames Normal Comments end###")
867        NCOM.append("###Data Section begins on the next line###")
868
869        specCommentsFlag=None
870        SCOM=[]
871        special_comments=self.extra_comments[0]
872        if len(special_comments)>0: 
873            SCOM=["###NASA Ames Special Comments follow###"]
874            specCommentsFlag=1
875        for scom in special_comments:
876            SCOM.append(scom)
877
878
879        #used_var_atts=("name", "long_name", "standard_name", "id",
880            #    "missing_value", "fill_value", "units",
881                #"nasa_ames_var_number", "nasa_ames_aux_var_number")
882        used_var_atts=("id",  "missing_value", "fill_value", "units", 
883                   "nasa_ames_var_number", "nasa_ames_aux_var_number")
884        varCommentsFlag=None
885
886        # Create a string for the Special comments to hold rank-zero vars
887        rankZeroVarsString=[]
888        for var in self.rankZeroVars:
889            rankZeroVarsString.append("\tVariable %s: %s" % (var.id, getBestName(var)))
890            for att in var.attributes.keys():
891                value=var.attributes[att]
892                if type(value) in (str, float, int):
893                    rankZeroVarsString.append("\t\t%s = %s" % (att, var.attributes[att]))
894            #print "VALUES", dir(var), var._data ; rankZeroVarsString.append("\t\tvalue = %s" % var._data)
895       
896        if len(rankZeroVarsString)>0:
897            rankZeroVarsString.insert(0, "###Singleton Variables defined in the source file follow###")
898            rankZeroVarsString.append("###Singleton Variables defined in the source file end###")
899
900        for var in self.orderedVars:
901            varflag="unused"
902            name=getBestName(var)
903            for scom,value in var.attributes.items():
904                if type(value) in (type([]), type(Numeric.array([0]))) and len(value)==1:
905                    value=value[0]
906                if type(value) in (str, float, int) and scom not in used_var_atts:
907                    if varflag=="unused":
908                        if varCommentsFlag==None:
909                            varCommentsFlag=1
910                            if specCommentsFlag==None:
911                                SCOM=["###NASA Ames Special Comments follow###"]+rankZeroVarsString
912                            SCOM.append("Additional Variable Attributes defined in the source file and not translated elsewhere:")
913                            SCOM.append("###Variable attributes from source (NetCDF) file follow###")
914                        varflag="using" 
915                        SCOM.append("\tVariable %s: %s" % (var.id, name))
916                    SCOM.append("\t\t%s = %s" % (scom, value))
917
918        if varCommentsFlag==1:  SCOM.append("###Variable attributes from source (NetCDF) file end###")
919        if specCommentsFlag==1:
920            SCOM.append("###NASA Ames Special Comments end###")
921
922        """used_var_atts=("name", "long_name", "standard_name", "id", "missing_value", "fill_value", "units")
923        for var in self.vars:
924            for scom,value in var.attributes.items():
925                name=getBestName(var)
926                if type(value) in (str, float, int) and scom not in used_var_atts:
927                    SCOM.append("\t%s: %s - %s" % (name, scom, value))"""
928
929        # Strip out empty lines (or returns)
930        NCOM_cleaned=[]
931        SCOM_cleaned=[]
932        #hiddenNewLineCount1=0
933        for c in NCOM:
934            if c.strip() not in ("", " ", "  "):
935                #hiddenNewLineCount1=hiddenNewLineCount1+c.count("\n")
936                # Replace new lines within one attribute with a newline and tab so easier to read
937                lines=c.split("\n")
938                for line in lines:
939                    if line!=lines[0]: line="\t"+line
940                    NCOM_cleaned.append(line)
941               
942        #hiddenNewLineCount2=0 
943        for c in SCOM:
944            if c.strip() not in ("", " ", "  "):               
945                #hiddenNewLineCount2=hiddenNewLineCount2+c.count("\n")
946                # Replace new lines within one attribute with a newline and tab so easier to read
947                #c=c.replace("\n", "\n\t")
948                #SCOM_cleaned.append(c)
949                lines=c.split("\n")
950                for line in lines:
951                    if line!=lines[0]: line="\t"+line
952                    SCOM_cleaned.append(line)
953                   
954        self.naDict["NCOM"]=NCOM_cleaned
955        self.naDict["NNCOML"]=len(self.naDict["NCOM"])#+hiddenNewLineCount1
956        self.naDict["SCOM"]=SCOM_cleaned
957        self.naDict["NSCOML"]=len(self.naDict["SCOM"])#+hiddenNewLineCount2
958        return
959
960
961    def defineGeneralHeader(self, header_items={}):
962        """
963        Defines known header items and overwrites any with header_items
964        key/value pairs.
965        """
966        # Check if DATE field previously known in NASA Ames file
967        time_now=time.strftime("%Y %m %d", time.localtime(time.time())).split()
968        if not self.naDict.has_key("RDATE"):
969            self.naDict["RDATE"]=time_now
970       
971        if self.ax0.isTime():
972            # Get first date in list
973            try:
974                (unit, start_date)=re.match("(\w+)\s+?since\s+?(\d+-\d+-\d+)", self.ax0.units).groups()           
975                comptime=cdtime.s2c(start_date)
976                first_day=comptime.add(self.naDict["X"][0][0], getattr(cdtime, unit.capitalize()))
977                self.naDict["DATE"]=string.replace(str(first_day).split(" ")[0], "-", " ").split()
978            except:
979                msg="Nappy Warning: Could not get the first date in the file. You will need to manually edit the output file."
980                print msg
981                self.outputMessage.append(msg)
982                self.naDict["DATE"]=("DATE", "NOT", "KNOWN")
983        else: 
984            if not self.naDict.has_key("DATE"):
985                msg="Nappy Warning: Could not get the first date in the file. You will need to manually edit the output file."
986                print msg
987                self.outputMessage.append(msg)
988                self.naDict["DATE"]=("DATE", "NOT", "KNOWN")
989        self.naDict["IVOL"]=1
990        self.naDict["NVOL"]=1
991        for key in header_items.keys():
992             self.naDict[key]=header_items[key]
993        return
994
995
996    def _useLocalRule(self, var, ruleArgs):
997        """
998        Applies some logic based on a local rule.
999        """
1000        ruleName=ruleArgs[0]
1001        rule=ruleArgs
1002        if ruleName=="aircraft":
1003            # Fixes aircraft data 2D time axis, missing values and does sub-selection by default
1004            flagVar=None
1005
1006            # return if variable is time
1007            if var.id=="Time":
1008                return None
1009
1010            if len(rule)<2: rule.append("av")
1011            if len(rule)<3: rule.append("flag")
1012            if rule[2]=="flag":
1013                # Only use flag var for processing real variable
1014                if var.id.strip()[-4:]=="FLAG": 
1015                    print "Ignore flag: %s" % var.id
1016                    return None 
1017
1018                flagID=var.id.strip()+"FLAG"
1019                try:
1020                    flagVar=self.cdmsfile(flagID)
1021                except:
1022                    raise "Cannot get flag variable for '%s'" % var.id
1023            elif rule[2]=="noflag":
1024                flagVar=None
1025
1026            timeVar=self.cdmsfile("Time")
1027            import localRules.aircraftData
1028            ruleArgs=[rule[1]]+rule[3:]
1029            fixedVar=localRules.aircraftData.AircraftData(var, timeVar, flagVar, ruleArgs).subSampledVar
1030            return fixedVar
1031        else:
1032            raise "Rule '%s' not yet defined." % ruleName
1033               
1034
1035usenc2nainstead="""if __name__=="__main__":
1036
1037    args=sys.argv[1:]
1038    if len(args)<4:
1039        print helpMessage
1040        print "Incorrect number of arguments used."
1041        sys.exit()
1042       
1043    for arg in args:
1044        if arg=="-i":
1045            infile=args[args.index(arg)+1]
1046        elif arg=="-o":
1047            outfile=args[args.index(arg)+1]
1048
1049    cdms2na(infile, outfile) """
Note: See TracBrowser for help on using the repository browser.