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

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

delimiter changes

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