source: nappy/trunk/nappy/naToCdms.py @ 3314

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

Re-factored under a nappy directory for a start.

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"""
6naToCdms.py
7===========
8
9Container module for mixin class NAToCdms that is sub-classed
10by NAFile classes.
11
12"""
13
14# Imports from python standard library
15import sys
16sys.path.append(r"..")
17import os
18import re
19import time
20
21# Import from nappy package
22import cdmsMap
23from naError import *
24import version
25
26# Import external packages (if available)
27if sys.platform.find("win")>-1:
28    pass
29else:
30    try:
31        import cdms, Numeric
32        cdms.setAutoBounds("off") 
33    except:
34        print "Cannot import cdms package, CDAT-dependent features will not work."
35
36# Define global variables
37safe_nc_id=re.compile("[\/\s\[\(\)\]\=\+\-\?\#\~\@\&\$\%\!\*\{\}\^]+")
38
39time_units_pattn=re.compile("\w+\s+since\s+\d{4}-\d{1,2}-\d{1,2}\s+\d+:\d+:\d+")
40
41class NAToCdms:
42    """
43    Abstract class inherited by standard NAFile classes for mapping of
44    NASA Ames to CDMS-type files.
45    """
46   
47    def toCdmsFile(self, filename, time_units=None, time_warning="yes", variables="all", \
48                   aux_variables="all", global_attributes={"Conventions":"CF-1.0"}, \
49                   rename_variables={}, ffi=None):
50        """
51        Main method for writing the existing NASA Ames dictionary out
52        to a CDMS (NetCDF) file.
53        """
54        self.outputMessage=[]  # for output printing message
55        if self.FFI in (2110, 2160, 2310): 
56            msg="""ERROR: Cannot convert NASA Ames File Format Index (FFI) %s to NetCDF.
57No mapping implemented yet.""" % self.FFI
58            print msg
59            self.outputMessage.append(msg)
60            sys.exit()
61           
62        print "V:", variables
63        #print "A:", aux_variables
64        #print "TW:", time_warning
65        self.variables=variables
66        self.aux_variables=aux_variables
67        self.time_warning=time_warning
68        self.rename_variables=rename_variables
69        # Modify self.rename_variables to give users a chance
70        #for key in self.rename_variables.keys():
71        #    value=self.rename_variables[key]
72        #    match=re.search(r"^(.*)\s+\([^\)]+\)$", value)
73        #    if match:
74        #        self.rename_variables[key]=match.groups()[0]
75        #
76       
77        if sys.platform.find("win")>-1:
78            raise NAPlatformError
79
80        self.time_units=time_units
81        # Read the data section of the NASA Ames file
82        self.readData()
83        print "Reading in data..."
84        self.cdmsFilename=filename
85        self.cdmsFile=cdms.open(self.cdmsFilename, 'w')
86       
87        # at file level: write global attributes
88        for key in global_attributes.keys():
89            setattr(self.cdmsFile, key, global_attributes[key])
90
91        for key in cdmsMap.fromNA.keys():
92            if type(key)==tuple:
93                if key==("SCOM", "NCOM"):
94                    # Map special and normal comments into the global comments section
95                   
96                    commentline=""
97                    if self.NSCOML>0:
98                        commentline=commentline+"###NASA Ames Special Comments follow###\n"
99                        for i in self.SCOM: 
100                            if i.strip() not in ("###NASA Ames Special Comments follow###", "###NASA Ames Special Comments end###",
101                                    "Additional Variable Attributes defined in the source file and not translated elsewhere:",
102                                    "Additional Global Attributes defined in the source file and not translated elsewhere:", "\n"):
103                                commentline=commentline+"\n"+i
104                        commentline=commentline+"\n###NASA Ames Special Comments end###\n"
105                    if self.NNCOML>0:
106                        commentline=commentline+"###NASA Ames Normal Comments follow###\n"
107                        for i in self.NCOM: 
108                            if i.strip() not in ("###NASA Ames Normal Comments follow###", "###NASA Ames Normal Comments end###", "###Data Section begins on the next line###",
109                                    "Additional Variable Attributes defined in the source file and not translated elsewhere:",
110                                    "Additional Global Attributes defined in the source file and not translated elsewhere:", "\n"):
111                                commentline=commentline+"\n"+i
112                        commentline=commentline+"\n###NASA Ames Normal Comments end###"
113                        commentline=commentline.replace("\n\n", "\n")
114                    self.cdmsFile.comment=commentline
115                elif key==("ONAME", "ORG"):
116                    # Map the two organisation NA files to the institution field in CDMS (NetCDF)
117                    self.cdmsFile.institution="%s (ONAME from NASA Ames file); %s (ORG from NASA Ames file)." % (self.ONAME, self.ORG)
118                else:
119                    item=(getattr(self, key[0]))+"\n"+(getattr(self, key[1]))
120                    setattr(self.cdmsFile, cdmsMap.fromNA[key], item)
121            elif key=="RDATE":
122                dateparts=getattr(self, "RDATE")
123                datestring="%.4d-%.2d-%.2d" % tuple(dateparts)
124                item=datestring+" - NASA Ames File created/revised.\n"
125                timestring=time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(time.time()))
126                item=item+"\n"+timestring+" - Converted to CDMS (NetCDF) format using nappy-%s." % version.version
127            else:
128                setattr(self.cdmsFile, cdmsMap.fromNA[key], getattr(self, key))
129
130        # Then do dimensions
131        if not hasattr(self, 'cdmsAxes'):  self.createCdmsAxes()
132
133        # Then do variables
134        if not hasattr(self, 'cdmsVariables'):  self.createCdmsVariables()
135        for var in self.cdmsVariables:
136            self.cdmsFile.write(var)
137
138        # Then do auxiliary variables
139        if hasattr(self, "NAUXV") and (type(self.NAUXV)==type(1) and self.NAUXV>0):   # Are there any auxiliary variables?
140            if not hasattr(self, 'cdmsAuxVariables'):  self.createCdmsAuxVariables()
141            for avar in self.cdmsAuxVariables:
142                self.cdmsFile.write(avar)
143
144        self.cdmsFile.close()
145        return "Cdmsfile '%s' written successfully." % self.cdmsFilename
146
147    def createCdmsVariables(self):
148        """
149        Creates cdms variable list for writing out.
150        """
151        self.cdmsVariables=[]
152        if self.variables=="all":   
153            for var_number in range(self.NV):
154                self.cdmsVariables.append(self.toCdmsVariable(var_number))
155        else:
156            if type(self.variables[0])==type(1): # They are integers = indices
157                for var_number in self.variables:
158                    self.cdmsVariables.append(self.toCdmsVariable(var_number))   
159            elif type(self.variables[0])==type(""):
160                for var_name in self.variables:
161                    if var_name in self.VNAME:
162                        var_number=self.VNAME.index(var_name)
163                        self.cdmsVariables.append(self.toCdmsVariable(var_number))
164                    else:
165                        raise "Variable name not known: "+var_name
166
167
168    def toCdmsVariable(self, var_number, attributes={}):
169        """
170        Creates a single cdms variable from the variable number provided in the list.
171        """
172        if sys.platform.find("win")>-1:
173            raise NAPlatformError
174        (varname, units, miss, scal)=self.getVariable(var_number)
175        #print self.V
176        msg="\nAdding variable: %s" % self.VNAME[var_number]
177        print msg
178        self.outputMessage.append(msg)
179        array=Numeric.array(self.V[var_number])
180        array=array*scal
181        # Set up axes
182        if not hasattr(self, 'cdmsAxes'):
183            self.createCdmsAxes()
184
185        # Set up variable
186        var=cdms.createVariable(array, axes=self.cdmsAxes, fill_value=miss, attributes=attributes)
187
188        # Sort units etc
189        if units:   var.units=units
190       
191        # Add the best variable name
192        if len(varname)<40:
193            var.id=safe_nc_id.sub("_", varname).lower()
194        else:
195            var.id="naVariable_%s" % (var_number)
196       
197        # Check if user wants to rename this variable
198        #print self.rename_variables, "3"*200
199        #if varname in self.rename_variables.keys():
200        #    varname=self.rename_variables[varname]
201           
202        var.long_name=var.name=var.title=varname
203
204        # If we have a standard name list then compare to it
205        if varname in ("standard_name_list",) or varname in ("badc_list",):
206            var.standard_name="name from the list###**"
207
208        # Add a NASA Ames variable number (for mapping correctly back to NASA Ames)
209        var.nasa_ames_var_number=var_number
210
211        return var
212
213    def createCdmsAuxVariables(self):
214        """
215        Creates a cdms variable from an auxiliary variable
216        """
217        self.cdmsAuxVariables=[]
218
219        if self.aux_variables=="all":   
220            for avar_number in range(self.NAUXV):
221                self.cdmsAuxVariables.append(self.auxToCdmsVariable(avar_number))
222        else:
223            if type(self.aux_variables[0])==type(1): # They are integers = indices
224                for avar_number in self.aux_variables:
225                    self.cdmsAuxVariables.append(self.auxToCdmsVariable(avar_number))   
226            elif type(self.aux_variables[0])==type(""):
227                for avar_name in self.aux_variables:
228                    if avar_name in self.ANAME:
229                        avar_number=self.ANAME.index(avar_name)
230                        self.cdmsAuxVariables.append(self.auxToCdmsVariable(avar_number)) 
231                    else:
232                        raise "Auxiliary variable name not known: "+avar_name       
233
234    def auxToCdmsVariable(self, avar_number, attributes={}):
235        """
236        Converts an auxiliary variable to a cdms variable.
237        """
238        if sys.platform.find("win")>-1:
239            raise NAPlatformError
240        (varname, units, miss, scal)=self.getAuxVariable(avar_number)
241        array=Numeric.array(self.A[avar_number])
242        array=array*scal
243
244        msg="\nAdding auxiliary variable: %s" % self.ANAME[avar_number]
245        print msg
246        self.outputMessage.append(msg)
247        # Set up axes
248        if not hasattr(self, 'cdmsAxes'):
249            self.createCdmsAxes()
250
251        # Set up variable
252        var=cdms.createVariable(array, axes=[self.cdmsAxes[0]], fill_value=miss, attributes=attributes)
253
254        # Sort units etc
255        if units:   var.units=units
256        if len(varname)<20:
257            var.id=safe_nc_id.sub("_", varname).lower()
258        else:
259            var.id="naAuxVariable_%s" % (avar_number)
260
261        var.long_name=var.name=varname
262        if varname in ("standard_name_list",) or varname in ("badc_list",):
263            var.standard_name="name from the list***"
264
265
266        # Add a NASA Ames auxiliary variable number (for mapping correctly back to NASA Ames)
267        var.nasa_ames_aux_var_number=avar_number
268
269        return var       
270
271    def createCdmsAxes(self): 
272        """
273        Creates cdms axes from information provided in the NASA Ames dictionary.
274        """
275        if sys.platform.find("win")>-1:
276            raise NAPlatformError
277        if not hasattr(self, 'cdmsAxes'):       
278            self.cdmsAxes=[]
279        for ivar_number in range(self.NIV):
280            self.cdmsAxes.append(self.toCdmsAxis(ivar_number))
281
282    def toCdmsAxis(self, ivar_number):
283        """
284        Creates a cdms axis from a NASA Ames independent variable.
285        """
286        if sys.platform.find("win")>-1:
287            raise NAPlatformError
288
289        if self._normalizedX=="no": self._normalizeIndVars()
290
291        if self.NIV==1:
292            array=self.X
293        else:
294            array=self.X[ivar_number]
295        axis=cdms.createAxis(array)
296        axis.id=axis.name=axis.long_name=self.XNAME[ivar_number]
297        (varname, units)=self.getIndependentVariable(ivar_number)
298       
299        # Sort units etc
300        if units:   axis.units=units
301        if len(varname)<20:
302            axis.id=safe_nc_id.sub("_", varname).lower()
303        else:
304            axis.id="naAuxVariable_%s" % (ivar_number)
305
306        if units: axis.units=units
307        axis_types=("longitude", "latitude", "level", "time")
308        designators={"longitude":axis.designateLongitude, "latitude":axis.designateLatitude,
309                     "level":axis.designateLevel, "time":axis.designateTime}
310        for axis_type in axis_types:
311            if re.search(axis_type, varname, re.IGNORECASE):
312                axis.standard_name=axis.id=axis_type
313                apply(designators[axis_type.lower()])
314                # Check warning for time units pattern
315                if axis.isTime() and (not hasattr(axis, "units") or not time_units_pattn.match(axis.units)):
316                    if self.time_units==None:
317                   
318                        timeUnitsInput="I WON'T MATCH"
319                        while timeUnitsInput!="" and not time_units_pattn.match(timeUnitsInput):
320                            message="""\nWARNING: Could not recognise time units. For true NetCDF compability
321please insert the correct time unit string below in the format:
322   
323    <units> since <YYYY>-<MM>-<DD> <hh>-<mm>-<ss>
324   
325Where:
326    <units> is a known time interval such as years, months, days, etc.
327    <YYYY> is the year, <MM> is the month, <DD> is the day,
328    <hh> is the hour, <mm> is minutes, <ss> is seconds.
329"""                         
330                            if self.time_warning=="yes":
331                                print message
332                                timeUnitsInput=raw_input("Please insert your time unit string here (or leave blank):").strip()
333                            else: 
334                                timeUnitsInput=""
335                                self.outputMessage.append(message)
336                        if timeUnitsInput!="":
337                            self.time_units=timeUnitsInput
338
339                    axis.units=self.time_units
340                    axis.long_name=axis.name="time (%s)" % self.time_units
341                    if axis.units==None: 
342                        if units:
343                            axis.units=units   
344                        else:
345                            axis.units="Not known"
346        return axis
347
348
349class NA2Cdms(NAToCdms):
350    """
351    Pseudonym for NAToCdms class.
352    """
353    pass
Note: See TracBrowser for help on using the repository browser.