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

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

Converted all global_atts to lists of key,value pairs so order can be maintained.

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