source: nappy/trunk/naFile.py @ 366

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/nappy/trunk/naFile.py@366
Revision 366, 10.3 KB checked in by astephen, 14 years ago (diff)

Latest version - for compatibility with web service. Includes multiple file
output on conversion from NetCDF.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
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"""
6naFile.py
7=========
8
9A containter module for the mixin base class NAFile that is subclassed
10for individual FFIs. Each FFI class is held in an individual file.
11
12"""
13
14# 08/05/04 updated by selatham for bug fixes and new write methods
15
16# Imports from python standard library
17import sys
18import time
19import re
20import StringIO
21
22# Imports from nappy package
23import naCore
24from textParser import *
25from naError import *
26import naToCdms
27
28class NAFile(naCore.NACore, naToCdms.NAToCdms):
29
30    """
31    NAFile class is a sub-class of NACore and NAToCdms mixin classes.
32    NAFile is also a mixin class and should not be called directly.
33   
34    NAFile holds all the methods are common to either all or more than
35    one NASA Ames FFI class. These methods set up the main read and write
36    functionality for the NASA Ames format.
37
38    When a sub-class of NAFile is called with a read ('r' - default) or
39    write ('w') mode the header in the file is automatically read or written.
40    However, the user must explicitly read or write the data section with a
41    call to the 'readData' or 'writeData' methods.
42
43    """
44
45    def __init__(self, filename, mode="r", naDict={}, spacer="    ", floatFormat="%8.3f"):
46        """
47        Initialization of class, decides if user wishes to read or write
48        NASA Ames file.
49        """
50        naCore.NACore.__init__(self)
51        self.naDict=naDict
52        self.filename=filename
53        self._open(mode)
54        self.spacer=spacer
55        self.floatFormat=floatFormat
56
57        if mode=="r":
58            self._normalizedX="yes"
59            self.readHeader()
60        elif mode=="w":
61            self._parseDictionary()
62            self.header=StringIO.StringIO()
63            self.writeHeader()
64            self.writeData()
65        else:
66            raise "Unknown file mode '%s'." % mode
67        self.close()
68       
69    def _open(self, mode):
70        "Wrapper to builtin open file function."
71        self.file=open(self.filename, mode)
72
73    def close(self):
74        "Wrapper to builtin close file function."
75        self.file.close()
76
77    def _parseDictionary(self):
78        """
79        Parser for the optional naDict argument containing a dictionary
80        of NASA Ames internal variables. These are saved as instance attributes
81        with the name used in the NASA Ames documentation.
82        """
83        for i in self.naDict.keys():
84            setattr(self, i, self.naDict[i])
85
86    def _readTopLine(self):
87        """
88        Reads number of header lines and File Format Index from top line.
89        Also assigns a value to NIV for the number of independent variables
90        based on the first character in the FFI.
91
92        Returns NLHEAD and FFI in a tuple.
93        """
94        (self.NLHEAD, self.FFI)=readItemsFromLine(self.file.readline(), 2, int)
95        self.NIV=int(self.FFI/1000)
96        return (self.NLHEAD, self.FFI)
97
98    def _readLines(self, nlines):
99        "Reads nlines lines from a file and returns them in a list."
100        lines=[]
101        for i in range(nlines):
102            lines.append(self.file.readline().strip())
103        return lines
104
105    def _checkForBlankLines(self, datalines):
106        """
107        Searches for empty lines in the middle of the data section and raises
108        as error if found. It ignores empty lines at the end of the file but
109        strips them out before returning a list of lines for reading.
110        """
111        empties=None
112        count=0
113        rtlines=[]
114        for line in datalines:
115            if re.match(r"^[\s\n\t\r]*$", line):
116                empties=1
117            else:
118                if empties==1:   # If data line found after empty line then raise
119                    raise "Empty line found in data section at line: %s" % count
120                else:
121                    rtlines.append(line)
122            count=count+1
123        return rtlines
124
125    def _readCommonHeader(self):
126        """
127        Reads the header section common to all NASA Ames files.
128        """
129        self._readTopLine()
130        self.ONAME=readItemFromLine(self.file.readline(), str)
131        self.ORG=readItemFromLine(self.file.readline(), str)
132        self.SNAME=readItemFromLine(self.file.readline(), str)
133        self.MNAME=readItemFromLine(self.file.readline(), str)
134        (self.IVOL, self.NVOL)=readItemsFromLine(self.file.readline(), 2, int)
135        dates=readItemsFromLine(self.file.readline(), 6, int)
136        (self.DATE, self.RDATE)=(dates[:3], dates[3:])
137
138    def _writeCommonHeader(self):
139        """
140        Writes the header section common to all NASA Ames files.
141        """
142        self.header.write("%s    %s\n" % (self.NLHEAD, self.FFI))
143        self.header.write("%s\n" % self.ONAME)
144        self.header.write("%s\n" % self.ORG)
145        self.header.write("%s\n" % self.SNAME)
146        self.header.write("%s\n" % self.MNAME)
147        self.header.write("%s    %s\n" % (self.IVOL, self.NVOL))
148        self.header.write("%s %s %s    %s %s %s\n" % (self.DATE[0], self.DATE[1], self.DATE[2], self.RDATE[0], self.RDATE[1], self.RDATE[2]))
149
150    def _readVariablesHeaderSection(self):
151        """
152        Reads the variables section of the header.
153        Assumes we are at the right point in the file.
154        """
155        self.NV=readItemFromLine(self.file.readline(), int)
156        self.VSCAL=readItemsFromUnknownLines(self.file, self.NV, float)
157        self.VMISS=readItemsFromUnknownLines(self.file, self.NV, float)
158        self.VNAME=readItemsFromLines(self._readLines(self.NV), self.NV, str)
159
160    def _writeVariablesHeaderSection(self):
161        """
162        Writes the variables section of the header.
163        Assumes we are at the right point in the file.
164        """       
165        self.header.write("%s\n" % self.NV)
166        self.header.write(("%s "*self.NV+"\n") % tuple(self.VSCAL))
167        self.header.write(("%s "*self.NV+"\n")  % tuple(self.VMISS))
168        self.header.write("%s\n"*self.NV % tuple(self.VNAME))
169
170    def _readAuxVariablesHeaderSection(self):
171        """
172        Reads the auxiliary variables section of the header.
173        Assumes we are at the right point in the file.
174        """
175        self.NAUXV=readItemFromLine(self.file.readline(), int)
176        if self.NAUXV>0:       
177            self.ASCAL=readItemsFromUnknownLines(self.file, self.NAUXV, float)
178            self.AMISS=readItemsFromUnknownLines(self.file, self.NAUXV, float)
179            self.ANAME=readItemsFromLines(self._readLines(self.NAUXV), self.NAUXV, str)
180
181    def _writeAuxVariablesHeaderSection(self):
182        """
183        Writes the auxiliary variables section of the header.
184        Assumes we are at the right point in the file.
185        """
186        self.header.write("%s\n" % self.NAUXV)
187        if self.NAUXV>0:
188            self.header.write(("%s "*self.NAUXV+"\n")  % tuple(self.ASCAL))
189            self.header.write(("%s "*self.NAUXV+"\n")  % tuple(self.AMISS))
190            self.header.write("%s\n"*self.NAUXV % tuple(self.ANAME))                    #08/11/04 selatham
191
192    def _readCharAuxVariablesHeaderSection(self):
193        """
194        Reads the character-encoded auxiliary variables section of the header.
195        Assumes we are at the right point in the file.
196        """
197        self.NAUXV=readItemFromLine(self.file.readline(), int)
198        self.NAUXC=readItemFromLine(self.file.readline(), int)
199        nonCharAuxVars=self.NAUXV-self.NAUXC
200        if self.NAUXV>0:
201            self.ASCAL=readItemsFromUnknownLines(self.file, nonCharAuxVars, float)
202            self.AMISS=readItemsFromUnknownLines(self.file, nonCharAuxVars, float)
203            self.LENA=readItemsFromUnknownLines(self.file, self.NAUXC, int)
204            for i in range(nonCharAuxVars):
205                self.LENA.insert(0, None)
206            self.AMISS=self.AMISS+readItemsFromUnknownLines(self.file, self.NAUXC, str)   
207            self.ANAME=readItemsFromLines(self._readLines(self.NAUXV), self.NAUXV, str)       
208           
209    def _readComments(self):
210        """
211        Reads the special and normal comments sections.
212        Assumes we are at the right point in the file.
213        """       
214        self.NSCOML=readItemFromLine(self.file.readline(), int)
215        self._readSpecialComments()
216        self.NNCOML=readItemFromLine(self.file.readline(), int)
217        self._readNormalComments()
218
219    def _writeComments(self):
220        """
221        Writes the special and normal comments sections.
222        Assumes we are at the right point in the file.
223        """ 
224        self.header.write("%s\n" % self.NSCOML)
225        self.header.write("%s\n"*self.NSCOML % tuple(self.SCOM))
226        self.header.write("%s\n" % self.NNCOML)
227        self.header.write("%s\n"*self.NNCOML % tuple(self.NCOM))
228
229    def _fixHeaderLength(self, header):
230        """
231        Takes the header StringIO object and counts the number of lines to
232        return a list of lines with the correct number of header lines in
233        the top line.
234        """
235        header.seek(0)
236        lines=header.readlines()
237        headlength=len(lines)
238        lines[0]="%s    %s\n" % (headlength, self.FFI)
239        return lines
240
241    def _readSpecialComments(self):
242        """
243        Reads the special comments section.       
244        Assumes that we are at the right point in the file and that NSCOML
245        variable is known.
246        """
247        self.SCOM=self._readLines(self.NSCOML)
248        return self.SCOM
249
250    def _readNormalComments(self):
251        """
252        Reads the normal comments section.       
253        Assumes that we are at the right point in the file and that NNCOML
254        variable is known.
255        """
256        self.NCOM=self._readLines(self.NNCOML)
257        return self.NCOM
258
259    def readData(self):
260        """
261        Reads the data section of the file. This method actually calls a number
262        of FFI specific methods to setup the data arrays (lists of lists) and
263        read the various data sections.
264
265        This method can be called directly by the user.
266        """
267        self._setupArrays()
268        datalines=open(self.filename).readlines()[self.NLHEAD:]
269        datalines=self._checkForBlankLines(datalines)
270
271        # Set up loop over unbounded indpendent variable
272        m=0   # Unbounded independent variable mark       
273        while len(datalines)>0:
274            datalines=self._readData1(datalines, m)
275            datalines=self._readData2(datalines, m)
276            m=m+1   
277
Note: See TracBrowser for help on using the repository browser.