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

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

Various nappy updates to improve comments and combining them with inserted comments.

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