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

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

Fixed and broken - got 2110 and 2310 writing from NetCDF. But the solution breaks the basic readNA/writeNA cycle. The reason is that the new code writes the data from NX, DX and X rather than A and NAUXV. Maybe the data really needs to be encoded to NAUXV and A in order for this to work unformly. Hmmmm.

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                    ind_var_name = self.na_dict["XNAME"][0]
378                    self.na_dict["ANAME"] = ["Number of '%s' values recorded in subsequent data records" % ind_var_name]
379                    self.na_dict["AMISS"] = [-9999.999]
380                    self.na_dict["ASCAL"] = [1.0]
381
382                # If FFI is 2310 then need to modify na_dict items for that
383                elif self.na_dict["FFI"] == 2310:
384                    new_x = []
385                    new_nx = []
386                    new_dx = []
387                    ax2_values = var.getAxis(1)[:].tolist()
388                    incr = ax2_values[1] - ax2_values[0]
389
390                    for i in self.ax0[:]:
391                        new_x.append([i, ax2_values])
392                        new_nx.append(len(ax2_values))
393                        new_dx.append(incr)
394
395                    # Re-assign to new lists
396                    self.na_dict["NX"] = new_nx
397                    self.na_dict["X"] = new_x
398                    self.na_dict["DX"] = new_dx
399
400                    # Now auxiliary variable info here with independent var info
401                    ind_var_name = self.na_dict["XNAME"][0]
402                    self.na_dict["ANAME"] = ["Number of '%s' values recorded in subsequent data records" % ind_var_name,
403                                             "'%s' value for first data point" % ind_var_name,
404                                             "'%s' increment" % ind_var_name]
405                    self.na_dict["AMISS"] = [-9999.999, -9999.999, -9999.999]
406                    self.na_dict["ASCAL"] = [1.0, 1.0, 1.0]
407 
408
409    def _defineNAAuxVars(self, aux_vars):
410        """
411        Method to define NASA Ames file object auxiliary variables and their
412        associated metadata.
413        """
414        # Initialise aux var itesms as empty lists unless already defined when
415        # setting up independent variables
416        for item in ("ANAME", "AMISS", "ASCAL", "A"):
417            if not self.na_dict.has_key(item):
418                self.na_dict[item] = [] 
419
420        for var in aux_vars:
421            name = nappy.cdms_utils.var_utils.getBestName(var)
422            self.na_dict["ANAME"].append(name)
423            miss = nappy.cdms_utils.var_utils.getMissingValue(var)
424            if type(miss) != type(1.1):  miss = miss[0]
425            self.na_dict["AMISS"].append(miss)
426            self.na_dict["ASCAL"].append(1)
427            # Populate the variable list with the array
428            self.na_dict["A"].append(var._data)
429
430        self.na_dict["NAUXV"] = len(self.na_dict["ANAME"])
431
432    def _appendAxisDefinition(self, axis):
433        """
434        Method to create the appropriate NASA Ames file object
435        items associated with an axis (independent variable in
436        NASA Ames). It appends to the various self.na_dict containers.
437        """
438        length = len(axis)
439
440        self.na_dict["NX"].append(length)
441        self.na_dict["XNAME"].append(nappy.cdms_utils.var_utils.getBestName(axis))
442        # If only one item in axis values
443        if length < 2:
444            self.na_dict["DX"].append(0)
445            self.na_dict["NXDEF"].append(length)
446            self.na_dict["X"].append(axis[:].tolist())       
447            return
448   
449        incr = axis[1] - axis[0]
450        for i in range(1, length):
451            if (axis[i] - axis[i - 1]) != incr:
452                self.na_dict["DX"].append(0)
453                self.na_dict["NXDEF"].append(length)
454                self.na_dict["X"].append(axis.tolist())
455                break
456        else: # If did not break out of the loop
457            max_length = length
458            if length > 3: 
459                max_length = 3
460            self.na_dict["DX"].append(incr)
461            self.na_dict["NXDEF"].append(max_length)
462            self.na_dict["X"].append(axis[:max_length])
463
464    def _defineNAGlobals(self):
465        """
466        Maps CDMS (NetCDF) global attributes into NASA Ames Header fields.
467        """
468        # Check if we should add to it with locally set rules
469        local_attributes = nappy.utils.getConfigDict()["local_attributes"]
470        for att, value in local_attributes.items():
471            if not nc_to_na_map.has_key(att):
472                nc_to_na_map[key] = value
473
474        self.extra_comments = [[],[],[]]  # Normal comments, special comments, other comments
475        convention_or_reference_comments = []
476
477        for key in self.globals.keys():
478            if key != "first_valid_date_of_data" and type(self.globals[key]) \
479                                       not in (type("s"), type(1.1), type(1)):
480                continue
481
482            # Loop through keys of header/comment items to map
483            if key in nc_to_na_map.keys():
484                if key == "history":
485                    time_string = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(time.time()))
486                    history = "History:\t%s - Converted to NASA Ames format using nappy-%s.\n\t%s" % \
487                                                 (time_string, version, self.globals[key])
488                    history = history.split("\n") 
489                    self.history = []
490                    for h in history:
491                        if h[:8] != "History:" and h[:1] != "\t": 
492                            h = "\t" + h
493                        self.history.append(h) 
494                   
495                elif key == "institution":
496                    # If fields came from NA then extract appropriate fields.
497                    match = re.match(r"(.*)\s+\(ONAME from NASA Ames file\);\s+(.*)\s+\(ORG from NASA Ames file\)\.", 
498                             self.globals[key])
499                    if match:
500                        self.na_dict["ONAME"] = match.groups()[0]
501                        self.na_dict["ORG"] = match.groups()[1]
502                    else:
503                        self.na_dict["ONAME"] = self.globals[key]
504                        self.na_dict["ORG"] = self.globals[key]           
505                    # NOTE: should probably do the following search and replace on all string lines
506                    self.na_dict["ONAME"] = self.na_dict["ONAME"].replace("\n", "  ")
507                    self.na_dict["ORG"] = self.na_dict["ORG"].replace("\n", "  ")
508                                   
509                elif key == "comment":
510                    # Need to work out if they are actually comments from NASA Ames in the first place
511                    comment_lines = self.globals[key].split("\n")
512                    normal_comments = []
513                    normal_comm_flag = None
514                    special_comments = []
515                    special_comm_flag = None
516
517                    for line in comment_lines:
518                        if line.find("###NASA Ames Special Comments follow###") > -1:
519                            special_comm_flag = 1
520                        elif line.find("###NASA Ames Special Comments end###") > -1:
521                            special_comm_flag = None
522                        elif line.find("###NASA Ames Normal Comments follow###") > -1:
523                            normal_comm_flag = 1
524                        elif line.find("###NASA Ames Normal Comments end###") > -1:
525                            normal_comm_flag = None     
526                        elif special_comm_flag == 1:
527                            special_comments.append(line)
528                        elif normal_comm_flag == 1:
529                            normal_comments.append(line)
530                        elif line.find("###Data Section begins on the next line###") > -1:
531                            pass
532                        else:
533                            normal_comments.append(line)           
534                   
535                    self.extra_comments = [special_comments, normal_comments, []]                   
536                                   
537                elif key == "first_valid_date_of_data":
538                    self.na_dict["DATE"] = self.globals[key]
539               
540                elif key in ("Conventions", "references"):
541                    #convention_or_reference_comments.append("%s:   %s" % (key, self.globals[key]))
542                    self.extra_comments[2].append("%s:   %s" % (key, self.globals[key]))
543                else:
544                    self.na_dict[nc_to_na_map[key]] = self.globals[key]
545            else:
546                self.extra_comments[2].append("%s:   %s" % (key, self.globals[key]))
547        return
548
549
550    def _defineNAComments(self, normal_comments=[], special_comments=[]):
551        """
552        Defines the Special and Normal comments sections in the NASA Ames file
553        object - including information gathered from the defineNAGlobals method.
554        """
555       
556        if hasattr(self, "ncom"):  normal_comments = self.ncom + normal_comments
557
558        NCOM = []
559        for ncom in normal_comments:
560            NCOM.append(ncom)
561
562        if len(NCOM) > 0:   NCOM.append("")
563       
564        if len(self.extra_comments[2]) > 0:
565            for excom in self.extra_comments[2]:
566                NCOM.append(excom)
567       
568        if len(self.extra_comments[1]) > 0: 
569            NCOM.append("Additional Global Attributes defined in the source file and not translated elsewhere:")
570            for excom in self.extra_comments[1]:
571                NCOM.append(excom)
572
573        if hasattr(self, "history"):
574            for h in self.history:
575                NCOM.append(h)
576       
577        if len(NCOM) > 0:
578            NCOM.insert(0, "###NASA Ames Normal Comments follow###")
579            NCOM.append("")
580            NCOM.append("###NASA Ames Normal Comments end###")
581            NCOM.append("###Data Section begins on the next line###")
582
583        spec_comm_flag = None
584        SCOM = []
585        special_comments = self.extra_comments[0]
586        if len(special_comments) > 0: 
587            SCOM = ["###NASA Ames Special Comments follow###"]
588
589            spec_comm_flag = 1
590        for scom in special_comments:
591            SCOM.append(scom)
592
593        used_var_atts = ("id",  "missing_value", "fill_value", "units", 
594                   "nasa_ames_var_number", "nasa_ames_aux_var_number")
595        var_comm_flag = None
596
597        # Create a string for the Special comments to hold rank-zero vars
598        rank_zero_vars_string = []
599
600        for var in self.rank_zero_vars:
601            rank_zero_vars_string.append("\tVariable %s: %s" % (var.id, nappy.cdms_utils.var_utils.getBestName(var)))
602
603            for att in var.attributes.keys():
604                value = var.attributes[att]
605
606                if type(value) in (type("s"), type(1.0), type(1)):
607
608                    rank_zero_vars_string.append("\t\t%s = %s" % (att, var.attributes[att]))
609
610        if len(rank_zero_vars_string) > 0:
611            rank_zero_vars_string.insert(0, "###Singleton Variables defined in the source file follow###")
612            rank_zero_vars_string.append("###Singleton Variables defined in the source file end###")
613
614        for var in self.ordered_vars:
615            varflag = "unused"
616            name = nappy.cdms_utils.var_utils.getBestName(var)
617
618            for scom,value in var.attributes.items():
619                if type(value) in (type([]), type(Numeric.array([0]))) and len(value) == 1:
620                    value = value[0]
621
622                if type(value) in (type("s"), type(1.1), type(1)) and scom not in used_var_atts:
623                    if varflag == "unused":
624                        if var_comm_flag == None:
625                            var_comm_flag = 1
626
627                    if spec_comm_flag == None:
628                        SCOM = ["###NASA Ames Special Comments follow###"] + rank_zero_vars_string
629                        SCOM.append("Additional Variable Attributes defined in the source file and not translated elsewhere:")
630                        SCOM.append("###Variable attributes from source (NetCDF) file follow###")
631                        varflag = "using" 
632
633                    SCOM.append("\tVariable %s: %s" % (var.id, name))
634                    SCOM.append("\t\t%s = %s" % (scom, value))
635
636        if var_comm_flag == 1: 
637            SCOM.append("###Variable attributes from source (NetCDF) file end###")
638        if spec_comm_flag == 1:
639            SCOM.append("###NASA Ames Special Comments end###")
640
641        # Strip out empty lines (or returns)
642        NCOM_cleaned = []
643        SCOM_cleaned = []
644
645        for c in NCOM:
646            if c.strip() not in ("", " ", "  "):
647                # Replace new lines within one attribute with a newline and tab so easier to read
648                lines = c.split("\n")
649                for line in lines:
650                    if line != lines[0]: 
651                        line = "\t" + line
652                    NCOM_cleaned.append(line)
653                       
654        for c in SCOM:
655            if c.strip() not in ("", " ", "  "):               
656                        # Replace new lines within one attribute with a newline and tab so easier to read
657                lines = c.split("\n")
658                for line in lines:
659                    if line != lines[0]: 
660                        line = "\t" + line
661                    SCOM_cleaned.append(line)
662                   
663        self.na_dict["NCOM"] = NCOM_cleaned
664        self.na_dict["NNCOML"] = len(self.na_dict["NCOM"])
665        self.na_dict["SCOM"] = SCOM_cleaned
666        self.na_dict["NSCOML"] = len(self.na_dict["SCOM"])
667        return
668
669
670    def _defineGeneralHeader(self, header_items={}):
671        """
672        Defines known header items and overwrites any with header_items
673        key/value pairs.
674        """
675        # Check if DATE field previously known in NASA Ames file
676        time_now = time.strftime("%Y %m %d", time.localtime(time.time())).split()
677        if not self.na_dict.has_key("RDATE"):
678            self.na_dict["RDATE"] = time_now
679       
680        if self.ax0.isTime():
681            # Get first date in list
682            try:
683                (unit, start_date) = re.match("(\w+)\s+?since\s+?(\d+-\d+-\d+)", self.ax0.units).groups()           
684                comptime = cdtime.s2c(start_date)
685                first_day = comptime.add(self.na_dict["X"][0], getattr(cdtime, unit.capitalize()))
686                self.na_dict["DATE"] = str(first_day).split(" ")[0].replace("-", " ").split()
687            except:
688                msg = "Nappy Warning: Could not get the first date in the file. You will need to manually edit the output file."
689                print msg
690                self.output_message.append(msg)
691                self.na_dict["DATE"] = ("DATE", "NOT", "KNOWN")
692        else: 
693            if not self.na_dict.has_key("DATE"):
694                msg = "Nappy Warning: Could not get the first date in the file. You will need to manually edit the output file."
695                print msg
696                self.output_message.append(msg)
697                self.na_dict["DATE"] = ("DATE", "NOT", "KNOWN")
698            else:
699                pass # i.e. use existing DATE
700
701        self.na_dict["IVOL"] = 1
702        self.na_dict["NVOL"] = 1
703        for key in header_items.keys():
704             self.na_dict[key] = header_items[key]
Note: See TracBrowser for help on using the repository browser.