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

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

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