source: nappy/trunk/nappy/na_file/na_file.py @ 3404

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

Minor fixes so all tests now work!

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