diff --git a/.gitignore b/.gitignore index 6114356095b41ff7d3694a6b4c540f24af651c7e..ce06d83c29d0f272a0d2924ca667c960b4cd8196 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ /.coverage *.egg-info __pycache__ - +docs/build/html diff --git a/docs/build/PLACEHOLDER b/docs/build/PLACEHOLDER new file mode 100644 index 0000000000000000000000000000000000000000..f5092967106f1bea0b9eb250f231f72710e55cb2 --- /dev/null +++ b/docs/build/PLACEHOLDER @@ -0,0 +1 @@ +... to make git keep a possibly empty directory ... \ No newline at end of file diff --git a/docs/source/conf.py b/docs/source/conf.py index 618c2f816e8ba2e1b2df9bba47c21a4dc23ba957..bbeffc2608606a934052443e61f48b286bf6023f 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -30,8 +30,7 @@ release = '2.0' # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. -extensions = [ -] +extensions = ['sphinx.ext.autodoc'] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] diff --git a/docs/source/index.rst b/docs/source/index.rst index e9e980e97a5773db40fef89bd2c1fac77a901246..49dd39bf709aa7bfea6f4f2cdfc70105eb2fc9d3 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -17,7 +17,7 @@ The ICARTT data format is described here: https://www-air.larc.nasa.gov/missions Installation ------------ -.. include:: ../../icartt/INSTALL.rst +.. include:: ../../INSTALL.rst Example ------------ @@ -54,12 +54,13 @@ Variable ^^^^^^^^^^ .. autoclass:: Variable - + :members: Dataset ^^^^^^^^^^ .. autoclass:: Dataset + :members: Indices and tables ================== diff --git a/docs/source/old_index.rst b/docs/source/old_index.rst deleted file mode 100644 index f8e3cc8bb394637c3e3d7b80e4e8aed2b981c5aa..0000000000000000000000000000000000000000 --- a/docs/source/old_index.rst +++ /dev/null @@ -1,57 +0,0 @@ -icartt -============================ - -icartt is an ICARTT file format reader and writer - -The ICARTT data format is described here: https://www-air.larc.nasa.gov/missions/etc/IcarttDataFormat.htm - -.. contents:: - :local: - :depth: 2 - -Installation ------------- - -.. include:: ../../icartt/INSTALL.rst - -Example ------------- - -:: - - import icartt - - # load a new dataset from an existing file - ict = icartt.Dataset('path/to/example.ict') - - # list variable names - ict.varnames - # e.g. ['Fractional_Day', 'UTC', 'JDAY', 'INDEX', 'FLIGHT', 'LOCAL_SUN_TIME', ... - - # get data for variable 'UTC': - ict['UTC'] - - # some metadata - ict.organization - ict.dataSource - ict.mission - - # write to (other) file: - with open('path/to/output.ict', 'wb') as f: - ict.write(f) - -API ----- - -.. module:: icartt - -Variable -^^^^^^^^^^ - -.. autoclass:: Variable - - -Dataset -^^^^^^^^^^ - -.. autoclass:: Dataset diff --git a/icartt/dataset.py b/icartt/dataset.py index 28555a63f656fcc2f37139da0713264978572402..540df478e7558a236f4d1ea47b8fe77f32bebcb3 100644 --- a/icartt/dataset.py +++ b/icartt/dataset.py @@ -56,34 +56,47 @@ class StandardNormalComments(collections.UserList): self.ingest(contents) class Variable(collections.UserList): - ''' - A Variable is a ICARTT variable description with name, units, scale and missing value. + '''An ICARTT variable description with name, units, scale and missing value. + + :param name: Name of the variable + :type name: str + + :param units: Units of the variable + :type units: str + + :param longname: Long name of the variable + :type longname: str + + :param scale: Scaling factor for the variable + :type scale: float, defaults to 1.0 + + :param miss: Missing value for the variable + :type miss: float, defaults to -99999.0 + + :param splitChar: Split character for text representation + :type splitChar: str, defaults to "," ''' @property def desc(self): - ''' - Return variable description string as it appears in an ICARTT file + '''Variable description string as it appears in an ICARTT file + + :return: description string + :rtype: str ''' return self.splitChar.join( [ str(self.name), str(self.units), str(self.longname) ] ) def append(self, *argv): - ''' - Append data to a variable. Depending on type (independent, dependent variable), + '''Append data to a variable. Depending on type (independent, dependent variable), all identifying (bounded and unbounded) independent variables need to be given. - Examples: - - - file type 1001, add value of independent variable: - ivar.append(234.4) - - file type 1001, add value of dependent variable: - ivar.append(234.4, 18.2) - - - file type 2110, add value of independent (unbounded) variable: - ivar.append(234.4) - - file type 2110, add value of independent (bounded) variable: - ivar.append(234.4, 9148.2) - - file type 2110, add value of dependent variable: - ivar.append(234.4, 9148.2, 34.2) + :param ivar: value of the independent (unbounded) variable + :type ivar: float + + :param ibvar: value of the independent (bounded) variable + :type ibvar: float, optional + + :param dvar: value of the dependent variable + :type dvar: float, optional ''' sanitized = lambda z: float(z) if not float(z) == float(self.miss) else float('NaN') @@ -102,6 +115,8 @@ class Variable(collections.UserList): self.data.append( x ) def __init__(self, name, units, longname, scale=1.0, miss=-99999.0, splitChar=","): + '''Constructor method + ''' self.name = name self.units = units self.longname = longname @@ -113,14 +128,24 @@ class Variable(collections.UserList): self.data = [] class Dataset: - ''' - An ICARTT dataset that can be created from scratch or read from a file, + '''An ICARTT dataset that can be created from scratch or read from a file, manipulated, and then written to a file. + + :param f: file path or file handle to use + :type f: str or file handle or stream object, defaults to None + + :param loadData: load data as well (or only header if False)? + :type loadData: bool, defaults to "True" + + :param splitChar: splitting character used to separate fields in a line + :type splitChar: str, defaults to "," ''' @property def nheader(self): - ''' - Header line count + '''Header line count + + :return: line count + :rtype: int ''' total = -1 if self.format == 1001: @@ -129,37 +154,50 @@ class Dataset: # 2: IVAR + IBVAR total = 16 + 2 + len(self.AUXVARS) + len(self.DVARS) + len(self.SCOM) + len(self.NCOM) return total - @property - def VARS(self): - ''' - Variables (independent + dependent + auxiliary) - ''' - vars = { self.IVAR.name: self.IVAR, **self.DVARS } - if self.format == 2110: - vars = { self.IBVAR.name: self.IBVAR, **vars, **self.AUXVARS } - return vars + @property def varnames(self): - ''' - Names of variables (independent and dependent) + '''Names of variables (independent and dependent) + + :return: list of variable names + :rtype: list ''' return [ x for x in self.VARS.keys() ] + @property def times(self): - ''' - Time steps of the data contained. + '''Time steps of the data + + :return: list of time steps + :rtype: list ''' return [ self.dateValid + datetime.timedelta(seconds=x) for x in self.IVAR ] + + @property + def VARS(self): + '''Variables (independent + dependent + auxiliary) + + :return: dictionary of all variables + :rtype: dict of Variable(s) + ''' + vars = { self.IVAR.name: self.IVAR, **self.DVARS } + if self.format == 2110: + vars = { self.IBVAR.name: self.IBVAR, **vars, **self.AUXVARS } + return vars def __getitem__(self, name): - ''' - Shortcut to enable access to variable data by name + '''Shortcut to enable access to variable data by name + + :return: variable data + :rtype: list ''' return self.VARS[name] def write_header(self, f=sys.stdout): - ''' - Write header to file handle + '''Write header + + :param f: handle to write to + :type f: file handle or StringIO stream, defaults to sys.stdout ''' def prnt(txt): f.write(str(txt) + "\n") @@ -232,8 +270,10 @@ class Dataset: prnt([ p(ibval, self.IBVAR) ] + [ p(dval[1], DVAR) for DVAR in self.DVARS.values() for dval in DVAR if (dval[0][0] == ival) and (dval[0][1] == ibval) ]) def write_data(self, f=sys.stdout): - ''' - Write data to file handle + '''Write data + + :param f: handle to write to + :type f: file handle or StringIO stream, defaults to sys.stdout ''' def prnt_data(vars): f.write( str(self.splitChar.join([ str(x) for x in vars ])) + "\n") @@ -243,18 +283,25 @@ class Dataset: elif self.format == 2110: nul = self._write_data_2110(prnt=prnt_data) else: - print("huh?") + warnings.warn("Unknown file format {:d}".format(self.format)) def write(self, f=sys.stdout): - ''' - Write to file handle + '''Write header and data + + :param f: handle to write to + :type f: file handle or StringIO stream, defaults to sys.stdout ''' self.write_header(f=f) self.write_data(f=f) def make_filename(self, date_format='%Y%m%d'): - ''' - Create ICARTT-compliant file name based on the information contained in the dataset + '''Create ICARTT-compliant file name based on the information contained in the dataset + + :param date_format: date format to use when parsing + :type date_format: str, defaults to '%Y%m%d' + + :return: file name generated + :rtype: string ''' fn = self.dataID + "_" +self.locationID + "_" +datetime.datetime.strftime(self.dateValid, date_format) fn += "_R" + str(self.revision) if not self.revision is None else "" @@ -264,8 +311,7 @@ class Dataset: return fn + ".ict" def read_header(self): - ''' - Read the ICARTT header (from file) + '''Read the ICARTT header (from file) ''' class Filehandle_with_linecounter: def __init__(self, f, splitChar): @@ -426,12 +472,12 @@ class Dataset: if self.nheader != nheader_suggested: warnings.warn("Number of header lines suggested in line 1 ({:d}) do not match actual header lines read ({:d})".format(nheader_suggested, self.nheader)) - def extract_items_1001(self, raw): + def _extract_items_1001(self, raw): for cur in range(len(raw)): self.IVAR.append(raw[cur][0]) nul = [ self.DVARS[key].append(raw[cur][0], raw[cur][i+1]) for i, key in enumerate(self.DVARS) ] - def extract_items_2110(self, raw): + def _extract_items_2110(self, raw): cur = 0 num_var_name = list(self.AUXVARS.keys())[0] while cur < len(raw): @@ -444,8 +490,7 @@ class Dataset: cur += 1 + nprimary def read_data(self): - ''' - Read ICARTT data (from file) + '''Read ICARTT data (from file) ''' if self.input_fhandle.closed: self.input_fhandle = open(self.input_fhandle.name) @@ -454,17 +499,16 @@ class Dataset: raw = [ line.split(self.splitChar) for line in self.input_fhandle ] if self.format == 1001: - nul = self.extract_items_1001(raw) + nul = self._extract_items_1001(raw) elif self.format == 2110: - nul = self.extract_items_1001(raw) + nul = self._extract_items_1001(raw) else: warnings.warn("Unknown file format: {:d}, could not read data.".format(self.format)) self.input_fhandle.close() def read(self): - ''' - Read ICARTT data and header + '''Read ICARTT data and header ''' self.read_header() self.read_data() @@ -477,10 +521,7 @@ class Dataset: pass def __init__(self, f=None, loadData=True, splitChar=","): - ''' - :param string/file f: file path or file object to use - :param bool loadData: load data as well (or only header if False)? - :param string splitChar: the splitting character used to separate fields in a line + '''Constructor method ''' self.format = 1001 self.version = None @@ -538,3 +579,22 @@ class Dataset: self.read_header() if loadData: self.read_data() + + +''' + - file type 1001, add value of independent variable: + ivar.append(234.4) + + - file type 1001, add value of dependent variable: + ivar.append(234.4, 18.2) + + + - file type 2110, add value of independent (unbounded) variable: + ivar.append(234.4) + + - file type 2110, add value of independent (bounded) variable: + ivar.append(234.4, 9148.2) + + - file type 2110, add value of dependent variable: + ivar.append(234.4, 9148.2, 34.2) +'''