source: nappy/trunk/nappy/nc_interface/cdms_to_na.py @ 3345

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/nappy/trunk/nappy/nc_interface/cdms_to_na.py@3345
Revision 3345, 28.3 KB checked in by astephen, 14 years ago (diff)
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
6"""
7cdms_to_na.py
8=============
9
10Holds the class CDMSToNA that converts a set of CDMS variables and global attributes.
11
12"""
13
14# Imports from python standard library
15import sys
16import os
17import time
18import string
19import re
20
21# Import from nappy package
22from nappy.na_error import na_error
23import nappy.utils
24import nappy.utils.common_utils
25import nappy.na_file.na_core
26
27
28nc_to_na_map = utils.getConfigDict()["nc_to_na_map"]
29
30# Import external packages (if available)
31if sys.platform.find("win") > -1:
32    raise na_error.NAPlatformError("Windows does not support CDMS. CDMS is required to convert to CDMS objects and NetCDF.")
33try:
34    import cdms, Numeric
35except:
36    raise Exception("Could not import third-party software. Nappy requires the CDMS and Numeric packages to be installed to convert to CDMS and NetCDF.")
37
38cdms.setAutoBounds("off") 
39
40compareAxes = nappy.cdms_utils.axis_utils.compareAxes
41compareVariables = nappy.cdms_utils.var_utils.compareVariables
42arrayToList = nappy.utils.list_manipulator.arrayToList
43listOfListsCreator = nappy.utils.list_manipulator.listOfListsCreator
44getBestName = nappy.cdms_utils.var_utils.getBestName
45getMissingValue = nappy.cdms_utils.var_utils.getMissingValue
46flatten2DTimeAxis = nappy.contrib.aircraft.aircraft_utils.flatten2DTimeAxis
47modifyNADictCopy = nappy.utils.common_utils.modifyNADictCopy
48
49
50cdms2na - 200 lines of code to do main conversion, needs to be split out into other stuff.
51 * getVariableCollections(f and varlist) --> (ordered_vars, other_vars)
52 * buildNADicts()
53 * writeToOutputFiles()
54
55class CdmsToNABuilder --> NAContentCollector: (naDict, varIDs, varBin)
56__init__ --> sets everything up and runs it move some to --> analyse()
57analyseVariables
58defineNAVars
59defineNAAuxVars
60getAxisDefinition
61defineNAGlobals
62defineNAComments
63defineGeneralHeader
64
65def cdms2na(ncfile, na_file_names, naVars={}, variables=None, nFilesOnly="no", 
66            ffi="automatic", spacer="    ", floatFormat="%g", size_limit=None):
67    """
68    Main conversion function that calls the appropriate classes and functions
69    to write a NASA Ames file.
70    """
71    #print infilename, outfilenames, nFilesOnly, naVars, variables
72    if type(na_file_names) == type("string"): 
73        na_file_names = [na_file_names]
74   
75    # Get which NASA Ames internal variables are allowed to be overwritten in the output files (i.e. by user inputs)
76    allowed_overwrite_metadata = ("DATE",  "RDATE", "ANAME", "MNAME",
77           "ONAME", "ORG", "SNAME", "VNAME")
78    arrayArgs=["DATE", "RDATE", "ANAME", "VNAME"]
79    # ANAME[a] - array of 'a' x ANAME strings - aux var names
80    # DATE (array of three) - UT date at which the data within the file starts
81    # MNAME - mission name
82    # ONAME - name of originator(s)
83    # ORG - org or affiliation of originator(s)
84    # RDATE (array of three) - date of data reduction or revision
85    # SNAME - source of measurement or model output VNAME[n] - array of 'n' x
86    # VNAME strings - var names.
87    output_message=[]
88    msg="Reading data from: %s\n" % infilename
89    print msg
90    output_message.append(msg)
91    cdms_file=cdms.open(infilename)
92    globals=cdms_file.attributes
93   
94    vars=[]
95    if not variables:
96        variables=cdms_file.listvariables()
97        #for var in cdms_file.listvariables():
98            #vars.append(cdms_file(var))   
99           
100    for variable in variables:
101        varObj=cdms_file(variable)
102        # Deal with singleton variables
103        if not hasattr(varObj, "rank"):
104                varMetadata=cdms_file[variable].attributes
105                varValue=varObj
106                #print varMetadata, varValue, varMetadata.keys(), varMetadata._obj_.id
107                varObj=cdms.createVariable(Numeric.array(varObj), id=getBestName(varMetadata).replace(" ", "_"), attributes=varMetadata)
108                #print varObj, dir(varObj); sys.exit()
109                varObj.value=varObj._data[0]
110                #varObj.rank=0
111               
112        #print varObj, varObj.attributes                         
113        vars.append(varObj)
114       
115    # Re-order variables if they have the attribute 'nasa_ames_var_number'
116    ordered_vars=[None]*1000
117    otherVars=[]
118    for var in vars:
119        varMetadata=cdms_file[var]
120        if hasattr(varMetadata, "nasa_ames_var_number"):
121            num=varMetadata.nasa_ames_var_number
122            ordered_vars[num]=var
123        else:
124            otherVars.append(var)
125   
126    vars=[]
127    for var in ordered_vars:
128        if var!=None:
129            vars.append(var)
130           
131    vars=vars+otherVars
132   
133    builder=NAContentCollector(vars, globals, rule=rule, cdms_file=cdms_file)
134    #print builder.naDict["X"]
135    builtNADicts=[[builder.naDict, builder.varIDs]]
136    if builder.varIDs==None:
137        msg="\nNo files created after variables parsed."
138        print msg
139        output_message.append(msg)
140        return output_message
141
142    while len(builder.varBin)>0:
143        builder=NAContentCollector(builder.varBin, globals, rule=rule, cdms_file=cdms_file)
144        output_message=output_message+builder.output_message
145        if builder.varIDs!=None:  builtNADicts.append([builder.naDict, builder.varIDs])
146
147    # Return only filenames if only want to know them now.
148    ncount=1
149    fileNames=[]
150    if nFilesOnly=="yes": 
151        for i in builtNADicts:
152            if len(builtNADicts)==1:
153                suffix=""
154            else:
155                suffix="_%s" % ncount
156            nameparts=outfilenames[0].split(".")   
157            newname=(".".join(nameparts[:-1]))+suffix+"."+nameparts[-1]
158            fileNames.append(newname)
159        ncount=ncount+1
160           
161        return fileNames
162               
163    msg="\n%s files to write" % len(builtNADicts)
164    print msg
165    output_message.append(msg)
166
167    count=1
168    ncount=1
169    for i in builtNADicts:
170        if len(outfilenames)==1:
171            if len(builtNADicts)==1:
172                suffix=""
173            else:
174                suffix="_%s" % ncount
175            nameparts=outfilenames[0].split(".")   
176            newname=(".".join(nameparts[:-1]))+suffix+"."+nameparts[-1]
177        else:
178            newname=outfilenames[count-1]
179 
180        msg="\nWriting output NASA Ames file: %s" % newname
181        print msg
182        output_message.append(msg)
183       
184        builtNADict=i[0]
185        for key in naVars.keys():
186            if key in allowed_overwrite_metadata:
187           
188                if key in arrayArgs:
189                    newItem=naVars[key].split()           
190                else:
191                    newItem=naVars[key]
192                                   
193                if newItem!=builtNADict[key]:
194                    builtNADict[key]=newItem
195                    msg="Metadata overwritten in output file: '%s' is now '%s'" % (key, builtNADict[key])
196                    print msg
197                    output_message.append(msg)
198       
199        fileList=[]
200        # Cope with size limits if specified and FFI is 1001
201        if size_limit and (builtNADict["FFI"]==1001 and len(builtNADict["V"][0])>size_limit):
202            varList=builtNADict["V"]
203            arrayLength=len(varList[0])
204            nvolInfo=divmod(arrayLength, size_limit)
205            nvol=nvolInfo[0]
206            if nvolInfo[1]>0: nvol=nvol+1
207            start=0
208            letterCount=0
209            ivol=0
210            while start<arrayLength:
211                ivol=ivol+1
212                end=start+size_limit
213                if end>arrayLength:
214                    end=arrayLength
215                currentBlock=[]
216                # Write new V array
217                for v in varList:
218                    currentBlock.append(v[start:end])
219
220                # Adjust X accordingly
221                NADictCopy=modifyNADictCopy(builtNADict, currentBlock, start, end, ivol, nvol)
222               
223                # Write data to output file
224                newnamePlusLetter="%s-%.3d.na" % (newname[:-3], ivol)
225                fileList.append(newnamePlusLetter)
226                general.openNAFile(newnamePlusLetter, 'w', NADictCopy, spacer=spacer, floatFormat=floatFormat)
227                msg="\nOutput files split on size limit: %s\nFilename used: %s" % (size_limit, newnamePlusLetter)
228                print msg
229                output_message.append(msg)
230                letterCount=letterCount+1
231                start=end
232
233
234        else:           
235            general.openNAFile(newname, 'w', builtNADict, spacer=spacer, floatFormat=floatFormat)
236
237        msg="\nWrote the following variables:"+"\n\t"+("\n\t".join(i[1][0]))
238        print msg
239        output_message.append(msg)
240       
241        if len(i[1][1])>0:
242            msg="\nWrote the following auxiliary variables:"
243            msg=msg+"\n\t"+("\n\t".join(i[1][1]))       
244           
245        if len(i[1][2])>0:
246            msg="\nWrote the following Singleton variables:"
247            msg=msg+"\n\t"+("\n\t".join(i[1][2]))
248
249        if len(fileList)>0:
250            msg=msg+("\n\nNASA Ames files written successfully: \n%s" % "\n".join(fileList))
251            count=count+len(fileList)
252        else:
253            msg=msg+"\n\nNASA Ames file written successfully: %s" % newname
254            count=count+1
255        ncount=ncount+1
256
257        print msg
258        output_message.append(msg)
259           
260    if (count-1)==1:
261        plural=""
262    else:
263        plural="s"           
264    msg="\n%s file%s written." % ((count-1), plural)
265    print msg
266    output_message.append(msg)
267    return output_message
268
269
270class NAContentCollector(NACore):
271    """
272    Class to build a NASA Ames File object from a set of
273    CDMS variables and global attributes (optional).
274    """
275   
276    def __init__(self, vars, global_attributes={}, cdms_file=None):
277        """
278        Sets up instance variables and calls appropriate methods to
279        generate sections of NASA Ames file object.
280        """
281        self.cdms_file=cdms_file
282        self.output_message=[]
283        self.na_dict={}
284        self.vars=vars
285        self.var_ids=None
286        self.globals=global_attributes 
287        self.rank_zero_vars=[]
288        self.rank_zero_var_ids=[]
289        (self.ordered_vars, auxVars)=self.analyseVariables()
290        if self.ordered_vars==None:
291            self.var_bin=[]
292        else:
293
294            self.var_ids=[[var.id for var in self.ordered_vars],[var.id for var in auxVars], self.rank_zero_var_ids]
295       
296            self.na_dict["NLHEAD"]="-999"
297       
298            #print [var.id for var in self.ordered_vars]
299            #print [var.rank() for var in self.ordered_vars]   
300            self.defineNAVars(self.ordered_vars)
301            self.defineNAAuxVars(auxVars)
302            self.defineNAGlobals()
303            self.defineNAComments()
304            self.defineGeneralHeader()
305            # Quick fudge
306            if self.na_dict["FFI"]==1001: self.na_dict["X"]=self.na_dict["X"][0]
307
308
309    def analyseVariables(self):
310        """
311        Method to examine the content of CDMS variables to return
312        a tuple of two lists containing variables and auxiliary variables
313        for the NASA Ames file object.
314        Variables not compatible with the first file are binned to be used next.
315        """
316        # Need to group the variables together in bins
317        self.var_bin=[]
318        # Get largest ranked variable as the one we use as standard
319        highrank=-1
320        best_var=None
321        count=0
322        for var in self.vars:
323            msg="Analysing: %s" % var.id
324            print msg
325            self.output_message.append(msg)
326            count=count+1
327
328            # get rank
329            rank=var.rank()
330
331            # Deal with singleton variables
332            if rank==0: 
333                self.rank_zero_vars.append(var)
334                self.rank_zero_var_ids.append(var.id)
335                continue
336           
337            if rank>highrank:
338                highrank=rank
339                best_var=var
340                best_var_index=count
341            elif rank==highrank:
342                if len(var.flat)>len(best_var.flat):
343                    best_var=var
344                    best_var_index=count
345       
346        if len(self.rank_zero_vars)==len(self.vars):  return (None, None)
347        if not best_var: 
348            print "No variables produced"
349            return (None, None)
350
351        vars4NA=[best_var]
352        auxVars4NA=[]
353        shape=best_var.shape
354        ndims=len(shape)
355        self.na_dict["NIV"]=ndims
356
357        # Work out which File Format Index is appropriate
358        if ndims in (2,3,4):
359            self.na_dict["FFI"]=10+(ndims*1000)
360        elif ndims>4:
361            raise "Cannot write variables defined against greater than 4 axes in NASA Ames format."
362        else:
363            if len(auxVars4NA)>0 or (self.na_dict.has_key("NAUXV") and self.na_dict["NAUXV"]>0):
364                self.na_dict["FFI"]=1010
365            else:
366                self.na_dict["FFI"]=1001
367        #print self.na_dict["FFI"]
368        axes=best_var.getAxisList()
369       
370        # Get other variable info
371        #print [v.id for v in self.vars], best_var_index
372        #print [v.id for v in self.vars[:best_var_index-1]+self.vars[best_var_index:]]
373        for var in self.vars[:best_var_index-1]+self.vars[best_var_index:]:
374
375            #print self.rank_zero_vars
376            #for rzv in self.rank_zero_vars: 
377            #    if var.id==rzv.id and var[0]==rzv[0]: continue
378            #print [v.id for v in self.rank_zero_vars]
379            if var.id in self.rank_zero_var_ids: continue
380            #print var.id, ndims, shape, len(var.shape), var.shape
381            if len(var.shape)!=ndims or var.shape!=shape: 
382                # Could it be an auxiliary variable
383                if len(var.shape)!=1: 
384                    self.var_bin.append(var)
385                    continue
386                caxis=var.getAxis(0)
387                if compareAxes(axes[0], caxis)==0: 
388                    self.var_bin.append(var)
389                    continue
390                # I think it is an auxiliary variable
391                auxVars4NA.append(var) 
392                # Also put it in var bin because auxiliary vars might be useful
393                self.var_bin.append(var)
394            else:
395                caxes=var.getAxisList()
396                #print var.id, "here"
397                for i in range(ndims):           
398                    if compareAxes(axes[i], caxes[i])==0:
399                        self.var_bin.append(var)
400                        continue
401                # OK, I think they are compatible
402                vars4NA.append(var)
403               
404        # Re-order if they previously came from NASA Ames files (i.e. including
405        # the attribute 'nasa_ames_var_number')
406        ordered_vars=[None]*1000
407        otherVars=[]
408        for var in vars4NA:
409            if hasattr(var, "nasa_ames_var_number"):
410                ordered_vars[var.nasa_ames_var_number[0]]=var
411            else:
412                otherVars.append(var)
413        # Remake vars4NA now in order
414        vars4NA=[]
415        for var in ordered_vars:
416            if var!=None: vars4NA.append(var)
417        vars4NA=vars4NA+otherVars
418
419        # Now re-order the Auxiliary variables if they previously came from NASA
420        # Ames files (i.e. including the attribute 'nasa_ames_aux_var_number')
421
422        orderedAuxVars=[None]*1000
423        otherAuxVars=[]
424        for var in auxVars4NA:
425            if hasattr(var, "nasa_ames_aux_var_number"):
426                orderedAuxVars[var.nasa_ames_aux_var_number[0]]=var
427            else:
428                otherAuxVars.append(var)
429        # Remake auxVars4NA now in order
430        auxVars4NA=[]
431        for var in orderedAuxVars:
432            if var!=None: auxVars4NA.append(var)
433        auxVars4NA=auxVars4NA+otherAuxVars     
434        return (vars4NA, auxVars4NA)
435
436
437    def defineNAVars(self, vars):
438        """
439        Method to define NASA Ames file object variables and their
440        associated metadata.
441        """
442        self.na_dict["NV"]=len(vars)
443        self.na_dict["VNAME"]=[]
444        self.na_dict["VMISS"]=[]
445        self.na_dict["VSCAL"]=[]
446        self.na_dict["V"]=[]
447        for var in vars:
448            name=getBestName(var)
449            self.na_dict["VNAME"].append(name)
450            miss=getMissingValue(var)
451            if type(miss) not in (float, int, long):  miss=miss[0]
452            self.na_dict["VMISS"].append(miss)
453            #print self.na_dict["VMISS"]
454            self.na_dict["VSCAL"].append(1)
455            # AND THE ARRAY
456            # Populate the variable list 
457            ######## NOTE - might not have to do this #####
458            ######## It  might handle writing from a Numeric array ########
459            self.na_dict["V"].append(var._data)
460            #listOfListsCreator(inlist, var.shape)
461            #arrayToList(var, inlist)
462
463            if not self.na_dict.has_key("X"):
464                self.na_dict["NXDEF"]=[]
465                self.na_dict["NX"]=[]
466                # Create independent variable information
467                #print var.id, var.getAxis(0)
468                self.ax0=var.getAxis(0)
469                self.na_dict["X"]=[list(self.ax0._data_)]
470                self.na_dict["XNAME"]=[getBestName(self.ax0)]
471                if len(self.ax0)==1:
472                    self.na_dict["DX"]=[0]
473                else:
474                    incr=self.ax0[1]-self.ax0[0]
475                    # Set default increment as gap between first two
476                    self.na_dict["DX"]=[incr]
477                    # Now overwrite it as zero if non-uniform interval in axis
478                    for i in range(1, len(self.ax0)):
479                        if (self.ax0[i]-self.ax0[i-1])!=incr:
480                            self.na_dict["DX"]=[0]
481                            break
482
483                # Now sort the rest of the axes
484                for axis in var.getAxisList()[1:]:
485                    self.getAxisDefinition(axis)
486
487
488    def defineNAAuxVars(self, auxVars):
489        """
490        Method to define NASA Ames file object auxiliary variables and their
491        associated metadata.
492        """
493        self.na_dict["NAUXV"]=len(auxVars)
494        self.na_dict["ANAME"]=[]
495        self.na_dict["AMISS"]=[]
496        self.na_dict["ASCAL"]=[]
497        self.na_dict["A"]=[]
498        for var in auxVars:
499            name=getBestName(var)
500            self.na_dict["ANAME"].append(name)
501            miss=getMissingValue(var)
502            if type(miss)!=float:  miss=miss[0]
503            self.na_dict["AMISS"].append(miss)
504            self.na_dict["ASCAL"].append(1)
505            # AND THE ARRAY
506            # Populate the variable list 
507            ######## NOTE - might not have to do this #####
508            ######## It  might handle writing from a Numeric array ########
509            self.na_dict["A"].append(var._data)
510            #listOfListsCreator(inlist, var.shape)
511            #arrayToList(var, inlist)     
512
513
514    def getAxisDefinition(self, axis):
515        """
516        Method to create the appropriate NASA Ames file object
517        items associated with an axis (independent variable in
518        NASA Ames).
519        """
520        length=len(axis)
521        self.na_dict["NX"].append(length)
522        self.na_dict["XNAME"].append(getBestName(axis))
523        # If only one item in axis values
524        if length<2:
525            self.na_dict["DX"].append(0)
526            self.na_dict["NXDEF"].append(length)
527            self.na_dict["X"].append(list(axis._data_))       
528            return
529   
530        incr=axis[1]-axis[0]
531        for i in range(1, length):
532            if (axis[i]-axis[i-1])!=incr:
533                self.na_dict["DX"].append(0)
534                self.na_dict["NXDEF"].append(length)
535                self.na_dict["X"].append(list(axis._data_))
536                break
537        else:
538            maxLength=length
539            if length>3: maxLength=3
540            self.na_dict["DX"].append(incr)
541            self.na_dict["NXDEF"].append(maxLength)
542            self.na_dict["X"].append(axis[:maxLength])
543        return
544
545
546    def defineNAGlobals(self):
547        """
548        Maps CDMS (NetCDF) global attributes into NASA Ames Header fields.
549        """
550        # Get the global mapping dictionary
551        globalmap=cdmsMap.toNA
552        # Check if we should add to it with locally set rules
553        locGlobs=localRules.localGlobalAttributes
554        for att in locGlobs.keys():
555            if not globalmap.has_key(att):
556                globalmap[key]=locGlobs[key]
557
558        self.extra_comments=[[],[],[]]  # Normal comments, special comments, other comments
559        conventionOrReferenceComments=[]
560        for key in self.globals.keys():
561            if key!="first_valid_date_of_data" and type(self.globals[key]) not in (str, float, int): continue
562            if key in globalmap.keys():
563                if key=="history":
564                    timestring=time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(time.time()))
565                    history="History:\t%s - Converted to NASA Ames format using nappy-%s.\n\t%s" % (timestring, version.version, self.globals[key])
566                    history=history.split("\n") 
567                    self.history=[]
568                    for h in history:
569                        if h[:8]!="History:" and h[:1]!="\t": h="\t"+h
570                        self.history.append(h) 
571                   
572                elif key=="institution":
573                    # If fields came from NA then extract appropriate fields.
574                    match=re.match(r"(.*)\s+\(ONAME from NASA Ames file\);\s+(.*)\s+\(ORG from NASA Ames file\)\.", self.globals[key])
575                    if match:
576                        self.na_dict["ONAME"]=match.groups()[0]
577                        self.na_dict["ORG"]=match.groups()[1]
578                    else:
579                        self.na_dict["ONAME"]=self.globals[key]
580                        self.na_dict["ORG"]=self.globals[key]               
581                   
582                    # NOte: should probably do the following search and replace on all string lines
583                    self.na_dict["ONAME"]=self.na_dict["ONAME"].replace("\n", "  ")
584                    self.na_dict["ORG"]=self.na_dict["ORG"].replace("\n", "  ")
585                                   
586                elif key=="comment":
587                    # Need to work out if they are actually comments from NASA Ames in the first place
588                    #self.ncom=[self.globals[key]]
589                    comLines=self.globals[key].split("\n")
590                    normComms=[]
591                    normCommFlag=None
592                    specComms=[]
593                    specCommFlag=None
594                    for line in comLines:
595                        if line.find("###NASA Ames Special Comments follow###")>-1:
596                            specCommFlag=1
597                        elif line.find("###NASA Ames Special Comments end###")>-1:
598                            specCommFlag=None
599                        elif line.find("###NASA Ames Normal Comments follow###")>-1:
600                            normCommFlag=1
601                        elif line.find("###NASA Ames Normal Comments end###")>-1:
602                            normCommFlag=None   
603                        elif specCommFlag==1:
604                            specComms.append(line)
605                        elif normCommFlag==1:
606                            normComms.append(line)
607                        elif line.find("###Data Section begins on the next line###")>-1:
608                            pass
609                        else:
610                            normComms.append(line)         
611                   
612                    self.extra_comments=[specComms, normComms, []]                 
613                                   
614                elif key=="first_valid_date_of_data":
615                    self.na_dict["DATE"]=self.globals[key]
616               
617                elif key in ("Conventions", "references"):
618                    #conventionOrReferenceComments.append("%s:   %s" % (key, self.globals[key]))
619                    self.extra_comments[2].append("%s:   %s" % (key, self.globals[key]))
620                else:
621                    self.na_dict[globalmap[key]]=self.globals[key]
622            else:
623                self.extra_comments[2].append("%s:   %s" % (key, self.globals[key]))
624        #self.extra_comments
625        return
626
627
628    def defineNAComments(self, normal_comments=[], special_comments=[]):
629        """
630        Defines the Special and Normal comments sections in the NASA Ames file
631        object - including information gathered from the defineNAGlobals method.
632        """
633       
634        if hasattr(self, "ncom"):  normal_comments=self.ncom+normal_comments
635        NCOM=[]
636        for ncom in normal_comments:
637            NCOM.append(ncom)
638        if len(NCOM)>0:   NCOM.append("")
639       
640        if len(self.extra_comments[2])>0:
641            for excom in self.extra_comments[2]:
642                NCOM.append(excom)
643       
644        if len(self.extra_comments[1])>0: 
645            NCOM.append("Additional Global Attributes defined in the source file and not translated elsewhere:")
646            for excom in self.extra_comments[1]:
647                NCOM.append(excom)
648
649        if hasattr(self, "history"):
650            for h in self.history:
651                NCOM.append(h)
652       
653        if len(NCOM)>0:
654            NCOM.insert(0, "###NASA Ames Normal Comments follow###")
655            NCOM.append("")
656            NCOM.append("###NASA Ames Normal Comments end###")
657        NCOM.append("###Data Section begins on the next line###")
658
659        specCommentsFlag=None
660        SCOM=[]
661        special_comments=self.extra_comments[0]
662        if len(special_comments)>0: 
663            SCOM=["###NASA Ames Special Comments follow###"]
664            specCommentsFlag=1
665        for scom in special_comments:
666            SCOM.append(scom)
667
668
669        #used_var_atts=("name", "long_name", "standard_name", "id",
670            #    "missing_value", "fill_value", "units",
671                #"nasa_ames_var_number", "nasa_ames_aux_var_number")
672        used_var_atts=("id",  "missing_value", "fill_value", "units", 
673                   "nasa_ames_var_number", "nasa_ames_aux_var_number")
674        varCommentsFlag=None
675
676        # Create a string for the Special comments to hold rank-zero vars
677        rank_zero_varsString=[]
678        for var in self.rank_zero_vars:
679            rank_zero_varsString.append("\tVariable %s: %s" % (var.id, getBestName(var)))
680            for att in var.attributes.keys():
681                value=var.attributes[att]
682                if type(value) in (str, float, int):
683                    rank_zero_varsString.append("\t\t%s = %s" % (att, var.attributes[att]))
684            #print "VALUES", dir(var), var._data ; rank_zero_varsString.append("\t\tvalue = %s" % var._data)
685       
686        if len(rank_zero_varsString)>0:
687            rank_zero_varsString.insert(0, "###Singleton Variables defined in the source file follow###")
688            rank_zero_varsString.append("###Singleton Variables defined in the source file end###")
689
690        for var in self.ordered_vars:
691            varflag="unused"
692            name=getBestName(var)
693            for scom,value in var.attributes.items():
694                if type(value) in (type([]), type(Numeric.array([0]))) and len(value)==1:
695                    value=value[0]
696                if type(value) in (str, float, int) and scom not in used_var_atts:
697                    if varflag=="unused":
698                        if varCommentsFlag==None:
699                            varCommentsFlag=1
700                            if specCommentsFlag==None:
701                                SCOM=["###NASA Ames Special Comments follow###"]+rank_zero_varsString
702                            SCOM.append("Additional Variable Attributes defined in the source file and not translated elsewhere:")
703                            SCOM.append("###Variable attributes from source (NetCDF) file follow###")
704                        varflag="using" 
705                        SCOM.append("\tVariable %s: %s" % (var.id, name))
706                    SCOM.append("\t\t%s = %s" % (scom, value))
707
708        if varCommentsFlag==1:  SCOM.append("###Variable attributes from source (NetCDF) file end###")
709        if specCommentsFlag==1:
710            SCOM.append("###NASA Ames Special Comments end###")
711
712        """used_var_atts=("name", "long_name", "standard_name", "id", "missing_value", "fill_value", "units")
713        for var in self.vars:
714            for scom,value in var.attributes.items():
715                name=getBestName(var)
716                if type(value) in (str, float, int) and scom not in used_var_atts:
717                    SCOM.append("\t%s: %s - %s" % (name, scom, value))"""
718
719        # Strip out empty lines (or returns)
720        NCOM_cleaned=[]
721        SCOM_cleaned=[]
722        #hiddenNewLineCount1=0
723        for c in NCOM:
724            if c.strip() not in ("", " ", "  "):
725                #hiddenNewLineCount1=hiddenNewLineCount1+c.count("\n")
726                # Replace new lines within one attribute with a newline and tab so easier to read
727                lines=c.split("\n")
728                for line in lines:
729                    if line!=lines[0]: line="\t"+line
730                    NCOM_cleaned.append(line)
731               
732        #hiddenNewLineCount2=0 
733        for c in SCOM:
734            if c.strip() not in ("", " ", "  "):               
735                #hiddenNewLineCount2=hiddenNewLineCount2+c.count("\n")
736                # Replace new lines within one attribute with a newline and tab so easier to read
737                #c=c.replace("\n", "\n\t")
738                #SCOM_cleaned.append(c)
739                lines=c.split("\n")
740                for line in lines:
741                    if line!=lines[0]: line="\t"+line
742                    SCOM_cleaned.append(line)
743                   
744        self.na_dict["NCOM"]=NCOM_cleaned
745        self.na_dict["NNCOML"]=len(self.na_dict["NCOM"])#+hiddenNewLineCount1
746        self.na_dict["SCOM"]=SCOM_cleaned
747        self.na_dict["NSCOML"]=len(self.na_dict["SCOM"])#+hiddenNewLineCount2
748        return
749
750
751    def defineGeneralHeader(self, header_items={}):
752        """
753        Defines known header items and overwrites any with header_items
754        key/value pairs.
755        """
756        # Check if DATE field previously known in NASA Ames file
757        time_now=time.strftime("%Y %m %d", time.localtime(time.time())).split()
758        if not self.na_dict.has_key("RDATE"):
759            self.na_dict["RDATE"]=time_now
760       
761        if self.ax0.isTime():
762            # Get first date in list
763            try:
764                (unit, start_date)=re.match("(\w+)\s+?since\s+?(\d+-\d+-\d+)", self.ax0.units).groups()           
765                comptime=cdtime.s2c(start_date)
766                first_day=comptime.add(self.na_dict["X"][0][0], getattr(cdtime, unit.capitalize()))
767                self.na_dict["DATE"]=string.replace(str(first_day).split(" ")[0], "-", " ").split()
768            except:
769                msg="Nappy Warning: Could not get the first date in the file. You will need to manually edit the output file."
770                print msg
771                self.output_message.append(msg)
772                self.na_dict["DATE"]=("DATE", "NOT", "KNOWN")
773        else: 
774            if not self.na_dict.has_key("DATE"):
775                msg="Nappy Warning: Could not get the first date in the file. You will need to manually edit the output file."
776                print msg
777                self.output_message.append(msg)
778                self.na_dict["DATE"]=("DATE", "NOT", "KNOWN")
779        self.na_dict["IVOL"]=1
780        self.na_dict["NVOL"]=1
781        for key in header_items.keys():
782             self.na_dict[key]=header_items[key]
783        return
784
Note: See TracBrowser for help on using the repository browser.