source: nappy/trunk/nappy/nc_interface/na_content_collector.py @ 3522

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/nappy/trunk/nappy/nc_interface/na_content_collector.py@3522
Revision 3522, 29.8 KB checked in by astephen, 11 years ago (diff)

Fixed tests and some bugs in na_content_collector.py

Line 
1#   Copyright (C) 2004 CCLRC & NERC( Natural Environment Research Council ).
2#   This software may be distributed under the terms of the
3#   Q Public License, version 1.0 or later. http://ndg.nerc.ac.uk/public_docs/QPublic_license.txt
4
5"""
6cdms_to_na.py
7=============
8
9Holds the class CDMSToNA that converts a set of CDMS variables and global attributes.
10
11"""
12
13# Imports from python standard library
14import sys
15import time
16import re
17
18# Import from nappy package
19from nappy.na_error import na_error
20import nappy.utils
21import nappy.cdms_utils.axis_utils
22import nappy.cdms_utils.var_utils
23import nappy.utils.common_utils
24import nappy.na_file.na_core
25
26config_dict = nappy.utils.getConfigDict()
27nc_to_na_map = config_dict["nc_to_na_map"]
28version = nappy.utils.getVersion()
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
40DEBUG = nappy.utils.getDebug() 
41
42class NAContentCollector(nappy.na_file.na_core.NACore):
43    """
44    Class to build a NASA Ames File object from a set of
45    CDMS variables and global attributes (optional).
46    """
47   
48    def __init__(self, variables, global_attributes={}, requested_ffi=None):
49        """
50        Sets up instance variables and calls appropriate methods to
51        generate sections of NASA Ames file object.
52
53        Input arguments are:
54          * variables - list/tuple of actual CDMS variables
55          * global_attributes - dictionary of user-defined globals to include.
56
57        Typical usage:
58        >>> x = NAContentCollector(["temp", "precip"])
59        >>> x.collectNAContent()
60        >>> if x.found_na == True:
61        ...     print x.na_dict, x.var_ids, x.unused_vars
62        """
63        self.output_message = []
64        self.na_dict = {}
65        self.vars = variables
66
67        # Note that self.var_ids will be a list containing:
68        #    [ordered_vars,  auxiliary_vars,   rank_zero_vars]
69        self.var_ids = None
70        self.globals = global_attributes       
71        self.requested_ffi = requested_ffi
72        self.rank_zero_vars = []
73        self.rank_zero_var_ids = []
74
75        # Create a flag to check if anything found
76        self.found_na = False
77
78
79    def collectNAContent(self):
80        """
81        Collect NASA Ames content. Save the contents to the following instance
82        attributes:
83         * self.na_dict
84         * self.var_ids
85         * self.unused_vars
86        """
87        (self.ordered_vars, aux_vars) = self._analyseVariables()
88     
89        if self.ordered_vars == []:
90            print "WARNING: No NASA Ames content created."
91            self.unused_vars = []
92        else:
93            self.var_ids = [[var.id for var in self.ordered_vars],
94                            [var.id for var in aux_vars], 
95                            self.rank_zero_var_ids]
96            self.na_dict["NLHEAD"] = -999
97            self._defineNAVars(self.ordered_vars)
98            self._defineNAAuxVars(aux_vars)
99            self._defineNAGlobals()
100            self._defineNAComments()
101            self._defineGeneralHeader()
102            self.found_na = True
103
104
105    def _analyseVariables(self):
106        """
107        Method to examine the content of CDMS variables to return
108        a tuple of two lists containing variables and auxiliary variables
109        for the NASA Ames file object.
110        Variables not compatible with the first file are put in self.unused_vars
111        """
112        self.unused_vars = []
113        ffis_limited = False
114
115        highest_rank = -1
116        best_var = None
117        count = 0
118
119        # Need to get highest ranked variable (most dimensions) so that we can work out FFI
120        for var in self.vars:
121            msg = "Analysing: %s" % var.id
122            print msg
123            self.output_message.append(msg)
124            count = count + 1
125            # get rank
126            rank = var.rank()
127
128            # Deal with singleton variables
129            if rank == 0: 
130                self.rank_zero_vars.append(var)
131                self.rank_zero_var_ids.append(var.id)
132                continue
133           
134            # Update highest if highest found or if equals highest with bigger size
135            if rank > highest_rank or (rank == highest_rank and var.size() > best_var.size()):
136                highest_rank = rank
137                best_var = var
138                best_var_index = count
139
140        # If all are zero ranked variables or no vars identified/found then we cannot write any to NASA Ames and return ([], [])
141        if len(self.rank_zero_vars) == len(self.vars) or best_var is None: 
142            return ([], [])
143
144        # Now start to sort the variables into main and auxiliary
145        vars_for_na = [best_var]
146        aux_vars_for_na = []
147        shape = best_var.shape
148        number_of_dims = len(shape)
149        self.na_dict["NIV"] = number_of_dims
150
151
152        # If 2D then do a quick test to see if 2310 is feasible (i.e. uniformly spaced 2nd axis)
153        if number_of_dims == 2:
154            ffis_limited = [2010, 2110]
155            axis = best_var.getAxis(1)
156            if nappy.cdms_utils.axis_utils.isUniformlySpaced(axis):
157                ffis_limited.append(2310)
158
159        # Get the axes for the main variable being used
160        best_var_axes = best_var.getAxisList()
161       
162        # Get other variable info
163        rest_of_the_vars = self.vars[:best_var_index - 1] + self.vars[best_var_index:]
164
165        for var in rest_of_the_vars:
166
167            if var.id in self.rank_zero_var_ids: continue
168
169            # What to do with variables that have different number of dimensions or different shape
170            if len(var.shape) != number_of_dims or var.shape != shape: 
171                # Could it be an auxiliary variable?
172                if len(var.shape) != 1: 
173                    self.unused_vars.append(var)
174                    continue
175
176                first_axis = var.getAxis(0)
177                # Check if axis is identical to first axis of main best variable, if so, can be auxiliary var
178                if nappy.cdms_utils.axis_utils.areAxesIdentical(best_var_axes[0], first_axis) == False: 
179
180                    # If not identical, then it might still qualify as an auxiliary every n time points - valid for 1020
181                    if len(var.shape) == 1:
182                        nvpm = nappy.cdms_utils.axis_utils.isAxisRegularlySpacedSubsetOf(first_axis, best_var_axes[0])
183                        # NVPM is the number of implied values which is equal to (len(ax2)/len(ax1))
184                        if nvpm:
185                            ffis_limited = [1020]
186                            self.na_dict["NVPM"] = nvpm
187                        else: # if returned False, i.e. not regular subset axis
188                            self.unused_vars.append(var)
189                    else:
190                        self.unused_vars.append(var)
191                        continue
192
193                else:
194                    # This could be used as a standard auxiliary variable
195                    if ffis_limited in ([1020],):
196                        # Already fixed on 1020 and cannot collect incompatible FFI vars so do not use
197                        self.unused_vars.append(var)
198                    else:
199                        aux_vars_for_na.append(var) 
200
201            else:
202                this_var_axes = var.getAxisList()
203
204                # Loop through dimensions
205                for i in range(number_of_dims):           
206                    if nappy.cdms_utils.axis_utils.areAxesIdentical(best_var_axes[i], this_var_axes[i]) == False:
207                        self.unused_vars.append(var)
208                        continue
209
210                # OK, I think the current variable is compatible to write with the best variable along with a NASA Ames file
211                vars_for_na.append(var)
212               
213        # Send vars_for_na AND aux_vars_for_na to a method to check if they have previously been mapped
214        # from NASA Ames. In which case we'll write them back in the order they were initially read from the input file.
215        (vars_for_na, aux_vars_for_na) = self._reorderVarsIfPreviouslyNA(vars_for_na, aux_vars_for_na)
216
217        # Get the FFI
218        self.na_dict["FFI"] = self._decideFileFormatIndex(number_of_dims, aux_vars_for_na, ffis_limited)
219        return (vars_for_na, aux_vars_for_na)
220
221
222    def _reorderVarsIfPreviouslyNA(self, vars_for_na, aux_vars_for_na):
223        """
224        Re-order if they previously came from NASA Ames files (i.e. including the
225        attribute 'nasa_ames_var_number'). Return re-ordered or unchanged pair of
226        (vars_for_na, aux_vars_for_na).
227        """
228        # THIS SHOULD REALLY BE DONE IN A LOOP
229        # First do the main variables
230        ordered_vars = [None] * 1000 # Make a long list to put vars in
231        # Create a list of other variables to collect up any that are not labelled as nasa ames variables
232        other_vars = []
233        for var in vars_for_na:
234            if hasattr(var, "nasa_ames_var_number"):
235                ordered_vars[var.nasa_ames_var_number[0]] = var
236            else:
237                other_vars.append(var)
238
239        # Remake vars_for_na now in new order and clean out any that are "None"
240        vars_for_na = []
241        for var in ordered_vars:
242            if var != None: 
243                vars_for_na.append(var)
244
245        vars_for_na = vars_for_na + other_vars
246
247        # Now re-order the Auxiliary variables if they previously came from NASA
248        ordered_aux_vars = [None] * 1000
249        other_aux_vars = []
250
251        for var in aux_vars_for_na:
252            if hasattr(var, "nasa_ames_aux_var_number"):
253                ordered_aux_vars[var.nasa_ames_aux_var_number[0]] = var
254            else:
255                other_aux_vars.append(var)
256
257        # Remake aux_vars_for_na now in order
258        aux_vars_for_na = []
259        for var in ordered_aux_vars:
260            if var != None: 
261                aux_vars_for_na.append(var)
262
263        aux_vars_for_na = aux_vars_for_na + other_aux_vars     
264        return (vars_for_na, aux_vars_for_na)
265
266
267    def _decideFileFormatIndex(self, number_of_dims, aux_vars_for_na, ffis_limited=False):
268        """
269        Based on the number of dimensions and the NASA Ames dictionary return
270        the File Format Index.
271        If there is a choice then make the most sensible selection.
272        If the user has specified a 'requested_ffi' then try and deliver
273        that. Raise an error if not possible.
274        """
275        # If ffis_limited is set then must use one of those
276        if self.requested_ffi and ffis_limited:
277            if self.requested_ffi not in ffis_limited:
278                raise Exception("Cannot write this data to FFI '" + str(self.requested_ffi) + "', can only write to: " + str(ffis_limited) + ".")
279            else:
280                return self.requested_ffi
281
282        # Base the sub-selection on number of dimensions
283        if number_of_dims > 4:
284            raise Exception("Cannot write variables defined against greater than 4 axes in NASA Ames format.")
285        elif number_of_dims > 2: 
286            ffi = 10 + (number_of_dims * 1000)
287        elif number_of_dims == 2:
288            if self.requested_ffi in (2010, 2110, 2310):
289                ffi = self.requested_ffi
290            else:
291                ffi = 2010
292        else:
293            if len(aux_vars_for_na) > 0 or (self.na_dict.has_key("NAUXV") and self.na_dict["NAUXV"] > 0):
294                ffi = 1010
295            else:
296                ffi = 1001
297
298        if self.requested_ffi and ffi != self.requested_ffi:
299            raise Exception("Cannot write this data to FFI '" + str(self.requested_ffi) + "', can only write to: " + str(ffi) + ".")
300        return ffi
301
302
303    def _defineNAVars(self, vars):
304        """
305        Method to define NASA Ames file object variables and their
306        associated metadata.
307        """
308        self.na_dict["NV"] = len(vars)
309        self.na_dict["VNAME"] = []
310        self.na_dict["VMISS"] = []
311        self.na_dict["VSCAL"] = []
312        self.na_dict["V"] = []
313
314        for var in vars:
315            name = nappy.cdms_utils.var_utils.getBestName(var)
316            self.na_dict["VNAME"].append(name)
317            miss = nappy.cdms_utils.var_utils.getMissingValue(var)
318            if type(miss) not in (type(1.2), type(1), type(1L)): 
319                miss = miss[0]
320            self.na_dict["VMISS"].append(miss)
321            self.na_dict["VSCAL"].append(1)
322            # Populate the variable list with the array
323            self.na_dict["V"].append(var._data)
324
325            # Create independent variable info
326            if not self.na_dict.has_key("X"):
327                # Set up lists ready to populate with values
328                self.na_dict["NXDEF"] = []
329                self.na_dict["NX"] = []
330
331                self.ax0 = var.getAxis(0)
332
333                self.na_dict["X"] = [self.ax0[:].tolist()]
334                self.na_dict["XNAME"] = [nappy.cdms_utils.var_utils.getBestName(self.ax0)]
335
336                if len(self.ax0) == 1:
337                    self.na_dict["DX"] = [0]
338                else:
339                    incr = self.ax0[1] - self.ax0[0]
340                    # Set default increment as gap between first two
341                    self.na_dict["DX"] = [incr]
342                    # Now overwrite it as zero if non-uniform interval in axis
343                    for i in range(1, len(self.ax0)):
344                        if (self.ax0[i] - self.ax0[i - 1]) != incr:
345                            self.na_dict["DX"] = [0]
346                            break
347
348                # If 1D only then "X" should only be a list and not list of lists
349                if self.na_dict["FFI"] in (1001, 1010, 1020):
350                    self.na_dict["X"] = self.na_dict["X"][0]
351
352                # If FFI is 1020 need to reduce axis down to reduced values as most are implied
353                if self.na_dict["FFI"] == 1020: 
354                    vals = self.na_dict["X"]
355                    self.na_dict["X"] = vals[0:len(vals):self.na_dict["NVPM"]] 
356
357                # Now add the rest of the axes to the self.na_dict objects
358                for axis in var.getAxisList()[1:]:
359                    self._appendAxisDefinition(axis)
360
361                # If FFI is 2110 then need to modify the "NX" and "X" lists to cope with odd shape
362                # Also need to add NX to auxiliary variables
363                if self.na_dict["FFI"] == 2110:
364                    new_x = []
365                    new_nx = []
366                    ax2_values = var.getAxis(1)[:].tolist()
367
368                    for i in self.ax0[:]:
369                        new_x.append([i, ax2_values])
370                        new_nx.append(len(ax2_values))
371
372                    # Re-assign to new lists
373                    self.na_dict["NX"] = new_nx
374                    self.na_dict["X"] = new_x                   
375
376                    # Now auxiliary variable info here with independent var info
377                    # First aux var is NX
378                    self.na_dict["A"] = [self.na_dict["NX"][:]]
379                    ind_var_name = self.na_dict["XNAME"][0]
380                    self.na_dict["ANAME"] = ["Number of '%s' values recorded in subsequent data records" % ind_var_name]
381                    self.na_dict["AMISS"] = [-9999.999]
382                    self.na_dict["ASCAL"] = [1.0]
383
384                # If FFI is 2310 then need to modify na_dict items for that
385                elif self.na_dict["FFI"] == 2310:
386                    new_x = []
387                    new_nx = []
388                    new_dx = []
389                    ax2_values = var.getAxis(1)[:].tolist()
390                    incr = ax2_values[1] - ax2_values[0]
391
392                    for i in self.ax0[:]:
393                        new_x.append([i, ax2_values])
394                        new_nx.append(len(ax2_values))
395                        new_dx.append(incr)
396
397                    # Re-assign to new lists
398                    self.na_dict["NX"] = new_nx
399                    self.na_dict["X"] = new_x
400                    self.na_dict["DX"] = new_dx
401
402                    # Now auxiliary variable info here with independent var info
403                    # First three aux vars are NX, X0 and DX
404                    self.na_dict["A"] = []
405                    self.na_dict["A"].append(self.na_dict["NX"][:])
406                    self.na_dict["A"].append([i[1][0] for i in self.na_dict["X"]])
407                    self.na_dict["A"].append(self.na_dict["DX"][:])
408                    ind_var_name = self.na_dict["XNAME"][0]
409                    self.na_dict["ANAME"] = ["Number of '%s' values recorded in subsequent data records" % ind_var_name,
410                                             "'%s' value for first data point" % ind_var_name,
411                                             "'%s' increment" % ind_var_name]
412                    self.na_dict["AMISS"] = [-9999.999, -9999.999, -9999.999]
413                    self.na_dict["ASCAL"] = [1.0, 1.0, 1.0]
414 
415
416    def _defineNAAuxVars(self, aux_vars):
417        """
418        Method to define NASA Ames file object auxiliary variables and their
419        associated metadata. Note that "A" may already have content if
420        independent variable items (relating to "X") are defined as aux vars.
421        """
422        # Initialise aux var itesms as empty lists unless already defined when
423        # setting up independent variables
424        for item in ("ANAME", "AMISS", "ASCAL", "A"):
425            if not self.na_dict.has_key(item):
426                self.na_dict[item] = [] 
427
428        for var in aux_vars:
429            name = nappy.cdms_utils.var_utils.getBestName(var)
430            self.na_dict["ANAME"].append(name)
431            miss = nappy.cdms_utils.var_utils.getMissingValue(var)
432            if type(miss) != type(1.1):  miss = miss[0]
433            self.na_dict["AMISS"].append(miss)
434            self.na_dict["ASCAL"].append(1)
435            # Populate the variable list with the array
436            self.na_dict["A"].append(var[:].tolist())
437
438        self.na_dict["NAUXV"] = len(self.na_dict["A"])
439
440    def _appendAxisDefinition(self, axis):
441        """
442        Method to create the appropriate NASA Ames file object
443        items associated with an axis (independent variable in
444        NASA Ames). It appends to the various self.na_dict containers.
445        """
446        length = len(axis)
447
448        self.na_dict["NX"].append(length)
449        self.na_dict["XNAME"].append(nappy.cdms_utils.var_utils.getBestName(axis))
450        # If only one item in axis values
451        if length < 2:
452            self.na_dict["DX"].append(0)
453            self.na_dict["NXDEF"].append(length)
454            self.na_dict["X"].append(axis[:].tolist())       
455            return
456   
457        incr = axis[1] - axis[0]
458        for i in range(1, length):
459            if (axis[i] - axis[i - 1]) != incr:
460                self.na_dict["DX"].append(0)
461                self.na_dict["NXDEF"].append(length)
462                self.na_dict["X"].append(axis[:].tolist())
463                break
464        else: # If did not break out of the loop
465            max_length = length
466            if length > 3: 
467                max_length = 3
468            self.na_dict["DX"].append(incr)
469            self.na_dict["NXDEF"].append(max_length)
470            self.na_dict["X"].append(axis[:max_length])
471
472    def _defineNAGlobals(self):
473        """
474        Maps CDMS (NetCDF) global attributes into NASA Ames Header fields.
475        """
476        # Check if we should add to it with locally set rules
477        local_attributes = nappy.utils.getConfigDict()["local_attributes"]
478        for att, value in local_attributes.items():
479            if not nc_to_na_map.has_key(att):
480                nc_to_na_map[key] = value
481
482        self.extra_comments = [[],[],[]]  # Normal comments, special comments, other comments
483        convention_or_reference_comments = []
484
485        for key in self.globals.keys():
486            if key != "first_valid_date_of_data" and type(self.globals[key]) \
487                                       not in (type("s"), type(1.1), type(1)):
488                continue
489
490            # Loop through keys of header/comment items to map
491            if key in nc_to_na_map.keys():
492                if key == "history":
493                    time_string = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(time.time()))
494                    history = "History:\t%s - Converted to NASA Ames format using nappy-%s.\n\t%s" % \
495                                                 (time_string, version, self.globals[key])
496                    history = history.split("\n") 
497                    self.history = []
498                    for h in history:
499                        if h[:8] != "History:" and h[:1] != "\t": 
500                            h = "\t" + h
501                        self.history.append(h) 
502                   
503                elif key == "institution":
504                    # If fields came from NA then extract appropriate fields.
505                    match = re.match(r"(.*)\s+\(ONAME from NASA Ames file\);\s+(.*)\s+\(ORG from NASA Ames file\)\.", 
506                             self.globals[key])
507                    if match:
508                        self.na_dict["ONAME"] = match.groups()[0]
509                        self.na_dict["ORG"] = match.groups()[1]
510                    else:
511                        self.na_dict["ONAME"] = self.globals[key]
512                        self.na_dict["ORG"] = self.globals[key]           
513                    # NOTE: should probably do the following search and replace on all string lines
514                    self.na_dict["ONAME"] = self.na_dict["ONAME"].replace("\n", "  ")
515                    self.na_dict["ORG"] = self.na_dict["ORG"].replace("\n", "  ")
516                                   
517                elif key == "comment":
518                    # Need to work out if they are actually comments from NASA Ames in the first place
519                    comment_lines = self.globals[key].split("\n")
520                    normal_comments = []
521                    normal_comm_flag = None
522                    special_comments = []
523                    special_comm_flag = None
524
525                    for line in comment_lines:
526                        if line.find("###NASA Ames Special Comments follow###") > -1:
527                            special_comm_flag = 1
528                        elif line.find("###NASA Ames Special Comments end###") > -1:
529                            special_comm_flag = None
530                        elif line.find("###NASA Ames Normal Comments follow###") > -1:
531                            normal_comm_flag = 1
532                        elif line.find("###NASA Ames Normal Comments end###") > -1:
533                            normal_comm_flag = None     
534                        elif special_comm_flag == 1:
535                            special_comments.append(line)
536                        elif normal_comm_flag == 1:
537                            normal_comments.append(line)
538                        elif line.find("###Data Section begins on the next line###") > -1:
539                            pass
540                        else:
541                            normal_comments.append(line)           
542                   
543                    self.extra_comments = [special_comments, normal_comments, []]                   
544                                   
545                elif key == "first_valid_date_of_data":
546                    self.na_dict["DATE"] = self.globals[key]
547               
548                elif key in ("Conventions", "references"):
549                    #convention_or_reference_comments.append("%s:   %s" % (key, self.globals[key]))
550                    self.extra_comments[2].append("%s:   %s" % (key, self.globals[key]))
551                else:
552                    self.na_dict[nc_to_na_map[key]] = self.globals[key]
553            else:
554                self.extra_comments[2].append("%s:   %s" % (key, self.globals[key]))
555        return
556
557
558    def _defineNAComments(self, normal_comments=[], special_comments=[]):
559        """
560        Defines the Special and Normal comments sections in the NASA Ames file
561        object - including information gathered from the defineNAGlobals method.
562        """
563       
564        if hasattr(self, "ncom"):  normal_comments = self.ncom + normal_comments
565
566        NCOM = []
567        for ncom in normal_comments:
568            NCOM.append(ncom)
569
570        if len(NCOM) > 0:   NCOM.append("")
571       
572        if len(self.extra_comments[2]) > 0:
573            for excom in self.extra_comments[2]:
574                NCOM.append(excom)
575       
576        if len(self.extra_comments[1]) > 0: 
577            NCOM.append("Additional Global Attributes defined in the source file and not translated elsewhere:")
578            for excom in self.extra_comments[1]:
579                NCOM.append(excom)
580
581        if hasattr(self, "history"):
582            for h in self.history:
583                NCOM.append(h)
584       
585        if len(NCOM) > 0:
586            NCOM.insert(0, "###NASA Ames Normal Comments follow###")
587            NCOM.append("")
588            NCOM.append("###NASA Ames Normal Comments end###")
589            NCOM.append("###Data Section begins on the next line###")
590
591        spec_comm_flag = None
592        SCOM = []
593        special_comments = self.extra_comments[0]
594        if len(special_comments) > 0: 
595            SCOM = ["###NASA Ames Special Comments follow###"]
596
597            spec_comm_flag = 1
598        for scom in special_comments:
599            SCOM.append(scom)
600
601        used_var_atts = ("id",  "missing_value", "fill_value", "units", 
602                   "nasa_ames_var_number", "nasa_ames_aux_var_number")
603        var_comm_flag = None
604
605        # Create a string for the Special comments to hold rank-zero vars
606        rank_zero_vars_string = []
607
608        for var in self.rank_zero_vars:
609            rank_zero_vars_string.append("\tVariable %s: %s" % (var.id, nappy.cdms_utils.var_utils.getBestName(var)))
610
611            for att in var.attributes.keys():
612                value = var.attributes[att]
613
614                if type(value) in (type("s"), type(1.0), type(1)):
615
616                    rank_zero_vars_string.append("\t\t%s = %s" % (att, var.attributes[att]))
617
618        if len(rank_zero_vars_string) > 0:
619            rank_zero_vars_string.insert(0, "###Singleton Variables defined in the source file follow###")
620            rank_zero_vars_string.append("###Singleton Variables defined in the source file end###")
621
622        for var in self.ordered_vars:
623            varflag = "unused"
624            name = nappy.cdms_utils.var_utils.getBestName(var)
625
626            for scom,value in var.attributes.items():
627                if type(value) in (type([]), type(Numeric.array([0]))) and len(value) == 1:
628                    value = value[0]
629
630                if type(value) in (type("s"), type(1.1), type(1)) and scom not in used_var_atts:
631                    if varflag == "unused":
632                        if var_comm_flag == None:
633                            var_comm_flag = 1
634
635                    if spec_comm_flag == None:
636                        SCOM = ["###NASA Ames Special Comments follow###"] + rank_zero_vars_string
637                        SCOM.append("Additional Variable Attributes defined in the source file and not translated elsewhere:")
638                        SCOM.append("###Variable attributes from source (NetCDF) file follow###")
639                        varflag = "using" 
640
641                    SCOM.append("\tVariable %s: %s" % (var.id, name))
642                    SCOM.append("\t\t%s = %s" % (scom, value))
643
644        if var_comm_flag == 1: 
645            SCOM.append("###Variable attributes from source (NetCDF) file end###")
646        if spec_comm_flag == 1:
647            SCOM.append("###NASA Ames Special Comments end###")
648
649        # Strip out empty lines (or returns)
650        NCOM_cleaned = []
651        SCOM_cleaned = []
652
653        for c in NCOM:
654            if c.strip() not in ("", " ", "  "):
655                # Replace new lines within one attribute with a newline and tab so easier to read
656                lines = c.split("\n")
657                for line in lines:
658                    if line != lines[0]: 
659                        line = "\t" + line
660                    NCOM_cleaned.append(line)
661                       
662        for c in SCOM:
663            if c.strip() not in ("", " ", "  "):               
664                        # Replace new lines within one attribute with a newline and tab so easier to read
665                lines = c.split("\n")
666                for line in lines:
667                    if line != lines[0]: 
668                        line = "\t" + line
669                    SCOM_cleaned.append(line)
670                   
671        self.na_dict["NCOM"] = NCOM_cleaned
672        self.na_dict["NNCOML"] = len(self.na_dict["NCOM"])
673        self.na_dict["SCOM"] = SCOM_cleaned
674        self.na_dict["NSCOML"] = len(self.na_dict["SCOM"])
675        return
676
677
678    def _defineGeneralHeader(self, header_items={}):
679        """
680        Defines known header items and overwrites any with header_items
681        key/value pairs.
682        """
683        # Check if DATE field previously known in NASA Ames file
684        time_now = [int(i) for i in time.strftime("%Y %m %d", time.localtime(time.time())).split()]
685
686        if not self.na_dict.has_key("RDATE"):
687            self.na_dict["RDATE"] = time_now
688       
689        if self.ax0.isTime():
690            # Get first date in list
691            try:
692                (unit, start_date) = re.match("(\w+)\s+?since\s+?(\d+-\d+-\d+)", self.ax0.units).groups()           
693                comptime = cdtime.s2c(start_date)
694                first_day = comptime.add(self.na_dict["X"][0], getattr(cdtime, unit.capitalize()))
695                self.na_dict["DATE"] = [int(i) for i in str(first_day).split(" ")[0].replace("-", " ").split()]
696            except:
697                msg = "Nappy Warning: Could not get the first date in the file. You will need to manually edit the output file."
698                print msg
699                self.output_message.append(msg)
700                self.na_dict["DATE"] = [-999] * 3 
701        else: 
702            if not self.na_dict.has_key("DATE"):
703                msg = "Nappy Warning: Could not get the first date in the file. You will need to manually edit the output file."
704                print msg
705                self.output_message.append(msg)
706                self.na_dict["DATE"] = [-999] * 3 
707            else:
708                pass # i.e. use existing DATE
709
710        self.na_dict["IVOL"] = 1
711        self.na_dict["NVOL"] = 1
712        for key in header_items.keys():
713             self.na_dict[key] = header_items[key]
Note: See TracBrowser for help on using the repository browser.