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

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

Adding annotation column

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